######################################################################## # 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 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 } } } }