upload all files

This commit is contained in:
2026-01-07 21:21:28 +08:00
parent 09d49d22c9
commit 8f53d867a2
220 changed files with 419218 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
Unless explicitly stated, all files which form part of autosetup
are released under the following license:
---------------------------------------------------------------------
autosetup - A build environment "autoconfigurator"
Copyright (c) 2010-2011, WorkWare Systems <http://workware.net.au/>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE WORKWARE SYSTEMS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WORKWARE
SYSTEMS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation
are those of the authors and should not be interpreted as representing
official policies, either expressed or implied, of WorkWare Systems.

View File

@@ -0,0 +1,11 @@
README.autosetup created by autosetup v0.7.2
This is the autosetup directory for a local install of autosetup.
It contains autosetup, support files and loadable modules.
*.tcl files in this directory are optional modules which
can be loaded with the 'use' directive.
*.auto files in this directory are auto-loaded.
For more information, see https://msteveb.github.io/autosetup/

View File

@@ -0,0 +1,454 @@
Maintaining Autosetup in the SQLite Tree
========================================================================
This document provides some tips and reminders for the SQLite
developers regarding using and maintaining the [Autosetup][]-based
build infrastructure. It is not an [Autosetup][] reference.
**Table of Contents**:
- [Autosetup API Reference](#apiref)
- [API Tips](#apitips)
- [Ensuring TCL Compatibility](#tclcompat)
- [Design Conventions](#conventions)
- Symbolic Names of Feature Flags
- Do Not Update Global Shared State
- [Updating Autosetup](#updating)
- ***[Patching Autosetup for Project-local changes](#patching)***
- [Branch-specific Customization](#branch-customization)
------------------------------------------------------------------------
<a name="apiref"></a>
Autosetup API Reference
========================================================================
The Autosetup API is quite extensive and can be read either in
the [files in the `autosetup` dir](/dir/autosetup) or using:
>
```
$ ./configure --reference | less
```
That will include any docs from any TCL files in the `./autosetup` dir
which contain certain (simple) markup defined by autosetup.
This project's own configuration-related TCL code is spread across the
following files:
- [proj.tcl][]: project-agnostic utility code for autosetup-driven
projects. This file is designed to be shared between this project,
other projects managed under the SQLite/Hwaci umbrella
(e.g. Fossil), and personal projects of SQLite's developers. It is
essentially an amalgamation of a decade's worth of autosetup-related
utility code.
- [sqlite-config.tcl][]: utility code which is too project-specific
for `proj.tcl`. We split this out of `auto.def` so that it can be
used by both `auto.def` and...
- [auto.def][]: the primary driver for the `./configure` process.
When we talk about "the configure script," we're technically
referring to this file, though it actually contains very little
of the TCL code.
- [autoconf/auto.def][]: the main driver script for the "autoconf"
bundle's configure script. It is essentially a slightly trimmed-down
version of the main `auto.def` file. The `autoconf` dir was ported
from the Autotools to Autosetup in the 3.49.0 dev cycle but retains
the "autoconf" name to minimize downstream disruption.
<a name="apitips"></a>
Autosetup API Tips
========================================================================
This section briefly covers only APIs which are frequently useful in
day-to-day maintenance and might not be immediately recognized as such
from a casual perusal of the relevant TCL files. The complete docs of
those with `proj-` prefix can be found in [proj.tcl][] and those with
an `sqlite-` prefix are in [sqlite-config.tcl][]. The others are part
of Autosetup's core packages and are scattered around [the TCL files
in ./autosetup](/dir/autosetup).
In (mostly) alphabetical order:
- **`file-isexec filename`**\
Should be used in place of `[file executable]`, as it will also
check for `${filename}.exe` on Windows platforms. However, on such
platforms it also assumes that _any_ existing file is executable.
- **`get-env VAR ?default?`**\
Will fetch an "environment variable" from the first of either: (1) a
KEY=VALUE passed to the configure script or (2) the system's
environment variables. Not to be confused with `getenv`, which only
does the latter and is rarely, if ever, useful in this tree.
- **`proj-get-env VAR ?default?`**\
Works like `get-env` but will, if that function finds no match,
look for a file named `./.env-$VAR` and, if found, return its
trimmed contents. This can be used, e.g., to set a developer's
local preferences for the default `CFLAGS`.\
Tip: adding `-O0` to `.env-CFLAGS` reduces rebuild times
considerably at the cost of performance in `make devtest` and the
like.
- **`proj-fatal msg`**\
Emits `$msg` to stderr and exits with non-zero. Its differences from
autosetup's `user-error` are purely cosmetic.
- **`proj-if-opt-truthy flag thenScript ?elseScript?`**\
Evals `thenScript` if the given `--flag` is truthy, else it
evals the optional `elseScript`.
- **`proj-indented-notice ?-error? ?-notice? msg`**\
Breaks its `msg` argument into lines, trims them, and emits them
with consistent indentation. Exactly how it emits depends on the
flags passed to it (or not), as covered in its docs. This will stick
out starkly from normal output and is intended to be used only for
important notices.
- **`proj-opt-truthy flag`**\
Returns 1 if `--flag`'s value is "truthy," i.e. one of (1, on,
enabled, yes, true).
- **`proj-opt-was-provided FLAG`**\
Returns 1 if `--FLAG` was explicitly provided to configure,
else 0. This distinction can be used to determine, e.g., whether
`--with-readline` was provided or whether we're searching for
readline by default. In the former case, failure to find it should
be treated as fatal, where in the latter case it's not.\
Unlike most functions which deal with `--flags`, this one does not
validate that `$FLAG` is a registered flag so will not fail fatally
if `$FLAG` is not registered as an Autosetup option.
- **`proj-val-truthy value`**\
Returns 1 if `$value` is "truthy," See `proj-opt-truthy` for the definition
of "truthy."
- **`proj-warn msg`**\
Emits `$msg` to stderr. Closely-related is autosetup's `user-notice`
(described below).
- **`sqlite-add-feature-flag ?-shell? FLAG...`**\
Adds the given feature flag to the CFLAGS which are specific to
building libsqlite3. It's intended to be passed one or more
`-DSQLITE_ENABLE_...`, or similar, flags. If the `-shell` flag is
used then it also passes its arguments to
`sqlite-add-shell-opt`. This is a no-op if `FLAG` is not provided or
is empty.
- **`sqlite-add-shell-opt FLAG...`**\
The shell-specific counterpart of `sqlite-add-feature-flag` which
only adds the given flag(s) to the CLI-shell-specific CFLAGS.
- **`sqlite-configure BUILD-NAME {script}`**\
This is where all configure `--flags` are defined for all known
build modes ("canonical" or "autoconf"). After processing all flags,
this function runs `$script`, which contains the build-mode-specific
configuration bits, and then runs any finalization bits which are
common to all build modes. The `auto.def` files are intended to contain
exactly two commands:
`use sqlite-config; sqlite-configure BUILD-NAME {script}`
- **`user-notice msg`**\
Queues `$msg` to be sent to stderr, but does not emit it until
either `show-notices` is called or the next time autosetup would
output something (it internally calls `show-notices`). This can be
used to generate warnings between a "checking for..." message and
its resulting "yes/no/whatever" message in such a way as to not
spoil the layout of such messages.
<a name="tclcompat"></a>
Ensuring TCL Compatibility
========================================================================
One of the significant benefits of using Autosetup is that (A) this
project uses many TCL scripts in the build process and (B) Autosetup
comes with a TCL interpreter named [JimTCL][].
It is important that any TCL files used by the configure process and
makefiles remain compatible with both [JimTCL][] and the canonical
TCL. Though JimTCL has outstanding compatibility with canonical TCL,
it does have a few corners with incompatibilities, e.g. regular
expressions. If a script runs in JimTCL without using any
JimTCL-specific features, then it's a certainty that it will run in
canonical TCL as well. The opposite, however, is not _always_ the
case.
When [`./configure`](/file/configure) is run, it goes through a
bootstrapping process to find a suitable TCL with which to run the
autosetup framework. The first step involves [finding or building a
TCL shell](/file/autosetup/autosetup-find-tclsh). That will first
search for an available `tclsh` (under several common names,
e.g. `tclsh8.6`) before falling back to compiling the copy of
`jimsh0.c` included in the source tree. i.e. it will prefer to use a
system-installed TCL for running the configure script. Once it finds
(or builds) a TCL shell, it then runs [a sanity test to ensure that
the shell is suitable](/file/autosetup/autosetup-test-tclsh) before
using it to run the main autosetup app.
There are two simple ways to ensure that running of the configure
process uses JimTCL instead of the canonical `tclsh`, and either
approach provides equally high assurances about configure script
compatibility across TCL implementations:
1. Build on a system with no `tclsh` installed in the `$PATH`. In that
case, the configure process will fall back to building the in-tree
copy of JimTCL.
2. Manually build `./jimsh0` in the top of the checkout with:\
`cc -o jimsh0 autosetup/jimsh0.c`\
With that in place, the configure script will prefer to use that
before looking for a system-level `tclsh`. Be aware, though, that
`make distclean` will remove that file.
**Note that `./jimsh0` is distinctly different from the `./jimsh`**
which gets built for code-generation purposes. The latter requires
non-default build flags to enable features which are
platform-dependent, most notably to make its `[file normalize]` work.
This means, for example, that the configure script and its utility
APIs must not use `[file normalize]`, but autosetup provides a
TCL-only implementation of `[file-normalize]` (note the dash) for
portable use in the configure script. Contrariwise, code-generation
scripts invoked via `make` may use `[file normalize]`, as they'll use
`./jimsh` or `tclsh` instead of `./jimsh0`.
Known TCL Incompatibilities
------------------------------------------------------------------------
A summary of known incompatibilities in JimTCL
- **CRNL line endings**: prior to 2025-02-05 `fconfigure -translation ...`
was a no-op in JimTCL, and it emits CRNL line endings by default on
Windows. Since then, it supports `-translation binary`, which is
close enough to `-translation lf` for our purposes. When working
with files using the `open` command, it is important to use mode
`"rb"` or `"wb"`, as appropriate, so that the output does not get
CRNL-mangled on Windows.
- **`file copy`** does not support multiple source files. See
[](/info/61f18c96183867fe) for a workaround.
- **Regular expressions**:
- Patterns treat `\nnn` octal values as back-references (which it
does not support). Those can be reformulated as demonstrated in
[](/info/aeac23359bb681c0).
- `regsub` does not support the `\y` flag. A workaround is demonstrated
in [](/info/c2e5dd791cce3ec4).
<a name="conventions"></a>
Design Conventions
========================================================================
This section describes the motivations for the most glaring of the
build's design decisions, in particular how they deviate from
historical, or even widely-conventional, practices.
Symbolic Names of Feature Flags
------------------------------------------------------------------------
Historically, the project's makefile has exclusively used
`UPPER_UNDERSCORE` form for makefile variables. This build, however,
primarily uses `X.y` format, where `X` is often a category label,
e.g. `CFLAGS`, and `y` is the specific instance of that category,
e.g. `CFLAGS.readline`.
When the configure script exports flags for consumption by filtered
files, e.g. [Makefile.in][] and the generated
`sqlite_cfg.h`, it does so in the more conventional `X_Y` form because
those flags get exported as as C `#define`s to `sqlite_cfg.h`, where
dots are not permitted.
The `X.y` convention is used in the makefiles primarily because the
person who did the initial port finds that considerably easier on the
eyes and fingers. In practice, the `X_Y` form of such exports is used
exactly once in [Makefile.in][], where it's translated from `@X_Y@`
into into `X.y` form for consumption by [Makefile.in][] and
[main.mk][]. For example:
>
```
LDFLAGS.shobj = @SHOBJ_LDFLAGS@
LDFLAGS.zlib = @LDFLAGS_ZLIB@
LDFLAGS.math = @LDFLAGS_MATH@
```
(That first one is defined by autosetup, and thus applies "LDFLAGS" as
the suffix rather than the prefix. Which is more legible is a matter
of taste, for which there is no accounting.)
Do Not Update Global Shared State
------------------------------------------------------------------------
In both the legacy Autotools-driven build and common Autosetup usage,
feature tests performed by the configure script may amend global flags
such as `LIBS`, `LDFLAGS`, and `CFLAGS`[^as-cflags]. That's
appropriate for a makefile which builds a single deliverable, but less
so for makefiles which produce multiple deliverables. Drawbacks of
that approach include:
- It's unlikely that every single deliverable will require the same
core set of those flags.
- It can be difficult to determine the origin of any given change to
that global state because those changes are hidden behind voodoo
performed outside the immediate visibility of the configure script's
maintainer.
- It can force the maintainers of the configure script to place tests
in a specific order so that the resulting flags get applied at
the correct time and/or in the correct order.\
(A real-life example: before the approach described below was taken
to collecting build-time flags, the test for `-rpath` had to come
_after_ the test for zlib because the results of the `-rpath` test
implicitly modified global state which broke the zlib feature
test. Because the feature tests no longer (intentionally) modify
shared global state, that is not an issue.)
In this build, cases where feature tests modify global state in such a
way that it may impact later feature tests are either (A) very
intentionally defined to do so (e.g. the `--with-wasi-sdk` flag has
invasive side-effects) or (B) are oversights (i.e. bugs).
This tree's [configure script][auto.def], [utility APIs][proj.tcl],
[Makefile.in][], and [main.mk][] therefore strive to separate the
results of any given feature test into its own well-defined
variables. For example:
- The linker flags for zlib are exported from the configure script as
`LDFLAGS_ZLIB`, which [Makefile.in][] and [main.mk][] then expose as
`LDFLAGS.zlib`.
- `CFLAGS_READLINE` (a.k.a. `CFLAGS.readline`) contains the `CFLAGS`
needed for including `libreadline`, `libedit`, or `linenoise`, and
`LDFLAGS_READLINE` (a.k.a. `LDFLAGS.readline`) is its link-time
counterpart.
It is then up to the Makefile to apply and order the flags however is
appropriate.
At the end of the configure script, the global `CFLAGS` _ideally_
holds only flags which are either relevant to all targets or, failing
that, will have no unintended side-effects on any targets. That said:
clients frequently pass custom `CFLAGS` to `./configure` or `make` to
set library-level feature toggles, e.g. `-DSQLITE_OMIT_FOO`, in which
case there is no practical way to avoid "polluting" the builds of
arbitrary makefile targets with those. _C'est la vie._
[^as-cflags]: But see this article for a detailed discussion of how
autosetup currently deals specifically with CFLAGS:
<https://msteveb.github.io/autosetup/articles/handling-cflags/>
<a name="updating"></a>
Updating Autosetup
========================================================================
Updating autosetup is, more often than not, painless. It requires having
a checked-out copy of [the autosetup git repository][autosetup-git]:
>
```
$ git clone https://github.com/msteveb/autosetup
$ cd autosetup
# Or, if it's already checked out:
$ git pull
```
Then, from the top-most directory of an SQLite checkout:
>
```
$ /path/to/autosetup-checkout/autosetup --install .
$ fossil status # show the modified files
```
Unless the upgrade made any incompatible changes (which is exceedingly
rare), that's all there is to it. After that's done, **apply a patch
for the change described in the following section**, test the
configure process, and check it in.
<a name="patching"></a>
Patching Autosetup for Project-local Changes
------------------------------------------------------------------------
Autosetup reserves the flag name **`--debug`** for its own purposes,
and its own special handling of `--enable-...` flags makes `--debug`
an alias for `--enable-debug`. As this project has a long history of
using `--enable-debug`, we patch autosetup to use the name
`--autosetup-debug` in place of `--debug`. That requires (as of this
writing) four small edits in
[/autosetup/autosetup](/file/autosetup/autosetup), as demonstrated in
[check-in 3296c8d3](/info/3296c8d3).
If autosetup is upgraded and this patch is _not_ applied the invoking
`./configure` will fail loudly because of the declaration of the
`debug` flag in `auto.def` - duplicated flags are not permitted.
<a name="branch-customization"></a>
Branch-specific Customization
========================================================================
Certain vendor-specific branches require slight configure script
customization. Rather than editing `sqlite-config.tcl` for this,
which frequently leads to merge conflicts, the following approach
is recommended:
In the vendor-specific branch, create a file named
`autosetup/sqlite-custom.tcl`.
That file should contain the following content...
If flag customization is required, add:
>
```
proc sqlite-custom-flags {} {
# If any existing --flags require different default values
# then call:
options-defaults {
flag-name new-default-value
...
}
# ^^^ That will replace the default value but will not update
# the --help text, which may lead to some confusion:
# https://github.com/msteveb/autosetup/issues/77
return {
{*} {
new-flag-name => {Help text}
...
}
}; #see below
}
```
That function must return either an empty string or a list in the form
used internally by [sqlite-config.tcl][]'s `sqlite-configure`.
Next, define:
>
```
proc sqlite-custom-handle-flags {} {
... do any custom flag handling here ...
}
```
That function, if defined, will be called relatively late in the
configure process, before any filtered files are generated but after
all other significant processing.
[Autosetup]: https://msteveb.github.io/autosetup/
[auto.def]: /file/auto.def
[autoconf/auto.def]: /file/autoconf/auto.def
[autosetup-git]: https://github.com/msteveb/autosetup
[proj.tcl]: /file/autosetup/proj.tcl
[sqlite-config.tcl]: /file/autosetup/sqlite-config.tcl
[Makefile.in]: /file/Makefile.in
[main.mk]: /file/main.mk
[JimTCL]: https://jim.tcl.tk

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
#!/bin/sh
# Looks for a suitable tclsh or jimsh in the PATH
# If not found, builds a bootstrap jimsh in current dir from source
# Prefer $autosetup_tclsh if is set in the environment (unless ./jimsh0 works)
# If an argument is given, use that as the test instead of autosetup-test-tclsh
d="`dirname "$0"`"
for tclsh in ./jimsh0 $autosetup_tclsh jimsh tclsh tclsh8.5 tclsh8.6 tclsh8.7; do
{ $tclsh "$d/${1-autosetup-test-tclsh}"; } 2>/dev/null && exit 0
done
echo 1>&2 "No installed jimsh or tclsh, building local bootstrap jimsh0"
for cc in ${CC_FOR_BUILD:-cc} gcc; do
{ $cc -o jimsh0 "$d/jimsh0.c"; } 2>/dev/null >/dev/null || continue
./jimsh0 "$d/${1-autosetup-test-tclsh}" && exit 0
done
echo 1>&2 "No working C compiler found. Tried ${CC_FOR_BUILD:-cc} and gcc."
echo false

View File

@@ -0,0 +1,20 @@
# A small Tcl script to verify that the chosen
# interpreter works. Sometimes we might e.g. pick up
# an interpreter for a different arch.
# Outputs the full path to the interpreter
if {[catch {info version} version] == 0} {
# This is Jim Tcl
if {$version >= 0.72} {
# Ensure that regexp works
regexp (a.*?) a
puts [info nameofexecutable]
exit 0
}
} elseif {[catch {info tclversion} version] == 0} {
if {$version >= 8.5 && ![string match 8.5a* [info patchlevel]]} {
puts [info nameofexecutable]
exit 0
}
}
exit 1

View File

@@ -0,0 +1,15 @@
# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# @synopsis:
#
# The 'cc-db' module provides a knowledge-base of system idiosyncrasies.
# In general, this module can always be included.
use cc
options {}
# openbsd needs sys/types.h to detect some system headers
cc-include-needs sys/socket.h sys/types.h
cc-include-needs netinet/in.h sys/types.h

View File

@@ -0,0 +1,187 @@
# Copyright (c) 2011 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# @synopsis:
#
# Provides a library of common tests on top of the 'cc' module.
use cc
# @cc-check-lfs
#
# The equivalent of the 'AC_SYS_LARGEFILE' macro.
#
# defines 'HAVE_LFS' if LFS is available,
# and defines '_FILE_OFFSET_BITS=64' if necessary
#
# Returns 1 if 'LFS' is available or 0 otherwise
#
proc cc-check-lfs {} {
cc-check-includes sys/types.h
msg-checking "Checking if -D_FILE_OFFSET_BITS=64 is needed..."
set lfs 1
if {[msg-quiet cc-with {-includes sys/types.h} {cc-check-sizeof off_t}] == 8} {
msg-result no
} elseif {[msg-quiet cc-with {-includes sys/types.h -cflags -D_FILE_OFFSET_BITS=64} {cc-check-sizeof off_t}] == 8} {
define _FILE_OFFSET_BITS 64
msg-result yes
} else {
set lfs 0
msg-result none
}
define-feature lfs $lfs
return $lfs
}
# @cc-check-endian
#
# The equivalent of the 'AC_C_BIGENDIAN' macro.
#
# defines 'HAVE_BIG_ENDIAN' if endian is known to be big,
# or 'HAVE_LITTLE_ENDIAN' if endian is known to be little.
#
# Returns 1 if determined, or 0 if not.
#
proc cc-check-endian {} {
cc-check-includes sys/types.h sys/param.h
set rc 0
msg-checking "Checking endian..."
cc-with {-includes {sys/types.h sys/param.h}} {
if {[cctest -code {
#if !defined(BIG_ENDIAN) || !defined(BYTE_ORDER)
#error unknown
#elif BYTE_ORDER != BIG_ENDIAN
#error little
#endif
}]} {
define-feature big-endian
msg-result "big"
set rc 1
} elseif {[cctest -code {
#if !defined(LITTLE_ENDIAN) || !defined(BYTE_ORDER)
#error unknown
#elif BYTE_ORDER != LITTLE_ENDIAN
#error big
#endif
}]} {
define-feature little-endian
msg-result "little"
set rc 1
} else {
msg-result "unknown"
}
}
return $rc
}
# @cc-check-flags flag ?...?
#
# Checks whether the given C/C++ compiler flags can be used. Defines feature
# names prefixed with 'HAVE_CFLAG' and 'HAVE_CXXFLAG' respectively, and
# appends working flags to '-cflags' and 'AS_CFLAGS' or 'AS_CXXFLAGS'.
proc cc-check-flags {args} {
set result 1
array set opts [cc-get-settings]
switch -exact -- $opts(-lang) {
c++ {
set lang C++
set prefix CXXFLAG
}
c {
set lang C
set prefix CFLAG
}
default {
autosetup-error "cc-check-flags failed with unknown language: $opts(-lang)"
}
}
foreach flag $args {
msg-checking "Checking whether the $lang compiler accepts $flag..."
if {[cctest -cflags $flag]} {
msg-result yes
define-feature $prefix$flag
cc-with [list -cflags [list $flag]]
define-append AS_${prefix}S $flag
} else {
msg-result no
set result 0
}
}
return $result
}
# @cc-check-standards ver ?...?
#
# Checks whether the C/C++ compiler accepts one of the specified '-std=$ver'
# options, and appends the first working one to '-cflags' and 'AS_CFLAGS' or
# 'AS_CXXFLAGS'.
proc cc-check-standards {args} {
array set opts [cc-get-settings]
foreach std $args {
if {[cc-check-flags -std=$std]} {
return $std
}
}
return ""
}
# Checks whether $keyword is usable as alignof
proc cctest_alignof {keyword} {
msg-checking "Checking for $keyword..."
if {[cctest -code "int x = ${keyword}(char), y = ${keyword}('x');"]} then {
msg-result ok
define-feature $keyword
} else {
msg-result "not found"
}
}
# @cc-check-c11
#
# Checks for several C11/C++11 extensions and their alternatives. Currently
# checks for '_Static_assert', '_Alignof', '__alignof__', '__alignof'.
proc cc-check-c11 {} {
msg-checking "Checking for _Static_assert..."
if {[cctest -code {
_Static_assert(1, "static assertions are available");
}]} then {
msg-result ok
define-feature _Static_assert
} else {
msg-result "not found"
}
cctest_alignof _Alignof
cctest_alignof __alignof__
cctest_alignof __alignof
}
# @cc-check-alloca
#
# The equivalent of the 'AC_FUNC_ALLOCA' macro.
#
# Checks for the existence of 'alloca'
# defines 'HAVE_ALLOCA' and returns 1 if it exists.
proc cc-check-alloca {} {
cc-check-some-feature alloca {
cctest -includes alloca.h -code { alloca (2 * sizeof (int)); }
}
}
# @cc-signal-return-type
#
# The equivalent of the 'AC_TYPE_SIGNAL' macro.
#
# defines 'RETSIGTYPE' to 'int' or 'void'.
proc cc-signal-return-type {} {
msg-checking "Checking return type of signal handlers..."
cc-with {-includes {sys/types.h signal.h}} {
if {[cctest -code {return *(signal (0, 0)) (0) == 1;}]} {
set type int
} else {
set type void
}
define RETSIGTYPE $type
msg-result $type
}
}

View File

@@ -0,0 +1,115 @@
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# @synopsis:
#
# The 'cc-shared' module provides support for shared libraries and shared objects.
# It defines the following variables:
#
## SH_CFLAGS Flags to use compiling sources destined for a shared library
## SH_LDFLAGS Flags to use linking (creating) a shared library
## SH_SOPREFIX Prefix to use to set the soname when creating a shared library
## SH_SOFULLPATH Set to 1 if the shared library soname should include the full install path
## SH_SOEXT Extension for shared libs
## SH_SOEXTVER Format for versioned shared libs - %s = version
## SHOBJ_CFLAGS Flags to use compiling sources destined for a shared object
## SHOBJ_LDFLAGS Flags to use linking a shared object, undefined symbols allowed
## SHOBJ_LDFLAGS_R - as above, but all symbols must be resolved
## SH_LINKRPATH Format for setting the rpath when linking an executable, %s = path
## SH_LINKFLAGS Flags to use linking an executable which will load shared objects
## LD_LIBRARY_PATH Environment variable which specifies path to shared libraries
## STRIPLIBFLAGS Arguments to strip a dynamic library
options {}
# Defaults: gcc on unix
define SHOBJ_CFLAGS -fPIC
define SHOBJ_LDFLAGS -shared
define SH_CFLAGS -fPIC
define SH_LDFLAGS -shared
define SH_LINKFLAGS -rdynamic
define SH_LINKRPATH "-Wl,-rpath -Wl,%s"
define SH_SOEXT .so
define SH_SOEXTVER .so.%s
define SH_SOPREFIX -Wl,-soname,
define LD_LIBRARY_PATH LD_LIBRARY_PATH
define STRIPLIBFLAGS --strip-unneeded
# Note: This is a helpful reference for identifying the toolchain
# http://sourceforge.net/apps/mediawiki/predef/index.php?title=Compilers
switch -glob -- [get-define host] {
*-*-darwin* {
define SHOBJ_CFLAGS "-dynamic -fno-common"
define SHOBJ_LDFLAGS "-bundle -undefined dynamic_lookup"
define SHOBJ_LDFLAGS_R -bundle
define SH_CFLAGS -dynamic
define SH_LDFLAGS -dynamiclib
define SH_LINKFLAGS ""
define SH_SOEXT .dylib
define SH_SOEXTVER .%s.dylib
define SH_SOPREFIX -Wl,-install_name,
define SH_SOFULLPATH
define LD_LIBRARY_PATH DYLD_LIBRARY_PATH
define STRIPLIBFLAGS -x
}
*-*-ming* - *-*-cygwin - *-*-msys {
define SHOBJ_CFLAGS ""
define SHOBJ_LDFLAGS -shared
define SH_CFLAGS ""
define SH_LDFLAGS -shared
define SH_LINKRPATH ""
define SH_LINKFLAGS ""
define SH_SOEXT .dll
define SH_SOEXTVER .dll
define SH_SOPREFIX ""
define LD_LIBRARY_PATH PATH
}
sparc* {
if {[msg-quiet cc-check-decls __SUNPRO_C]} {
msg-result "Found sun stdio compiler"
# sun stdio compiler
# XXX: These haven't been fully tested.
define SHOBJ_CFLAGS -KPIC
define SHOBJ_LDFLAGS "-G"
define SH_CFLAGS -KPIC
define SH_LINKFLAGS -Wl,-export-dynamic
define SH_SOPREFIX -Wl,-h,
}
}
*-*-solaris* {
if {[msg-quiet cc-check-decls __SUNPRO_C]} {
msg-result "Found sun stdio compiler"
# sun stdio compiler
# XXX: These haven't been fully tested.
define SHOBJ_CFLAGS -KPIC
define SHOBJ_LDFLAGS "-G"
define SH_CFLAGS -KPIC
define SH_LINKFLAGS -Wl,-export-dynamic
define SH_SOPREFIX -Wl,-h,
}
}
*-*-hpux* {
define SHOBJ_CFLAGS +z
define SHOBJ_LDFLAGS -b
define SH_CFLAGS +z
define SH_LDFLAGS -b
define SH_LINKFLAGS -Wl,+s
define SH_LINKRPATH "-Wl,+b -Wl,%s"
define SH_SOPREFIX -Wl,+h,
define STRIPLIBFLAGS -Wl,-s
}
*-*-haiku {
define SHOBJ_CFLAGS ""
define SHOBJ_LDFLAGS -shared
define SH_CFLAGS ""
define SH_LDFLAGS -shared
define SH_LINKFLAGS ""
define SH_SOPREFIX ""
define LD_LIBRARY_PATH LIBRARY_PATH
}
}
if {![is-defined SHOBJ_LDFLAGS_R]} {
define SHOBJ_LDFLAGS_R [get-define SHOBJ_LDFLAGS]
}

View File

@@ -0,0 +1,758 @@
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# @synopsis:
#
# The 'cc' module supports checking various 'features' of the C or C++
# compiler/linker environment. Common commands are 'cc-check-includes',
# 'cc-check-types', 'cc-check-functions', 'cc-with' and 'make-config-header'
#
# The following environment variables are used if set:
#
## CC - C compiler
## CXX - C++ compiler
## CPP - C preprocessor
## CCACHE - Set to "none" to disable automatic use of ccache
## CPPFLAGS - Additional C preprocessor compiler flags (C and C++), before CFLAGS, CXXFLAGS
## CFLAGS - Additional C compiler flags
## CXXFLAGS - Additional C++ compiler flags
## LDFLAGS - Additional compiler flags during linking
## LINKFLAGS - ?How is this different from LDFLAGS?
## LIBS - Additional libraries to use (for all tests)
## CROSS - Tool prefix for cross compilation
#
# The following variables are defined from the corresponding
# environment variables if set.
#
## CC_FOR_BUILD
## LD
use system
options {}
# Checks for the existence of the given function by linking
#
proc cctest_function {function} {
cctest -link 1 -declare "extern void $function\(void);" -code "$function\();"
}
# Checks for the existence of the given type by compiling
proc cctest_type {type} {
cctest -code "$type _x;"
}
# Checks for the existence of the given type/structure member.
# e.g. "struct stat.st_mtime"
proc cctest_member {struct_member} {
# split at the first dot
regexp {^([^.]+)[.](.*)$} $struct_member -> struct member
cctest -code "static $struct _s; return sizeof(_s.$member);"
}
# Checks for the existence of the given define by compiling
#
proc cctest_define {name} {
cctest -code "#ifndef $name\n#error not defined\n#endif"
}
# Checks for the existence of the given name either as
# a macro (#define) or an rvalue (such as an enum)
#
proc cctest_decl {name} {
cctest -code "#ifndef $name\n(void)$name;\n#endif"
}
# @cc-check-sizeof type ...
#
# Checks the size of the given types (between 1 and 32, inclusive).
# Defines a variable with the size determined, or 'unknown' otherwise.
# e.g. for type 'long long', defines 'SIZEOF_LONG_LONG'.
# Returns the size of the last type.
#
proc cc-check-sizeof {args} {
foreach type $args {
msg-checking "Checking for sizeof $type..."
set size unknown
# Try the most common sizes first
foreach i {4 8 1 2 16 32} {
if {[cctest -code "static int _x\[sizeof($type) == $i ? 1 : -1\] = { 1 };"]} {
set size $i
break
}
}
msg-result $size
set define [feature-define-name $type SIZEOF_]
define $define $size
}
# Return the last result
get-define $define
}
# Checks for each feature in $list by using the given script.
#
# When the script is evaluated, $each is set to the feature
# being checked, and $extra is set to any additional cctest args.
#
# Returns 1 if all features were found, or 0 otherwise.
proc cc-check-some-feature {list script} {
set ret 1
foreach each $list {
if {![check-feature $each $script]} {
set ret 0
}
}
return $ret
}
# @cc-check-includes includes ...
#
# Checks that the given include files can be used.
proc cc-check-includes {args} {
cc-check-some-feature $args {
set with {}
if {[dict exists $::autosetup(cc-include-deps) $each]} {
set deps [dict keys [dict get $::autosetup(cc-include-deps) $each]]
msg-quiet cc-check-includes {*}$deps
foreach i $deps {
if {[have-feature $i]} {
lappend with $i
}
}
}
if {[llength $with]} {
cc-with [list -includes $with] {
cctest -includes $each
}
} else {
cctest -includes $each
}
}
}
# @cc-include-needs include required ...
#
# Ensures that when checking for '$include', a check is first
# made for each '$required' file, and if found, it is included with '#include'.
proc cc-include-needs {file args} {
foreach depfile $args {
dict set ::autosetup(cc-include-deps) $file $depfile 1
}
}
# @cc-check-types type ...
#
# Checks that the types exist.
proc cc-check-types {args} {
cc-check-some-feature $args {
cctest_type $each
}
}
# @cc-check-defines define ...
#
# Checks that the given preprocessor symbols are defined.
proc cc-check-defines {args} {
cc-check-some-feature $args {
cctest_define $each
}
}
# @cc-check-decls name ...
#
# Checks that each given name is either a preprocessor symbol or rvalue
# such as an enum. Note that the define used is 'HAVE_DECL_xxx'
# rather than 'HAVE_xxx'.
proc cc-check-decls {args} {
set ret 1
foreach name $args {
msg-checking "Checking for $name..."
set r [cctest_decl $name]
define-feature "decl $name" $r
if {$r} {
msg-result "ok"
} else {
msg-result "not found"
set ret 0
}
}
return $ret
}
# @cc-check-functions function ...
#
# Checks that the given functions exist (can be linked).
proc cc-check-functions {args} {
cc-check-some-feature $args {
cctest_function $each
}
}
# @cc-check-members type.member ...
#
# Checks that the given type/structure members exist.
# A structure member is of the form 'struct stat.st_mtime'.
proc cc-check-members {args} {
cc-check-some-feature $args {
cctest_member $each
}
}
# @cc-check-function-in-lib function libs ?otherlibs?
#
# Checks that the given function can be found in one of the libs.
#
# First checks for no library required, then checks each of the libraries
# in turn.
#
# If the function is found, the feature is defined and 'lib_$function' is defined
# to '-l$lib' where the function was found, or "" if no library required.
# In addition, '-l$lib' is prepended to the 'LIBS' define.
#
# If additional libraries may be needed for linking, they should be specified
# with '$extralibs' as '-lotherlib1 -lotherlib2'.
# These libraries are not automatically added to 'LIBS'.
#
# Returns 1 if found or 0 if not.
#
proc cc-check-function-in-lib {function libs {otherlibs {}}} {
msg-checking "Checking libs for $function..."
set found 0
cc-with [list -libs $otherlibs] {
if {[cctest_function $function]} {
msg-result "none needed"
define lib_$function ""
incr found
} else {
foreach lib $libs {
cc-with [list -libs -l$lib] {
if {[cctest_function $function]} {
msg-result -l$lib
define lib_$function -l$lib
# prepend to LIBS
define LIBS "-l$lib [get-define LIBS]"
incr found
break
}
}
}
}
}
define-feature $function $found
if {!$found} {
msg-result "no"
}
return $found
}
# @cc-check-tools tool ...
#
# Checks for existence of the given compiler tools, taking
# into account any cross compilation prefix.
#
# For example, when checking for 'ar', first 'AR' is checked on the command
# line and then in the environment. If not found, '${host}-ar' or
# simply 'ar' is assumed depending upon whether cross compiling.
# The path is searched for this executable, and if found 'AR' is defined
# to the executable name.
# Note that even when cross compiling, the simple 'ar' is used as a fallback,
# but a warning is generated. This is necessary for some toolchains.
#
# It is an error if the executable is not found.
#
proc cc-check-tools {args} {
foreach tool $args {
set TOOL [string toupper $tool]
set exe [get-env $TOOL [get-define cross]$tool]
if {[find-executable $exe]} {
define $TOOL $exe
continue
}
if {[find-executable $tool]} {
msg-result "Warning: Failed to find $exe, falling back to $tool which may be incorrect"
define $TOOL $tool
continue
}
user-error "Failed to find $exe"
}
}
# @cc-check-progs prog ...
#
# Checks for existence of the given executables on the path.
#
# For example, when checking for 'grep', the path is searched for
# the executable, 'grep', and if found 'GREP' is defined as 'grep'.
#
# If the executable is not found, the variable is defined as 'false'.
# Returns 1 if all programs were found, or 0 otherwise.
#
proc cc-check-progs {args} {
set failed 0
foreach prog $args {
set PROG [string toupper $prog]
msg-checking "Checking for $prog..."
if {![find-executable $prog]} {
msg-result no
define $PROG false
incr failed
} else {
msg-result ok
define $PROG $prog
}
}
expr {!$failed}
}
# @cc-path-progs prog ...
#
# Like cc-check-progs, but sets the define to the full path rather
# than just the program name.
#
proc cc-path-progs {args} {
set failed 0
foreach prog $args {
set PROG [string toupper $prog]
msg-checking "Checking for $prog..."
set path [find-executable-path $prog]
if {$path eq ""} {
msg-result no
define $PROG false
incr failed
} else {
msg-result $path
define $PROG $path
}
}
expr {!$failed}
}
# Adds the given settings to $::autosetup(ccsettings) and
# returns the old settings.
#
proc cc-add-settings {settings} {
if {[llength $settings] % 2} {
autosetup-error "settings list is missing a value: $settings"
}
set prev [cc-get-settings]
# workaround a bug in some versions of jimsh by forcing
# conversion of $prev to a list
llength $prev
array set new $prev
foreach {name value} $settings {
switch -exact -- $name {
-cflags - -includes {
# These are given as lists
lappend new($name) {*}[list-non-empty $value]
}
-declare {
lappend new($name) $value
}
-libs {
# Note that new libraries are added before previous libraries
set new($name) [list {*}[list-non-empty $value] {*}$new($name)]
}
-link - -lang - -nooutput {
set new($name) $value
}
-source - -sourcefile - -code {
# XXX: These probably are only valid directly from cctest
set new($name) $value
}
default {
autosetup-error "unknown cctest setting: $name"
}
}
}
cc-store-settings [array get new]
return $prev
}
proc cc-store-settings {new} {
set ::autosetup(ccsettings) $new
}
proc cc-get-settings {} {
return $::autosetup(ccsettings)
}
# Similar to cc-add-settings, but each given setting
# simply replaces the existing value.
#
# Returns the previous settings
proc cc-update-settings {args} {
set prev [cc-get-settings]
cc-store-settings [dict merge $prev $args]
return $prev
}
# @cc-with settings ?{ script }?
#
# Sets the given 'cctest' settings and then runs the tests in '$script'.
# Note that settings such as '-lang' replace the current setting, while
# those such as '-includes' are appended to the existing setting.
#
# If no script is given, the settings become the default for the remainder
# of the 'auto.def' file.
#
## cc-with {-lang c++} {
## # This will check with the C++ compiler
## cc-check-types bool
## cc-with {-includes signal.h} {
## # This will check with the C++ compiler, signal.h and any existing includes.
## ...
## }
## # back to just the C++ compiler
## }
#
# The '-libs' setting is special in that newer values are added *before* earlier ones.
#
## cc-with {-libs {-lc -lm}} {
## cc-with {-libs -ldl} {
## cctest -libs -lsocket ...
## # libs will be in this order: -lsocket -ldl -lc -lm
## }
## }
#
# If you wish to invoke something like cc-check-flags but not have -cflags updated,
# use the following idiom:
#
## cc-with {} {
## cc-check-flags ...
## }
proc cc-with {settings args} {
if {[llength $args] == 0} {
cc-add-settings $settings
} elseif {[llength $args] > 1} {
autosetup-error "usage: cc-with settings ?script?"
} else {
set save [cc-add-settings $settings]
set rc [catch {uplevel 1 [lindex $args 0]} result info]
cc-store-settings $save
if {$rc != 0} {
return -code [dict get $info -code] $result
}
return $result
}
}
# @cctest ?settings?
#
# Low level C/C++ compiler checker. Compiles and or links a small C program
# according to the arguments and returns 1 if OK, or 0 if not.
#
# Supported settings are:
#
## -cflags cflags A list of flags to pass to the compiler
## -includes list A list of includes, e.g. {stdlib.h stdio.h}
## -declare code Code to declare before main()
## -link 1 Don't just compile, link too
## -lang c|c++ Use the C (default) or C++ compiler
## -libs liblist List of libraries to link, e.g. {-ldl -lm}
## -code code Code to compile in the body of main()
## -source code Compile a complete program. Ignore -includes, -declare and -code
## -sourcefile file Shorthand for -source [readfile [get-define srcdir]/$file]
## -nooutput 1 Treat any compiler output (e.g. a warning) as an error
#
# Unless '-source' or '-sourcefile' is specified, the C program looks like:
#
## #include <firstinclude> /* same for remaining includes in the list */
## declare-code /* any code in -declare, verbatim */
## int main(void) {
## code /* any code in -code, verbatim */
## return 0;
## }
#
# And the command line looks like:
#
## CC -cflags CFLAGS CPPFLAGS conftest.c -o conftest.o
## CXX -cflags CXXFLAGS CPPFLAGS conftest.cpp -o conftest.o
#
# And if linking:
#
## CC LDFLAGS -cflags CFLAGS conftest.c -o conftest -libs LIBS
## CXX LDFLAGS -cflags CXXFLAGS conftest.c -o conftest -libs LIBS
#
# Any failures are recorded in 'config.log'
#
proc cctest {args} {
set tmp conftest__
# Easiest way to merge in the settings
cc-with $args {
array set opts [cc-get-settings]
}
if {[info exists opts(-sourcefile)]} {
set opts(-source) [readfile [get-define srcdir]/$opts(-sourcefile) "#error can't find $opts(-sourcefile)"]
}
if {[info exists opts(-source)]} {
set lines $opts(-source)
} else {
foreach i $opts(-includes) {
if {$opts(-code) ne "" && ![feature-checked $i]} {
# Compiling real code with an unchecked header file
# Quickly (and silently) check for it now
# Remove all -includes from settings before checking
set saveopts [cc-update-settings -includes {}]
msg-quiet cc-check-includes $i
cc-store-settings $saveopts
}
if {$opts(-code) eq "" || [have-feature $i]} {
lappend source "#include <$i>"
}
}
lappend source {*}$opts(-declare)
lappend source "int main(void) {"
lappend source $opts(-code)
lappend source "return 0;"
lappend source "}"
set lines [join $source \n]
}
# Build the command line
set cmdline {}
lappend cmdline {*}[get-define CCACHE]
switch -exact -- $opts(-lang) {
c++ {
set src conftest__.cpp
lappend cmdline {*}[get-define CXX]
set cflags [get-define CXXFLAGS]
}
c {
set src conftest__.c
lappend cmdline {*}[get-define CC]
set cflags [get-define CFLAGS]
}
default {
autosetup-error "cctest called with unknown language: $opts(-lang)"
}
}
if {$opts(-link)} {
lappend cmdline {*}[get-define LDFLAGS]
} else {
lappend cflags {*}[get-define CPPFLAGS]
set tmp conftest__.o
lappend cmdline -c
}
lappend cmdline {*}$opts(-cflags) {*}[get-define cc-default-debug ""] {*}$cflags
lappend cmdline $src -o $tmp
if {$opts(-link)} {
lappend cmdline {*}$opts(-libs) {*}[get-define LIBS]
}
# At this point we have the complete command line and the
# complete source to be compiled. Get the result from cache if
# we can
if {[info exists ::cc_cache($cmdline,$lines)]} {
msg-checking "(cached) "
set ok $::cc_cache($cmdline,$lines)
if {$::autosetup(debug)} {
configlog "From cache (ok=$ok): [join $cmdline]"
configlog "============"
configlog $lines
configlog "============"
}
return $ok
}
writefile $src $lines\n
set ok 1
set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
if {$err || ($opts(-nooutput) && [string length $result])} {
configlog "Failed: [join $cmdline]"
configlog $result
configlog "============"
configlog "The failed code was:"
configlog $lines
configlog "============"
set ok 0
} elseif {$::autosetup(debug)} {
configlog "Compiled OK: [join $cmdline]"
configlog "============"
configlog $lines
configlog "============"
}
file delete $src
file delete $tmp
# cache it
set ::cc_cache($cmdline,$lines) $ok
return $ok
}
# @make-autoconf-h outfile ?auto-patterns=HAVE_*? ?bare-patterns=SIZEOF_*?
#
# Deprecated - see 'make-config-header'
proc make-autoconf-h {file {autopatterns {HAVE_*}} {barepatterns {SIZEOF_* HAVE_DECL_*}}} {
user-notice "*** make-autoconf-h is deprecated -- use make-config-header instead"
make-config-header $file -auto $autopatterns -bare $barepatterns
}
# @make-config-header outfile ?-auto patternlist? ?-bare patternlist? ?-none patternlist? ?-str patternlist? ...
#
# Examines all defined variables which match the given patterns
# and writes an include file, '$file', which defines each of these.
# Variables which match '-auto' are output as follows:
# - defines which have the value '0' are ignored.
# - defines which have integer values are defined as the integer value.
# - any other value is defined as a string, e.g. '"value"'
# Variables which match '-bare' are defined as-is.
# Variables which match '-str' are defined as a string, e.g. '"value"'
# Variables which match '-none' are omitted.
#
# Note that order is important. The first pattern that matches is selected.
# Default behaviour is:
#
## -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_* -none *
#
# If the file would be unchanged, it is not written.
proc make-config-header {file args} {
set guard _[string toupper [regsub -all {[^a-zA-Z0-9]} [file tail $file] _]]
file mkdir [file dirname $file]
set lines {}
lappend lines "#ifndef $guard"
lappend lines "#define $guard"
# Add some defaults
lappend args -bare {SIZEOF_* HAVE_DECL_*} -auto HAVE_*
foreach n [lsort [dict keys [all-defines]]] {
set value [get-define $n]
set type [calc-define-output-type $n $args]
switch -exact -- $type {
-bare {
# Just output the value unchanged
}
-none {
continue
}
-str {
set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
}
-auto {
# Automatically determine the type
if {$value eq "0"} {
lappend lines "/* #undef $n */"
continue
}
if {![string is integer -strict $value]} {
set value \"[string map [list \\ \\\\ \" \\\"] $value]\"
}
}
"" {
continue
}
default {
autosetup-error "Unknown type in make-config-header: $type"
}
}
lappend lines "#define $n $value"
}
lappend lines "#endif"
set buf [join $lines \n]
write-if-changed $file $buf {
msg-result "Created $file"
}
}
proc calc-define-output-type {name spec} {
foreach {type patterns} $spec {
foreach pattern $patterns {
if {[string match $pattern $name]} {
return $type
}
}
}
return ""
}
proc cc-init {} {
global autosetup
# Initialise some values from the environment or commandline or default settings
foreach i {LDFLAGS LIBS CPPFLAGS LINKFLAGS CFLAGS} {
lassign $i var default
define $var [get-env $var $default]
}
if {[env-is-set CC]} {
# Set by the user, so don't try anything else
set try [list [get-env CC ""]]
} else {
# Try some reasonable options
set try [list [get-define cross]cc [get-define cross]gcc]
}
define CC [find-an-executable {*}$try]
if {[get-define CC] eq ""} {
user-error "Could not find a C compiler. Tried: [join $try ", "]"
}
define CPP [get-env CPP "[get-define CC] -E"]
# XXX: Could avoid looking for a C++ compiler until requested
# If CXX isn't found, it is set to the empty string.
if {[env-is-set CXX]} {
define CXX [find-an-executable -required [get-env CXX ""]]
} else {
define CXX [find-an-executable [get-define cross]c++ [get-define cross]g++]
}
# CXXFLAGS default to CFLAGS if not specified
define CXXFLAGS [get-env CXXFLAGS [get-define CFLAGS]]
# May need a CC_FOR_BUILD, so look for one
define CC_FOR_BUILD [find-an-executable [get-env CC_FOR_BUILD ""] cc gcc false]
# These start empty and never come from the user or environment
define AS_CFLAGS ""
define AS_CPPFLAGS ""
define AS_CXXFLAGS ""
define CCACHE [find-an-executable [get-env CCACHE ccache]]
# If any of these are set in the environment, propagate them to the AUTOREMAKE commandline
foreach i {CC CXX CCACHE CPP CFLAGS CXXFLAGS CXXFLAGS LDFLAGS LIBS CROSS CPPFLAGS LINKFLAGS CC_FOR_BUILD LD} {
if {[env-is-set $i]} {
# Note: If the variable is set on the command line, get-env will return that value
# so the command line will continue to override the environment
define-append-argv AUTOREMAKE $i=[get-env $i ""]
}
}
# Initial cctest settings
cc-store-settings {-cflags {} -includes {} -declare {} -link 0 -lang c -libs {} -code {} -nooutput 0}
set autosetup(cc-include-deps) {}
msg-result "C compiler...[get-define CCACHE] [get-define CC] [get-define CFLAGS] [get-define CPPFLAGS]"
if {[get-define CXX] ne "false"} {
msg-result "C++ compiler...[get-define CCACHE] [get-define CXX] [get-define CXXFLAGS] [get-define CPPFLAGS]"
}
msg-result "Build C compiler...[get-define CC_FOR_BUILD]"
# On Darwin, we prefer to use -g0 to avoid creating .dSYM directories
# but some compilers may not support it, so test here.
switch -glob -- [get-define host] {
*-*-darwin* {
if {[cctest -cflags {-g0}]} {
define cc-default-debug -g0
}
}
}
if {![cc-check-includes stdlib.h]} {
user-error "Compiler does not work. See config.log"
}
}
cc-init

View File

@@ -0,0 +1,24 @@
#
# Run this TCL script to find and print the pathname for the tclConfig.sh
# file. Used by ../configure
#
if {[catch {
set libdir [tcl::pkgconfig get libdir,install]
}]} {
puts stderr "tclsh too old: does not support tcl::pkgconfig"
exit 1
}
if {![file exists $libdir]} {
puts stderr "tclsh reported library directory \"$libdir\" does not exist"
exit 1
}
if {![file exists $libdir/tclConfig.sh]} {
set n1 $libdir/tcl$::tcl_version
if {[file exists $n1/tclConfig.sh]} {
set libdir $n1
} else {
puts stderr "cannot find tclConfig.sh in either $libdir or $n1"
exit 1
}
}
puts $libdir

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
# Copyright (c) 2016 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# @synopsis:
#
# The 'pkg-config' module allows package information to be found via 'pkg-config'.
#
# If not cross-compiling, the package path should be determined automatically
# by 'pkg-config'.
# If cross-compiling, the default package path is the compiler sysroot.
# If the C compiler doesn't support '-print-sysroot', the path can be supplied
# by the '--sysroot' option or by defining 'SYSROOT'.
#
# 'PKG_CONFIG' may be set to use an alternative to 'pkg-config'.
use cc
options {
sysroot:dir => "Override compiler sysroot for pkg-config search path"
}
# @pkg-config-init ?required?
#
# Initialises the 'pkg-config' system. Unless '$required' is set to 0,
# it is a fatal error if a usable 'pkg-config' is not found .
#
# This command will normally be called automatically as required,
# but it may be invoked explicitly if lack of 'pkg-config' is acceptable.
#
# Returns 1 if ok, or 0 if 'pkg-config' not found/usable (only if '$required' is 0).
#
proc pkg-config-init {{required 1}} {
if {[is-defined HAVE_PKG_CONFIG]} {
return [get-define HAVE_PKG_CONFIG]
}
set found 0
define PKG_CONFIG [get-env PKG_CONFIG pkg-config]
msg-checking "Checking for pkg-config..."
if {[catch {exec [get-define PKG_CONFIG] --version} version]} {
msg-result "[get-define PKG_CONFIG] (not found)"
if {$required} {
user-error "No usable pkg-config"
}
} else {
msg-result $version
define PKG_CONFIG_VERSION $version
set found 1
if {[opt-str sysroot o]} {
define SYSROOT [file-normalize $o]
msg-result "Using specified sysroot [get-define SYSROOT]"
} elseif {[get-define build] ne [get-define host]} {
if {[catch {exec-with-stderr {*}[get-define CC] -print-sysroot} result errinfo] == 0} {
# Use the compiler sysroot, if there is one
define SYSROOT $result
msg-result "Found compiler sysroot $result"
} else {
configlog "[get-define CC] -print-sysroot: $result"
set msg "pkg-config: Cross compiling, but no compiler sysroot and no --sysroot supplied"
if {$required} {
user-error $msg
} else {
msg-result $msg
}
set found 0
}
}
if {[is-defined SYSROOT]} {
set sysroot [get-define SYSROOT]
# XXX: It's possible that these should be set only when invoking pkg-config
global env
set env(PKG_CONFIG_DIR) ""
# Supposedly setting PKG_CONFIG_LIBDIR means that PKG_CONFIG_PATH is ignored,
# but it doesn't seem to work that way in practice
set env(PKG_CONFIG_PATH) ""
# Do we need to try /usr/local as well or instead?
set env(PKG_CONFIG_LIBDIR) $sysroot/usr/lib/pkgconfig:$sysroot/usr/share/pkgconfig
set env(PKG_CONFIG_SYSROOT_DIR) $sysroot
}
}
define HAVE_PKG_CONFIG $found
return $found
}
# @pkg-config module ?requirements?
#
# Use 'pkg-config' to find the given module meeting the given requirements.
# e.g.
#
## pkg-config pango >= 1.37.0
#
# If found, returns 1 and sets 'HAVE_PKG_PANGO' to 1 along with:
#
## PKG_PANGO_VERSION to the found version
## PKG_PANGO_LIBS to the required libs (--libs-only-l)
## PKG_PANGO_LDFLAGS to the required linker flags (--libs-only-L)
## PKG_PANGO_CFLAGS to the required compiler flags (--cflags)
#
# If not found, returns 0.
#
proc pkg-config {module args} {
set ok [pkg-config-init]
msg-checking "Checking for $module $args..."
if {!$ok} {
msg-result "no pkg-config"
return 0
}
set pkgconfig [get-define PKG_CONFIG]
set ret [catch {exec $pkgconfig --modversion "$module $args"} version]
configlog "$pkgconfig --modversion $module $args: $version"
if {$ret} {
msg-result "not found"
return 0
}
# Sometimes --modversion succeeds but because of dependencies it isn't usable
# This seems to show up with --cflags
set ret [catch {exec $pkgconfig --cflags $module} cflags]
if {$ret} {
msg-result "unusable ($version - see config.log)"
configlog "$pkgconfig --cflags $module"
configlog $cflags
return 0
}
msg-result $version
set prefix [feature-define-name $module PKG_]
define HAVE_${prefix}
define ${prefix}_VERSION $version
define ${prefix}_CFLAGS $cflags
define ${prefix}_LIBS [exec $pkgconfig --libs-only-l $module]
define ${prefix}_LDFLAGS [exec $pkgconfig --libs-only-L $module]
return 1
}
# @pkg-config-get module setting
#
# Convenience access to the results of 'pkg-config'.
#
# For example, '[pkg-config-get pango CFLAGS]' returns
# the value of 'PKG_PANGO_CFLAGS', or '""' if not defined.
proc pkg-config-get {module name} {
set prefix [feature-define-name $module PKG_]
get-define ${prefix}_${name} ""
}
# @pkg-config-get-var module variable
#
# Return the value of the given variable from the given pkg-config module.
# The module must already have been successfully detected with pkg-config.
# e.g.
#
## if {[pkg-config harfbuzz >= 2.5]} {
## define harfbuzz_libdir [pkg-config-get-var harfbuzz libdir]
## }
#
# Returns the empty string if the variable isn't defined.
proc pkg-config-get-var {module variable} {
set pkgconfig [get-define PKG_CONFIG]
set prefix [feature-define-name $module HAVE_PKG_]
exec $pkgconfig $module --variable $variable
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,420 @@
# Copyright (c) 2010 WorkWare Systems http://www.workware.net.au/
# All rights reserved
# @synopsis:
#
# This module supports common system interrogation and options
# such as '--host', '--build', '--prefix', and setting 'srcdir', 'builddir', and 'EXEEXT'.
#
# It also support the "feature" naming convention, where searching
# for a feature such as 'sys/type.h' defines 'HAVE_SYS_TYPES_H'.
#
# It defines the following variables, based on '--prefix' unless overridden by the user:
#
## datadir
## sysconfdir
## sharedstatedir
## localstatedir
## infodir
## mandir
## includedir
#
# If '--prefix' is not supplied, it defaults to '/usr/local' unless 'options-defaults { prefix ... }' is used *before*
# including the 'system' module.
if {[is-defined defaultprefix]} {
user-notice "Note: defaultprefix is deprecated. Use options-defaults to set default options"
options-defaults [list prefix [get-define defaultprefix]]
}
options {
host:host-alias => {a complete or partial cpu-vendor-opsys for the system where
the application will run (defaults to the same value as --build)}
build:build-alias => {a complete or partial cpu-vendor-opsys for the system
where the application will be built (defaults to the
result of running config.guess)}
prefix:dir=/usr/local => {the target directory for the build (default: '@default@')}
# These (hidden) options are supported for autoconf/automake compatibility
exec-prefix:
bindir:
sbindir:
includedir:
mandir:
infodir:
libexecdir:
datadir:
libdir:
sysconfdir:
sharedstatedir:
localstatedir:
runstatedir:
maintainer-mode=0
dependency-tracking=0
silent-rules=0
program-prefix:
program-suffix:
program-transform-name:
x-includes:
x-libraries:
}
# @check-feature name { script }
#
# defines feature '$name' to the return value of '$script',
# which should be 1 if found or 0 if not found.
#
# e.g. the following will define 'HAVE_CONST' to 0 or 1.
#
## check-feature const {
## cctest -code {const int _x = 0;}
## }
proc check-feature {name code} {
msg-checking "Checking for $name..."
set r [uplevel 1 $code]
define-feature $name $r
if {$r} {
msg-result "ok"
} else {
msg-result "not found"
}
return $r
}
# @have-feature name ?default=0?
#
# Returns the value of feature '$name' if defined, or '$default' if not.
#
# See 'feature-define-name' for how the "feature" name
# is translated into the "define" name.
#
proc have-feature {name {default 0}} {
get-define [feature-define-name $name] $default
}
# @define-feature name ?value=1?
#
# Sets the feature 'define' to '$value'.
#
# See 'feature-define-name' for how the "feature" name
# is translated into the "define" name.
#
proc define-feature {name {value 1}} {
define [feature-define-name $name] $value
}
# @feature-checked name
#
# Returns 1 if feature '$name' has been checked, whether true or not.
#
proc feature-checked {name} {
is-defined [feature-define-name $name]
}
# @feature-define-name name ?prefix=HAVE_?
#
# Converts a "feature" name to the corresponding "define",
# e.g. 'sys/stat.h' becomes 'HAVE_SYS_STAT_H'.
#
# Converts '*' to 'P' and all non-alphanumeric to underscore.
#
proc feature-define-name {name {prefix HAVE_}} {
string toupper $prefix[regsub -all {[^a-zA-Z0-9]} [regsub -all {[*]} $name p] _]
}
# @write-if-changed filename contents ?script?
#
# If '$filename' doesn't exist, or it's contents are different to '$contents',
# the file is written and '$script' is evaluated.
#
# Otherwise a "file is unchanged" message is displayed.
proc write-if-changed {file buf {script {}}} {
set old [readfile $file ""]
if {$old eq $buf && [file exists $file]} {
msg-result "$file is unchanged"
} else {
writefile $file $buf\n
uplevel 1 $script
}
}
# @include-file infile mapping
#
# The core of make-template, called recursively for each @include
# directive found within that template so that this proc's result
# is the fully-expanded template.
#
# The mapping parameter is how we expand @varname@ within the template.
# We do that inline within this step only for @include directives which
# can have variables in the filename arg. A separate substitution pass
# happens when this recursive function returns, expanding the rest of
# the variables.
#
proc include-file {infile mapping} {
# A stack of true/false conditions, one for each nested conditional
# starting with "true"
set condstack {1}
set result {}
set linenum 0
foreach line [split [readfile $infile] \n] {
incr linenum
if {[regexp {^@(if|else|endif)(\s*)(.*)} $line -> condtype condspace condargs]} {
if {$condtype eq "if"} {
if {[string length $condspace] == 0} {
autosetup-error "$infile:$linenum: Invalid expression: $line"
}
if {[llength $condargs] == 1} {
# ABC => [get-define ABC] ni {0 ""}
# !ABC => [get-define ABC] in {0 ""}
lassign $condargs condvar
if {[regexp {^!(.*)} $condvar -> condvar]} {
set op in
} else {
set op ni
}
set condexpr "\[[list get-define $condvar]\] $op {0 {}}"
} else {
# Translate alphanumeric ABC into [get-define ABC] and leave the
# rest of the expression untouched
regsub -all {([A-Z][[:alnum:]_]*)} $condargs {[get-define \1]} condexpr
}
if {[catch [list expr $condexpr] condval]} {
dputs $condval
autosetup-error "$infile:$linenum: Invalid expression: $line"
}
dputs "@$condtype: $condexpr => $condval"
}
if {$condtype ne "if"} {
if {[llength $condstack] <= 1} {
autosetup-error "$infile:$linenum: Error: @$condtype missing @if"
} elseif {[string length $condargs] && [string index $condargs 0] ne "#"} {
autosetup-error "$infile:$linenum: Error: Extra arguments after @$condtype"
}
}
switch -exact $condtype {
if {
# push condval
lappend condstack $condval
}
else {
# Toggle the last entry
set condval [lpop condstack]
set condval [expr {!$condval}]
lappend condstack $condval
}
endif {
if {[llength $condstack] == 0} {
user-notice "$infile:$linenum: Error: @endif missing @if"
}
lpop condstack
}
}
continue
}
# Only continue if the stack contains all "true"
if {"0" in $condstack} {
continue
}
if {[regexp {^@include\s+(.*)} $line -> filearg]} {
set incfile [string map $mapping $filearg]
if {[file exists $incfile]} {
lappend ::autosetup(deps) [file-normalize $incfile]
lappend result {*}[include-file $incfile $mapping]
} else {
user-error "$infile:$linenum: Include file $incfile is missing"
}
continue
}
if {[regexp {^@define\s+(\w+)\s+(.*)} $line -> var val]} {
define $var $val
continue
}
lappend result $line
}
return $result
}
# @make-template template ?outfile?
#
# Reads the input file '<srcdir>/$template' and writes the output file '$outfile'
# (unless unchanged).
# If '$outfile' is blank/omitted, '$template' should end with '.in' which
# is removed to create the output file name.
#
# Each pattern of the form '@define@' is replaced with the corresponding
# "define", if it exists, or left unchanged if not.
#
# The special value '@srcdir@' is substituted with the relative
# path to the source directory from the directory where the output
# file is created, while the special value '@top_srcdir@' is substituted
# with the relative path to the top level source directory.
#
# Conditional sections may be specified as follows:
## @if NAME eq "value"
## lines
## @else
## lines
## @endif
#
# Where 'NAME' is a defined variable name and '@else' is optional.
# Note that variables names *must* start with an uppercase letter.
# If the expression does not match, all lines through '@endif' are ignored.
#
# The alternative forms may also be used:
## @if NAME (true if the variable is defined, but not empty and not "0")
## @if !NAME (opposite of the form above)
## @if <general-tcl-expression>
#
# In the general Tcl expression, any words beginning with an uppercase letter
# are translated into [get-define NAME]
#
# Expressions may be nested
#
proc make-template {template {out {}}} {
set infile [file join $::autosetup(srcdir) $template]
if {![file exists $infile]} {
user-error "Template $template is missing"
}
# Define this as late as possible
define AUTODEPS $::autosetup(deps)
if {$out eq ""} {
if {[file ext $template] ne ".in"} {
autosetup-error "make_template $template has no target file and can't guess"
}
set out [file rootname $template]
}
set outdir [file dirname $out]
# Make sure the directory exists
file mkdir $outdir
# Set up srcdir and top_srcdir to be relative to the target dir
define srcdir [relative-path [file join $::autosetup(srcdir) $outdir] $outdir]
define top_srcdir [relative-path $::autosetup(srcdir) $outdir]
# Build map from global defines to their values so they can be
# substituted into @include file names.
proc build-define-mapping {} {
set mapping {}
foreach {n v} [array get ::define] {
lappend mapping @$n@ $v
}
return $mapping
}
set mapping [build-define-mapping]
set result [include-file $infile $mapping]
# Rebuild the define mapping in case we ran across @define
# directives in the template or a file it @included, then
# apply that mapping to the expanded template.
set mapping [build-define-mapping]
write-if-changed $out [string map $mapping [join $result \n]] {
msg-result "Created [relative-path $out] from [relative-path $template]"
}
}
proc system-init {} {
global autosetup
# build/host tuples and cross-compilation prefix
opt-str build build ""
define build_alias $build
if {$build eq ""} {
define build [config_guess]
} else {
define build [config_sub $build]
}
opt-str host host ""
define host_alias $host
if {$host eq ""} {
define host [get-define build]
set cross ""
} else {
define host [config_sub $host]
set cross $host-
}
define cross [get-env CROSS $cross]
# build/host _cpu, _vendor and _os
foreach type {build host} {
set v [get-define $type]
if {![regexp {^([^-]+)-([^-]+)-(.*)$} $v -> cpu vendor os]} {
user-error "Invalid canonical $type: $v"
}
define ${type}_cpu $cpu
define ${type}_vendor $vendor
define ${type}_os $os
}
opt-str prefix prefix /usr/local
# These are for compatibility with autoconf
define target [get-define host]
define prefix $prefix
define builddir $autosetup(builddir)
define srcdir $autosetup(srcdir)
define top_srcdir $autosetup(srcdir)
define abs_top_srcdir [file-normalize $autosetup(srcdir)]
define abs_top_builddir [file-normalize $autosetup(builddir)]
# autoconf supports all of these
define exec_prefix [opt-str exec-prefix exec_prefix $prefix]
foreach {name defpath} {
bindir /bin
sbindir /sbin
libexecdir /libexec
libdir /lib
} {
define $name [opt-str $name o $exec_prefix$defpath]
}
foreach {name defpath} {
datadir /share
sharedstatedir /com
infodir /share/info
mandir /share/man
includedir /include
} {
define $name [opt-str $name o $prefix$defpath]
}
if {$prefix ne {/usr}} {
opt-str sysconfdir sysconfdir $prefix/etc
} else {
opt-str sysconfdir sysconfdir /etc
}
define sysconfdir $sysconfdir
define localstatedir [opt-str localstatedir o /var]
define runstatedir [opt-str runstatedir o /run]
define SHELL [get-env SHELL [find-an-executable sh bash ksh]]
# These could be used to generate Makefiles following some automake conventions
define AM_SILENT_RULES [opt-bool silent-rules]
define AM_MAINTAINER_MODE [opt-bool maintainer-mode]
define AM_DEPENDENCY_TRACKING [opt-bool dependency-tracking]
# Windows vs. non-Windows
switch -glob -- [get-define host] {
*-*-ming* - *-*-cygwin - *-*-msys {
define-feature windows
define EXEEXT .exe
}
default {
define EXEEXT ""
}
}
# Display
msg-result "Host System...[get-define host]"
msg-result "Build System...[get-define build]"
}
system-init

View File

@@ -0,0 +1,4 @@
The *.tcl files in this directory are part of the SQLite's "autoconf"
bundle which are specific to the TEA(-ish) build. During the tarball
generation process, they are copied into <TOP>/autoconf/autosetup/teaish
(which itself is created as part of that process).

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,214 @@
########################################################################
# 2025 April 7
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# * May you do good and not evil.
# * May you find forgiveness for yourself and forgive others.
# * May you share freely, never taking more than you give.
#
########################################################################
# ----- @module feature-tests.tcl -----
# @section TEA-ish collection of feature tests.
#
# Functions in this file with a prefix of teaish__ are
# private/internal APIs. Those with a prefix of teaish- are
# public APIs.
# @teaish-check-libz
#
# Checks for zlib.h and the function deflate in libz. If found,
# prepends -lz to the extension's ldflags and returns 1, else returns
# 0. It also defines LDFLAGS_LIBZ to the libs flag.
#
proc teaish-check-libz {} {
teaish-check-cached "Checking for libz" {
set rc 0
if {[msg-quiet cc-check-includes zlib.h] && [msg-quiet proj-check-function-in-lib deflate z]} {
teaish-ldflags-prepend [define LDFLAGS_LIBZ [get-define lib_deflate]]
undefine lib_deflate
incr rc
}
expr $rc
}
}
# @teaish-check-librt ?funclist?
#
# Checks whether -lrt is needed for any of the given functions. If
# so, appends -lrt via [teaish-ldflags-prepend] and returns 1, else
# returns 0. It also defines LDFLAGS_LIBRT to the libs flag or an
# empty string.
#
# Some systems (ex: SunOS) require -lrt in order to use nanosleep.
#
proc teaish-check-librt {{funclist {fdatasync nanosleep}}} {
teaish-check-cached -nostatus "Checking whether ($funclist) need librt" {
define LDFLAGS_LIBRT ""
foreach func $funclist {
if {[msg-quiet proj-check-function-in-lib $func rt]} {
set ldrt [get-define lib_${func}]
undefine lib_${func}
if {"" ne $ldrt} {
teaish-ldflags-prepend -r [define LDFLAGS_LIBRT $ldrt]
msg-result $ldrt
return 1
} else {
msg-result "no lib needed"
return 1
}
}
}
msg-result "not found"
return 0
}
}
# @teaish-check-stdint
#
# A thin proxy for [cc-with] which checks for <stdint.h> and the
# various fixed-size int types it declares. It defines HAVE_STDINT_T
# to 0 or 1 and (if it's 1) defines HAVE_XYZ_T for each XYZ int type
# to 0 or 1, depending on whether its available.
proc teaish-check-stdint {} {
teaish-check-cached "Checking for stdint.h" {
msg-quiet cc-with {-includes stdint.h} \
{cc-check-types int8_t int16_t int32_t int64_t intptr_t \
uint8_t uint16_t uint32_t uint64_t uintptr_t}
}
}
# @teaish-is-mingw
#
# Returns 1 if building for mingw, else 0.
proc teaish-is-mingw {} {
return [expr {
[string match *mingw* [get-define host]] &&
![file exists /dev/null]
}]
}
# @teaish-check-libdl
#
# Checks for whether dlopen() can be found and whether it requires
# -ldl for linking. If found, returns 1, defines LDFLAGS_DLOPEN to the
# linker flags (if any), and passes those flags to
# teaish-ldflags-prepend. It unconditionally defines HAVE_DLOPEN to 0
# or 1 (the its return result value).
proc teaish-check-dlopen {} {
teaish-check-cached -nostatus "Checking for dlopen()" {
set rc 0
set lfl ""
if {[cc-with {-includes dlfcn.h} {
cctest -link 1 -declare "extern char* dlerror(void);" -code "dlerror();"}]} {
msg-result "-ldl not needed"
incr rc
} elseif {[cc-check-includes dlfcn.h]} {
incr rc
if {[cc-check-function-in-lib dlopen dl]} {
set lfl [get-define lib_dlopen]
undefine lib_dlopen
msg-result " dlopen() needs $lfl"
} else {
msg-result " - dlopen() not found in libdl. Assuming dlopen() is built-in."
}
} else {
msg-result "not found"
}
teaish-ldflags-prepend [define LDFLAGS_DLOPEN $lfl]
define HAVE_DLOPEN $rc
}
}
#
# @teaish-check-libmath
#
# Handles the --enable-math flag. Returns 1 if found, else 0.
# If found, it prepends -lm (if needed) to the linker flags.
proc teaish-check-libmath {} {
teaish-check-cached "Checking for libc math library" {
set lfl ""
set rc 0
if {[msg-quiet proj-check-function-in-lib ceil m]} {
incr rc
set lfl [get-define lib_ceil]
undefine lib_ceil
teaish-ldflags-prepend $lfl
msg-checking "$lfl "
}
define LDFLAGS_LIBMATH $lfl
expr $rc
}
}
# @teaish-import-features ?-flags? feature-names...
#
# For each $name in feature-names... it invokes:
#
# use teaish/feature/$name
#
# to load TEAISH_AUTOSETUP_DIR/feature/$name.tcl
#
# By default, if a proc named teaish-check-${name}-options is defined
# after sourcing a file, it is called and its result is passed to
# proj-append-options. This can be suppressed with the -no-options
# flag.
#
# Flags:
#
# -no-options: disables the automatic running of
# teaish-check-NAME-options,
#
# -run: if the function teaish-check-NAME exists after importing
# then it is called. This flag must not be used when calling this
# function from teaish-options. This trumps both -pre and -post.
#
# -pre: if the function teaish-check-NAME exists after importing
# then it is passed to [teaish-checks-queue -pre].
#
# -post: works like -pre but instead uses[teaish-checks-queue -post].
proc teaish-import-features {args} {
set pk ""
set doOpt 1
proj-parse-simple-flags args flags {
-no-options 0 {set doOpt 0}
-run 0 {expr 1}
-pre 0 {set pk -pre}
-post 0 {set pk -post}
}
#
# TODO: never import the same module more than once. The "use"
# command is smart enough to not do that but we would need to
# remember whether or not any teaish-check-${arg}* procs have been
# called before, and skip them.
#
if {$flags(-run) && "" ne $pk} {
proj-error "Cannot use both -run and $pk" \
" (called from [proj-scope 1])"
}
foreach arg $args {
uplevel "use teaish/feature/$arg"
if {$doOpt} {
set n "teaish-check-${arg}-options"
if {[llength [info proc $n]] > 0} {
if {"" ne [set x [$n]]} {
options-add $x
}
}
}
if {$flags(-run)} {
set n "teaish-check-${arg}"
if {[llength [info proc $n]] > 0} {
uplevel 1 $n
}
} elseif {"" ne $pk} {
set n "teaish-check-${arg}"
if {[llength [info proc $n]] > 0} {
teaish-checks-queue {*}$pk $n
}
}
}
}

View File

@@ -0,0 +1,293 @@
########################################################################
# 2025 April 5
#
# The author disclaims copyright to this source code. In place of
# a legal notice, here is a blessing:
#
# * May you do good and not evil.
# * May you find forgiveness for yourself and forgive others.
# * May you share freely, never taking more than you give.
#
########################################################################
#
# Helper routines for running tests on teaish extensions
#
########################################################################
# ----- @module teaish/tester.tcl -----
#
# @section TEA-ish Testing APIs.
#
# Though these are part of the autosup dir hierarchy, they are not
# intended to be run from autosetup code. Rather, they're for use
# with/via teaish.tester.tcl and target canonical Tcl only, not JimTcl
# (which the autosetup pieces do target).
#
# @test-current-scope ?lvl?
#
# Returns the name of the _calling_ proc from ($lvl + 1) levels up the
# call stack (where the caller's level will be 1 up from _this_
# call). If $lvl would resolve to global scope "global scope" is
# returned and if it would be negative then a string indicating such
# is returned (as opposed to throwing an error).
#
proc test-current-scope {{lvl 0}} {
#uplevel [expr {$lvl + 1}] {lindex [info level 0] 0}
set ilvl [info level]
set offset [expr {$ilvl - $lvl - 1}]
if { $offset < 0} {
return "invalid scope ($offset)"
} elseif { $offset == 0} {
return "global scope"
} else {
return [lindex [info level $offset] 0]
}
}
# @test-msg
#
# Emits all arugments to stdout.
#
proc test-msg {args} {
puts "$args"
}
# @test-warn
#
# Emits all arugments to stderr.
#
proc test-warn {args} {
puts stderr "WARNING: $args"
}
#
# @test-error msg
#
# Triggers a test-failed error with a string describing the calling
# scope and the provided message.
#
proc test-fail {args} {
#puts stderr "ERROR: \[[test-current-scope 1]]: $msg"
#exit 1
error "FAIL: \[[test-current-scope 1]]: $args"
}
array set ::test__Counters {}
array set ::test__Config {
verbose-assert 0 verbose-affirm 0
}
# Internal impl for affirm and assert.
#
# $args = ?-v? script {msg-on-fail ""}
proc test__affert {failMode args} {
if {$failMode} {
set what assert
} else {
set what affirm
}
set verbose $::test__Config(verbose-$what)
if {"-v" eq [lindex $args 0]} {
lassign $args - script msg
if {1 == [llength $args]} {
# If -v is the only arg, toggle default verbose mode
set ::test__Config(verbose-$what) [expr {!$::test__Config(verbose-$what)}]
return
}
incr verbose
} else {
lassign $args script msg
}
incr ::test__Counters($what)
if {![uplevel 1 expr [list $script]]} {
if {"" eq $msg} {
set msg $script
}
set txt [join [list $what # $::test__Counters($what) "failed:" $msg]]
if {$failMode} {
puts stderr $txt
exit 1
} else {
error $txt
}
} elseif {$verbose} {
puts stderr [join [list $what # $::test__Counters($what) "passed:" $script]]
}
}
#
# @affirm ?-v? script ?msg?
#
# Works like a conventional assert method does, but reports failures
# using [error] instead of [exit]. If -v is used, it reports passing
# assertions to stderr. $script is evaluated in the caller's scope as
# an argument to [expr].
#
proc affirm {args} {
tailcall test__affert 0 {*}$args
}
#
# @assert ?-v? script ?msg?
#
# Works like [affirm] but exits on error.
#
proc assert {args} {
tailcall test__affert 1 {*}$args
}
#
# @assert-matches ?-e? pattern ?-e? rhs ?msg?
#
# Equivalent to assert {[string match $pattern $rhs]} except that
# if either of those are prefixed with an -e flag, they are eval'd
# and their results are used.
#
proc assert-matches {args} {
set evalLhs 0
set evalRhs 0
if {"-e" eq [lindex $args 0]} {
incr evalLhs
set args [lassign $args -]
}
set args [lassign $args pattern]
if {"-e" eq [lindex $args 0]} {
incr evalRhs
set args [lassign $args -]
}
set args [lassign $args rhs msg]
if {$evalLhs} {
set pattern [uplevel 1 $pattern]
}
if {$evalRhs} {
set rhs [uplevel 1 $rhs]
}
#puts "***pattern=$pattern\n***rhs=$rhs"
tailcall test__affert 1 \
[join [list \[ string match [list $pattern] [list $rhs] \]]] $msg
# why does this not work? [list \[ string match [list $pattern] [list $rhs] \]] $msg
# "\[string match [list $pattern] [list $rhs]\]"
}
#
# @test-assert testId script ?msg?
#
# Works like [assert] but emits $testId to stdout first.
#
proc test-assert {testId script {msg ""}} {
puts "test $testId"
tailcall test__affert 1 $script $msg
}
#
# @test-expect testId script result
#
# Runs $script in the calling scope and compares its result to
# $result, minus any leading or trailing whitespace. If they differ,
# it triggers an [assert].
#
proc test-expect {testId script result} {
puts "test $testId"
set x [string trim [uplevel 1 $script]]
set result [string trim $result]
tailcall test__affert 0 [list "{$x}" eq "{$result}"] \
"\nEXPECTED: <<$result>>\nGOT: <<$x>>"
}
#
# @test-catch cmd ?...args?
#
# Runs [cmd ...args], repressing any exception except to possibly log
# the failure. Returns 1 if it caught anything, 0 if it didn't.
#
proc test-catch {cmd args} {
if {[catch {
uplevel 1 $cmd {*}$args
} rc xopts]} {
puts "[test-current-scope] ignoring failure of: $cmd [lindex $args 0]: $rc"
return 1
}
return 0
}
#
# @test-catch-matching pattern (script|cmd args...)
#
# Works like test-catch, but it expects its argument(s) to to throw an
# error matching the given string (checked with [string match]). If
# they do not throw, or the error does not match $pattern, this
# function throws, else it returns 1.
#
# If there is no second argument, the $cmd is assumed to be a script,
# and will be eval'd in the caller's scope.
#
# TODO: add -glob and -regex flags to control matching flavor.
#
proc test-catch-matching {pattern cmd args} {
if {[catch {
#puts "**** catch-matching cmd=$cmd args=$args"
if {0 == [llength $args]} {
uplevel 1 $cmd {*}$args
} else {
$cmd {*}$args
}
} rc xopts]} {
if {[string match $pattern $rc]} {
return 1
} else {
error "[test-current-scope] exception does not match {$pattern}: {$rc}"
}
}
error "[test-current-scope] expecting to see an error matching {$pattern}"
}
if {![array exists ::teaish__BuildFlags]} {
array set ::teaish__BuildFlags {}
}
#
# @teaish-build-flag3 flag tgtVar ?dflt?
#
# If the current build has the configure-time flag named $flag set
# then tgtVar is assigned its value and 1 is returned, else tgtVal is
# assigned $dflt and 0 is returned.
#
# Caveat #1: only valid when called in the context of teaish's default
# "make test" recipe, e.g. from teaish.test.tcl. It is not valid from
# a teaish.tcl configure script because (A) the state it relies on
# doesn't fully exist at that point and (B) that level of the API has
# more direct access to the build state. This function requires that
# an external script have populated its internal state, which is
# normally handled via teaish.tester.tcl.in.
#
# Caveat #2: defines in the style of HAVE_FEATURENAME with a value of
# 0 are, by long-standing configure script conventions, treated as
# _undefined_ here.
#
proc teaish-build-flag3 {flag tgtVar {dflt ""}} {
upvar $tgtVar tgt
if {[info exists ::teaish__BuildFlags($flag)]} {
set tgt $::teaish__BuildFlags($flag)
return 1;
} elseif {0==[array size ::teaish__BuildFlags]} {
test-warn \
"\[[test-current-scope]] was called from " \
"[test-current-scope 1] without the build flags imported."
}
set tgt $dflt
return 0
}
#
# @teaish-build-flag flag ?dflt?
#
# Convenience form of teaish-build-flag3 which returns the
# configure-time-defined value of $flag or "" if it's not defined (or
# if it's an empty string).
#
proc teaish-build-flag {flag {dflt ""}} {
set tgt ""
teaish-build-flag3 $flag tgt $dflt
return $tgt
}