mirror of
https://github.com/sqlite/sqlite.git
synced 2024-11-28 16:09:31 +01:00
eb83747441
FossilOrigin-Name: c8b24c590ef318e687ab76cd1a5d6c8fed84389e3ebbe544aa8b15759324958a
1119 lines
38 KiB
Tcl
1119 lines
38 KiB
Tcl
########################################################################
|
|
# 2024 September 25
|
|
#
|
|
# 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.
|
|
#
|
|
########################################################################
|
|
# Routines for Steve Bennett's autosetup which are common to trees
|
|
# managed in and around the umbrella of the SQLite project.
|
|
#
|
|
# The intent is that these routines be relatively generic, independent
|
|
# of a given project.
|
|
#
|
|
# This file was initially derived from one used in the libfossil
|
|
# project, authored by the same person who ported it here, and this is
|
|
# noted here only as an indication that there are no licensing issues
|
|
# despite this code having a handful of near-twins running around a
|
|
# handful of third-party source trees.
|
|
#
|
|
########################################################################
|
|
#
|
|
# Design notes:
|
|
#
|
|
# - Symbols with a suffix of _ are intended for internal use within
|
|
# this file, and are not part of the API which auto.def files should
|
|
# rely on.
|
|
#
|
|
# - By and large, autosetup prefers to update global state with the
|
|
# results of feature checks, e.g. whether the compiler supports flag
|
|
# --X. In this developer's opinion that (A) causes more confusion
|
|
# than it solves[^1] and (B) adds an unnecessary layer of "voodoo"
|
|
# between the autosetup user and its internals. This module, in
|
|
# contrast, instead injects the results of its own tests into
|
|
# well-defined variables and leaves the integration of those values
|
|
# to the caller's discretion.
|
|
#
|
|
# [1]: As an example: testing for the -rpath flag, using
|
|
# cc-check-flags, can break later checks which use
|
|
# [cc-check-function-in-lib ...] because the resulting -rpath flag
|
|
# implicitly becomes part of those tests. In the case of an rpath
|
|
# test, downstream tests may not like the $prefix/lib path added by
|
|
# the rpath test. To avoid such problems, we avoid (intentionally)
|
|
# updating global state via feature tests.
|
|
########################################################################
|
|
|
|
########################################################################
|
|
# $proj_ is an internal-use-only array for storing whatever generic
|
|
# internal stuff we need stored.
|
|
array set proj_ {}
|
|
set proj_(isatty) [isatty? stdout]
|
|
|
|
proc proj-warn {msg} {
|
|
puts stderr [proj-bold "WARNING: $msg"]
|
|
}
|
|
proc proj-fatal {msg} {
|
|
show-notices
|
|
puts stderr [proj-bold "ERROR: $msg"]
|
|
exit 1
|
|
}
|
|
|
|
########################################################################
|
|
# Kind of like a C assert if uplevel (eval) of $script is false,
|
|
# triggers a fatal error.
|
|
proc proj-assert {script} {
|
|
if {1 == [get-env proj-assert 0]} {
|
|
msg-result [proj-bold "asserting: [string trim $script]"]
|
|
}
|
|
if {![uplevel 1 $script]} {
|
|
proj-fatal "Assertion failed: $script"
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# If this function believes that the current console might support
|
|
# ANSI escape sequences then this returns $str wrapped in a sequence
|
|
# to bold that text, else it returns $str as-is.
|
|
proc proj-bold {str} {
|
|
if {$::autosetup(iswin) || !$::proj_(isatty)} {
|
|
return $str
|
|
}
|
|
return "\033\[1m${str}\033\[0m"
|
|
}
|
|
|
|
########################################################################
|
|
# Takes a multi-line message and emits it with consistent indentation
|
|
# using [user-notice] (which means its rendering will (A) go to stderr
|
|
# and (B) be delayed until the next time autosetup goes to output a
|
|
# message).
|
|
#
|
|
# If its first argument is -error then it renders the message
|
|
# immediately and then exits.
|
|
proc proj-indented-notice {args} {
|
|
set fErr ""
|
|
if {"-error" eq [lindex $args 0]} {
|
|
set args [lassign $args fErr]
|
|
}
|
|
set lines [split [join $args] \n]
|
|
foreach line $lines {
|
|
user-notice " [string trimleft $line]"
|
|
}
|
|
if {"" ne $fErr} {
|
|
show-notices
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# Returns 1 if cross-compiling, else 0.
|
|
proc proj-is-cross-compiling {} {
|
|
return [expr {[get-define host] ne [get-define build]}]
|
|
}
|
|
|
|
########################################################################
|
|
# proj-lshift_ shifts $count elements from the list named $listVar
|
|
# and returns them as a new list. On empty input, returns "".
|
|
#
|
|
# Modified slightly from: https://wiki.tcl-lang.org/page/lshift
|
|
proc proj-lshift_ {listVar {count 1}} {
|
|
upvar 1 $listVar l
|
|
if {![info exists l]} {
|
|
# make the error message show the real variable name
|
|
error "can't read \"$listVar\": no such variable"
|
|
}
|
|
if {![llength $l]} {
|
|
# error Empty
|
|
return ""
|
|
}
|
|
set r [lrange $l 0 [incr count -1]]
|
|
set l [lreplace $l [set l 0] $count]
|
|
return $r
|
|
}
|
|
|
|
########################################################################
|
|
# Expects to receive string input, which it splits on newlines, strips
|
|
# out any lines which begin with an number of whitespace followed by a
|
|
# '#', and returns a value containing the [append]ed results of each
|
|
# remaining line with a \n between each.
|
|
proc proj-strip-hash-comments_ {val} {
|
|
set x {}
|
|
foreach line [split $val \n] {
|
|
if {![string match "#*" [string trimleft $line]]} {
|
|
append x $line \n
|
|
}
|
|
}
|
|
return $x
|
|
}
|
|
|
|
########################################################################
|
|
# A proxy for cc-check-function-in-lib which "undoes" any changes that
|
|
# routine makes to the LIBS define. Returns the result of
|
|
# cc-check-function-in-lib.
|
|
proc proj-check-function-in-lib {function libs {otherlibs {}}} {
|
|
set found 0
|
|
define-push {LIBS} {
|
|
set found [cc-check-function-in-lib $function $libs $otherlibs]
|
|
}
|
|
return $found
|
|
}
|
|
|
|
########################################################################
|
|
# Searches for $header in a combination of dirs and subdirs, specified
|
|
# by the -dirs {LIST} and -subdirs {LIST} flags (each of which have
|
|
# sane defaults). Returns either the first matching dir or an empty
|
|
# string. The return value does not contain the filename part.
|
|
proc proj-search-for-header-dir {header args} {
|
|
set subdirs {include}
|
|
set dirs {/usr /usr/local /mingw}
|
|
# Debatable:
|
|
# if {![proj-is-cross-compiling]} {
|
|
# lappend dirs [get-define prefix]
|
|
# }
|
|
while {[llength $args]} {
|
|
switch -exact -- [lindex $args 0] {
|
|
-dirs { set args [lassign $args - dirs] }
|
|
-subdirs { set args [lassign $args - subdirs] }
|
|
default {
|
|
proj-fatal "Unhandled argument: $args"
|
|
}
|
|
}
|
|
}
|
|
foreach dir $dirs {
|
|
foreach sub $subdirs {
|
|
if {[file exists $dir/$sub/$header]} {
|
|
return "$dir/$sub"
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
########################################################################
|
|
# Usage: proj-find-executable-path ?-v? binaryName
|
|
#
|
|
# Works similarly to autosetup's [find-executable-path $binName] but:
|
|
#
|
|
# - If the first arg is -v, it's verbose about searching, else it's quiet.
|
|
#
|
|
# Returns the full path to the result or an empty string.
|
|
proc proj-find-executable-path {args} {
|
|
set binName $args
|
|
set verbose 0
|
|
if {[lindex $args 0] eq "-v"} {
|
|
set verbose 1
|
|
set args [lassign $args - binName]
|
|
msg-checking "Looking for $binName ... "
|
|
}
|
|
set check [find-executable-path $binName]
|
|
if {$verbose} {
|
|
if {"" eq $check} {
|
|
msg-result "not found"
|
|
} else {
|
|
msg-result $check
|
|
}
|
|
}
|
|
return $check
|
|
}
|
|
|
|
########################################################################
|
|
# Uses [proj-find-executable-path $binName] to (verbosely) search for
|
|
# a binary, sets a define (see below) to the result, and returns the
|
|
# result (an empty string if not found).
|
|
#
|
|
# The define'd name is: if defName is empty then "BIN_X" is used,
|
|
# where X is the upper-case form of $binName with any '-' characters
|
|
# replaced with '_'.
|
|
proc proj-bin-define {binName {defName {}}} {
|
|
set check [proj-find-executable-path -v $binName]
|
|
if {"" eq $defName} {
|
|
set defName "BIN_[string toupper [string map {- _} $binName]]"
|
|
}
|
|
define $defName $check
|
|
return $check
|
|
}
|
|
|
|
########################################################################
|
|
# Usage: proj-first-bin-of bin...
|
|
#
|
|
# Looks for the first binary found of the names passed to this
|
|
# function. If a match is found, the full path to that binary is
|
|
# returned, else "" is returned.
|
|
#
|
|
# Despite using cc-path-progs to do the search, this function clears
|
|
# any define'd name that function stores for the result (because the
|
|
# caller has no sensible way of knowing which result it was unless
|
|
# they pass only a single argument).
|
|
proc proj-first-bin-of {args} {
|
|
set rc ""
|
|
foreach b $args {
|
|
set u [string toupper $b]
|
|
# Note that cc-path-progs defines $u to false if it finds no match.
|
|
if {[cc-path-progs $b]} {
|
|
set rc [get-define $u]
|
|
}
|
|
undefine $u
|
|
if {"" ne $rc} break
|
|
}
|
|
return $rc
|
|
}
|
|
|
|
########################################################################
|
|
# Returns 1 if the user specifically provided the given configure
|
|
# flag, else 0. This can be used to distinguish between options which
|
|
# have a default value and those which were explicitly provided by the
|
|
# user, even if the latter is done in a way which uses the default
|
|
# value.
|
|
#
|
|
# For example, with a configure flag defined like:
|
|
#
|
|
# { foo-bar:=baz => {its help text} }
|
|
#
|
|
# This function will, when passed foo-bar, return 1 only if the user
|
|
# passes --foo-bar to configure, even if that invocation would resolve
|
|
# to the default value of baz. If the user does not explicitly pass in
|
|
# --foo-bar (with or without a value) then this returns 0.
|
|
proc proj-opt-was-provided {key} {
|
|
dict exists $::autosetup(optset) $key
|
|
}
|
|
|
|
########################################################################
|
|
# Force-set autosetup option $flag to $val. The value can be fetched
|
|
# later with [opt-val], [opt-bool], and friends.
|
|
#
|
|
# Returns $val.
|
|
proc proj-opt-set {flag {val 1}} {
|
|
global autosetup
|
|
if {$flag ni $::autosetup(options)} {
|
|
# We have to add this to autosetup(options) or else future calls
|
|
# to [opt-bool $flag] will fail validation of $flag.
|
|
lappend ::autosetup(options) $flag
|
|
}
|
|
dict set ::autosetup(optset) $flag $val
|
|
return $val
|
|
}
|
|
|
|
########################################################################
|
|
# Returns 1 if $val appears to be a truthy value, else returns
|
|
# 0. Truthy values are any of {1 on enabled yes}
|
|
proc proj-val-truthy {val} {
|
|
expr {$val in {1 on enabled yes}}
|
|
}
|
|
|
|
########################################################################
|
|
# Returns 1 if [opt-val $flag] appears to be a truthy value or
|
|
# [opt-bool $flag] is true. See proj-val-truthy.
|
|
proc proj-opt-truthy {flag} {
|
|
if {[proj-val-truthy [opt-val $flag]]} { return 1 }
|
|
set rc 0
|
|
catch {
|
|
# opt-bool will throw if $flag is not a known boolean flag
|
|
set rc [opt-bool $flag]
|
|
}
|
|
return $rc
|
|
}
|
|
|
|
########################################################################
|
|
# If [proj-opt-truthy $flag] is true, eval $then, else eval $else.
|
|
proc proj-if-opt-truthy {boolFlag thenScript {elseScript {}}} {
|
|
if {[proj-opt-truthy $boolFlag]} {
|
|
uplevel 1 $thenScript
|
|
} else {
|
|
uplevel 1 $elseScript
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# If [proj-opt-truthy $flag] then [define $def $iftrue] else [define
|
|
# $def $iffalse]. If $msg is not empty, output [msg-checking $msg] and
|
|
# a [msg-results ...] which corresponds to the result. Returns 1 if
|
|
# the opt-truthy check passes, else 0.
|
|
proc proj-define-if-opt-truthy {flag def {msg ""} {iftrue 1} {iffalse 0}} {
|
|
if {"" ne $msg} {
|
|
msg-checking "$msg "
|
|
}
|
|
set rcMsg ""
|
|
set rc 0
|
|
if {[proj-opt-truthy $flag]} {
|
|
define $def $iftrue
|
|
set rc 1
|
|
} else {
|
|
define $def $iffalse
|
|
}
|
|
switch -- [proj-val-truthy [get-define $def]] {
|
|
0 { set rcMsg no }
|
|
1 { set rcMsg yes }
|
|
}
|
|
if {"" ne $msg} {
|
|
msg-result $rcMsg
|
|
}
|
|
return $rc
|
|
}
|
|
|
|
########################################################################
|
|
# Args: [-v] optName defName {descr {}}
|
|
#
|
|
# Checks [proj-opt-truthy $optName] and calls [define $defName X]
|
|
# where X is 0 for false and 1 for true. descr is an optional
|
|
# [msg-checking] argument which defaults to $defName. Returns X.
|
|
#
|
|
# If args[0] is -v then the boolean semantics are inverted: if
|
|
# the option is set, it gets define'd to 0, else 1. Returns the
|
|
# define'd value.
|
|
proc proj-opt-define-bool {args} {
|
|
set invert 0
|
|
if {[lindex $args 0] eq "-v"} {
|
|
set invert 1
|
|
set args [lrange $args 1 end]
|
|
}
|
|
set optName [proj-lshift_ args]
|
|
set defName [proj-lshift_ args]
|
|
set descr [proj-lshift_ args]
|
|
if {"" eq $descr} {
|
|
set descr $defName
|
|
}
|
|
set rc 0
|
|
msg-checking "$descr ... "
|
|
if {[proj-opt-truthy $optName]} {
|
|
if {0 eq $invert} {
|
|
set rc 1
|
|
} else {
|
|
set rc 0
|
|
}
|
|
} elseif {0 ne $invert} {
|
|
set rc 1
|
|
}
|
|
msg-result $rc
|
|
define $defName $rc
|
|
return $rc
|
|
}
|
|
|
|
########################################################################
|
|
# Check for module-loading APIs (libdl/libltdl)...
|
|
#
|
|
# Looks for libltdl or dlopen(), the latter either in -ldl or built in
|
|
# to libc (as it is on some platforms). Returns 1 if found, else
|
|
# 0. Either way, it `define`'s:
|
|
#
|
|
# - HAVE_LIBLTDL to 1 or 0 if libltdl is found/not found
|
|
# - HAVE_LIBDL to 1 or 0 if dlopen() is found/not found
|
|
# - LDFLAGS_MODULE_LOADER one of ("-lltdl", "-ldl", or ""), noting
|
|
# that -ldl may legally be empty on some platforms even if
|
|
# HAVE_LIBDL is true (indicating that dlopen() is available without
|
|
# extra link flags). LDFLAGS_MODULE_LOADER also gets "-rdynamic" appended
|
|
# to it because otherwise trying to open DLLs will result in undefined
|
|
# symbol errors.
|
|
#
|
|
# Note that if it finds LIBLTDL it does not look for LIBDL, so will
|
|
# report only that is has LIBLTDL.
|
|
proc proj-check-module-loader {} {
|
|
msg-checking "Looking for module-loader APIs... "
|
|
if {99 ne [get-define LDFLAGS_MODULE_LOADER 99]} {
|
|
if {1 eq [get-define HAVE_LIBLTDL 0]} {
|
|
msg-result "(cached) libltdl"
|
|
return 1
|
|
} elseif {1 eq [get-define HAVE_LIBDL 0]} {
|
|
msg-result "(cached) libdl"
|
|
return 1
|
|
}
|
|
# else: wha???
|
|
}
|
|
set HAVE_LIBLTDL 0
|
|
set HAVE_LIBDL 0
|
|
set LDFLAGS_MODULE_LOADER ""
|
|
set rc 0
|
|
puts "" ;# cosmetic kludge for cc-check-XXX
|
|
if {[cc-check-includes ltdl.h] && [cc-check-function-in-lib lt_dlopen ltdl]} {
|
|
set HAVE_LIBLTDL 1
|
|
set LDFLAGS_MODULE_LOADER "-lltdl -rdynamic"
|
|
msg-result " - Got libltdl."
|
|
set rc 1
|
|
} elseif {[cc-with {-includes dlfcn.h} {
|
|
cctest -link 1 -declare "extern char* dlerror(void);" -code "dlerror();"}]} {
|
|
msg-result " - This system can use dlopen() without -ldl."
|
|
set HAVE_LIBDL 1
|
|
set LDFLAGS_MODULE_LOADER ""
|
|
set rc 1
|
|
} elseif {[cc-check-includes dlfcn.h]} {
|
|
set HAVE_LIBDL 1
|
|
set rc 1
|
|
if {[cc-check-function-in-lib dlopen dl]} {
|
|
msg-result " - dlopen() needs libdl."
|
|
set LDFLAGS_MODULE_LOADER "-ldl -rdynamic"
|
|
} else {
|
|
msg-result " - dlopen() not found in libdl. Assuming dlopen() is built-in."
|
|
set LDFLAGS_MODULE_LOADER "-rdynamic"
|
|
}
|
|
}
|
|
define HAVE_LIBLTDL $HAVE_LIBLTDL
|
|
define HAVE_LIBDL $HAVE_LIBDL
|
|
define LDFLAGS_MODULE_LOADER $LDFLAGS_MODULE_LOADER
|
|
return $rc
|
|
}
|
|
|
|
########################################################################
|
|
# Sets all flags which would be set by proj-check-module-loader to
|
|
# empty/falsy values, as if those checks had failed to find a module
|
|
# loader. Intended to be called in place of that function when
|
|
# a module loader is explicitly not desired.
|
|
proc proj-no-check-module-loader {} {
|
|
define HAVE_LIBDL 0
|
|
define HAVE_LIBLTDL 0
|
|
define LDFLAGS_MODULE_LOADER ""
|
|
}
|
|
|
|
########################################################################
|
|
# Opens the given file, reads all of its content, and returns it. If
|
|
# the first arg is -trim, the contents of the file named by the second
|
|
# argument are trimmed before returning them.
|
|
proc proj-file-content {args} {
|
|
set trim 0
|
|
set fname $args
|
|
if {"-trim" eq [lindex $args 0]} {
|
|
set trim 1
|
|
lassign $args - fname
|
|
}
|
|
set fp [open $fname r]
|
|
set rc [read $fp]
|
|
close $fp
|
|
if {$trim} { return [string trim $rc] }
|
|
return $rc
|
|
}
|
|
|
|
########################################################################
|
|
# Returns the contents of the given file as an array of lines, with
|
|
# the EOL stripped from each input line.
|
|
proc proj-file-content-list {fname} {
|
|
set fp [open $fname r]
|
|
set rc {}
|
|
while { [gets $fp line] >= 0 } {
|
|
lappend rc $line
|
|
}
|
|
close $fp
|
|
return $rc
|
|
}
|
|
|
|
########################################################################
|
|
# Checks the compiler for compile_commands.json support. If passed an
|
|
# argument it is assumed to be the name of an autosetup boolean config
|
|
# which controls whether to run/skip this check.
|
|
#
|
|
# Returns 1 if supported, else 0. Defines MAKE_COMPILATION_DB to "yes"
|
|
# if supported, "no" if not.
|
|
#
|
|
# This test has a long history of false positive results because of
|
|
# compilers reacting differently to the -MJ flag.
|
|
proc proj-check-compile-commands {{configOpt {}}} {
|
|
msg-checking "compile_commands.json support... "
|
|
if {"" ne $configOpt && ![proj-opt-truthy $configOpt]} {
|
|
msg-result "explicitly disabled"
|
|
define MAKE_COMPILATION_DB no
|
|
return 0
|
|
} else {
|
|
if {[cctest -lang c -cflags {/dev/null -MJ} -source {}]} {
|
|
# This test reportedly incorrectly succeeds on one of
|
|
# Martin G.'s older systems. drh also reports a false
|
|
# positive on an unspecified older Mac system.
|
|
msg-result "compiler supports compile_commands.json"
|
|
define MAKE_COMPILATION_DB yes
|
|
return 1
|
|
} else {
|
|
msg-result "compiler does not support compile_commands.json"
|
|
define MAKE_COMPILATION_DB no
|
|
return 0
|
|
}
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# Runs the 'touch' command on one or more files, ignoring any errors.
|
|
proc proj-touch {filename} {
|
|
catch { exec touch {*}$filename }
|
|
}
|
|
|
|
########################################################################
|
|
# Usage:
|
|
#
|
|
# proj-make-from-dot-in ?-touch? filename(s)...
|
|
#
|
|
# Uses [make-template] to create makefile(-like) file(s) $filename
|
|
# from $filename.in but explicitly makes the output read-only, to
|
|
# avoid inadvertent editing (who, me?).
|
|
#
|
|
# If the first argument is -touch then the generated file is touched
|
|
# to update its timestamp. This can be used as a workaround for
|
|
# cases where (A) autosetup does not update the file because it was
|
|
# not really modified and (B) the file *really* needs to be updated to
|
|
# please the build process.
|
|
#
|
|
# Failures when running chmod or touch are silently ignored.
|
|
proc proj-make-from-dot-in {args} {
|
|
set filename $args
|
|
set touch 0
|
|
if {[lindex $args 0] eq "-touch"} {
|
|
set touch 1
|
|
set filename [lrange $args 1 end]
|
|
}
|
|
foreach f $filename {
|
|
set f [string trim $f]
|
|
catch { exec chmod u+w $f }
|
|
make-template $f.in $f
|
|
if {$touch} {
|
|
proj-touch $f
|
|
}
|
|
catch { exec chmod -w $f }
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# Checks for the boolean configure option named by $flagname. If set,
|
|
# it checks if $CC seems to refer to gcc. If it does (or appears to)
|
|
# then it defines CC_PROFILE_FLAG to "-pg" and returns 1, else it
|
|
# defines CC_PROFILE_FLAG to "" and returns 0.
|
|
#
|
|
# Note that the resulting flag must be added to both CFLAGS and
|
|
# LDFLAGS in order for binaries to be able to generate "gmon.out". In
|
|
# order to avoid potential problems with escaping, space-containing
|
|
# tokens, and interfering with autosetup's use of these vars, this
|
|
# routine does not directly modify CFLAGS or LDFLAGS.
|
|
proc proj-check-profile-flag {{flagname profile}} {
|
|
#puts "flagname=$flagname ?[proj-opt-truthy $flagname]?"
|
|
if {[proj-opt-truthy $flagname]} {
|
|
set CC [get-define CC]
|
|
regsub {.*ccache *} $CC "" CC
|
|
# ^^^ if CC="ccache gcc" then [exec] treats "ccache gcc" as a
|
|
# single binary name and fails. So strip any leading ccache part
|
|
# for this purpose.
|
|
if { ![catch { exec $CC --version } msg]} {
|
|
if {[string first gcc $CC] != -1} {
|
|
define CC_PROFILE_FLAG "-pg"
|
|
return 1
|
|
}
|
|
}
|
|
}
|
|
define CC_PROFILE_FLAG ""
|
|
return 0
|
|
}
|
|
|
|
########################################################################
|
|
# Returns 1 if this appears to be a Windows environment (MinGw,
|
|
# Cygwin, MSys), else returns 0. The optional argument is the name of
|
|
# an autosetup define which contains platform name info, defaulting to
|
|
# "host" (meaning, somewhat counterintuitively, the target system, not
|
|
# the current host). The other legal value is "build" (the build
|
|
# machine, i.e. the local host). If $key == "build" then some
|
|
# additional checks may be performed which are not applicable when
|
|
# $key == "host".
|
|
proc proj-looks-like-windows {{key host}} {
|
|
global autosetup
|
|
switch -glob -- [get-define $key] {
|
|
*-*-ming* - *-*-cygwin - *-*-msys {
|
|
return 1
|
|
}
|
|
}
|
|
if {$key eq "build"} {
|
|
# These apply only to the local OS, not a cross-compilation target,
|
|
# as the above check potentially can.
|
|
if {$::autosetup(iswin)} { return 1 }
|
|
if {[find-an-executable cygpath] ne "" || $::tcl_platform(os)=="Windows NT"} {
|
|
return 1
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
########################################################################
|
|
# Looks at either the 'host' (==compilation target platform) or
|
|
# 'build' (==the being-built-on platform) define value and returns if
|
|
# if that value seems to indicate that it represents a Mac platform,
|
|
# else returns 0.
|
|
proc proj-looks-like-mac {{key host}} {
|
|
switch -glob -- [get-define $key] {
|
|
*apple* {
|
|
return 1
|
|
}
|
|
default {
|
|
return 0
|
|
}
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# Checks autosetup's "host" and "build" defines to see if the build
|
|
# host and target are Windows-esque (Cygwin, MinGW, MSys). If the
|
|
# build environment is then BUILD_EXEEXT is [define]'d to ".exe", else
|
|
# "". If the target, a.k.a. "host", is then TARGET_EXEEXT is
|
|
# [define]'d to ".exe", else "".
|
|
proc proj-exe-extension {} {
|
|
set rH ""
|
|
set rB ""
|
|
if {[proj-looks-like-windows host]} {
|
|
set rH ".exe"
|
|
}
|
|
if {[proj-looks-like-windows build]} {
|
|
set rB ".exe"
|
|
}
|
|
define BUILD_EXEEXT $rB
|
|
define TARGET_EXEEXT $rH
|
|
}
|
|
|
|
########################################################################
|
|
# Works like proj-exe-extension except that it defines BUILD_DLLEXT
|
|
# and TARGET_DLLEXT to one of (.so, ,dll, .dylib).
|
|
#
|
|
# Trivia: for .dylib files, the linker needs the -dynamiclib flag
|
|
# instead of -shared.
|
|
proc proj-dll-extension {} {
|
|
proc inner {key} {
|
|
switch -glob -- [get-define $key] {
|
|
*apple* {
|
|
return ".dylib"
|
|
}
|
|
*-*-ming* - *-*-cygwin - *-*-msys {
|
|
return ".dll"
|
|
}
|
|
default {
|
|
return ".so"
|
|
}
|
|
}
|
|
}
|
|
define BUILD_DLLEXT [inner build]
|
|
define TARGET_DLLEXT [inner host]
|
|
}
|
|
|
|
########################################################################
|
|
# Static-library counterpart of proj-dll-extension. Defines
|
|
# BUILD_LIBEXT and TARGET_LIBEXT to the conventional static library
|
|
# extension for the being-built-on resp. the target platform.
|
|
proc proj-lib-extension {} {
|
|
proc inner {key} {
|
|
switch -glob -- [get-define $key] {
|
|
*-*-ming* - *-*-cygwin - *-*-msys {
|
|
return ".lib"
|
|
}
|
|
default {
|
|
return ".a"
|
|
}
|
|
}
|
|
}
|
|
define BUILD_LIBEXT [inner build]
|
|
define TARGET_LIBEXT [inner host]
|
|
}
|
|
|
|
########################################################################
|
|
# Calls all of the proj-*-extension functions.
|
|
proc proj-file-extensions {} {
|
|
proj-exe-extension
|
|
proj-dll-extension
|
|
proj-lib-extension
|
|
}
|
|
|
|
########################################################################
|
|
# Expects a list of file names. If any one of them does not exist in
|
|
# the filesystem, it fails fatally with an informative message.
|
|
# Returns the last file name it checks. If the first argument is -v
|
|
# then it emits msg-checking/msg-result messages for each file.
|
|
proc proj-affirm-files-exist {args} {
|
|
set rc ""
|
|
set verbose 0
|
|
if {[lindex $args 0] eq "-v"} {
|
|
set verbose 1
|
|
set args [lrange $args 1 end]
|
|
}
|
|
foreach f $args {
|
|
if {$verbose} { msg-checking "Looking for $f ... " }
|
|
if {![file exists $f]} {
|
|
user-error "not found: $f"
|
|
}
|
|
if {$verbose} { msg-result "" }
|
|
set rc $f
|
|
}
|
|
return rc
|
|
}
|
|
|
|
########################################################################
|
|
# Emscripten is used for doing in-tree builds of web-based WASM stuff,
|
|
# as opposed to WASI-based WASM or WASM binaries we import from other
|
|
# places. This is only set up for Unix-style OSes and is untested
|
|
# anywhere but Linux.
|
|
#
|
|
# Defines the following:
|
|
#
|
|
# - EMSDK_HOME = top dir of the emsdk or "". It looks for
|
|
# --with-emsdk=DIR or the $EMSDK environment variable.
|
|
# - EMSDK_ENV = path to EMSDK_HOME/emsdk_env.sh or ""
|
|
# - BIN_EMCC = $EMSDK_HOME/upstream/emscripten/emcc or ""
|
|
# - HAVE_EMSDK = 0 or 1 (this function's return value)
|
|
#
|
|
# Returns 1 if EMSDK_ENV is found, else 0. If EMSDK_HOME is not empty
|
|
# but BIN_EMCC is then emcc was not found in the EMSDK_HOME, in which
|
|
# case we have to rely on the fact that sourcing $EMSDK_ENV from a
|
|
# shell will add emcc to the $PATH.
|
|
proc proj-check-emsdk {} {
|
|
set emsdkHome [opt-val with-emsdk]
|
|
define EMSDK_HOME ""
|
|
define EMSDK_ENV ""
|
|
define BIN_EMCC ""
|
|
msg-checking "Emscripten SDK? "
|
|
if {$emsdkHome eq ""} {
|
|
# Fall back to checking the environment. $EMSDK gets set by
|
|
# sourcing emsdk_env.sh.
|
|
set emsdkHome [get-env EMSDK ""]
|
|
}
|
|
set rc 0
|
|
if {$emsdkHome ne ""} {
|
|
define EMSDK_HOME $emsdkHome
|
|
set emsdkEnv "$emsdkHome/emsdk_env.sh"
|
|
if {[file exists $emsdkEnv]} {
|
|
msg-result "$emsdkHome"
|
|
define EMSDK_ENV $emsdkEnv
|
|
set rc 1
|
|
set emcc "$emsdkHome/upstream/emscripten/emcc"
|
|
if {[file exists $emcc]} {
|
|
define BIN_EMCC $emcc
|
|
}
|
|
} else {
|
|
msg-result "emsdk_env.sh not found in $emsdkHome"
|
|
}
|
|
} else {
|
|
msg-result "not found"
|
|
}
|
|
define HAVE_EMSDK $rc
|
|
return $rc
|
|
}
|
|
|
|
########################################################################
|
|
# Tries various approaches to handling the -rpath link-time
|
|
# flag. Defines LDFLAGS_RPATH to that/those flag(s) or an empty
|
|
# string. Returns 1 if it finds an option, else 0.
|
|
#
|
|
# By default, the rpath is set to $prefix/lib. However, if
|
|
# --libdir=... is explicitly passed to configure then that value is
|
|
# used.
|
|
#
|
|
# Achtung: we have seen platforms which report that a given option
|
|
# checked here will work but then fails at build-time, and the current
|
|
# order of checks reflects that.
|
|
proc proj-check-rpath {} {
|
|
set rc 1
|
|
if {[proj-opt-was-provided libdir]} {
|
|
set lp "[get-define libdir]"
|
|
} else {
|
|
set lp "[get-define prefix]/lib"
|
|
}
|
|
# If we _don't_ use cc-with {} here (to avoid updating the global
|
|
# CFLAGS or LIBS or whatever it is that cc-check-flags updates) then
|
|
# downstream tests may fail because the resulting rpath gets
|
|
# implicitly injected into them.
|
|
cc-with {} {
|
|
if {[cc-check-flags "-rpath $lp"]} {
|
|
define LDFLAGS_RPATH "-rpath $lp"
|
|
} elseif {[cc-check-flags "-Wl,-rpath,$lp"]} {
|
|
define LDFLAGS_RPATH "-Wl,-rpath,$lp"
|
|
} elseif {[cc-check-flags "-Wl,-rpath -Wl,$lp"]} {
|
|
define LDFLAGS_RPATH "-Wl,-rpath -Wl,$lp"
|
|
} elseif {[cc-check-flags -Wl,-R$lp]} {
|
|
define LDFLAGS_RPATH "-Wl,-R$lp"
|
|
} else {
|
|
define LDFLAGS_RPATH ""
|
|
set rc 0
|
|
}
|
|
}
|
|
return $rc
|
|
}
|
|
|
|
########################################################################
|
|
# Internal helper for proj-dump-defs-json. Expects to be passed a
|
|
# [define] name and the variadic $args which are passed to
|
|
# proj-dump-defs-json. 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 proj-defs-type_ {name spec} {
|
|
foreach {type patterns} $spec {
|
|
foreach pattern $patterns {
|
|
if {[string match $pattern $name]} {
|
|
return $type
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
########################################################################
|
|
# Internal helper for proj-defs-format_: returns a JSON-ish quoted
|
|
# form of the given string-type values. It only performs the most
|
|
# basic of escaping. The input must not contain any control
|
|
# characters.
|
|
proc proj-quote-str_ {value} {
|
|
return \"[string map [list \\ \\\\ \" \\\"] $value]\"
|
|
}
|
|
|
|
########################################################################
|
|
# An internal impl detail of proj-dump-defs-json. Requires a data
|
|
# type specifier, as used by make-config-header, and a value. Returns
|
|
# the formatted value or the value $::proj_(defs-skip) if the caller
|
|
# should skip emitting that value.
|
|
set proj_(defs-skip) "-proj-defs-format_ sentinel"
|
|
proc proj-defs-format_ {type value} {
|
|
switch -exact -- $type {
|
|
-bare {
|
|
# Just output the value unchanged
|
|
}
|
|
-none {
|
|
set value $::proj_(defs-skip)
|
|
}
|
|
-str {
|
|
set value [proj-quote-str_ $value]
|
|
}
|
|
-auto {
|
|
# Automatically determine the type
|
|
if {![string is integer -strict $value]} {
|
|
set value [proj-quote-str_ $value]
|
|
}
|
|
}
|
|
-array {
|
|
set ar {}
|
|
foreach v $value {
|
|
set v [proj-defs-format_ -auto $v]
|
|
if {$::proj_(defs-skip) ne $v} {
|
|
lappend ar $v
|
|
}
|
|
}
|
|
set value "\[ [join $ar {, }] \]"
|
|
}
|
|
"" {
|
|
set value $::proj_(defs-skip)
|
|
}
|
|
default {
|
|
proj-fatal "Unknown type in proj-dump-defs-json: $type"
|
|
}
|
|
}
|
|
return $value
|
|
}
|
|
|
|
########################################################################
|
|
# This function works almost identically to autosetup's
|
|
# make-config-header but emits its output in JSON form. It is not a
|
|
# fully-functional JSON emitter, and will emit broken JSON for
|
|
# complicated outputs, but should be sufficient for purposes of
|
|
# emitting most configure vars (numbers and simple strings).
|
|
#
|
|
# In addition to the formatting flags supported by make-config-header,
|
|
# it also supports:
|
|
#
|
|
# -array {patterns...}
|
|
#
|
|
# Any defines matching the given patterns will be treated as a list of
|
|
# values, each of which will be formatted as if it were in an -auto {...}
|
|
# set, and the define will be emitted to JSON in the form:
|
|
#
|
|
# "ITS_NAME": [ "value1", ...valueN ]
|
|
#
|
|
# Achtung: if a given -array pattern contains values which themselves
|
|
# contains spaces...
|
|
#
|
|
# define-append foo {"-DFOO=bar baz" -DBAR="baz barre"}
|
|
#
|
|
# will lead to:
|
|
#
|
|
# ["-DFOO=bar baz", "-DBAR=\"baz", "barre\""]
|
|
#
|
|
# Neither is especially satisfactory (and the second is useless), and
|
|
# handling of such values is subject to change if any such values ever
|
|
# _really_ need to be processed by our source trees.
|
|
proc proj-dump-defs-json {file args} {
|
|
file mkdir [file dirname $file]
|
|
set lines {}
|
|
lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_*
|
|
foreach n [lsort [dict keys [all-defines]]] {
|
|
set type [proj-defs-type_ $n $args]
|
|
set value [proj-defs-format_ $type [get-define $n]]
|
|
if {$::proj_(defs-skip) ne $value} {
|
|
lappend lines "\"$n\": ${value}"
|
|
}
|
|
}
|
|
set buf {}
|
|
lappend buf [join $lines ",\n"]
|
|
write-if-changed $file $buf {
|
|
msg-result "Created $file"
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# Expects a list of pairs of configure flags which have been
|
|
# registered with autosetup, in this form:
|
|
#
|
|
# { alias1 => canonical1
|
|
# aliasN => canonicalN ... }
|
|
#
|
|
# The names must not have their leading -- part and must be in the
|
|
# form which autosetup will expect for passing to [opt-val NAME] and
|
|
# friends.
|
|
#
|
|
# Comment lines are permitted in the input.
|
|
#
|
|
# For each pair of ALIAS and CANONICAL, if --ALIAS is provided but
|
|
# --CANONICAL is not, the value of the former is copied to the
|
|
# latter. If --ALIAS is not provided, this is a no-op. If both have
|
|
# explicitly been provided a fatal usage error is triggered.
|
|
#
|
|
# Motivation: autosetup enables "hidden aliases" in [options] lists,
|
|
# and elides the aliases from --help output but does no further
|
|
# handling of them. For example, when --alias is a hidden alias of
|
|
# --canonical and a user passes --alias=X, [opt-val canonical] returns
|
|
# no value. i.e. the script must check both [opt-val alias] and
|
|
# [opt-val canonical]. The intent here is that this function be
|
|
# passed such mappings immediately after [options] is called, to carry
|
|
# over any values from hidden aliases into their canonical names, such
|
|
# that [opt-value canonical] will return X if --alias=X is passed to
|
|
# configure.
|
|
proc proj-xfer-options-aliases {mapping} {
|
|
foreach {hidden - canonical} [proj-strip-hash-comments_ $mapping] {
|
|
if {[proj-opt-was-provided $hidden]} {
|
|
if {[proj-opt-was-provided $canonical]} {
|
|
proj-fatal "both --$canonical and its alias --$hidden were used. Use only one or the other."
|
|
} else {
|
|
proj-opt-set $canonical [opt-val $hidden]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# Arguable/debatable...
|
|
#
|
|
# When _not_ cross-compiling and CC_FOR_BUILD is _not_ explcitely
|
|
# specified, force CC_FOR_BUILD to be the same as CC, so that:
|
|
#
|
|
# ./configure CC=clang
|
|
#
|
|
# will use CC_FOR_BUILD=clang, instead of cc, for building in-tree
|
|
# tools. This is based off of an email discussion and is thought to
|
|
# be likely to cause less confusion than seeing 'cc' invocations
|
|
# will when the user passes CC=clang.
|
|
#
|
|
# Sidebar: if we do this before the cc package is installed, it gets
|
|
# reverted by that package. Ergo, the cc package init will tell the
|
|
# user "Build C compiler...cc" shortly before we tell them otherwise.
|
|
proc proj-redefine-cc-for-build {} {
|
|
if {![proj-is-cross-compiling]
|
|
&& [get-define CC] ne [get-define CC_FOR_BUILD]
|
|
&& "nope" eq [get-env CC_FOR_BUILD "nope"]} {
|
|
user-notice "Re-defining CC_FOR_BUILD to CC=[get-define CC]. To avoid this, explicitly pass CC_FOR_BUILD=..."
|
|
define CC_FOR_BUILD [get-define CC]
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# Attempts to determine whether the given linenoise header file is of
|
|
# the "antirez" or "msteveb" flavor. It returns 2 for msteveb, else 1
|
|
# (it does not validate that the header otherwise contains the
|
|
# linenoise API).
|
|
proc proj-which-linenoise {dotH} {
|
|
set srcHeader [proj-file-content $dotH]
|
|
if {[string match *userdata* $srcHeader]} {
|
|
return 2
|
|
} else {
|
|
return 1
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
#
|
|
# "Re-export" the autoconf-conventional --XYZdir flags into something
|
|
# which is more easily overridable from a make invocation.
|
|
#
|
|
# Based off of notes in <https://sqlite.org/forum/forumpost/00d12a41f7>.
|
|
#
|
|
# Consider:
|
|
#
|
|
# $ ./configure --prefix=/foo
|
|
# $ make install prefix=/blah
|
|
#
|
|
# In that make invocation, $(libdir) would, at make-time, normally be
|
|
# hard-coded to /foo/lib, rather than /blah/lib. That happens because
|
|
# the autosetup exports conventional $prefix-based values for the
|
|
# numerious autoconfig-compatible XYZdir vars at configure-time. What
|
|
# we would normally want, however, is that --libdir derives from the
|
|
# make-time $(prefix). The distinction between configure-time and
|
|
# make-time is the significant factor there.
|
|
#
|
|
# This function attempts to reconcile those vars in such a way that
|
|
# they will derive, at make-time, from $(prefix) in a conventional
|
|
# manner unless they are explicitly overridden at configure-time, in
|
|
# which case those overrides takes precedence.
|
|
#
|
|
# Each --XYZdir flag which is explicitly passed to configure is
|
|
# exported as-is, as are those which default to some top-level system
|
|
# directory, e.g. /etc or /var. All which derive from either $prefix
|
|
# or $exec_prefix are exported in the form of a Makefile var
|
|
# reference, e.g. libdir=${exec_prefix}/lib. Ergo, if
|
|
# --exec-prefix=FOO is passed to configure, libdir will still derive,
|
|
# at make-time, from whatever exec_prefix is passed to make, and will
|
|
# use FOO if exec_prefix is not overridden. Without this
|
|
# post-processing, libdir would be cemented in as FOO/lib at
|
|
# configure-time, so would be tedious to override properly via a make
|
|
# invocation.
|
|
#
|
|
proc proj-redirect-autoconf-dir-vars {} {
|
|
set prefix [get-define prefix]
|
|
set exec_prefix [get-define exec_prefix $prefix]
|
|
# Note that the ${...} here refers to make-side var derefs, not
|
|
# TCL-side vars.
|
|
foreach {flag makeVar makeDeref} {
|
|
exec-prefix exec_prefix ${prefix}
|
|
libdir libdir ${exec_prefix}/lib
|
|
datadir datadir ${prefix}/share
|
|
mandir mandir ${datadir}/man
|
|
includedir includedir ${prefix}/include
|
|
bindir bindir ${exec_prefix}/bin
|
|
libdir libdir ${exec_prefix}/lib
|
|
sbindir sbindir ${exec_prefix}/sbin
|
|
sysconfdir sysconfdir /etc
|
|
sharedstatedir sharedstatedir ${prefix}/com
|
|
localstatedir localstatedir /var
|
|
runstatedir runstatedir /run
|
|
infodir infodir ${datadir}/info
|
|
libexec libexec ${exec_prefix}/libexec
|
|
} {
|
|
# puts "flag=$flag var=$makeVar makeDeref=$makeDeref"
|
|
if {[proj-opt-was-provided $flag]} {
|
|
#set x "+"
|
|
define $makeVar [opt-val $flag]
|
|
} else {
|
|
#set x "~"
|
|
define $makeVar $makeDeref
|
|
}
|
|
#puts "$x $makeVar = [get-define $makeVar]"
|
|
}
|
|
}
|
|
|
|
########################################################################
|
|
# If a file named .env-$flag exists, this function returns a
|
|
# trimmed copy of its contents, else it returns $dflt. The intended
|
|
# usage is that things like developer-specific CFLAGS preferences can
|
|
# be stored in .env-CFLAGS.
|
|
proc proj-env-file {flag {dflt ""}} {
|
|
set fn ".env-${flag}"
|
|
if {[file readable $fn]} {
|
|
return [proj-file-content -trim $fn]
|
|
}
|
|
return $dflt
|
|
}
|
|
|
|
########################################################################
|
|
# Extracts the value of "environment" variable $var from the first of
|
|
# the following places where it's defined:
|
|
#
|
|
# - Passed to configure as $var=...
|
|
# - Exists as an environment variable
|
|
# - A file named .env-$var (see [proj-env-file])
|
|
#
|
|
# If none of those are set, $dflt is returned.
|
|
proc proj-get-env {var {dflt ""}} {
|
|
return [get-env $var [proj-env-file $var $dflt]]
|
|
}
|