Home | History | Annotate | Line # | Download | only in teaish
      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