diff options
Diffstat (limited to 'autoconf')
-rw-r--r-- | autoconf/tea/Makefile.in | 73 | ||||
-rw-r--r-- | autoconf/tea/_teaish.tester.tcl.in (renamed from autoconf/tea/teaish.tester.tcl.in) | 8 | ||||
-rw-r--r-- | autoconf/tea/autosetup/README.txt | 4 | ||||
-rw-r--r-- | autoconf/tea/autosetup/core.tcl | 2368 | ||||
-rw-r--r-- | autoconf/tea/autosetup/feature-tests.tcl | 214 | ||||
-rw-r--r-- | autoconf/tea/autosetup/tester.tcl | 193 | ||||
-rw-r--r-- | autoconf/tea/teaish.tcl | 2 |
7 files changed, 64 insertions, 2798 deletions
diff --git a/autoconf/tea/Makefile.in b/autoconf/tea/Makefile.in index ad71c8b3e..5b2ad4c69 100644 --- a/autoconf/tea/Makefile.in +++ b/autoconf/tea/Makefile.in @@ -1,5 +1,9 @@ all: # +# Unless this file is named Makefile.in, you are probably looking +# at an automatically generated/filtered copy and should probably not +# edit it. +# # This makefile is part of the teaish framework, a tool for building # Tcl extensions, conceptually related to TEA/tclconfig but using the # Autosetup configuration system instead of the GNU Autotools. @@ -47,6 +51,12 @@ tx.dll8 = @TEAISH_DLL8@ tx.dll9 = @TEAISH_DLL9@ tx.dll = $(tx.dll$(TCL_MAJOR_VERSION)) tx.dir = @TEAISH_EXT_DIR@ +@if TEAISH_TM_TCL +# Input filename for tcl::tm-style module +tx.tm = @TEAISH_TM_TCL@ +# Target filename for tcl::tm-style installation +tx.tm.tgt = $(tx.name.pkg)-$(tx.version).tm +@endif @if TEAISH_DIST_NAME tx.name.dist = @TEAISH_DIST_NAME@ @@ -157,23 +167,12 @@ tx.LDFLAGS = # tx.dist.files = @TEAISH_DIST_FILES@ -# -# May get amended with generated file names. They are cleaned up by -# the 'clean' rules. Client code which wants to clean up extra stuff -# should do so by adding their cleanup target (e.g. clean-extension) -# as a dependency to the 'clean' target, like so: -# -# clean: distclean-extension -# distclean: distclean-extension -# -teaish__cleanExtra = - # List of deps which may trigger an auto-reconfigure. # teaish__autogen.deps = \ $(tx.makefile.in) $(teaish.makefile.in) \ $(tx.tcl) \ - @TEAISH_PKGINDEX_TCL_IN@ \ + @TEAISH_PKGINDEX_TCL_IN@ @TEAISH_TM_TCL_IN@ \ @AUTODEPS@ @if TEAISH_MAKEFILE_IN @@ -271,10 +270,10 @@ test: test-post # Cleanup rules... # #.PHONY: clean-pre clean-core clean-post clean-extension -clean-extension: # this name is reserved for use by teaish.make +# clean-pre: clean-core: clean-pre - rm -f $(tx.dll8) $(tx.dll9) tclsh $(teaish__cleanExtra) + rm -f $(tx.dll8) $(tx.dll9) tclsh clean-post: clean-core clean: clean-post @@ -300,26 +299,59 @@ distclean-core: distclean-pre @if TEAISH_TEST_TCL_IN rm -f @TEAISH_TEST_TCL@ @endif -distclean-extension: # this name is reserved for use by teaish.make distclean-post: distclean-core distclean: distclean-post +# +# The (dist)clean-extension targets are reserved for use by +# client-side teaish.make. +# +# Client code which wants to clean up extra stuff should do so by +# adding their cleanup target (e.g. clean-extension) as a dependency +# to the 'clean' target, like so: +# +# clean: distclean-extension +# distclean: distclean-extension +# +distclean-extension: +clean-extension: # # Installation rules... # +@if TEAISH_ENABLE_INSTALL .PHONY: install-pre install-core install-post install-test install-prepre install-extension install-extension: # this name is reserved for use by teaish.make + +@if TEAISH_ENABLE_DLL install-prepre: $(tx.dll) +@else +install-prepre: +@endif + +@if TEAISH_TM_TCL +install-core.tmdir = $(DESTDIR)@TEAISH_TCL_TM_DIR@ +@endif + install-pre: install-prepre install-core: install-pre @if [ ! -d "$(DESTDIR)$(TCLLIBDIR)" ]; then \ set -x; $(INSTALL) -d "$(DESTDIR)$(TCLLIBDIR)"; \ fi # ^^^^ on some platforms, install -d fails if the target already exists. +@if TEAISH_ENABLE_DLL $(INSTALL) $(tx.dll) "$(DESTDIR)$(TCLLIBDIR)" - $(INSTALL.noexec) pkgIndex.tcl "$(DESTDIR)$(TCLLIBDIR)" +@endif +@if TEAISH_PKGINDEX_TCL + $(INSTALL.noexec) "@TEAISH_PKGINDEX_TCL@" "$(DESTDIR)$(TCLLIBDIR)" +@endif @if TEAISH_PKGINIT_TCL - $(INSTALL.noexec) @TEAISH_PKGINIT_TCL@ "$(DESTDIR)$(TCLLIBDIR)" + $(INSTALL.noexec) "@TEAISH_PKGINIT_TCL@" "$(DESTDIR)$(TCLLIBDIR)" +@endif +@if TEAISH_TM_TCL + @if [ ! -d "$(install-core.tmdir)" ]; then \ + set -x; $(INSTALL) -d "$(install-core.tmdir)"; \ + fi + $(INSTALL.noexec) "@TEAISH_TM_TCL@" "$(install-core.tmdir)/$(tx.tm.tgt)" @endif install-test: install-core @echo "Post-install test of [package require $(tx.name.pkg) $(tx.version)]..."; \ @@ -344,10 +376,17 @@ install: install-post uninstall-extension: # this name is reserved for use by teaish.make uninstall-pre: uninstall-core: uninstall-pre +@if TEAISH_ENABLE_DLL rm -fr "$(DESTDIR)$(TCLLIBDIR)" +@endif +@if TEAISH_TM_TCL + rm -f "$(DESTDIR)$(install-core.tmdir)/$(tx.tm.tgt)" +@endif + uninstall-post: uninstall-core @echo "Uninstalled Tcl extension $(tx.name) $(tx.version)" uninstall: uninstall-post +@endif # TEAISH_ENABLE_INSTALL @if TEAISH_MAKEFILE_IN Makefile: $(tx.makefile.in) diff --git a/autoconf/tea/teaish.tester.tcl.in b/autoconf/tea/_teaish.tester.tcl.in index 4e203cd78..59d11f0a8 100644 --- a/autoconf/tea/teaish.tester.tcl.in +++ b/autoconf/tea/_teaish.tester.tcl.in @@ -1,6 +1,6 @@ # -*- tcl -*- # -# Unless this file is named teaish.tester.tcl.in, you are probably +# Unless this file is named _teaish.tester.tcl.in, you are probably # looking at an automatically generated/filtered copy and should # probably not edit it. # @@ -28,6 +28,12 @@ apply {{file} { source -encoding utf-8 $file }} [join {@TEAISH_PKGINIT_TCL@}] @endif +@if TEAISH_TM_TCL +apply {{file} { + set dir [file dirname $::argv0] + source -encoding utf-8 $file +}} [join {@TEAISH_TM_TCL@}] +@endif @if TEAISH_TEST_TCL apply {{file} { # Populate state for [tester.tcl::teaish-build-flag*] diff --git a/autoconf/tea/autosetup/README.txt b/autoconf/tea/autosetup/README.txt deleted file mode 100644 index e11519b04..000000000 --- a/autoconf/tea/autosetup/README.txt +++ /dev/null @@ -1,4 +0,0 @@ -The *.tcl files in this directory are part of the SQLite's "autoconf" -bundle which are specific to the TEA(-ish) build. During the tarball -generation process, they are copied into <TOP>/autoconf/autosetup/teaish -(which itself is created as part of that process). diff --git a/autoconf/tea/autosetup/core.tcl b/autoconf/tea/autosetup/core.tcl deleted file mode 100644 index 4b3eb9a82..000000000 --- a/autoconf/tea/autosetup/core.tcl +++ /dev/null @@ -1,2368 +0,0 @@ -######################################################################## -# 2025 April 5 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# * May you do good and not evil. -# * May you find forgiveness for yourself and forgive others. -# * May you share freely, never taking more than you give. -# -######################################################################## -# ----- @module teaish.tcl ----- -# @section TEA-ish ((TCL Extension Architecture)-ish) -# -# Functions in this file with a prefix of teaish__ are -# private/internal APIs. Those with a prefix of teaish- are -# public APIs. -# -# Teaish has a hard dependency on proj.tcl, and any public API members -# of that module are considered legal for use by teaish extensions. -# -# Project home page: https://fossil.wanderinghorse.net/r/teaish - -use proj - -# -# API-internal settings and shared state. -array set teaish__Config [proj-strip-hash-comments { - # - # Teaish's version number, not to be confused with - # teaish__PkgInfo(-version). - # - version 0.1-beta - - # set to 1 to enable some internal debugging output - debug-enabled 0 - - # - # 0 = don't yet have extension's pkgindex - # 0x01 = found TEAISH_EXT_DIR/pkgIndex.tcl.in - # 0x02 = found srcdir/pkgIndex.tcl.in - # 0x10 = found TEAISH_EXT_DIR/pkgIndex.tcl (static file) - # 0x20 = static-pkgIndex.tcl pragma: behave as if 0x10 - # - # Reminder: it's significant that the bottom 4 bits be - # cases where teaish manages ./pkgIndex.tcl. - # - pkgindex-policy 0 - - # - # The pkginit counterpart of pkgindex-policy: - # - # 0 = no pkginit - # 0x01 = found default X.in: generate X from X.in - # 0x10 = found static pkginit file X - # 0x02 = user-provided X.in generates ./X. - # 0x20 = user-provided static pkginit file X - # - # The 0x0f bits indicate that teaish is responsible for cleaning up - # the (generated) pkginit file. - # - pkginit-policy 0 - - # - # If 1+ then teaish__verbose will emit messages. - # - verbose 0 - - # - # Mapping of pkginfo -flags to their TEAISH_xxx define (if any). - # This must not be modified. - # - pkginfo-f2d { - -name TEAISH_NAME - -name.dist TEAISH_DIST_NAME - -name.pkg TEAISH_PKGNAME - -version TEAISH_VERSION - -libDir TEAISH_LIBDIR_NAME - -loadPrefix TEAISH_LOAD_PREFIX - -vsatisfies TEAISH_VSATISFIES - -pkgInit.tcl TEAISH_PKGINIT_TCL - -pkgInit.tcl.in TEAISH_PKGINIT_TCL_IN - -url TEAISH_URL - -options {} - -pragmas {} - } - - # - # Queues for use with teaish-checks-queue and teaish-checks-run. - # - queued-checks-pre {} - queued-checks-post {} - - # Whether or not "make dist" parts are enabled. They get enabled - # when building from an extension's dir, disabled when building - # elsewhere. - dist-enabled 1 - - # By default we enable compilation of a native extension but if the - # extension has no native code or the user wants to take that over - # via teaish.make.in or provide a script-only extension, we will - # elide the default compilation rules if this is 0. - dll-enabled 1 - - # Files to include in the "make dist" bundle. - dist-files {} - - # List of source files for the extension. - extension-src {} - - # Path to the teaish.tcl file. - teaish.tcl {} - - # Dir where teaish.tcl is found. - extension-dir {} - - # Whether the generates TEASH_VSATISFIES_CODE should error out on a - # satisfies error. If 0, it uses return instead of error. - vsatisfies-error 1 - - # Whether or not to allow a "full dist" - a "make dist" build which - # includes both the extension and teaish. By default this is only on - # if the extension dir is teaish's dir. - dist-full-enabled 0 -}] -set teaish__Config(core-dir) $::autosetup(libdir)/teaish - -# -# Array of info managed by teaish-pkginfo-get and friends. Has the -# same set of keys as $teaish__Config(pkginfo-f2d). -# -array set teaish__PkgInfo {} - -# -# Runs {*}$args if $lvl is <= the current verbosity level, else it has -# no side effects. -# -proc teaish__verbose {lvl args} { - if {$lvl <= $::teaish__Config(verbose)} { - {*}$args - } -} - -# -# @teaish-argv-has flags... -# -# Returns true if any arg in $::argv matches any of the given globs, -# else returns false. -# -proc teaish-argv-has {args} { - foreach glob $args { - foreach arg $::argv { - if {[string match $glob $arg]} { - return 1 - } - } - } - return 0 -} - -if {[teaish-argv-has --teaish-verbose --t-v]} { - # Check this early so that we can use verbose-only messages in the - # pre-options-parsing steps. - set ::teaish__Config(verbose) 1 - #teaish__verbose 1 msg-result "--teaish-verbose activated" -} - -msg-quiet use system ; # Outputs "Host System" and "Build System" lines -if {"--help" ni $::argv} { - teaish__verbose 1 msg-result "TEA(ish) Version = $::teaish__Config(version)" - teaish__verbose 1 msg-result "Source dir = $::autosetup(srcdir)" - teaish__verbose 1 msg-result "Build dir = $::autosetup(builddir)" -} - -# -# @teaish-configure-core -# -# Main entry point for the TEA-ish configure process. auto.def's primary -# (ideally only) job should be to call this. -# -proc teaish-configure-core {} { - proj-tweak-default-env-dirs - - set ::teaish__Config(install-mode) [teaish-argv-has --teaish-install*] - set ::teaish__Config(create-ext-mode) \ - [teaish-argv-has --teaish-create-extension=* --t-c-e=*] - set gotExt 0; # True if an extension config is found - if {!$::teaish__Config(create-ext-mode) - && !$::teaish__Config(install-mode)} { - # Don't look for an extension if we're in --t-c-e or --t-i mode - set gotExt [teaish__find_extension] - } - - # - # Set up the core --flags. This needs to come before teaish.tcl is - # sourced so that that file can use teaish-pkginfo-set to append - # options. - # - options-add [proj-strip-hash-comments { - with-tcl:DIR - => {Directory containing tclConfig.sh or a directory one level up from - that, from which we can derive a directory containing tclConfig.sh. - Defaults to the $TCL_HOME environment variable.} - - with-tclsh:PATH - => {Full pathname of tclsh to use. It is used for trying to find - tclConfig.sh. Warning: if its containing dir has multiple tclsh - versions, it may select the wrong tclConfig.sh! - Defaults to the $TCLSH environment variable.} - - # TEA has --with-tclinclude but it appears to only be useful for - # building an extension against an uninstalled copy of TCL's own - # source tree. The policy here is that either we get that info - # from tclConfig.sh or we give up. - # - # with-tclinclude:DIR - # => {Specify the directory which contains the tcl.h. This should not - # normally be required, as that information comes from tclConfig.sh.} - - # We _generally_ want to reduce the possibility of flag collisions with - # extensions, and thus use a teaish-... prefix on most flags. However, - # --teaish-extension-dir is frequently needed, so... - # - # As of this spontaneous moment, we'll settle on using --t-A-X to - # abbreviate --teaish-A...-X... flags when doing so is - # unambiguous... - ted: t-e-d: - teaish-extension-dir:DIR - => {Looks for an extension in the given directory instead of the current - dir.} - - t-c-e: - teaish-create-extension:TARGET_DIRECTORY - => {Writes stub files for creating an extension. Will refuse to overwrite - existing files without --teaish-force.} - - t-f - teaish-force - => {Has a context-dependent meaning (autosetup defines --force for its - own use).} - - t-d-d - teaish-dump-defines - => {Dump all configure-defined vars to config.defines.txt} - - t-v:=0 - teaish-verbose:=0 - => {Enable more (often extraneous) messages from the teaish core.} - - t-d - teaish-debug=0 => {Enable teaish-specific debug output} - - t-i - teaish-install:=auto - => {Installs a copy of teaish, including autosetup, to the target dir. - When used with --teaish-create-extension=DIR, a value of "auto" - (no no value) will inherit that directory.} - - #TODO: --teaish-install-extension:=dir as short for - # --t-c-e=dir --t-i - - t-e-p: - teaish-extension-pkginfo:pkginfo - => {For use with --teaish-create-extension. If used, it must be a - list of arguments for use with teaish-pkginfo-set, e.g. - --teaish-extension-pkginfo="-name Foo -version 2.3"} - - t-v-c - teaish-vsatisfies-check=1 - => {Disable the configure-time "vsatisfies" check on the target tclsh.} - - }]; # main options. - - if {$gotExt} { - proj-assert {"" ne [teaish-pkginfo-get -name]} - proj-assert {[file exists $::teaish__Config(teaish.tcl)]} \ - "Expecting to have found teaish.tcl by now" - uplevel 1 {source $::teaish__Config(teaish.tcl)} - # Set up some default values if the extension did not set them. - # This must happen _after_ it's sourced but before - # teaish-configure is called. - array set f2d $::teaish__Config(pkginfo-f2d) - foreach {pflag key type val} { - - TEAISH_CFLAGS -v "" - - TEAISH_LDFLAGS -v "" - - TEAISH_MAKEFILE -v "" - - TEAISH_MAKEFILE_CODE -v "" - - TEAISH_MAKEFILE_IN -v "" - - TEAISH_PKGINDEX_TCL -v "" - - TEAISH_PKGINDEX_TCL_IN -v "" - - TEAISH_TEST_TCL -v "" - - TEAISH_TEST_TCL_IN -v "" - - -version :f2d: -v 0.0.0 - -name.pkg :f2d: -e {teaish-pkginfo-get -name} - -name.dist :f2d: -e {teaish-pkginfo-get -name} - -libDir :f2d: -e { - join [list \ - [teaish-pkginfo-get -name.pkg] \ - [teaish-pkginfo-get -version]] "" - } - -loadPrefix :f2d: -e { - string totitle [teaish-get -name.pkg] - } - -vsatisfies :f2d: -v {{Tcl 8.5-}} - -pkgInit.tcl :f2d: -v "" - -pkgInit.tcl.in :f2d: -v "" - -url :f2d: -v "" - } { - set isPIFlag [expr {"-" ne $pflag}] - if {$isPIFlag} { - if {[info exists ::teaish__PkgInfo($pflag)]} { - # Was already set - skip it. - continue; - } - proj-assert {{:f2d:} eq $key} - set key $f2d($pflag) - } - proj-assert {"" ne $key} - set got [get-define $key "<nope>"] - if {"<nope>" ne $got} { - # Was already set - skip it. - continue - } - switch -exact -- $type { - -v {} - -e { set val [eval $val] } - default { proj-error "Invalid type flag: $type" } - } - #puts "***** defining default $pflag $key {$val} isPIFlag=$isPIFlag got=$got" - define $key $val - if {$isPIFlag} { - set ::teaish__PkgInfo($pflag) $val - } - } - unset isPIFlag pflag key type val - array unset f2d - }; # sourcing extension's teaish.tcl - - if {[llength [info proc teaish-options]] > 0} { - # Add options defined by teaish-options, which is assumed to be - # imported via [teaish-get -teaish-tcl]. - set o [teaish-options] - if {"" ne $o} { - options-add $o - } - } - #set opts [proj-options-combine] - #lappend opts teaish-debug => {x}; #testing dupe entry handling - if {[catch {options {}} msg xopts]} { - # Workaround for <https://github.com/msteveb/autosetup/issues/73> - # where [options] behaves oddly on _some_ TCL builds when it's - # called from deeper than the global scope. - dict incr xopts -level - return {*}$xopts $msg - } - - proj-xfer-options-aliases { - t-c-e => teaish-create-extension - t-d => teaish-debug - t-d-d => teaish-dump-defines - ted => teaish-extension-dir - t-e-d => teaish-extension-dir - t-e-p => teaish-extension-pkginfo - t-f => teaish-force - t-i => teaish-install - t-v => teaish-verbose - t-v-c => teaish-vsatisfies-check - } - - scan [opt-val teaish-verbose 0] %d ::teaish__Config(verbose) - set ::teaish__Config(debug-enabled) [opt-bool teaish-debug] - - set exitEarly 0 - if {[proj-opt-was-provided teaish-create-extension]} { - teaish__create_extension [opt-val teaish-create-extension] - incr exitEarly - } - if {$::teaish__Config(install-mode)} { - teaish__install - incr exitEarly - } - - if {$exitEarly} { - file delete -force config.log - return - } - proj-assert {1==$gotExt} "Else we cannot have gotten this far" - - teaish__configure_phase1 -} - - -# -# Internal config-time debugging output routine. It is not legal to -# call this from the global scope. -# -proc teaish-debug {msg} { - if {$::teaish__Config(debug-enabled)} { - puts stderr [proj-bold "** DEBUG: \[[proj-scope 1]\]: $msg"] - } -} - -# -# Runs "phase 1" of the configuration, immediately after processing -# --flags. This is what will import the client-defined teaish.tcl. -# -proc teaish__configure_phase1 {} { - msg-result \ - [join [list "Configuring build of Tcl extension" \ - [proj-bold [teaish-pkginfo-get -name] \ - [teaish-pkginfo-get -version]] "..."]] - - uplevel 1 { - use cc cc-db cc-shared cc-lib; # pkg-config - } - teaish__check_tcl - apply {{} { - # - # If --prefix or --exec-prefix are _not_ provided, use their - # TCL_... counterpart from tclConfig.sh. Caveat: by the time we can - # reach this point, autosetup's system.tcl will have already done - # some non-trivial amount of work with these to create various - # derived values from them, so we temporarily end up with a mishmash - # of autotools-compatibility var values. That will be straightened - # out in the final stage of the configure script via - # [proj-remap-autoconf-dir-vars]. - # - foreach {flag uflag tclVar} { - prefix prefix TCL_PREFIX - exec-prefix exec_prefix TCL_EXEC_PREFIX - } { - if {![proj-opt-was-provided $flag]} { - if {"exec-prefix" eq $flag} { - # If --exec-prefix was not used, ensure that --exec-prefix - # derives from the --prefix we may have just redefined. - set v {${prefix}} - } else { - set v [get-define $tclVar "???"] - teaish__verbose 1 msg-result "Using \$$tclVar for --$flag=$v" - } - proj-assert {"???" ne $v} "Expecting teaish__check_tcl to have defined $tclVar" - #puts "*** $flag $uflag $tclVar = $v" - proj-opt-set $flag $v - define $uflag $v - - # ^^^ As of here, all autotools-compatibility vars which derive - # from --$flag, e.g. --libdir, still derive from the default - # --$flag value which was active when system.tcl was - # included. So long as those flags are not explicitly passed to - # the configure script, those will be straightened out via - # [proj-remap-autoconf-dir-vars]. - } - } - }}; # --[exec-]prefix defaults - teaish__check_common_bins - # - # Set up library file names - # - proj-file-extensions - teaish__define_pkginfo_derived * - - teaish-checks-run -pre - if {[llength [info proc teaish-configure]] > 0} { - # teaish-configure is assumed to be imported via - # teaish.tcl - teaish-configure - } - teaish-checks-run -post - - apply {{} { - # Set up "vsatisfies" code for pkgIndex.tcl.in, - # teaish.tester.tcl.in, and for a configure-time check. We would - # like to put this before [teaish-checks-run -pre] but it's - # marginally conceivable that a client may need to dynamically - # calculate the vsatisfies and set it via [teaish-configure]. - set vs [get-define TEAISH_VSATISFIES ""] - if {"" eq $vs} return - set code {} - set n 0 - # Treat $vs as a list-of-lists {{Tcl 8.5-} {Foo 1.0- -3.0} ...} - # and generate Tcl which will run package vsatisfies tests with - # that info. - foreach pv $vs { - set n [llength $pv] - if {$n < 2} { - proj-error "-vsatisfies: {$pv} appears malformed. Whole list is: $vs" - } - set pkg [lindex $pv 0] - set vcheck {} - for {set i 1} {$i < $n} {incr i} { - lappend vcheck [lindex $pv $i] - } - if {[opt-bool teaish-vsatisfies-check]} { - set tclsh [get-define TCLSH_CMD] - set vsat "package vsatisfies \[ package provide $pkg \] $vcheck" - set vputs "puts \[ $vsat \]" - #puts "*** vputs = $vputs" - scan [exec echo $vputs | $tclsh] %d vvcheck - if {0 == $vvcheck} { - proj-fatal -up $tclsh "check failed:" $vsat - } - } - lappend code [string trim [subst -nocommands -nobackslashes { -if { ![package vsatisfies [package provide $pkg] $vcheck] } { - if {$::teaish__Config(vsatisfies-error)} { - error {Package $::teaish__PkgInfo(-name) $::teaish__PkgInfo(-version) requires $pv} - } else { - return - } -}}]] - }; # foreach pv - define TEAISH_VSATISFIES_CODE [join $code "\n"] - }}; # vsatisfies - - if {[proj-looks-like-windows]} { - # Without this, linking of an extension will not work on Cygwin or - # Msys2. - msg-result "Using USE_TCL_STUBS for Unix(ish)-on-Windows environment" - teaish-cflags-add -DUSE_TCL_STUBS=1 - } - - #define AS_LIBDIR $::autosetup(libdir) - define TEAISH_TESTUTIL_TCL $::teaish__Config(core-dir)/tester.tcl - - apply {{} { - # - # Ensure we have a pkgIndex.tcl and don't have a stale generated one - # when rebuilding for different --with-tcl=... values. - # - if {!$::teaish__Config(pkgindex-policy)} { - proj-error "Cannot determine which pkgIndex.tcl to use" - } - set tpi [proj-coalesce \ - [get-define TEAISH_PKGINDEX_TCL_IN] \ - [get-define TEAISH_PKGINDEX_TCL]] - proj-assert {$tpi ne ""} \ - "TEAISH_PKGINDEX_TCL should have been set up by now" - teaish__verbose 1 msg-result "Using pkgIndex from $tpi" - if {0x0f & $::teaish__Config(pkgindex-policy)} { - # Don't leave stale pkgIndex.tcl laying around yet don't delete - # or overwrite a user-managed static pkgIndex.tcl. - file delete -force -- [get-define TEAISH_PKGINDEX_TCL] - proj-dot-ins-append [get-define TEAISH_PKGINDEX_TCL_IN] - } else { - teaish-dist-add [file tail $tpi] - } - }}; # $::teaish__Config(pkgindex-policy) - - # - # Ensure we clean up TEAISH_PKGINIT_TCL if needed and @-process - # TEAISH_PKGINIT_TCL_IN if needed. - # - if {0x0f & $::teaish__Config(pkginit-policy)} { - file delete -force -- [get-define TEAISH_PKGINIT_TCL] - proj-dot-ins-append [get-define TEAISH_PKGINIT_TCL_IN] - } - - apply {{} { - # Queue up any remaining dot-in files - set dotIns [list] - foreach d { - TEAISH_TESTER_TCL_IN - TEAISH_TEST_TCL_IN - TEAISH_MAKEFILE_IN - } { - lappend dotIns [get-define $d ""] - } - lappend dotIns $::autosetup(srcdir)/Makefile.in; # must be after TEAISH_MAKEFILE_IN - foreach f $dotIns { - if {"" ne $f} { - proj-dot-ins-append $f - } - } - }} - - define TEAISH_DIST_FULL \ - [expr { - $::teaish__Config(dist-enabled) - && $::teaish__Config(dist-full-enabled) - }] - - define TEAISH_AUTOSETUP_DIR $::teaish__Config(core-dir) - define TEAISH_ENABLE_DIST $::teaish__Config(dist-enabled) - define TEAISH_ENABLE_DLL $::teaish__Config(dll-enabled) - define TEAISH_TCL $::teaish__Config(teaish.tcl) - - define TEAISH_DIST_FILES [join $::teaish__Config(dist-files)] - define TEAISH_EXT_DIR [join $::teaish__Config(extension-dir)] - define TEAISH_EXT_SRC [join $::teaish__Config(extension-src)] - proj-setup-autoreconfig TEAISH_AUTORECONFIG - foreach f { - TEAISH_CFLAGS - TEAISH_LDFLAGS - } { - # Ensure that any of these lists are flattened - define $f [join [get-define $f]] - } - define TEAISH__DEFINES_MAP \ - [teaish__dump_defs_to_list]; # injected into teaish.tester.tcl - proj-remap-autoconf-dir-vars - proj-dot-ins-process -validate; # do not [define] after this point - proj-if-opt-truthy teaish-dump-defines { - make-config-header config.defines.txt \ - -none {TEAISH__* TEAISH_*_CODE} \ - -str { - BIN_* CC LD AR INSTALL LDFLAG* CFLAGS* *_LDFLAGS *_CFLAGS - } \ - -bare {HAVE_*} \ - -auto {*} - } - - # - # If these are set up before call [options], it triggers an - # "option already defined" error. - # - #proj-opt-set teaish.tcl [get-define ] - #proj-opt-set teaish.make.in [get-define ] - - # - # $::autosetup(builddir)/.configured is a workaround to prevent - # concurrent executions of TEAISH_AUTORECONFIG. MUST come last in - # the configure process. - # - #proj-file-write $::autosetup(builddir)/.configured "" -}; # teaish__configure_phase1 - -# -# Run checks for required binaries. -# -proc teaish__check_common_bins {} { - if {"" eq [proj-bin-define install]} { - proj-warn "Cannot find install binary, so 'make install' will not work." - define BIN_INSTALL false - } - if {"" eq [proj-bin-define zip]} { - proj-warn "Cannot find zip, so 'make dist.zip' will not work." - } - if {"" eq [proj-bin-define tar]} { - proj-warn "Cannot find tar, so 'make dist.tgz' will not work." - } -} - -# -# TCL... -# -# teaish__check_tcl performs most of the --with-tcl and --with-tclsh -# handling. Some related bits and pieces are performed before and -# after that function is called. -# -# Important [define]'d vars: -# -# - TCLSH_CMD is the path to the canonical tclsh or "". -# -# - TCL_CONFIG_SH is the path to tclConfig.sh or "". -# -# - TCLLIBDIR is the dir to which the extension library gets -# - installed. -# -proc teaish__check_tcl {} { - define TCLSH_CMD false ; # Significant is that it exits with non-0 - define TCLLIBDIR "" ; # Installation dir for TCL extension lib - define TCL_CONFIG_SH ""; # full path to tclConfig.sh - - # Clear out all vars which would harvest from tclConfig.sh so that - # the late-config validation of @VARS@ works even if --disable-tcl - # is used. - proj-tclConfig-sh-to-autosetup "" - - # TODO: better document the steps this is taking. - set srcdir $::autosetup(srcdir) - msg-result "Checking for a suitable tcl... " - set use_tcl 1 - set withSh [opt-val with-tclsh [proj-get-env TCLSH]] - set tclHome [opt-val with-tcl [proj-get-env TCL_HOME]] - if {[string match */lib $tclHome]} { - # TEA compatibility kludge: its --with-tcl wants the lib - # dir containing tclConfig.sh. - #proj-warn "Replacing --with-tcl=$tclHome for TEA compatibility" - regsub {/lib^} $tclHome "" tclHome - msg-result "NOTE: stripped /lib suffix from --with-tcl=$tclHome (a TEA-ism)" - } - if {0} { - # This misinteracts with the $TCL_PREFIX default: it will use the - # autosetup-defined --prefix default - if {"prefix" eq $tclHome} { - set tclHome [get-define prefix] - } - } - teaish-debug "use_tcl ${use_tcl}" - teaish-debug "withSh=${withSh}" - teaish-debug "tclHome=$tclHome" - if {"" eq $withSh && "" eq $tclHome} { - # If neither --with-tclsh nor --with-tcl are provided, try to find - # a workable tclsh. - set withSh [proj-first-bin-of tclsh9.1 tclsh9.0 tclsh8.6 tclsh] - teaish-debug "withSh=${withSh}" - } - - set doConfigLookup 1 ; # set to 0 to test the tclConfig.sh-not-found cases - if {"" ne $withSh} { - # --with-tclsh was provided or found above. Validate it and use it - # to trump any value passed via --with-tcl=DIR. - if {![file-isexec $withSh]} { - proj-error "TCL shell $withSh is not executable" - } else { - define TCLSH_CMD $withSh - #msg-result "Using tclsh: $withSh" - } - if {$doConfigLookup && - [catch {exec $withSh $::autosetup(libdir)/find_tclconfig.tcl} result] == 0} { - set tclHome $result - } - if {"" ne $tclHome && [file isdirectory $tclHome]} { - teaish__verbose 1 msg-result "$withSh recommends the tclConfig.sh from $tclHome" - } else { - proj-warn "$withSh is unable to recommend a tclConfig.sh" - set use_tcl 0 - } - } - set cfg "" - set tclSubdirs {tcl9.1 tcl9.0 tcl8.6 tcl8.5 lib} - while {$use_tcl} { - if {"" ne $tclHome} { - # Ensure that we can find tclConfig.sh under ${tclHome}/... - if {$doConfigLookup} { - if {[file readable "${tclHome}/tclConfig.sh"]} { - set cfg "${tclHome}/tclConfig.sh" - } else { - foreach i $tclSubdirs { - if {[file readable "${tclHome}/$i/tclConfig.sh"]} { - set cfg "${tclHome}/$i/tclConfig.sh" - break - } - } - } - } - if {"" eq $cfg} { - proj-error "No tclConfig.sh found under ${tclHome}" - } - } else { - # If we have not yet found a tclConfig.sh file, look in $libdir - # which is set automatically by autosetup or via the --prefix - # command-line option. See - # https://sqlite.org/forum/forumpost/e04e693439a22457 - set libdir [get-define libdir] - if {[file readable "${libdir}/tclConfig.sh"]} { - set cfg "${libdir}/tclConfig.sh" - } else { - foreach i $tclSubdirs { - if {[file readable "${libdir}/$i/tclConfig.sh"]} { - set cfg "${libdir}/$i/tclConfig.sh" - break - } - } - } - if {![file readable $cfg]} { - break - } - } - teaish__verbose 1 msg-result "Using tclConfig.sh = $cfg" - break - }; # while {$use_tcl} - define TCL_CONFIG_SH $cfg - # Export a subset of tclConfig.sh to the current TCL-space. If $cfg - # is an empty string, this emits empty-string entries for the - # various options we're interested in. - proj-tclConfig-sh-to-autosetup $cfg - - if {"" eq $withSh && $cfg ne ""} { - # We have tclConfig.sh but no tclsh. Attempt to locate a tclsh - # based on info from tclConfig.sh. - set tclExecPrefix [get-define TCL_EXEC_PREFIX] - proj-assert {"" ne $tclExecPrefix} - set tryThese [list \ - $tclExecPrefix/bin/tclsh[get-define TCL_VERSION] \ - $tclExecPrefix/bin/tclsh ] - foreach trySh $tryThese { - if {[file-isexec $trySh]} { - set withSh $trySh - break - } - } - if {![file-isexec $withSh]} { - proj-warn "Cannot find a usable tclsh (tried: $tryThese)" - } - } - define TCLSH_CMD $withSh - if {$use_tcl} { - # Set up the TCLLIBDIR - set tcllibdir [get-env TCLLIBDIR ""] - set extDirName [get-define TEAISH_LIBDIR_NAME] - if {"" eq $tcllibdir} { - # Attempt to extract TCLLIBDIR from TCL's $auto_path - if {"" ne $withSh && - [catch {exec echo "puts stdout \$auto_path" | "$withSh"} result] == 0} { - foreach i $result { - if {[file isdirectory $i]} { - set tcllibdir $i/$extDirName - break - } - } - } else { - proj-error "Cannot determine TCLLIBDIR." - } - } - define TCLLIBDIR $tcllibdir - }; # find TCLLIBDIR - - if {[file-isexec $withSh]} { - teaish__verbose 1 msg-result "Using tclsh = $withSh" - if {$cfg ne ""} { - define HAVE_TCL 1 - } else { - proj-warn "Found tclsh but no tclConfig.sh." - } - } - show-notices - # If TCL is not found: if it was explicitly requested then fail - # fatally, else just emit a warning. If we can find the APIs needed - # to generate a working JimTCL then that will suffice for build-time - # TCL purposes (see: proc sqlite-determine-codegen-tcl). - if {![file-isexec $withSh]} { - proj-error "Did not find tclsh" - } elseif {"" eq $cfg} { - proj-indented-notice -error { - Cannot find a usable tclConfig.sh file. Use - --with-tcl=DIR to specify a directory near which tclConfig.sh can be - found, or --with-tclsh=/path/to/tclsh to allow the tclsh binary - to locate its tclConfig.sh. - } - } - msg-result "Using Tcl [get-define TCL_VERSION] from [get-define TCL_PREFIX]." - teaish__tcl_platform_quirks -}; # teaish__check_tcl - -# -# Perform last-minute platform-specific tweaks to account for quirks. -# -proc teaish__tcl_platform_quirks {} { - define TEAISH_POSTINST_PREREQUIRE "" - switch -glob -- [get-define host] { - *-haiku { - # Haiku's default TCLLIBDIR is "all wrong": it points to a - # read-only virtual filesystem mount-point. We bend it back to - # fit under $TCL_PACKAGE_PATH here. - foreach {k d} { - vj TCL_MAJOR_VERSION - vn TCL_MINOR_VERSION - pp TCL_PACKAGE_PATH - ld TCLLIBDIR - } { - set $k [get-define $d] - } - if {[string match /packages/* $ld]} { - set old $ld - set tail [file tail $ld] - if {8 == $vj} { - set ld "${pp}/tcl${vj}.${vn}/${tail}" - } else { - proj-assert {9 == $vj} - set ld "${pp}/${tail}" - } - define TCLLIBDIR $ld - # [load foo.so], without a directory part, does not work via - # automated tests on Haiku (but works when run - # manually). Similarly, the post-install [package require ...] - # test fails, presumably for a similar reason. We work around - # the former in teaish.tester.tcl.in. We work around the - # latter by amending the post-install check's ::auto_path (in - # Makefile.in). This code MUST NOT contain any single-quotes. - define TEAISH_POSTINST_PREREQUIRE \ - [join [list set ::auto_path \ - \[ linsert \$::auto_path 0 $ld \] \; \ - ]] - proj-indented-notice [subst -nocommands -nobackslashes { - Haiku users take note: patching target installation dir to match - Tcl's home because Haiku's is not writable. - - Original : $old - Substitute: $ld - }] - } - } - } -}; # teaish__tcl_platform_quirks - -# -# Searches $::argv and/or the build dir and/or the source dir for -# teaish.tcl and friends. Fails if it cannot find teaish.tcl or if -# there are other irreconcilable problems. If it returns 0 then it did -# not find an extension but the --help flag was seen, in which case -# that's not an error. -# -# This does not _load_ the extension, it primarily locates the files -# which make up an extension and fills out no small amount of teaish -# state related to that. -# -proc teaish__find_extension {} { - proj-assert {!$::teaish__Config(install-mode)} - teaish__verbose 1 msg-result "Looking for teaish extension..." - - # Helper for the foreach loop below. - set checkTeaishTcl {{mustHave fid dir} { - if {[file isdirectory $dir]} { - set f [file join $dir $fid] - if {[file readable $f]} { - return [file-normalize $f] - } elseif {$mustHave} { - proj-error "Missing required $dir/$fid" - } - } elseif {$mustHave} { - proj-error "--teaish-extension-dir=$dir does not reference a directory" - } - return "" - }} - - # - # We have to handle some flags manually because the extension must - # be loaded before [options] is run (so that the extension can - # inject its own options). - # - set dirBld $::autosetup(builddir); # dir we're configuring under - set dirSrc $::autosetup(srcdir); # where teaish's configure script lives - set extT ""; # teaish.tcl - set largv {}; # rewritten $::argv - set gotHelpArg 0; # got the --help - foreach arg $::argv { - #puts "*** arg=$arg" - switch -glob -- $arg { - --ted=* - - --t-e-d=* - - --teaish-extension-dir=* { - # Ensure that $extD refers to a directory and contains a - # teaish.tcl. - regexp -- {--[^=]+=(.+)} $arg - extD - set extD [file-normalize $extD] - if {![file isdirectory $extD]} { - proj-error "--teaish-extension-dir value is not a directory: $extD" - } - set extT [apply $checkTeaishTcl 1 teaish.tcl $extD] - set ::teaish__Config(extension-dir) $extD - } - --help { - incr gotHelpArg - lappend largv $arg - } - default { - lappend largv $arg - } - } - } - set ::argv $largv - - set dirExt $::teaish__Config(extension-dir); # dir with the extension - # - # teaish.tcl is a TCL script which implements various - # interfaces described by this framework. - # - # We use the first one we find in the builddir or srcdir. - # - if {"" eq $extT} { - set flist [list] - proj-assert {$dirExt eq ""} - lappend flist $dirBld/teaish.tcl $dirSrc/teaish.tcl - if {![proj-first-file-found extT $flist]} { - if {$gotHelpArg} { - # Tell teaish-configure-core that the lack of extension is not - # an error when --help or --teaish-install is used. - return 0; - } - proj-indented-notice -error " -Did not find any of: $flist - -If you are attempting an out-of-tree build, use - --teaish-extension-dir=/path/to/extension" - } - } - if {![file readable $extT]} { - proj-error "extension tcl file is not readable: $extT" - } - set ::teaish__Config(teaish.tcl) $extT - set dirExt [file dirname $extT] - - set ::teaish__Config(extension-dir) $dirExt - set ::teaish__Config(blddir-is-extdir) [expr {$dirBld eq $dirExt}] - set ::teaish__Config(dist-enabled) $::teaish__Config(blddir-is-extdir); # may change later - set ::teaish__Config(dist-full-enabled) \ - [expr {[file-normalize $::autosetup(srcdir)] - eq [file-normalize $::teaish__Config(extension-dir)]}] - - set addDist {{file} { - teaish-dist-add [file tail $file] - }} - apply $addDist $extT - - teaish__verbose 1 msg-result "Extension dir = [teaish-get -dir]" - teaish__verbose 1 msg-result "Extension config = $extT" - - teaish-pkginfo-set -name [file tail [file dirname $extT]] - - # - # teaish.make[.in] provides some of the info for the main makefile, - # like which source(s) to build and their build flags. - # - # We use the first one of teaish.make.in or teaish.make we find in - # $dirExt. - # - if {[proj-first-file-found extM \ - [list \ - $dirExt/teaish.make.in \ - $dirExt/teaish.make \ - ]]} { - if {[string match *.in $extM]} { - define TEAISH_MAKEFILE_IN $extM - define TEAISH_MAKEFILE [file rootname [file tail $extM]] - } else { - define TEAISH_MAKEFILE_IN "" - define TEAISH_MAKEFILE $extM - } - apply $addDist $extM - teaish__verbose 1 msg-result "Extension makefile = $extM" - } else { - define TEAISH_MAKEFILE_IN "" - define TEAISH_MAKEFILE "" - } - - # Look for teaish.pkginit.tcl[.in] - set piPolicy 0 - if {[proj-first-file-found extI \ - [list \ - $dirExt/teaish.pkginit.tcl.in \ - $dirExt/teaish.pkginit.tcl \ - ]]} { - if {[string match *.in $extI]} { - # Generate teaish.pkginit.tcl from $extI. - define TEAISH_PKGINIT_TCL_IN $extI - define TEAISH_PKGINIT_TCL [file rootname [file tail $extI]] - set piPolicy 0x01 - } else { - # Assume static $extI. - define TEAISH_PKGINIT_TCL_IN "" - define TEAISH_PKGINIT_TCL $extI - set piPolicy 0x10 - } - apply $addDist $extI - teaish__verbose 1 msg-result "Extension post-load init = $extI" - define TEAISH_PKGINIT_TCL_TAIL \ - [file tail [get-define TEAISH_PKGINIT_TCL]]; # for use in pkgIndex.tcl.in - } - set ::teaish__Config(pkginit-policy) $piPolicy - - # Look for pkgIndex.tcl[.in]... - set piPolicy 0 - if {[proj-first-file-found extPI $dirExt/pkgIndex.tcl.in]} { - # Generate ./pkgIndex.tcl from $extPI. - define TEAISH_PKGINDEX_TCL_IN $extPI - define TEAISH_PKGINDEX_TCL [file rootname [file tail $extPI]] - apply $addDist $extPI - set piPolicy 0x01 - } elseif {$dirExt ne $dirSrc - && [proj-first-file-found extPI $dirSrc/pkgIndex.tcl.in]} { - # Generate ./pkgIndex.tcl from $extPI. - define TEAISH_PKGINDEX_TCL_IN $extPI - define TEAISH_PKGINDEX_TCL [file rootname [file tail $extPI]] - set piPolicy 0x02 - } elseif {[proj-first-file-found extPI $dirExt/pkgIndex.tcl]} { - # Assume $extPI's a static file and use it. - define TEAISH_PKGINDEX_TCL_IN "" - define TEAISH_PKGINDEX_TCL $extPI - apply $addDist $extPI - set piPolicy 0x10 - } - # Reminder: we have to delay removal of stale TEAISH_PKGINDEX_TCL - # and the proj-dot-ins-append of TEAISH_PKGINDEX_TCL_IN until much - # later in the process. - set ::teaish__Config(pkgindex-policy) $piPolicy - - # Look for teaish.test.tcl[.in] - proj-assert {"" ne $dirExt} - set flist [list $dirExt/teaish.test.tcl.in $dirExt/teaish.test.tcl] - if {[proj-first-file-found ttt $flist]} { - if {[string match *.in $ttt]} { - # Generate teaish.test.tcl from $ttt - set xt [file rootname [file tail $ttt]] - file delete -force -- $xt; # ensure no stale copy is used - define TEAISH_TEST_TCL $xt - define TEAISH_TEST_TCL_IN $ttt - } else { - define TEAISH_TEST_TCL $ttt - define TEAISH_TEST_TCL_IN "" - } - apply $addDist $ttt - } else { - define TEAISH_TEST_TCL "" - define TEAISH_TEST_TCL_IN "" - } - - # Look for teaish.tester.tcl[.in] - set flist [list $dirExt/teaish.tester.tcl.in $dirSrc/teaish.tester.tcl.in] - if {[proj-first-file-found ttt $flist]} { - # Generate teaish.test.tcl from $ttt - set xt [file rootname [file tail $ttt]] - file delete -force -- $xt; # ensure no stale copy is used - define TEAISH_TESTER_TCL $xt - define TEAISH_TESTER_TCL_IN $ttt - if {[lindex $flist 0] eq $ttt} { - apply $addDist $ttt - } - unset ttt xt - } else { - if {[file exists [set ttt [file join $dirSrc teaish.tester.tcl.in]]]} { - set xt [file rootname [file tail $ttt]] - define TEAISH_TESTER_TCL $xt - define TEAISH_TESTER_TCL_IN $ttt - } else { - define TEAISH_TESTER_TCL "" - define TEAISH_TESTER_TCL_IN "" - } - } - unset flist - - # TEAISH_OUT_OF_EXT_TREE = 1 if we're building from a dir other - # than the extension's home dir. - define TEAISH_OUT_OF_EXT_TREE \ - [expr {[file-normalize $::autosetup(builddir)] ne \ - [file-normalize $::teaish__Config(extension-dir)]}] - return 1 -}; # teaish__find_extension - -# -# @teaish-cflags-add ?-p|prepend? ?-define? cflags... -# -# Equivalent to [proj-define-amend TEAISH_CFLAGS {*}$args]. -# -proc teaish-cflags-add {args} { - proj-define-amend TEAISH_CFLAGS {*}$args -} - -# -# @teaish-define-to-cflag ?flags? defineName...|{defineName...} -# -# Uses [proj-define-to-cflag] to expand a list of [define] keys, each -# one a separate argument, to CFLAGS-style -D... form then appends -# that to the current TEAISH_CFLAGS. -# -# It accepts these flags from proj-define-to-cflag: -quote, -# -zero-undef. It does _not_ support its -list flag. -# -# It accepts its non-flag argument(s) in 2 forms: (1) each arg is a -# single [define] key or (2) its one arg is a list of such keys. -# -# TODO: document teaish's well-defined (as it were) defines for this -# purpose. At a bare minimum: -# -# - TEAISH_NAME -# - TEAISH_PKGNAME -# - TEAISH_VERSION -# - TEAISH_LIBDIR_NAME -# - TEAISH_LOAD_PREFIX -# - TEAISH_URL -# -proc teaish-define-to-cflag {args} { - set flags {} - while {[string match -* [lindex $args 0]]} { - set arg [lindex $args 0] - switch -exact -- $arg { - -quote - - -zero-undef { - lappend flags $arg - set args [lassign $args -] - } - default break - } - } - if {1 == [llength $args]} { - set args [list {*}[lindex $args 0]] - } - #puts "***** flags=$flags args=$args" - teaish-cflags-add [proj-define-to-cflag {*}$flags {*}$args] -} - -# -# @teaish-cflags-for-tea ?...CFLAGS? -# -# Adds several -DPACKAGE_... CFLAGS using the extension's metadata, -# all as quoted strings. Those symbolic names are commonly used in -# TEA-based builds, and this function is intended to simplify porting -# of such builds. The -D... flags added are: -# -# -DPACKAGE_VERSION=... -# -DPACKAGE_NAME=... -# -DPACKAGE_URL=... -# -DPACKAGE_STRING=... -# -# Any arguments are passed-on as-is to teaish-cflags-add. -# -proc teaish-cflags-for-tea {args} { - set name $::teaish__PkgInfo(-name) - set version $::teaish__PkgInfo(-version) - set pstr [join [list $name $version]] - teaish-cflags-add \ - {*}$args \ - '-DPACKAGE_VERSION="$version"' \ - '-DPACKAGE_NAME="$name"' \ - '-DPACKAGE_STRING="$pstr"' \ - '-DPACKAGE_URL="[teaish-get -url]"' -} - -# -# @teaish-ldflags-add ?-p|-prepend? ?-define? ldflags... -# -# Equivalent to [proj-define-amend TEAISH_LDFLAGS {*}$args]. -# -# Typically, -lXYZ flags need to be in "reverse" order, with each -lY -# resolving symbols for -lX's to its left. This order is largely -# historical, and not relevant on all environments, but it is -# technically correct and still relevant on some environments. -# -# See: teaish-ldflags-prepend -# -proc teaish-ldflags-add {args} { - proj-define-amend TEAISH_LDFLAGS {*}$args -} - -# -# @teaish-ldflags-prepend args... -# -# Functionally equivalent to [teaish-ldflags-add -p {*}$args] -# -proc teaish-ldflags-prepend {args} { - teaish-ldflags-add -p {*}$args -} - -# -# @teaish-src-add ?-dist? ?-dir? src-files... -# -# Appends all non-empty $args to the project's list of C/C++ source or -# (in some cases) object files. -# -# If passed -dist then it also passes each filename, as-is, to -# [teaish-dist-add]. -# -# If passed -dir then each src-file has [teaish-get -dir] prepended to -# it before they're added to the list. As often as not, that will be -# the desired behavior so that out-of-tree builds can find the -# sources, but there are cases where it's not desired (e.g. when using -# a source file from outside of the extension's dir, or when adding -# object files (which are typically in the build tree)). -# -proc teaish-src-add {args} { - set i 0 - proj-parse-simple-flags args flags { - -dist 0 {expr 1} - -dir 0 {expr 1} - } - if {$flags(-dist)} { - teaish-dist-add {*}$args - } - if {$flags(-dir)} { - set xargs {} - foreach arg $args { - if {"" ne $arg} { - lappend xargs [file join $::teaish__Config(extension-dir) $arg] - } - } - set args $xargs - } - lappend ::teaish__Config(extension-src) {*}$args -} - -# -# @teaish-dist-add files-or-dirs... -# -# Adds the given files to the list of files to include with the "make -# dist" rules. -# -# This is a no-op when the current build is not in the extension's -# directory, as dist support is disabled in out-of-tree builds. -# -# It is not legal to call this until [teaish-get -dir] has been -# reliably set (via teaish__find_extension). -# -proc teaish-dist-add {args} { - if {$::teaish__Config(blddir-is-extdir)} { - # ^^^ reminder: we ignore $::teaish__Config(dist-enabled) here - # because the client might want to implement their own dist - # rules. - #proj-warn "**** args=$args" - lappend ::teaish__Config(dist-files) {*}$args - } -} - -# teaish-install-add files... -# Equivalent to [proj-define-apend TEAISH_INSTALL_FILES ...]. -#proc teaish-install-add {args} { -# proj-define-amend TEAISH_INSTALL_FILES {*}$args -#} - -# -# @teash-make-add args... -# -# Appends makefile code to the TEAISH_MAKEFILE_CODE define. Each -# arg may be any of: -# -# -tab: emit a literal tab -# -nl: emit a literal newline -# -nltab: short for -nl -tab -# -bnl: emit a backslash-escaped end-of-line -# -bnltab: short for -eol -tab -# -# Anything else is appended verbatim. This function adds no additional -# spacing between each argument nor between subsequent invocations. -# Generally speaking, a series of calls to this function need to -# be sure to end the series with a newline. -proc teaish-make-add {args} { - set out [get-define TEAISH_MAKEFILE_CODE ""] - foreach a $args { - switch -exact -- $a { - -bnl { set a " \\\n" } - -bnltab { set a " \\\n\t" } - -tab { set a "\t" } - -nl { set a "\n" } - -nltab { set a "\n\t" } - } - append out $a - } - define TEAISH_MAKEFILE_CODE $out -} - -# Internal helper to generate a clean/distclean rule name -proc teaish__cleanup_rule {{tgt clean}} { - set x [incr ::teaish__Config(teaish__cleanup_rule-counter-${tgt})] - return ${tgt}-_${x}_ -} - -# @teaish-make-obj objfile srcfile ?...args? -# -# Uses teaish-make-add to inject makefile rules for $objfile from -# $srcfile, which is assumed to be C code which uses libtcl. Unless -# -recipe is used (see below) it invokes the compiler using the -# makefile-defined $(CC.tcl) which, in the default Makefile.in -# template, includes any flags needed for building against the -# configured Tcl. -# -# This always terminates the resulting code with a newline. -# -# Any arguments after the 2nd may be flags described below or, if no -# -recipe is provided, flags for the compiler call. -# -# -recipe {...} -# Uses the trimmed value of {...} as the recipe, prefixing it with -# a single hard-tab character. -# -# -deps {...} -# List of extra files to list as dependencies of $o. Good luck -# escaping non-trivial cases properly. -# -# -clean -# Generate cleanup rules as well. -proc teaish-make-obj {o src args} { - set consume 0 - set clean 0 - set flag "" - array set flags {} - set xargs {} - foreach arg $args { - if {$consume} { - set consume 0 - set flags($flag) $arg - continue - } - switch -exact -- $arg { - -clean {incr clean} - -recipe - - -deps { - set flag $arg - incr consume - } - default { - lappend xargs $arg - } - } - } - teaish-make-add \ - "# [proj-scope 1] -> [proj-scope] $o $src" -nl \ - "$o: $src $::teaish__Config(teaish.tcl)" - if {[info exists flags(-deps)]} { - teaish-make-add " " [join $flags(-deps)] - } - teaish-make-add -nltab - if {[info exists flags(-recipe)]} { - teaish-make-add [string trim $flags(-recipe)] -nl - } else { - teaish-make-add [join [list \$(CC.tcl) -c $src {*}$xargs]] -nl - } - if {$clean} { - set rule [teaish__cleanup_rule] - teaish-make-add \ - "clean: $rule\n$rule:\n\trm -f \"$o\"\n" - } -} - -# -# @teaish-make-clean ?-r? ?-dist? ...files|{...files} -# -# Adds makefile rules for cleaning up the given files via the "make -# clean" or (if -dist is used) "make distclean" makefile rules. The -r -# flag uses "rm -fr" instead of "rm -f", so be careful with that. -# -# The file names are taken literally as arguments to "rm", so they may -# be shell wildcards to be resolved at cleanup-time. To clean up whole -# directories, pass the -r flag. Each name gets quoted in -# double-quotes, so spaces in names should not be a problem (but -# double-quotes in names will be). -# -proc teaish-make-clean {args} { - if {1 == [llength $args]} { - set args [list {*}[lindex $args 0]] - } - - set tgt clean - set rmflags "-f" - proj-parse-simple-flags args flags { - -dist 0 { - set tgt distclean - } - -r 0 { - set rmflags "-fr" - } - } - set rule [teaish__cleanup_rule $tgt] - teaish-make-add "# [proj-scope 1] -> [proj-scope]: [join $args]\n" - teaish-make-add "${rule}:\n\trm ${rmflags}" - foreach a $args { - teaish-make-add " \"$a\"" - } - teaish-make-add "\n${tgt}: ${rule}\n" -} - -# -# @teaish-make-config-header filename -# -# Invokes autosetup's [make-config-header] and passes it $filename and -# a relatively generic list of options for controlling which defined -# symbols get exported. Clients which need more control over the -# exports can copy/paste/customize this. -# -# The exported file is then passed to [proj-touch] because, in -# practice, that's sometimes necessary to avoid build dependency -# issues. -# -proc teaish-make-config-header {filename} { - make-config-header $filename \ - -none {HAVE_CFLAG_* LDFLAGS_* SH_* TEAISH__* TEAISH_*_CODE} \ - -auto {SIZEOF_* HAVE_* TEAISH_* TCL_*} \ - -none * - proj-touch $filename; # help avoid frequent unnecessary auto-reconfig -} - -# -# @teaish-feature-cache-set ?$key? value -# -# Sets a feature-check cache entry with the given key. -# See proj-cache-set for the key's semantics. -# -proc teaish-feature-cache-set {{key 0} val} { - proj-cache-set $key 1 $val -} - -# -# @teaish-feature-cache-check ?$key? tgtVarName -# -# Checks for a feature-check cache entry with the given key. -# See proj-cache-set for the key's semantics. -# -# If the feature-check cache has a matching entry then this function -# assigns its value to tgtVar and returns 1, else it assigns tgtVar to -# "" and returns 0. -# -# See proj-cache-check for $key's semantics. -# -proc teaish-feature-cache-check {{key 0} tgtVar} { - upvar $tgtVar tgt - proj-cache-check $key 1 tgt -} - -# -# @teaish-check-cached@ ?-nostatus? msg script -# -# A proxy for feature-test impls which handles caching of a feature -# flag check on per-function basis, using the calling scope's name as -# the cache key. -# -# It emits [msg-checking $msg]. If $msg is empty then it defaults to -# the name of the caller's scope. At the end, it will [msg-result "ok"] -# [msg-result "no"] unless -nostatus is used, in which case the caller -# is responsible for emitting at least a newline when it's done. -# -# This function checks for a cache hit before running $script and -# caching the result. If no hit is found then $script is run in the -# calling scope and its result value is stored in the cache. This -# routine will intercept a 'return' from $script. -# -# Flags: -# -# -nostatus = do not emit "ok" or "no" at the end. This presumes -# that the caller will emit at least one newline before turning. -# -proc teaish-check-cached {args} { - proj-parse-simple-flags args flags { - -nostatus 0 {expr 1} - } - lassign $args msg script - if {"" eq $msg} { - set msg [proj-scope 1] - } - msg-checking "${msg} ... " - if {[teaish-feature-cache-check 1 check]} { - msg-checking "(cached) " - if {$check} {msg-result "ok"} else {msg-result "no"} - return $check - } else { - set code [catch {uplevel 1 $script} rc xopt] - #puts "***** cached-check got code=$code rc=$rc" - if {$code in {0 2}} { - teaish-feature-cache-set 1 $rc - if {!$flags(-nostatus)} { - if {$rc} { - msg-result "ok" - } else { - msg-result "no" - } - } - } else { - #puts "**** code=$code rc=$rc xopt=$xopt" - teaish-feature-cache-set 1 0 - } - #puts "**** code=$code rc=$rc" - return {*}$xopt $rc - } -} - -# -# Internal helper for teaish__defs_format_: returns a JSON-ish quoted -# form of the given string-type values. -# -# If $asList is true then the return value is in {$value} form. If -# $asList is false it only performs the most basic of escaping and -# the input must not contain any control characters. -# -proc teaish__quote_str {asList value} { - if {$asList} { - return [join [list "\{" $value "\}"] ""] - } - return \"[string map [list \\ \\\\ \" \\\"] $value]\" -} - -# -# Internal helper for teaish__dump_defs_to_list. Expects to be passed -# a name and the variadic $args which are passed to -# teaish__dump_defs_to_list.. If it finds a pattern match for the -# given $name in the various $args, it returns the type flag for that -# $name, e.g. "-str" or "-bare", else returns an empty string. -# -proc teaish__defs_type {name spec} { - foreach {type patterns} $spec { - foreach pattern $patterns { - if {[string match $pattern $name]} { - return $type - } - } - } - return "" -} - -# -# An internal impl detail. Requires a data type specifier, as used by -# make-config-header, and a value. Returns the formatted value or the -# value $::teaish__Config(defs-skip) if the caller should skip -# emitting that value. -# -# In addition to -str, -auto, etc., as defined by make-config-header, -# it supports: -# -# -list {...} will cause non-integer values to be quoted in {...} -# instead of quotes. -# -# -autolist {...} works like -auto {...} except that it falls back to -# -list {...} type instead of -str {...} style for non-integers. -# -# -array {...} emits the output in something which, for conservative -# inputs, will be a valid JSON array. It can only handle relatively -# simple values with no control characters in them. -# -set teaish__Config(defs-skip) "-teaish__defs_format sentinel" -proc teaish__defs_format {type value} { - switch -exact -- $type { - -bare { - # Just output the value unchanged - } - -none { - set value $::teaish__Config(defs-skip) - } - -str { - set value [teaish__quote_str 0 $value] - } - -auto { - # Automatically determine the type - if {![string is integer -strict $value]} { - set value [teaish__quote_str 0 $value] - } - } - -autolist { - if {![string is integer -strict $value]} { - set value [teaish__quote_str 1 $value] - } - } - -list { - set value [teaish__quote_str 1 $value] - } - -array { - set ar {} - foreach v $value { - set v [teaish__defs_format -auto $v] - if {$::teaish__Config(defs-skip) ne $v} { - lappend ar $v - } - } - set value "\[ [join $ar {, }] \]" - } - "" { - set value $::teaish__Config(defs-skip) - } - default { - proj-error \ - "Unknown [project-current-scope] -type ($type) called from" \ - [proj-scope 1] - } - } - return $value -} - -# -# Returns Tcl code in the form of code which evaluates to a list of -# configure-time DEFINEs in the form {key val key2 val...}. It may -# misbehave for values which are not numeric or simple strings. -# -proc teaish__dump_defs_to_list {args} { - set lines {} - lappend lines "\{" - set skipper $::teaish__Config(defs-skip) - lappend args \ - -none { - TEAISH__* - TEAISH_MAKEFILE_CODE - AM_* AS_* - } \ - -auto { - SIZEOF_* HAVE_* - } \ - -autolist * - foreach n [lsort [dict keys [all-defines]]] { - set type [teaish__defs_type $n $args] - set value [teaish__defs_format $type [get-define $n]] - if {$skipper ne $value} { - lappend lines "$n $value" - } - } - lappend lines "\}" - return [join $lines "\n"] -} - -# -# teaish__pragma ...flags -# -# Offers a way to tweak how teaish's core behaves in some cases, in -# particular those which require changing how the core looks for an -# extension and its files. -# -# Accepts the following flags. Those marked with [L] are safe to use -# during initial loading of tclish.tcl (recall that most teaish APIs -# cannot be used until [teaish-configure] is called). -# -# static-pkgIndex.tcl [L]: Tells teaish that ./pkgIndex.tcl is not -# a generated file, so it will not try to overwrite or delete -# it. Errors out if it does not find pkgIndex.tcl in the -# extension's dir. -# -# no-dist [L]: tells teaish to elide the 'make dist' recipe -# from the generated Makefile. -# -# no-dll [L]: tells teaish to elide the DLL-building recipe -# from the generated Makefile. -# -# no-vsatisfies-error [L]: tells teaish that failure to match the -# -vsatisfies value should simply "return" instead of "error". -# -# no-tester [L]: disables automatic generation of teaish.test.tcl -# even if a copy of teaish.tester.tcl.in is found. -# -# no-full-dist [L]: changes the "make dist" rules to never include -# a copy of teaish itself. By default it will include itself only -# if the extension lives in the same directory as teaish. -# -# full-dist [L]: changes the "make dist" rules to always include -# a copy of teaish itself. -# -# Emits a warning message for unknown arguments. -# -proc teaish__pragma {args} { - foreach arg $args { - switch -exact -- $arg { - - static-pkgIndex.tcl { - set tpi [file join $::teaish__Config(extension-dir) pkgIndex.tcl] - if {[file exists $tpi]} { - define TEAISH_PKGINDEX_TCL_IN "" - define TEAISH_PKGINDEX_TCL $tpi - set ::teaish__Config(pkgindex-policy) 0x20 - } else { - proj-error "$arg: found no package-local pkgIndex.tcl\[.in]" - } - } - - no-dist { - set ::teaish__Config(dist-enabled) 0 - } - - full-dist { - set ::teaish__Config(dist-full-enabled) 1 - } - - no-full-dist { - set ::teaish__Config(dist-full-enabled) 0 - } - - no-dll { - set ::teaish__Config(dll-enabled) 0 - } - - no-vsatisfies-error { - set ::teaish__Config(vsatisfies-error) 0 - } - - no-tester { - define TEAISH_TESTER_TCL_IN "" - define TEAISH_TESTER_TCL "" - } - - default { - proj-error "Unknown flag: $arg" - } - } - } -} - -# -# @teaish-pkginfo-set ...flags -# -# The way to set up the initial package state. Used like: -# -# teaish-pkginfo-set -name foo -version 0.1.2 -# -# Or: -# -# teaish-pkginfo-set ?-vars|-subst? {-name foo -version 0.1.2} -# -# The latter may be easier to write for a multi-line invocation. -# -# For the second call form, passing the -vars flag tells it to perform -# a [subst] of (only) variables in the {...} part from the calling -# scope. The -subst flag will cause it to [subst] the {...} with -# command substitution as well (but no backslash substitution). When -# using -subst for string concatenation, e.g. with -libDir -# foo[get-version-number], be sure to wrap the value in braces: -# -libDir {foo[get-version-number]}. -# -# Each pkginfo flag corresponds to one piece of extension package -# info. Teaish provides usable default values for all of these flags, -# but at least the -name and -version should be set by clients. -# e.g. the default -name is the directory name the extension lives in, -# which may change (e.g. when building it from a "make dist" bundle). -# -# The flags: -# -# -name theName: The extension's name. It defaults to the name of the -# directory containing the extension. (In TEA this would be the -# PACKAGE_NAME, not to be confused with...) -# -# -name.pkg pkg-provide-name: The extension's name for purposes of -# Tcl_PkgProvide(), [package require], and friends. It defaults to -# the `-name`, and is normally the same, but some projects (like -# SQLite) have a different name here than they do in their -# historical TEA PACKAGE_NAME. -# -# -version version: The extension's package version. Defaults to -# 0.0.0. -# -# -libDir dirName: The base name of the directory into which this -# extension should be installed. It defaults to a concatenation of -# `-name.pkg` and `-version`. -# -# -loadPrefix prefix: For use as the second argument passed to -# Tcl's `load` command in the package-loading process. It defaults -# to title-cased `-name.pkg` because Tcl's `load` plugin system -# expects it in that form. -# -# -options {...}: If provided, it must be a list compatible with -# Autosetup's `options-add` function. These can also be set up via -# `teaish-options`. -# -# -vsatisfies {{...} ...}: Expects a list-of-lists of conditions -# for Tcl's `package vsatisfies` command: each list entry is a -# sub-list of `{PkgName Condition...}`. Teaish inserts those -# checks via its default pkgIndex.tcl.in and teaish.tester.tcl.in -# templates to verify that the system's package dependencies meet -# these requirements. The default value is `{{Tcl 8.5-}}` (recall -# that it's a list-of-lists), as 8.5 is the minimum Tcl version -# teaish will run on, but some extensions may require newer -# versions or dependencies on other packages. As a special case, -# if `-vsatisfies` is given a single token, e.g. `8.6-`, then it -# is transformed into `{Tcl $thatToken}`, i.e. it checks the Tcl -# version which the package is being run with. If given multiple -# lists, each `package provides` check is run in the given -# order. Failure to meet a `vsatisfies` condition triggers an -# error. -# -# -url {...}: an optional URL for the extension. -# -# -pragmas {...} A list of infrequently-needed lower-level -# directives which can influence teaish, including: -# -# static-pkgIndex.tcl: tells teaish that the client manages their -# own pkgIndex.tcl, so that teaish won't try to overwrite it -# using a template. -# -# no-dist: tells teaish to elide the "make dist" recipe from the -# makefile so that the client can implement it. -# -# no-dll: tells teaish to elide the makefile rules which build -# the DLL, as well as any templated test script and pkgIndex.tcl -# references to them. The intent here is to (A) support -# client-defined build rules for the DLL and (B) eventually -# support script-only extensions. -# -# Unsupported flags or pragmas will trigger an error. -# -# Potential pothole: setting certain state, e.g. -version, after the -# initial call requires recalculating of some [define]s. Any such -# changes should be made as early as possible in teaish-configure so -# that any later use of those [define]s gets recorded properly (not -# with the old value). This is particularly relevant when it is not -# possible to determine the -version or -name until teaish-configure -# has been called, and it's updated dynamically from -# teaish-configure. Notably: -# -# - If -version or -name are updated, -libDir will almost certainly -# need to be explicitly set along with them. -# -# - If -name is updated, -loadPrefix probably needs to be as well. -# -proc teaish-pkginfo-set {args} { - set doVars 0 - set doCommands 0 - set xargs $args - set recalc {} - foreach arg $args { - switch -exact -- $arg { - -vars { - incr doVars - set xargs [lassign $xargs -] - } - -subst { - incr doVars - incr doCommands - set xargs [lassign $xargs -] - } - default { - break - } - } - } - set args $xargs - unset xargs - if {1 == [llength $args] && [llength [lindex $args 0]] > 1} { - # Transform a single {...} arg into the canonical call form - set a [list {*}[lindex $args 0]] - if {$doVars || $doCommands} { - set sflags -nobackslashes - if {!$doCommands} { - lappend sflags -nocommands - } - set a [uplevel 1 [list subst {*}$sflags $a]] - } - set args $a - } - set sentinel "<nope>" - set flagDefs [list] - foreach {f d} $::teaish__Config(pkginfo-f2d) { - lappend flagDefs $f => $sentinel - } - proj-parse-simple-flags args flags $flagDefs - if {[llength $args]} { - proj-error -up "Too many (or unknown) arguments to [proj-scope]: $args" - } - foreach {f d} $::teaish__Config(pkginfo-f2d) { - if {$sentinel eq [set v $flags($f)]} continue - switch -exact -- $f { - -options { - proj-assert {"" eq $d} - options-add $v - } - -pragmas { - foreach p $v { - teaish__pragma $p - } - } - -vsatisfies { - if {1 == [llength $v] && 1 == [llength [lindex $v 0]]} { - # Transform X to {Tcl $X} - set v [list [join [list Tcl $v]]] - } - define $d $v - } - -pkgInit.tcl.in { - # Generate pkginit file X from X.in - set ::teaish__Config(pkginit-policy) 0x02 - set x [file join $::teaish__Config(extension-dir) $v] - define TEAISH_PKGINIT_TCL_IN $x - set fout [file rootname [file tail $v]] - define TEAISH_PKGINIT_TCL $fout - define TEAISH_PKGINIT_TCL_TAIL $fout - set ::teaish__PkgInfo(-pkgInit.tcl) {} - teaish-dist-add $v - set v $x - } - -pkgInit.tcl { - # Static pkginit file X - set ::teaish__Config(pkginit-policy) 0x20 - set x [file join $::teaish__Config(extension-dir) $v] - define TEAISH_PKGINIT_TCL $x - define TEAISH_PKGINIT_TCL_IN "" - define TEAISH_PKGINIT_TCL_TAIL [file tail $v] - set ::teaish__PkgInfo(-pkgInit.tcl.in) {} - teaish-dist-add $v - set v $x - } - default { - define $d $v - } - } - set ::teaish__PkgInfo($f) $v - if {$f in {-name -version -libDir -loadPrefix}} { - lappend recalc $f - } - } - if {"" ne $recalc} { - teaish__define_pkginfo_derived $recalc - } -} - -# -# @teaish-pkginfo-get ?arg? -# -# If passed no arguments, it returns the extension config info in the -# same form accepted by teaish-pkginfo-set. -# -# If passed one -flagname arg then it returns the value of that config -# option. -# -# Else it treats arg as the name of caller-scoped variable to -# which this function assigns an array containing the configuration -# state of this extension, in the same structure accepted by -# teaish-pkginfo-set. In this case it returns an empty string. -# -proc teaish-pkginfo-get {args} { - set cases {} - set argc [llength $args] - set rv {} - switch -exact $argc { - 0 { - # Return a list of (-flag value) pairs - lappend cases default {{ - if {[info exists ::teaish__PkgInfo($flag)]} { - lappend rv $flag $::teaish__PkgInfo($flag) - } else { - lappend rv $flag [get-define $defName] - } - }} - } - - 1 { - set arg $args - if {[string match -* $arg]} { - # Return the corresponding -flag's value - lappend cases $arg {{ - if {[info exists ::teaish__PkgInfo($flag)]} { - return $::teaish__PkgInfo($flag) - } else { - return [get-define $defName] - } - }} - } else { - # Populate target with an array of (-flag value). - upvar $arg tgt - array set tgt {} - lappend cases default {{ - if {[info exists ::teaish__PkgInfo($flag)]} { - set tgt($flag) $::teaish__PkgInfo($flag) - } else { - set tgt($flag) [get-define $defName] - } - }} - } - } - - default { - proj-error "invalid arg count from [proj-scope 1]" - } - } - - foreach {flag defName} $::teaish__Config(pkginfo-f2d) { - switch -exact -- $flag [join $cases] - } - if {0 == $argc} { return $rv } -} - -# (Re)set some defines based on pkginfo state. $flags is the list of -# pkginfo -flags which triggered this, or "*" for the initial call. -proc teaish__define_pkginfo_derived {flags} { - set all [expr {{*} in $flags}] - if {$all || "-version" in $flags || "-name" in $flags} { - set name $::teaish__PkgInfo(-name) ; # _not_ -name.pkg - if {[info exists ::teaish__PkgInfo(-version)]} { - set pkgver $::teaish__PkgInfo(-version) - set libname "lib" - if {[string match *-cygwin [get-define host]]} { - set libname cyg - } - define TEAISH_DLL8_BASENAME $libname$name$pkgver - define TEAISH_DLL9_BASENAME ${libname}tcl9$name$pkgver - set ext [get-define TARGET_DLLEXT] - define TEAISH_DLL8 [get-define TEAISH_DLL8_BASENAME]$ext - define TEAISH_DLL9 [get-define TEAISH_DLL9_BASENAME]$ext - } - } - if {$all || "-libDir" in $flags} { - if {[info exists ::teaish__PkgInfo(-libDir)]} { - define TCLLIBDIR \ - [file dirname [get-define TCLLIBDIR]]/$::teaish__PkgInfo(-libDir) - } - } -} - -# -# @teaish-checks-queue -pre|-post args... -# -# Queues one or more arbitrary "feature test" functions to be run when -# teaish-checks-run is called. $flag must be one of -pre or -post to -# specify whether the tests should be run before or after -# teaish-configure is run. Each additional arg is the name of a -# feature-test proc. -# -proc teaish-checks-queue {flag args} { - if {$flag ni {-pre -post}} { - proj-error "illegal flag: $flag" - } - lappend ::teaish__Config(queued-checks${flag}) {*}$args -} - -# -# @teaish-checks-run -pre|-post -# -# Runs all feature checks queued using teaish-checks-queue -# then cleares the queue. -# -proc teaish-checks-run {flag} { - if {$flag ni {-pre -post}} { - proj-error "illegal flag: $flag" - } - #puts "*** running $flag: $::teaish__Config(queued-checks${flag})" - set foo 0 - foreach f $::teaish__Config(queued-checks${flag}) { - if {![teaish-feature-cache-check $f foo]} { - set v [$f] - teaish-feature-cache-set $f $v - } - } - set ::teaish__Config(queued-checks${flag}) {} -} - -# -# A general-purpose getter for various teaish state. Requires one -# flag, which determines its result value. Flags marked with [L] below -# are safe for using at load-time, before teaish-pkginfo-set is called -# -# -dir [L]: returns the extension's directory, which may differ from -# the teaish core dir or the build dir. -# -# -teaish-home [L]: the "home" dir of teaish itself, which may -# differ from the extension dir or build dir. -# -# -build-dir [L]: the build directory (typically the current working -# -dir). -# -# Any of the teaish-pkginfo-get/get flags: returns the same as -# teaish-pkginfo-get. Not safe for use until teaish-pkginfo-set has -# been called. -# -# Triggers an error if passed an unknown flag. -# -proc teaish-get {flag} { - #-teaish.tcl {return $::teaish__Config(teaish.tcl)} - switch -exact -- $flag { - -dir { - return $::teaish__Config(extension-dir) - } - -teaish-home { - return $::autosetup(srcdir) - } - -build-dir { - return $::autosetup(builddir) - } - default { - if {[info exists ::teaish__PkgInfo($flag)]} { - return $::teaish__PkgInfo($flag) - } - } - } - proj-error "Unhandled flag: $flag" -} - -# -# Handles --teaish-create-extension=TARGET-DIR -# -proc teaish__create_extension {dir} { - set force [opt-bool teaish-force] - if {"" eq $dir} { - proj-error "--teaish-create-extension=X requires a directory name." - } - file mkdir $dir/generic - set cwd [pwd] - #set dir [file-normalize [file join $cwd $dir]] - teaish__verbose 1 msg-result "Created dir $dir" - cd $dir - if {!$force} { - # Ensure that we don't blindly overwrite anything - foreach f { - generic/teaish.c - teaish.tcl - teaish.make.in - teaish.test.tcl - } { - if {[file exists $f]} { - error "Cowardly refusing to overwrite $dir/$f. Use --teaish-force to overwrite." - } - } - } - - set name [file tail $dir] - set pkgName $name - set version 0.0.1 - set loadPrefix [string totitle $pkgName] - set content {teaish-pkginfo-set } - #puts "0 content=$content" - if {[opt-str teaish-extension-pkginfo epi]} { - set epi [string trim $epi] - if {[string match "*\n*" $epi]} { - set epi "{$epi}" - } elseif {![string match "{*}" $epi]} { - append content "\{" $epi "\}" - } else { - append content $epi - } - #puts "2 content=$content\nepi=$epi" - } else { - append content [subst -nocommands -nobackslashes {{ - -name ${name} - -name.pkg ${pkgName} - -name.dist ${pkgName} - -version ${version} - -loadPrefix $loadPrefix - -libDir ${name}${version} - -vsatisfies {{Tcl 8.5-}} - -url {} - -options {} - -pragmas {full-dist} - }}] - #puts "3 content=$content" - } - #puts "1 content=$content" - append content "\n" { -#proc teaish-options {} { - # Return a list and/or use \[options-add\] to add new - # configure flags. This is called before teaish's - # bootstrapping is finished, so only teaish-* - # APIs which are explicitly noted as being safe - # early on may be used here. Any autosetup-related - # APIs may be used here. - # - # Return an empty string if there are no options to - # add or if they are added using \[options-add\]. - # - # If there are no options to add, this proc need - # not be defined. -#} - -# Called by teaish once bootstrapping is complete. -# This function is responsible for the client-specific -# parts of the configuration process. -proc teaish-configure {} { - teaish-src-add -dir -dist generic/teaish.c - teaish-define-to-cflag -quote TEAISH_PKGNAME TEAISH_VERSION - - # TODO: your code goes here.. -} -}; # $content - proj-file-write teaish.tcl $content - teaish__verbose 1 msg-result "Created teaish.tcl" - - set content "# Teaish test script. -# When this tcl script is invoked via 'make test' it will have loaded -# the package, run any teaish.pkginit.tcl code, and loaded -# autosetup/teaish/tester.tcl. -" - proj-file-write teaish.test.tcl $content - teaish__verbose 1 msg-result "Created teaish.test.tcl" - - set content [subst -nocommands -nobackslashes { -#include <tcl.h> -static int -${loadPrefix}_Cmd(ClientData cdata, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]){ - Tcl_SetObjResult(interp, Tcl_NewStringObj("this is the ${name} extension", -1)); - return TCL_OK; -} - -extern int DLLEXPORT ${loadPrefix}_Init(Tcl_Interp *interp){ - if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) { - return TCL_ERROR; - } - if (Tcl_PkgProvide(interp, TEAISH_PKGNAME, TEAISH_VERSION) == TCL_ERROR) { - return TCL_ERROR; - } - Tcl_CreateObjCommand(interp, TEAISH_PKGNAME, ${loadPrefix}_Cmd, NULL, NULL); - return TCL_OK; -} -}] - proj-file-write generic/teaish.c $content - teaish__verbose 1 msg-result "Created generic/teaish.c" - - set content "# teaish makefile for the ${name} extension -# tx.src = \$(tx.dir)/generic/teaish.c -# tx.LDFLAGS = -# tx.CFLAGS = -" - proj-file-write teaish.make.in $content - teaish__verbose 1 msg-result "Created teaish.make.in" - - msg-result "Created new extension $name in \[$dir]." - - cd $cwd - set ::teaish__Config(install-ext-dir) $dir -} - -# -# Internal helper for teaish__install -# -proc teaish__install_file {f destDir force} { - set dest $destDir/[file tail $f] - if {[file isdirectory $f]} { - file mkdir $dest - } elseif {!$force && [file exists $dest]} { - array set st1 [file stat $f] - array set st2 [file stat $dest] - if {($st1(mtime) == $st2(mtime)) - && ($st1(size) == $st2(size))} { - if {[file tail $f] in { - pkgIndex.tcl.in - teaish.tester.tcl.in - }} { - # Assume they're the same. In the scope of the "make dist" - # rules, this happens legitimately when an extension with a - # copy of teaish installed in the same dir assumes that the - # pkgIndex.tcl.in and teaish.tester.tcl.in belong to the - # extension, whereas teaish believes they belong to teaish. - # So we end up with dupes of those. - return - } - } - proj-error -up "Cowardly refusing to overwrite \[$dest\]." \ - "Use --teaish-force to enable overwriting." - } else { - # file copy -force $f $destDir; # loses +x bit - # - # JimTcl doesn't have [file attribute], so we can't use that here - # (in the context of an autosetup configure script). - exec cp -p $f $dest - } -} - -# -# Installs a copy of teaish, with autosetup, to $dDest, which defaults -# to the --teaish-install=X or --teash-create-extension=X dir. Won't -# overwrite files unless --teaish-force is used. -# -proc teaish__install {{dDest ""}} { - if {$dDest in {auto ""}} { - set dDest [opt-val teaish-install] - if {$dDest in {auto ""}} { - if {[info exists ::teaish__Config(install-ext-dir)]} { - set dDest $::teaish__Config(install-ext-dir) - } - } - } - set force [opt-bool teaish-force] - if {$dDest in {auto ""}} { - proj-error "Cannot determine installation directory." - } elseif {!$force && [file exists $dDest/auto.def]} { - proj-error \ - "Target dir looks like it already contains teaish and/or autosetup: $dDest" \ - "\nUse --teaish-force to overwrite it." - } - - set dSrc $::autosetup(srcdir) - set dAS $::autosetup(libdir) - set dAST $::teaish__Config(core-dir) - set dASTF $dAST/feature - teaish__verbose 1 msg-result "Installing teaish to \[$dDest\]..." - if {$::teaish__Config(verbose)>1} { - msg-result "dSrc = $dSrc" - msg-result "dAS = $dAS" - msg-result "dAST = $dAST" - msg-result "dASTF = $dASTF" - msg-result "dDest = $dDest" - } - - # Dest subdirs... - set ddAS $dDest/autosetup - set ddAST $ddAS/teaish - set ddASTF $ddAST/feature - foreach {srcDir destDir} [list \ - $dAS $ddAS \ - $dAST $ddAST \ - $dASTF $ddASTF \ - ] { - teaish__verbose 1 msg-result "Copying files to $destDir..." - file mkdir $destDir - foreach f [glob -directory $srcDir *] { - if {[string match {*~} $f] || [string match "#*#" [file tail $f]]} { - # Editor-generated backups and emacs lock files - continue - } - teaish__verbose 2 msg-result "\t$f" - teaish__install_file $f $destDir $force - } - } - teaish__verbose 1 msg-result "Copying files to $dDest..." - foreach f { - auto.def configure Makefile.in pkgIndex.tcl.in - teaish.tester.tcl.in - } { - teaish__verbose 2 msg-result "\t$f" - teaish__install_file $dSrc/$f $dDest $force - } - set ::teaish__Config(install-self-dir) $dDest - msg-result "Teaish $::teaish__Config(version) installed in \[$dDest\]." -} diff --git a/autoconf/tea/autosetup/feature-tests.tcl b/autoconf/tea/autosetup/feature-tests.tcl deleted file mode 100644 index 6c927d1a7..000000000 --- a/autoconf/tea/autosetup/feature-tests.tcl +++ /dev/null @@ -1,214 +0,0 @@ -######################################################################## -# 2025 April 7 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# * May you do good and not evil. -# * May you find forgiveness for yourself and forgive others. -# * May you share freely, never taking more than you give. -# -######################################################################## -# ----- @module feature-tests.tcl ----- -# @section TEA-ish collection of feature tests. -# -# Functions in this file with a prefix of teaish__ are -# private/internal APIs. Those with a prefix of teaish- are -# public APIs. - - -# @teaish-check-libz -# -# Checks for zlib.h and the function deflate in libz. If found, -# prepends -lz to the extension's ldflags and returns 1, else returns -# 0. It also defines LDFLAGS_LIBZ to the libs flag. -# -proc teaish-check-libz {} { - teaish-check-cached "Checking for libz" { - set rc 0 - if {[msg-quiet cc-check-includes zlib.h] && [msg-quiet proj-check-function-in-lib deflate z]} { - teaish-ldflags-prepend [define LDFLAGS_LIBZ [get-define lib_deflate]] - undefine lib_deflate - incr rc - } - expr $rc - } -} - -# @teaish-check-librt ?funclist? -# -# Checks whether -lrt is needed for any of the given functions. If -# so, appends -lrt via [teaish-ldflags-prepend] and returns 1, else -# returns 0. It also defines LDFLAGS_LIBRT to the libs flag or an -# empty string. -# -# Some systems (ex: SunOS) require -lrt in order to use nanosleep. -# -proc teaish-check-librt {{funclist {fdatasync nanosleep}}} { - teaish-check-cached -nostatus "Checking whether ($funclist) need librt" { - define LDFLAGS_LIBRT "" - foreach func $funclist { - if {[msg-quiet proj-check-function-in-lib $func rt]} { - set ldrt [get-define lib_${func}] - undefine lib_${func} - if {"" ne $ldrt} { - teaish-ldflags-prepend -r [define LDFLAGS_LIBRT $ldrt] - msg-result $ldrt - return 1 - } else { - msg-result "no lib needed" - return 1 - } - } - } - msg-result "not found" - return 0 - } -} - -# @teaish-check-stdint -# -# A thin proxy for [cc-with] which checks for <stdint.h> and the -# various fixed-size int types it declares. It defines HAVE_STDINT_T -# to 0 or 1 and (if it's 1) defines HAVE_XYZ_T for each XYZ int type -# to 0 or 1, depending on whether its available. -proc teaish-check-stdint {} { - teaish-check-cached "Checking for stdint.h" { - msg-quiet cc-with {-includes stdint.h} \ - {cc-check-types int8_t int16_t int32_t int64_t intptr_t \ - uint8_t uint16_t uint32_t uint64_t uintptr_t} - } -} - -# @teaish-is-mingw -# -# Returns 1 if building for mingw, else 0. -proc teaish-is-mingw {} { - return [expr { - [string match *mingw* [get-define host]] && - ![file exists /dev/null] - }] -} - -# @teaish-check-libdl -# -# Checks for whether dlopen() can be found and whether it requires -# -ldl for linking. If found, returns 1, defines LDFLAGS_DLOPEN to the -# linker flags (if any), and passes those flags to -# teaish-ldflags-prepend. It unconditionally defines HAVE_DLOPEN to 0 -# or 1 (the its return result value). -proc teaish-check-dlopen {} { - teaish-check-cached -nostatus "Checking for dlopen()" { - set rc 0 - set lfl "" - if {[cc-with {-includes dlfcn.h} { - cctest -link 1 -declare "extern char* dlerror(void);" -code "dlerror();"}]} { - msg-result "-ldl not needed" - incr rc - } elseif {[cc-check-includes dlfcn.h]} { - incr rc - if {[cc-check-function-in-lib dlopen dl]} { - set lfl [get-define lib_dlopen] - undefine lib_dlopen - msg-result " dlopen() needs $lfl" - } else { - msg-result " - dlopen() not found in libdl. Assuming dlopen() is built-in." - } - } else { - msg-result "not found" - } - teaish-ldflags-prepend [define LDFLAGS_DLOPEN $lfl] - define HAVE_DLOPEN $rc - } -} - -# -# @teaish-check-libmath -# -# Handles the --enable-math flag. Returns 1 if found, else 0. -# If found, it prepends -lm (if needed) to the linker flags. -proc teaish-check-libmath {} { - teaish-check-cached "Checking for libc math library" { - set lfl "" - set rc 0 - if {[msg-quiet proj-check-function-in-lib ceil m]} { - incr rc - set lfl [get-define lib_ceil] - undefine lib_ceil - teaish-ldflags-prepend $lfl - msg-checking "$lfl " - } - define LDFLAGS_LIBMATH $lfl - expr $rc - } -} - -# @teaish-import-features ?-flags? feature-names... -# -# For each $name in feature-names... it invokes: -# -# use teaish/feature/$name -# -# to load TEAISH_AUTOSETUP_DIR/feature/$name.tcl -# -# By default, if a proc named teaish-check-${name}-options is defined -# after sourcing a file, it is called and its result is passed to -# proj-append-options. This can be suppressed with the -no-options -# flag. -# -# Flags: -# -# -no-options: disables the automatic running of -# teaish-check-NAME-options, -# -# -run: if the function teaish-check-NAME exists after importing -# then it is called. This flag must not be used when calling this -# function from teaish-options. This trumps both -pre and -post. -# -# -pre: if the function teaish-check-NAME exists after importing -# then it is passed to [teaish-checks-queue -pre]. -# -# -post: works like -pre but instead uses[teaish-checks-queue -post]. -proc teaish-import-features {args} { - set pk "" - set doOpt 1 - proj-parse-simple-flags args flags { - -no-options 0 {set doOpt 0} - -run 0 {expr 1} - -pre 0 {set pk -pre} - -post 0 {set pk -post} - } - # - # TODO: never import the same module more than once. The "use" - # command is smart enough to not do that but we would need to - # remember whether or not any teaish-check-${arg}* procs have been - # called before, and skip them. - # - if {$flags(-run) && "" ne $pk} { - proj-error "Cannot use both -run and $pk" \ - " (called from [proj-scope 1])" - } - - foreach arg $args { - uplevel "use teaish/feature/$arg" - if {$doOpt} { - set n "teaish-check-${arg}-options" - if {[llength [info proc $n]] > 0} { - if {"" ne [set x [$n]]} { - options-add $x - } - } - } - if {$flags(-run)} { - set n "teaish-check-${arg}" - if {[llength [info proc $n]] > 0} { - uplevel 1 $n - } - } elseif {"" ne $pk} { - set n "teaish-check-${arg}" - if {[llength [info proc $n]] > 0} { - teaish-checks-queue {*}$pk $n - } - } - } -} diff --git a/autoconf/tea/autosetup/tester.tcl b/autoconf/tea/autosetup/tester.tcl deleted file mode 100644 index 5c546e841..000000000 --- a/autoconf/tea/autosetup/tester.tcl +++ /dev/null @@ -1,193 +0,0 @@ -######################################################################## -# 2025 April 5 -# -# The author disclaims copyright to this source code. In place of -# a legal notice, here is a blessing: -# -# * May you do good and not evil. -# * May you find forgiveness for yourself and forgive others. -# * May you share freely, never taking more than you give. -# -######################################################################## -# -# Helper routines for running automated tests on teaish extensions -# -######################################################################## -# ----- @module teaish-tester.tcl ----- -# -# @section TEA-ish Testing APIs. -# -# Though these are part of the autosup dir hierarchy, they are not -# intended to be run from autosetup code. Rather, they're for -# use with/via teaish.tester.tcl. - -# -# @test-current-scope ?lvl? -# -# Returns the name of the _calling_ proc from ($lvl + 1) levels up the -# call stack (where the caller's level will be 1 up from _this_ -# call). If $lvl would resolve to global scope "global scope" is -# returned and if it would be negative then a string indicating such -# is returned (as opposed to throwing an error). -# -proc test-current-scope {{lvl 0}} { - #uplevel [expr {$lvl + 1}] {lindex [info level 0] 0} - set ilvl [info level] - set offset [expr {$ilvl - $lvl - 1}] - if { $offset < 0} { - return "invalid scope ($offset)" - } elseif { $offset == 0} { - return "global scope" - } else { - return [lindex [info level $offset] 0] - } -} - -# @test-msg -# -# Emits all arugments to stdout. -# -proc test-msg {args} { - puts "$args" -} - -# @test-warn -# -# Emits all arugments to stderr. -# -proc test-warn {args} { - puts stderr "WARNING: $args" -} - -# -# @test-error msg -# -# Triggers a test-failed error with a string describing the calling -# scope and the provided message. -# -proc test-fail {args} { - #puts stderr "ERROR: \[[test-current-scope 1]]: $msg" - #exit 1 - error "FAIL: \[[test-current-scope 1]]: $args" -} - -# -# Internal impl for assert-likes. Should not be called directly by -# client code. -# -proc test__assert {lvl script {msg ""}} { - set src "expr \{ $script \}" - # puts "XXXX evalling $src"; - if {![uplevel $lvl $src]} { - if {"" eq $msg} { - set msg $script - } - set caller1 [test-current-scope $lvl] - incr lvl - set caller2 [test-current-scope $lvl] - error "Assertion failed in: \[$caller2 -> $caller1]]: $msg" - } -} - -# -# @assert script ?message? -# -# Kind of like a C assert: if uplevel (eval) of [expr {$script}] is -# false, a fatal error is triggered. The error message, by default, -# includes the body of the failed assertion, but if $msg is set then -# that is used instead. -# -proc assert {script {msg ""}} { - test__assert 1 $script $msg -} - -# -# @test-assert testId script ?msg? -# -# Works like [assert] but emits $testId to stdout first. -# -proc test-assert {testId script {msg ""}} { - puts "test $testId" - test__assert 2 $script $msg -} - -# -# @test-expect testId script result -# -# Runs $script in the calling scope and compares its result to -# $result, minus any leading or trailing whitespace. If they differ, -# it triggers an [assert]. -# -proc test-expect {testId script result} { - puts "test $testId" - set x [string trim [uplevel 1 $script]] - set result [string trim $result] - test__assert 1 {$x eq $result} \ - "\nEXPECTED: <<$result>>\nGOT: <<$x>>" -} - -# -# @test-catch cmd ?...args? -# -# Runs [cmd ...args], repressing any exception except to possibly log -# the failure. Returns 1 if it caught anything, 0 if it didn't. -# -proc test-catch {cmd args} { - if {[catch { - $cmd {*}$args - } rc xopts]} { - puts "[test-current-scope] ignoring failure of: $cmd [lindex $args 0]: $rc" - return 1 - } - return 0 -} - -if {![array exists ::teaish__BuildFlags]} { - array set ::teaish__BuildFlags {} -} - -# -# @teaish-build-flag3 flag tgtVar ?dflt? -# -# If the current build has the configure-time flag named $flag set -# then tgtVar is assigned its value and 1 is returned, else tgtVal is -# assigned $dflt and 0 is returned. -# -# Caveat #1: only valid when called in the context of teaish's default -# "make test" recipe, e.g. from teaish.test.tcl. It is not valid from -# a teaish.tcl configure script because (A) the state it relies on -# doesn't fully exist at that point and (B) that level of the API has -# more direct access to the build state. This function requires that -# an external script have populated its internal state, which is -# normally handled via teaish.tester.tcl.in. -# -# Caveat #2: defines in the style of HAVE_FEATURENAME with a value of -# 0 are, by long-standing configure script conventions, treated as -# _undefined_ here. -# -proc teaish-build-flag3 {flag tgtVar {dflt ""}} { - upvar $tgtVar tgt - if {[info exists ::teaish__BuildFlags($flag)]} { - set tgt $::teaish__BuildFlags($flag) - return 1; - } elseif {0==[array size ::teaish__BuildFlags]} { - test-warn \ - "\[[test-current-scope]] was called from " \ - "[test-current-scope 1] without the build flags imported." - } - set tgt $dflt - return 0 -} - -# -# @teaish-build-flag flag ?dflt? -# -# Convenience form of teaish-build-flag3 which returns the -# configure-time-defined value of $flag or "" if it's not defined (or -# if it's an empty string). -# -proc teaish-build-flag {flag {dflt ""}} { - set tgt "" - teaish-build-flag3 $flag tgt $dflt - return $tgt -} diff --git a/autoconf/tea/teaish.tcl b/autoconf/tea/teaish.tcl index 87d059c32..9333495aa 100644 --- a/autoconf/tea/teaish.tcl +++ b/autoconf/tea/teaish.tcl @@ -117,7 +117,7 @@ proc teaish-options {} { # work needed for this extension. # proc teaish-configure {} { - use teaish/feature-tests + use teaish/feature teaish-src-add -dist -dir generic/tclsqlite3.c |