1 1.1 christos ######################################################################## 2 1.1 christos # 2025 April 5 3 1.1 christos # 4 1.1 christos # The author disclaims copyright to this source code. In place of 5 1.1 christos # a legal notice, here is a blessing: 6 1.1 christos # 7 1.1 christos # * May you do good and not evil. 8 1.1 christos # * May you find forgiveness for yourself and forgive others. 9 1.1 christos # * May you share freely, never taking more than you give. 10 1.1 christos # 11 1.1 christos ######################################################################## 12 1.1 christos # ----- @module teaish.tcl ----- 13 1.1 christos # @section TEA-ish ((TCL Extension Architecture)-ish) 14 1.1 christos # 15 1.1 christos # Functions in this file with a prefix of teaish__ are 16 1.1 christos # private/internal APIs. Those with a prefix of teaish- are 17 1.1 christos # public APIs. 18 1.1 christos # 19 1.1 christos # Teaish has a hard dependency on proj.tcl, and any public API members 20 1.1 christos # of that module are considered legal for use by teaish extensions. 21 1.1 christos # 22 1.1 christos # Project home page: https://fossil.wanderinghorse.net/r/teaish 23 1.1 christos 24 1.1 christos use proj 25 1.1 christos 26 1.1 christos # 27 1.1 christos # API-internal settings and shared state. 28 1.1 christos array set teaish__Config [proj-strip-hash-comments { 29 1.1 christos # 30 1.1 christos # Teaish's version number, not to be confused with 31 1.1 christos # teaish__PkgInfo(-version). 32 1.1 christos # 33 1.1 christos version 0.1-beta 34 1.1 christos 35 1.1 christos # set to 1 to enable some internal debugging output 36 1.1 christos debug-enabled 0 37 1.1 christos 38 1.1 christos # 39 1.1 christos # 0 = don't yet have extension's pkgindex 40 1.1 christos # 0x01 = found TEAISH_EXT_DIR/pkgIndex.tcl.in 41 1.1 christos # 0x02 = found srcdir/pkgIndex.tcl.in 42 1.1 christos # 0x10 = found TEAISH_EXT_DIR/pkgIndex.tcl (static file) 43 1.1 christos # 0x20 = static-pkgIndex.tcl pragma: behave as if 0x10 44 1.1 christos # 0x100 = disabled by -tm.tcl.in 45 1.1 christos # 0x200 = disabled by -tm.tcl 46 1.1 christos # 47 1.1 christos # Reminder: it's significant that the bottom 4 bits be 48 1.1 christos # cases where teaish manages ./pkgIndex.tcl. 49 1.1 christos # 50 1.1 christos pkgindex-policy 0 51 1.1 christos 52 1.1 christos # 53 1.1 christos # The pkginit counterpart of pkgindex-policy: 54 1.1 christos # 55 1.1 christos # 0 = no pkginit 56 1.1 christos # 0x01 = found default X.in: generate X from X.in 57 1.1 christos # 0x10 = found static pkginit file X 58 1.1 christos # 0x02 = user-provided X.in generates ./X. 59 1.1 christos # 0x20 = user-provided static pkginit file X 60 1.1 christos # 61 1.1 christos # The 0x0f bits indicate that teaish is responsible for cleaning up 62 1.1 christos # the (generated) pkginit file. 63 1.1 christos # 64 1.1 christos pkginit-policy 0 65 1.1 christos # 66 1.1 christos # 0 = no tm.tcl 67 1.1 christos # 0x01 = tm.tcl.in 68 1.1 christos # 0x10 = static tm.tcl 69 1.1 christos tm-policy 0 70 1.1 christos 71 1.1 christos # 72 1.1 christos # If 1+ then teaish__verbose will emit messages. 73 1.1 christos # 74 1.1 christos verbose 0 75 1.1 christos 76 1.1 christos # 77 1.1 christos # Mapping of pkginfo -flags to their TEAISH_xxx define (if any). 78 1.1 christos # This must not be modified after initialization. 79 1.1 christos # 80 1.1 christos pkginfo-f2d { 81 1.1 christos -name TEAISH_NAME 82 1.1 christos -name.dist TEAISH_DIST_NAME 83 1.1 christos -name.pkg TEAISH_PKGNAME 84 1.1 christos -version TEAISH_VERSION 85 1.1 christos -libDir TEAISH_LIBDIR_NAME 86 1.1 christos -loadPrefix TEAISH_LOAD_PREFIX 87 1.1 christos -vsatisfies TEAISH_VSATISFIES 88 1.1 christos -pkgInit.tcl TEAISH_PKGINIT_TCL 89 1.1 christos -pkgInit.tcl.in TEAISH_PKGINIT_TCL_IN 90 1.1 christos -url TEAISH_URL 91 1.1 christos -tm.tcl TEAISH_TM_TCL 92 1.1 christos -tm.tcl.in TEAISH_TM_TCL_IN 93 1.1 christos -options {} 94 1.1 christos -pragmas {} 95 1.1 christos -src {} 96 1.1 christos } 97 1.1 christos 98 1.1 christos # 99 1.1 christos # Queues for use with teaish-checks-queue and teaish-checks-run. 100 1.1 christos # 101 1.1 christos queued-checks-pre {} 102 1.1 christos queued-checks-post {} 103 1.1 christos 104 1.1 christos # Whether or not "make dist" parts are enabled. They get enabled 105 1.1 christos # when building from an extension's dir, disabled when building 106 1.1 christos # elsewhere. 107 1.1 christos dist-enabled 1 108 1.1 christos # Whether or not "make install" parts are enabled. By default 109 1.1 christos # they are, but we have a single use case where they're 110 1.1 christos # both unnecessary and unhelpful, so... 111 1.1 christos install-enabled 1 112 1.1 christos 113 1.1 christos # By default we enable compilation of a native extension but if the 114 1.1 christos # extension has no native code or the user wants to take that over 115 1.1 christos # via teaish.make.in or provide a script-only extension, we will 116 1.1 christos # elide the default compilation rules if this is 0. 117 1.1 christos dll-enabled 1 118 1.1 christos 119 1.1 christos # Files to include in the "make dist" bundle. 120 1.1 christos dist-files {} 121 1.1 christos 122 1.1 christos # List of source files for the extension. 123 1.1 christos extension-src {} 124 1.1 christos 125 1.1 christos # Path to the teaish.tcl file. 126 1.1 christos teaish.tcl {} 127 1.1 christos 128 1.1 christos # Dir where teaish.tcl is found. 129 1.1 christos extension-dir {} 130 1.1 christos 131 1.1 christos # Whether the generates TEASH_VSATISFIES_CODE should error out on a 132 1.1 christos # satisfies error. If 0, it uses return instead of error. 133 1.1 christos vsatisfies-error 1 134 1.1 christos 135 1.1 christos # Whether or not to allow a "full dist" - a "make dist" build which 136 1.1 christos # includes both the extension and teaish. By default this is only on 137 1.1 christos # if the extension dir is teaish's dir. 138 1.1 christos dist-full-enabled 0 139 1.1 christos }] 140 1.1 christos set teaish__Config(core-dir) $::autosetup(libdir)/teaish 141 1.1 christos 142 1.1 christos # 143 1.1 christos # Array of info managed by teaish-pkginfo-get and friends. Has the 144 1.1 christos # same set of keys as $teaish__Config(pkginfo-f2d). 145 1.1 christos # 146 1.1 christos array set teaish__PkgInfo {} 147 1.1 christos 148 1.1 christos # 149 1.1 christos # Runs {*}$args if $lvl is <= the current verbosity level, else it has 150 1.1 christos # no side effects. 151 1.1 christos # 152 1.1 christos proc teaish__verbose {lvl args} { 153 1.1 christos if {$lvl <= $::teaish__Config(verbose)} { 154 1.1 christos {*}$args 155 1.1 christos } 156 1.1 christos } 157 1.1 christos 158 1.1 christos # 159 1.1 christos # @teaish-argv-has flags... 160 1.1 christos # 161 1.1 christos # Returns true if any arg in $::argv matches any of the given globs, 162 1.1 christos # else returns false. 163 1.1 christos # 164 1.1 christos proc teaish-argv-has {args} { 165 1.1 christos foreach glob $args { 166 1.1 christos foreach arg $::argv { 167 1.1 christos if {[string match $glob $arg]} { 168 1.1 christos return 1 169 1.1 christos } 170 1.1 christos } 171 1.1 christos } 172 1.1 christos return 0 173 1.1 christos } 174 1.1 christos 175 1.1 christos if {[teaish-argv-has --teaish-verbose --t-v]} { 176 1.1 christos # Check this early so that we can use verbose-only messages in the 177 1.1 christos # pre-options-parsing steps. 178 1.1 christos set ::teaish__Config(verbose) 1 179 1.1 christos #teaish__verbose 1 msg-result "--teaish-verbose activated" 180 1.1 christos } 181 1.1 christos 182 1.1 christos msg-quiet use system ; # Outputs "Host System" and "Build System" lines 183 1.1 christos if {"--help" ni $::argv} { 184 1.1 christos teaish__verbose 1 msg-result "TEA(ish) Version = $::teaish__Config(version)" 185 1.1 christos teaish__verbose 1 msg-result "Source dir = $::autosetup(srcdir)" 186 1.1 christos teaish__verbose 1 msg-result "Build dir = $::autosetup(builddir)" 187 1.1 christos } 188 1.1 christos 189 1.1 christos # 190 1.1 christos # @teaish-configure-core 191 1.1 christos # 192 1.1 christos # Main entry point for the TEA-ish configure process. auto.def's primary 193 1.1 christos # (ideally only) job should be to call this. 194 1.1 christos # 195 1.1 christos proc teaish-configure-core {} { 196 1.1 christos proj-tweak-default-env-dirs 197 1.1 christos 198 1.1 christos set ::teaish__Config(install-mode) [teaish-argv-has --teaish-install*] 199 1.1 christos set ::teaish__Config(create-ext-mode) \ 200 1.1 christos [teaish-argv-has --teaish-create-extension=* --t-c-e=*] 201 1.1 christos set gotExt 0; # True if an extension config is found 202 1.1 christos if {!$::teaish__Config(create-ext-mode) 203 1.1 christos && !$::teaish__Config(install-mode)} { 204 1.1 christos # Don't look for an extension if we're in --t-c-e or --t-i mode 205 1.1 christos set gotExt [teaish__find_extension] 206 1.1 christos } 207 1.1 christos 208 1.1 christos # 209 1.1 christos # Set up the core --flags. This needs to come before teaish.tcl is 210 1.1 christos # sourced so that that file can use teaish-pkginfo-set to append 211 1.1 christos # options. 212 1.1 christos # 213 1.1 christos options-add [proj-strip-hash-comments { 214 1.1 christos with-tcl:DIR 215 1.1 christos => {Directory containing tclConfig.sh or a directory one level up from 216 1.1 christos that, from which we can derive a directory containing tclConfig.sh. 217 1.1 christos Defaults to the $TCL_HOME environment variable.} 218 1.1 christos 219 1.1 christos with-tclsh:PATH 220 1.1 christos => {Full pathname of tclsh to use. It is used for trying to find 221 1.1 christos tclConfig.sh. Warning: if its containing dir has multiple tclsh 222 1.1 christos versions, it may select the wrong tclConfig.sh! 223 1.1 christos Defaults to the $TCLSH environment variable.} 224 1.1 christos 225 1.1 christos tcl-stubs=0 => {Enable use of Tcl stubs library.} 226 1.1 christos 227 1.1 christos # TEA has --with-tclinclude but it appears to only be useful for 228 1.1 christos # building an extension against an uninstalled copy of TCL's own 229 1.1 christos # source tree. The policy here is that either we get that info 230 1.1 christos # from tclConfig.sh or we give up. 231 1.1 christos # 232 1.1 christos # with-tclinclude:DIR 233 1.1 christos # => {Specify the directory which contains the tcl.h. This should not 234 1.1 christos # normally be required, as that information comes from tclConfig.sh.} 235 1.1 christos 236 1.1 christos # We _generally_ want to reduce the possibility of flag collisions with 237 1.1 christos # extensions, and thus use a teaish-... prefix on most flags. However, 238 1.1 christos # --teaish-extension-dir is frequently needed, so... 239 1.1 christos # 240 1.1 christos # As of this spontaneous moment, we'll settle on using --t-A-X to 241 1.1 christos # abbreviate --teaish-A...-X... flags when doing so is 242 1.1 christos # unambiguous... 243 1.1 christos ted: t-e-d: 244 1.1 christos teaish-extension-dir:DIR 245 1.1 christos => {Looks for an extension in the given directory instead of the current 246 1.1 christos dir.} 247 1.1 christos 248 1.1 christos t-c-e: 249 1.1 christos teaish-create-extension:TARGET_DIRECTORY 250 1.1 christos => {Writes stub files for creating an extension. Will refuse to overwrite 251 1.1 christos existing files without --teaish-force.} 252 1.1 christos 253 1.1 christos t-f 254 1.1 christos teaish-force 255 1.1 christos => {Has a context-dependent meaning (autosetup defines --force for its 256 1.1 christos own use).} 257 1.1 christos 258 1.1 christos t-d-d 259 1.1 christos teaish-dump-defines 260 1.1 christos => {Dump all configure-defined vars to config.defines.txt} 261 1.1 christos 262 1.1 christos t-v:=0 263 1.1 christos teaish-verbose:=0 264 1.1 christos => {Enable more (often extraneous) messages from the teaish core.} 265 1.1 christos 266 1.1 christos t-d 267 1.1 christos teaish-debug=0 => {Enable teaish-specific debug output} 268 1.1 christos 269 1.1 christos t-i 270 1.1 christos teaish-install:=auto 271 1.1 christos => {Installs a copy of teaish, including autosetup, to the target dir. 272 1.1 christos When used with --teaish-create-extension=DIR, a value of "auto" 273 1.1 christos (no no value) will inherit that directory.} 274 1.1 christos 275 1.1 christos #TODO: --teaish-install-extension:=dir as short for 276 1.1 christos # --t-c-e=dir --t-i 277 1.1 christos 278 1.1 christos t-e-p: 279 1.1 christos teaish-extension-pkginfo:pkginfo 280 1.1 christos => {For use with --teaish-create-extension. If used, it must be a 281 1.1 christos list of arguments for use with teaish-pkginfo-set, e.g. 282 1.1 christos --teaish-extension-pkginfo="-name Foo -version 2.3"} 283 1.1 christos 284 1.1 christos t-v-c 285 1.1 christos teaish-vsatisfies-check=1 286 1.1 christos => {Disable the configure-time "vsatisfies" check on the target tclsh.} 287 1.1 christos 288 1.1 christos }]; # main options. 289 1.1 christos 290 1.1 christos if {$gotExt} { 291 1.1 christos # We found an extension. Source it... 292 1.1 christos set ttcl $::teaish__Config(teaish.tcl) 293 1.1 christos proj-assert {"" ne [teaish-pkginfo-get -name]} 294 1.1 christos proj-assert {[file exists $ttcl]} \ 295 1.1 christos "Expecting to have found teaish.(tcl|config) by now" 296 1.1 christos if {[string match *.tcl $ttcl]} { 297 1.1 christos uplevel 1 {source $::teaish__Config(teaish.tcl)} 298 1.1 christos } else { 299 1.1 christos teaish-pkginfo-set {*}[proj-file-content -trim $ttcl] 300 1.1 christos } 301 1.1 christos unset ttcl 302 1.1 christos # Set up some default values if the extension did not set them. 303 1.1 christos # This must happen _after_ it's sourced but before 304 1.1 christos # teaish-configure is called. 305 1.1 christos array set f2d $::teaish__Config(pkginfo-f2d) 306 1.1 christos foreach {pflag key type val} { 307 1.1 christos - TEAISH_CFLAGS -v "" 308 1.1 christos - TEAISH_LDFLAGS -v "" 309 1.1 christos - TEAISH_MAKEFILE -v "" 310 1.1 christos - TEAISH_MAKEFILE_CODE -v "" 311 1.1 christos - TEAISH_MAKEFILE_IN -v "" 312 1.1 christos - TEAISH_PKGINDEX_TCL -v "" 313 1.1 christos - TEAISH_PKGINDEX_TCL_IN -v "" 314 1.1 christos - TEAISH_PKGINIT_TCL -v "" 315 1.1 christos - TEAISH_PKGINIT_TCL_IN -v "" 316 1.1 christos - TEAISH_PKGINIT_TCL_TAIL -v "" 317 1.1 christos - TEAISH_TEST_TCL -v "" 318 1.1 christos - TEAISH_TEST_TCL_IN -v "" 319 1.1 christos 320 1.1 christos -version - -v 0.0.0 321 1.1 christos -name.pkg - -e {set ::teaish__PkgInfo(-name)} 322 1.1 christos -name.dist - -e {set ::teaish__PkgInfo(-name)} 323 1.1 christos -libDir - -e { 324 1.1 christos join [list \ 325 1.1 christos $::teaish__PkgInfo(-name.pkg) \ 326 1.1 christos $::teaish__PkgInfo(-version)] "" 327 1.1 christos } 328 1.1 christos -loadPrefix - -e { 329 1.1 christos string totitle $::teaish__PkgInfo(-name.pkg) 330 1.1 christos } 331 1.1 christos -vsatisfies - -v {{Tcl 8.5-}} 332 1.1 christos -pkgInit.tcl - -v "" 333 1.1 christos -pkgInit.tcl.in - -v "" 334 1.1 christos -url - -v "" 335 1.1 christos -tm.tcl - -v "" 336 1.1 christos -tm.tcl.in - -v "" 337 1.1 christos -src - -v "" 338 1.1 christos } { 339 1.1 christos #proj-assert 0 {Just testing} 340 1.1 christos set isPIFlag [expr {"-" ne $pflag}] 341 1.1 christos if {$isPIFlag} { 342 1.1 christos if {[info exists ::teaish__PkgInfo($pflag)]} { 343 1.1 christos # Was already set - skip it. 344 1.1 christos continue; 345 1.1 christos } 346 1.1 christos proj-assert {{-} eq $key};# "Unexpected pflag=$pflag key=$key type=$type val=$val" 347 1.1 christos set key $f2d($pflag) 348 1.1 christos } 349 1.1 christos if {"" ne $key} { 350 1.1 christos if {"<nope>" ne [get-define $key "<nope>"]} { 351 1.1 christos # Was already set - skip it. 352 1.1 christos continue 353 1.1 christos } 354 1.1 christos } 355 1.1 christos switch -exact -- $type { 356 1.1 christos -v {} 357 1.1 christos -e { set val [eval $val] } 358 1.1 christos default { proj-error "Invalid type flag: $type" } 359 1.1 christos } 360 1.1 christos #puts "***** defining default $pflag $key {$val} isPIFlag=$isPIFlag" 361 1.1 christos if {$key ne ""} { 362 1.1 christos define $key $val 363 1.1 christos } 364 1.1 christos if {$isPIFlag} { 365 1.1 christos set ::teaish__PkgInfo($pflag) $val 366 1.1 christos } 367 1.1 christos } 368 1.1 christos unset isPIFlag pflag key type val 369 1.1 christos array unset f2d 370 1.1 christos }; # sourcing extension's teaish.tcl 371 1.1 christos 372 1.1 christos if {[llength [info proc teaish-options]] > 0} { 373 1.1 christos # Add options defined by teaish-options, which is assumed to be 374 1.1 christos # imported via [teaish-get -teaish-tcl]. 375 1.1 christos set o [teaish-options] 376 1.1 christos if {"" ne $o} { 377 1.1 christos options-add $o 378 1.1 christos } 379 1.1 christos } 380 1.1 christos #set opts [proj-options-combine] 381 1.1 christos #lappend opts teaish-debug => {x}; #testing dupe entry handling 382 1.1 christos if {[catch {options {}} msg xopts]} { 383 1.1 christos # Workaround for <https://github.com/msteveb/autosetup/issues/73> 384 1.1 christos # where [options] behaves oddly on _some_ TCL builds when it's 385 1.1 christos # called from deeper than the global scope. 386 1.1 christos dict incr xopts -level 387 1.1 christos return {*}$xopts $msg 388 1.1 christos } 389 1.1 christos 390 1.1 christos proj-xfer-options-aliases { 391 1.1 christos t-c-e => teaish-create-extension 392 1.1 christos t-d => teaish-debug 393 1.1 christos t-d-d => teaish-dump-defines 394 1.1 christos ted => teaish-extension-dir 395 1.1 christos t-e-d => teaish-extension-dir 396 1.1 christos t-e-p => teaish-extension-pkginfo 397 1.1 christos t-f => teaish-force 398 1.1 christos t-i => teaish-install 399 1.1 christos t-v => teaish-verbose 400 1.1 christos t-v-c => teaish-vsatisfies-check 401 1.1 christos } 402 1.1 christos 403 1.1 christos scan [opt-val teaish-verbose 0] %d ::teaish__Config(verbose) 404 1.1 christos set ::teaish__Config(debug-enabled) [opt-bool teaish-debug] 405 1.1 christos 406 1.1 christos set exitEarly 0 407 1.1 christos if {[proj-opt-was-provided teaish-create-extension]} { 408 1.1 christos teaish__create_extension [opt-val teaish-create-extension] 409 1.1 christos incr exitEarly 410 1.1 christos } 411 1.1 christos if {$::teaish__Config(install-mode)} { 412 1.1 christos teaish__install 413 1.1 christos incr exitEarly 414 1.1 christos } 415 1.1 christos 416 1.1 christos if {$exitEarly} { 417 1.1 christos file delete -force config.log 418 1.1 christos return 419 1.1 christos } 420 1.1 christos proj-assert {1==$gotExt} "Else we cannot have gotten this far" 421 1.1 christos 422 1.1 christos teaish__configure_phase1 423 1.1 christos } 424 1.1 christos 425 1.1 christos 426 1.1 christos # 427 1.1 christos # Internal config-time debugging output routine. It is not legal to 428 1.1 christos # call this from the global scope. 429 1.1 christos # 430 1.1 christos proc teaish-debug {msg} { 431 1.1 christos if {$::teaish__Config(debug-enabled)} { 432 1.1 christos puts stderr [proj-bold "** DEBUG: \[[proj-scope 1]\]: $msg"] 433 1.1 christos } 434 1.1 christos } 435 1.1 christos 436 1.1 christos # 437 1.1 christos # Runs "phase 1" of the configuration, immediately after processing 438 1.1 christos # --flags. This is what will import the client-defined teaish.tcl. 439 1.1 christos # 440 1.1 christos proc teaish__configure_phase1 {} { 441 1.1 christos msg-result \ 442 1.1 christos [join [list "Configuring build of Tcl extension" \ 443 1.1 christos [proj-bold [teaish-pkginfo-get -name] \ 444 1.1 christos [teaish-pkginfo-get -version]] "..."]] 445 1.1 christos 446 1.1 christos uplevel 1 { 447 1.1 christos use cc cc-db cc-shared cc-lib; # pkg-config 448 1.1 christos } 449 1.1 christos teaish__check_tcl 450 1.1 christos apply {{} { 451 1.1 christos # 452 1.1 christos # If --prefix or --exec-prefix are _not_ provided, use their 453 1.1 christos # TCL_... counterpart from tclConfig.sh. Caveat: by the time we can 454 1.1 christos # reach this point, autosetup's system.tcl will have already done 455 1.1 christos # some non-trivial amount of work with these to create various 456 1.1 christos # derived values from them, so we temporarily end up with a mishmash 457 1.1 christos # of autotools-compatibility var values. That will be straightened 458 1.1 christos # out in the final stage of the configure script via 459 1.1 christos # [proj-remap-autoconf-dir-vars]. 460 1.1 christos # 461 1.1 christos foreach {flag uflag tclVar} { 462 1.1 christos prefix prefix TCL_PREFIX 463 1.1 christos exec-prefix exec_prefix TCL_EXEC_PREFIX 464 1.1 christos } { 465 1.1 christos if {![proj-opt-was-provided $flag]} { 466 1.1 christos if {"exec-prefix" eq $flag} { 467 1.1 christos # If --exec-prefix was not used, ensure that --exec-prefix 468 1.1 christos # derives from the --prefix we may have just redefined. 469 1.1 christos set v {${prefix}} 470 1.1 christos } else { 471 1.1 christos set v [get-define $tclVar "???"] 472 1.1 christos teaish__verbose 1 msg-result "Using \$$tclVar for --$flag=$v" 473 1.1 christos } 474 1.1 christos proj-assert {"???" ne $v} "Expecting teaish__check_tcl to have defined $tclVar" 475 1.1 christos #puts "*** $flag $uflag $tclVar = $v" 476 1.1 christos proj-opt-set $flag $v 477 1.1 christos define $uflag $v 478 1.1 christos 479 1.1 christos # ^^^ As of here, all autotools-compatibility vars which derive 480 1.1 christos # from --$flag, e.g. --libdir, still derive from the default 481 1.1 christos # --$flag value which was active when system.tcl was 482 1.1 christos # included. So long as those flags are not explicitly passed to 483 1.1 christos # the configure script, those will be straightened out via 484 1.1 christos # [proj-remap-autoconf-dir-vars]. 485 1.1 christos } 486 1.1 christos } 487 1.1 christos }}; # --[exec-]prefix defaults 488 1.1 christos teaish__check_common_bins 489 1.1 christos # 490 1.1 christos # Set up library file names 491 1.1 christos # 492 1.1 christos proj-file-extensions 493 1.1 christos teaish__define_pkginfo_derived * 494 1.1 christos 495 1.1 christos teaish-checks-run -pre 496 1.1 christos if {[llength [info proc teaish-configure]] > 0} { 497 1.1 christos # teaish-configure is assumed to be imported via 498 1.1 christos # teaish.tcl 499 1.1 christos teaish-configure 500 1.1 christos } 501 1.1 christos teaish-checks-run -post 502 1.1 christos 503 1.1 christos define TEAISH_USE_STUBS [opt-bool tcl-stubs] 504 1.1 christos 505 1.1 christos apply {{} { 506 1.1 christos # Set up "vsatisfies" code for pkgIndex.tcl.in, 507 1.1 christos # _teaish.tester.tcl.in, and for a configure-time check. We would 508 1.1 christos # like to put this before [teaish-checks-run -pre] but it's 509 1.1 christos # marginally conceivable that a client may need to dynamically 510 1.1 christos # calculate the vsatisfies and set it via [teaish-configure]. 511 1.1 christos set vs [get-define TEAISH_VSATISFIES ""] 512 1.1 christos if {"" eq $vs} return 513 1.1 christos set code {} 514 1.1 christos set n 0 515 1.1 christos # Treat $vs as a list-of-lists {{Tcl 8.5-} {Foo 1.0- -3.0} ...} 516 1.1 christos # and generate Tcl which will run package vsatisfies tests with 517 1.1 christos # that info. 518 1.1 christos foreach pv $vs { 519 1.1 christos set n [llength $pv] 520 1.1 christos if {$n < 2} { 521 1.1 christos proj-error "-vsatisfies: {$pv} appears malformed. Whole list is: $vs" 522 1.1 christos } 523 1.1 christos set pkg [lindex $pv 0] 524 1.1 christos set vcheck {} 525 1.1 christos for {set i 1} {$i < $n} {incr i} { 526 1.1 christos lappend vcheck [lindex $pv $i] 527 1.1 christos } 528 1.1 christos if {[opt-bool teaish-vsatisfies-check]} { 529 1.1 christos set tclsh [get-define TCLSH_CMD] 530 1.1 christos set vsat "package vsatisfies \[ package provide $pkg \] $vcheck" 531 1.1 christos set vputs "puts \[ $vsat \]" 532 1.1 christos #puts "*** vputs = $vputs" 533 1.1 christos scan [exec echo $vputs | $tclsh] %d vvcheck 534 1.1 christos if {![info exists vvcheck] || 0 == $vvcheck} { 535 1.1 christos proj-fatal -up $tclsh "check failed:" $vsat 536 1.1 christos } 537 1.1 christos } 538 1.1 christos if {$::teaish__Config(vsatisfies-error)} { 539 1.1 christos set vunsat \ 540 1.1 christos [list error [list Package \ 541 1.1 christos $::teaish__PkgInfo(-name) $::teaish__PkgInfo(-version) \ 542 1.1 christos requires $pv]] 543 1.1 christos } else { 544 1.1 christos set vunsat return 545 1.1 christos } 546 1.1 christos lappend code \ 547 1.1 christos [string trim [subst -nocommands \ 548 1.1 christos {if { ![package vsatisfies [package provide $pkg] $vcheck] } {\n $vunsat\n}}]] 549 1.1 christos }; # foreach pv 550 1.1 christos define TEAISH_VSATISFIES_CODE [join $code "\n"] 551 1.1 christos }}; # vsatisfies 552 1.1 christos 553 1.1 christos if {[proj-looks-like-windows]} { 554 1.1 christos # Without this, linking of an extension will not work on Cygwin or 555 1.1 christos # Msys2. 556 1.1 christos msg-result "Using USE_TCL_STUBS for Unix(ish)-on-Windows environment" 557 1.1 christos teaish-cflags-add -DUSE_TCL_STUBS=1 558 1.1 christos } 559 1.1 christos 560 1.1 christos #define AS_LIBDIR $::autosetup(libdir) 561 1.1 christos define TEAISH_TESTUTIL_TCL $::teaish__Config(core-dir)/tester.tcl 562 1.1 christos 563 1.1 christos apply {{} { 564 1.1 christos # 565 1.1 christos # Ensure we have a pkgIndex.tcl and don't have a stale generated one 566 1.1 christos # when rebuilding for different --with-tcl=... values. 567 1.1 christos # 568 1.1 christos if {!$::teaish__Config(pkgindex-policy)} { 569 1.1 christos proj-error "Cannot determine which pkgIndex.tcl to use" 570 1.1 christos } 571 1.1 christos if {0x300 & $::teaish__Config(pkgindex-policy)} { 572 1.1 christos teaish__verbose 1 msg-result "pkgIndex disabled by -tm.tcl(.in)" 573 1.1 christos } else { 574 1.1 christos set tpi [proj-coalesce \ 575 1.1 christos [get-define TEAISH_PKGINDEX_TCL_IN] \ 576 1.1 christos [get-define TEAISH_PKGINDEX_TCL]] 577 1.1 christos proj-assert {$tpi ne ""} \ 578 1.1 christos "TEAISH_PKGINDEX_TCL should have been set up by now" 579 1.1 christos teaish__verbose 1 msg-result "Using pkgIndex from $tpi" 580 1.1 christos if {0x0f & $::teaish__Config(pkgindex-policy)} { 581 1.1 christos # Don't leave stale pkgIndex.tcl laying around yet don't delete 582 1.1 christos # or overwrite a user-managed static pkgIndex.tcl. 583 1.1 christos file delete -force -- [get-define TEAISH_PKGINDEX_TCL] 584 1.1 christos proj-dot-ins-append [get-define TEAISH_PKGINDEX_TCL_IN] 585 1.1 christos } else { 586 1.1 christos teaish-dist-add [file tail $tpi] 587 1.1 christos } 588 1.1 christos } 589 1.1 christos }}; # $::teaish__Config(pkgindex-policy) 590 1.1 christos 591 1.1 christos # 592 1.1 christos # Ensure we clean up TEAISH_PKGINIT_TCL if needed and @-process 593 1.1 christos # TEAISH_PKGINIT_TCL_IN if needed. 594 1.1 christos # 595 1.1 christos if {0x0f & $::teaish__Config(pkginit-policy)} { 596 1.1 christos file delete -force -- [get-define TEAISH_PKGINIT_TCL] 597 1.1 christos proj-dot-ins-append [get-define TEAISH_PKGINIT_TCL_IN] \ 598 1.1 christos [get-define TEAISH_PKGINIT_TCL] 599 1.1 christos } 600 1.1 christos if {0x0f & $::teaish__Config(tm-policy)} { 601 1.1 christos file delete -force -- [get-define TEAISH_TM_TCL] 602 1.1 christos proj-dot-ins-append [get-define TEAISH_TM_TCL_IN] 603 1.1 christos } 604 1.1 christos 605 1.1 christos apply {{} { 606 1.1 christos # Queue up any remaining dot-in files 607 1.1 christos set dotIns [list] 608 1.1 christos foreach {dIn => dOut} { 609 1.1 christos TEAISH_TESTER_TCL_IN => TEAISH_TESTER_TCL 610 1.1 christos TEAISH_TEST_TCL_IN => TEAISH_TEST_TCL 611 1.1 christos TEAISH_MAKEFILE_IN => TEAISH_MAKEFILE 612 1.1 christos } { 613 1.1 christos lappend dotIns [get-define $dIn ""] [get-define $dOut ""] 614 1.1 christos } 615 1.1 christos lappend dotIns $::autosetup(srcdir)/Makefile.in Makefile; # must be after TEAISH_MAKEFILE_IN. 616 1.1 christos # Much later: probably because of timestamps for deps purposes :-? 617 1.1 christos #puts "dotIns=$dotIns" 618 1.1 christos foreach {i o} $dotIns { 619 1.1 christos if {"" ne $i && "" ne $o} { 620 1.1 christos #puts " pre-dot-ins-append: \[$i\] -> \[$o\]" 621 1.1 christos proj-dot-ins-append $i $o 622 1.1 christos } 623 1.1 christos } 624 1.1 christos }} 625 1.1 christos 626 1.1 christos define TEAISH_DIST_FULL \ 627 1.1 christos [expr { 628 1.1 christos $::teaish__Config(dist-enabled) 629 1.1 christos && $::teaish__Config(dist-full-enabled) 630 1.1 christos }] 631 1.1 christos 632 1.1 christos define TEAISH_AUTOSETUP_DIR $::teaish__Config(core-dir) 633 1.1 christos define TEAISH_ENABLE_DIST $::teaish__Config(dist-enabled) 634 1.1 christos define TEAISH_ENABLE_INSTALL $::teaish__Config(install-enabled) 635 1.1 christos define TEAISH_ENABLE_DLL $::teaish__Config(dll-enabled) 636 1.1 christos define TEAISH_TCL $::teaish__Config(teaish.tcl) 637 1.1 christos 638 1.1 christos define TEAISH_DIST_FILES [join $::teaish__Config(dist-files)] 639 1.1 christos define TEAISH_EXT_DIR [join $::teaish__Config(extension-dir)] 640 1.1 christos define TEAISH_EXT_SRC [join $::teaish__Config(extension-src)] 641 1.1 christos proj-setup-autoreconfig TEAISH_AUTORECONFIG 642 1.1 christos foreach f { 643 1.1 christos TEAISH_CFLAGS 644 1.1 christos TEAISH_LDFLAGS 645 1.1 christos } { 646 1.1 christos # Ensure that any of these lists are flattened 647 1.1 christos define $f [join [get-define $f]] 648 1.1 christos } 649 1.1 christos proj-remap-autoconf-dir-vars 650 1.1 christos set tdefs [teaish__defines_to_list] 651 1.1 christos define TEAISH__DEFINES_MAP $tdefs; # injected into _teaish.tester.tcl 652 1.1 christos 653 1.1 christos # 654 1.1 christos # NO [define]s after this point! 655 1.1 christos # 656 1.1 christos proj-if-opt-truthy teaish-dump-defines { 657 1.1 christos proj-file-write config.defines.txt $tdefs 658 1.1 christos } 659 1.1 christos proj-dot-ins-process -validate 660 1.1 christos 661 1.1 christos }; # teaish__configure_phase1 662 1.1 christos 663 1.1 christos # 664 1.1 christos # Run checks for required binaries. 665 1.1 christos # 666 1.1 christos proc teaish__check_common_bins {} { 667 1.1 christos if {"" eq [proj-bin-define install]} { 668 1.1 christos proj-warn "Cannot find install binary, so 'make install' will not work." 669 1.1 christos define BIN_INSTALL false 670 1.1 christos } 671 1.1 christos if {"" eq [proj-bin-define zip]} { 672 1.1 christos proj-warn "Cannot find zip, so 'make dist.zip' will not work." 673 1.1 christos } 674 1.1 christos if {"" eq [proj-bin-define tar]} { 675 1.1 christos proj-warn "Cannot find tar, so 'make dist.tgz' will not work." 676 1.1 christos } 677 1.1 christos } 678 1.1 christos 679 1.1 christos # 680 1.1 christos # TCL... 681 1.1 christos # 682 1.1 christos # teaish__check_tcl performs most of the --with-tcl and --with-tclsh 683 1.1 christos # handling. Some related bits and pieces are performed before and 684 1.1 christos # after that function is called. 685 1.1 christos # 686 1.1 christos # Important [define]'d vars: 687 1.1 christos # 688 1.1 christos # - TCLSH_CMD is the path to the canonical tclsh or "". 689 1.1 christos # 690 1.1 christos # - TCL_CONFIG_SH is the path to tclConfig.sh or "". 691 1.1 christos # 692 1.1 christos # - TCLLIBDIR is the dir to which the extension library gets 693 1.1 christos # - installed. 694 1.1 christos # 695 1.1 christos proc teaish__check_tcl {} { 696 1.1 christos define TCLSH_CMD false ; # Significant is that it exits with non-0 697 1.1 christos define TCLLIBDIR "" ; # Installation dir for TCL extension lib 698 1.1 christos define TCL_CONFIG_SH ""; # full path to tclConfig.sh 699 1.1 christos 700 1.1 christos # Clear out all vars which would harvest from tclConfig.sh so that 701 1.1 christos # the late-config validation of @VARS@ works even if --disable-tcl 702 1.1 christos # is used. 703 1.1 christos proj-tclConfig-sh-to-autosetup "" 704 1.1 christos 705 1.1 christos # TODO: better document the steps this is taking. 706 1.1 christos set srcdir $::autosetup(srcdir) 707 1.1 christos msg-result "Checking for a suitable tcl... " 708 1.1 christos set use_tcl 1 709 1.1 christos set withSh [opt-val with-tclsh [proj-get-env TCLSH]] 710 1.1 christos set tclHome [opt-val with-tcl [proj-get-env TCL_HOME]] 711 1.1 christos if {[string match */lib $tclHome]} { 712 1.1 christos # TEA compatibility kludge: its --with-tcl wants the lib 713 1.1 christos # dir containing tclConfig.sh. 714 1.1 christos #proj-warn "Replacing --with-tcl=$tclHome for TEA compatibility" 715 1.1 christos regsub {/lib^} $tclHome "" tclHome 716 1.1 christos msg-result "NOTE: stripped /lib suffix from --with-tcl=$tclHome (a TEA-ism)" 717 1.1 christos } 718 1.1 christos if {0} { 719 1.1 christos # This misinteracts with the $TCL_PREFIX default: it will use the 720 1.1 christos # autosetup-defined --prefix default 721 1.1 christos if {"prefix" eq $tclHome} { 722 1.1 christos set tclHome [get-define prefix] 723 1.1 christos } 724 1.1 christos } 725 1.1 christos teaish-debug "use_tcl ${use_tcl}" 726 1.1 christos teaish-debug "withSh=${withSh}" 727 1.1 christos teaish-debug "tclHome=$tclHome" 728 1.1 christos if {"" eq $withSh && "" eq $tclHome} { 729 1.1 christos # If neither --with-tclsh nor --with-tcl are provided, try to find 730 1.1 christos # a workable tclsh. 731 1.1 christos set withSh [proj-first-bin-of tclsh9.1 tclsh9.0 tclsh8.6 tclsh] 732 1.1 christos teaish-debug "withSh=${withSh}" 733 1.1 christos } 734 1.1 christos 735 1.1 christos set doConfigLookup 1 ; # set to 0 to test the tclConfig.sh-not-found cases 736 1.1 christos if {"" ne $withSh} { 737 1.1 christos # --with-tclsh was provided or found above. Validate it and use it 738 1.1 christos # to trump any value passed via --with-tcl=DIR. 739 1.1 christos if {![file-isexec $withSh]} { 740 1.1 christos proj-error "TCL shell $withSh is not executable" 741 1.1 christos } else { 742 1.1 christos define TCLSH_CMD $withSh 743 1.1 christos #msg-result "Using tclsh: $withSh" 744 1.1 christos } 745 1.1 christos if {$doConfigLookup && 746 1.1 christos [catch {exec $withSh $::autosetup(libdir)/find_tclconfig.tcl} result] == 0} { 747 1.1 christos set tclHome $result 748 1.1 christos } 749 1.1 christos if {"" ne $tclHome && [file isdirectory $tclHome]} { 750 1.1 christos teaish__verbose 1 msg-result "$withSh recommends the tclConfig.sh from $tclHome" 751 1.1 christos } else { 752 1.1 christos proj-warn "$withSh is unable to recommend a tclConfig.sh" 753 1.1 christos set use_tcl 0 754 1.1 christos } 755 1.1 christos } 756 1.1 christos set cfg "" 757 1.1 christos set tclSubdirs {tcl9.1 tcl9.0 tcl8.6 tcl8.5 lib} 758 1.1 christos while {$use_tcl} { 759 1.1 christos if {"" ne $tclHome} { 760 1.1 christos # Ensure that we can find tclConfig.sh under ${tclHome}/... 761 1.1 christos if {$doConfigLookup} { 762 1.1 christos if {[file readable "${tclHome}/tclConfig.sh"]} { 763 1.1 christos set cfg "${tclHome}/tclConfig.sh" 764 1.1 christos } else { 765 1.1 christos foreach i $tclSubdirs { 766 1.1 christos if {[file readable "${tclHome}/$i/tclConfig.sh"]} { 767 1.1 christos set cfg "${tclHome}/$i/tclConfig.sh" 768 1.1 christos break 769 1.1 christos } 770 1.1 christos } 771 1.1 christos } 772 1.1 christos } 773 1.1 christos if {"" eq $cfg} { 774 1.1 christos proj-error "No tclConfig.sh found under ${tclHome}" 775 1.1 christos } 776 1.1 christos } else { 777 1.1 christos # If we have not yet found a tclConfig.sh file, look in $libdir 778 1.1 christos # which is set automatically by autosetup or via the --prefix 779 1.1 christos # command-line option. See 780 1.1 christos # https://sqlite.org/forum/forumpost/e04e693439a22457 781 1.1 christos set libdir [get-define libdir] 782 1.1 christos if {[file readable "${libdir}/tclConfig.sh"]} { 783 1.1 christos set cfg "${libdir}/tclConfig.sh" 784 1.1 christos } else { 785 1.1 christos foreach i $tclSubdirs { 786 1.1 christos if {[file readable "${libdir}/$i/tclConfig.sh"]} { 787 1.1 christos set cfg "${libdir}/$i/tclConfig.sh" 788 1.1 christos break 789 1.1 christos } 790 1.1 christos } 791 1.1 christos } 792 1.1 christos if {![file readable $cfg]} { 793 1.1 christos break 794 1.1 christos } 795 1.1 christos } 796 1.1 christos teaish__verbose 1 msg-result "Using tclConfig.sh = $cfg" 797 1.1 christos break 798 1.1 christos }; # while {$use_tcl} 799 1.1 christos define TCL_CONFIG_SH $cfg 800 1.1 christos # Export a subset of tclConfig.sh to the current TCL-space. If $cfg 801 1.1 christos # is an empty string, this emits empty-string entries for the 802 1.1 christos # various options we're interested in. 803 1.1 christos proj-tclConfig-sh-to-autosetup $cfg 804 1.1 christos 805 1.1 christos if {"" eq $withSh && $cfg ne ""} { 806 1.1 christos # We have tclConfig.sh but no tclsh. Attempt to locate a tclsh 807 1.1 christos # based on info from tclConfig.sh. 808 1.1 christos set tclExecPrefix [get-define TCL_EXEC_PREFIX] 809 1.1 christos proj-assert {"" ne $tclExecPrefix} 810 1.1 christos set tryThese [list \ 811 1.1 christos $tclExecPrefix/bin/tclsh[get-define TCL_VERSION] \ 812 1.1 christos $tclExecPrefix/bin/tclsh ] 813 1.1 christos foreach trySh $tryThese { 814 1.1 christos if {[file-isexec $trySh]} { 815 1.1 christos set withSh $trySh 816 1.1 christos break 817 1.1 christos } 818 1.1 christos } 819 1.1 christos if {![file-isexec $withSh]} { 820 1.1 christos proj-warn "Cannot find a usable tclsh (tried: $tryThese)" 821 1.1 christos } 822 1.1 christos } 823 1.1 christos define TCLSH_CMD $withSh 824 1.1 christos if {$use_tcl} { 825 1.1 christos # Set up the TCLLIBDIR 826 1.1 christos set tcllibdir [get-env TCLLIBDIR ""] 827 1.1 christos set extDirName [teaish-pkginfo-get -libDir] 828 1.1 christos if {"" eq $tcllibdir} { 829 1.1 christos # Attempt to extract TCLLIBDIR from TCL's $auto_path 830 1.1 christos if {"" ne $withSh && 831 1.1 christos [catch {exec echo "puts stdout \$auto_path" | "$withSh"} result] == 0} { 832 1.1 christos foreach i $result { 833 1.1 christos if {![string match //zip* $i] && [file isdirectory $i]} { 834 1.1 christos # isdirectory actually passes on //zipfs:/..., but those are 835 1.1 christos # useless for our purposes 836 1.1 christos set tcllibdir $i/$extDirName 837 1.1 christos break 838 1.1 christos } 839 1.1 christos } 840 1.1 christos } else { 841 1.1 christos proj-error "Cannot determine TCLLIBDIR." 842 1.1 christos } 843 1.1 christos } 844 1.1 christos define TCLLIBDIR $tcllibdir 845 1.1 christos }; # find TCLLIBDIR 846 1.1 christos 847 1.1 christos set gotSh [file-isexec $withSh] 848 1.1 christos set tmdir ""; # first tcl::tm::list entry 849 1.1 christos if {$gotSh} { 850 1.1 christos catch { 851 1.1 christos set tmli [exec echo {puts [tcl::tm::list]} | $withSh] 852 1.1 christos # Reminder: this list contains many names of dirs which do not 853 1.1 christos # exist but are legitimate. If we rely only on an is-dir check, 854 1.1 christos # we can end up not finding any of the many candidates. 855 1.1 christos set firstDir "" 856 1.1 christos foreach d $tmli { 857 1.1 christos if {"" eq $firstDir && ![string match //*:* $d]} { 858 1.1 christos # First non-VFS entry, e.g. not //zipfs: 859 1.1 christos set firstDir $d 860 1.1 christos } 861 1.1 christos if {[file isdirectory $d]} { 862 1.1 christos set tmdir $d 863 1.1 christos break 864 1.1 christos } 865 1.1 christos } 866 1.1 christos if {"" eq $tmdir} { 867 1.1 christos set tmdir $firstDir 868 1.1 christos } 869 1.1 christos }; # find tcl::tm path 870 1.1 christos } 871 1.1 christos define TEAISH_TCL_TM_DIR $tmdir 872 1.1 christos 873 1.1 christos # Finally, let's wrap up... 874 1.1 christos if {$gotSh} { 875 1.1 christos teaish__verbose 1 msg-result "Using tclsh = $withSh" 876 1.1 christos if {$cfg ne ""} { 877 1.1 christos define HAVE_TCL 1 878 1.1 christos } else { 879 1.1 christos proj-warn "Found tclsh but no tclConfig.sh." 880 1.1 christos } 881 1.1 christos if {"" eq $tmdir} { 882 1.1 christos proj-warn "Did not find tcl::tm directory." 883 1.1 christos } 884 1.1 christos } 885 1.1 christos show-notices 886 1.1 christos # If TCL is not found: if it was explicitly requested then fail 887 1.1 christos # fatally, else just emit a warning. If we can find the APIs needed 888 1.1 christos # to generate a working JimTCL then that will suffice for build-time 889 1.1 christos # TCL purposes (see: proc sqlite-determine-codegen-tcl). 890 1.1 christos if {!$gotSh} { 891 1.1 christos proj-error "Did not find tclsh" 892 1.1 christos } elseif {"" eq $cfg} { 893 1.1 christos proj-indented-notice -error { 894 1.1 christos Cannot find a usable tclConfig.sh file. Use --with-tcl=DIR to 895 1.1 christos specify a directory near which tclConfig.sh can be found, or 896 1.1 christos --with-tclsh=/path/to/tclsh to allow the tclsh binary to locate 897 1.1 christos its tclConfig.sh, with the caveat that a symlink to tclsh, or 898 1.1 christos wrapper script around it, e.g. ~/bin/tclsh -> 899 1.1 christos $HOME/tcl/9.0/bin/tclsh9.1, may not work because tclsh emits 900 1.1 christos different library paths for the former than the latter. 901 1.1 christos } 902 1.1 christos } 903 1.1 christos msg-result "Using Tcl [get-define TCL_VERSION] from [get-define TCL_PREFIX]." 904 1.1 christos teaish__tcl_platform_quirks 905 1.1 christos }; # teaish__check_tcl 906 1.1 christos 907 1.1 christos # 908 1.1 christos # Perform last-minute platform-specific tweaks to account for quirks. 909 1.1 christos # 910 1.1 christos proc teaish__tcl_platform_quirks {} { 911 1.1 christos define TEAISH_POSTINST_PREREQUIRE "" 912 1.1 christos switch -glob -- [get-define host] { 913 1.1 christos *-haiku { 914 1.1 christos # Haiku's default TCLLIBDIR is "all wrong": it points to a 915 1.1 christos # read-only virtual filesystem mount-point. We bend it back to 916 1.1 christos # fit under $TCL_PACKAGE_PATH here. 917 1.1 christos foreach {k d} { 918 1.1 christos vj TCL_MAJOR_VERSION 919 1.1 christos vn TCL_MINOR_VERSION 920 1.1 christos pp TCL_PACKAGE_PATH 921 1.1 christos ld TCLLIBDIR 922 1.1 christos } { 923 1.1 christos set $k [get-define $d] 924 1.1 christos } 925 1.1 christos if {[string match /packages/* $ld]} { 926 1.1 christos set old $ld 927 1.1 christos set tail [file tail $ld] 928 1.1 christos if {8 == $vj} { 929 1.1 christos set ld "${pp}/tcl${vj}.${vn}/${tail}" 930 1.1 christos } else { 931 1.1 christos proj-assert {9 == $vj} 932 1.1 christos set ld "${pp}/${tail}" 933 1.1 christos } 934 1.1 christos define TCLLIBDIR $ld 935 1.1 christos # [load foo.so], without a directory part, does not work via 936 1.1 christos # automated tests on Haiku (but works when run 937 1.1 christos # manually). Similarly, the post-install [package require ...] 938 1.1 christos # test fails, presumably for a similar reason. We work around 939 1.1 christos # the former in _teaish.tester.tcl.in. We work around the 940 1.1 christos # latter by amending the post-install check's ::auto_path (in 941 1.1 christos # Makefile.in). This code MUST NOT contain any single-quotes. 942 1.1 christos define TEAISH_POSTINST_PREREQUIRE \ 943 1.1 christos [join [list set ::auto_path \ 944 1.1 christos \[ linsert \$::auto_path 0 $ld \] \; \ 945 1.1 christos ]] 946 1.1 christos proj-indented-notice [subst -nocommands -nobackslashes { 947 1.1 christos Haiku users take note: patching target installation dir to match 948 1.1 christos Tcl's home because Haiku's is not writable. 949 1.1 christos 950 1.1 christos Original : $old 951 1.1 christos Substitute: $ld 952 1.1 christos }] 953 1.1 christos } 954 1.1 christos } 955 1.1 christos } 956 1.1 christos }; # teaish__tcl_platform_quirks 957 1.1 christos 958 1.1 christos # 959 1.1 christos # Searches $::argv and/or the build dir and/or the source dir for 960 1.1 christos # teaish.tcl and friends. Fails if it cannot find teaish.tcl or if 961 1.1 christos # there are other irreconcilable problems. If it returns 0 then it did 962 1.1 christos # not find an extension but the --help flag was seen, in which case 963 1.1 christos # that's not an error. 964 1.1 christos # 965 1.1 christos # This does not _load_ the extension, it primarily locates the files 966 1.1 christos # which make up an extension and fills out no small amount of teaish 967 1.1 christos # state related to that. 968 1.1 christos # 969 1.1 christos proc teaish__find_extension {} { 970 1.1 christos proj-assert {!$::teaish__Config(install-mode)} 971 1.1 christos teaish__verbose 1 msg-result "Looking for teaish extension..." 972 1.1 christos 973 1.1 christos # Helper for the foreach loop below. 974 1.1 christos set checkTeaishTcl {{mustHave fid dir} { 975 1.1 christos set f [file join $dir $fid] 976 1.1 christos if {[file readable $f]} { 977 1.1 christos file-normalize $f 978 1.1 christos } elseif {$mustHave} { 979 1.1 christos proj-error "Missing required $dir/$fid" 980 1.1 christos } 981 1.1 christos }} 982 1.1 christos 983 1.1 christos # 984 1.1 christos # We have to handle some flags manually because the extension must 985 1.1 christos # be loaded before [options] is run (so that the extension can 986 1.1 christos # inject its own options). 987 1.1 christos # 988 1.1 christos set dirBld $::autosetup(builddir); # dir we're configuring under 989 1.1 christos set dirSrc $::autosetup(srcdir); # where teaish's configure script lives 990 1.1 christos set extT ""; # teaish.tcl 991 1.1 christos set largv {}; # rewritten $::argv 992 1.1 christos set gotHelpArg 0; # got the --help 993 1.1 christos foreach arg $::argv { 994 1.1 christos #puts "*** arg=$arg" 995 1.1 christos switch -glob -- $arg { 996 1.1 christos --ted=* - 997 1.1 christos --t-e-d=* - 998 1.1 christos --teaish-extension-dir=* { 999 1.1 christos # Ensure that $extD refers to a directory and contains a 1000 1.1 christos # teaish.tcl. 1001 1.1 christos regexp -- {--[^=]+=(.+)} $arg - extD 1002 1.1 christos set extD [file-normalize $extD] 1003 1.1 christos if {![file isdirectory $extD]} { 1004 1.1 christos proj-error "--teaish-extension-dir value is not a directory: $extD" 1005 1.1 christos } 1006 1.1 christos set extT [apply $checkTeaishTcl 0 teaish.config $extD] 1007 1.1 christos if {"" eq $extT} { 1008 1.1 christos set extT [apply $checkTeaishTcl 1 teaish.tcl $extD] 1009 1.1 christos } 1010 1.1 christos set ::teaish__Config(extension-dir) $extD 1011 1.1 christos } 1012 1.1 christos --help { 1013 1.1 christos incr gotHelpArg 1014 1.1 christos lappend largv $arg 1015 1.1 christos } 1016 1.1 christos default { 1017 1.1 christos lappend largv $arg 1018 1.1 christos } 1019 1.1 christos } 1020 1.1 christos } 1021 1.1 christos set ::argv $largv 1022 1.1 christos 1023 1.1 christos set dirExt $::teaish__Config(extension-dir); # dir with the extension 1024 1.1 christos # 1025 1.1 christos # teaish.tcl is a TCL script which implements various 1026 1.1 christos # interfaces described by this framework. 1027 1.1 christos # 1028 1.1 christos # We use the first one we find in the builddir or srcdir. 1029 1.1 christos # 1030 1.1 christos if {"" eq $extT} { 1031 1.1 christos set flist [list] 1032 1.1 christos proj-assert {$dirExt eq ""} 1033 1.1 christos lappend flist $dirBld/teaish.tcl $dirBld/teaish.config $dirSrc/teaish.tcl 1034 1.1 christos if {![proj-first-file-found extT $flist]} { 1035 1.1 christos if {$gotHelpArg} { 1036 1.1 christos # Tell teaish-configure-core that the lack of extension is not 1037 1.1 christos # an error when --help or --teaish-install is used. 1038 1.1 christos return 0; 1039 1.1 christos } 1040 1.1 christos proj-indented-notice -error " 1041 1.1 christos Did not find any of: $flist 1042 1.1 christos 1043 1.1 christos If you are attempting an out-of-tree build, use 1044 1.1 christos --teaish-extension-dir=/path/to/extension" 1045 1.1 christos } 1046 1.1 christos } 1047 1.1 christos if {![file readable $extT]} { 1048 1.1 christos proj-error "extension tcl file is not readable: $extT" 1049 1.1 christos } 1050 1.1 christos set ::teaish__Config(teaish.tcl) $extT 1051 1.1 christos set dirExt [file dirname $extT] 1052 1.1 christos 1053 1.1 christos set ::teaish__Config(extension-dir) $dirExt 1054 1.1 christos set ::teaish__Config(blddir-is-extdir) [expr {$dirBld eq $dirExt}] 1055 1.1 christos set ::teaish__Config(dist-enabled) $::teaish__Config(blddir-is-extdir); # may change later 1056 1.1 christos set ::teaish__Config(dist-full-enabled) \ 1057 1.1 christos [expr {[file-normalize $::autosetup(srcdir)] 1058 1.1 christos eq [file-normalize $::teaish__Config(extension-dir)]}] 1059 1.1 christos 1060 1.1 christos set addDist {{file} { 1061 1.1 christos teaish-dist-add [file tail $file] 1062 1.1 christos }} 1063 1.1 christos apply $addDist $extT 1064 1.1 christos 1065 1.1 christos teaish__verbose 1 msg-result "Extension dir = [teaish-get -dir]" 1066 1.1 christos teaish__verbose 1 msg-result "Extension config = $extT" 1067 1.1 christos 1068 1.1 christos teaish-pkginfo-set -name [file tail [file dirname $extT]] 1069 1.1 christos 1070 1.1 christos # 1071 1.1 christos # teaish.make[.in] provides some of the info for the main makefile, 1072 1.1 christos # like which source(s) to build and their build flags. 1073 1.1 christos # 1074 1.1 christos # We use the first one of teaish.make.in or teaish.make we find in 1075 1.1 christos # $dirExt. 1076 1.1 christos # 1077 1.1 christos if {[proj-first-file-found extM \ 1078 1.1 christos [list \ 1079 1.1 christos $dirExt/teaish.make.in \ 1080 1.1 christos $dirExt/teaish.make \ 1081 1.1 christos ]]} { 1082 1.1 christos if {[string match *.in $extM]} { 1083 1.1 christos define TEAISH_MAKEFILE_IN $extM 1084 1.1 christos define TEAISH_MAKEFILE _[file rootname [file tail $extM]] 1085 1.1 christos } else { 1086 1.1 christos define TEAISH_MAKEFILE_IN "" 1087 1.1 christos define TEAISH_MAKEFILE $extM 1088 1.1 christos } 1089 1.1 christos apply $addDist $extM 1090 1.1 christos teaish__verbose 1 msg-result "Extension makefile = $extM" 1091 1.1 christos } else { 1092 1.1 christos define TEAISH_MAKEFILE_IN "" 1093 1.1 christos define TEAISH_MAKEFILE "" 1094 1.1 christos } 1095 1.1 christos 1096 1.1 christos # Look for teaish.pkginit.tcl[.in] 1097 1.1 christos set piPolicy 0 1098 1.1 christos if {[proj-first-file-found extI \ 1099 1.1 christos [list \ 1100 1.1 christos $dirExt/teaish.pkginit.tcl.in \ 1101 1.1 christos $dirExt/teaish.pkginit.tcl \ 1102 1.1 christos ]]} { 1103 1.1 christos if {[string match *.in $extI]} { 1104 1.1 christos # Generate teaish.pkginit.tcl from $extI. 1105 1.1 christos define TEAISH_PKGINIT_TCL_IN $extI 1106 1.1 christos define TEAISH_PKGINIT_TCL [file rootname [file tail $extI]] 1107 1.1 christos set piPolicy 0x01 1108 1.1 christos } else { 1109 1.1 christos # Assume static $extI. 1110 1.1 christos define TEAISH_PKGINIT_TCL_IN "" 1111 1.1 christos define TEAISH_PKGINIT_TCL $extI 1112 1.1 christos set piPolicy 0x10 1113 1.1 christos } 1114 1.1 christos apply $addDist $extI 1115 1.1 christos teaish__verbose 1 msg-result "Extension post-load init = $extI" 1116 1.1 christos define TEAISH_PKGINIT_TCL_TAIL \ 1117 1.1 christos [file tail [get-define TEAISH_PKGINIT_TCL]]; # for use in pkgIndex.tcl.in 1118 1.1 christos } 1119 1.1 christos set ::teaish__Config(pkginit-policy) $piPolicy 1120 1.1 christos 1121 1.1 christos # Look for pkgIndex.tcl[.in]... 1122 1.1 christos set piPolicy 0 1123 1.1 christos if {[proj-first-file-found extPI $dirExt/pkgIndex.tcl.in]} { 1124 1.1 christos # Generate ./pkgIndex.tcl from $extPI. 1125 1.1 christos define TEAISH_PKGINDEX_TCL_IN $extPI 1126 1.1 christos define TEAISH_PKGINDEX_TCL [file rootname [file tail $extPI]] 1127 1.1 christos apply $addDist $extPI 1128 1.1 christos set piPolicy 0x01 1129 1.1 christos } elseif {$dirExt ne $dirSrc 1130 1.1 christos && [proj-first-file-found extPI $dirSrc/pkgIndex.tcl.in]} { 1131 1.1 christos # Generate ./pkgIndex.tcl from $extPI. 1132 1.1 christos define TEAISH_PKGINDEX_TCL_IN $extPI 1133 1.1 christos define TEAISH_PKGINDEX_TCL [file rootname [file tail $extPI]] 1134 1.1 christos set piPolicy 0x02 1135 1.1 christos } elseif {[proj-first-file-found extPI $dirExt/pkgIndex.tcl]} { 1136 1.1 christos # Assume $extPI's a static file and use it. 1137 1.1 christos define TEAISH_PKGINDEX_TCL_IN "" 1138 1.1 christos define TEAISH_PKGINDEX_TCL $extPI 1139 1.1 christos apply $addDist $extPI 1140 1.1 christos set piPolicy 0x10 1141 1.1 christos } 1142 1.1 christos # Reminder: we have to delay removal of stale TEAISH_PKGINDEX_TCL 1143 1.1 christos # and the proj-dot-ins-append of TEAISH_PKGINDEX_TCL_IN until much 1144 1.1 christos # later in the process. 1145 1.1 christos set ::teaish__Config(pkgindex-policy) $piPolicy 1146 1.1 christos 1147 1.1 christos # Look for teaish.test.tcl[.in] 1148 1.1 christos proj-assert {"" ne $dirExt} 1149 1.1 christos set flist [list $dirExt/teaish.test.tcl.in $dirExt/teaish.test.tcl] 1150 1.1 christos if {[proj-first-file-found ttt $flist]} { 1151 1.1 christos if {[string match *.in $ttt]} { 1152 1.1 christos # Generate _teaish.test.tcl from $ttt 1153 1.1 christos set xt _[file rootname [file tail $ttt]] 1154 1.1 christos file delete -force -- $xt; # ensure no stale copy is used 1155 1.1 christos define TEAISH_TEST_TCL $xt 1156 1.1 christos define TEAISH_TEST_TCL_IN $ttt 1157 1.1 christos } else { 1158 1.1 christos define TEAISH_TEST_TCL $ttt 1159 1.1 christos define TEAISH_TEST_TCL_IN "" 1160 1.1 christos } 1161 1.1 christos apply $addDist $ttt 1162 1.1 christos } else { 1163 1.1 christos define TEAISH_TEST_TCL "" 1164 1.1 christos define TEAISH_TEST_TCL_IN "" 1165 1.1 christos } 1166 1.1 christos 1167 1.1 christos # Look for _teaish.tester.tcl[.in] 1168 1.1 christos set flist [list $dirExt/_teaish.tester.tcl.in $dirSrc/_teaish.tester.tcl.in] 1169 1.1 christos if {[proj-first-file-found ttt $flist]} { 1170 1.1 christos # Generate teaish.test.tcl from $ttt 1171 1.1 christos set xt [file rootname [file tail $ttt]] 1172 1.1 christos file delete -force -- $xt; # ensure no stale copy is used 1173 1.1 christos define TEAISH_TESTER_TCL $xt 1174 1.1 christos define TEAISH_TESTER_TCL_IN $ttt 1175 1.1 christos if {[lindex $flist 0] eq $ttt} { 1176 1.1 christos apply $addDist $ttt 1177 1.1 christos } 1178 1.1 christos unset ttt xt 1179 1.1 christos } else { 1180 1.1 christos if {[file exists [set ttt [file join $dirSrc _teaish.tester.tcl.in]]]} { 1181 1.1 christos set xt [file rootname [file tail $ttt]] 1182 1.1 christos define TEAISH_TESTER_TCL $xt 1183 1.1 christos define TEAISH_TESTER_TCL_IN $ttt 1184 1.1 christos } else { 1185 1.1 christos define TEAISH_TESTER_TCL "" 1186 1.1 christos define TEAISH_TESTER_TCL_IN "" 1187 1.1 christos } 1188 1.1 christos } 1189 1.1 christos unset flist 1190 1.1 christos 1191 1.1 christos # TEAISH_OUT_OF_EXT_TREE = 1 if we're building from a dir other 1192 1.1 christos # than the extension's home dir. 1193 1.1 christos define TEAISH_OUT_OF_EXT_TREE \ 1194 1.1 christos [expr {[file-normalize $::autosetup(builddir)] ne \ 1195 1.1 christos [file-normalize $::teaish__Config(extension-dir)]}] 1196 1.1 christos return 1 1197 1.1 christos }; # teaish__find_extension 1198 1.1 christos 1199 1.1 christos # 1200 1.1 christos # @teaish-cflags-add ?-p|prepend? ?-define? cflags... 1201 1.1 christos # 1202 1.1 christos # Equivalent to [proj-define-amend TEAISH_CFLAGS {*}$args]. 1203 1.1 christos # 1204 1.1 christos proc teaish-cflags-add {args} { 1205 1.1 christos proj-define-amend TEAISH_CFLAGS {*}$args 1206 1.1 christos } 1207 1.1 christos 1208 1.1 christos # 1209 1.1 christos # @teaish-define-to-cflag ?flags? defineName...|{defineName...} 1210 1.1 christos # 1211 1.1 christos # Uses [proj-define-to-cflag] to expand a list of [define] keys, each 1212 1.1 christos # one a separate argument, to CFLAGS-style -D... form then appends 1213 1.1 christos # that to the current TEAISH_CFLAGS. 1214 1.1 christos # 1215 1.1 christos # It accepts these flags from proj-define-to-cflag: -quote, 1216 1.1 christos # -zero-undef. It does _not_ support its -list flag. 1217 1.1 christos # 1218 1.1 christos # It accepts its non-flag argument(s) in 2 forms: (1) each arg is a 1219 1.1 christos # single [define] key or (2) its one arg is a list of such keys. 1220 1.1 christos # 1221 1.1 christos # TODO: document teaish's well-defined (as it were) defines for this 1222 1.1 christos # purpose. At a bare minimum: 1223 1.1 christos # 1224 1.1 christos # - TEAISH_NAME 1225 1.1 christos # - TEAISH_PKGNAME 1226 1.1 christos # - TEAISH_VERSION 1227 1.1 christos # - TEAISH_LIBDIR_NAME 1228 1.1 christos # - TEAISH_LOAD_PREFIX 1229 1.1 christos # - TEAISH_URL 1230 1.1 christos # 1231 1.1 christos proc teaish-define-to-cflag {args} { 1232 1.1 christos set flags {} 1233 1.1 christos while {[string match -* [lindex $args 0]]} { 1234 1.1 christos set arg [lindex $args 0] 1235 1.1 christos switch -exact -- $arg { 1236 1.1 christos -quote - 1237 1.1 christos -zero-undef { 1238 1.1 christos lappend flags $arg 1239 1.1 christos set args [lassign $args -] 1240 1.1 christos } 1241 1.1 christos default break 1242 1.1 christos } 1243 1.1 christos } 1244 1.1 christos if {1 == [llength $args]} { 1245 1.1 christos set args [list {*}[lindex $args 0]] 1246 1.1 christos } 1247 1.1 christos #puts "***** flags=$flags args=$args" 1248 1.1 christos teaish-cflags-add [proj-define-to-cflag {*}$flags {*}$args] 1249 1.1 christos } 1250 1.1 christos 1251 1.1 christos # 1252 1.1 christos # @teaish-cflags-for-tea ?...CFLAGS? 1253 1.1 christos # 1254 1.1 christos # Adds several -DPACKAGE_... CFLAGS using the extension's metadata, 1255 1.1 christos # all as quoted strings. Those symbolic names are commonly used in 1256 1.1 christos # TEA-based builds, and this function is intended to simplify porting 1257 1.1 christos # of such builds. The -D... flags added are: 1258 1.1 christos # 1259 1.1 christos # -DPACKAGE_VERSION=... 1260 1.1 christos # -DPACKAGE_NAME=... 1261 1.1 christos # -DPACKAGE_URL=... 1262 1.1 christos # -DPACKAGE_STRING=... 1263 1.1 christos # 1264 1.1 christos # Any arguments are passed-on as-is to teaish-cflags-add. 1265 1.1 christos # 1266 1.1 christos proc teaish-cflags-for-tea {args} { 1267 1.1 christos set name $::teaish__PkgInfo(-name) 1268 1.1 christos set version $::teaish__PkgInfo(-version) 1269 1.1 christos set pstr [join [list $name $version]] 1270 1.1 christos teaish-cflags-add \ 1271 1.1 christos {*}$args \ 1272 1.1 christos '-DPACKAGE_VERSION="$version"' \ 1273 1.1 christos '-DPACKAGE_NAME="$name"' \ 1274 1.1 christos '-DPACKAGE_STRING="$pstr"' \ 1275 1.1 christos '-DPACKAGE_URL="[teaish-get -url]"' 1276 1.1 christos } 1277 1.1 christos 1278 1.1 christos # 1279 1.1 christos # @teaish-ldflags-add ?-p|-prepend? ?-define? ldflags... 1280 1.1 christos # 1281 1.1 christos # Equivalent to [proj-define-amend TEAISH_LDFLAGS {*}$args]. 1282 1.1 christos # 1283 1.1 christos # Typically, -lXYZ flags need to be in "reverse" order, with each -lY 1284 1.1 christos # resolving symbols for -lX's to its left. This order is largely 1285 1.1 christos # historical, and not relevant on all environments, but it is 1286 1.1 christos # technically correct and still relevant on some environments. 1287 1.1 christos # 1288 1.1 christos # See: teaish-ldflags-prepend 1289 1.1 christos # 1290 1.1 christos proc teaish-ldflags-add {args} { 1291 1.1 christos proj-define-amend TEAISH_LDFLAGS {*}$args 1292 1.1 christos } 1293 1.1 christos 1294 1.1 christos # 1295 1.1 christos # @teaish-ldflags-prepend args... 1296 1.1 christos # 1297 1.1 christos # Functionally equivalent to [teaish-ldflags-add -p {*}$args] 1298 1.1 christos # 1299 1.1 christos proc teaish-ldflags-prepend {args} { 1300 1.1 christos teaish-ldflags-add -p {*}$args 1301 1.1 christos } 1302 1.1 christos 1303 1.1 christos # 1304 1.1 christos # @teaish-src-add ?-dist? ?-dir? src-files... 1305 1.1 christos # 1306 1.1 christos # Appends all non-empty $args to the project's list of C/C++ source or 1307 1.1 christos # (in some cases) object files. 1308 1.1 christos # 1309 1.1 christos # If passed -dist then it also passes each filename, as-is, to 1310 1.1 christos # [teaish-dist-add]. 1311 1.1 christos # 1312 1.1 christos # If passed -dir then each src-file has [teaish-get -dir] prepended to 1313 1.1 christos # it before they're added to the list. As often as not, that will be 1314 1.1 christos # the desired behavior so that out-of-tree builds can find the 1315 1.1 christos # sources, but there are cases where it's not desired (e.g. when using 1316 1.1 christos # a source file from outside of the extension's dir, or when adding 1317 1.1 christos # object files (which are typically in the build tree)). 1318 1.1 christos # 1319 1.1 christos proc teaish-src-add {args} { 1320 1.1 christos proj-parse-simple-flags args flags { 1321 1.1 christos -dist 0 {expr 1} 1322 1.1 christos -dir 0 {expr 1} 1323 1.1 christos } 1324 1.1 christos if {$flags(-dist)} { 1325 1.1 christos teaish-dist-add {*}$args 1326 1.1 christos } 1327 1.1 christos if {$flags(-dir)} { 1328 1.1 christos set xargs {} 1329 1.1 christos foreach arg $args { 1330 1.1 christos if {"" ne $arg} { 1331 1.1 christos lappend xargs [file join $::teaish__Config(extension-dir) $arg] 1332 1.1 christos } 1333 1.1 christos } 1334 1.1 christos set args $xargs 1335 1.1 christos } 1336 1.1 christos lappend ::teaish__Config(extension-src) {*}$args 1337 1.1 christos } 1338 1.1 christos 1339 1.1 christos # 1340 1.1 christos # @teaish-dist-add files-or-dirs... 1341 1.1 christos # 1342 1.1 christos # Adds the given files to the list of files to include with the "make 1343 1.1 christos # dist" rules. 1344 1.1 christos # 1345 1.1 christos # This is a no-op when the current build is not in the extension's 1346 1.1 christos # directory, as dist support is disabled in out-of-tree builds. 1347 1.1 christos # 1348 1.1 christos # It is not legal to call this until [teaish-get -dir] has been 1349 1.1 christos # reliably set (via teaish__find_extension). 1350 1.1 christos # 1351 1.1 christos proc teaish-dist-add {args} { 1352 1.1 christos if {$::teaish__Config(blddir-is-extdir)} { 1353 1.1 christos # ^^^ reminder: we ignore $::teaish__Config(dist-enabled) here 1354 1.1 christos # because the client might want to implement their own dist 1355 1.1 christos # rules. 1356 1.1 christos #proj-warn "**** args=$args" 1357 1.1 christos lappend ::teaish__Config(dist-files) {*}$args 1358 1.1 christos } 1359 1.1 christos } 1360 1.1 christos 1361 1.1 christos # teaish-install-add files... 1362 1.1 christos # Equivalent to [proj-define-apend TEAISH_INSTALL_FILES ...]. 1363 1.1 christos #proc teaish-install-add {args} { 1364 1.1 christos # proj-define-amend TEAISH_INSTALL_FILES {*}$args 1365 1.1 christos #} 1366 1.1 christos 1367 1.1 christos # 1368 1.1 christos # @teash-make-add args... 1369 1.1 christos # 1370 1.1 christos # Appends makefile code to the TEAISH_MAKEFILE_CODE define. Each 1371 1.1 christos # arg may be any of: 1372 1.1 christos # 1373 1.1 christos # -tab: emit a literal tab 1374 1.1 christos # -nl: emit a literal newline 1375 1.1 christos # -nltab: short for -nl -tab 1376 1.1 christos # -bnl: emit a backslash-escaped end-of-line 1377 1.1 christos # -bnltab: short for -eol -tab 1378 1.1 christos # 1379 1.1 christos # Anything else is appended verbatim. This function adds no additional 1380 1.1 christos # spacing between each argument nor between subsequent invocations. 1381 1.1 christos # Generally speaking, a series of calls to this function need to 1382 1.1 christos # be sure to end the series with a newline. 1383 1.1 christos proc teaish-make-add {args} { 1384 1.1 christos set out [get-define TEAISH_MAKEFILE_CODE ""] 1385 1.1 christos foreach a $args { 1386 1.1 christos switch -exact -- $a { 1387 1.1 christos -bnl { set a " \\\n" } 1388 1.1 christos -bnltab { set a " \\\n\t" } 1389 1.1 christos -tab { set a "\t" } 1390 1.1 christos -nl { set a "\n" } 1391 1.1 christos -nltab { set a "\n\t" } 1392 1.1 christos } 1393 1.1 christos append out $a 1394 1.1 christos } 1395 1.1 christos define TEAISH_MAKEFILE_CODE $out 1396 1.1 christos } 1397 1.1 christos 1398 1.1 christos # Internal helper to generate a clean/distclean rule name 1399 1.1 christos proc teaish__cleanup_rule {{tgt clean}} { 1400 1.1 christos set x [incr ::teaish__Config(teaish__cleanup_rule-counter-${tgt})] 1401 1.1 christos return ${tgt}-_${x}_ 1402 1.1 christos } 1403 1.1 christos 1404 1.1 christos # @teaish-make-obj ?flags? ?...args? 1405 1.1 christos # 1406 1.1 christos # Uses teaish-make-add to inject makefile rules for $objfile from 1407 1.1 christos # $srcfile, which is assumed to be C code which uses libtcl. Unless 1408 1.1 christos # -recipe is used (see below) it invokes the compiler using the 1409 1.1 christos # makefile-defined $(CC.tcl) which, in the default Makefile.in 1410 1.1 christos # template, includes any flags needed for building against the 1411 1.1 christos # configured Tcl. 1412 1.1 christos # 1413 1.1 christos # This always terminates the resulting code with a newline. 1414 1.1 christos # 1415 1.1 christos # Any arguments after the 2nd may be flags described below or, if no 1416 1.1 christos # -recipe is provided, flags for the compiler call. 1417 1.1 christos # 1418 1.1 christos # -obj obj-filename.o 1419 1.1 christos # 1420 1.1 christos # -src src-filename.c 1421 1.1 christos # 1422 1.1 christos # -recipe {...} 1423 1.1 christos # Uses the trimmed value of {...} as the recipe, prefixing it with 1424 1.1 christos # a single hard-tab character. 1425 1.1 christos # 1426 1.1 christos # -deps {...} 1427 1.1 christos # List of extra files to list as dependencies of $o. 1428 1.1 christos # 1429 1.1 christos # -clean 1430 1.1 christos # Generate cleanup rules as well. 1431 1.1 christos proc teaish-make-obj {args} { 1432 1.1 christos proj-parse-simple-flags args flags { 1433 1.1 christos -clean 0 {expr 1} 1434 1.1 christos -recipe => {} 1435 1.1 christos -deps => {} 1436 1.1 christos -obj => {} 1437 1.1 christos -src => {} 1438 1.1 christos } 1439 1.1 christos #parray flags 1440 1.1 christos if {"" eq $flags(-obj)} { 1441 1.1 christos set args [lassign $args flags(-obj)] 1442 1.1 christos if {"" eq $flags(-obj)} { 1443 1.1 christos proj-error "Missing -obj flag." 1444 1.1 christos } 1445 1.1 christos } 1446 1.1 christos foreach f {-deps -src} { 1447 1.1 christos set flags($f) [string trim [string map {\n " "} $flags($f)]] 1448 1.1 christos } 1449 1.1 christos foreach f {-deps -src} { 1450 1.1 christos set flags($f) [string trim $flags($f)] 1451 1.1 christos } 1452 1.1 christos #parray flags 1453 1.1 christos #puts "-- args=$args" 1454 1.1 christos teaish-make-add \ 1455 1.1 christos "# [proj-scope 1] -> [proj-scope] $flags(-obj) $flags(-src)" -nl \ 1456 1.1 christos "$flags(-obj): $flags(-src) $::teaish__Config(teaish.tcl)" 1457 1.1 christos if {[info exists flags(-deps)]} { 1458 1.1 christos teaish-make-add " " [join $flags(-deps)] 1459 1.1 christos } 1460 1.1 christos teaish-make-add -nltab 1461 1.1 christos if {[info exists flags(-recipe)]} { 1462 1.1 christos teaish-make-add [string trim $flags(-recipe)] -nl 1463 1.1 christos } else { 1464 1.1 christos teaish-make-add [join [list \$(CC.tcl) -c $flags(-src) {*}$args]] -nl 1465 1.1 christos } 1466 1.1 christos if {$flags(-clean)} { 1467 1.1 christos set rule [teaish__cleanup_rule] 1468 1.1 christos teaish-make-add \ 1469 1.1 christos "clean: $rule\n$rule:\n\trm -f \"$flags(-obj)\"\n" 1470 1.1 christos } 1471 1.1 christos } 1472 1.1 christos 1473 1.1 christos # 1474 1.1 christos # @teaish-make-clean ?-r? ?-dist? ...files|{...files} 1475 1.1 christos # 1476 1.1 christos # Adds makefile rules for cleaning up the given files via the "make 1477 1.1 christos # clean" or (if -dist is used) "make distclean" makefile rules. The -r 1478 1.1 christos # flag uses "rm -fr" instead of "rm -f", so be careful with that. 1479 1.1 christos # 1480 1.1 christos # The file names are taken literally as arguments to "rm", so they may 1481 1.1 christos # be shell wildcards to be resolved at cleanup-time. To clean up whole 1482 1.1 christos # directories, pass the -r flag. Each name gets quoted in 1483 1.1 christos # double-quotes, so spaces in names should not be a problem (but 1484 1.1 christos # double-quotes in names will be). 1485 1.1 christos # 1486 1.1 christos proc teaish-make-clean {args} { 1487 1.1 christos if {1 == [llength $args]} { 1488 1.1 christos set args [list {*}[lindex $args 0]] 1489 1.1 christos } 1490 1.1 christos 1491 1.1 christos set tgt clean 1492 1.1 christos set rmflags "-f" 1493 1.1 christos proj-parse-simple-flags args flags { 1494 1.1 christos -dist 0 { 1495 1.1 christos set tgt distclean 1496 1.1 christos } 1497 1.1 christos -r 0 { 1498 1.1 christos set rmflags "-fr" 1499 1.1 christos } 1500 1.1 christos } 1501 1.1 christos set rule [teaish__cleanup_rule $tgt] 1502 1.1 christos teaish-make-add "# [proj-scope 1] -> [proj-scope]: [join $args]\n" 1503 1.1 christos teaish-make-add "${rule}:\n\trm ${rmflags}" 1504 1.1 christos foreach a $args { 1505 1.1 christos teaish-make-add " \"$a\"" 1506 1.1 christos } 1507 1.1 christos teaish-make-add "\n${tgt}: ${rule}\n" 1508 1.1 christos } 1509 1.1 christos 1510 1.1 christos # 1511 1.1 christos # @teaish-make-config-header filename 1512 1.1 christos # 1513 1.1 christos # Invokes autosetup's [make-config-header] and passes it $filename and 1514 1.1 christos # a relatively generic list of options for controlling which defined 1515 1.1 christos # symbols get exported. Clients which need more control over the 1516 1.1 christos # exports can copy/paste/customize this. 1517 1.1 christos # 1518 1.1 christos # The exported file is then passed to [proj-touch] because, in 1519 1.1 christos # practice, that's sometimes necessary to avoid build dependency 1520 1.1 christos # issues. 1521 1.1 christos # 1522 1.1 christos proc teaish-make-config-header {filename} { 1523 1.1 christos make-config-header $filename \ 1524 1.1 christos -none {HAVE_CFLAG_* LDFLAGS_* SH_* TEAISH__* TEAISH_*_CODE} \ 1525 1.1 christos -auto {SIZEOF_* HAVE_* TEAISH_* TCL_*} \ 1526 1.1 christos -none * 1527 1.1 christos proj-touch $filename; # help avoid frequent unnecessary auto-reconfig 1528 1.1 christos } 1529 1.1 christos 1530 1.1 christos # 1531 1.1 christos # @teaish-feature-cache-set $key value 1532 1.1 christos # 1533 1.1 christos # Sets a feature-check cache entry with the given key. 1534 1.1 christos # See proj-cache-set for the key's semantics. $key should 1535 1.1 christos # normally be 0. 1536 1.1 christos # 1537 1.1 christos proc teaish-feature-cache-set {key val} { 1538 1.1 christos proj-cache-set -key $key -level 1 $val 1539 1.1 christos } 1540 1.1 christos 1541 1.1 christos # 1542 1.1 christos # @teaish-feature-cache-check key tgtVarName 1543 1.1 christos # 1544 1.1 christos # Checks for a feature-check cache entry with the given key. 1545 1.1 christos # See proj-cache-set for the key's semantics. 1546 1.1 christos # 1547 1.1 christos # $key should also almost always be 0 but, due to a tclsh 1548 1.1 christos # incompatibility in 1 OS, it cannot have a default value unless it's 1549 1.1 christos # the second argument (but it should be the first one). 1550 1.1 christos # 1551 1.1 christos # If the feature-check cache has a matching entry then this function 1552 1.1 christos # assigns its value to tgtVar and returns 1, else it assigns tgtVar to 1553 1.1 christos # "" and returns 0. 1554 1.1 christos # 1555 1.1 christos # See proj-cache-check for $key's semantics. 1556 1.1 christos # 1557 1.1 christos proc teaish-feature-cache-check {key tgtVar} { 1558 1.1 christos upvar $tgtVar tgt 1559 1.1 christos proj-cache-check -key $key -level 1 tgt 1560 1.1 christos } 1561 1.1 christos 1562 1.1 christos # 1563 1.1 christos # @teaish-check-cached@ ?flags? msg script... 1564 1.1 christos # 1565 1.1 christos # A proxy for feature-test impls which handles caching of a feature 1566 1.1 christos # flag check on per-function basis, using the calling scope's name as 1567 1.1 christos # the cache key. 1568 1.1 christos # 1569 1.1 christos # It emits [msg-checking $msg]. If $msg is empty then it defaults to 1570 1.1 christos # the name of the caller's scope. The -nomsg flag suppresses the 1571 1.1 christos # message for non-cache-hit checks. At the end, it will [msg-result 1572 1.1 christos # "ok"] [msg-result "no"] unless -nostatus is used, in which case the 1573 1.1 christos # caller is responsible for emitting at least a newline when it's 1574 1.1 christos # done. The -msg-0 and -msg-1 flags can be used to change the ok/no 1575 1.1 christos # text. 1576 1.1 christos # 1577 1.1 christos # This function checks for a cache hit before running $script and 1578 1.1 christos # caching the result. If no hit is found then $script is run in the 1579 1.1 christos # calling scope and its result value is stored in the cache. This 1580 1.1 christos # routine will intercept a 'return' from $script. 1581 1.1 christos # 1582 1.1 christos # $script may be a command and its arguments, as opposed to a single 1583 1.1 christos # script block. 1584 1.1 christos # 1585 1.1 christos # Flags: 1586 1.1 christos # 1587 1.1 christos # -nostatus = do not emit "ok" or "no" at the end. This presumes 1588 1.1 christos # that either $script will emit at least one newline before 1589 1.1 christos # returning or the caller will account for it. Because of how this 1590 1.1 christos # function is typically used, -nostatus is not honored when the 1591 1.1 christos # response includes a cached result. 1592 1.1 christos # 1593 1.1 christos # -quiet = disable output from Autosetup's msg-checking and 1594 1.1 christos # msg-result for the duration of the $script check. Note that when 1595 1.1 christos # -quiet is in effect, Autosetup's user-notice can be used to queue 1596 1.1 christos # up output to appear after the check is done. Also note that 1597 1.1 christos # -quiet has no effect on _this_ function, only the $script part. 1598 1.1 christos # 1599 1.1 christos # -nomsg = do not emit $msg for initial check. Like -nostatus, this 1600 1.1 christos # flag is not honored when the response includes a cached result 1601 1.1 christos # because it would otherwise produce no output (which is confusing 1602 1.1 christos # in this context). This is useful when a check runs several other 1603 1.1 christos # verbose checks and they emit all the necessary info. 1604 1.1 christos # 1605 1.1 christos # -msg-0 and -msg-1 MSG = strings to show when the check has failed 1606 1.1 christos # resp. passed. Defaults are "no" and "ok". The 0 and 1 refer to the 1607 1.1 christos # result value from teaish-feature-cache-check. 1608 1.1 christos # 1609 1.1 christos # -key cachekey = set the cache context key. Only needs to be 1610 1.1 christos # explicit when using this function multiple times from a single 1611 1.1 christos # scope. See proj-cache-check and friends for details on the key 1612 1.1 christos # name. Its default is the name of the scope which calls this 1613 1.1 christos # function. 1614 1.1 christos # 1615 1.1 christos proc teaish-check-cached {args} { 1616 1.1 christos proj-parse-simple-flags args flags { 1617 1.1 christos -nostatus 0 {expr 1} 1618 1.1 christos -quiet 0 {expr 1} 1619 1.1 christos -key => 1 1620 1.1 christos -nomsg 0 {expr 1} 1621 1.1 christos -msg-0 => no 1622 1.1 christos -msg-1 => ok 1623 1.1 christos } 1624 1.1 christos set args [lassign $args msg] 1625 1.1 christos set script [join $args] 1626 1.1 christos if {"" eq $msg} { 1627 1.1 christos set msg [proj-scope 1] 1628 1.1 christos } 1629 1.1 christos if {[teaish-feature-cache-check $flags(-key) check]} { 1630 1.1 christos #if {0 == $flags(-nomsg)} { 1631 1.1 christos msg-checking "${msg} ... (cached) " 1632 1.1 christos #} 1633 1.1 christos #if {!$flags(-nostatus)} { 1634 1.1 christos msg-result $flags(-msg-[expr {0 != ${check}}]) 1635 1.1 christos #} 1636 1.1 christos return $check 1637 1.1 christos } else { 1638 1.1 christos if {0 == $flags(-nomsg)} { 1639 1.1 christos msg-checking "${msg} ... " 1640 1.1 christos } 1641 1.1 christos if {$flags(-quiet)} { 1642 1.1 christos incr ::autosetup(msg-quiet) 1643 1.1 christos } 1644 1.1 christos set code [catch {uplevel 1 $script} rc xopt] 1645 1.1 christos if {$flags(-quiet)} { 1646 1.1 christos incr ::autosetup(msg-quiet) -1 1647 1.1 christos } 1648 1.1 christos #puts "***** cached-check got code=$code rc=$rc" 1649 1.1 christos if {$code in {0 2}} { 1650 1.1 christos teaish-feature-cache-set 1 $rc 1651 1.1 christos if {!$flags(-nostatus)} { 1652 1.1 christos msg-result $flags(-msg-[expr {0 != ${rc}}]) 1653 1.1 christos } else { 1654 1.1 christos #show-notices; # causes a phantom newline because we're in a 1655 1.1 christos #msg-checking scope, so... 1656 1.1 christos if {[info exists ::autosetup(notices)]} { 1657 1.1 christos show-notices 1658 1.1 christos } 1659 1.1 christos } 1660 1.1 christos } else { 1661 1.1 christos #puts "**** code=$code rc=$rc xopt=$xopt" 1662 1.1 christos teaish-feature-cache-set 1 0 1663 1.1 christos } 1664 1.1 christos #puts "**** code=$code rc=$rc" 1665 1.1 christos return {*}$xopt $rc 1666 1.1 christos } 1667 1.1 christos } 1668 1.1 christos 1669 1.1 christos # 1670 1.1 christos # Internal helper for teaish__defs_format_: returns a JSON-ish quoted 1671 1.1 christos # form of the given string-type values. 1672 1.1 christos # 1673 1.1 christos # If $asList is true then the return value is in {$value} form. If 1674 1.1 christos # $asList is false it only performs the most basic of escaping and 1675 1.1 christos # the input must not contain any control characters. 1676 1.1 christos # 1677 1.1 christos proc teaish__quote_str {asList value} { 1678 1.1 christos if {$asList} { 1679 1.1 christos return "{${value}}" 1680 1.1 christos } 1681 1.1 christos return \"[string map [list \\ \\\\ \" \\\"] $value]\" 1682 1.1 christos } 1683 1.1 christos 1684 1.1 christos # 1685 1.1 christos # Internal helper for teaish__defines_to_list. Expects to be passed 1686 1.1 christos # a name and the variadic $args which are passed to 1687 1.1 christos # teaish__defines_to_list.. If it finds a pattern match for the 1688 1.1 christos # given $name in the various $args, it returns the type flag for that 1689 1.1 christos # $name, e.g. "-str" or "-bare", else returns an empty string. 1690 1.1 christos # 1691 1.1 christos proc teaish__defs_type {name spec} { 1692 1.1 christos foreach {type patterns} $spec { 1693 1.1 christos foreach pattern $patterns { 1694 1.1 christos if {[string match $pattern $name]} { 1695 1.1 christos return $type 1696 1.1 christos } 1697 1.1 christos } 1698 1.1 christos } 1699 1.1 christos return "" 1700 1.1 christos } 1701 1.1 christos 1702 1.1 christos # 1703 1.1 christos # An internal impl detail. Requires a data type specifier, as used by 1704 1.1 christos # Autosetup's [make-config-header], and a value. Returns the formatted 1705 1.1 christos # value or the value $::teaish__Config(defs-skip) if the caller should 1706 1.1 christos # skip emitting that value. 1707 1.1 christos # 1708 1.1 christos # In addition to -str, -auto, etc., as defined by make-config-header, 1709 1.1 christos # it supports: 1710 1.1 christos # 1711 1.1 christos # -list {...} will cause non-integer values to be quoted in {...} 1712 1.1 christos # instead of quotes. 1713 1.1 christos # 1714 1.1 christos # -autolist {...} works like -auto {...} except that it falls back to 1715 1.1 christos # -list {...} type instead of -str {...} style for non-integers. 1716 1.1 christos # 1717 1.1 christos # -jsarray {...} emits the output in something which, for 1718 1.1 christos # conservative inputs, will be a valid JSON array. It can only 1719 1.1 christos # handle relatively simple values with no control characters in 1720 1.1 christos # them. 1721 1.1 christos # 1722 1.1 christos set teaish__Config(defs-skip) "-teaish__defs_format sentinel" 1723 1.1 christos proc teaish__defs_format {type value} { 1724 1.1 christos switch -exact -- $type { 1725 1.1 christos -bare { 1726 1.1 christos # Just output the value unchanged 1727 1.1 christos } 1728 1.1 christos -none { 1729 1.1 christos set value $::teaish__Config(defs-skip) 1730 1.1 christos } 1731 1.1 christos -str { 1732 1.1 christos set value [teaish__quote_str 0 $value] 1733 1.1 christos } 1734 1.1 christos -auto { 1735 1.1 christos # Automatically determine the type 1736 1.1 christos if {![string is integer -strict $value]} { 1737 1.1 christos set value [teaish__quote_str 0 $value] 1738 1.1 christos } 1739 1.1 christos } 1740 1.1 christos -autolist { 1741 1.1 christos if {![string is integer -strict $value]} { 1742 1.1 christos set value [teaish__quote_str 1 $value] 1743 1.1 christos } 1744 1.1 christos } 1745 1.1 christos -list { 1746 1.1 christos set value [teaish__quote_str 1 $value] 1747 1.1 christos } 1748 1.1 christos -jsarray { 1749 1.1 christos set ar {} 1750 1.1 christos foreach v $value { 1751 1.1 christos if {![string is integer -strict $v]} { 1752 1.1 christos set v [teaish__quote_str 0 $v] 1753 1.1 christos } 1754 1.1 christos if {$::teaish__Config(defs-skip) ne $v} { 1755 1.1 christos lappend ar $v 1756 1.1 christos } 1757 1.1 christos } 1758 1.1 christos set value [concat \[ [join $ar {, }] \]] 1759 1.1 christos } 1760 1.1 christos "" { 1761 1.1 christos # (Much later:) Why do we do this? 1762 1.1 christos set value $::teaish__Config(defs-skip) 1763 1.1 christos } 1764 1.1 christos default { 1765 1.1 christos proj-error \ 1766 1.1 christos "Unknown [proj-scope] -type ($type) called from" \ 1767 1.1 christos [proj-scope 1] 1768 1.1 christos } 1769 1.1 christos } 1770 1.1 christos return $value 1771 1.1 christos } 1772 1.1 christos 1773 1.1 christos # 1774 1.1 christos # Returns Tcl code in the form of code which evaluates to a list of 1775 1.1 christos # configure-time DEFINEs in the form {key val key2 val...}. It may 1776 1.1 christos # misbehave for values which are not numeric or simple strings. Some 1777 1.1 christos # defines are specifically filtered out of the result, either because 1778 1.1 christos # their irrelevant to teaish or because they may be arbitrarily large 1779 1.1 christos # (e.g. makefile content). 1780 1.1 christos # 1781 1.1 christos # The $args are explained in the docs for internal-use-only 1782 1.1 christos # [teaish__defs_format]. The default mode is -autolist. 1783 1.1 christos # 1784 1.1 christos proc teaish__defines_to_list {args} { 1785 1.1 christos set lines {} 1786 1.1 christos lappend lines "\{" 1787 1.1 christos set skipper $::teaish__Config(defs-skip) 1788 1.1 christos set args [list \ 1789 1.1 christos -none { 1790 1.1 christos TEAISH__* 1791 1.1 christos TEAISH_*_CODE 1792 1.1 christos AM_* AS_* 1793 1.1 christos } \ 1794 1.1 christos {*}$args \ 1795 1.1 christos -autolist *] 1796 1.1 christos foreach d [lsort [dict keys [all-defines]]] { 1797 1.1 christos set type [teaish__defs_type $d $args] 1798 1.1 christos set value [teaish__defs_format $type [get-define $d]] 1799 1.1 christos if {$skipper ne $value} { 1800 1.1 christos lappend lines "$d $value" 1801 1.1 christos } 1802 1.1 christos } 1803 1.1 christos lappend lines "\}" 1804 1.1 christos tailcall join $lines "\n" 1805 1.1 christos } 1806 1.1 christos 1807 1.1 christos # 1808 1.1 christos # teaish__pragma ...flags 1809 1.1 christos # 1810 1.1 christos # Offers a way to tweak how teaish's core behaves in some cases, in 1811 1.1 christos # particular those which require changing how the core looks for an 1812 1.1 christos # extension and its files. 1813 1.1 christos # 1814 1.1 christos # Accepts the following flags. Those marked with [L] are safe to use 1815 1.1 christos # during initial loading of tclish.tcl (recall that most teaish APIs 1816 1.1 christos # cannot be used until [teaish-configure] is called). 1817 1.1 christos # 1818 1.1 christos # static-pkgIndex.tcl [L]: Tells teaish that ./pkgIndex.tcl is not 1819 1.1 christos # a generated file, so it will not try to overwrite or delete 1820 1.1 christos # it. Errors out if it does not find pkgIndex.tcl in the 1821 1.1 christos # extension's dir. 1822 1.1 christos # 1823 1.1 christos # no-dist [L]: tells teaish to elide the 'make dist' recipe 1824 1.1 christos # from the generated Makefile. 1825 1.1 christos # 1826 1.1 christos # no-dll [L]: tells teaish to elide the DLL-building recipe 1827 1.1 christos # from the generated Makefile. 1828 1.1 christos # 1829 1.1 christos # no-vsatisfies-error [L]: tells teaish that failure to match the 1830 1.1 christos # -vsatisfies value should simply "return" instead of "error". 1831 1.1 christos # 1832 1.1 christos # no-tester [L]: disables automatic generation of teaish.test.tcl 1833 1.1 christos # even if a copy of _teaish.tester.tcl.in is found. 1834 1.1 christos # 1835 1.1 christos # no-full-dist [L]: changes the "make dist" rules to never include 1836 1.1 christos # a copy of teaish itself. By default it will include itself only 1837 1.1 christos # if the extension lives in the same directory as teaish. 1838 1.1 christos # 1839 1.1 christos # full-dist [L]: changes the "make dist" rules to always include 1840 1.1 christos # a copy of teaish itself. 1841 1.1 christos # 1842 1.1 christos # Emits a warning message for unknown arguments. 1843 1.1 christos # 1844 1.1 christos proc teaish__pragma {args} { 1845 1.1 christos foreach arg $args { 1846 1.1 christos switch -exact -- $arg { 1847 1.1 christos 1848 1.1 christos static-pkgIndex.tcl { 1849 1.1 christos if {$::teaish__Config(tm-policy)} { 1850 1.1 christos proj-fatal -up "Cannot use pragma $arg together with -tm.tcl or -tm.tcl.in." 1851 1.1 christos } 1852 1.1 christos set tpi [file join $::teaish__Config(extension-dir) pkgIndex.tcl] 1853 1.1 christos if {[file exists $tpi]} { 1854 1.1 christos define TEAISH_PKGINDEX_TCL_IN "" 1855 1.1 christos define TEAISH_PKGINDEX_TCL $tpi 1856 1.1 christos set ::teaish__Config(pkgindex-policy) 0x20 1857 1.1 christos } else { 1858 1.1 christos proj-error "pragma $arg: found no package-local pkgIndex.tcl\[.in]" 1859 1.1 christos } 1860 1.1 christos } 1861 1.1 christos 1862 1.1 christos no-dist { 1863 1.1 christos set ::teaish__Config(dist-enabled) 0 1864 1.1 christos } 1865 1.1 christos 1866 1.1 christos no-install { 1867 1.1 christos set ::teaish__Config(install-enabled) 0 1868 1.1 christos } 1869 1.1 christos 1870 1.1 christos full-dist { 1871 1.1 christos set ::teaish__Config(dist-full-enabled) 1 1872 1.1 christos } 1873 1.1 christos 1874 1.1 christos no-full-dist { 1875 1.1 christos set ::teaish__Config(dist-full-enabled) 0 1876 1.1 christos } 1877 1.1 christos 1878 1.1 christos no-dll { 1879 1.1 christos set ::teaish__Config(dll-enabled) 0 1880 1.1 christos } 1881 1.1 christos 1882 1.1 christos no-vsatisfies-error { 1883 1.1 christos set ::teaish__Config(vsatisfies-error) 0 1884 1.1 christos } 1885 1.1 christos 1886 1.1 christos no-tester { 1887 1.1 christos define TEAISH_TESTER_TCL_IN "" 1888 1.1 christos define TEAISH_TESTER_TCL "" 1889 1.1 christos } 1890 1.1 christos 1891 1.1 christos default { 1892 1.1 christos proj-error "Unknown flag: $arg" 1893 1.1 christos } 1894 1.1 christos } 1895 1.1 christos } 1896 1.1 christos } 1897 1.1 christos 1898 1.1 christos # 1899 1.1 christos # @teaish-pkginfo-set ...flags 1900 1.1 christos # 1901 1.1 christos # The way to set up the initial package state. Used like: 1902 1.1 christos # 1903 1.1 christos # teaish-pkginfo-set -name foo -version 0.1.2 1904 1.1 christos # 1905 1.1 christos # Or: 1906 1.1 christos # 1907 1.1 christos # teaish-pkginfo-set ?-vars|-subst? {-name foo -version 0.1.2} 1908 1.1 christos # 1909 1.1 christos # The latter may be easier to write for a multi-line invocation. 1910 1.1 christos # 1911 1.1 christos # For the second call form, passing the -vars flag tells it to perform 1912 1.1 christos # a [subst] of (only) variables in the {...} part from the calling 1913 1.1 christos # scope. The -subst flag will cause it to [subst] the {...} with 1914 1.1 christos # command substitution as well (but no backslash substitution). When 1915 1.1 christos # using -subst for string concatenation, e.g. with -libDir 1916 1.1 christos # foo[get-version-number], be sure to wrap the value in braces: 1917 1.1 christos # -libDir {foo[get-version-number]}. 1918 1.1 christos # 1919 1.1 christos # Each pkginfo flag corresponds to one piece of extension package 1920 1.1 christos # info. Teaish provides usable default values for all of these flags, 1921 1.1 christos # but at least the -name and -version should be set by clients. 1922 1.1 christos # e.g. the default -name is the directory name the extension lives in, 1923 1.1 christos # which may change (e.g. when building it from a "make dist" bundle). 1924 1.1 christos # 1925 1.1 christos # The flags: 1926 1.1 christos # 1927 1.1 christos # -name theName: The extension's name. It defaults to the name of the 1928 1.1 christos # directory containing the extension. (In TEA this would be the 1929 1.1 christos # PACKAGE_NAME, not to be confused with...) 1930 1.1 christos # 1931 1.1 christos # -name.pkg pkg-provide-name: The extension's name for purposes of 1932 1.1 christos # Tcl_PkgProvide(), [package require], and friends. It defaults to 1933 1.1 christos # the `-name`, and is normally the same, but some projects (like 1934 1.1 christos # SQLite) have a different name here than they do in their 1935 1.1 christos # historical TEA PACKAGE_NAME. 1936 1.1 christos # 1937 1.1 christos # -version version: The extension's package version. Defaults to 1938 1.1 christos # 0.0.0. 1939 1.1 christos # 1940 1.1 christos # -libDir dirName: The base name of the directory into which this 1941 1.1 christos # extension should be installed. It defaults to a concatenation of 1942 1.1 christos # `-name.pkg` and `-version`. 1943 1.1 christos # 1944 1.1 christos # -loadPrefix prefix: For use as the second argument passed to 1945 1.1 christos # Tcl's `load` command in the package-loading process. It defaults 1946 1.1 christos # to title-cased `-name.pkg` because Tcl's `load` plugin system 1947 1.1 christos # expects it in that form. 1948 1.1 christos # 1949 1.1 christos # -options {...}: If provided, it must be a list compatible with 1950 1.1 christos # Autosetup's `options-add` function. These can also be set up via 1951 1.1 christos # `teaish-options`. 1952 1.1 christos # 1953 1.1 christos # -vsatisfies {{...} ...}: Expects a list-of-lists of conditions 1954 1.1 christos # for Tcl's `package vsatisfies` command: each list entry is a 1955 1.1 christos # sub-list of `{PkgName Condition...}`. Teaish inserts those 1956 1.1 christos # checks via its default pkgIndex.tcl.in and _teaish.tester.tcl.in 1957 1.1 christos # templates to verify that the system's package dependencies meet 1958 1.1 christos # these requirements. The default value is `{{Tcl 8.5-}}` (recall 1959 1.1 christos # that it's a list-of-lists), as 8.5 is the minimum Tcl version 1960 1.1 christos # teaish will run on, but some extensions may require newer 1961 1.1 christos # versions or dependencies on other packages. As a special case, 1962 1.1 christos # if `-vsatisfies` is given a single token, e.g. `8.6-`, then it 1963 1.1 christos # is transformed into `{Tcl $thatToken}`, i.e. it checks the Tcl 1964 1.1 christos # version which the package is being run with. If given multiple 1965 1.1 christos # lists, each `package provides` check is run in the given 1966 1.1 christos # order. Failure to meet a `vsatisfies` condition triggers an 1967 1.1 christos # error. 1968 1.1 christos # 1969 1.1 christos # -url {...}: an optional URL for the extension. 1970 1.1 christos # 1971 1.1 christos # -pragmas {...} A list of infrequently-needed lower-level 1972 1.1 christos # directives which can influence teaish, including: 1973 1.1 christos # 1974 1.1 christos # static-pkgIndex.tcl: tells teaish that the client manages their 1975 1.1 christos # own pkgIndex.tcl, so that teaish won't try to overwrite it 1976 1.1 christos # using a template. 1977 1.1 christos # 1978 1.1 christos # no-dist: tells teaish to elide the "make dist" recipe from the 1979 1.1 christos # makefile so that the client can implement it. 1980 1.1 christos # 1981 1.1 christos # no-dll: tells teaish to elide the makefile rules which build 1982 1.1 christos # the DLL, as well as any templated test script and pkgIndex.tcl 1983 1.1 christos # references to them. The intent here is to (A) support 1984 1.1 christos # client-defined build rules for the DLL and (B) eventually 1985 1.1 christos # support script-only extensions. 1986 1.1 christos # 1987 1.1 christos # Unsupported flags or pragmas will trigger an error. 1988 1.1 christos # 1989 1.1 christos # Potential pothole: setting certain state, e.g. -version, after the 1990 1.1 christos # initial call requires recalculating of some [define]s. Any such 1991 1.1 christos # changes should be made as early as possible in teaish-configure so 1992 1.1 christos # that any later use of those [define]s gets recorded properly (not 1993 1.1 christos # with the old value). This is particularly relevant when it is not 1994 1.1 christos # possible to determine the -version or -name until teaish-configure 1995 1.1 christos # has been called, and it's updated dynamically from 1996 1.1 christos # teaish-configure. Notably: 1997 1.1 christos # 1998 1.1 christos # - If -version or -name are updated, -libDir will almost certainly 1999 1.1 christos # need to be explicitly set along with them. 2000 1.1 christos # 2001 1.1 christos # - If -name is updated, -loadPrefix probably needs to be as well. 2002 1.1 christos # 2003 1.1 christos proc teaish-pkginfo-set {args} { 2004 1.1 christos set doVars 0 2005 1.1 christos set doCommands 0 2006 1.1 christos set xargs $args 2007 1.1 christos set recalc {} 2008 1.1 christos foreach arg $args { 2009 1.1 christos switch -exact -- $arg { 2010 1.1 christos -vars { 2011 1.1 christos incr doVars 2012 1.1 christos set xargs [lassign $xargs -] 2013 1.1 christos } 2014 1.1 christos -subst { 2015 1.1 christos incr doVars 2016 1.1 christos incr doCommands 2017 1.1 christos set xargs [lassign $xargs -] 2018 1.1 christos } 2019 1.1 christos default { 2020 1.1 christos break 2021 1.1 christos } 2022 1.1 christos } 2023 1.1 christos } 2024 1.1 christos set args $xargs 2025 1.1 christos unset xargs 2026 1.1 christos if {1 == [llength $args] && [llength [lindex $args 0]] > 1} { 2027 1.1 christos # Transform a single {...} arg into the canonical call form 2028 1.1 christos set a [list {*}[lindex $args 0]] 2029 1.1 christos if {$doVars || $doCommands} { 2030 1.1 christos set sflags -nobackslashes 2031 1.1 christos if {!$doCommands} { 2032 1.1 christos lappend sflags -nocommands 2033 1.1 christos } 2034 1.1 christos set a [uplevel 1 [list subst {*}$sflags $a]] 2035 1.1 christos } 2036 1.1 christos set args $a 2037 1.1 christos } 2038 1.1 christos set sentinel "<nope>" 2039 1.1 christos set flagDefs [list] 2040 1.1 christos foreach {f d} $::teaish__Config(pkginfo-f2d) { 2041 1.1 christos lappend flagDefs $f => $sentinel 2042 1.1 christos } 2043 1.1 christos proj-parse-simple-flags args flags $flagDefs 2044 1.1 christos if {[llength $args]} { 2045 1.1 christos proj-error -up "Too many (or unknown) arguments to [proj-scope]: $args" 2046 1.1 christos } 2047 1.1 christos foreach {f d} $::teaish__Config(pkginfo-f2d) { 2048 1.1 christos if {$sentinel eq [set v $flags($f)]} continue 2049 1.1 christos switch -exact -- $f { 2050 1.1 christos 2051 1.1 christos -options { 2052 1.1 christos proj-assert {"" eq $d} 2053 1.1 christos options-add $v 2054 1.1 christos } 2055 1.1 christos 2056 1.1 christos -pragmas { 2057 1.1 christos teaish__pragma {*}$v 2058 1.1 christos } 2059 1.1 christos 2060 1.1 christos -vsatisfies { 2061 1.1 christos if {1 == [llength $v] && 1 == [llength [lindex $v 0]]} { 2062 1.1 christos # Transform X to {Tcl $X} 2063 1.1 christos set v [list [join [list Tcl $v]]] 2064 1.1 christos } 2065 1.1 christos define $d $v 2066 1.1 christos } 2067 1.1 christos 2068 1.1 christos -pkgInit.tcl - 2069 1.1 christos -pkgInit.tcl.in { 2070 1.1 christos if {0x22 & $::teaish__Config(pkginit-policy)} { 2071 1.1 christos proj-fatal "Cannot use -pkgInit.tcl(.in) more than once." 2072 1.1 christos } 2073 1.1 christos set x [file join $::teaish__Config(extension-dir) $v] 2074 1.1 christos set tTail [file tail $v] 2075 1.1 christos if {"-pkgInit.tcl.in" eq $f} { 2076 1.1 christos # Generate pkginit file X from X.in 2077 1.1 christos set pI 0x02 2078 1.1 christos set tIn $x 2079 1.1 christos set tOut [file rootname $tTail] 2080 1.1 christos set other -pkgInit.tcl 2081 1.1 christos } else { 2082 1.1 christos # Static pkginit file X 2083 1.1 christos set pI 0x20 2084 1.1 christos set tIn "" 2085 1.1 christos set tOut $x 2086 1.1 christos set other -pkgInit.tcl.in 2087 1.1 christos } 2088 1.1 christos set ::teaish__Config(pkginit-policy) $pI 2089 1.1 christos set ::teaish__PkgInfo($other) {} 2090 1.1 christos define TEAISH_PKGINIT_TCL_IN $tIn 2091 1.1 christos define TEAISH_PKGINIT_TCL $tOut 2092 1.1 christos define TEAISH_PKGINIT_TCL_TAIL $tTail 2093 1.1 christos teaish-dist-add $v 2094 1.1 christos set v $x 2095 1.1 christos } 2096 1.1 christos 2097 1.1 christos -src { 2098 1.1 christos set d $::teaish__Config(extension-dir) 2099 1.1 christos foreach f $v { 2100 1.1 christos lappend ::teaish__Config(dist-files) $f 2101 1.1 christos lappend ::teaish__Config(extension-src) $d/$f 2102 1.1 christos lappend ::teaish__PkgInfo(-src) $f 2103 1.1 christos # ^^^ so that default-value initialization in 2104 1.1 christos # teaish-configure-core recognizes that it's been set. 2105 1.1 christos } 2106 1.1 christos } 2107 1.1 christos 2108 1.1 christos -tm.tcl - 2109 1.1 christos -tm.tcl.in { 2110 1.1 christos if {0x30 & $::teaish__Config(pkgindex-policy)} { 2111 1.1 christos proj-fatal "Cannot use $f together with a pkgIndex.tcl." 2112 1.1 christos } elseif {$::teaish__Config(tm-policy)} { 2113 1.1 christos proj-fatal "Cannot use -tm.tcl(.in) more than once." 2114 1.1 christos } 2115 1.1 christos set x [file join $::teaish__Config(extension-dir) $v] 2116 1.1 christos if {"-tm.tcl.in" eq $f} { 2117 1.1 christos # Generate tm file X from X.in 2118 1.1 christos set pT 0x02 2119 1.1 christos set pI 0x100 2120 1.1 christos set tIn $x 2121 1.1 christos set tOut [file rootname [file tail $v]] 2122 1.1 christos set other -tm.tcl 2123 1.1 christos } else { 2124 1.1 christos # Static tm file X 2125 1.1 christos set pT 0x20 2126 1.1 christos set pI 0x200 2127 1.1 christos set tIn "" 2128 1.1 christos set tOut $x 2129 1.1 christos set other -tm.tcl.in 2130 1.1 christos } 2131 1.1 christos set ::teaish__Config(pkgindex-policy) $pI 2132 1.1 christos set ::teaish__Config(tm-policy) $pT 2133 1.1 christos set ::teaish__PkgInfo($other) {} 2134 1.1 christos define TEAISH_TM_TCL_IN $tIn 2135 1.1 christos define TEAISH_TM_TCL $tOut 2136 1.1 christos define TEAISH_PKGINDEX_TCL "" 2137 1.1 christos define TEAISH_PKGINDEX_TCL_IN "" 2138 1.1 christos define TEAISH_PKGINDEX_TCL_TAIL "" 2139 1.1 christos teaish-dist-add $v 2140 1.1 christos teaish__pragma no-dll 2141 1.1 christos set v $x 2142 1.1 christos } 2143 1.1 christos 2144 1.1 christos default { 2145 1.1 christos proj-assert {"" ne $d} 2146 1.1 christos define $d $v 2147 1.1 christos } 2148 1.1 christos } 2149 1.1 christos set ::teaish__PkgInfo($f) $v 2150 1.1 christos if {$f in {-name -version -libDir -loadPrefix}} { 2151 1.1 christos lappend recalc $f 2152 1.1 christos } 2153 1.1 christos } 2154 1.1 christos if {"" ne $recalc} { 2155 1.1 christos teaish__define_pkginfo_derived $recalc 2156 1.1 christos } 2157 1.1 christos } 2158 1.1 christos 2159 1.1 christos # 2160 1.1 christos # @teaish-pkginfo-get ?arg? 2161 1.1 christos # 2162 1.1 christos # If passed no arguments, it returns the extension config info in the 2163 1.1 christos # same form accepted by teaish-pkginfo-set. 2164 1.1 christos # 2165 1.1 christos # If passed one -flagname arg then it returns the value of that config 2166 1.1 christos # option. 2167 1.1 christos # 2168 1.1 christos # Else it treats arg as the name of caller-scoped variable to 2169 1.1 christos # which this function assigns an array containing the configuration 2170 1.1 christos # state of this extension, in the same structure accepted by 2171 1.1 christos # teaish-pkginfo-set. In this case it returns an empty string. 2172 1.1 christos # 2173 1.1 christos proc teaish-pkginfo-get {args} { 2174 1.1 christos set cases {} 2175 1.1 christos set argc [llength $args] 2176 1.1 christos set rv {} 2177 1.1 christos switch -exact $argc { 2178 1.1 christos 0 { 2179 1.1 christos # Return a list of (-flag value) pairs 2180 1.1 christos lappend cases default {{ 2181 1.1 christos if {[info exists ::teaish__PkgInfo($flag)]} { 2182 1.1 christos lappend rv $flag $::teaish__PkgInfo($flag) 2183 1.1 christos } else { 2184 1.1 christos lappend rv $flag [get-define $defName] 2185 1.1 christos } 2186 1.1 christos }} 2187 1.1 christos } 2188 1.1 christos 2189 1.1 christos 1 { 2190 1.1 christos set arg $args 2191 1.1 christos if {[string match -* $arg]} { 2192 1.1 christos # Return the corresponding -flag's value 2193 1.1 christos lappend cases $arg {{ 2194 1.1 christos if {[info exists ::teaish__PkgInfo($flag)]} { 2195 1.1 christos return $::teaish__PkgInfo($flag) 2196 1.1 christos } else { 2197 1.1 christos return [get-define $defName] 2198 1.1 christos } 2199 1.1 christos }} 2200 1.1 christos } else { 2201 1.1 christos # Populate target with an array of (-flag value). 2202 1.1 christos upvar $arg tgt 2203 1.1 christos array set tgt {} 2204 1.1 christos lappend cases default {{ 2205 1.1 christos if {[info exists ::teaish__PkgInfo($flag)]} { 2206 1.1 christos set tgt($flag) $::teaish__PkgInfo($flag) 2207 1.1 christos } else { 2208 1.1 christos set tgt($flag) [get-define $defName] 2209 1.1 christos } 2210 1.1 christos }} 2211 1.1 christos } 2212 1.1 christos } 2213 1.1 christos 2214 1.1 christos default { 2215 1.1 christos proj-error "invalid arg count from [proj-scope 1]" 2216 1.1 christos } 2217 1.1 christos } 2218 1.1 christos 2219 1.1 christos foreach {flag defName} $::teaish__Config(pkginfo-f2d) { 2220 1.1 christos switch -exact -- $flag [join $cases] 2221 1.1 christos } 2222 1.1 christos if {0 == $argc} { return $rv } 2223 1.1 christos } 2224 1.1 christos 2225 1.1 christos # (Re)set some defines based on pkginfo state. $flags is the list of 2226 1.1 christos # pkginfo -flags which triggered this, or "*" for the initial call. 2227 1.1 christos proc teaish__define_pkginfo_derived {flags} { 2228 1.1 christos set all [expr {{*} in $flags}] 2229 1.1 christos if {$all || "-version" in $flags || "-name" in $flags} { 2230 1.1 christos set name $::teaish__PkgInfo(-name) ; # _not_ -name.pkg 2231 1.1 christos if {[info exists ::teaish__PkgInfo(-version)]} { 2232 1.1 christos set pkgver $::teaish__PkgInfo(-version) 2233 1.1 christos set libname "lib" 2234 1.1 christos if {[string match *-cygwin [get-define host]]} { 2235 1.1 christos set libname cyg 2236 1.1 christos } 2237 1.1 christos define TEAISH_DLL8_BASENAME $libname$name$pkgver 2238 1.1 christos define TEAISH_DLL9_BASENAME ${libname}tcl9$name$pkgver 2239 1.1 christos set ext [get-define TARGET_DLLEXT] 2240 1.1 christos define TEAISH_DLL8 [get-define TEAISH_DLL8_BASENAME]$ext 2241 1.1 christos define TEAISH_DLL9 [get-define TEAISH_DLL9_BASENAME]$ext 2242 1.1 christos } 2243 1.1 christos } 2244 1.1 christos if {$all || "-libDir" in $flags} { 2245 1.1 christos if {[info exists ::teaish__PkgInfo(-libDir)]} { 2246 1.1 christos define TCLLIBDIR \ 2247 1.1 christos [file dirname [get-define TCLLIBDIR]]/$::teaish__PkgInfo(-libDir) 2248 1.1 christos } 2249 1.1 christos } 2250 1.1 christos } 2251 1.1 christos 2252 1.1 christos # 2253 1.1 christos # @teaish-checks-queue -pre|-post args... 2254 1.1 christos # 2255 1.1 christos # Queues one or more arbitrary "feature test" functions to be run when 2256 1.1 christos # teaish-checks-run is called. $flag must be one of -pre or -post to 2257 1.1 christos # specify whether the tests should be run before or after 2258 1.1 christos # teaish-configure is run. Each additional arg is the name of a 2259 1.1 christos # feature-test proc. 2260 1.1 christos # 2261 1.1 christos proc teaish-checks-queue {flag args} { 2262 1.1 christos if {$flag ni {-pre -post}} { 2263 1.1 christos proj-error "illegal flag: $flag" 2264 1.1 christos } 2265 1.1 christos lappend ::teaish__Config(queued-checks${flag}) {*}$args 2266 1.1 christos } 2267 1.1 christos 2268 1.1 christos # 2269 1.1 christos # @teaish-checks-run -pre|-post 2270 1.1 christos # 2271 1.1 christos # Runs all feature checks queued using teaish-checks-queue 2272 1.1 christos # then cleares the queue. 2273 1.1 christos # 2274 1.1 christos proc teaish-checks-run {flag} { 2275 1.1 christos if {$flag ni {-pre -post}} { 2276 1.1 christos proj-error "illegal flag: $flag" 2277 1.1 christos } 2278 1.1 christos #puts "*** running $flag: $::teaish__Config(queued-checks${flag})" 2279 1.1 christos set foo 0 2280 1.1 christos foreach f $::teaish__Config(queued-checks${flag}) { 2281 1.1 christos if {![teaish-feature-cache-check $f foo]} { 2282 1.1 christos set v [$f] 2283 1.1 christos teaish-feature-cache-set $f $v 2284 1.1 christos } 2285 1.1 christos } 2286 1.1 christos set ::teaish__Config(queued-checks${flag}) {} 2287 1.1 christos } 2288 1.1 christos 2289 1.1 christos # 2290 1.1 christos # A general-purpose getter for various teaish state. Requires one 2291 1.1 christos # flag, which determines its result value. Flags marked with [L] below 2292 1.1 christos # are safe for using at load-time, before teaish-pkginfo-set is called 2293 1.1 christos # 2294 1.1 christos # -dir [L]: returns the extension's directory, which may differ from 2295 1.1 christos # the teaish core dir or the build dir. 2296 1.1 christos # 2297 1.1 christos # -teaish-home [L]: the "home" dir of teaish itself, which may 2298 1.1 christos # differ from the extension dir or build dir. 2299 1.1 christos # 2300 1.1 christos # -build-dir [L]: the build directory (typically the current working 2301 1.1 christos # -dir). 2302 1.1 christos # 2303 1.1 christos # Any of the teaish-pkginfo-get/get flags: returns the same as 2304 1.1 christos # teaish-pkginfo-get. Not safe for use until teaish-pkginfo-set has 2305 1.1 christos # been called. 2306 1.1 christos # 2307 1.1 christos # Triggers an error if passed an unknown flag. 2308 1.1 christos # 2309 1.1 christos proc teaish-get {flag} { 2310 1.1 christos #-teaish.tcl {return $::teaish__Config(teaish.tcl)} 2311 1.1 christos switch -exact -- $flag { 2312 1.1 christos -dir { 2313 1.1 christos return $::teaish__Config(extension-dir) 2314 1.1 christos } 2315 1.1 christos -teaish-home { 2316 1.1 christos return $::autosetup(srcdir) 2317 1.1 christos } 2318 1.1 christos -build-dir { 2319 1.1 christos return $::autosetup(builddir) 2320 1.1 christos } 2321 1.1 christos default { 2322 1.1 christos if {[info exists ::teaish__PkgInfo($flag)]} { 2323 1.1 christos return $::teaish__PkgInfo($flag) 2324 1.1 christos } 2325 1.1 christos } 2326 1.1 christos } 2327 1.1 christos proj-error "Unhandled flag: $flag" 2328 1.1 christos } 2329 1.1 christos 2330 1.1 christos # 2331 1.1 christos # Handles --teaish-create-extension=TARGET-DIR 2332 1.1 christos # 2333 1.1 christos proc teaish__create_extension {dir} { 2334 1.1 christos set force [opt-bool teaish-force] 2335 1.1 christos if {"" eq $dir} { 2336 1.1 christos proj-error "--teaish-create-extension=X requires a directory name." 2337 1.1 christos } 2338 1.1 christos file mkdir $dir/generic 2339 1.1 christos set cwd [pwd] 2340 1.1 christos #set dir [file-normalize [file join $cwd $dir]] 2341 1.1 christos teaish__verbose 1 msg-result "Created dir $dir" 2342 1.1 christos cd $dir 2343 1.1 christos if {!$force} { 2344 1.1 christos # Ensure that we don't blindly overwrite anything 2345 1.1 christos foreach f { 2346 1.1 christos generic/teaish.c 2347 1.1 christos teaish.tcl 2348 1.1 christos teaish.make.in 2349 1.1 christos teaish.test.tcl 2350 1.1 christos } { 2351 1.1 christos if {[file exists $f]} { 2352 1.1 christos error "Cowardly refusing to overwrite $dir/$f. Use --teaish-force to overwrite." 2353 1.1 christos } 2354 1.1 christos } 2355 1.1 christos } 2356 1.1 christos 2357 1.1 christos set name [file tail $dir] 2358 1.1 christos set pkgName $name 2359 1.1 christos set version 0.0.1 2360 1.1 christos set loadPrefix [string totitle $pkgName] 2361 1.1 christos set content {teaish-pkginfo-set } 2362 1.1 christos #puts "0 content=$content" 2363 1.1 christos if {[opt-str teaish-extension-pkginfo epi]} { 2364 1.1 christos set epi [string trim $epi] 2365 1.1 christos if {[string match "*\n*" $epi]} { 2366 1.1 christos set epi "{$epi}" 2367 1.1 christos } elseif {![string match "{*}" $epi]} { 2368 1.1 christos append content "\{" $epi "\}" 2369 1.1 christos } else { 2370 1.1 christos append content $epi 2371 1.1 christos } 2372 1.1 christos #puts "2 content=$content\nepi=$epi" 2373 1.1 christos } else { 2374 1.1 christos append content [subst -nocommands -nobackslashes {{ 2375 1.1 christos -name ${name} 2376 1.1 christos -name.pkg ${pkgName} 2377 1.1 christos -name.dist ${pkgName} 2378 1.1 christos -version ${version} 2379 1.1 christos -loadPrefix $loadPrefix 2380 1.1 christos -libDir ${name}${version} 2381 1.1 christos -vsatisfies {{Tcl 8.5-}} 2382 1.1 christos -url {} 2383 1.1 christos -options {} 2384 1.1 christos -pragmas {full-dist} 2385 1.1 christos }}] 2386 1.1 christos #puts "3 content=$content" 2387 1.1 christos } 2388 1.1 christos #puts "1 content=$content" 2389 1.1 christos append content "\n" { 2390 1.1 christos #proc teaish-options {} { 2391 1.1 christos # Return a list and/or use \[options-add\] to add new 2392 1.1 christos # configure flags. This is called before teaish's 2393 1.1 christos # bootstrapping is finished, so only teaish-* 2394 1.1 christos # APIs which are explicitly noted as being safe 2395 1.1 christos # early on may be used here. Any autosetup-related 2396 1.1 christos # APIs may be used here. 2397 1.1 christos # 2398 1.1 christos # Return an empty string if there are no options to 2399 1.1 christos # add or if they are added using \[options-add\]. 2400 1.1 christos # 2401 1.1 christos # If there are no options to add, this proc need 2402 1.1 christos # not be defined. 2403 1.1 christos #} 2404 1.1 christos 2405 1.1 christos # Called by teaish once bootstrapping is complete. 2406 1.1 christos # This function is responsible for the client-specific 2407 1.1 christos # parts of the configuration process. 2408 1.1 christos proc teaish-configure {} { 2409 1.1 christos teaish-src-add -dir -dist generic/teaish.c 2410 1.1 christos teaish-define-to-cflag -quote TEAISH_PKGNAME TEAISH_VERSION 2411 1.1 christos 2412 1.1 christos # TODO: your code goes here.. 2413 1.1 christos } 2414 1.1 christos }; # $content 2415 1.1 christos proj-file-write teaish.tcl $content 2416 1.1 christos teaish__verbose 1 msg-result "Created teaish.tcl" 2417 1.1 christos 2418 1.1 christos set content "# Teaish test script. 2419 1.1 christos # When this tcl script is invoked via 'make test' it will have loaded 2420 1.1 christos # the package, run any teaish.pkginit.tcl code, and loaded 2421 1.1 christos # autosetup/teaish/tester.tcl. 2422 1.1 christos " 2423 1.1 christos proj-file-write teaish.test.tcl $content 2424 1.1 christos teaish__verbose 1 msg-result "Created teaish.test.tcl" 2425 1.1 christos 2426 1.1 christos set content [subst -nocommands -nobackslashes { 2427 1.1 christos #include <tcl.h> 2428 1.1 christos static int 2429 1.1 christos ${loadPrefix}_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]){ 2430 1.1 christos Tcl_SetObjResult(interp, Tcl_NewStringObj("this is the ${name} extension", -1)); 2431 1.1 christos return TCL_OK; 2432 1.1 christos } 2433 1.1 christos 2434 1.1 christos extern int DLLEXPORT ${loadPrefix}_Init(Tcl_Interp *interp){ 2435 1.1 christos if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) { 2436 1.1 christos return TCL_ERROR; 2437 1.1 christos } 2438 1.1 christos if (Tcl_PkgProvide(interp, TEAISH_PKGNAME, TEAISH_VERSION) == TCL_ERROR) { 2439 1.1 christos return TCL_ERROR; 2440 1.1 christos } 2441 1.1 christos Tcl_CreateObjCommand(interp, TEAISH_PKGNAME, ${loadPrefix}_Cmd, NULL, NULL); 2442 1.1 christos return TCL_OK; 2443 1.1 christos } 2444 1.1 christos }] 2445 1.1 christos proj-file-write generic/teaish.c $content 2446 1.1 christos teaish__verbose 1 msg-result "Created generic/teaish.c" 2447 1.1 christos 2448 1.1 christos set content "# teaish makefile for the ${name} extension 2449 1.1 christos # tx.src = \$(tx.dir)/generic/teaish.c 2450 1.1 christos # tx.LDFLAGS = 2451 1.1 christos # tx.CFLAGS = 2452 1.1 christos " 2453 1.1 christos proj-file-write teaish.make.in $content 2454 1.1 christos teaish__verbose 1 msg-result "Created teaish.make.in" 2455 1.1 christos 2456 1.1 christos msg-result "Created new extension \[$dir\]." 2457 1.1 christos 2458 1.1 christos cd $cwd 2459 1.1 christos set ::teaish__Config(install-ext-dir) $dir 2460 1.1 christos } 2461 1.1 christos 2462 1.1 christos # 2463 1.1 christos # Internal helper for teaish__install 2464 1.1 christos # 2465 1.1 christos proc teaish__install_file {f destDir force} { 2466 1.1 christos set dest $destDir/[file tail $f] 2467 1.1 christos if {[file isdirectory $f]} { 2468 1.1 christos file mkdir $dest 2469 1.1 christos } elseif {!$force && [file exists $dest]} { 2470 1.1 christos array set st1 [file stat $f] 2471 1.1 christos array set st2 [file stat $dest] 2472 1.1 christos if {($st1(mtime) == $st2(mtime)) 2473 1.1 christos && ($st1(size) == $st2(size))} { 2474 1.1 christos if {[file tail $f] in { 2475 1.1 christos pkgIndex.tcl.in 2476 1.1 christos _teaish.tester.tcl.in 2477 1.1 christos }} { 2478 1.1 christos # Assume they're the same. In the scope of the "make dist" 2479 1.1 christos # rules, this happens legitimately when an extension with a 2480 1.1 christos # copy of teaish installed in the same dir assumes that the 2481 1.1 christos # pkgIndex.tcl.in and _teaish.tester.tcl.in belong to the 2482 1.1 christos # extension, whereas teaish believes they belong to teaish. 2483 1.1 christos # So we end up with dupes of those. 2484 1.1 christos return 2485 1.1 christos } 2486 1.1 christos } 2487 1.1 christos proj-error -up "Cowardly refusing to overwrite \[$dest\]." \ 2488 1.1 christos "Use --teaish-force to enable overwriting." 2489 1.1 christos } else { 2490 1.1 christos # file copy -force $f $destDir; # loses +x bit 2491 1.1 christos # 2492 1.1 christos # JimTcl doesn't have [file attribute], so we can't use that here 2493 1.1 christos # (in the context of an autosetup configure script). 2494 1.1 christos exec cp -p $f $dest 2495 1.1 christos } 2496 1.1 christos } 2497 1.1 christos 2498 1.1 christos # 2499 1.1 christos # Installs a copy of teaish, with autosetup, to $dDest, which defaults 2500 1.1 christos # to the --teaish-install=X or --teash-create-extension=X dir. Won't 2501 1.1 christos # overwrite files unless --teaish-force is used. 2502 1.1 christos # 2503 1.1 christos proc teaish__install {{dDest ""}} { 2504 1.1 christos if {$dDest in {auto ""}} { 2505 1.1 christos set dDest [opt-val teaish-install] 2506 1.1 christos if {$dDest in {auto ""}} { 2507 1.1 christos if {[info exists ::teaish__Config(install-ext-dir)]} { 2508 1.1 christos set dDest $::teaish__Config(install-ext-dir) 2509 1.1 christos } 2510 1.1 christos } 2511 1.1 christos } 2512 1.1 christos set force [opt-bool teaish-force] 2513 1.1 christos if {$dDest in {auto ""}} { 2514 1.1 christos proj-error "Cannot determine installation directory." 2515 1.1 christos } elseif {!$force && [file exists $dDest/auto.def]} { 2516 1.1 christos proj-error \ 2517 1.1 christos "Target dir looks like it already contains teaish and/or autosetup: $dDest" \ 2518 1.1 christos "\nUse --teaish-force to overwrite it." 2519 1.1 christos } 2520 1.1 christos 2521 1.1 christos set dSrc $::autosetup(srcdir) 2522 1.1 christos set dAS $::autosetup(libdir) 2523 1.1 christos set dAST $::teaish__Config(core-dir) 2524 1.1 christos set dASTF $dAST/feature 2525 1.1 christos teaish__verbose 1 msg-result "Installing teaish to \[$dDest\]..." 2526 1.1 christos if {$::teaish__Config(verbose)>1} { 2527 1.1 christos msg-result "dSrc = $dSrc" 2528 1.1 christos msg-result "dAS = $dAS" 2529 1.1 christos msg-result "dAST = $dAST" 2530 1.1 christos msg-result "dASTF = $dASTF" 2531 1.1 christos msg-result "dDest = $dDest" 2532 1.1 christos } 2533 1.1 christos 2534 1.1 christos # Dest subdirs... 2535 1.1 christos set ddAS $dDest/autosetup 2536 1.1 christos set ddAST $ddAS/teaish 2537 1.1 christos set ddASTF $ddAST/feature 2538 1.1 christos foreach {srcDir destDir} [list \ 2539 1.1 christos $dAS $ddAS \ 2540 1.1 christos $dAST $ddAST \ 2541 1.1 christos $dASTF $ddASTF \ 2542 1.1 christos ] { 2543 1.1 christos teaish__verbose 1 msg-result "Copying files to $destDir..." 2544 1.1 christos file mkdir $destDir 2545 1.1 christos foreach f [glob -nocomplain -directory $srcDir *] { 2546 1.1 christos if {[string match {*~} $f] || [string match "#*#" [file tail $f]]} { 2547 1.1 christos # Editor-generated backups and emacs lock files 2548 1.1 christos continue 2549 1.1 christos } 2550 1.1 christos teaish__verbose 2 msg-result "\t$f" 2551 1.1 christos teaish__install_file $f $destDir $force 2552 1.1 christos } 2553 1.1 christos } 2554 1.1 christos teaish__verbose 1 msg-result "Copying files to $dDest..." 2555 1.1 christos foreach f { 2556 1.1 christos auto.def configure Makefile.in pkgIndex.tcl.in 2557 1.1 christos _teaish.tester.tcl.in 2558 1.1 christos } { 2559 1.1 christos teaish__verbose 2 msg-result "\t$f" 2560 1.1 christos teaish__install_file $dSrc/$f $dDest $force 2561 1.1 christos } 2562 1.1 christos set ::teaish__Config(install-self-dir) $dDest 2563 1.1 christos msg-result "Teaish $::teaish__Config(version) installed in \[$dDest\]." 2564 1.1 christos } 2565