1 ######################################################################## 2 # 2025 April 7 3 # 4 # The author disclaims copyright to this source code. In place of 5 # a legal notice, here is a blessing: 6 # 7 # * May you do good and not evil. 8 # * May you find forgiveness for yourself and forgive others. 9 # * May you share freely, never taking more than you give. 10 # 11 ######################################################################## 12 # ----- @module feature-tests.tcl ----- 13 # @section TEA-ish collection of feature tests. 14 # 15 # Functions in this file with a prefix of teaish__ are 16 # private/internal APIs. Those with a prefix of teaish- are 17 # public APIs. 18 19 20 # @teaish-check-libz 21 # 22 # Checks for zlib.h and the function deflate in libz. If found, 23 # prepends -lz to the extension's ldflags and returns 1, else returns 24 # 0. It also defines LDFLAGS_LIBZ to the libs flag. 25 # 26 proc teaish-check-libz {} { 27 teaish-check-cached "Checking for libz" { 28 set rc 0 29 if {[msg-quiet cc-check-includes zlib.h] && [msg-quiet proj-check-function-in-lib deflate z]} { 30 teaish-ldflags-prepend [define LDFLAGS_LIBZ [get-define lib_deflate]] 31 undefine lib_deflate 32 incr rc 33 } 34 expr $rc 35 } 36 } 37 38 # @teaish-check-librt ?funclist? 39 # 40 # Checks whether -lrt is needed for any of the given functions. If 41 # so, appends -lrt via [teaish-ldflags-prepend] and returns 1, else 42 # returns 0. It also defines LDFLAGS_LIBRT to the libs flag or an 43 # empty string. 44 # 45 # Some systems (ex: SunOS) require -lrt in order to use nanosleep. 46 # 47 proc teaish-check-librt {{funclist {fdatasync nanosleep}}} { 48 teaish-check-cached -nostatus "Checking whether ($funclist) need librt" { 49 define LDFLAGS_LIBRT "" 50 foreach func $funclist { 51 if {[msg-quiet proj-check-function-in-lib $func rt]} { 52 set ldrt [get-define lib_${func}] 53 undefine lib_${func} 54 if {"" ne $ldrt} { 55 teaish-ldflags-prepend -r [define LDFLAGS_LIBRT $ldrt] 56 msg-result $ldrt 57 return 1 58 } else { 59 msg-result "no lib needed" 60 return 1 61 } 62 } 63 } 64 msg-result "not found" 65 return 0 66 } 67 } 68 69 # @teaish-check-stdint 70 # 71 # A thin proxy for [cc-with] which checks for <stdint.h> and the 72 # various fixed-size int types it declares. It defines HAVE_STDINT_T 73 # to 0 or 1 and (if it's 1) defines HAVE_XYZ_T for each XYZ int type 74 # to 0 or 1, depending on whether its available. 75 proc teaish-check-stdint {} { 76 teaish-check-cached "Checking for stdint.h" { 77 msg-quiet cc-with {-includes stdint.h} \ 78 {cc-check-types int8_t int16_t int32_t int64_t intptr_t \ 79 uint8_t uint16_t uint32_t uint64_t uintptr_t} 80 } 81 } 82 83 # @teaish-is-mingw 84 # 85 # Returns 1 if building for mingw, else 0. 86 proc teaish-is-mingw {} { 87 return [expr { 88 [string match *mingw* [get-define host]] && 89 ![file exists /dev/null] 90 }] 91 } 92 93 # @teaish-check-libdl 94 # 95 # Checks for whether dlopen() can be found and whether it requires 96 # -ldl for linking. If found, returns 1, defines LDFLAGS_DLOPEN to the 97 # linker flags (if any), and passes those flags to 98 # teaish-ldflags-prepend. It unconditionally defines HAVE_DLOPEN to 0 99 # or 1 (the its return result value). 100 proc teaish-check-dlopen {} { 101 teaish-check-cached -nostatus "Checking for dlopen()" { 102 set rc 0 103 set lfl "" 104 if {[cc-with {-includes dlfcn.h} { 105 cctest -link 1 -declare "extern char* dlerror(void);" -code "dlerror();"}]} { 106 msg-result "-ldl not needed" 107 incr rc 108 } elseif {[cc-check-includes dlfcn.h]} { 109 incr rc 110 if {[cc-check-function-in-lib dlopen dl]} { 111 set lfl [get-define lib_dlopen] 112 undefine lib_dlopen 113 msg-result " dlopen() needs $lfl" 114 } else { 115 msg-result " - dlopen() not found in libdl. Assuming dlopen() is built-in." 116 } 117 } else { 118 msg-result "not found" 119 } 120 teaish-ldflags-prepend [define LDFLAGS_DLOPEN $lfl] 121 define HAVE_DLOPEN $rc 122 } 123 } 124 125 # 126 # @teaish-check-libmath 127 # 128 # Handles the --enable-math flag. Returns 1 if found, else 0. 129 # If found, it prepends -lm (if needed) to the linker flags. 130 proc teaish-check-libmath {} { 131 teaish-check-cached "Checking for libc math library" { 132 set lfl "" 133 set rc 0 134 if {[msg-quiet proj-check-function-in-lib ceil m]} { 135 incr rc 136 set lfl [get-define lib_ceil] 137 undefine lib_ceil 138 teaish-ldflags-prepend $lfl 139 msg-checking "$lfl " 140 } 141 define LDFLAGS_LIBMATH $lfl 142 expr $rc 143 } 144 } 145 146 # @teaish-import-features ?-flags? feature-names... 147 # 148 # For each $name in feature-names... it invokes: 149 # 150 # use teaish/feature/$name 151 # 152 # to load TEAISH_AUTOSETUP_DIR/feature/$name.tcl 153 # 154 # By default, if a proc named teaish-check-${name}-options is defined 155 # after sourcing a file, it is called and its result is passed to 156 # proj-append-options. This can be suppressed with the -no-options 157 # flag. 158 # 159 # Flags: 160 # 161 # -no-options: disables the automatic running of 162 # teaish-check-NAME-options, 163 # 164 # -run: if the function teaish-check-NAME exists after importing 165 # then it is called. This flag must not be used when calling this 166 # function from teaish-options. This trumps both -pre and -post. 167 # 168 # -pre: if the function teaish-check-NAME exists after importing 169 # then it is passed to [teaish-checks-queue -pre]. 170 # 171 # -post: works like -pre but instead uses[teaish-checks-queue -post]. 172 proc teaish-import-features {args} { 173 set pk "" 174 set doOpt 1 175 proj-parse-simple-flags args flags { 176 -no-options 0 {set doOpt 0} 177 -run 0 {expr 1} 178 -pre 0 {set pk -pre} 179 -post 0 {set pk -post} 180 } 181 # 182 # TODO: never import the same module more than once. The "use" 183 # command is smart enough to not do that but we would need to 184 # remember whether or not any teaish-check-${arg}* procs have been 185 # called before, and skip them. 186 # 187 if {$flags(-run) && "" ne $pk} { 188 proj-error "Cannot use both -run and $pk" \ 189 " (called from [proj-scope 1])" 190 } 191 192 foreach arg $args { 193 uplevel "use teaish/feature/$arg" 194 if {$doOpt} { 195 set n "teaish-check-${arg}-options" 196 if {[llength [info proc $n]] > 0} { 197 if {"" ne [set x [$n]]} { 198 options-add $x 199 } 200 } 201 } 202 if {$flags(-run)} { 203 set n "teaish-check-${arg}" 204 if {[llength [info proc $n]] > 0} { 205 uplevel 1 $n 206 } 207 } elseif {"" ne $pk} { 208 set n "teaish-check-${arg}" 209 if {[llength [info proc $n]] > 0} { 210 teaish-checks-queue {*}$pk $n 211 } 212 } 213 } 214 } 215