mkccode.tcl 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. #!/usr/bin/tclsh
  2. #
  3. # Use this script to build C-language source code for a program that uses
  4. # tclsqlite.c together with custom TCL scripts and/or C extensions for
  5. # either SQLite or TCL.
  6. #
  7. # Usage example:
  8. #
  9. # tclsh mkccode.tcl -DENABLE_FEATURE_XYZ demoapp.c.in >demoapp.c
  10. #
  11. # The demoapp.c.in file contains a mixture of C code, TCL script, and
  12. # processing directives used by mktclsqliteprog.tcl to build the final C-code
  13. # output file. Most lines of demoapp.c.in are copied straight through into
  14. # the output. The following control directives are recognized:
  15. #
  16. # BEGIN_STRING
  17. #
  18. # This marks the beginning of large string literal - usually a TCL
  19. # script of some kind. Subsequent lines of text through the first
  20. # line that begins with END_STRING are converted into a C-language
  21. # string literal.
  22. #
  23. # INCLUDE path
  24. #
  25. # The path argument is the name of a file to be inserted in place of
  26. # the INCLUDE line. The path can begin with $ROOT to signify the
  27. # root of the SQLite source tree, or $HOME to signify the directory
  28. # that contains the demoapp.c.in input script itself. If the path does
  29. # not begin with either $ROOT or $HOME, then it is interpreted relative
  30. # to the current working directory.
  31. #
  32. # If the INCLUDE occurs in the middle of BEGIN_STRING...END_STRING
  33. # then all of the text in the input file is converted into C-language
  34. # string literals.
  35. #
  36. # IFDEF macro
  37. # IFNDEF macro
  38. # ELSE
  39. # ENDIF
  40. #
  41. # The text from "IFDEF macro" down to the next ELSE or ENDIF is
  42. # included only if -Dmacro appears as a command-line argument.
  43. # The "IFNDEF macro" simply inverts the initial test.
  44. #
  45. # None of the control directives described above will nest. Only the
  46. # top-level input file ("demoapp.c.in" in the example) is interpreted.
  47. # referenced files are copied verbatim.
  48. #
  49. proc usage {} {
  50. puts stderr "Usage: $::argv0 \[OPTIONS\] TEMPLATE >OUTPUT"
  51. exit 1
  52. }
  53. set infile {}
  54. foreach ax $argv {
  55. if {[string match -D* $ax]} {
  56. if {[string match *=* $ax]} {
  57. regexp -- {-D([^=]+)=(.*)} $ax all name value
  58. set DEF($name) $value
  59. } else {
  60. set DEF([string range $ax 2 end]) 1
  61. }
  62. continue
  63. }
  64. if {[string match -* $ax]} {
  65. puts stderr "$::argv0: Unknown option \"$ax\""
  66. usage
  67. }
  68. if {$infile!=""} {
  69. puts stderr "$::argv0: Surplus argument: \"$ax\""
  70. usage
  71. }
  72. set infile $ax
  73. }
  74. set ROOT [file normalize [file dir $argv0]/..]
  75. set HOME [file normalize [file dir $infile]]
  76. set in [open $infile rb]
  77. puts [subst {/* DO NOT EDIT
  78. **
  79. ** This file was generated by \"$argv0 $argv\".
  80. ** To make changes, edit $infile then rerun the generator
  81. ** command.
  82. */}]
  83. set instr 0
  84. set omit {}
  85. set nomit 0
  86. set ln 0
  87. while {1} {
  88. set line [gets $in]
  89. incr ln
  90. if {[eof $in]} break
  91. if {[regexp {^INCLUDE (.*)} $line all path]} {
  92. if {$nomit>0 && [string match *1* $omit]} continue
  93. if {0} {
  94. # https://github.com/msteveb/jimtcl/issues/320
  95. regsub {^\$ROOT\y} $path $ROOT path
  96. regsub {^\$HOME\y} $path $HOME path
  97. } else {
  98. set path [string map "\$ROOT $ROOT" $path]
  99. set path [string map "\$HOME $HOME" $path]
  100. # or: set path [string map "\$HOME $HOME \$ROOT $ROOT" $path]
  101. }
  102. set in2 [open $path rb]
  103. puts "/* INCLUDE $path */"
  104. if {$instr} {
  105. while {1} {
  106. set line [gets $in2]
  107. if {[eof $in2]} break
  108. set x [string map "\\\\ \\\\\\\\ \\\" \\\\\"" $line]
  109. puts "\"$x\\n\""
  110. }
  111. } else {
  112. puts [read $in2]
  113. }
  114. puts "/* END $path */"
  115. close $in2
  116. continue
  117. }
  118. if {[regexp {^BEGIN_STRING} $line]} {
  119. set instr 1
  120. puts "/* BEGIN_STRING */"
  121. continue
  122. }
  123. if {[regexp {^END_STRING} $line]} {
  124. set instr 0
  125. puts "/* END_STRING */"
  126. continue
  127. }
  128. if {[regexp {^IFNDEF +([A-Za-z_0-9]+)} $line all name]} {
  129. set omit $omit[info exists DEF($name)]
  130. incr nomit
  131. continue
  132. }
  133. if {[regexp {^IFDEF +([A-Za-z_0-9]+)} $line all name]} {
  134. set omit $omit[expr {![info exists DEF($name)]}]
  135. incr nomit
  136. continue
  137. }
  138. if {[regexp {^ELSE} $line]} {
  139. if {!$nomit} {
  140. puts stderr "$infile:$ln: ELSE without a prior IFDEF"
  141. exit 1
  142. }
  143. set omit [string range $omit 0 end-1][expr {![string index $omit end]}]
  144. continue
  145. }
  146. if {[regexp {^ENDIF} $line]} {
  147. if {!$nomit} {
  148. puts stderr "$infile:$ln: ENDIF without a prior IFDEF"
  149. exit 1
  150. }
  151. incr nomit -1
  152. set omit [string range $omit 0 [expr {$nomit-1}]]
  153. continue
  154. }
  155. if {$nomit>0 && [string match *1* $omit]} {
  156. # noop
  157. } elseif {$instr} {
  158. set x [string map "\\\\ \\\\\\\\ \\\" \\\\\"" $line]
  159. puts "\"$x\\n\""
  160. } else {
  161. puts $line
  162. }
  163. }
  164. if {$nomit} {
  165. puts stderr "$infile:$ln: One or more unterminated IFDEFs"
  166. exit 1
  167. }