#/usr/bin/tclsh # ^^^ help out editors which guess this file's content type. # # This is the main autosetup-compatible configure script for the # SQLite project. # # This script should be kept compatible with JimTCL, a copy of which # is included in this source tree as ./autosetup/jimsh0.c. The number # of incompatibilities between canonical TCL and JimTCL is very low # and alternative formulations of incompatible constructs have, so # far, been easy to find. # # JimTCL: https://jim.tcl.tk # use cc cc-db cc-shared cc-lib proj pkg-config # $DUMP_DEFINES_TXT is the file emitted by --dump-defines, intended # only for build debugging and not part of the public build interface. set DUMP_DEFINES_TXT ./config.defines.txt # $DUMP_DEFINES_JSON is the autosetup counterpart of the historical # "DEFS" var which was generated by the autotools in the pre-processed # autotools builds (but not in the canonical tree). Generation of this # file is disabled (via an empty file name) until/unless someone # voices a specific interest in it. The original motivating use case # is handled fine by sqlite_cfg.h. set DUMP_DEFINES_JSON ""; #./config.defines.json ######################################################################## # Regarding flag compatibility with the historical autotool configure # script: # # A very long story made short, autosetup's --flag handling has # some behaviors which make it impossible to implement 100% identical # flags compared to the historical autotools build. The differences # are documented here: # # 1) --debug is used by autosetup itself, so we have to rename it to # --with-debug. We cannot use --enable-debug because that is, for # autosetup, and alias for --debug=1. # # 2) In autosetup, all flags starting with (--enable, --disable) are # forced to be booleans and receive special handling in how they're # resolved. Because of that we have to rename: # # 2.1) --enable-tempstore[=no] to --with-tempstore[=no]. # ######################################################################## # A gentle introduction to flags handling in autosetup # # Reference: https://msteveb.github.io/autosetup/developer/ # # All configure flags must be described in an 'options' call, which # must appear very early on in this script. The general syntax is: # # FLAG => {Help text} # # Where FLAG can have any of the following formats: # # boolopt => "a boolean option which defaults to disabled" # boolopt2=1 => "a boolean option which defaults to enabled" # stringopt: => "an option which takes an argument, e.g. --stringopt=value" # stringopt2:=value => "an option where the argument is optional and defaults to 'value'" # optalias booltopt3 => "a boolean with a hidden alias. --optalias is not shown in --help" # # Autosetup does no small amount of specialized handling for flags, # especially booleans. Each bool-type --FLAG implicitly gets # --enable-FLAG and --disable-FLAG forms, and explicitly adding flags # with those prefixes will force them to be boolean flags. e.g. we # define a flag "readline", which will be interpreted in one of two # ways, depending on how it's invoked and how its default is defined: # # --enable-readline ==> boolean true # --disable-readline ==> boolean false # # Trying to pass --readline or --readline=1 or --readline=0 will # result in an "unrecognized option" error, despite the the options # call listing the flag as "readline". # # The behavior described above can lead lead to some confusion when # writing help text. For example: # # options { json=1 {Disable JSON functions} } # # The reason the help text says "disable" is because a boolean option # defaulting to true is, in the --help text, rendered as: # # --disable-json Disable JSON functions # # Whereas a bool flag which defaults to false will instead render as: # # --enable-FLAG # # Non-boolean flags, in contrast, use the names specifically given to # them in the 'options' invocation. e.g. "with-tcl" is the --with-tcl # flag. Autosetup may, however, choose to automatically alter the help # text, as demonstrated here: # # options { # readline=1 => {Disable readline support} # with-readline-lib: => {Readline library} # with-readline-inc: => {Readline include paths} # } # # $ ./configure --help | grep readline # --disable-readline disable readline support # --with-readline-lib specify readline library # --with-readline-inc specify readline include paths # # Note that it prefixed and lower-case the help message. Whether # that's a feature or a bug can be debated. # # Fetching values for flags: # # booleans: use one of: # - [opt-bool FLAG] is autosetup's built-in command for this, but we # have some convenience variants: # - [proj-opt-truthy FLAG] # - [proj-opt-if-truthy FLAG {THEN} {ELSE}] # # Non-boolean (i.e. string) flags: # - [opt-val FLAG ?default?] # ######################################################################## set flags { # with-debug:=1 => {Enable debug build flags} shared=1 => {Disable build of shared libary} static=1 => {Disable build of static library (mostly)} amalgamation=1 => {Disable the amalgamation and instead build all files separately.} # # threadsafe=1 => {Disable mutexing} with-tempstore:=no => {Use an in-ram database for temporary tables: never,no,yes,always} largefile=1 => {Disable large file support} load-extension=1 => {Disable loading of external extensions} math=1 => {Disable math functions} json=1 => {Disable JSON functions} memsys5 => {Enable MEMSYS5} memsys3 => {Enable MEMSYS3} fts3 => {Enable the FTS3 extension} fts4 => {Enable the FTS4 extension} fts5 => {Enable the FTS5 extension} update-limit => {Enable the UPDATE/DELETE LIMIT clause} geopoly => {Enable the GEOPOLY extension} rtree => {Enable the RTREE extension} session => {Enable the SESSION extension} all => {Enable FTS4, FTS5, Geopoly, RTree, Sessions} # # # --with-tcl=DIR may be either a dir containing tclConfig.sh or a # dir one level up from that from which we can derive a dir # containing tclConfig.sh. with-tcl:DIR => {Root of path containing tclConfig.sh} # If --with-tclsh=X given, it is used for (A) trying to find # tclConfig.sh and (B) all TCL-based code generation. Warning: if # its containing dir has multiple tclsh versions, it may select the # wrong tclConfig.sh! with-tclsh:PATH => {Full pathname of tclsh to use} # --disable-tcl only disables building of the SQLite TCL extension, # not the requirement for TCL. This tree requires TCL for code # generation but can use the in-tree copy of autosetup/jimsh0.c for # that. The SQLite TCL extension and, by extension, the test code # require a canonical tclsh. tcl=1 => {Disable components which require TCL-dev} # # readline=1 => {Disable readline support} # --with-readline-lib is a backwards-compatible alias for # --with-readline-ldflags with-readline-lib: with-readline-ldflags:=auto => {Readline LDFLAGS, e.g. -lreadline -lncurses} # --with-readline-inc is a backwards-compatible alias for # --with-readline-cflags. with-readline-inc: with-readline-cflags:=auto => {Readline CFLAGS, e.g. -I/path/to/includes} with-readline-header:PATH => {Full path to readline.h, from which --with-readline-cflags will be derived} with-linenoise:DIR => {Source directory for linenoise.c and linenoise.h} editline=0 => {BSD editline support} # # with-icu-ldflags:LDFLAGS => {Enable SQLITE_ENABLE_ICU and add the given linker flags for the ICU libraries} with-icu-config:=auto => {Enable SQLITE_ENABLE_ICU and fetch linker flags from the given icu-config binary} icu-collations=0 => {Enable SQLITE_ENABLE_ICU_COLLATIONS. Requires --with-icu-ldflags=... or --with-icu-config} # # with-wasi-sdk:=/opt/wasi-sdk => {Top-most dir of the wasi-sdk for a WASI build} with-emsdk:DIR => {Top-most dir of the Emscripten SDK installation} # # test-status => {Enable status of tests} gcov=0 => {Enable coverage testing using gcov} linemacros => {Enable #line macros in the amalgamation.} dump-defines=0 => {Dump autosetup defines to $DUMP_DEFINES_TXT (for build debugging)} # } if {"" ne $DUMP_DEFINES_JSON} { lappend flags \ defines-json-include-lowercase=0 \ => {Include lower-case defines (primarily system paths) in $DUMP_DEFINES_JSON} } options [subst $flags] unset flags # # Carry values from hidden --flag aliases over to their canonical flag # forms. # proj-xfer-options-aliases { with-readline-inc => with-readline-cflags with-readline-lib => with-readline-ldflags } set srcdir $::autosetup(srcdir) set top_srcdir [get-define abs_top_srcdir] set PACKAGE_VERSION [readfile $srcdir/VERSION] define PACKAGE_NAME "sqlite" define PACKAGE_URL {https://sqlite.org} define PACKAGE_VERSION $PACKAGE_VERSION define PACKAGE_STRING "[get-define PACKAGE_NAME] $PACKAGE_VERSION" define PACKAGE_BUGREPORT [get-define PACKAGE_URL]/forum msg-result "srcdir = $srcdir" msg-result "top_srcdir = $top_srcdir" msg-result [proj-bold "Configuring SQLite version $PACKAGE_VERSION"] # # SQLITE_AUTORECONFIG contains make target rules for re-running the # configure script with the same arguments it was initially invoked # with. This can be used to automatically reconfigure # define-append SQLITE_AUTORECONFIG cd '$::autosetup(builddir)' && '$top_srcdir/configure' #{*}$::autosetup(argv) breaks with --flag='val with spaces', so... foreach arg $::autosetup(argv) { define-append SQLITE_AUTORECONFIG '$arg' } # Are we cross-compiling? set cross_compiling [proj-is-cross-compiling] if {![file exists sqlite3.pc.in]} { msg-result "This appears to be an out-of-tree build." } cc-check-tools ld ar define OPT_FEATURE_FLAGS {} ; # -DSQLITE_OMIT/ENABLE flags. define OPT_SHELL {} ; # CFLAGS for the sqlite3 CLI app ######################################################################## # Adds $args, if not empty, to OPT_FEATURE_FLAGS. # If the first arg is -shell then it strips that arg # and passes the remaining args th sqlite-add-shell-opt # in addition to adding them to OPT_FEATURE_FLAGS. proc sqlite-add-feature-flag {args} { set shell "" if {"-shell" eq [lindex $args 0]} { set args [lassign $args shell] } if {"" ne $args} { if {"" ne $shell} { sqlite-add-shell-opt {*}$args } define-append OPT_FEATURE_FLAGS {*}$args } } # Appends $args, if not empty, to OPT_SHELL. proc sqlite-add-shell-opt {args} { if {"" ne $args} { define-append OPT_SHELL {*}$args } } # Pass msg-debug=1 to configure to enable obnoxiously loud output from # msg-debug. set msgDebugEnabled [proj-val-truthy [get-env msg-debug 0]] proc msg-debug {msg} { if {$::msgDebugEnabled} { puts stderr [proj-bold "** DEBUG: $msg"] } } proj-file-extensions if {".exe" eq [get-define TARGET_EXEEXT]} { define SQLITE_OS_UNIX 0 define SQLITE_OS_WIN 1 # todo? add -DSQLITE_OS_WIN=1 to CFLAGS or CFLAGS_sqlite3_os? } else { define SQLITE_OS_UNIX 1 define SQLITE_OS_WIN 0 # todo? add -DSQLITE_OS_UNIX=1 to CFLAGS or CFLAGS_sqlite3_os } ######### # Programs needed if {"" eq [proj-bin-define install]} { proj-warn "Cannot find install binary, so 'make install' will not work." } ######################################################################## # We differentiate between two C compilers: the one used for binaries # which are to run on the build system (in autosetup it's called # CC_FOR_BUILD and in Makefile.in it's $(B.cc)) and the one used for # compiling binaries for the target system (CC a.k.a. $(T.cc)). # Normally they're the same, but they will differ when # cross-compiling. define BUILD_CFLAGS [get-env BUILD_CFLAGS {-g}] ######################################################################## # Handle --with-wasi-sdk=DIR # # This must be early because it may change the toolchain and disable # several config options. proc sqlite-check-wasi-sdk {} { set wasiSdkDir [opt-val with-wasi-sdk] ; # ??? [lindex [opt-val with-wasi-sdk] end] define HAVE_WASI_SDK 0 #puts "x wasiSdkDir=$wasiSdkDir foo=[lindex [opt-val with-wasi-sdk] end]" if {$wasiSdkDir eq ""} { return 0 } elseif {$::cross_compiling} { proj-fatal "Cannot combine --with-wasi-sdk with cross-compilation" } msg-result "Checking WASI SDK directory \[$wasiSdkDir]... " #puts "prefix = [prefix $wasiSdkDir/bin {clang ld}]" proj-affirm-files-exist -v {*}[prefix "$wasiSdkDir/bin/" {clang wasm-ld}] msg-result "Using wasi-sdk clang, disabling: tcl, CLI shell, DLL, loadable extensions, threading" define HAVE_WASI_SDK 1 define WASI_SDK_DIR $wasiSdkDir proj-opt-set load-extension 0 ;# ==> --disable-load-extension proj-opt-set threadsafe 0 ;# ==> --threadsafe=0 proj-opt-set tcl 0 ;# ==> --disable-tcl proj-opt-set shared 0 ;# ==> --disable-shared set cross_compiling 1 # Changing --host and --target have no effect here except to possibly # cause confusion. autoconf has finished processing them by this # point. # # host_alias=wasm32-wasi # target=wasm32-wasi # # Merely changing CC and LD to the wasi-sdk's is enough to get # sqlite3.o building in WASM format. # XXX CC="${wasiSdkDir}/bin/clang" # XXX LD="${wasiSdkDir}/bin/wasm-ld" # XXX RANLIB="${wasiSdkDir}/bin/llvm-ranlib" define CC "${wasiSdkDir}/bin/clang" define LD "${wasiSdkDir}/bin/wasm-ld" #define STRIP "${wasiSdkDir}/bin/strip" return 1 }; # sqlite-check-wasi-sdk sqlite-check-wasi-sdk # # Enable large file support (if special flags are necessary) define HAVE_LFS 0 if {[opt-bool largefile]} { cc-check-lfs } # # Check for needed/wanted data types 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} # # Check for needed/wanted functions cc-check-functions gmtime_r isnan localtime_r localtime_s \ malloc_usable_size strchrnul usleep utime pread pread64 pwrite pwrite64 proj-check-function-in-lib fdatasync rt define LDFLAGS_FDATASYNC [get-define lib_fdatasync] undefine lib_fdatasync # # Check for needed/wanted headers cc-check-includes \ sys/types.h sys/stat.h dlfcn.h unistd.h \ stdlib.h malloc.h memory.h \ string.h strings.h \ inttypes.h if {[cc-check-includes zlib.h] && [proj-check-function-in-lib deflate z]} { # TODO: port over the more sophisticated zlib search from the fossil auto.def define HAVE_ZLIB 1 define LDFLAGS_ZLIB -lz sqlite-add-shell-opt -DSQLITE_HAVE_ZLIB=1 } else { define HAVE_ZLIB 0 define LDFLAGS_ZLIB "" } proj-check-rpath; # Determine proper rpath-handling flags. proj-define-if-opt-truthy shared ENABLE_SHARED "Build shared library?" if {![proj-define-if-opt-truthy static ENABLE_STATIC \ "Build static library?"]} { proj-warn "Static lib build may be implicitly re-activated by other components, e.g. some test apps." } proj-define-if-opt-truthy amalgamation USE_AMALGAMATION "Use amalgamation for builds?" proj-define-if-opt-truthy gcov USE_GCOV "Use gcov?" proj-define-if-opt-truthy test-status TSTRNNR_OPTS \ "test-runner flags:" {--status} {} proj-define-if-opt-truthy linemacros AMALGAMATION_LINE_MACROS \ "Use #line macros in the amalgamation:" msg-checking "SQLITE_DEBUG build? " proj-if-opt-truthy with-debug { define SQLITE_DEBUG 1 define TARGET_DEBUG {-g -DSQLITE_DEBUG=1 -DSQLITE_ENABLE_SELECTTRACE -DSQLITE_ENABLE_WHERETRACE -O0 -Wall} msg-result yes } { define TARGET_DEBUG {-DNDEBUG} msg-result no } ######################################################################## # TCL... # # sqlite-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: # # - HAVE_TCL indicates whether we have a tclsh suitable for building # the TCL SQLite extension and, by extension, the testing # infrastructure. This must only be 1 for environments where # tclConfig.sh can be found. # # - TCLSH_CMD is the path to the canonical tclsh or "". It never # refers to jimtcl. # # - TCL_CONFIG_SH is the path to tclConfig.sh or "". # # - TCLLIBDIR is the dir to which libtclsqlite3 gets installed. # # - TCLLIB_RPATH = the -rpath flag specific to libtclsqlite3, which # will usually differ from the rpath used by the rest of the lib. # # - BTCLSH = the path to the tcl interpreter used for in-tree code # generation. It may be jimtcl or the canonical tclsh but may not # be empty - this tree requires TCL to generated numerous # components. # proc sqlite-check-tcl {} { define TCLSH_CMD false ; # Significant is that it exits with non-0 define HAVE_TCL 0 ; # Will be enabled via --tcl or a successful search define TCLLIBDIR "" ; # Installation dir for TCL extension lib define TCLLIB_RPATH "" ; # rpath for TCL extension lib define TCL_CONFIG_SH ""; # full path to tclConfig.sh if {![opt-bool tcl]} { msg-result "TCL disabled via --disable-tcl. Will not be able to run tests." return } # TODO: document the steps this is taking. global top_srcdir msg-result "Checking for a suitable tcl... " proj-assert {proj-opt-truthy tcl} set use_tcl 1 set with_tclsh [opt-val with-tclsh] set with_tcl [opt-val with-tcl] msg-debug "sqlite-check-tcl: use_tcl ${use_tcl}" msg-debug "sqlite-check-tcl: with_tclsh=${with_tclsh}" msg-debug "sqlite-check-tcl: with_tcl=$with_tcl" if {"" eq $with_tclsh && "" eq $with_tcl} { # If neither --with-tclsh nor --with-tcl are provided, try to find # a workable tclsh. set with_tclsh [proj-first-bin-of tclsh9.0 tclsh8.6 tclsh] msg-debug "sqlite-check-tcl: with_tclsh=${with_tclsh}" } if {"" ne $with_tclsh} { # --with-tclsh was provided. Validate it and use it to trump any # value passed via --with-tcl=DIR. if {![file isfile $with_tclsh]} { proj-fatal "TCL shell $with_tclsh is not a file" } elseif {![file-isexec $with_tclsh]} { proj-fatal "TCL shell $with_tclsh is not executable" } else { define TCLSH_CMD $with_tclsh #msg-result "Using tclsh: $with_tclsh" } if {[catch {exec $with_tclsh $top_srcdir/tool/find_tclconfig.tcl} result] == 0} { set with_tcl $result } if {"" ne $with_tcl && [file isdir $with_tcl]} { msg-result "$with_tclsh recommends the tclConfig.sh from $with_tcl" } else { proj-warn "$with_tclsh is unable to recommand a tclConfig.sh" set use_tcl 0 } } set cfg "" set tclSubdirs {tcl9.0 tcl8.6 lib} while {1} { if {$use_tcl} { if {"" ne $with_tcl} { # Ensure that we can find tclConfig.sh under ${with_tcl}/... if {[file readable "${with_tcl}/tclConfig.sh"]} { set cfg "${with_tcl}/tclConfig.sh" } else { foreach i $tclSubdirs { if {[file readable "${with_tcl}/$i/tclConfig.sh"]} { set cfg "${with_tcl}/$i/tclConfig.sh" break } } } if {"" eq $cfg} { proj-fatal "No tclConfig.sh found under ${with_tcl}" } } else { # If we have not yet found a tclConfig.sh file, look in # $libdir which is set automatically by autosetup or by 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]} { proj-indented-notice { WARNING: Cannot find a usable tclConfig.sh file. Use --with-tcl=DIR to specify a directory where tclConfig.sh can be found. SQLite does not use TCL internally, but TCL is required for testing. } break } } msg-result "Using tclConfig.sh: $cfg" } else { proj-warn "Unable to run tests because no tclConfig.sh file could be located" } break } define TCL_CONFIG_SH $cfg # Export a subset of tclConfig.sh to the current TCL-space. If the # config is not available, this emits empty-string entries for the # various options we're interested in. eval [exec "${top_srcdir}/tool/tclConfigShToTcl.sh" "$cfg"] if {"" eq $with_tclsh} { proj-assert {expr {$cfg ne ""}} proj-assert {expr {"" ne [get-define TCL_EXEC_PREFIX]}} set with_tclsh [get-define TCL_EXEC_PREFIX]/bin/tclsh[get-define TCL_VERSION] if {![file-isexec $with_tclsh]} { set with_tclsh2 [get-define TCL_EXEC_PREFIX]/bin/tclsh if {![file-isexec $with_tclsh2]} { proj-warn "Cannot find a usable tclsh (tried: $with_tclsh $with_tclsh2)" } else { set with_tclsh $with_tclsh2 } } } define TCLSH_CMD $with_tclsh proj-assert {expr {( $cfg eq "" && 0 == $use_tcl ) \ || ( $cfg ne "" && 1 == $use_tcl )}} if {$use_tcl} { # Set up the TCLLIBDIR and TCLLIB_RPATH # # 2024-10-28: calculation of TCLLIBDIR is now done via the shell # in the main.mk (search it for T.tcllibdir) so that # static/hand-written makefiles which import main.mk do not have # to define that before importing main.mk. Even so, we export # TCLLIBDIR from here for the benefit of those who want to provide # it at configure-time and have it "stick", without having to # provide it on each make invocation or set it in their # environment. set tcllibdir [get-env TCLLIBDIR ""] if {"" eq $tcllibdir} { # Attempt to extract TCLLIBDIR from TCL's $auto_path if {[catch {exec echo "puts stdout \$auto_path" | "$with_tclsh"} result] == 0} { foreach i $result { if {[file isdir $i]} { set tcllibdir $i/sqlite3 break } } } else { proj-warn "Cannot determine TCLLIBDIR" } } set tclrpath "" if {"" ne $tcllibdir} { set rp [get-define SH_LINKRPATH] set tclrpath [string map [list "%s" $tcllibdir] $rp] # Reminder: tclConfig.sh has TCL_LD_SEARCH_FLAGS to set the # rpath but (A) it includes an unexpand var ref to # ${LIB_RUNTIME_DIR}, which must be set in the makefile and (B) # that flag is inherently compiler-dependent so it's not as # portable as tclConfig.sh assumes. We'll instead use the rpath # flag which autosetup determines for the current compiler. } define TCLLIBDIR $tcllibdir define TCLLIB_RPATH $tclrpath #msg-debug "TCLLIB_RPATH = [get-define TCLLIB_RPATH]" }; # find TCLLIBDIR if {[file exists $with_tclsh]} { msg-result "Using tclsh: $with_tclsh" define HAVE_TCL 1 } else { proj-warn "Cannot find a usable tclsh, so cannot run tests." } }; # sqlite-check-tcl sqlite-check-tcl ######################################################################## # sqlite-determine-codegen-tcl checks which TCL to use as a code # generator. By default, prefer jimsh simply because we have it # in-tree (it's part of autosetup) unless --with-tclsh=X is used, in # which case prefix X. # # Returns the name of the TCL it selects. Fails fatally if it cannot # detect a TCL appropriate for code generation. # # Defines: # # - BTCLSH = the TCL shell used for code generation. It may set this # to an unexpanded makefile var name. # # - CFLAGS_JIMSH = any flags needed for buildng a BTCLSH-compatible # jimsh. proc sqlite-determine-codegen-tcl {} { msg-result "Checking for TCL to use for code generation... " define CFLAGS_JIMSH {} set cgtcl [opt-val with-tclsh jimsh] if {"jimsh" ne $cgtcl} { # When --with-tclsh=X is used, use that for all TCL purposes, # including in-tree code generation, per developer request. define BTCLSH "\$(TCLSH_CMD)" } else { # These headers are technically optional for JimTCL but necessary if # we want to use it for code generation: set sysh [cc-check-includes dirent.h sys/time.h] if {$sysh && [cc-check-functions realpath]} { define-append CFLAGS_JIMSH -DHAVE_REALPATH define BTCLSH "\$(JIMSH)" } elseif {$sysh && [cc-check-functions _fullpath]} { # _fullpath() is a Windows API define-append CFLAGS_JIMSH -DHAVE__FULLPATH define BTCLSH "\$(JIMSH)" } elseif {[file exists [get-define TCLSH_CMD]]} { set cgtcl [get-define TCLSH_CMD] define BTCLSH "\$(TCLSH_CMD)" } else { # One last-ditch effort to find TCLSH_CMD: use info from # tclConfig.sh to try to find a tclsh if {"" eq [get-define TCLSH_CMD]} { set tpre [get-define TCL_EXEC_PREFIX] if {"" ne $tpre} { set tv [get-define TCL_VERSION] if {[file-isexec "${tpre}/bin/tclsh${tv}"]} { define TCLSH_CMD "${tpre}/bin/tclsh${tv}" } elseif {[file-isexec "${tpre}/bin/tclsh"]} { define TCLSH_CMD "${tpre}/bin/tclsh" } } } set cgtcl [get-define TCLSH_CMD] if {![file exists $cgtcl]} { proj-fatal "Cannot find a tclsh to use for code generation." } define BTCLSH "\$(TCLSH_CMD)" } } return $cgtcl }; # sqlite-determine-codegen-tcl msg-result "TCL for code generation: [sqlite-determine-codegen-tcl]" # /TCL ######################################################################## ######################################################################## # Thread safety? msg-checking "Support threadsafe operation? " proj-if-opt-truthy threadsafe { msg-result yes sqlite-add-feature-flag -DSQLITE_THREADSAFE=1 if {![proj-check-function-in-lib pthread_create pthread] || ![proj-check-function-in-lib pthread_mutexattr_init pthread]} { user-error "Missing required pthread bits" } define LDFLAGS_PTHREAD [get-define lib_pthread_create] undefine lib_pthread_create } { msg-result no sqlite-add-feature-flag -DSQLITE_THREADSAFE=0 define LDFLAGS_PTHREAD "" } ######################################################################## # Do we want temporary databases in memory? # if {1} { set ts [opt-val with-tempstore no] set tsn 1 msg-checking "Use an in-ram database for temporary tables? " switch -- $ts { never { set tsn 0 } no { set tsn 1 } yes { set tsn 2 } always { set tsn 3 } default { user-error "Invalid with-tempstore value \[$ts]. Use one of: never, no, yes, always" } } msg-result $ts define TEMP_STORE $tsn unset ts tsn } ######################################################################## # Attempts to determine whether the given linenoise header file is of # the "antirez" or "msteveb" flavor. It returns 1 for antirez, 2 for # msteveb, and 0 if it's neither. proc sqlite-which-linenoise {dotH} { set sourceOrig { #include #include #include /* size_t has to be be in there _somewhere_ */ } append sourceOrig [proj-file-content $dotH] set sourceTail { int main(void) { linenoiseSetCompletionCallback(0 $arg); return 0; } } set arg "" set source $sourceOrig append source [subst $sourceTail] if {[cctest -nooutput 1 -source $source]} { return 1 } set arg ", 0" set source $sourceOrig append source [subst $sourceTail] if {[cctest -nooutput 1 -source $source]} { return 2 } return 0 } ######################################################################## # sqlite-check-line-editing jumps through proverbial hoops to try to # find a working line-editing library, setting: # # - HAVE_READLINE to 0 or 1 # - HAVE_LINENOISE to 0, 1, or 2 # - HAVE_EDITLINE to 0 or 1 # # Only one of ^^^ those will be set to 1. # # - LDFLAGS_READLINE = linker flags or empty string # # - CFLAGS_READLINE = compilation flags for clients or empty string. # # Note that LDFLAGS_READLINE and CFLAGS_READLINE may refer to # linenoise or editline, not necessarily libreadline. In some cases # it will set HAVE_READLINE=1 when it's really using editline, for # reasons described in this function's comments. # # Returns a string describing which line-editing approach to use, or # "none" if no option is available. # # Order of checks: # # 1) --with-linenoise trumps all others # # 2) --editline trumps --readline # # 3) --disable-readline trumps --readline # # 4) Default to automatic search for optional readline # # 5) Try to find readline resp. editline. If it's not found AND the # corresponding --FEATURE flag was explicitly given, fail fatally, # else fail silently. proc sqlite-check-line-editing {} { msg-result "Checking for line-editing capability..." define HAVE_READLINE 0 define HAVE_LINENOISE 0 define HAVE_EDITLINE 0 define LDFLAGS_READLINE "" define CFLAGS_READLINE "" set failIfNotFound 0 ; # Set to 1 for explicit --FEATURE requests set libsForReadline {readline edit} ; # -l names to check for readline(). # The libedit check changes this. set editLibName "readline" ; # "readline" or "editline" set editLibDef "HAVE_READLINE" ; # "HAVE_READLINE" or "HAVE_EDITLINE" set dirLn [opt-val with-linenoise] if {"" ne $dirLn} { # Use linenoise from a copy of its sources (not a library)... if {![file isdir $dirLn]} { proj-fatal "--with-linenoise value is not a directory" } set lnH $dirLn/linenoise.h if {![file exists $lnH] } { proj-fatal "Cannot find linenoise.h in $dirLn" } set flavor "" switch -- [sqlite-which-linenoise $lnH] { 1 { set flavor "antirez" define HAVE_LINENOISE 1 } 2 { set flavor "msteveb" define HAVE_LINENOISE 2 } default { user-error "Cannot determine the flavor of linenoise from $lnH" } } set lnC "" set lnCOpts {linenoise-ship.c linenoise.c} foreach f $lnCOpts { if {[file exists $dirLn/$f]} { set lnC $dirLn/$f break; } } if {"" eq $lnC} { proj-fatal "Cannot find any of $lnCOpts in $dirLn" } msg-result "Using $flavor linenoise from $dirLn" define CFLAGS_READLINE "-I$dirLn $lnC" sqlite-add-shell-opt -DHAVE_LINENOISE=[get-define HAVE_LINENOISE] return "linenoise ($flavor)" } elseif {[opt-bool editline]} { # libedit mimics libreadline and on some systems does not have its # own header installed (instead, that of libreadline is used). # # shell.c historically expects HAVE_EDITLINE to be set for # libedit, but it then expects to see , which # some system's don't actually have, despite having libedit. If # we end up finding below, we will use # -DHAVE_EDITLINE=1, else we will use -DHAVE_READLINE=1. In either # case, we will link against libedit. set failIfNotFound 1 set libsForReadline {edit} set editLibName editline } elseif {![opt-bool readline]} { msg-result "Readline support explicitly disabled with --disable-readline" return "none" } elseif {[proj-opt-was-provided readline]} { # If an explicit --[enable-]readline was used, fail if it's not # found, else treat the feature as optional. set failIfNotFound 1 } # Transform with-readline-header=X to with-readline-cflags=-I... set v [opt-val with-readline-header] proj-opt-set with-readline-header "" if {"" ne $v} { if {"auto" eq $v} { proj-opt-set with-readline-cflags auto } else { set v [file dirname $v] if {[string match */readline $v]} { # Special case: if the path includes .../readline/readline.h, set # the -I to one dir up from that because our sources include # or . Reminder: if # auto.def is being run by jimsh0 then [file normalize] will not # work! set v [file dirname $v] } proj-opt-set with-readline-cflags "-I$v" } } # Look for readline.h set rlInc [opt-val with-readline-cflags auto] if {"auto" eq $rlInc} { set rlInc "" if {$::cross_compiling} { # ^^^ this check is derived from the legacy configure script. proj-warn "Skipping check for readline.h because we're cross-compiling." } else { set dirs "[get-define prefix] /usr /usr/local /usr/local/readline /usr/contrib /mingw" set subdirs "include/$editLibName" if {"editline" eq $editLibName} { lappend subdirs include/readline # ^^^ editline, on some systems, does not have its own header, # and uses libreadline's header. } lappend subdirs include # ^^^ The dirs and subdirs lists are, except for the inclusion # of $prefix and editline, from the legacy configure script set rlInc [proj-search-for-header-dir readline.h \ -dirs $dirs -subdirs $subdirs] if {"" ne $rlInc} { if {[string match */readline $rlInc]} { set rlInc [file dirname $rlInc]; # shell #include's } elseif {[string match */editline $rlInc]} { set editLibDef HAVE_EDITLINE set rlInc [file dirname $rlInc]; # shell #include's } set rlInc "-I${rlInc}" } } } elseif {"" ne $rlInc && ![string match *-I* $rlInc]} { proj-fatal "Argument to --with-readline-cflags is intended to be CFLAGS and contain -I..." } # If readline.h was found/specified, look for lib(readline|edit)... # # This is not quite straightforward because both libreadline and # libedit typically require some other library which (according to # legacy autotools-generated tests) provides tgetent(3). On some # systems that's built into libreadline/edit, on some (most?) its in # lib[n]curses, and on some it's in libtermcap. set rlLib "" if {"" ne $rlInc} { set rlLib [opt-val with-readline-ldflags] if {"" eq $rlLib || "auto" eq $rlLib} { set rlLib "" set libTerm "" if {[proj-check-function-in-lib tgetent "$editLibName ncurses curses termcap"]} { # ^^^ that libs list comes from the legacy configure script ^^^ set libTerm [get-define lib_tgetent] undefine lib_tgetent } if {$editLibName eq $libTerm} { set rlLib $libTerm } elseif {[proj-check-function-in-lib readline $libsForReadline $libTerm]} { set rlLib [get-define lib_readline] lappend rlLib $libTerm undefine lib_readline } } } # If we found a library, configure the build to use it... if {"" ne $rlLib} { if {"editline" eq $editLibName && "HAVE_READLINE" eq $editLibDef} { proj-indented-notice { NOTE: the local libedit but uses so we will compile with -DHAVE_READLINE=1 but will link with libedit. } } set rlLib [join $rlLib] set rlInc [join $rlInc] define LDFLAGS_READLINE $rlLib define CFLAGS_READLINE $rlInc proj-assert {expr {$editLibDef in {HAVE_READLINE HAVE_EDITLINE}}} proj-assert {expr {$editLibName in {readline editline}}} sqlite-add-shell-opt -D${editLibDef}=1 msg-result "Using $editLibName flags: $rlInc $rlLib" # Check whether rl_completion_matches() has a signature we can use # and disable that sub-feature if it doesn't. if {![cctest \ -cflags "$rlInc -D${editLibDef}" -libs $rlLib -nooutput 1 -source { #include #ifdef HAVE_EDITLINE #include #else #include #endif static char * rcg(const char *z, int i){(void)z; (void)i; return 0;} int main(void) { char ** x = rl_completion_matches("one", rcg); (void)x; return 0; } }]} { user-notice "WARNING: readline-style completion disabled due to rl_completion_matches() signature mismatch" show-notices sqlite-add-shell-opt -DSQLITE_OMIT_READLINE_COMPLETION } return $editLibName } if {$failIfNotFound} { proj-fatal "Explicit --$editLibName failed to find a matching library." } return "none" }; # sqlite-check-line-editing msg-result "Line-editing support for the sqlite3 shell: [sqlite-check-line-editing]" proj-if-opt-truthy load-extension { if {[proj-check-function-in-lib dlopen dl]} { define LDFLAGS_DLOPEN [get-define lib_dlopen] undefine lib_dlopen } else { user-error "dlopen() not found. Use --disable-load-extension to bypass this check." } } { define LDFLAGS_DLOPEN "" sqlite-add-feature-flag {-DSQLITE_OMIT_LOAD_EXTENSION=1} msg-result "Disabling loadable extensions." } proj-if-opt-truthy math { if {![proj-check-function-in-lib ceil m]} { user-error "Cannot find libm functions. Use --disable-math to bypass this." } define LDFLAGS_MATH [get-define lib_ceil] undefine lib_ceil sqlite-add-feature-flag {-DSQLITE_ENABLE_MATH_FUNCTIONS} msg-result "Enabling math SQL functions [get-define LDFLAGS_MATH]" } { define LDFLAGS_MATH "" msg-result "Disabling math SQL functions" } ######################################################################## # ICU - International Components for Unicode # # Handles these flags: # # --with-icu-ldflags=LDFLAGS # --with-icu-config[=/path/to/icu-config] # --enable-icu-collations # # If both icu-ldflags and icu-config are provided, they are # cumulative. If neither are provided, icu-collations is not honored # and a warning is emitted if it is provided. # # Design note: though we can automatically enable ICU if the # icu-config binary is found, we specifically do not. ICU is always an # opt-in feature. proc sqlite-check-icu {} { define LDFLAGS_ICU [join [opt-val with-icu-ldflags ""]] # Flags sets seen in the wild for ICU: # - -licui18n -licuuc -licudata # - -licui18n -licuuc # - /usr/local/bin/icu-config --ldflags if {[proj-opt-was-provided with-icu-config]} { set bin [opt-val with-icu-config] if {"auto" eq $bin} { set bin [proj-first-bin-of \ [get-define prefix]/bin/icu-config \ /usr/local/bin/icu-config \ /usr/bin/icu-config] if {"" eq $bin} { proj-fatal "--with-icu-config=auto cannot find icu-config binary" } } if {[file-isexec $bin]} { set x [exec $bin --ldflags] if {"" eq $x} { proj-fatal "$bin --ldflags returned no data" } define-append LDFLAGS_ICU $x } else { proj-fatal "--with-icu-config=$bin does not refer to an executable" } } set flags [define LDFLAGS_ICU [string trim [get-define LDFLAGS_ICU]]] if {"" ne $flags} { sqlite-add-feature-flag -shell -DSQLITE_ENABLE_ICU msg-result "Enabling ICU support with libs: $flags" if {[opt-bool icu-collations]} { msg-result "Enabling ICU collations." sqlite-add-feature-flag -shell -DSQLITE_ENABLE_ICU_COLLATIONS # Recall that shell.c builds with sqlite3.c } } elseif {[opt-bool icu-collations]} { proj-warn "ignoring --enable-icu-collations because neither --with-icu-ldflags nor --with-icu-config provided any linker flags" } else { msg-result "ICU support is disabled." } }; # sqlite-check-icu sqlite-check-icu ######################################################################## # Emscripten SDK for building the web-based wasm components. # proc sqlite-check-emsdk {} { set emccsh $::srcdir/tool/emcc.sh if {![get-define HAVE_WASI_SDK] && [proj-check-emsdk]} { define EMCC_WRAPPER $emccsh proj-make-from-dot-in $emccsh catch {exec chmod u+x $emccsh} } else { define EMCC_WRAPPER "" file delete -force $emccsh } } sqlite-check-emsdk ######################################################################## # Check for log(3) in libm and die with an error if it is not # found. $featureName should be the feature name which requires that # function (it's used only in error messages). defines LDFLAGS_MATH to # the required linker flags (which may be empty even if the math APIs # are found, depending on the OS). proc affirm-have-math {featureName} { if {![msg-quiet proj-check-function-in-lib log m]} { user-error "Missing math APIs for $featureName" } define LDFLAGS_MATH [get-define lib_log ""] undefine lib_log } ######################################################################## # Handle various SQLITE_ENABLE_... feature flags. msg-result "Feature flags..." foreach {boolFlag featureFlag ifSetEvalThis} { all {} { proj-opt-set fts4 proj-opt-set fts5 proj-opt-set geopoly proj-opt-set rtree proj-opt-set session } fts4 -DSQLITE_ENABLE_FTS4 {affirm-have-math fts4} fts5 -DSQLITE_ENABLE_FTS5 {affirm-have-math fts5} geopoly -DSQLITE_ENABLE_GEOPOLY {proj-opt-set rtree} rtree -DSQLITE_ENABLE_RTREE {} session {-DSQLITE_ENABLE_SESSION -DSQLITE_ENABLE_PREUPDATE_HOOK} {} update-limit -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT {} memsys5 -DSQLITE_ENABLE_MEMSYS5 {} memsys3 {} { if {[opt-bool memsys5]} { proj-warn "not enabling memsys3 because memsys5 is enabled." expr 0 } else { sqlite-add-feature-flag -DSQLITE_ENABLE_MEMSYS3 } } } { proj-if-opt-truthy $boolFlag { sqlite-add-feature-flag $featureFlag if {0 != [eval $ifSetEvalThis] && "all" ne $boolFlag} { msg-result " + $boolFlag" } } { if {"all" ne $boolFlag} { msg-result " - $boolFlag" } } } ######################################################################## # Invert the above loop's logic for some explicit SQLITE_OMIT_... # cases. If config option $boolFlag is set, [sqlite-add-feature-flag # $featureFlag], where $featureFlag is intended to be # -DSQLITE_OMIT_... foreach {boolFlag featureFlag} { json -DSQLITE_OMIT_JSON } { if {[proj-opt-truthy $boolFlag]} { msg-result " + $boolFlag" } else { sqlite-add-feature-flag $featureFlag msg-result " - $boolFlag" } } ######################################################################### # Show the final feature flag sets: set oFF [get-define OPT_FEATURE_FLAGS] if {"" ne $oFF} { define OPT_FEATURE_FLAGS [lsort -unique $oFF] msg-result "Library feature flags: [get-define OPT_FEATURE_FLAGS]" } set oFF [get-define OPT_SHELL] if {"" ne $oFF} { define OPT_SHELL [lsort -unique $oFF] msg-result "Shell options: [get-define OPT_SHELL]" } unset oFF ######################################################################## # Maybe extend JimTCL a bit. As of this writing (2024-09-27) it only # needs (-DHAVE_REALPATH or -DHAVE__FULLPATH) and -DHAVE_DIRENT_H to # be compatible with our code generators. It can, however, be slightly # extended via easy-to-detect features, should we need them. if {0 && "" ne [get-define CFLAGS_JIMSH]} { foreach jimFunc {opendir fsync isascii} { if {[cc-check-functions $jimFunc]} { define-append CFLAGS_JIMSH -DHAVE_[string toupper $jimFunc] } } #These are hard-coded into jimsh0.c, so we must not -D them: #foreach jimDef {HAVE_UNISTD_H HAVE_DIRENT_H} { # if {[is-defined $jimDef]} { # define-append CFLAGS_JIMSH -D$jimDef # } #} define-append CFLAGS_JIMSH -DHAVE_LONG_LONG; # SQLite relies on long long, so we know it's available }; # JimTCL ######################################################################## # Generate the output files. # # Potential TODO (unclear): in sqlite3.pc.in, do we need to include # any CFLAGS_READLINE, CFLAGS_ZLIB, etc in its "Cflags:" section? proj-make-from-dot-in -touch Makefile sqlite3.pc if {0} { # Requires a hand-written sqlite_cfg.h.in... proj-make-from-dot-in sqlite_cfg.h # vs... } else { # Requires no input template... make-config-header sqlite_cfg.h \ -bare {SIZEOF_* HAVE_DECL_*} \ -none {HAVE_CFLAG_* LDFLAGS_* SH_* SQLITE_AUTORECONFIG TARGET_* USE_GCOV TCL_*} \ -auto {HAVE_* PACKAGE_*} \ -none * proj-touch sqlite_cfg.h ; # help avoid frequent unnecessary @SQLITE_AUTORECONFIG@ } #TODO proj-make-from-dot-in ext/wasm/GNUmakefile if {"" ne $DUMP_DEFINES_JSON} { ######################################################################## # Dump config-defines.json... # Demonstrate (mis?)handling of spaces in JSON-export array values: # define-append OPT_FOO.list {"-DFOO=bar baz" -DBAR="baz barre"} define OPT_FEATURE_FLAGS.list [get-define OPT_FEATURE_FLAGS] define OPT_SHELL.list [get-define OPT_SHELL] set dumpDefsOpt { -bare {SIZEOF_* HAVE_DECL_*} -none {HAVE_CFLAG_* LDFLAGS_* SH_* SQLITE_AUTORECONFIG TARGET_* USE_GCOV TCL_*} -array {*.list} -auto {OPT_* PACKAGE_* HAVE_*} } if {[opt-bool defines-json-include-lowercase]} { lappend dumpDefsOpt -none {lib_*} ; # remnants from proj-check-function-in-lib and friends lappend dumpDefsOpt -auto {[a-z]*} } lappend dumpDefsOpt -none * proj-dump-defs-json $DUMP_DEFINES_JSON {*}$dumpDefsOpt undefine OPT_FEATURE_FLAGS.list undefine OPT_SHELL.list } ######################################################################## # Some build-dev/debug-only output proj-if-opt-truthy dump-defines { msg-result "--dump-defines is creating $::DUMP_DEFINES_TXT" make-config-header $::DUMP_DEFINES_TXT \ -bare {SQLITE_OS* SQLITE_DEBUG USE_*} \ -str {BIN_* CC LD AR LDFLAG* OPT_*} \ -auto {*} # achtung: ^^^^ whichever SQLITE_OS_foo flag which is set to 0 will # get _undefined_ here unless it's part of the -bare set. if {0} { foreach x [all-defines] { puts "\t$x" } } }