diff --git a/Makefile.msc b/Makefile.msc
index af9f2b9c7a..a40fea8934 100644
--- a/Makefile.msc
+++ b/Makefile.msc
@@ -884,7 +884,7 @@ RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
!ENDIF
!IF $(DEBUG)>2
-TCC = $(TCC) -DSQLITE_DEBUG=1
+TCC = $(TCC) -DSQLITE_DEBUG=1 -DSQLITE_USE_W32_FOR_CONSOLE_IO
RCC = $(RCC) -DSQLITE_DEBUG=1
!IF $(DYNAMIC_SHELL)==0
TCC = $(TCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE
@@ -1973,10 +1973,6 @@ fuzzcheck.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
fuzzcheck-asan.exe: $(FUZZCHECK_SRC) $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) /fsanitize=address $(FUZZCHECK_OPTS) $(FUZZCHECK_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
-run-fuzzcheck: fuzzcheck.exe fuzzcheck-asan.exe
- fuzzcheck --spinner $(FUZZDB)
- fuzzcheck-asan --spinner $(FUZZDB)
-
ossshell.exe: $(OSSSHELL_SRC) $(SQLITE3C) $(SQLITE3H)
$(LTLINK) $(NO_WARN) $(FUZZCHECK_OPTS) $(OSSSHELL_SRC) $(SQLITE3C) /link $(LDFLAGS) $(LTLINKOPTS)
diff --git a/autoconf/Makefile.msc b/autoconf/Makefile.msc
index d7284af23a..dfa2dcfd5b 100644
--- a/autoconf/Makefile.msc
+++ b/autoconf/Makefile.msc
@@ -724,7 +724,7 @@ RCC = $(RCC) -DSQLITE_ENABLE_API_ARMOR=1
!ENDIF
!IF $(DEBUG)>2
-TCC = $(TCC) -DSQLITE_DEBUG=1
+TCC = $(TCC) -DSQLITE_DEBUG=1 -DSQLITE_USE_W32_FOR_CONSOLE_IO
RCC = $(RCC) -DSQLITE_DEBUG=1
!IF $(DYNAMIC_SHELL)==0
TCC = $(TCC) -DSQLITE_ENABLE_WHERETRACE -DSQLITE_ENABLE_SELECTTRACE
diff --git a/autoconf/tea/Makefile.in b/autoconf/tea/Makefile.in
index 911717bc43..ad71c8b3e2 100644
--- a/autoconf/tea/Makefile.in
+++ b/autoconf/tea/Makefile.in
@@ -34,8 +34,8 @@ INSTALL.noexec = $(INSTALL) -m 0644
# any given teaish build.
#
tx.name = @TEAISH_NAME@
-tx.pkgName = @TEAISH_PKGNAME@
tx.version = @TEAISH_VERSION@
+tx.name.pkg = @TEAISH_PKGNAME@
tx.libdir = @TEAISH_LIBDIR_NAME@
tx.loadPrefix = @TEAISH_LOAD_PREFIX@
tx.tcl = @TEAISH_TCL@
@@ -46,7 +46,13 @@ tx.dll9.basename = @TEAISH_DLL9_BASENAME@
tx.dll8 = @TEAISH_DLL8@
tx.dll9 = @TEAISH_DLL9@
tx.dll = $(tx.dll$(TCL_MAJOR_VERSION))
-tx.dir = @TEAISH_DIR@
+tx.dir = @TEAISH_EXT_DIR@
+
+@if TEAISH_DIST_NAME
+tx.name.dist = @TEAISH_DIST_NAME@
+@else
+tx.name.dist = $(teaish.name)
+@endif
teaish.dir = @abs_top_srcdir@
#teaish.dir.autosetup = @TEAISH_AUTOSETUP_DIR@
@@ -69,6 +75,7 @@ libdir = @libdir@
libexecdir = @libexecdir@
localstatedir = @localstatedir@
mandir = @mandir@
+prefix = @prefix@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
@@ -81,16 +88,17 @@ sysconfdir = @sysconfdir@
#
TCLSH = @TCLSH_CMD@
TCL_CONFIG_SH = @TCL_CONFIG_SH@
-TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@
-TCL_LIB_SPEC = @TCL_LIB_SPEC@
-TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@
TCL_EXEC_PREFIX = @TCL_EXEC_PREFIX@
-TCL_VERSION = @TCL_VERSION@
+TCL_INCLUDE_SPEC = @TCL_INCLUDE_SPEC@
+TCL_LIBS = @TCL_LIBS@
+TCL_LIB_SPEC = @TCL_LIB_SPEC@
TCL_MAJOR_VERSION = @TCL_MAJOR_VERSION@
TCL_MINOR_VERSION = @TCL_MINOR_VERSION@
TCL_PATCH_LEVEL = @TCL_PATCH_LEVEL@
+TCL_PREFIX = @TCL_PREFIX@
TCL_SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
-TCL_LIBS = @TCL_LIBS@
+TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@
+TCL_VERSION = @TCL_VERSION@
TCLLIBDIR = @TCLLIBDIR@
#
@@ -107,10 +115,15 @@ CFLAGS.configure = @SH_CFLAGS@ @TEAISH_CFLAGS@ @CFLAGS@ @CPPFLAGS@ $(TCL_INCLUDE
# LDFLAGS.configure = LDFLAGS as known at configure-time.
#
# This ordering is deliberate: flags populated via tcl's
-# [teaish-ldflags-add] should preceed LDFLAGS (which typically
+# [teaish-ldflags-add] should precede LDFLAGS (which typically
# comes from the ./configure command-line invocation).
#
-LDFLAGS.configure = @SH_LDFLAGS@ @TEAISH_LDFLAGS@ @LDFLAGS@ $(TCL_STUB_LIB_SPEC)
+LDFLAGS.configure = @TEAISH_LDFLAGS@ @LDFLAGS@
+
+#
+# Linker flags for linkhing a shared library.
+#
+LDFLAGS.shlib = @SH_LDFLAGS@
#
# The following tx.XYZ vars may be populated/modified by teaish.tcl
@@ -119,11 +132,11 @@ LDFLAGS.configure = @SH_LDFLAGS@ @TEAISH_LDFLAGS@ @LDFLAGS@ $(TCL_STUB_LIB_SPEC)
#
# tx.src is the list of source or object files to include in the
-# (single) compiler invocation. This will initially contain any
-# sources passed to [teaish-src-add], but may also be appended to
-# by teaish.make.
+# (single) compiler/linker invocation. This will initially contain any
+# sources passed to [teaish-src-add], but may also be appended to by
+# teaish.make.
#
-tx.src =@TEAISH_SRC@
+tx.src =@TEAISH_EXT_SRC@
#
# tx.CFLAGS is typically set by teaish.make, whereas TEAISH_CFLAGS
@@ -161,38 +174,66 @@ teaish__autogen.deps = \
$(tx.makefile.in) $(teaish.makefile.in) \
$(tx.tcl) \
@TEAISH_PKGINDEX_TCL_IN@ \
- @TEAISH_MODULE_TEST_TCL@ \
@AUTODEPS@
+@if TEAISH_MAKEFILE_IN
+$(tx.makefile): $(tx.makefile.in)
+@endif
+
+teaish.autoreconfig = \
+ @TEAISH_AUTORECONFIG@
+
#
# Problem: when more than one target can invoke TEAISH_AUTORECONFIG,
# we can get parallel reconfigures running. Thus, targets which
# may require reconfigure should depend on...
#
config.log: $(teaish__autogen.deps)
- @TEAISH_AUTORECONFIG@
+ $(teaish.autoreconfig)
# ^^^ We would love to skip this when running [dist]clean, but there's
# no POSIX Make-portable way to do that. GNU Make can.
.PHONY: reconfigure
reconfigure:
- @TEAISH_AUTORECONFIG@
+ $(teaish.autoreconfig)
$(teaish.makefile): $(teaish__auto.def) $(teaish.makefile.in) \
@AUTODEPS@
+@if TEAISH_TESTER_TCL_IN
@TEAISH_TESTER_TCL_IN@:
@TEAISH_TESTER_TCL@: @TEAISH_TESTER_TCL_IN@
config.log: @TEAISH_TESTER_TCL@
+@endif
+#
+# CC variant for compiling Tcl-using sources.
+#
+CC.tcl = \
+ $(CC) -o $@ $(CFLAGS.configure) $(CFLAGS) $(tx.CFLAGS)
+
+#
+# CC variant for linking $(tx.src) into an extension DLL. Note that
+# $(tx.src) must come before $(LDFLAGS...) for linking to third-party
+# libs to work.
+#
+CC.dll = \
+ $(CC.tcl) $(tx.src) $(LDFLAGS.shlib) \
+ $(LDFLAGS.configure) $(LDFLAGS) $(tx.LDFLAGS) $(TCL_STUB_LIB_SPEC)
+
+@if TEAISH_ENABLE_DLL
#
# The rest of this makefile exists solely to support this brief
# target: the extension shared lib.
#
$(tx.dll): $(tx.src) config.log
- $(CC) -o $@ $(CFLAGS.configure) $(CFLAGS) $(tx.CFLAGS) \
- $(tx.src) $(LDFLAGS.configure) $(LDFLAGS) $(tx.LDFLAGS)
+ @if [ "x" = "x$(tx.src)" ]; then \
+ echo "Makefile var tx.src (source/object files) is empty" 1>&2; \
+ exit 1; \
+ fi
+ $(CC.dll)
all: $(tx.dll)
+@endif # TEAISH_ENABLE_DLL
tclsh: $(teaish.makefile) config.log
@{ echo "#!/bin/sh"; echo 'exec $(TCLSH) "$$@"'; } > $@
@@ -200,23 +241,29 @@ tclsh: $(teaish.makefile) config.log
@echo "Created $@"
#
-# If the extension includes teaish.test.tcl then provide a "test"
-# target which which runs that script, passing it (1) the full path to
-# extension's DLL (which also provides the script with a way to get
-# the test directory) and (2) a script of test utility code intended for
-# sourcing by the client.
+# Run the generated test script.
#
-# If the extension has no test script, add a small one which
-# simply loads the DLL and success if it can.
-#
-#
-tx.tester.args = $(tx.dll) $(tx.loadPrefix) @TEAISH_MODULE_TEST_TCL@
-.PHONY: test-pre test-core test test-post test-extension
-test-extension: # this name is reserved for use by teaish.make
-test-prepre: $(tx.dll) @TEAISH_TESTER_TCL@
+.PHONY: test-pre test-prepre test-core test test-post test-extension
+test-extension: # this name is reserved for use by teaish.make[.in]
+@if TEAISH_ENABLE_DLL
+test-prepre: $(tx.dll)
+@endif
+@if TEAISH_TESTER_TCL
+test-core.args = @TEAISH_TESTER_TCL@
+@if TEAISH_ENABLE_DLL
+test-core.args += '$(tx.dll)' '$(tx.loadPrefix)'
+@else
+test-core.args += '' ''
+@endif
+test-core.args += @TEAISH_TESTUTIL_TCL@
+test-core: test-pre
+ $(TCLSH) $(test-core.args)
+test-prepre: @TEAISH_TESTER_TCL@
+@else # !TEAISH_TESTER_TCL
+test-prepre:
+@endif # TEAISH_TESTER_TCL
test-pre: test-prepre
test-core: test-pre
- $(TCLSH) @TEAISH_TESTER_TCL@ $(tx.tester.args)
test-post: test-core
test: test-post
@@ -232,7 +279,6 @@ clean-post: clean-core
clean: clean-post
.PHONY: distclean-pre distclean-core distclean-post clean-extension
-distclean-extension: # this name is reserved for use by teaish.make
distclean-pre: clean
distclean-core: distclean-pre
rm -f Makefile
@@ -254,6 +300,7 @@ distclean-core: distclean-pre
@if TEAISH_TEST_TCL_IN
rm -f @TEAISH_TEST_TCL@
@endif
+distclean-extension: # this name is reserved for use by teaish.make
distclean-post: distclean-core
distclean: distclean-post
@@ -275,14 +322,16 @@ install-core: install-pre
$(INSTALL.noexec) @TEAISH_PKGINIT_TCL@ "$(DESTDIR)$(TCLLIBDIR)"
@endif
install-test: install-core
- @echo 'package require $(tx.pkgName) $(tx.version)' > $@.tcl
- @echo "Post-install test of [package require $(tx.pkgName) $(tx.version)]..."
- @if $(TCLSH) $@.tcl ; then \
- echo "test passed"; \
- rm -f $@.tcl; \
+ @echo "Post-install test of [package require $(tx.name.pkg) $(tx.version)]..."; \
+ if echo \
+ 'set c 0; ' \
+ '@TEAISH_POSTINST_PREREQUIRE@' \
+ 'if {[catch {package require $(tx.name.pkg) $(tx.version)}]} {incr c};' \
+ 'exit $$c' \
+ | $(TCLSH) ; then \
+ echo "passed"; \
else \
- echo "TEST FAILED"; \
- rm -f $@.tcl; \
+ echo "FAILED"; \
exit 1; \
fi
install-post: install-test
@@ -300,35 +349,50 @@ uninstall-post: uninstall-core
@echo "Uninstalled Tcl extension $(tx.name) $(tx.version)"
uninstall: uninstall-post
-Makefile: config.log $(teaish.makefile.in)
@if TEAISH_MAKEFILE_IN
-config.log: @TEAISH_MAKEFILE_IN@
+Makefile: $(tx.makefile.in)
+config.log: $(teaish.makefile.in)
@endif
#
# Package archive generation ("dist") rules...
#
@if TEAISH_ENABLE_DIST
+@if BIN_TAR
@if BIN_ZIP
+
+# When installing teaish as part of "make dist", we need to run
+# configure with similar flags to what we last configured with but we
+# must not pass on any extension-specific flags, as those won't be
+# recognized when running in --teaish-install mode, causing
+# the sub-configure to fail.
+dist.flags = --with-tclsh=$(TCLSH)
+dist.reconfig = $(teaish.dir)/configure $(dist.flags)
+
# Temp dir for dist.zip. Must be different than dist.tgz or else
# parallel builds may hose the dist.
teaish__dist.tmp.zip = teaish__dist_zip
#
# Make a distribution zip file...
#
-dist.zip.dir = $(tx.name)-$(tx.version)
-dist.zip = $(dist.zip.dir).zip
+dist.basename = $(tx.name.dist)-$(tx.version)
+dist.zip = $(dist.basename).zip
.PHONY: dist.zip dist.zip-core dist.zip-post
#dist.zip-pre:
-# We apparently can't add a pre-hook here, even if dist.zip-pre is
-# .PHONY, else "make dist" rebuilds the archive each time it's run.
+# We apparently can't add a pre-hook here, else "make dist" rebuilds
+# the archive each time it's run.
$(dist.zip): $(tx.dist.files)
@rm -fr $(teaish__dist.tmp.zip)
- @mkdir -p $(teaish__dist.tmp.zip)/$(dist.zip.dir)
+ @mkdir -p $(teaish__dist.tmp.zip)/$(dist.basename)
@tar cf $(teaish__dist.tmp.zip)/tmp.tar $(tx.dist.files)
- @tar xf $(teaish__dist.tmp.zip)/tmp.tar -C $(teaish__dist.tmp.zip)/$(dist.zip.dir)
- @rm -f $(dist.zip.dir)/tmp.tar $(dist.zip)
- @cd $(teaish__dist.tmp.zip) && zip -q -r ../$(dist.zip) $(dist.zip.dir)
+ @tar xf $(teaish__dist.tmp.zip)/tmp.tar -C $(teaish__dist.tmp.zip)/$(dist.basename)
+@if TEAISH_DIST_FULL
+ @$(dist.reconfig) \
+ --teaish-install=$(teaish__dist.tmp.zip)/$(dist.basename) \
+ --t-e-d=$(teaish__dist.tmp.zip)/$(dist.basename) >/dev/null
+@endif
+ @rm -f $(dist.basename)/tmp.tar $(dist.zip)
+ @cd $(teaish__dist.tmp.zip) && zip -q -r ../$(dist.zip) $(dist.basename)
@rm -fr $(teaish__dist.tmp.zip)
@ls -la $(dist.zip)
dist.zip-core: $(dist.zip)
@@ -340,23 +404,27 @@ undist-zip:
undist: undist-zip
@endif #BIN_ZIP
-@if BIN_TAR
#
# Make a distribution tarball...
#
teaish__dist.tmp.tgz = teaish__dist_tgz
-dist.tgz.dir = $(tx.name)-$(tx.version)
-dist.tgz = $(dist.tgz.dir).tar.gz
+dist.tgz = $(dist.basename).tar.gz
.PHONY: dist.tgz dist.tgz-core dist.tgz-post
# dist.tgz-pre:
# see notes in dist.zip
$(dist.tgz): $(tx.dist.files)
@rm -fr $(teaish__dist.tmp.tgz)
- @mkdir -p $(teaish__dist.tmp.tgz)/$(dist.tgz.dir)
+ @mkdir -p $(teaish__dist.tmp.tgz)/$(dist.basename)
@tar cf $(teaish__dist.tmp.tgz)/tmp.tar $(tx.dist.files)
- @tar xf $(teaish__dist.tmp.tgz)/tmp.tar -C $(teaish__dist.tmp.tgz)/$(dist.tgz.dir)
- @rm -f $(dist.tgz.dir)/tmp.tar $(dist.tgz)
- @cd $(teaish__dist.tmp.tgz) && tar czf ../$(dist.tgz) $(dist.tgz.dir)
+ @tar xf $(teaish__dist.tmp.tgz)/tmp.tar -C $(teaish__dist.tmp.tgz)/$(dist.basename)
+@if TEAISH_DIST_FULL
+ @rm -f $(teaish__dist.tmp.tgz)/$(dist.basename)/pkgIndex.tcl.in; # kludge
+ @$(dist.reconfig) \
+ --teaish-install=$(teaish__dist.tmp.tgz)/$(dist.basename) \
+ --t-e-d=$(teaish__dist.tmp.zip)/$(dist.basename) >/dev/null
+@endif
+ @rm -f $(dist.basename)/tmp.tar $(dist.tgz)
+ @cd $(teaish__dist.tmp.tgz) && tar czf ../$(dist.tgz) $(dist.basename)
@rm -fr $(teaish__dist.tmp.tgz)
@ls -la $(dist.tgz)
dist.tgz-core: $(dist.tgz)
@@ -366,16 +434,34 @@ dist: dist.tgz
undist-tgz:
rm -f $(dist.tgz)
undist: undist-tgz
+@else #!BIN_TAR
+dist:
+ @echo "The dist rules require tar, which configure did not find." 1>&2; exit 1
@endif #BIN_TAR
-@else
+@else #!TEAISH_ENABLE_DIST
undist:
dist:
@if TEAISH_OUT_OF_EXT_TREE
@echo "'dist' can only be used from an extension's home dir" 1>&2; \
- echo "In this case: @TEAISH_DIR@" 1>&2; exit 1
+ echo "In this case: @TEAISH_EXT_DIR@" 1>&2; exit 1
@endif
@endif #TEAISH_ENABLE_DIST
+Makefile: @TEAISH_TCL@
+
+@if TEAISH_MAKEFILE_CODE
+#
+# TEAISH_MAKEFILE_CODE may contain literal makefile code, which
+# gets pasted verbatim here. Either [define TEAISH_MAKEFILE_CODE
+# ...] or use [teaish-make-add] to incrementally build up this
+# content.
+#
+#
+@TEAISH_MAKEFILE_CODE@
+#
+@endif
+
+@if TEAISH_MAKEFILE
#
# TEAISH_MAKEFILE[_IN] defines any extension-specific state this file
# needs.
@@ -386,7 +472,7 @@ dist:
# - tx.src = list of the extension's source files, being sure to
# prefix each with $(tx.dir) (if it's in the same dir as the
# extension) so that out-of-tree builds can find them. Optionally,
-# [define] TEAISH_SRC or pass them to [teaish-src-add].
+# [define] TEAISH_EXT_SRC or pass them to [teaish-src-add].
#
# It may optionally set the following vars:
#
@@ -399,31 +485,12 @@ dist:
# It may optionally hook into various targets as documented in
# /doc/extensions.md in the canonical teaish source tree.
#
-# Interestingly, we don't have to pre-filter teaish.makefile.in -
-# we can just import it into here. That skips its teaish-specific
-# validation though. Hmm.
+# Interestingly, we don't have to pre-filter teaish.makefile.in - we
+# can just @include it here. That skips its teaish-specific validation
+# though. Hmm.
#
-#@if TEAISH_MAKEFILE_IN
-## TEAISH_MAKEFILE_IN ==>
-#Makefile: @TEAISH_MAKEFILE_IN@
-#@include @TEAISH_MAKEFILE_IN@
-#@endif
-#@if !TEAISH_MAKEFILE_IN
-@if TEAISH_MAKEFILE
-# TEAISH_MAKEFILE ==>
+#
Makefile: @TEAISH_MAKEFILE@
@include @TEAISH_MAKEFILE@
-@endif #TEAISH_MAKEFILE
-#@endif #!TEAISH_MAKEFILE_IN
-
-#
-# TEAISH_MAKEFILE_CODE may contain literal makefile code, which
-# gets pasted verbatim here. Either [define TEAISH_MAKEFILE_CODE
-# ...] or use [teaish-make-add] to incrementally build up this
-# content.
-#
-@if TEAISH_MAKEFILE_CODE
-# TEAISH_MAKEFILE_CODE ==>
-Makefile: @TEAISH_TCL@
-@TEAISH_MAKEFILE_CODE@
-@endif #TEAISH_MAKEFILE_CODE
+#
+@endif
diff --git a/autoconf/tea/auto.def b/autoconf/tea/auto.def
index 861257cce3..7170b3d1fe 100644
--- a/autoconf/tea/auto.def
+++ b/autoconf/tea/auto.def
@@ -1,7 +1,8 @@
#/do/not/tclsh
# ^^^ help out editors which guess this file's content type.
#
-# Main configure script entry point for the "TEA-via-autosetup"
-# framework.
+# Main configure script entry point for the TEA(ish) framework. All
+# extension-specific customization goes in teaish.tcl.in or
+# teaish.tcl.
use teaish/core
teaish-configure-core
diff --git a/autoconf/tea/autosetup/core.tcl b/autoconf/tea/autosetup/core.tcl
index 4c9aee4d66..4b3eb9a824 100644
--- a/autoconf/tea/autosetup/core.tcl
+++ b/autoconf/tea/autosetup/core.tcl
@@ -23,23 +23,44 @@
use proj
-define TEAISH_CORE_VERSION 0.1-beta
-
#
# API-internal settings and shared state.
array set teaish__Config [proj-strip-hash-comments {
+ #
+ # Teaish's version number, not to be confused with
+ # teaish__PkgInfo(-version).
+ #
+ version 0.1-beta
+
# set to 1 to enable some internal debugging output
debug-enabled 0
+
#
# 0 = don't yet have extension's pkgindex
- # 0x01 = teaish__find_extension found TEAISH_DIR/pkgIndex.tcl
- # 0x02 = teaish__find_extension found srcdir/pkgIndex.tcl.in
- # 0x04 = teaish__find_extension found TEAISH_DIR/pkgIndex.tcl (static file)
- # 0x10 = teaish-pragma was called: behave as if 0x04
+ # 0x01 = found TEAISH_EXT_DIR/pkgIndex.tcl.in
+ # 0x02 = found srcdir/pkgIndex.tcl.in
+ # 0x10 = found TEAISH_EXT_DIR/pkgIndex.tcl (static file)
+ # 0x20 = static-pkgIndex.tcl pragma: behave as if 0x10
+ #
+ # Reminder: it's significant that the bottom 4 bits be
+ # cases where teaish manages ./pkgIndex.tcl.
#
- # This might no longer be needed.
pkgindex-policy 0
+ #
+ # The pkginit counterpart of pkgindex-policy:
+ #
+ # 0 = no pkginit
+ # 0x01 = found default X.in: generate X from X.in
+ # 0x10 = found static pkginit file X
+ # 0x02 = user-provided X.in generates ./X.
+ # 0x20 = user-provided static pkginit file X
+ #
+ # The 0x0f bits indicate that teaish is responsible for cleaning up
+ # the (generated) pkginit file.
+ #
+ pkginit-policy 0
+
#
# If 1+ then teaish__verbose will emit messages.
#
@@ -47,15 +68,21 @@ array set teaish__Config [proj-strip-hash-comments {
#
# Mapping of pkginfo -flags to their TEAISH_xxx define (if any).
+ # This must not be modified.
#
pkginfo-f2d {
- -name TEAISH_NAME
- -pkgName TEAISH_PKGNAME
- -libDir TEAISH_LIBDIR_NAME
- -loadPrefix TEAISH_LOAD_PREFIX
- -version TEAISH_VERSION
- -vsatisfies TEAISH_VSATISFIES_TCL
- -options {}
+ -name TEAISH_NAME
+ -name.dist TEAISH_DIST_NAME
+ -name.pkg TEAISH_PKGNAME
+ -version TEAISH_VERSION
+ -libDir TEAISH_LIBDIR_NAME
+ -loadPrefix TEAISH_LOAD_PREFIX
+ -vsatisfies TEAISH_VSATISFIES
+ -pkgInit.tcl TEAISH_PKGINIT_TCL
+ -pkgInit.tcl.in TEAISH_PKGINIT_TCL_IN
+ -url TEAISH_URL
+ -options {}
+ -pragmas {}
}
#
@@ -64,9 +91,46 @@ array set teaish__Config [proj-strip-hash-comments {
queued-checks-pre {}
queued-checks-post {}
+ # Whether or not "make dist" parts are enabled. They get enabled
+ # when building from an extension's dir, disabled when building
+ # elsewhere.
+ dist-enabled 1
+
+ # By default we enable compilation of a native extension but if the
+ # extension has no native code or the user wants to take that over
+ # via teaish.make.in or provide a script-only extension, we will
+ # elide the default compilation rules if this is 0.
+ dll-enabled 1
+
+ # Files to include in the "make dist" bundle.
+ dist-files {}
+
+ # List of source files for the extension.
+ extension-src {}
+
+ # Path to the teaish.tcl file.
+ teaish.tcl {}
+
+ # Dir where teaish.tcl is found.
+ extension-dir {}
+
+ # Whether the generates TEASH_VSATISFIES_CODE should error out on a
+ # satisfies error. If 0, it uses return instead of error.
+ vsatisfies-error 1
+
+ # Whether or not to allow a "full dist" - a "make dist" build which
+ # includes both the extension and teaish. By default this is only on
+ # if the extension dir is teaish's dir.
+ dist-full-enabled 0
}]
set teaish__Config(core-dir) $::autosetup(libdir)/teaish
+#
+# Array of info managed by teaish-pkginfo-get and friends. Has the
+# same set of keys as $teaish__Config(pkginfo-f2d).
+#
+array set teaish__PkgInfo {}
+
#
# Runs {*}$args if $lvl is <= the current verbosity level, else it has
# no side effects.
@@ -103,9 +167,9 @@ if {[teaish-argv-has --teaish-verbose --t-v]} {
msg-quiet use system ; # Outputs "Host System" and "Build System" lines
if {"--help" ni $::argv} {
- teaish__verbose 1 msg-result "TEA(ish) Version = [get-define TEAISH_CORE_VERSION]"
- teaish__verbose 1 msg-result "Source dir = $::autosetup(srcdir)"
- teaish__verbose 1 msg-result "Build dir = $::autosetup(builddir)"
+ teaish__verbose 1 msg-result "TEA(ish) Version = $::teaish__Config(version)"
+ teaish__verbose 1 msg-result "Source dir = $::autosetup(srcdir)"
+ teaish__verbose 1 msg-result "Build dir = $::autosetup(builddir)"
}
#
@@ -117,9 +181,13 @@ if {"--help" ni $::argv} {
proc teaish-configure-core {} {
proj-tweak-default-env-dirs
+ set ::teaish__Config(install-mode) [teaish-argv-has --teaish-install*]
+ set ::teaish__Config(create-ext-mode) \
+ [teaish-argv-has --teaish-create-extension=* --t-c-e=*]
set gotExt 0; # True if an extension config is found
- if {![teaish-argv-has --teaish-create-extension=* --t-c-e=*]} {
- # Don't look for an extension if we're in --t-c-e mode
+ if {!$::teaish__Config(create-ext-mode)
+ && !$::teaish__Config(install-mode)} {
+ # Don't look for an extension if we're in --t-c-e or --t-i mode
set gotExt [teaish__find_extension]
}
@@ -142,8 +210,8 @@ proc teaish-configure-core {} {
# TEA has --with-tclinclude but it appears to only be useful for
# building an extension against an uninstalled copy of TCL's own
- # source tree. Either we get that info from tclConfig.sh or we
- # give up.
+ # source tree. The policy here is that either we get that info
+ # from tclConfig.sh or we give up.
#
# with-tclinclude:DIR
# => {Specify the directory which contains the tcl.h. This should not
@@ -153,70 +221,105 @@ proc teaish-configure-core {} {
# extensions, and thus use a teaish-... prefix on most flags. However,
# --teaish-extension-dir is frequently needed, so...
#
- # As of this spontaneous moment, we'll formalize using using
- # --t-X-Y to abbreviate teaish flags when doing so is
+ # As of this spontaneous moment, we'll settle on using --t-A-X to
+ # abbreviate --teaish-A...-X... flags when doing so is
# unambiguous...
ted: t-e-d:
teaish-extension-dir:DIR
- => {Looks for an extension in the given directory instead of the current dir.}
+ => {Looks for an extension in the given directory instead of the current
+ dir.}
t-c-e:
teaish-create-extension:TARGET_DIRECTORY
=> {Writes stub files for creating an extension. Will refuse to overwrite
- existing files without --force.}
+ existing files without --teaish-force.}
t-f
teaish-force
- => {Has a context-dependent meaning (autosetup defines --force for its own use)}
+ => {Has a context-dependent meaning (autosetup defines --force for its
+ own use).}
t-d-d
- teaish-dump-defines => {Dump all configure-defined vars to config.defines.txt}
+ teaish-dump-defines
+ => {Dump all configure-defined vars to config.defines.txt}
- t-v
- teaish-verbose=0
+ t-v:=0
+ teaish-verbose:=0
=> {Enable more (often extraneous) messages from the teaish core.}
t-d
- teaish-debug => {Enable teaish-specific debug output}
+ teaish-debug=0 => {Enable teaish-specific debug output}
+
+ t-i
+ teaish-install:=auto
+ => {Installs a copy of teaish, including autosetup, to the target dir.
+ When used with --teaish-create-extension=DIR, a value of "auto"
+ (no no value) will inherit that directory.}
+
+ #TODO: --teaish-install-extension:=dir as short for
+ # --t-c-e=dir --t-i
+
+ t-e-p:
+ teaish-extension-pkginfo:pkginfo
+ => {For use with --teaish-create-extension. If used, it must be a
+ list of arguments for use with teaish-pkginfo-set, e.g.
+ --teaish-extension-pkginfo="-name Foo -version 2.3"}
+
+ t-v-c
+ teaish-vsatisfies-check=1
+ => {Disable the configure-time "vsatisfies" check on the target tclsh.}
+
}]; # main options.
if {$gotExt} {
- set ttcl [get-define TEAISH_TCL]
- proj-assert {[file exists $ttcl]} "Expecting to have found teaish.tcl by now"
- uplevel 1 [list source $ttcl]
proj-assert {"" ne [teaish-pkginfo-get -name]}
- unset ttcl
+ proj-assert {[file exists $::teaish__Config(teaish.tcl)]} \
+ "Expecting to have found teaish.tcl by now"
+ uplevel 1 {source $::teaish__Config(teaish.tcl)}
# Set up some default values if the extension did not set them.
- # This must happen _after_ it's sourced.
+ # This must happen _after_ it's sourced but before
+ # teaish-configure is called.
+ array set f2d $::teaish__Config(pkginfo-f2d)
foreach {pflag key type val} {
- TEAISH_CFLAGS -v ""
- - TEAISH_DIST_FILES -v ""
- TEAISH_LDFLAGS -v ""
- TEAISH_MAKEFILE -v ""
- TEAISH_MAKEFILE_CODE -v ""
- TEAISH_MAKEFILE_IN -v ""
- TEAISH_PKGINDEX_TCL -v ""
- TEAISH_PKGINDEX_TCL_IN -v ""
- - TEAISH_PKGINIT_TCL -v ""
- - TEAISH_PKGINIT_TCL_IN -v ""
- - TEAISH_SRC -v ""
- TEAISH_TEST_TCL -v ""
- TEAISH_TEST_TCL_IN -v ""
- -version TEAISH_VERSION -v 0.0.0
- -pkgName TEAISH_PKGNAME -e {teaish-pkginfo-get -name}
- -libDir TEAISH_LIBDIR_NAME -e {join [list \
- [teaish-pkginfo-get -pkgName] \
- [teaish-pkginfo-get -version]]}
- -loadPrefix TEAISH_LOAD_PREFIX -e {string totitle [get-define TEAISH_PKGNAME ""]}
- -vsatisfies TEAISH_VSATISFIES_TCL -v 8.5-
- } {
- set isDefOnly [expr {"-" eq $pflag}]
- if {!$isDefOnly && [info exists ::teaish__Config($pflag)]} {
- continue;
+ -version :f2d: -v 0.0.0
+ -name.pkg :f2d: -e {teaish-pkginfo-get -name}
+ -name.dist :f2d: -e {teaish-pkginfo-get -name}
+ -libDir :f2d: -e {
+ join [list \
+ [teaish-pkginfo-get -name.pkg] \
+ [teaish-pkginfo-get -version]] ""
}
+ -loadPrefix :f2d: -e {
+ string totitle [teaish-get -name.pkg]
+ }
+ -vsatisfies :f2d: -v {{Tcl 8.5-}}
+ -pkgInit.tcl :f2d: -v ""
+ -pkgInit.tcl.in :f2d: -v ""
+ -url :f2d: -v ""
+ } {
+ set isPIFlag [expr {"-" ne $pflag}]
+ if {$isPIFlag} {
+ if {[info exists ::teaish__PkgInfo($pflag)]} {
+ # Was already set - skip it.
+ continue;
+ }
+ proj-assert {{:f2d:} eq $key}
+ set key $f2d($pflag)
+ }
+ proj-assert {"" ne $key}
set got [get-define $key ""]
- if {$isDefOnly && "" ne $got} {
+ if {"" ne $got} {
+ # Was already set - skip it.
continue
}
switch -exact -- $type {
@@ -224,18 +327,19 @@ proc teaish-configure-core {} {
-e { set val [eval $val] }
default { proj-error "Invalid type flag: $type" }
}
- #puts "***** defining default $pflag $key {$val} isDefOnly=$isDefOnly got=$got"
+ #puts "***** defining default $pflag $key {$val} isPIFlag=$isPIFlag got=$got"
define $key $val
- if {!$isDefOnly} {
- set ::teaish__Config($pflag) $val
+ if {$isPIFlag} {
+ set ::teaish__PkgInfo($pflag) $val
}
}
- unset key type val
+ unset isPIFlag pflag key type val
+ array unset f2d
}; # sourcing extension's teaish.tcl
if {[llength [info proc teaish-options]] > 0} {
# Add options defined by teaish-options, which is assumed to be
- # imported via TEAISH_TCL.
+ # imported via [teaish-get -teaish-tcl].
set o [teaish-options]
if {"" ne $o} {
options-add $o
@@ -257,20 +361,33 @@ proc teaish-configure-core {} {
t-d-d => teaish-dump-defines
ted => teaish-extension-dir
t-e-d => teaish-extension-dir
+ t-e-p => teaish-extension-pkginfo
t-f => teaish-force
+ t-i => teaish-install
t-v => teaish-verbose
+ t-v-c => teaish-vsatisfies-check
}
- set ::teaish__Config(verbose) [opt-bool teaish-verbose]
+ scan [opt-val teaish-verbose 0] %d ::teaish__Config(verbose)
set ::teaish__Config(debug-enabled) [opt-bool teaish-debug]
+ set exitEarly 0
if {[proj-opt-was-provided teaish-create-extension]} {
teaish__create_extension [opt-val teaish-create-extension]
+ incr exitEarly
+ }
+ if {$::teaish__Config(install-mode)} {
+ teaish__install
+ incr exitEarly
+ }
+
+ if {$exitEarly} {
+ file delete -force config.log
return
}
proj-assert {1==$gotExt} "Else we cannot have gotten this far"
- teaish__configure-phase1
+ teaish__configure_phase1
}
@@ -280,7 +397,7 @@ proc teaish-configure-core {} {
#
proc teaish-debug {msg} {
if {$::teaish__Config(debug-enabled)} {
- puts stderr [proj-bold "** DEBUG: \[[proj-current-scope 1]\]: $msg"]
+ puts stderr [proj-bold "** DEBUG: \[[proj-scope 1]\]: $msg"]
}
}
@@ -288,7 +405,7 @@ proc teaish-debug {msg} {
# Runs "phase 1" of the configuration, immediately after processing
# --flags. This is what will import the client-defined teaish.tcl.
#
-proc teaish__configure-phase1 {} {
+proc teaish__configure_phase1 {} {
msg-result \
[join [list "Configuring build of Tcl extension" \
[proj-bold [teaish-pkginfo-get -name] \
@@ -337,51 +454,64 @@ proc teaish__configure-phase1 {} {
}
}}; # --[exec-]prefix defaults
teaish__check_common_bins
-
#
# Set up library file names
#
proj-file-extensions
- apply {{} {
- set name [teaish-pkginfo-get -name]; # _not_ -pkgName
- set pkgver [teaish-pkginfo-get -version]
- set libname "lib"
- if {[string match *-cygwin [get-define host]]} {
- set libname cyg
- }
- define TEAISH_DLL8_BASENAME $libname$name$pkgver
- define TEAISH_DLL9_BASENAME ${libname}tcl9$name$pkgver
- set ext [get-define TARGET_DLLEXT]
- define TEAISH_DLL8 [get-define TEAISH_DLL8_BASENAME]$ext
- define TEAISH_DLL9 [get-define TEAISH_DLL9_BASENAME]$ext
- }}
+ teaish__define_pkginfo_derived *
teaish-checks-run -pre
if {[llength [info proc teaish-configure]] > 0} {
# teaish-configure is assumed to be imported via
- # TEAISH_TCL
+ # teaish.tcl
teaish-configure
}
teaish-checks-run -post
- if {0} {
- # Reminder: we cannot do a TEAISH_VSATISFIES_TCL check like the following
- # from here because _this_ tcl instance is very possibly not the one
- # which will be hosting the extension.
- if {$::autosetup(istcl)} {
- # ^^^ this is a canonical Tcl, not JimTcl
- set vsat [get-define TEAISH_VSATISFIES_TCL ""]
- if {$vsat ne ""
- && ![package vsatisfies [package provide Tcl] $vsat]} {
- error [join [list "Tcl package vsatisfies failed for" \
- [teaish-pkginfo-get -name] \
- [teaish-pkginfo-get -version] \
- ": expecting vsatisfies to match ($vsat)"]]
+ apply {{} {
+ # Set up "vsatisfies" code for pkgIndex.tcl.in,
+ # teaish.tester.tcl.in, and for a configure-time check. We would
+ # like to put this before [teaish-checks-run -pre] but it's
+ # marginally conceivable that a client may need to dynamically
+ # calculate the vsatisfies and set it via [teaish-configure].
+ set vs [get-define TEAISH_VSATISFIES ""]
+ if {"" eq $vs} return
+ set code {}
+ set n 0
+ # Treat $vs as a list-of-lists {{Tcl 8.5-} {Foo 1.0- -3.0} ...}
+ # and generate Tcl which will run package vsatisfies tests with
+ # that info.
+ foreach pv $vs {
+ set n [llength $pv]
+ if {$n < 2} {
+ proj-error "-vsatisfies: {$pv} appears malformed. Whole list is: $vs"
}
- unset vsat
- }
+ set pkg [lindex $pv 0]
+ set vcheck {}
+ for {set i 1} {$i < $n} {incr i} {
+ lappend vcheck [lindex $pv $i]
+ }
+ if {[opt-bool teaish-vsatisfies-check]} {
+ set tclsh [get-define TCLSH_CMD]
+ set vsat "package vsatisfies \[ package provide $pkg \] $vcheck"
+ set vputs "puts \[ $vsat \]"
+ #puts "*** vputs = $vputs"
+ scan [exec echo $vputs | $tclsh] %d vvcheck
+ if {0 == $vvcheck} {
+ proj-fatal -up $tclsh "check failed:" $vsat
+ }
+ }
+ lappend code [string trim [subst -nocommands -nobackslashes {
+if { ![package vsatisfies [package provide $pkg] $vcheck] } {
+ if {$::teaish__Config(vsatisfies-error)} {
+ error {Package $::teaish__PkgInfo(-name) $::teaish__PkgInfo(-version) requires $pv}
+ } else {
+ return
}
-
+}}]]
+ }; # foreach pv
+ define TEAISH_VSATISFIES_CODE [join $code "\n"]
+ }}; # vsatisfies
if {[proj-looks-like-windows]} {
# Without this, linking of an extension will not work on Cygwin or
@@ -391,7 +521,7 @@ proc teaish__configure-phase1 {} {
}
#define AS_LIBDIR $::autosetup(libdir)
- define TEAISH_MODULE_TEST_TCL $::teaish__Config(core-dir)/tester.tcl
+ define TEAISH_TESTUTIL_TCL $::teaish__Config(core-dir)/tester.tcl
apply {{} {
#
@@ -407,32 +537,72 @@ proc teaish__configure-phase1 {} {
proj-assert {$tpi ne ""} \
"TEAISH_PKGINDEX_TCL should have been set up by now"
teaish__verbose 1 msg-result "Using pkgIndex from $tpi"
+ if {0x0f & $::teaish__Config(pkgindex-policy)} {
+ # Don't leave stale pkgIndex.tcl laying around yet don't delete
+ # or overwrite a user-managed static pkgIndex.tcl.
+ file delete -force -- [get-define TEAISH_PKGINDEX_TCL]
+ proj-dot-ins-append [get-define TEAISH_PKGINDEX_TCL_IN]
+ } else {
+ teaish-dist-add [file tail $tpi]
+ }
}}; # $::teaish__Config(pkgindex-policy)
- set dEx $::teaish__Config(teaish-dir)
- set dSrc $::autosetup(srcdir)
+ #
+ # Ensure we clean up TEAISH_PKGINIT_TCL if needed and @-process
+ # TEAISH_PKGINIT_TCL_IN if needed.
+ #
+ if {0x0f & $::teaish__Config(pkginit-policy)} {
+ file delete -force -- [get-define TEAISH_PKGINIT_TCL]
+ proj-dot-ins-append [get-define TEAISH_PKGINIT_TCL_IN]
+ }
- proj-dot-ins-append $dSrc/Makefile.in
- proj-dot-ins-append $dSrc/teaish.tester.tcl.in
+ apply {{} {
+ # Queue up any remaining dot-in files
+ set dotIns [list]
+ foreach d {
+ TEAISH_TESTER_TCL_IN
+ TEAISH_TEST_TCL_IN
+ TEAISH_MAKEFILE_IN
+ } {
+ lappend dotIns [get-define $d ""]
+ }
+ lappend dotIns $::autosetup(srcdir)/Makefile.in; # must be after TEAISH_MAKEFILE_IN
+ foreach f $dotIns {
+ if {"" ne $f} {
+ proj-dot-ins-append $f
+ }
+ }
+ }}
- define TEAISH_ENABLE_DIST [expr {![get-define TEAISH_OUT_OF_EXT_TREE]}]
- define TEAISH_AUTOSETUP_DIR $::teaish__Config(core-dir)
+ define TEAISH_DIST_FULL \
+ [expr {
+ $::teaish__Config(dist-enabled)
+ && $::teaish__Config(dist-full-enabled)
+ }]
+
+ define TEAISH_AUTOSETUP_DIR $::teaish__Config(core-dir)
+ define TEAISH_ENABLE_DIST $::teaish__Config(dist-enabled)
+ define TEAISH_ENABLE_DLL $::teaish__Config(dll-enabled)
+ define TEAISH_TCL $::teaish__Config(teaish.tcl)
+
+ define TEAISH_DIST_FILES [join $::teaish__Config(dist-files)]
+ define TEAISH_EXT_DIR [join $::teaish__Config(extension-dir)]
+ define TEAISH_EXT_SRC [join $::teaish__Config(extension-src)]
proj-setup-autoreconfig TEAISH_AUTORECONFIG
foreach f {
TEAISH_CFLAGS
TEAISH_LDFLAGS
- TEAISH_SRC
- TEAISH_DIST_FILES
} {
+ # Ensure that any of these lists are flattened
define $f [join [get-define $f]]
}
- proj-remap-autoconf-dir-vars
define TEAISH__DEFINES_MAP \
[teaish__dump_defs_to_list]; # injected into teaish.tester.tcl
+ proj-remap-autoconf-dir-vars
proj-dot-ins-process -validate; # do not [define] after this point
proj-if-opt-truthy teaish-dump-defines {
make-config-header config.defines.txt \
- -none {TEAISH__* TEAISH_MAKEFILE_CODE} \
+ -none {TEAISH__* TEAISH_*_CODE} \
-str {
BIN_* CC LD AR INSTALL LDFLAG* CFLAGS* *_LDFLAGS *_CFLAGS
} \
@@ -453,7 +623,7 @@ proc teaish__configure-phase1 {} {
# the configure process.
#
#proj-file-write $::autosetup(builddir)/.configured ""
-}
+}; # teaish__configure_phase1
#
# Run checks for required binaries.
@@ -501,65 +671,72 @@ proc teaish__check_tcl {} {
set srcdir $::autosetup(srcdir)
msg-result "Checking for a suitable tcl... "
set use_tcl 1
- set with_tclsh [opt-val with-tclsh [proj-get-env TCLSH]]
- set with_tcl [opt-val with-tcl [proj-get-env TCL_HOME]]
+ set withSh [opt-val with-tclsh [proj-get-env TCLSH]]
+ set tclHome [opt-val with-tcl [proj-get-env TCL_HOME]]
+ if {[string match */lib $tclHome]} {
+ # TEA compatibility kludge: its --with-tcl wants the lib
+ # dir containing tclConfig.sh.
+ #proj-warn "Replacing --with-tcl=$tclHome for TEA compatibility"
+ regsub {/lib^} $tclHome "" tclHome
+ msg-result "NOTE: stripped /lib suffix from --with-tcl=$tclHome (a TEA-ism)"
+ }
if {0} {
# This misinteracts with the $TCL_PREFIX default: it will use the
# autosetup-defined --prefix default
- if {"prefix" eq $with_tcl} {
- set with_tcl [get-define prefix]
+ if {"prefix" eq $tclHome} {
+ set tclHome [get-define prefix]
}
}
teaish-debug "use_tcl ${use_tcl}"
- teaish-debug "with_tclsh=${with_tclsh}"
- teaish-debug "with_tcl=$with_tcl"
- if {"" eq $with_tclsh && "" eq $with_tcl} {
+ teaish-debug "withSh=${withSh}"
+ teaish-debug "tclHome=$tclHome"
+ if {"" eq $withSh && "" eq $tclHome} {
# If neither --with-tclsh nor --with-tcl are provided, try to find
# a workable tclsh.
- set with_tclsh [proj-first-bin-of tclsh9.1 tclsh9.0 tclsh8.6 tclsh]
- teaish-debug "with_tclsh=${with_tclsh}"
+ set withSh [proj-first-bin-of tclsh9.1 tclsh9.0 tclsh8.6 tclsh]
+ teaish-debug "withSh=${withSh}"
}
set doConfigLookup 1 ; # set to 0 to test the tclConfig.sh-not-found cases
- if {"" ne $with_tclsh} {
+ if {"" ne $withSh} {
# --with-tclsh was provided or found above. Validate it and use it
# to trump any value passed via --with-tcl=DIR.
- if {![file-isexec $with_tclsh]} {
- proj-error "TCL shell $with_tclsh is not executable"
+ if {![file-isexec $withSh]} {
+ proj-error "TCL shell $withSh is not executable"
} else {
- define TCLSH_CMD $with_tclsh
- #msg-result "Using tclsh: $with_tclsh"
+ define TCLSH_CMD $withSh
+ #msg-result "Using tclsh: $withSh"
}
if {$doConfigLookup &&
- [catch {exec $with_tclsh $::autosetup(libdir)/find_tclconfig.tcl} result] == 0} {
- set with_tcl $result
+ [catch {exec $withSh $::autosetup(libdir)/find_tclconfig.tcl} result] == 0} {
+ set tclHome $result
}
- if {"" ne $with_tcl && [file isdirectory $with_tcl]} {
- teaish__verbose 1 msg-result "$with_tclsh recommends the tclConfig.sh from $with_tcl"
+ if {"" ne $tclHome && [file isdirectory $tclHome]} {
+ teaish__verbose 1 msg-result "$withSh recommends the tclConfig.sh from $tclHome"
} else {
- proj-warn "$with_tclsh is unable to recommend a tclConfig.sh"
+ proj-warn "$withSh is unable to recommend a tclConfig.sh"
set use_tcl 0
}
}
set cfg ""
- set tclSubdirs {tcl9.1 tcl9.0 tcl8.6 lib}
+ set tclSubdirs {tcl9.1 tcl9.0 tcl8.6 tcl8.5 lib}
while {$use_tcl} {
- if {"" ne $with_tcl} {
- # Ensure that we can find tclConfig.sh under ${with_tcl}/...
+ if {"" ne $tclHome} {
+ # Ensure that we can find tclConfig.sh under ${tclHome}/...
if {$doConfigLookup} {
- if {[file readable "${with_tcl}/tclConfig.sh"]} {
- set cfg "${with_tcl}/tclConfig.sh"
+ if {[file readable "${tclHome}/tclConfig.sh"]} {
+ set cfg "${tclHome}/tclConfig.sh"
} else {
foreach i $tclSubdirs {
- if {[file readable "${with_tcl}/$i/tclConfig.sh"]} {
- set cfg "${with_tcl}/$i/tclConfig.sh"
+ if {[file readable "${tclHome}/$i/tclConfig.sh"]} {
+ set cfg "${tclHome}/$i/tclConfig.sh"
break
}
}
}
}
if {"" eq $cfg} {
- proj-error "No tclConfig.sh found under ${with_tcl}"
+ proj-error "No tclConfig.sh found under ${tclHome}"
}
} else {
# If we have not yet found a tclConfig.sh file, look in $libdir
@@ -583,14 +760,14 @@ proc teaish__check_tcl {} {
}
teaish__verbose 1 msg-result "Using tclConfig.sh = $cfg"
break
- }
+ }; # while {$use_tcl}
define TCL_CONFIG_SH $cfg
# Export a subset of tclConfig.sh to the current TCL-space. If $cfg
# is an empty string, this emits empty-string entries for the
# various options we're interested in.
proj-tclConfig-sh-to-autosetup $cfg
- if {"" eq $with_tclsh && $cfg ne ""} {
+ if {"" eq $withSh && $cfg ne ""} {
# We have tclConfig.sh but no tclsh. Attempt to locate a tclsh
# based on info from tclConfig.sh.
set tclExecPrefix [get-define TCL_EXEC_PREFIX]
@@ -600,23 +777,23 @@ proc teaish__check_tcl {} {
$tclExecPrefix/bin/tclsh ]
foreach trySh $tryThese {
if {[file-isexec $trySh]} {
- set with_tclsh $trySh
+ set withSh $trySh
break
}
}
- if {![file-isexec $with_tclsh]} {
+ if {![file-isexec $withSh]} {
proj-warn "Cannot find a usable tclsh (tried: $tryThese)"
}
}
- define TCLSH_CMD $with_tclsh
+ define TCLSH_CMD $withSh
if {$use_tcl} {
# Set up the TCLLIBDIR
set tcllibdir [get-env TCLLIBDIR ""]
set extDirName [get-define TEAISH_LIBDIR_NAME]
if {"" eq $tcllibdir} {
# Attempt to extract TCLLIBDIR from TCL's $auto_path
- if {"" ne $with_tclsh &&
- [catch {exec echo "puts stdout \$auto_path" | "$with_tclsh"} result] == 0} {
+ if {"" ne $withSh &&
+ [catch {exec echo "puts stdout \$auto_path" | "$withSh"} result] == 0} {
foreach i $result {
if {[file isdirectory $i]} {
set tcllibdir $i/$extDirName
@@ -630,8 +807,8 @@ proc teaish__check_tcl {} {
define TCLLIBDIR $tcllibdir
}; # find TCLLIBDIR
- if {[file-isexec $with_tclsh]} {
- teaish__verbose 1 msg-result "Using tclsh = $with_tclsh"
+ if {[file-isexec $withSh]} {
+ teaish__verbose 1 msg-result "Using tclsh = $withSh"
if {$cfg ne ""} {
define HAVE_TCL 1
} else {
@@ -643,19 +820,71 @@ proc teaish__check_tcl {} {
# fatally, else just emit a warning. If we can find the APIs needed
# to generate a working JimTCL then that will suffice for build-time
# TCL purposes (see: proc sqlite-determine-codegen-tcl).
- if {![file-isexec $with_tclsh]} {
+ if {![file-isexec $withSh]} {
proj-error "Did not find tclsh"
} elseif {"" eq $cfg} {
proj-indented-notice -error {
Cannot find a usable tclConfig.sh file. Use
- --with-tcl=DIR to specify a directory where tclConfig.sh can be
+ --with-tcl=DIR to specify a directory near which tclConfig.sh can be
found, or --with-tclsh=/path/to/tclsh to allow the tclsh binary
to locate its tclConfig.sh.
}
}
- msg-result "Using Tcl [get-define TCL_VERSION]."
+ msg-result "Using Tcl [get-define TCL_VERSION] from [get-define TCL_PREFIX]."
+ teaish__tcl_platform_quirks
}; # teaish__check_tcl
+#
+# Perform last-minute platform-specific tweaks to account for quirks.
+#
+proc teaish__tcl_platform_quirks {} {
+ define TEAISH_POSTINST_PREREQUIRE ""
+ switch -glob -- [get-define host] {
+ *-haiku {
+ # Haiku's default TCLLIBDIR is "all wrong": it points to a
+ # read-only virtual filesystem mount-point. We bend it back to
+ # fit under $TCL_PACKAGE_PATH here.
+ foreach {k d} {
+ vj TCL_MAJOR_VERSION
+ vn TCL_MINOR_VERSION
+ pp TCL_PACKAGE_PATH
+ ld TCLLIBDIR
+ } {
+ set $k [get-define $d]
+ }
+ if {[string match /packages/* $ld]} {
+ set old $ld
+ set tail [file tail $ld]
+ if {8 == $vj} {
+ set ld "${pp}/tcl${vj}.${vn}/${tail}"
+ } else {
+ proj-assert {9 == $vj}
+ set ld "${pp}/${tail}"
+ }
+ define TCLLIBDIR $ld
+ # [load foo.so], without a directory part, does not work via
+ # automated tests on Haiku (but works when run
+ # manually). Similarly, the post-install [package require ...]
+ # test fails, presumably for a similar reason. We work around
+ # the former in teaish.tester.tcl.in. We work around the
+ # latter by amending the post-install check's ::auto_path (in
+ # Makefile.in). This code MUST NOT contain any single-quotes.
+ define TEAISH_POSTINST_PREREQUIRE \
+ [join [list set ::auto_path \
+ \[ linsert \$::auto_path 0 $ld \] \; \
+ ]]
+ proj-indented-notice [subst -nocommands -nobackslashes {
+ Haiku users take note: patching target installation dir to match
+ Tcl's home because Haiku's is not writable.
+
+ Original : $old
+ Substitute: $ld
+ }]
+ }
+ }
+ }
+}; # teaish__tcl_platform_quirks
+
#
# Searches $::argv and/or the build dir and/or the source dir for
# teaish.tcl and friends. Fails if it cannot find teaish.tcl or if
@@ -663,16 +892,16 @@ proc teaish__check_tcl {} {
# not find an extension but the --help flag was seen, in which case
# that's not an error.
#
-# This does not _load_ the extension, it simply locates the files
-# which make up an extension.
-#
-# This sets up lots of defines, e.g. TEAISH_DIR.
+# This does not _load_ the extension, it primarily locates the files
+# which make up an extension and fills out no small amount of teaish
+# state related to that.
#
proc teaish__find_extension {} {
-
+ proj-assert {!$::teaish__Config(install-mode)}
teaish__verbose 1 msg-result "Looking for teaish extension..."
+
# Helper for the foreach loop below.
- set lambdaMT {{mustHave fid dir} {
+ set checkTeaishTcl {{mustHave fid dir} {
if {[file isdirectory $dir]} {
set f [file join $dir $fid]
if {[file readable $f]} {
@@ -685,12 +914,12 @@ proc teaish__find_extension {} {
}
return ""
}}
+
#
# We have to handle some flags manually because the extension must
# be loaded before [options] is run (so that the extension can
# inject its own options).
#
- #set extM ""; # teaish.make.in
set dirBld $::autosetup(builddir); # dir we're configuring under
set dirSrc $::autosetup(srcdir); # where teaish's configure script lives
set extT ""; # teaish.tcl
@@ -709,12 +938,12 @@ proc teaish__find_extension {} {
if {![file isdirectory $extD]} {
proj-error "--teaish-extension-dir value is not a directory: $extD"
}
- set extT [apply $lambdaMT 1 teaish.tcl $extD]
- define TEAISH_DIR $extD
- set ::teaish__Config(teaish-dir) $extD
+ set extT [apply $checkTeaishTcl 1 teaish.tcl $extD]
+ set ::teaish__Config(extension-dir) $extD
}
--help {
incr gotHelpArg
+ lappend largv $arg
}
default {
lappend largv $arg
@@ -723,9 +952,7 @@ proc teaish__find_extension {} {
}
set ::argv $largv
- set dirExt [proj-coalesce \
- [get-define TEAISH_DIR ""] \
- $dirBld]; # dir with the extension
+ set dirExt $::teaish__Config(extension-dir); # dir with the extension
#
# teaish.tcl is a TCL script which implements various
# interfaces described by this framework.
@@ -733,14 +960,13 @@ proc teaish__find_extension {} {
# We use the first one we find in the builddir or srcdir.
#
if {"" eq $extT} {
- set flist [list $dirExt/teaish.tcl]
- if {$dirExt ne $dirSrc} {
- lappend flist $dirSrc/teaish.tcl
- }
- if {![proj-first-file-found $flist extT]} {
+ set flist [list]
+ proj-assert {$dirExt eq ""}
+ lappend flist $dirBld/teaish.tcl $dirSrc/teaish.tcl
+ if {![proj-first-file-found extT $flist]} {
if {$gotHelpArg} {
# Tell teaish-configure-core that the lack of extension is not
- # an error when --help is used.
+ # an error when --help or --teaish-install is used.
return 0;
}
proj-indented-notice -error "
@@ -753,24 +979,22 @@ If you are attempting an out-of-tree build, use
if {![file readable $extT]} {
proj-error "extension tcl file is not readable: $extT"
}
- define TEAISH_TCL $extT
+ set ::teaish__Config(teaish.tcl) $extT
+ set dirExt [file dirname $extT]
+
+ set ::teaish__Config(extension-dir) $dirExt
+ set ::teaish__Config(blddir-is-extdir) [expr {$dirBld eq $dirExt}]
+ set ::teaish__Config(dist-enabled) $::teaish__Config(blddir-is-extdir); # may change later
+ set ::teaish__Config(dist-full-enabled) \
+ [expr {[file-normalize $::autosetup(srcdir)]
+ eq [file-normalize $::teaish__Config(extension-dir)]}]
- if {"" eq $dirExt} {
- # If this wasn't set via --teaish-extension-dir then derive it from
- # $extT.
- #puts "extT=$extT dirExt=$dirExt"
- set dirExt [file dirname $extT]
- }
- define TEAISH_DIR $dirExt
- set ::teaish__Config(teaish-dir) $dirExt
- set ::teaish__Config(blddir-is-extdir) \
- [define TEAISH_ENABLE_DIST [expr {$dirBld eq $dirExt}]]
set addDist {{file} {
teaish-dist-add [file tail $file]
}}
apply $addDist $extT
- teaish__verbose 1 msg-result "Extension dir = [get-define TEAISH_DIR]"
+ teaish__verbose 1 msg-result "Extension dir = [teaish-get -dir]"
teaish__verbose 1 msg-result "Extension config = $extT"
teaish-pkginfo-set -name [file tail [file dirname $extT]]
@@ -782,81 +1006,86 @@ If you are attempting an out-of-tree build, use
# We use the first one of teaish.make.in or teaish.make we find in
# $dirExt.
#
- if {[proj-first-file-found \
- [list $dirExt/teaish.make.in $dirExt/teaish.make] \
- extM]} {
+ if {[proj-first-file-found extM \
+ [list \
+ $dirExt/teaish.make.in \
+ $dirExt/teaish.make \
+ ]]} {
if {[string match *.in $extM]} {
define TEAISH_MAKEFILE_IN $extM
define TEAISH_MAKEFILE [file rootname [file tail $extM]]
- proj-dot-ins-append $extM [get-define TEAISH_MAKEFILE]
} else {
define TEAISH_MAKEFILE_IN ""
define TEAISH_MAKEFILE $extM
}
apply $addDist $extM
- teaish__verbose 1 msg-result "Extension makefile = $extM"
+ teaish__verbose 1 msg-result "Extension makefile = $extM"
} else {
define TEAISH_MAKEFILE_IN ""
define TEAISH_MAKEFILE ""
}
# Look for teaish.pkginit.tcl[.in]
- if {[proj-first-file-found \
- [list $dirExt/teaish.pkginit.tcl.in $dirExt/teaish.pkginit.tcl] \
- extI]} {
+ set piPolicy 0
+ if {[proj-first-file-found extI \
+ [list \
+ $dirExt/teaish.pkginit.tcl.in \
+ $dirExt/teaish.pkginit.tcl \
+ ]]} {
if {[string match *.in $extI]} {
- proj-dot-ins-append $extI
+ # Generate teaish.pkginit.tcl from $extI.
define TEAISH_PKGINIT_TCL_IN $extI
- define TEAISH_PKGINIT_TCL [file tail [file rootname $extI]]
+ define TEAISH_PKGINIT_TCL [file rootname [file tail $extI]]
+ set piPolicy 0x01
} else {
+ # Assume static $extI.
define TEAISH_PKGINIT_TCL_IN ""
define TEAISH_PKGINIT_TCL $extI
+ set piPolicy 0x10
}
apply $addDist $extI
teaish__verbose 1 msg-result "Extension post-load init = $extI"
define TEAISH_PKGINIT_TCL_TAIL \
[file tail [get-define TEAISH_PKGINIT_TCL]]; # for use in pkgIndex.tcl.in
}
+ set ::teaish__Config(pkginit-policy) $piPolicy
# Look for pkgIndex.tcl[.in]...
set piPolicy 0
- if {[proj-first-file-found $dirExt/pkgIndex.tcl.in extPI]} {
- # Generate ./pkgIndex.tcl from it.
+ if {[proj-first-file-found extPI $dirExt/pkgIndex.tcl.in]} {
+ # Generate ./pkgIndex.tcl from $extPI.
define TEAISH_PKGINDEX_TCL_IN $extPI
define TEAISH_PKGINDEX_TCL [file rootname [file tail $extPI]]
- proj-dot-ins-append $extPI
- file delete -force -- [get-define TEAISH_PKGINDEX_TCL]
apply $addDist $extPI
set piPolicy 0x01
} elseif {$dirExt ne $dirSrc
- && [proj-first-file-found $dirSrc/pkgIndex.tcl.in extPI]} {
- # Generate ./pkgIndex.tcl from it.
+ && [proj-first-file-found extPI $dirSrc/pkgIndex.tcl.in]} {
+ # Generate ./pkgIndex.tcl from $extPI.
define TEAISH_PKGINDEX_TCL_IN $extPI
define TEAISH_PKGINDEX_TCL [file rootname [file tail $extPI]]
- proj-dot-ins-append $extPI
- file delete -force -- [get-define TEAISH_PKGINDEX_TCL]
set piPolicy 0x02
- } elseif {[proj-first-file-found $dirExt/pkgIndex.tcl extPI]} {
- # Assume it's a static file and use it.
+ } elseif {[proj-first-file-found extPI $dirExt/pkgIndex.tcl]} {
+ # Assume $extPI's a static file and use it.
define TEAISH_PKGINDEX_TCL_IN ""
define TEAISH_PKGINDEX_TCL $extPI
apply $addDist $extPI
- set piPolicy 0x04
+ set piPolicy 0x10
}
-
+ # Reminder: we have to delay removal of stale TEAISH_PKGINDEX_TCL
+ # and the proj-dot-ins-append of TEAISH_PKGINDEX_TCL_IN until much
+ # later in the process.
set ::teaish__Config(pkgindex-policy) $piPolicy
# Look for teaish.test.tcl[.in]
proj-assert {"" ne $dirExt}
set flist [list $dirExt/teaish.test.tcl.in $dirExt/teaish.test.tcl]
- if {[proj-first-file-found $flist ttt]} {
+ if {[proj-first-file-found ttt $flist]} {
if {[string match *.in $ttt]} {
# Generate teaish.test.tcl from $ttt
set xt [file rootname [file tail $ttt]]
file delete -force -- $xt; # ensure no stale copy is used
define TEAISH_TEST_TCL $xt
define TEAISH_TEST_TCL_IN $ttt
- proj-dot-ins-append $ttt $xt
} else {
define TEAISH_TEST_TCL $ttt
define TEAISH_TEST_TCL_IN ""
@@ -869,31 +1098,33 @@ If you are attempting an out-of-tree build, use
# Look for teaish.tester.tcl[.in]
set flist [list $dirExt/teaish.tester.tcl.in $dirSrc/teaish.tester.tcl.in]
- if {[proj-first-file-found $flist ttt]} {
+ if {[proj-first-file-found ttt $flist]} {
# Generate teaish.test.tcl from $ttt
set xt [file rootname [file tail $ttt]]
file delete -force -- $xt; # ensure no stale copy is used
define TEAISH_TESTER_TCL $xt
define TEAISH_TESTER_TCL_IN $ttt
- proj-dot-ins-append $ttt $xt
if {[lindex $flist 0] eq $ttt} {
apply $addDist $ttt
}
+ unset ttt xt
} else {
- set ttt [file join $dirSrc teaish.tester.tcl.in]
- set xt [file rootname [file tail $ttt]]
- proj-dot-ins-append $ttt $xt
- define TEAISH_TESTER_TCL $xt
- define TEAISH_TESTER_TCL_IN $ttt
+ if {[file exists [set ttt [file join $dirSrc teaish.tester.tcl.in]]]} {
+ set xt [file rootname [file tail $ttt]]
+ define TEAISH_TESTER_TCL $xt
+ define TEAISH_TESTER_TCL_IN $ttt
+ } else {
+ define TEAISH_TESTER_TCL ""
+ define TEAISH_TESTER_TCL_IN ""
+ }
}
- unset flist xt ttt
+ unset flist
# TEAISH_OUT_OF_EXT_TREE = 1 if we're building from a dir other
# than the extension's home dir.
- set dteaish [file-normalize [get-define TEAISH_DIR]]
define TEAISH_OUT_OF_EXT_TREE \
- [expr {[file-normalize $::autosetup(builddir)] ne $dteaish}]
-
+ [expr {[file-normalize $::autosetup(builddir)] ne \
+ [file-normalize $::teaish__Config(extension-dir)]}]
return 1
}; # teaish__find_extension
@@ -907,14 +1138,73 @@ proc teaish-cflags-add {args} {
}
#
-# @teaish-define-to-cflag defineName...
+# @teaish-define-to-cflag ?flags? defineName...|{defineName...}
#
# Uses [proj-define-to-cflag] to expand a list of [define] keys, each
# one a separate argument, to CFLAGS-style -D... form then appends
# that to the current TEAISH_CFLAGS.
#
+# It accepts these flags from proj-define-to-cflag: -quote,
+# -zero-undef. It does _not_ support its -list flag.
+#
+# It accepts its non-flag argument(s) in 2 forms: (1) each arg is a
+# single [define] key or (2) its one arg is a list of such keys.
+#
+# TODO: document teaish's well-defined (as it were) defines for this
+# purpose. At a bare minimum:
+#
+# - TEAISH_NAME
+# - TEAISH_PKGNAME
+# - TEAISH_VERSION
+# - TEAISH_LIBDIR_NAME
+# - TEAISH_LOAD_PREFIX
+# - TEAISH_URL
+#
proc teaish-define-to-cflag {args} {
- teaish-cflags-add [proj-define-to-cflag {*}$args]
+ set flags {}
+ while {[string match -* [lindex $args 0]]} {
+ set arg [lindex $args 0]
+ switch -exact -- $arg {
+ -quote -
+ -zero-undef {
+ lappend flags $arg
+ set args [lassign $args -]
+ }
+ default break
+ }
+ }
+ if {1 == [llength $args]} {
+ set args [list {*}[lindex $args 0]]
+ }
+ #puts "***** flags=$flags args=$args"
+ teaish-cflags-add [proj-define-to-cflag {*}$flags {*}$args]
+}
+
+#
+# @teaish-cflags-for-tea ?...CFLAGS?
+#
+# Adds several -DPACKAGE_... CFLAGS using the extension's metadata,
+# all as quoted strings. Those symbolic names are commonly used in
+# TEA-based builds, and this function is intended to simplify porting
+# of such builds. The -D... flags added are:
+#
+# -DPACKAGE_VERSION=...
+# -DPACKAGE_NAME=...
+# -DPACKAGE_URL=...
+# -DPACKAGE_STRING=...
+#
+# Any arguments are passed-on as-is to teaish-cflags-add.
+#
+proc teaish-cflags-for-tea {args} {
+ set name $::teaish__PkgInfo(-name)
+ set version $::teaish__PkgInfo(-version)
+ set pstr [join [list $name $version]]
+ teaish-cflags-add \
+ {*}$args \
+ '-DPACKAGE_VERSION="$version"' \
+ '-DPACKAGE_NAME="$name"' \
+ '-DPACKAGE_STRING="$pstr"' \
+ '-DPACKAGE_URL="[teaish-get -url]"'
}
#
@@ -945,53 +1235,59 @@ proc teaish-ldflags-prepend {args} {
#
# @teaish-src-add ?-dist? ?-dir? src-files...
#
-# Appends all non-empty $args to TEAISH_SRC.
+# Appends all non-empty $args to the project's list of C/C++ source or
+# (in some cases) object files.
#
# If passed -dist then it also passes each filename, as-is, to
# [teaish-dist-add].
#
-# If passed -dir then each src-file has the TEAISH_DIR prepended to
-# it for before they're added to TEAISH_SRC. As often as not, that
-# will be the desired behavior so that out-of-tree builds can find the
+# If passed -dir then each src-file has [teaish-get -dir] prepended to
+# it before they're added to the list. As often as not, that will be
+# the desired behavior so that out-of-tree builds can find the
# sources, but there are cases where it's not desired (e.g. when using
-# a source file from outside of the extension's dir).
+# a source file from outside of the extension's dir, or when adding
+# object files (which are typically in the build tree)).
#
proc teaish-src-add {args} {
set i 0
proj-parse-simple-flags args flags {
- -dist 0 {return 1}
- -dir 0 {return 1}
+ -dist 0 {expr 1}
+ -dir 0 {expr 1}
}
if {$flags(-dist)} {
teaish-dist-add {*}$args
}
if {$flags(-dir)} {
set xargs {}
- set d [get-define TEAISH_DIR]
foreach arg $args {
if {"" ne $arg} {
- lappend xargs [file join $d $arg]
+ lappend xargs [file join $::teaish__Config(extension-dir) $arg]
}
}
set args $xargs
}
- proj-define-append TEAISH_SRC {*}$args
+ lappend ::teaish__Config(extension-src) {*}$args
}
#
# @teaish-dist-add files-or-dirs...
#
-# Equivalent to [proj-define-apend TEAISH_DIST_FILES ...].
+# Adds the given files to the list of files to include with the "make
+# dist" rules.
#
# This is a no-op when the current build is not in the extension's
# directory, as dist support is disabled in out-of-tree builds.
#
-# It is not legal to call this until TEAISH_DIR has been reliably set
-# (via teaish__find_extension).
+# It is not legal to call this until [teaish-get -dir] has been
+# reliably set (via teaish__find_extension).
#
proc teaish-dist-add {args} {
if {$::teaish__Config(blddir-is-extdir)} {
- proj-define-amend TEAISH_DIST_FILES {*}$args
+ # ^^^ reminder: we ignore $::teaish__Config(dist-enabled) here
+ # because the client might want to implement their own dist
+ # rules.
+ #proj-warn "**** args=$args"
+ lappend ::teaish__Config(dist-files) {*}$args
}
}
@@ -1002,7 +1298,7 @@ proc teaish-dist-add {args} {
#}
#
-# @teash-append-make args...
+# @teash-make-add args...
#
# Appends makefile code to the TEAISH_MAKEFILE_CODE define. Each
# arg may be any of:
@@ -1010,18 +1306,19 @@ proc teaish-dist-add {args} {
# -tab: emit a literal tab
# -nl: emit a literal newline
# -nltab: short for -nl -tab
-# -eol: emit a backslash-escaped end-of-line
-# -eoltab: short for -eol -tab
+# -bnl: emit a backslash-escaped end-of-line
+# -bnltab: short for -eol -tab
#
# Anything else is appended verbatim. This function adds no additional
# spacing between each argument nor between subsequent invocations.
-#
+# Generally speaking, a series of calls to this function need to
+# be sure to end the series with a newline.
proc teaish-make-add {args} {
set out [get-define TEAISH_MAKEFILE_CODE ""]
foreach a $args {
switch -exact -- $a {
- -eol { set a " \\\n" }
- -eoltab { set a " \\\n\t" }
+ -bnl { set a " \\\n" }
+ -bnltab { set a " \\\n\t" }
-tab { set a "\t" }
-nl { set a "\n" }
-nltab { set a "\n\t" }
@@ -1031,6 +1328,116 @@ proc teaish-make-add {args} {
define TEAISH_MAKEFILE_CODE $out
}
+# Internal helper to generate a clean/distclean rule name
+proc teaish__cleanup_rule {{tgt clean}} {
+ set x [incr ::teaish__Config(teaish__cleanup_rule-counter-${tgt})]
+ return ${tgt}-_${x}_
+}
+
+# @teaish-make-obj objfile srcfile ?...args?
+#
+# Uses teaish-make-add to inject makefile rules for $objfile from
+# $srcfile, which is assumed to be C code which uses libtcl. Unless
+# -recipe is used (see below) it invokes the compiler using the
+# makefile-defined $(CC.tcl) which, in the default Makefile.in
+# template, includes any flags needed for building against the
+# configured Tcl.
+#
+# This always terminates the resulting code with a newline.
+#
+# Any arguments after the 2nd may be flags described below or, if no
+# -recipe is provided, flags for the compiler call.
+#
+# -recipe {...}
+# Uses the trimmed value of {...} as the recipe, prefixing it with
+# a single hard-tab character.
+#
+# -deps {...}
+# List of extra files to list as dependencies of $o. Good luck
+# escaping non-trivial cases properly.
+#
+# -clean
+# Generate cleanup rules as well.
+proc teaish-make-obj {o src args} {
+ set consume 0
+ set clean 0
+ set flag ""
+ array set flags {}
+ set xargs {}
+ foreach arg $args {
+ if {$consume} {
+ set consume 0
+ set flags($flag) $arg
+ continue
+ }
+ switch -exact -- $arg {
+ -clean {incr clean}
+ -recipe -
+ -deps {
+ set flag $arg
+ incr consume
+ }
+ default {
+ lappend xargs $arg
+ }
+ }
+ }
+ teaish-make-add \
+ "# [proj-scope 1] -> [proj-scope] $o $src" -nl \
+ "$o: $src $::teaish__Config(teaish.tcl)"
+ if {[info exists flags(-deps)]} {
+ teaish-make-add " " [join $flags(-deps)]
+ }
+ teaish-make-add -nltab
+ if {[info exists flags(-recipe)]} {
+ teaish-make-add [string trim $flags(-recipe)] -nl
+ } else {
+ teaish-make-add [join [list \$(CC.tcl) -c $src {*}$xargs]] -nl
+ }
+ if {$clean} {
+ set rule [teaish__cleanup_rule]
+ teaish-make-add \
+ "clean: $rule\n$rule:\n\trm -f \"$o\"\n"
+ }
+}
+
+#
+# @teaish-make-clean ?-r? ?-dist? ...files|{...files}
+#
+# Adds makefile rules for cleaning up the given files via the "make
+# clean" or (if -dist is used) "make distclean" makefile rules. The -r
+# flag uses "rm -fr" instead of "rm -f", so be careful with that.
+#
+# The file names are taken literally as arguments to "rm", so they may
+# be shell wildcards to be resolved at cleanup-time. To clean up whole
+# directories, pass the -r flag. Each name gets quoted in
+# double-quotes, so spaces in names should not be a problem (but
+# double-quotes in names will be).
+#
+proc teaish-make-clean {args} {
+ if {1 == [llength $args]} {
+ set args [list {*}[lindex $args 0]]
+ }
+
+ set tgt clean
+ set rmflags "-f"
+ proj-parse-simple-flags args flags {
+ -dist 0 {
+ set tgt distclean
+ }
+ -r 0 {
+ set rmflags "-fr"
+ }
+ }
+ set rule [teaish__cleanup_rule $tgt]
+ teaish-make-add "# [proj-scope 1] -> [proj-scope]: [join $args]\n"
+ teaish-make-add "${rule}:\n\trm ${rmflags}"
+ foreach a $args {
+ teaish-make-add " \"$a\""
+ }
+ teaish-make-add "\n${tgt}: ${rule}\n"
+}
+
#
# @teaish-make-config-header filename
#
@@ -1045,8 +1452,7 @@ proc teaish-make-add {args} {
#
proc teaish-make-config-header {filename} {
make-config-header $filename \
- -bare {} \
- -none {HAVE_CFLAG_* LDFLAGS_* SH_*} \
+ -none {HAVE_CFLAG_* LDFLAGS_* SH_* TEAISH__* TEAISH_*_CODE} \
-auto {SIZEOF_* HAVE_* TEAISH_* TCL_*} \
-none *
proj-touch $filename; # help avoid frequent unnecessary auto-reconfig
@@ -1107,7 +1513,7 @@ proc teaish-check-cached {args} {
}
lassign $args msg script
if {"" eq $msg} {
- set msg [proj-current-scope 1]
+ set msg [proj-scope 1]
}
msg-checking "${msg} ... "
if {[teaish-feature-cache-check 1 check]} {
@@ -1229,7 +1635,7 @@ proc teaish__defs_format {type value} {
default {
proj-error \
"Unknown [project-current-scope] -type ($type) called from" \
- [proj-current-scope 1]
+ [proj-scope 1]
}
}
return $value
@@ -1266,7 +1672,7 @@ proc teaish__dump_defs_to_list {args} {
}
#
-# @teaish-pragma ...flags
+# teaish__pragma ...flags
#
# Offers a way to tweak how teaish's core behaves in some cases, in
# particular those which require changing how the core looks for an
@@ -1276,90 +1682,281 @@ proc teaish__dump_defs_to_list {args} {
# during initial loading of tclish.tcl (recall that most teaish APIs
# cannot be used until [teaish-configure] is called).
#
-# --have-own-pkgIndex.tcl [L]: Tells teaish that ./pkgIndex.tcl is
-# not a generated file, so it will not try to overwrite or delete
-# it.
+# static-pkgIndex.tcl [L]: Tells teaish that ./pkgIndex.tcl is not
+# a generated file, so it will not try to overwrite or delete
+# it. Errors out if it does not find pkgIndex.tcl in the
+# extension's dir.
+#
+# no-dist [L]: tells teaish to elide the 'make dist' recipe
+# from the generated Makefile.
+#
+# no-dll [L]: tells teaish to elide the DLL-building recipe
+# from the generated Makefile.
+#
+# no-vsatisfies-error [L]: tells teaish that failure to match the
+# -vsatisfies value should simply "return" instead of "error".
+#
+# no-tester [L]: disables automatic generation of teaish.test.tcl
+# even if a copy of teaish.tester.tcl.in is found.
+#
+# no-full-dist [L]: changes the "make dist" rules to never include
+# a copy of teaish itself. By default it will include itself only
+# if the extension lives in the same directory as teaish.
+#
+# full-dist [L]: changes the "make dist" rules to always include
+# a copy of teaish itself.
#
# Emits a warning message for unknown arguments.
#
-proc teaish-pragma {args} {
+proc teaish__pragma {args} {
foreach arg $args {
switch -exact -- $arg {
- --have-own-pkgIndex.tcl {
- set flist [list \
- [file join $::teaish__Config(teaish-dir) pkgIndex.tcl.in] \
- [file join $::teaish__Config(teaish-dir) pkgIndex.tcl]]
- if {[proj-first-file-found $flist tpi]} {
- if {[string match *.in $tpi]} {
- define TEAISH_PKGINDEX_TCL_IN $tpi
- teaish-dist-add [file tail $tpi]
- define TEAISH_PKGINDEX_TCL [file rootname [file tail $pi]]
- } else {
- define TEAISH_PKGINDEX_TCL_IN ""
- define TEAISH_PKGINDEX_TCL $tpi
- teaish-dist-add [file tail $tpi]
- }
+ static-pkgIndex.tcl {
+ set tpi [file join $::teaish__Config(extension-dir) pkgIndex.tcl]
+ if {[file exists $tpi]} {
+ define TEAISH_PKGINDEX_TCL_IN ""
+ define TEAISH_PKGINDEX_TCL $tpi
+ set ::teaish__Config(pkgindex-policy) 0x20
} else {
- proj-error "teaish-pragma $arg found no package-local pkgIndex.tcl\[.in]"
+ proj-error "$arg: found no package-local pkgIndex.tcl\[.in]"
}
- set ::teaish__Config(pkgindex-policy) 0x10
+ }
+
+ no-dist {
+ set ::teaish__Config(dist-enabled) 0
+ }
+
+ full-dist {
+ set ::teaish__Config(dist-full-enabled) 1
+ }
+
+ no-full-dist {
+ set ::teaish__Config(dist-full-enabled) 0
+ }
+
+ no-dll {
+ set ::teaish__Config(dll-enabled) 0
+ }
+
+ no-vsatisfies-error {
+ set ::teaish__Config(vsatisfies-error) 0
+ }
+
+ no-tester {
+ define TEAISH_TESTER_TCL_IN ""
+ define TEAISH_TESTER_TCL ""
}
default {
- proj-warn "Unknown [proj-current-scope] flag: $arg"
+ proj-error "Unknown flag: $arg"
}
}
-
-# --disable-dist [L]: disables the "dist" parts of the filtered
-# Makefile. May be used during initial loading of teaish.tcl.
-#
-# --disable-dist {
-# define TEAISH_ENABLE_DIST 0
-# }
}
}
#
# @teaish-pkginfo-set ...flags
#
-# The preferred way to set up the initial package state. Used like:
+# The way to set up the initial package state. Used like:
#
# teaish-pkginfo-set -name foo -version 0.1.2
#
-# Where each flag corresponds to one piece of extension package info.
+# Or:
#
-# -name TEAISH_NAME
-# -pkgName TEAISH_PKGNAME
-# -libDir TEAISH_LIBDIR_NAME
-# -loadPrefix TEAISH_LOAD_PREFIX
-# -version TEAISH_VERSION
-# -vsatisfies TEAISH_VSATISFIES_TCL
-# -options {...} optional [options-add] value
+# teaish-pkginfo-set ?-vars|-subst? {-name foo -version 0.1.2}
+#
+# The latter may be easier to write for a multi-line invocation.
+#
+# For the second call form, passing the -vars flag tells it to perform
+# a [subst] of (only) variables in the {...} part from the calling
+# scope. The -subst flag will cause it to [subst] the {...} with
+# command substitution as well (but no backslash substitution). When
+# using -subst for string concatenation, e.g. with -libDir
+# foo[get-version-number], be sure to wrap the value in braces:
+# -libDir {foo[get-version-number]}.
+#
+# Each pkginfo flag corresponds to one piece of extension package
+# info. Teaish provides usable default values for all of these flags,
+# but at least the -name and -version should be set by clients.
+# e.g. the default -name is the directory name the extension lives in,
+# which may change (e.g. when building it from a "make dist" bundle).
+#
+# The flags:
+#
+# -name theName: The extension's name. It defaults to the name of the
+# directory containing the extension. (In TEA this would be the
+# PACKAGE_NAME, not to be confused with...)
+#
+# -name.pkg pkg-provide-name: The extension's name for purposes of
+# Tcl_PkgProvide(), [package require], and friends. It defaults to
+# the `-name`, and is normally the same, but some projects (like
+# SQLite) have a different name here than they do in their
+# historical TEA PACKAGE_NAME.
+#
+# -version version: The extension's package version. Defaults to
+# 0.0.0.
+#
+# -libDir dirName: The base name of the directory into which this
+# extension should be installed. It defaults to a concatenation of
+# `-name.pkg` and `-version`.
+#
+# -loadPrefix prefix: For use as the second argument passed to
+# Tcl's `load` command in the package-loading process. It defaults
+# to title-cased `-name.pkg` because Tcl's `load` plugin system
+# expects it in that form.
+#
+# -options {...}: If provided, it must be a list compatible with
+# Autosetup's `options-add` function. These can also be set up via
+# `teaish-options`.
+#
+# -vsatisfies {{...} ...}: Expects a list-of-lists of conditions
+# for Tcl's `package vsatisfies` command: each list entry is a
+# sub-list of `{PkgName Condition...}`. Teaish inserts those
+# checks via its default pkgIndex.tcl.in and teaish.tester.tcl.in
+# templates to verify that the system's package dependencies meet
+# these requirements. The default value is `{{Tcl 8.5-}}` (recall
+# that it's a list-of-lists), as 8.5 is the minimum Tcl version
+# teaish will run on, but some extensions may require newer
+# versions or dependencies on other packages. As a special case,
+# if `-vsatisfies` is given a single token, e.g. `8.6-`, then it
+# is transformed into `{Tcl $thatToken}`, i.e. it checks the Tcl
+# version which the package is being run with. If given multiple
+# lists, each `package provides` check is run in the given
+# order. Failure to meet a `vsatisfies` condition triggers an
+# error.
+#
+# -url {...}: an optional URL for the extension.
+#
+# -pragmas {...} A list of infrequently-needed lower-level
+# directives which can influence teaish, including:
+#
+# static-pkgIndex.tcl: tells teaish that the client manages their
+# own pkgIndex.tcl, so that teaish won't try to overwrite it
+# using a template.
+#
+# no-dist: tells teaish to elide the "make dist" recipe from the
+# makefile so that the client can implement it.
+#
+# no-dll: tells teaish to elide the makefile rules which build
+# the DLL, as well as any templated test script and pkgIndex.tcl
+# references to them. The intent here is to (A) support
+# client-defined build rules for the DLL and (B) eventually
+# support script-only extensions.
+#
+# Unsupported flags or pragmas will trigger an error.
+#
+# Potential pothole: setting certain state, e.g. -version, after the
+# initial call requires recalculating of some [define]s. Any such
+# changes should be made as early as possible in teaish-configure so
+# that any later use of those [define]s gets recorded properly (not
+# with the old value). This is particularly relevant when it is not
+# possible to determine the -version or -name until teaish-configure
+# has been called, and it's updated dynamically from
+# teaish-configure. Notably:
+#
+# - If -version or -name are updated, -libDir will almost certainly
+# need to be explicitly set along with them.
+#
+# - If -name is updated, -loadPrefix probably needs to be as well.
#
proc teaish-pkginfo-set {args} {
+ set doVars 0
+ set doCommands 0
+ set xargs $args
+ set recalc {}
+ foreach arg $args {
+ switch -exact -- $arg {
+ -vars {
+ incr doVars
+ set xargs [lassign $xargs -]
+ }
+ -subst {
+ incr doVars
+ incr doCommands
+ set xargs [lassign $xargs -]
+ }
+ default {
+ break
+ }
+ }
+ }
+ set args $xargs
+ unset xargs
+ if {1 == [llength $args] && [llength [lindex $args 0]] > 1} {
+ # Transform a single {...} arg into the canonical call form
+ set a [list {*}[lindex $args 0]]
+ if {$doVars || $doCommands} {
+ set sflags -nobackslashes
+ if {!$doCommands} {
+ lappend sflags -nocommands
+ }
+ set a [uplevel 1 [list subst {*}$sflags $a]]
+ }
+ set args $a
+ }
set sentinel ""
- set f2d $::teaish__Config(pkginfo-f2d)
set flagDefs [list]
- foreach {f d} $f2d {
+ foreach {f d} $::teaish__Config(pkginfo-f2d) {
lappend flagDefs $f => $sentinel
}
proj-parse-simple-flags args flags $flagDefs
if {[llength $args]} {
- proj-error -up "Too many (or unknown) arguments to [proj-current-scope]: $args"
+ proj-error -up "Too many (or unknown) arguments to [proj-scope]: $args"
}
- foreach {f d} $f2d {
- if {$sentinel ne [set v $flags($f)]} {
- switch -exact -- $f {
- -options {
- options-add $v
- }
- default {
- define $d $v
+ foreach {f d} $::teaish__Config(pkginfo-f2d) {
+ if {$sentinel eq [set v $flags($f)]} continue
+ switch -exact -- $f {
+ -options {
+ proj-assert {"" eq $d}
+ options-add $v
+ }
+ -pragmas {
+ foreach p $v {
+ teaish__pragma $p
}
}
- set ::teaish__Config($f) $v
+ -vsatisfies {
+ if {1 == [llength $v] && 1 == [llength [lindex $v 0]]} {
+ # Transform X to {Tcl $X}
+ set v [list [join [list Tcl $v]]]
+ }
+ define $d $v
+ }
+ -pkgInit.tcl.in {
+ # Generate pkginit file X from X.in
+ set ::teaish__Config(pkginit-policy) 0x02
+ set x [file join $::teaish__Config(extension-dir) $v]
+ define TEAISH_PKGINIT_TCL_IN $x
+ set fout [file rootname [file tail $v]]
+ define TEAISH_PKGINIT_TCL $fout
+ define TEAISH_PKGINIT_TCL_TAIL $fout
+ set ::teaish__PkgInfo(-pkgInit.tcl) {}
+ teaish-dist-add $v
+ set v $x
+ }
+ -pkgInit.tcl {
+ # Static pkginit file X
+ set ::teaish__Config(pkginit-policy) 0x20
+ set x [file join $::teaish__Config(extension-dir) $v]
+ define TEAISH_PKGINIT_TCL $x
+ define TEAISH_PKGINIT_TCL_IN ""
+ define TEAISH_PKGINIT_TCL_TAIL [file tail $v]
+ set ::teaish__PkgInfo(-pkgInit.tcl.in) {}
+ teaish-dist-add $v
+ set v $x
+ }
+ default {
+ define $d $v
+ }
}
+ set ::teaish__PkgInfo($f) $v
+ if {$f in {-name -version -libDir -loadPrefix}} {
+ lappend recalc $f
+ }
+ }
+ if {"" ne $recalc} {
+ teaish__define_pkginfo_derived $recalc
}
}
@@ -1385,8 +1982,8 @@ proc teaish-pkginfo-get {args} {
0 {
# Return a list of (-flag value) pairs
lappend cases default {{
- if {[info exists ::teaish__Config($flag)]} {
- lappend rv $flag $::teaish__Config($flag)
+ if {[info exists ::teaish__PkgInfo($flag)]} {
+ lappend rv $flag $::teaish__PkgInfo($flag)
} else {
lappend rv $flag [get-define $defName]
}
@@ -1398,8 +1995,8 @@ proc teaish-pkginfo-get {args} {
if {[string match -* $arg]} {
# Return the corresponding -flag's value
lappend cases $arg {{
- if {[info exists ::teaish__Config($flag)]} {
- return $::teaish__Config($flag)
+ if {[info exists ::teaish__PkgInfo($flag)]} {
+ return $::teaish__PkgInfo($flag)
} else {
return [get-define $defName]
}
@@ -1409,8 +2006,8 @@ proc teaish-pkginfo-get {args} {
upvar $arg tgt
array set tgt {}
lappend cases default {{
- if {[info exists ::teaish__Config($flag)]} {
- set tgt($flag) $::teaish__Config($flag)
+ if {[info exists ::teaish__PkgInfo($flag)]} {
+ set tgt($flag) $::teaish__PkgInfo($flag)
} else {
set tgt($flag) [get-define $defName]
}
@@ -1419,7 +2016,7 @@ proc teaish-pkginfo-get {args} {
}
default {
- proj-error "invalid arg count from [proj-current-scope 1]"
+ proj-error "invalid arg count from [proj-scope 1]"
}
}
@@ -1429,21 +2026,31 @@ proc teaish-pkginfo-get {args} {
if {0 == $argc} { return $rv }
}
-#proc teaish-pget {flag} {
-# teaish-pkginfo-get $flag
-#}
-
-#
-# @teaish-enable-dist ?yes?
-#
-# Explicitly enables or disables the "dist" rules in the default
-# Makefile.in. This is equivalent to defining TEAISH_ENABLE_DIST
-# to $yes (which must be 0 or 1).
-#
-# By default, dist creation is enabled.
-#
-proc teaish-enable-dist {{yes 1}} {
- define TEAISH_ENABLE_DIST $yes
+# (Re)set some defines based on pkginfo state. $flags is the list of
+# pkginfo -flags which triggered this, or "*" for the initial call.
+proc teaish__define_pkginfo_derived {flags} {
+ set all [expr {{*} in $flags}]
+ if {$all || "-version" in $flags || "-name" in $flags} {
+ set name $::teaish__PkgInfo(-name) ; # _not_ -name.pkg
+ if {[info exists ::teaish__PkgInfo(-version)]} {
+ set pkgver $::teaish__PkgInfo(-version)
+ set libname "lib"
+ if {[string match *-cygwin [get-define host]]} {
+ set libname cyg
+ }
+ define TEAISH_DLL8_BASENAME $libname$name$pkgver
+ define TEAISH_DLL9_BASENAME ${libname}tcl9$name$pkgver
+ set ext [get-define TARGET_DLLEXT]
+ define TEAISH_DLL8 [get-define TEAISH_DLL8_BASENAME]$ext
+ define TEAISH_DLL9 [get-define TEAISH_DLL9_BASENAME]$ext
+ }
+ }
+ if {$all || "-libDir" in $flags} {
+ if {[info exists ::teaish__PkgInfo(-libDir)]} {
+ define TCLLIBDIR \
+ [file dirname [get-define TCLLIBDIR]]/$::teaish__PkgInfo(-libDir)
+ }
+ }
}
#
@@ -1483,6 +2090,47 @@ proc teaish-checks-run {flag} {
set ::teaish__Config(queued-checks${flag}) {}
}
+#
+# A general-purpose getter for various teaish state. Requires one
+# flag, which determines its result value. Flags marked with [L] below
+# are safe for using at load-time, before teaish-pkginfo-set is called
+#
+# -dir [L]: returns the extension's directory, which may differ from
+# the teaish core dir or the build dir.
+#
+# -teaish-home [L]: the "home" dir of teaish itself, which may
+# differ from the extension dir or build dir.
+#
+# -build-dir [L]: the build directory (typically the current working
+# -dir).
+#
+# Any of the teaish-pkginfo-get/get flags: returns the same as
+# teaish-pkginfo-get. Not safe for use until teaish-pkginfo-set has
+# been called.
+#
+# Triggers an error if passed an unknown flag.
+#
+proc teaish-get {flag} {
+ #-teaish.tcl {return $::teaish__Config(teaish.tcl)}
+ switch -exact -- $flag {
+ -dir {
+ return $::teaish__Config(extension-dir)
+ }
+ -teaish-home {
+ return $::autosetup(srcdir)
+ }
+ -build-dir {
+ return $::autosetup(builddir)
+ }
+ default {
+ if {[info exists ::teaish__PkgInfo($flag)]} {
+ return $::teaish__PkgInfo($flag)
+ }
+ }
+ }
+ proj-error "Unhandled flag: $flag"
+}
+
#
# Handles --teaish-create-extension=TARGET-DIR
#
@@ -1491,15 +2139,15 @@ proc teaish__create_extension {dir} {
if {"" eq $dir} {
proj-error "--teaish-create-extension=X requires a directory name."
}
- file mkdir $dir
+ file mkdir $dir/generic
set cwd [pwd]
#set dir [file-normalize [file join $cwd $dir]]
- msg-result "Created dir $dir"
+ teaish__verbose 1 msg-result "Created dir $dir"
cd $dir
if {!$force} {
# Ensure that we don't blindly overwrite anything
foreach f {
- teaish.c
+ generic/teaish.c
teaish.tcl
teaish.make.in
teaish.test.tcl
@@ -1514,15 +2162,35 @@ proc teaish__create_extension {dir} {
set pkgName $name
set version 0.0.1
set loadPrefix [string totitle $pkgName]
- set content "teaish-pkginfo-set \
- -name ${name} \
- -pkgName ${pkgName} \
- -version ${version} \
- -loadPrefix $loadPrefix \
- -libDir ${name}${version}
- -vsatisfies 8.5- \
- -options { foo=1 => {Disable foo} }
-
+ set content {teaish-pkginfo-set }
+ #puts "0 content=$content"
+ if {[opt-str teaish-extension-pkginfo epi]} {
+ set epi [string trim $epi]
+ if {[string match "*\n*" $epi]} {
+ set epi "{$epi}"
+ } elseif {![string match "{*}" $epi]} {
+ append content "\{" $epi "\}"
+ } else {
+ append content $epi
+ }
+ #puts "2 content=$content\nepi=$epi"
+ } else {
+ append content [subst -nocommands -nobackslashes {{
+ -name ${name}
+ -name.pkg ${pkgName}
+ -name.dist ${pkgName}
+ -version ${version}
+ -loadPrefix $loadPrefix
+ -libDir ${name}${version}
+ -vsatisfies {{Tcl 8.5-}}
+ -url {}
+ -options {}
+ -pragmas {full-dist}
+ }}]
+ #puts "3 content=$content"
+ }
+ #puts "1 content=$content"
+ append content "\n" {
#proc teaish-options {} {
# Return a list and/or use \[options-add\] to add new
# configure flags. This is called before teaish's
@@ -1531,20 +2199,25 @@ proc teaish__create_extension {dir} {
# early on may be used here. Any autosetup-related
# APIs may be used here.
#
- # Return an empty string if there are no options to add
- # or if they are added using \[options-add\].
+ # Return an empty string if there are no options to
+ # add or if they are added using \[options-add\].
+ #
+ # If there are no options to add, this proc need
+ # not be defined.
#}
+# Called by teaish once bootstrapping is complete.
+# This function is responsible for the client-specific
+# parts of the configuration process.
proc teaish-configure {} {
- set d \[get-define TEAISH_DIR\]
- teaish-src-add \$d/teaish.c
- teaish-dist-add teaish.c
+ teaish-src-add -dir -dist generic/teaish.c
+ teaish-define-to-cflag -quote TEAISH_PKGNAME TEAISH_VERSION
# TODO: your code goes here..
}
-"
+}; # $content
proj-file-write teaish.tcl $content
- msg-result "Created teaish.tcl"
+ teaish__verbose 1 msg-result "Created teaish.tcl"
set content "# Teaish test script.
# When this tcl script is invoked via 'make test' it will have loaded
@@ -1552,7 +2225,7 @@ proc teaish-configure {} {
# autosetup/teaish/tester.tcl.
"
proj-file-write teaish.test.tcl $content
- msg-result "Created teaish.test.tcl"
+ teaish__verbose 1 msg-result "Created teaish.test.tcl"
set content [subst -nocommands -nobackslashes {
#include
@@ -1566,25 +2239,130 @@ extern int DLLEXPORT ${loadPrefix}_Init(Tcl_Interp *interp){
if (Tcl_InitStubs(interp, TCL_VERSION, 0) == NULL) {
return TCL_ERROR;
}
- if (Tcl_PkgProvide(interp, "${name}", "${version}") == TCL_ERROR) {
+ if (Tcl_PkgProvide(interp, TEAISH_PKGNAME, TEAISH_VERSION) == TCL_ERROR) {
return TCL_ERROR;
}
- Tcl_CreateObjCommand(interp, "${name}", ${loadPrefix}_Cmd, NULL, NULL);
+ Tcl_CreateObjCommand(interp, TEAISH_PKGNAME, ${loadPrefix}_Cmd, NULL, NULL);
return TCL_OK;
}
}]
- proj-file-write teaish.c $content
- msg-result "Created teaish.c"
+ proj-file-write generic/teaish.c $content
+ teaish__verbose 1 msg-result "Created generic/teaish.c"
set content "# teaish makefile for the ${name} extension
-# tx.src = \$(tx.dir)/teaish.c
+# tx.src = \$(tx.dir)/generic/teaish.c
# tx.LDFLAGS =
# tx.CFLAGS =
"
proj-file-write teaish.make.in $content
- msg-result "Created teaish.make.in"
+ teaish__verbose 1 msg-result "Created teaish.make.in"
- msg-result "Created new extension $name in \[$dir]"
+ msg-result "Created new extension $name in \[$dir]."
cd $cwd
+ set ::teaish__Config(install-ext-dir) $dir
+}
+
+#
+# Internal helper for teaish__install
+#
+proc teaish__install_file {f destDir force} {
+ set dest $destDir/[file tail $f]
+ if {[file isdirectory $f]} {
+ file mkdir $dest
+ } elseif {!$force && [file exists $dest]} {
+ array set st1 [file stat $f]
+ array set st2 [file stat $dest]
+ if {($st1(mtime) == $st2(mtime))
+ && ($st1(size) == $st2(size))} {
+ if {[file tail $f] in {
+ pkgIndex.tcl.in
+ teaish.tester.tcl.in
+ }} {
+ # Assume they're the same. In the scope of the "make dist"
+ # rules, this happens legitimately when an extension with a
+ # copy of teaish installed in the same dir assumes that the
+ # pkgIndex.tcl.in and teaish.tester.tcl.in belong to the
+ # extension, whereas teaish believes they belong to teaish.
+ # So we end up with dupes of those.
+ return
+ }
+ }
+ proj-error -up "Cowardly refusing to overwrite \[$dest\]." \
+ "Use --teaish-force to enable overwriting."
+ } else {
+ # file copy -force $f $destDir; # loses +x bit
+ #
+ # JimTcl doesn't have [file attribute], so we can't use that here
+ # (in the context of an autosetup configure script).
+ exec cp -p $f $dest
+ }
+}
+
+#
+# Installs a copy of teaish, with autosetup, to $dDest, which defaults
+# to the --teaish-install=X or --teash-create-extension=X dir. Won't
+# overwrite files unless --teaish-force is used.
+#
+proc teaish__install {{dDest ""}} {
+ if {$dDest in {auto ""}} {
+ set dDest [opt-val teaish-install]
+ if {$dDest in {auto ""}} {
+ if {[info exists ::teaish__Config(install-ext-dir)]} {
+ set dDest $::teaish__Config(install-ext-dir)
+ }
+ }
+ }
+ set force [opt-bool teaish-force]
+ if {$dDest in {auto ""}} {
+ proj-error "Cannot determine installation directory."
+ } elseif {!$force && [file exists $dDest/auto.def]} {
+ proj-error \
+ "Target dir looks like it already contains teaish and/or autosetup: $dDest" \
+ "\nUse --teaish-force to overwrite it."
+ }
+
+ set dSrc $::autosetup(srcdir)
+ set dAS $::autosetup(libdir)
+ set dAST $::teaish__Config(core-dir)
+ set dASTF $dAST/feature
+ teaish__verbose 1 msg-result "Installing teaish to \[$dDest\]..."
+ if {$::teaish__Config(verbose)>1} {
+ msg-result "dSrc = $dSrc"
+ msg-result "dAS = $dAS"
+ msg-result "dAST = $dAST"
+ msg-result "dASTF = $dASTF"
+ msg-result "dDest = $dDest"
+ }
+
+ # Dest subdirs...
+ set ddAS $dDest/autosetup
+ set ddAST $ddAS/teaish
+ set ddASTF $ddAST/feature
+ foreach {srcDir destDir} [list \
+ $dAS $ddAS \
+ $dAST $ddAST \
+ $dASTF $ddASTF \
+ ] {
+ teaish__verbose 1 msg-result "Copying files to $destDir..."
+ file mkdir $destDir
+ foreach f [glob -directory $srcDir *] {
+ if {[string match {*~} $f] || [string match "#*#" [file tail $f]]} {
+ # Editor-generated backups and emacs lock files
+ continue
+ }
+ teaish__verbose 2 msg-result "\t$f"
+ teaish__install_file $f $destDir $force
+ }
+ }
+ teaish__verbose 1 msg-result "Copying files to $dDest..."
+ foreach f {
+ auto.def configure Makefile.in pkgIndex.tcl.in
+ teaish.tester.tcl.in
+ } {
+ teaish__verbose 2 msg-result "\t$f"
+ teaish__install_file $dSrc/$f $dDest $force
+ }
+ set ::teaish__Config(install-self-dir) $dDest
+ msg-result "Teaish $::teaish__Config(version) installed in \[$dDest\]."
}
diff --git a/autoconf/tea/autosetup/feature-tests.tcl b/autoconf/tea/autosetup/feature-tests.tcl
index 9d77092c82..6c927d1a77 100644
--- a/autoconf/tea/autosetup/feature-tests.tcl
+++ b/autoconf/tea/autosetup/feature-tests.tcl
@@ -186,7 +186,7 @@ proc teaish-import-features {args} {
#
if {$flags(-run) && "" ne $pk} {
proj-error "Cannot use both -run and $pk" \
- " (called from [proj-current-scope 1])"
+ " (called from [proj-scope 1])"
}
foreach arg $args {
diff --git a/autoconf/tea/autosetup/tester.tcl b/autoconf/tea/autosetup/tester.tcl
index 199f64dafb..5c546e841d 100644
--- a/autoconf/tea/autosetup/tester.tcl
+++ b/autoconf/tea/autosetup/tester.tcl
@@ -29,6 +29,7 @@
# 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]
@@ -45,6 +46,7 @@ proc test-current-scope {{lvl 0}} {
# @test-msg
#
# Emits all arugments to stdout.
+#
proc test-msg {args} {
puts "$args"
}
@@ -113,12 +115,15 @@ proc test-assert {testId script {msg ""}} {
# @test-expect testId script result
#
# Runs $script in the calling scope and compares its result to
-# $result. If they differ, it triggers an [assert].
+# $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 [uplevel 1 $script]
- test__assert 1 {$x eq $result} "\nEXPECTED: <<$result>>\nGOT: <<$x>>"
+ set x [string trim [uplevel 1 $script]]
+ set result [string trim $result]
+ test__assert 1 {$x eq $result} \
+ "\nEXPECTED: <<$result>>\nGOT: <<$x>>"
}
#
@@ -142,7 +147,11 @@ if {![array exists ::teaish__BuildFlags]} {
}
#
-# @teaish-build-flag2 flag tgtVar ?dflt?
+# @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
@@ -152,15 +161,11 @@ if {![array exists ::teaish__BuildFlags]} {
# an external script have populated its internal state, which is
# normally handled via teaish.tester.tcl.in.
#
-# 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 #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-flag2 {flag tgtVar {dflt ""}} {
+proc teaish-build-flag3 {flag tgtVar {dflt ""}} {
upvar $tgtVar tgt
if {[info exists ::teaish__BuildFlags($flag)]} {
set tgt $::teaish__BuildFlags($flag)
@@ -177,12 +182,12 @@ proc teaish-build-flag2 {flag tgtVar {dflt ""}} {
#
# @teaish-build-flag flag ?dflt?
#
-# Convenience form of teaish-build-flag2 which returns the
+# 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-flag2 $flag tgt $dflt
+ teaish-build-flag3 $flag tgt $dflt
return $tgt
}
diff --git a/autoconf/tea/pkgIndex.tcl.in b/autoconf/tea/pkgIndex.tcl.in
index 6ed6bb68db..c93fcc6854 100644
--- a/autoconf/tea/pkgIndex.tcl.in
+++ b/autoconf/tea/pkgIndex.tcl.in
@@ -6,14 +6,14 @@
# edit it.
#
# Adapted from https://core.tcl-lang.org/tcltls
-@if TEAISH_VSATISFIES_TCL
-if {![package vsatisfies [package provide Tcl] @TEAISH_VSATISFIES_TCL@]} {
- error "Package @TEAISH_PKGNAME@ @TEAISH_VERSION@ requires Tcl @TEAISH_VSATISFIES_TCL@"
-}
+@if TEAISH_VSATISFIES_CODE
+@TEAISH_VSATISFIES_CODE@
@endif
if {[package vsatisfies [package provide Tcl] 9.0-]} {
package ifneeded {@TEAISH_PKGNAME@} {@TEAISH_VERSION@} [list apply {{dir} {
+@if TEAISH_ENABLE_DLL
load [file join $dir {@TEAISH_DLL9@}] @TEAISH_LOAD_PREFIX@
+@endif
@if TEAISH_PKGINIT_TCL_TAIL
set initScript [file join $dir {@TEAISH_PKGINIT_TCL_TAIL@}]
if {[file exists $initScript]} {
@@ -23,11 +23,13 @@ if {[package vsatisfies [package provide Tcl] 9.0-]} {
}} $dir]
} else {
package ifneeded {@TEAISH_PKGNAME@} {@TEAISH_VERSION@} [list apply {{dir} {
+@if TEAISH_ENABLE_DLL
if {[string tolower [file extension {@TEAISH_DLL8@}]] in [list .dll .dylib .so]} {
load [file join $dir {@TEAISH_DLL8@}] @TEAISH_LOAD_PREFIX@
} else {
load {} @TEAISH_LOAD_PREFIX@
}
+@endif
@if TEAISH_PKGINIT_TCL_TAIL
set initScript [file join $dir {@TEAISH_PKGINIT_TCL_TAIL@}]
if {[file exists $initScript]} {
diff --git a/autoconf/tea/teaish.tcl b/autoconf/tea/teaish.tcl
index eedd9bdaf4..87d059c328 100644
--- a/autoconf/tea/teaish.tcl
+++ b/autoconf/tea/teaish.tcl
@@ -1,35 +1,83 @@
-# Teaish configure script for the SQLite TCL extension
-
-apply {{} {
- set version [proj-file-content -trim [get-define TEAISH_DIR]/../VERSION]
- proj-assert {[string match 3.*.* $version]}
- teaish-pkginfo-set \
- -name sqlite \
- -pkgName sqlite3 \
- -version $version \
- -loadPrefix Sqlite3 \
- -vsatisfies 8.6- \
- -libDir sqlite$version
-}}
+# Teaish configure script for the SQLite Tcl extension
#
-# Object for communicating certain config-time state across various
-# auto.def-related pieces.
+# State for disparate config-time pieces.
#
-array set sqliteConfig [subst [proj-strip-hash-comments {
+array set sqlite__Config [proj-strip-hash-comments {
#
# The list of feature --flags which the --all flag implies. This
# requires special handling in a few places.
#
all-flag-enables {fts3 fts4 fts5 rtree geopoly}
-}]]
+
+ # >0 if building in the canonical tree. -1=undetermined
+ is-canonical -1
+}]
+
+#
+# Set up the package info for teaish...
+#
+apply {{dir} {
+ # Figure out the version number...
+ set version ""
+ if {[file exists $dir/../VERSION]} {
+ # The canonical SQLite TEA(ish) build
+ set version [proj-file-content -trim $dir/../VERSION]
+ set ::sqlite__Config(is-canonical) 1
+ set distname sqlite-tcl
+ } elseif {[file exists $dir/generic/tclsqlite3.c]} {
+ # The copy from the teaish tree, used as a dev/test bed before
+ # updating SQLite's tree.
+ set ::sqlite__Config(is-canonical) 0
+ set fd [open $dir/generic/tclsqlite3.c rb]
+ while {[gets $fd line] >=0} {
+ if {[regexp {^#define[ ]+SQLITE_VERSION[ ]+"(3.+)"} \
+ $line - version]} {
+ set distname sqlite-teaish
+ break
+ }
+ }
+ close $fd
+ }
+
+ if {"" eq $version} {
+ proj-fatal "Cannot determine the SQLite version number"
+ }
+
+ proj-assert {$::sqlite__Config(is-canonical) > -1}
+ proj-assert {[string match 3.*.* $version]} \
+ "Unexpected SQLite version: $version"
+
+ set pragmas {}
+ if {$::sqlite__Config(is-canonical)} {
+ # Disable "make dist" in the canonical tree. That tree is
+ # generated from several pieces and creating/testing working
+ # "dist" rules for that sub-build currently feels unnecessary. The
+ # copy in the teaish tree, though, should be able to "make dist".
+ lappend pragmas no-dist
+ } else {
+ lappend pragmas full-dist
+ }
+
+ teaish-pkginfo-set -vars {
+ -name sqlite
+ -name.pkg sqlite3
+ -version $version
+ -name.dist $distname
+ -vsatisfies 8.6-
+ -libDir sqlite$version
+ -pragmas $pragmas
+ }
+}} [teaish-get -dir]
#
# Must return either an empty string or a list in the form accepted by
# autosetup's [options] function.
#
proc teaish-options {} {
- return [proj-strip-hash-comments [subst -nocommands -nobackslashes {
+ # These flags and defaults mostly derive from the historical TEA
+ # build. Some, like ICU, are taken from the canonical SQLite tree.
+ return [subst -nocommands -nobackslashes {
with-system-sqlite=0
=> {Use the system-level SQLite instead of the copy in this tree.
Also requires use of --override-sqlite-version so that the build
@@ -48,7 +96,7 @@ proc teaish-options {} {
geopoly => {Enable the GEOPOLY extension}
rtree => {Enable the RTREE extension}
session => {Enable the SESSION extension}
- all=1 => {Disable $::sqliteConfig(all-flag-enables)}
+ all=1 => {Disable $::sqlite__Config(all-flag-enables)}
with-icu-ldflags:LDFLAGS
=> {Enable SQLITE_ENABLE_ICU and add the given linker flags for the
ICU libraries. e.g. on Ubuntu systems, try '-licui18n -licuuc -licudata'.}
@@ -61,7 +109,7 @@ proc teaish-options {} {
icu-collations=0
=> {Enable SQLITE_ENABLE_ICU_COLLATIONS. Requires --with-icu-ldflags=...
or --with-icu-config}
- }]]
+ }]
}
#
@@ -69,12 +117,10 @@ proc teaish-options {} {
# work needed for this extension.
#
proc teaish-configure {} {
- teaish-enable-dist 0
use teaish/feature-tests
- set srcdir [get-define TEAISH_DIR]
teaish-src-add -dist -dir generic/tclsqlite3.c
- teaish-cflags-add -I${srcdir}/..
+
if {[proj-opt-was-provided override-sqlite-version]} {
teaish-pkginfo-set -version [opt-val override-sqlite-version]
proj-warn "overriding sqlite version number:" [teaish-pkginfo-get -version]
@@ -103,6 +149,8 @@ proc teaish-configure {} {
msg-result "Using system-level sqlite3."
teaish-cflags-add -DUSE_SYSTEM_SQLITE
teaish-ldflags-add -lsqlite3
+ } elseif {$::sqlite__Config(is-canonical)} {
+ teaish-cflags-add -I[teaish-get -dir]/..
}
teaish-check-librt
@@ -116,9 +164,8 @@ proc teaish-configure {} {
sqlite-handle-common-feature-flags; # must be late in the process
}; # teaish-configure
-
define OPT_FEATURE_FLAGS {} ; # -DSQLITE_OMIT/ENABLE flags.
-########################################################################
+#
# Adds $args, if not empty, to OPT_FEATURE_FLAGS. This is intended only for holding
# -DSQLITE_ENABLE/OMIT/... flags, but that is not enforced here.
proc sqlite-add-feature-flag {args} {
@@ -127,7 +174,7 @@ proc sqlite-add-feature-flag {args} {
}
}
-########################################################################
+#
# Check for log(3) in libm and die with an error if it is not
# found. $featureName should be the feature name which requires that
# function (it's used only in error messages). defines LDFLAGS_MATH to
@@ -148,13 +195,13 @@ proc sqlite-affirm-have-math {featureName} {
}
}
-########################################################################
+#
# Handle various SQLITE_ENABLE/OMIT_... feature flags.
proc sqlite-handle-common-feature-flags {} {
msg-result "Feature flags..."
if {![opt-bool all]} {
# Special handling for --disable-all
- foreach flag $::sqliteConfig(all-flag-enables) {
+ foreach flag $::sqlite__Config(all-flag-enables) {
if {![proj-opt-was-provided $flag]} {
proj-opt-set $flag 0
}
@@ -175,7 +222,7 @@ proc sqlite-handle-common-feature-flags {} {
# The --geopoly flag, though, will automatically re-enable
# --rtree, so --disable-rtree won't actually disable anything in
# that case.
- foreach k $::sqliteConfig(all-flag-enables) {
+ foreach k $::sqlite__Config(all-flag-enables) {
if {![proj-opt-was-provided $k]} {
proj-opt-set $k 1
}
@@ -206,7 +253,7 @@ proc sqlite-handle-common-feature-flags {} {
}
}
}
- ########################################################################
+ #
# Invert the above loop's logic for some SQLITE_OMIT_... cases. If
# config option $boolFlag is false, [sqlite-add-feature-flag
# $featureFlag], where $featureFlag is intended to be
@@ -222,7 +269,7 @@ proc sqlite-handle-common-feature-flags {} {
}
}
- #########################################################################
+ ##
# Remove duplicates from the final feature flag sets and show them
# to the user.
set oFF [get-define OPT_FEATURE_FLAGS]
@@ -236,7 +283,7 @@ proc sqlite-handle-common-feature-flags {} {
teaish-cflags-add -define OPT_FEATURE_FLAGS
}; # sqlite-handle-common-feature-flags
-########################################################################
+#
# If --enable-threadsafe is set, this adds -DSQLITE_THREADSAFE=1 to
# OPT_FEATURE_FLAGS and sets LDFLAGS_PTHREAD to the linker flags
# needed for linking pthread (possibly an empty string). If
@@ -258,10 +305,8 @@ proc sqlite-handle-threadsafe {} {
teaish-ldflags-prepend $ldf
undefine lib_pthread_create
undefine lib_pthread_mutexattr_init
- } elseif {[proj-opt-was-provided threadsafe]} {
- user-error "Missing required pthread libraries. Use --disable-threadsafe to disable this check."
} else {
- msg-result "pthread support not detected"
+ user-error "Missing required pthread libraries. Use --disable-threadsafe to disable this check."
}
# Recall that LDFLAGS_PTHREAD might be empty even if pthreads if
# found because it's in -lc on some platforms.
@@ -271,30 +316,31 @@ proc sqlite-handle-threadsafe {} {
} else {
#
# If user does not specify --[disable-]threadsafe then select a
- # default based on whether it looks like TCL has threading
+ # default based on whether it looks like Tcl has threading
# support.
#
- #puts "TCL_LIBS = [get-define TCL_LIBS]"
- if {[string match *pthread* [get-define TCL_LIBS]]} {
- # ^^^ FIXME: there must be a better way of testing this
+ catch {
+ scan [exec echo {puts [tcl::pkgconfig get threaded]} | [get-define TCLSH_CMD]] \
+ %d enable
+ }
+ if {$enable} {
set flagName "--threadsafe"
set lblAbled "enabled"
- set enable 1
- msg-result "yes"
+ msg-result yes
} else {
set flagName "--disable-threadsafe"
set lblAbled "disabled"
- set enable 0
- msg-result "no"
+ msg-result no
}
- msg-result "NOTICE: defaulting to ${flagName} because TCL has threading ${lblAbled}."
- # ^^^ We don't need to link against -lpthread in the is-enabled case.
+ msg-result "Defaulting to ${flagName} because Tcl has threading ${lblAbled}."
+ # ^^^ We (probably) don't need to link against -lpthread in the
+ # is-enabled case. We might in the case of static linking. Unsure.
}
sqlite-add-feature-flag -DSQLITE_THREADSAFE=${enable}
return $enable
}
-########################################################################
+#
# Handles the --enable-load-extension flag. Returns 1 if the support
# is enabled, else 0. If support for that feature is not found, a
# fatal error is triggered if --enable-load-extension is explicitly
@@ -346,7 +392,7 @@ proc sqlite-handle-load-extension {} {
return $found
}
-########################################################################
+#
# ICU - International Components for Unicode
#
# Handles these flags:
@@ -446,7 +492,7 @@ proc sqlite-handle-icu {} {
}; # sqlite-handle-icu
-########################################################################
+#
# Handles the --with-tempstore flag.
#
# The test fixture likes to set SQLITE_TEMP_STORE on its own, so do
@@ -471,7 +517,7 @@ proc sqlite-handle-tempstore {} {
}
}
-########################################################################
+#
# Handles the --enable-math flag.
proc sqlite-handle-math {} {
proj-if-opt-truthy math {
@@ -490,18 +536,13 @@ proc sqlite-handle-math {} {
}
}
-########################################################################
+#
# Move -DSQLITE_OMIT... and -DSQLITE_ENABLE... flags from CFLAGS and
# CPPFLAGS to OPT_FEATURE_FLAGS and remove them from BUILD_CFLAGS.
proc sqlite-munge-cflags {} {
# Move CFLAGS and CPPFLAGS entries matching -DSQLITE_OMIT* and
# -DSQLITE_ENABLE* to OPT_FEATURE_FLAGS. This behavior is derived
- # from the legacy build and was missing the 3.48.0 release (the
- # initial Autosetup port).
- # https://sqlite.org/forum/forumpost/9801e54665afd728
- #
- # Handling of CPPFLAGS, as well as removing ENABLE/OMIT from
- # CFLAGS/CPPFLAGS, was missing in the 3.49.0 release as well.
+ # from the pre-3.48 build.
#
# If any configure flags for features are in conflict with
# CFLAGS/CPPFLAGS-specified feature flags, all bets are off. There
diff --git a/autoconf/tea/teaish.tester.tcl.in b/autoconf/tea/teaish.tester.tcl.in
index 315d82350b..4e203cd785 100644
--- a/autoconf/tea/teaish.tester.tcl.in
+++ b/autoconf/tea/teaish.tester.tcl.in
@@ -5,17 +5,27 @@
# probably not edit it.
#
# This is the wrapper script invoked by teaish's "make test" recipe.
-@if TEAISH_VSATISFIES_TCL
-if {![package vsatisfies [package provide Tcl] @TEAISH_VSATISFIES_TCL@]} {
- error "Package @TEAISH_PKGNAME@ @TEAISH_VERSION@ requires Tcl @TEAISH_VSATISFIES_TCL@"
-}
+# It gets passed 3 args:
+#
+# $1 = the DLL name, or "" if the extension has no DLL
+#
+# $2 = the "load prefix" for Tcl's [load] or empty if $1 is empty
+#
+# $3 = the /path/to/teaish/tester.tcl (test utility code)
+#
+@if TEAISH_VSATISFIES_CODE
+@TEAISH_VSATISFIES_CODE@
@endif
-load [lindex $::argv 0] [lindex $::argv 1];
-source [lindex $::argv 2]; # teaish/tester.tcl
+if {[llength [lindex $::argv 0]] > 0} {
+ load [file normalize [lindex $::argv 0]] [lindex $::argv 1];
+ # ----^^^^^^^ needed on Haiku when argv 0 is just a filename, else
+ # load cannot find the file.
+}
+source -encoding utf-8 [lindex $::argv 2]; # teaish/tester.tcl
@if TEAISH_PKGINIT_TCL
apply {{file} {
set dir [file dirname $::argv0]
- source $file
+ source -encoding utf-8 $file
}} [join {@TEAISH_PKGINIT_TCL@}]
@endif
@if TEAISH_TEST_TCL
@@ -24,7 +34,7 @@ apply {{file} {
array set ::teaish__BuildFlags @TEAISH__DEFINES_MAP@
set dir [file normalize [file dirname $file]]
#test-fail "Just testing"
- source $file
+ source -encoding utf-8 $file
}} [join {@TEAISH_TEST_TCL@}]
@else # TEAISH_TEST_TCL
# No $TEAISH_TEST_TCL provided, so here's a default test which simply
diff --git a/autosetup/jimsh0.c b/autosetup/jimsh0.c
index 1a6453d0c8..b035524c96 100644
--- a/autosetup/jimsh0.c
+++ b/autosetup/jimsh0.c
@@ -9132,7 +9132,7 @@ int Jim_StringEqObj(Jim_Obj *aObjPtr, Jim_Obj *bObjPtr)
const char *sA = Jim_GetString(aObjPtr, &Alen);
const char *sB = Jim_GetString(bObjPtr, &Blen);
- return Alen == Blen && memcmp(sA, sB, Alen) == 0;
+ return Alen == Blen && *sA == *sB && memcmp(sA, sB, Alen) == 0;
}
}
@@ -10242,7 +10242,7 @@ static int JimCommandsHT_KeyCompare(void *privdata, const void *key1, const void
int len1, len2;
const char *str1 = Jim_GetStringNoQualifier((Jim_Obj *)key1, &len1);
const char *str2 = Jim_GetStringNoQualifier((Jim_Obj *)key2, &len2);
- return len1 == len2 && memcmp(str1, str2, len1) == 0;
+ return len1 == len2 && *str1 == *str2 && memcmp(str1, str2, len1) == 0;
}
static void JimCommandsHT_ValDestructor(void *interp, void *val)
@@ -13864,6 +13864,13 @@ static int JimExprOpNumUnary(Jim_Interp *interp, struct JimExprNode *node)
case JIM_EXPROP_NOT:
wC = !bA;
break;
+ case JIM_EXPROP_UNARYPLUS:
+ case JIM_EXPROP_UNARYMINUS:
+ rc = JIM_ERR;
+ Jim_SetResultFormatted(interp,
+ "can't use non-numeric string as operand of \"%s\"",
+ node->type == JIM_EXPROP_UNARYPLUS ? "+" : "-");
+ break;
default:
abort();
}
@@ -19868,16 +19875,22 @@ wrongargs:
}
else if (errorCodeObj) {
int len = Jim_ListLength(interp, argv[idx + 1]);
- int i;
- ret = JIM_OK;
+ if (len > Jim_ListLength(interp, errorCodeObj)) {
- for (i = 0; i < len; i++) {
- Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
- Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
- if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
- ret = -1;
- break;
+ ret = -1;
+ }
+ else {
+ int i;
+ ret = JIM_OK;
+
+ for (i = 0; i < len; i++) {
+ Jim_Obj *matchObj = Jim_ListGetIndex(interp, argv[idx + 1], i);
+ Jim_Obj *objPtr = Jim_ListGetIndex(interp, errorCodeObj, i);
+ if (Jim_StringCompareObj(interp, matchObj, objPtr, 0) != 0) {
+ ret = -1;
+ break;
+ }
}
}
}
@@ -20253,7 +20266,7 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
}
case OPT_SET:
- return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG);
+ return Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 4, argv[argc - 1], JIM_ERRMSG | JIM_UNSHARED);
case OPT_EXISTS:{
int rc = Jim_DictKeysVector(interp, argv[2], argv + 3, argc - 3, &objPtr, JIM_NONE);
@@ -20265,7 +20278,7 @@ static int Jim_DictCoreCommand(Jim_Interp *interp, int argc, Jim_Obj *const *arg
}
case OPT_UNSET:
- if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_NONE) != JIM_OK) {
+ if (Jim_SetDictKeysVector(interp, argv[2], argv + 3, argc - 3, NULL, JIM_UNSHARED) != JIM_OK) {
return JIM_ERR;
}
return JIM_OK;
diff --git a/autosetup/proj.tcl b/autosetup/proj.tcl
index 2e272a3b52..4691cfe36f 100644
--- a/autosetup/proj.tcl
+++ b/autosetup/proj.tcl
@@ -8,7 +8,13 @@
# * May you find forgiveness for yourself and forgive others.
# * May you share freely, never taking more than you give.
#
-########################################################################
+
+#
+# ----- @module proj.tcl -----
+# @section Project-agnostic Helper APIs
+#
+
+#
# Routines for Steve Bennett's autosetup which are common to trees
# managed in and around the umbrella of the SQLite project.
#
@@ -25,13 +31,12 @@
# 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
+# - Symbols with _ separators are intended for internal use within
# this file, and are not part of the API which auto.def files should
-# rely on.
+# rely on. Symbols with - separators are public APIs.
#
# - By and large, autosetup prefers to update global state with the
# results of feature checks, e.g. whether the compiler supports flag
@@ -49,10 +54,7 @@
# 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.
-########################################################################
-
-# ----- @module proj.tcl -----
-# @section Project Helper APIs
+#
#
# $proj__Config is an internal-use-only array for storing whatever generic
@@ -83,7 +85,7 @@ set proj__Config(isatty) [isatty? stdout]
#
proc proj-warn {args} {
show-notices
- puts stderr [join [list "WARNING:" {*}$args] " "]
+ puts stderr [join [list "WARNING: \[[proj-scope 1]\]: " {*}$args] " "]
}
#
@@ -103,7 +105,7 @@ proc proj-fatal {args} {
set args [lassign $args -]
incr lvl
}
- puts stderr [join [list "FATAL: \[[proj-current-scope $lvl]]: " {*}$args]]
+ puts stderr [join [list "FATAL: \[[proj-scope $lvl]]: " {*}$args]]
exit 1
}
@@ -119,27 +121,26 @@ proc proj-error {args} {
set args [lassign $args -]
incr lvl
}
- error [join [list "\[[proj-current-scope $lvl]]:" {*}$args]]
+ error [join [list "\[[proj-scope $lvl]]:" {*}$args]]
}
#
# @proj-assert script ?message?
#
-# Kind of like a C assert: if uplevel (eval) of [expr {$script}] is
-# false, a fatal error is triggered. The error message, by default,
-# includes the body of the failed assertion, but if $msg is set then
-# that is used instead.
+# Kind of like a C assert: if uplevel of [list expr $script] is false,
+# a fatal error is triggered. The error message, by default, includes
+# the body of the failed assertion, but if $msg is set then that is
+# used instead.
#
proc proj-assert {script {msg ""}} {
if {1 == [get-env proj-assert 0]} {
msg-result [proj-bold "asserting: $script"]
}
- set x "expr \{ $script \}"
- if {![uplevel 1 $x]} {
+ if {![uplevel 1 [list expr $script]]} {
if {"" eq $msg} {
set msg $script
}
- proj-fatal "Assertion failed: $msg"
+ proj-fatal "Assertion failed in \[[proj-scope 1]\]: $msg"
}
}
@@ -212,7 +213,7 @@ proc proj-indented-notice {args} {
# Returns 1 if cross-compiling, else 0.
#
proc proj-is-cross-compiling {} {
- return [expr {[get-define host] ne [get-define build]}]
+ expr {[get-define host] ne [get-define build]}
}
#
@@ -249,7 +250,7 @@ proc proj-cflags-without-werror {{var CFLAGS}} {
default { lappend rv $f }
}
}
- return [join $rv " "]
+ join $rv " "
}
#
@@ -297,7 +298,7 @@ proc proj-search-for-header-dir {header args} {
-dirs { set args [lassign $args - dirs] }
-subdirs { set args [lassign $args - subdirs] }
default {
- proj-fatal "Unhandled argument: $args"
+ proj-error "Unhandled argument: $args"
}
}
}
@@ -375,7 +376,8 @@ 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.
+ # Note that cc-path-progs defines $u to "false" if it finds no
+ # match.
if {[cc-path-progs $b]} {
set rc [get-define $u]
}
@@ -519,7 +521,7 @@ proc proj-define-for-opt {flag def {msg ""} {iftrue 1} {iffalse 0}} {
# @proj-opt-define-bool ?-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
+# 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
@@ -529,7 +531,7 @@ proc proj-define-for-opt {flag def {msg ""} {iftrue 1} {iffalse 0}} {
proc proj-opt-define-bool {args} {
set invert 0
if {[lindex $args 0] eq "-v"} {
- set invert 1
+ incr invert
lassign $args - optName defName descr
} else {
lassign $args optName defName descr
@@ -540,14 +542,9 @@ proc proj-opt-define-bool {args} {
#puts "optName=$optName defName=$defName descr=$descr"
set rc 0
msg-checking "[join $descr] ... "
- if {[proj-opt-truthy $optName]} {
- if {0 eq $invert} {
- set rc 1
- } else {
- set rc 0
- }
- } elseif {0 ne $invert} {
- set rc 1
+ set rc [proj-opt-truthy $optName]
+ if {$invert} {
+ set rc [expr {!$rc}]
}
msg-result $rc
define $defName $rc
@@ -1341,7 +1338,7 @@ proc proj-dump-defs-json {file args} {
# that [opt-value canonical] will return X if --alias=X is passed to
# configure.
#
-# That said: autosetup's [opt-src] does support alias forms, but it
+# That said: autosetup's [opt-str] does support alias forms, but it
# requires that the caller know all possible aliases. It's simpler, in
# terms of options handling, if there's only a single canonical name
# which each down-stream call of [opt-...] has to know.
@@ -1427,11 +1424,11 @@ proc proj-which-linenoise {dotH} {
# 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
+# Each autoconf-relvant --XYZ 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 at make-time. Without this
@@ -1467,7 +1464,7 @@ proc proj-remap-autoconf-dir-vars {} {
}
# Maintenance reminder: the [join] call is to avoid {braces}
# around the output when someone passes in,
- # e.g. --libdir=\${prefix}/foo/bar. The Debian package build
+ # e.g. --libdir=\${prefix}/foo/bar. Debian's SQLite package build
# script does that.
}
}
@@ -1501,11 +1498,11 @@ proc proj-env-file {flag {dflt ""}} {
# If none of those are set, $dflt is returned.
#
proc proj-get-env {var {dflt ""}} {
- return [get-env $var [proj-env-file $var $dflt]]
+ get-env $var [proj-env-file $var $dflt]
}
#
-# @proj-current-scope ?lvl?
+# @proj-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_
@@ -1513,7 +1510,7 @@ proc proj-get-env {var {dflt ""}} {
# returned and if it would be negative then a string indicating such
# is returned (as opposed to throwing an error).
#
-proc proj-current-scope {{lvl 0}} {
+proc proj-scope {{lvl 0}} {
#uplevel [expr {$lvl + 1}] {lindex [info level 0] 0}
set ilvl [info level]
set offset [expr {$ilvl - $lvl - 1}]
@@ -1526,6 +1523,14 @@ proc proj-current-scope {{lvl 0}} {
}
}
+#
+# Deprecated name of [proj-scope].
+#
+proc proj-current-scope {{lvl 0}} {
+ puts stderr \
+ "Deprecated proj-current-scope called from [proj-scope 1]. Use proj-scope instead."
+ proj-scope [incr lvl]
+}
#
# Converts parts of tclConfig.sh to autosetup [define]s.
@@ -1547,6 +1552,7 @@ proc proj-tclConfig-sh-to-autosetup {tclConfigSh} {
TCL_VERSION
TCL_MAJOR_VERSION
TCL_MINOR_VERSION
+ TCL_PACKAGE_PATH
TCL_PATCH_LEVEL
TCL_SHLIB_SUFFIX
}
@@ -1563,22 +1569,7 @@ proc proj-tclConfig-sh-to-autosetup {tclConfigSh} {
lappend shBody "exit"
set shBody [join $shBody "\n"]
#puts "shBody=$shBody\n"; exit
- if {0} {
- # This doesn't work but would be preferable to using a temp file...
- set fd [open "| sh" "rw"]
- #puts "fd = $fd"; exit
- puts $fd $shBody
- flush $fd
- set rd [read $fd]
- close $fd
- puts "rd=$rd"; exit 1
- eval $rd
- } else {
- set shName ".tclConfigSh.tcl"
- proj-file-write $shName $shBody
- eval [exec sh $shName $tclConfigSh]
- file delete -force $shName
- }
+ eval [exec echo $shBody | sh]
}
#
@@ -1635,7 +1626,7 @@ proc proj-tweak-default-env-dirs {} {
# If $postProcessScript is not empty then, during
# [proj-dot-ins-process], it will be eval'd immediately after
# processing the file. In the context of that script, the vars
-# $fileIn and $fileOut will be set to the input and output file
+# $dotInsIn and $dotInsOut will be set to the input and output file
# names. This can be used, for example, to make the output file
# executable or perform validation on its contents.
#
@@ -1657,7 +1648,7 @@ proc proj-dot-ins-append {fileIn args} {
proj-fatal "Too many arguments: $fileIn $args"
}
}
- #puts "******* [proj-current-scope]: adding $fileIn"
+ #puts "******* [proj-scope]: adding $fileIn"
lappend ::proj__Config(dot-in-files) $fileIn
}
@@ -1701,7 +1692,7 @@ proc proj-dot-ins-process {args} {
-validate 0 {expr 1}
}
if {[llength $args] > 0} {
- error "Invalid argument to [proj-current-scope]: $args"
+ error "Invalid argument to [proj-scope]: $args"
}
foreach f $::proj__Config(dot-in-files) {
proj-assert {3==[llength $f]} \
@@ -1713,7 +1704,10 @@ proc proj-dot-ins-process {args} {
proj-validate-no-unresolved-ats $fOut
}
if {"" ne $fScript} {
- uplevel 1 "set fileIn $fIn; set fileOut $fOut; eval {$fScript}"
+ uplevel 1 [join [list set dotInsIn $fIn \; \
+ set dotInsOut $fOut \; \
+ eval \{${fScript}\} \; \
+ unset dotInsIn dotInsOut]]
}
}
if {$flags(-clear)} {
@@ -1752,13 +1746,13 @@ proc proj-validate-no-unresolved-ats {args} {
}
#
-# @proj-first-found fileList tgtVar
+# @proj-first-file-found tgtVar fileList
#
-# Searches $fileList for an existing file. If one is found, its name is
-# assigned to tgtVar and 1 is returned, else tgtVar is not modified
+# Searches $fileList for an existing file. If one is found, its name
+# is assigned to tgtVar and 1 is returned, else tgtVar is set to ""
# and 0 is returned.
#
-proc proj-first-file-found {fileList tgtVar} {
+proc proj-first-file-found {tgtVar fileList} {
upvar $tgtVar tgt
foreach f $fileList {
if {[file exists $f]} {
@@ -1766,6 +1760,7 @@ proc proj-first-file-found {fileList tgtVar} {
return 1
}
}
+ set tgt ""
return 0
}
@@ -1775,19 +1770,10 @@ proc proj-first-file-found {fileList tgtVar} {
# can be used to automatically reconfigure.
#
proc proj-setup-autoreconfig {defName} {
- set squote {{arg} {
- # Wrap $arg in single-quotes if it looks like it might need that
- # to avoid mis-handling as a shell argument. We assume that $arg
- # will never contain any single-quote characters.
- if {[string match {*[ &;$*"]*} $arg]} { return '$arg' }
- return $arg
- }}
- define-append $defName cd [apply $squote $::autosetup(builddir)] \
- && [apply $squote $::autosetup(srcdir)/configure]
- #{*}$::autosetup(argv) breaks with --flag='val with spaces', so...
- foreach arg $::autosetup(argv) {
- define-append $defName [apply $squote $arg]
- }
+ define $defName \
+ [join [list \
+ cd \"$::autosetup(builddir)\" \
+ && [get-define AUTOREMAKE "error - missing @AUTOREMAKE@"]]]
}
#
@@ -1805,21 +1791,22 @@ proc proj-define-append {defineName args} {
}
#
-# @prod-define-amend ?-p|-prepend? ?-define? FLAG args...
+# @prod-define-amend ?-p|-prepend? ?-d|-define? defineName args...
#
# A proxy for Autosetup's [define-append].
#
-# Appends all non-empty $args to the define named by $FLAG unless. If
+# Appends all non-empty $args to the define named by $defineName. If
# one of (-p | -prepend) are used it instead prepends them, in their
-# given order, to $FLAG.
+# given order, to $defineName.
#
# If -define is used then each argument is assumed to be a [define]'d
# flag and [get-define X ""] is used to fetch it.
#
-# Typically, -lXYZ flags need to be in "reverse" order, with each -lY
-# resolving symbols for -lX's to its left. This order is largely
-# historical, and not relevant on all environments, but it is
-# technically correct and still relevant on some environments.
+# Re. linker flags: typically, -lXYZ flags need to be in "reverse"
+# order, with each -lY resolving symbols for -lX's to its left. This
+# order is largely historical, and not relevant on all environments,
+# but it is technically correct and still relevant on some
+# environments.
#
# See: proj-append-to
#
@@ -1830,9 +1817,9 @@ proc proj-define-amend {args} {
set xargs [list]
foreach arg $args {
switch -exact -- $arg {
- -p - -prepend { set prepend 1 }
- -d - -define { set isdefs 1 }
"" {}
+ -p - -prepend { incr prepend }
+ -d - -define { incr isdefs }
default {
if {"" eq $defName} {
set defName $arg
@@ -1842,6 +1829,9 @@ proc proj-define-amend {args} {
}
}
}
+ if {"" eq $defName} {
+ proj-error "Missing defineName argument in call from [proj-scope 1]"
+ }
if {$isdefs} {
set args $xargs
set xargs [list]
@@ -1863,47 +1853,51 @@ proc proj-define-amend {args} {
}
#
-# @proj-define-to-cflag ?-list? defineName...
+# @proj-define-to-cflag ?-list? ?-quote? ?-zero-undef? defineName...
#
-# Treat each argument as the name of a [define]
-# and attempt to render it like a CFLAGS value:
+# Treat each argument as the name of a [define] and renders it like a
+# CFLAGS value in one of the following forms:
#
# -D$name
-# -D$name=value
+# -D$name=integer (strict integer matches only)
+# '-D$name=value' (without -quote)
+# '-D$name="value"' (with -quote)
#
-# If treats integers as numbers and everything else as a quoted
+# It treats integers as numbers and everything else as a quoted
# string, noting that it does not handle strings which themselves
# contain quotes.
#
+# The -zero-undef flag causes no -D to be emitted for integer values
+# of 0.
+#
# By default it returns the result as string of all -D... flags,
# but if passed the -list flag it will return a list of the
# individual CFLAGS.
#
proc proj-define-to-cflag {args} {
set rv {}
- set xargs {}
- set returnList 0;
- foreach arg $args {
- switch -exact -- $arg {
- -list {incr returnList}
- default {
- lappend xargs $arg
- }
- }
+ proj-parse-simple-flags args flags {
+ -list 0 {expr 1}
+ -quote 0 {expr 1}
+ -zero-undef 0 {expr 1}
}
- foreach d $xargs {
+ foreach d $args {
set v [get-define $d ""]
- set li [list -D${d}]
- if {[string is integer -strict $v]} {
- lappend li = $v
- } elseif {"" eq $d} {
+ set li {}
+ if {"" eq $d} {
+ set v "-D${d}"
+ } elseif {[string is integer -strict $v]} {
+ if {!$flags(-zero-undef) || $v ne "0"} {
+ set v "-D${d}=$v"
+ }
+ } elseif {$flags(-quote)} {
+ set v "'-D${d}=\"$v\"'"
} else {
- lappend li = {"} $v {"}
+ set v "'-D${d}=$v'"
}
- lappend rv [join $li ""]
+ lappend rv $v
}
- if {$returnList} { return $rv }
- return [join $rv]
+ expr {$flags(-list) ? $rv : [join $rv]}
}
@@ -1967,7 +1961,7 @@ array set proj__Cache {}
# Returns a cache key for the given argument:
#
# integer: relative call stack levels to get the scope name of for
-# use as a key. [proj-current-scope [expr {1 + $arg + addLevel}]] is
+# use as a key. [proj-scope [expr {1 + $arg + addLevel}]] is
# then used to generate the key. i.e. the default of 0 uses the
# calling scope's name as the key.
#
@@ -1978,7 +1972,7 @@ array set proj__Cache {}
proc proj-cache-key {{addLevel 0} arg} {
if {"-" eq $arg} {set arg 0}
if {[string is integer -strict $arg]} {
- return [proj-current-scope [expr {$arg + $addLevel + 1}]]
+ return [proj-scope [expr {$arg + $addLevel + 1}]]
}
return $arg
}
@@ -2064,7 +2058,7 @@ proc proj-coalesce {args} {
# -flag defaultValue {script}
#
# -flag => defaultValue
-# -----^--^ (wiith spaces there!)
+# -----^--^ (with spaces there!)
#
# Repeated for each flag.
#
@@ -2096,8 +2090,8 @@ proc proj-coalesce {args} {
# This function assumes that each flag is unique, and using a flag
# more than once behaves in a last-one-wins fashion.
#
-# Any $argv entries not described in $prototype are not treated
-# as flags.
+# Any argvName entries not described in $prototype are not treated as
+# flags.
#
# Returns the number of flags it processed in $argvName.
#
@@ -2113,6 +2107,10 @@ proc proj-coalesce {args} {
# After that $flags would contain {-foo 1 -bar {blah} -no-baz 2}
# and $args would be {8 9 10}.
#
+# Potential TODOs: consider using lappend instead of set so that any
+# given flag can be used more than once. Or add a syntax to indicate
+# that.
+#
proc proj-parse-simple-flags {argvName tgtArrayName prototype} {
upvar $argvName argv
upvar $tgtArrayName tgt
@@ -2121,26 +2119,28 @@ proc proj-parse-simple-flags {argvName tgtArrayName prototype} {
array set consuming {}
set n [llength $prototype]
# Figure out what our flags are...
- for {set i 0} {$i < $n} {} {
+ for {set i 0} {$i < $n} {incr i} {
set k [lindex $prototype $i]
#puts "**** #$i of $n k=$k"
proj-assert {[string match -* $k]} \
- "Invalid flag value for [proj-current-scope]: $k"
+ "Invalid flag value: $k"
set v ""
set s ""
- if {"=>" eq [lindex $prototype [expr {$i + 1}]]} {
- incr i 2
- if {$i >= $n} {
- proj-fatal "Missing argument for $k => flag"
+ switch -exact -- [lindex $prototype [expr {$i + 1}]] {
+ => {
+ incr i 2
+ if {$i >= $n} {
+ proj-error "Missing argument for $k => flag"
+ }
+ set consuming($k) 1
+ set v [lindex $prototype $i]
+ }
+ default {
+ set v [lindex $prototype [incr i]]
+ set s [lindex $prototype [incr i]]
+ set scripts($k) $s
}
- set consuming($k) 1
- set v [lindex $prototype $i]
- } else {
- set v [lindex $prototype [incr i]]
- set s [lindex $prototype [incr i]]
- set scripts($k) $s
}
- incr i
#puts "**** #$i of $n k=$k v=$v s=$s"
set dflt($k) $v
}
@@ -2160,7 +2160,7 @@ proc proj-parse-simple-flags {argvName tgtArrayName prototype} {
} elseif {[info exists tgt($arg)]} {
if {[info exists consuming($arg)]} {
if {$i + 1 >= $n} {
- proj-fatal "Missing argument for $arg flag"
+ proj-assert 0 {Cannot happen - bounds already checked}
}
set tgt($arg) [lindex $argv [incr i]]
} elseif {"" eq $scripts($arg)} {
diff --git a/autosetup/sqlite-config.tcl b/autosetup/sqlite-config.tcl
index 3f7d174d7a..1df6e233ff 100644
--- a/autosetup/sqlite-config.tcl
+++ b/autosetup/sqlite-config.tcl
@@ -538,7 +538,7 @@ proc msg-debug {msg} {
# the debug message. It is not legal to call this from the global
# scope.
proc proc-debug {msg} {
- msg-debug "\[[proj-current-scope 1]\]: $msg"
+ msg-debug "\[[proj-scope 1]\]: $msg"
}
define OPT_FEATURE_FLAGS {} ; # -DSQLITE_OMIT/ENABLE flags.
@@ -994,7 +994,7 @@ proc sqlite-handle-emsdk {} {
proj-bin-define wasm-opt BIN_WASM_OPT
}
proj-dot-ins-append $emccSh.in $emccSh {
- catch {exec chmod u+x $fileOut}
+ catch {exec chmod u+x $dotInsOut}
}
proj-dot-ins-append $extWasmConfig.in $extWasmConfig
} else {
diff --git a/doc/testrunner.md b/doc/testrunner.md
index d0248573ee..d1696e9d1d 100644
--- a/doc/testrunner.md
+++ b/doc/testrunner.md
@@ -15,6 +15,7 @@
3.1. Commands to Run SQLite Tests
3.2. Running ZipVFS Tests
3.3. Investigating Source Code Test Failures
+ 3.4. External Fuzzcheck Databases
4. Extra testrunner.tcl Options
5. Controlling CPU Core Utilization
@@ -23,47 +24,62 @@
# 1. Overview
-testrunner.tcl is a Tcl script used to run multiple SQLite tests using
-multiple jobs. It supports the following types of tests:
+The testrunner.tcl program is a Tcl script used to run multiple SQLite
+tests in parallel, thus reducing testing time on multi-core machines.
+It supports the following types of tests:
* Tcl test scripts.
+ * Fuzzcheck tests, including using an external fuzzcheck database.
+
* Tests run with `make` commands. Examples:
- - `make mdevtest`
+ - `make devtest`
- `make releasetest`
- `make sdevtest`
- `make testrunner`
-testrunner.tcl pipes the output of all tests and builds run into log file
-**testrunner.log**, created in the current working directory. Search this
-file to find details of errors. Suggested search commands:
+The testrunner.tcl program stores output of all tests and builds run in
+log file **testrunner.log**, created in the current working directory.
+Search this file to find details of errors. Suggested search commands:
* `grep "^!" testrunner.log`
* `grep failed testrunner.log`
-testrunner.tcl also populates SQLite database **testrunner.db**. This database
-contains details of all tests run, running and to be run. A useful query
-might be:
+The testrunner.tcl program also populates SQLite database **testrunner.db**.
+This database contains details of all tests run, running and to be run.
+A useful query might be:
```
SELECT * FROM script WHERE state='failed'
```
+You can get a summary of errors in a prior run by invoking commands like
+these:
+
+```
+ tclsh $(TESTDIR)/testrunner.tcl errors
+ tclsh $(TESTDIR)/testrunner.tcl errors -v
+```
+
Running the command:
```
- ./testfixture $(TESTDIR)/testrunner.tcl status
+ tclsh $(TESTDIR)/testrunner.tcl status
```
in the directory containing the testrunner.db database runs various queries
to produce a succinct report on the state of a running testrunner.tcl script.
-Running:
+A good way to keep and eye on test progress is to run either of the two
+following commands:
```
- watch ./testfixture $(TESTDIR)/testrunner.tcl status
+ watch tclsh $(TESTDIR)/testrunner.tcl status
+ tclsh $(TESTDIR)/testrunner.tcl status -d 2
```
-in another terminal is a good way to keep an eye on a long running test.
+Both of the commands above accomplish about the same thing, but the second
+one has the advantage of not requiring "watch" to be installed on your
+system.
Sometimes testrunner.tcl uses the `testfixture` binary that it is run with
to run tests (see "Binary Tests" below). Sometimes it builds testfixture and
@@ -239,9 +255,7 @@ of the specific tests run.
As with source code tests, one or more patterns
may be appended to any of the above commands (mdevtest, sdevtest or release).
-In that case only Tcl tests (no fuzz or other tests) that match the specified
-pattern are run. For example, to run the just the Tcl rtree tests in all
-builds and configurations supported by "release":
+Pattern matching is used for both Tcl tests and fuzz tests.
```
tclsh $TESTDIR/testrunner.tcl release rtree%
@@ -292,6 +306,34 @@ target to build. This may be used either to run a `make` command test directly,
or else to build a testfixture (or testfixture.exe) binary with which to
run a Tcl test script, as described above.
+
+## 3.4 External Fuzzcheck Databases
+
+Testrunner.tcl will also run fuzzcheck against an external (out of tree)
+database, for example fuzzcheck databases generated by dbsqlfuzz. To do
+this, simply add the "`--fuzzdb` *FILENAME*" command-line option or set
+the FUZZDB environment variable to the name of the external
+database. For large external databases, testrunner.tcl will automatically use
+the "`--slice`" command-line option of fuzzcheck to divide the work up into
+multiple jobs, to increase parallelism.
+
+Thus, for example, to run a full releasetest including an external
+dbsqlfuzz database, run a command like one of these:
+
+```
+ tclsh test/testrunner.tcl releasetest --fuzzdb ../fuzz/20250415.db
+ FUZZDB=../fuzz/20250415.db make releasetest
+ nmake /f Makefile.msc FUZZDB=../fuzz/20250415.db releasetest
+```
+
+The patternlist option to testrunner.tcl will match against fuzzcheck
+databases. So if you want to run *only* tests involving the external
+database, you can use a command something like this:
+
+```
+ tclsh test/testrunner.tcl releasetest 20250415 --fuzzdb ../fuzz/20250415.db
+```
+
# 4. Extra testrunner.tcl Options
@@ -315,16 +357,22 @@ would normally execute into the testrunner.log file. Example:
tclsh $TESTDIR/testrunner.tcl --dryrun mdevtest"
```
-The **--explain** option is similar to --dryrun in that it prevents testrunner.tcl
-from building any binaries or running any tests. The difference is that --explain
-prints on standard output a human-readable summary of all the builds and tests that
-would have been run.
+The **--explain** option is similar to --dryrun in that it prevents
+testrunner.tcl from building any binaries or running any tests. The
+difference is that --explain prints on standard output a human-readable
+summary of all the builds and tests that would have been run.
```
# Show what builds and tests would have been run
tclsh $TESTDIR/testrunner.tcl --explain mdevtest
```
+The **--status** option uses VT100 escape sequences to display the test
+status full-screen. This is similar to running
+"`watch test/testrunner status`" in a separate window, just more convenient.
+Unfortunately, this option does not work correctly on Windows, due to the
+sketchy implementation of VT100 escapes on the Windows console.
+
# 5. Controlling CPU Core Utilization
diff --git a/ext/fts5/test/fts5matchinfo.test b/ext/fts5/test/fts5matchinfo.test
index f81b076c18..a3bce869fb 100644
--- a/ext/fts5/test/fts5matchinfo.test
+++ b/ext/fts5/test/fts5matchinfo.test
@@ -528,9 +528,12 @@ do_execsql_test 16.0 {
BEGIN EXCLUSIVE;
}
-sqlite3 db2 test.db
do_test 16.1 {
- catchsql { SELECT * FROM t1 } db2
+ set rc [catch {
+ sqlite3 db2 test.db
+ db2 eval {SELECT * FROM t1}
+ } errmsg]
+ lappend rc $errmsg
} {1 {database is locked}}
do_execsql_test 16.2 {
@@ -542,4 +545,3 @@ do_test 16.3 {
} {0 {}}
finish_test
-
diff --git a/main.mk b/main.mk
index aaf2a2c250..ba2208275b 100644
--- a/main.mk
+++ b/main.mk
@@ -2245,119 +2245,6 @@ fuzzcheck-ubsan$(T.exe): $(FUZZCHECK_SRC) sqlite3.c sqlite3.h $(FUZZCHECK_DEP)
fuzzy: fuzzcheck-ubsan$(T.exe)
xbin: fuzzcheck-ubsan$(T.exe)
-# Usage: FUZZDB=filename make run-fuzzcheck
-#
-# Where filename is a fuzzcheck database, this target builds and runs
-# fuzzcheck, fuzzcheck-asan, and fuzzcheck-ubsan on that database.
-#
-# FUZZDB can be a glob pattern of two or more databases. Example:
-#
-# FUZZDB=test/fuzzdata*.db make run-fuzzcheck
-#
-# The original rules for this target were like this:
-#
-# run-fuzzcheck: fuzzcheck$(T.exe) fuzzcheck-asan$(T.exe) fuzzcheck-ubsan$(T.exe)
-# @if test "$(FUZZDB)" = ""; then echo 'ERROR: No FUZZDB specified. Rerun with FUZZDB=filename'; exit 1; fi
-# ./fuzzcheck$(T.exe) --spinner $(FUZZDB)
-# ./fuzzcheck-asan$(T.exe) --spinner $(FUZZDB)
-# ./fuzzcheck-ubsan$(T.exe) --spinner $(FUZZDB)
-#
-# What follows is a decomposition of these rules in a way that allows make
-# to run things in parallel when using the -jN option.
-#
-FUZZDB-check:
- @if test "$(FUZZDB)" = ""; then echo 'ERROR: No FUZZDB specified. Rerun with FUZZDB=filename'; exit 1; fi
-run-fuzzcheck: run-fuzzcheck-n0
-run-fuzzcheck-n0: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 0 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-n1
-run-fuzzcheck-n1: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 1 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-n2
-run-fuzzcheck-n2: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 2 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-n3
-run-fuzzcheck-n3: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 3 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-n4
-run-fuzzcheck-n4: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 4 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-n5
-run-fuzzcheck-n5: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 5 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-n6
-run-fuzzcheck-n6: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 6 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-n7
-run-fuzzcheck-n7: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 7 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-n8
-run-fuzzcheck-n8: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 8 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-n9
-run-fuzzcheck-n9: FUZZDB-check fuzzcheck$(T.exe)
- ./fuzzcheck$(T.exe) --slice 9 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a0
-run-fuzzcheck-a0: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 0 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a1
-run-fuzzcheck-a1: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 1 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a2
-run-fuzzcheck-a2: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 2 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a3
-run-fuzzcheck-a3: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 3 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a4
-run-fuzzcheck-a4: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 4 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a5
-run-fuzzcheck-a5: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 5 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a6
-run-fuzzcheck-a6: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 6 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a7
-run-fuzzcheck-a7: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 7 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a8
-run-fuzzcheck-a8: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 8 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-a9
-run-fuzzcheck-a9: FUZZDB-check fuzzcheck-asan$(T.exe)
- ./fuzzcheck-asan$(T.exe) --slice 9 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u0
-run-fuzzcheck-u0: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 0 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u1
-run-fuzzcheck-u1: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 1 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u2
-run-fuzzcheck-u2: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 2 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u3
-run-fuzzcheck-u3: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 3 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u4
-run-fuzzcheck-u4: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 4 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u5
-run-fuzzcheck-u5: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 5 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u6
-run-fuzzcheck-u6: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 6 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u7
-run-fuzzcheck-u7: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 7 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u8
-run-fuzzcheck-u8: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 8 10 $(FUZZDB)
-run-fuzzcheck: run-fuzzcheck-u9
-run-fuzzcheck-u9: FUZZDB-check fuzzcheck-ubsan$(T.exe)
- ./fuzzcheck-ubsan$(T.exe) --slice 9 10 $(FUZZDB)
-
ossshell$(T.exe): $(TOP)/test/ossfuzz.c $(TOP)/test/ossshell.c sqlite3.c sqlite3.h
$(T.link) -o $@ $(FUZZCHECK_OPT) $(TOP)/test/ossshell.c \
diff --git a/manifest b/manifest
index 6d9d9b1cf0..9a6de043c7 100644
--- a/manifest
+++ b/manifest
@@ -1,12 +1,12 @@
-C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\swal2\sbranch.
-D 2025-04-21T12:56:24.808
+C Merge\sthe\slatest\strunk\schanges\sinto\sthe\swal2\sbranch.
+D 2025-05-06T21:38:45.322
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md e108e1e69ae8e8a59e93c455654b8ac9356a11720d3345df2a4743e9590fb20d
F Makefile.in c3e414df4dc8dfb12f1f6baf129fcb6d18cd0ebd3c9109370fb3fceeeef9a37a
F Makefile.linux-generic bd3e3cacd369821a6241d4ea1967395c962dfe3057e38cb0a435cee0e8b789d0
-F Makefile.msc 7f78b2f5817694e7b75d1ed9480b8efbe9e7576068b8e91797e6ed99867b7846
+F Makefile.msc 3e6234d032b455f988a353e9c3e29c34f5d1feab80ea78426b7498affdc47350
F README.md e28077cfbef795e99c9c75ed95aa7257a1166709b562076441a8506ac421b7c1
F VERSION 001dea55eb8304ec9130b6b44a32d3fc349f279d45a7e224fc0730c3cb8e2372
F art/icon-243x273.gif 9750b734f82fdb3dc43127753d5e6fbf3b62c9f4e136c2fbf573b2f57ea87af5
@@ -18,24 +18,24 @@ F art/sqlite370.svg 40b7e2fe8aac3add5d56dd86ab8d427a4eca5bcb3fe4f8946cb3794e1821
F auto.def 82c32443a91c1062f7a48beec37dbb2d8d03447b1286bce8df5ebb6d8d353f8a
F autoconf/Makefile.fallback 22fe523eb36dfce31e0f6349f782eb084e86a5620b2b0b4f84a2d6133f53f5ac
F autoconf/Makefile.in 36516827bb5d2309422bbcbf53e873fa22ef179f5c25b1d3dc6a7255e63270b7
-F autoconf/Makefile.msc 5bc67d3912444c40c6f96d003e5c90663e51abb83d204a520110b1b2038dcd8b
+F autoconf/Makefile.msc f15ad424ca2820df8e39d9157965710af0a64d87773706706a12ea4f96e3a0d8
F autoconf/README.first f1d3876e9a7852c22f275a6f06814e64934cecbc0b5b9617d64849094c1fd136
F autoconf/README.txt b749816b8452b3af994dc6d607394bef3df1736d7e09359f1087de8439a52807
F autoconf/auto.def 3d994f3a9cc9b712dbce92a5708570ddcf3b988141b6eb738f2ed16127a9f0ac
-F autoconf/tea/Makefile.in 41159d167c3b1a9bab7253f268d1ad1dc71ab8a9a6ccc9744a313aba40249a6a
+F autoconf/tea/Makefile.in 8c00e2ed350754d6b45681318ed7e4578aed8ad732abcac0593c1b10dc29e5a6
F autoconf/tea/README.txt 656d4686c509d375f5988ff3deda94f65fe6cd8358cd55d1f1dcc7b6e2ff73aa
-F autoconf/tea/auto.def 81e2617cfb90d53c19b53b3ec632cd2893bf32f2e5dd272b1116fadf2ea86c2d
+F autoconf/tea/auto.def ce95b9450e2fa4ba5dc857e208fe10f4e6f2d737796ac3278aee6079db417529
F autoconf/tea/autosetup/README.txt b40071e6f8506500a2f7f71d5fc69e0bf87b9d7678dd9da1e5b4d0acbf40b1ca
-F autoconf/tea/autosetup/core.tcl dea4684df6c5765a8fa866b4a6d79c73ba413ad37bb7c70e1be3631cd9eb2831
-F autoconf/tea/autosetup/feature-tests.tcl 307cac35806817fc87bd0d92004364ee682c0e99f2ab505291500493ab6c7a5f
-F autoconf/tea/autosetup/tester.tcl a201ef9e91dde182e73015d09f94a3c40dd534ce38642167ebfd5884801d1e5a
+F autoconf/tea/autosetup/core.tcl 7d942639871111e2fcef571c9d5a6e2dc75972eb214cf814a6b99f1e2b25182f
+F autoconf/tea/autosetup/feature-tests.tcl 18194fb79a24d30e5bbdeab40999616f39278b53a27525349ded033af2fd73be
+F autoconf/tea/autosetup/tester.tcl c293695a0ab5d9e8d0ceeb0ee422f90e8a6aa9f0c7c51acd0b6d9f09d8edfed3
F autoconf/tea/configure d0b12b984edca6030d1976375b80157ac78b5b90a5b4f0dcee39357f63f4a80b x
F autoconf/tea/doc/sqlite3.n 9a97f4f717ceab73004ea412af7960625c1cb24b5c25e4ae4c8b5d8fa4300f4e
F autoconf/tea/license.terms 13bd403c9610fd2b76ece0ab50c4c5eda933d523
-F autoconf/tea/pkgIndex.tcl.in a41b98633e184703136914e588638423047eae71244a30061ccd8edf92498436
-F autoconf/tea/teaish.tcl 2bc7ca287baa076daca513ff9342c00f499b1cceb8302a93d4a6231b00b52504
+F autoconf/tea/pkgIndex.tcl.in e07da6b94561f4aa382bab65b1ccceb04701b97bf59d007c1d1f20a222b22d07
+F autoconf/tea/teaish.tcl 8e124f33cbaf9309f3e49be4e7018a03b8f3a52f8c8d9e1e5419f4f7b0eae59e
F autoconf/tea/teaish.test.tcl cfe94e1fb79dd078f650295be59843d470125e0cc3a17a1414c1fb8d77f4aea6
-F autoconf/tea/teaish.tester.tcl.in 63059e35289ac663c7d0052e6c0089a309fee75225e86e4ec5b3d9f2c1d9290a
+F autoconf/tea/teaish.tester.tcl.in 31ac5b7b1e226b7e1bfc6b578a6c1a51550306ef7afae5949eec046df006ca7d
F autosetup/LICENSE 41a26aebdd2cd185d1e2b210f71b7ce234496979f6b35aef2cbf6b80cbed4ce4
F autosetup/README.autosetup a78ff8c4a3d2636a4268736672a74bf14a82f42687fcf0631a70c516075c031e
F autosetup/README.md f324bb9f9bf1cc787122034df53fbfdfed28ee2657e6652b763d992ab0d04829
@@ -48,11 +48,11 @@ F autosetup/cc-db.tcl 6e0ed90146197a5a05b245e649975c07c548e30926b218ca3e1d4dc034
F autosetup/cc-lib.tcl 493c5935b5dd3bf9bd4eca89b07c8b1b1a9356d61783035144e21795facf7360
F autosetup/cc-shared.tcl 4f024e94a47f427ba61de1739f6381ef0080210f9fae89112d5c1de1e5460d78
F autosetup/cc.tcl c0fcc50ca91deff8741e449ddad05bcd08268bc31177e613a6343bbd1fd3e45f
-F autosetup/find_tclconfig.tcl e64886ffe3b982d4df42cd28ed91fe0b5940c2c5785e126c1821baf61bc86a7e w tool/find_tclconfig.tcl
-F autosetup/jimsh0.c a57c16e65dcffc9c76e496757cb3f7fb47e01ecbd1631a0a5e01751fc856f049
+F autosetup/find_tclconfig.tcl e64886ffe3b982d4df42cd28ed91fe0b5940c2c5785e126c1821baf61bc86a7e
+F autosetup/jimsh0.c 563b966c137a4ce3c9333e5196723b7ac0919140a9d7989eb440463cd855c367
F autosetup/pkg-config.tcl 4e635bf39022ff65e0d5434339dd41503ea48fc53822c9c5bde88b02d3d952ba
-F autosetup/proj.tcl 11e46ff237ca96d07fd14fe96132f8e77fee6442d8baa2e808bbe95016583c56
-F autosetup/sqlite-config.tcl bde169c42d5d5331485ae7785f5dd19ca1aa01315787e8306441c472ad4e4201
+F autosetup/proj.tcl 0287234d817e800ab0e10d46bf98545ba5762edd69e5dd0e2902029a7e6c3555
+F autosetup/sqlite-config.tcl 7ff986f6c3951f3aec5608522cbf772d8d04a0d26cc894289e2ca4836e018719
F autosetup/system.tcl 51d4be76cd9a9074704b584e5c9cbba616202c8468cf9ba8a4f8294a7ab1dba9
F configure 9a00b21dfd13757bbfb8d89b30660a89ec1f8f3a79402b8f9f9b6fc475c3303a x
F contrib/sqlitecon.tcl eb4c6578e08dd353263958da0dc620f8400b869a50d06e271ab0be85a51a08d3
@@ -64,7 +64,7 @@ F doc/jsonb.md ede3238186e3a90bb79d20b2a6a06d0f9429a38e069e9da0efbad0f2ed48b924
F doc/lemon.html 89ea833a6f71773ab1a9063fbb7fb9b32147bc0b1057b53ecab94a3b30c0aef5
F doc/pager-invariants.txt 83aa3a4724b2d7970cc3f3461f0295c46d4fc19a835a5781cbb35cb52feb0577
F doc/tcl-extension-testing.md b88861804fc1eaf83249f8e206334189b61e150c360e1b80d0dcf91af82354f5
-F doc/testrunner.md 15583cf8c7d8a1c3378fd5d4319ca769a14c4d950a5df9b015d01d5be290dc69
+F doc/testrunner.md 5ee928637e03f136a25fef852c5ed975932e31927bd9b05a574424ae18c31019
F doc/trusted-schema.md 33625008620e879c7bcfbbfa079587612c434fa094d338b08242288d358c3e8a
F doc/vdbesort-memory.md 4da2639c14cd24a31e0af694b1a8dd37eaf277aff3867e9a8cc14046bc49df56
F doc/vfs-shm.txt 1a55f3f0e7b6745931b117ba5c9df3640d7a0536f532ef0052563100f4416f86
@@ -206,7 +206,7 @@ F ext/fts5/test/fts5lastrowid.test f36298a1fb9f988bde060a274a7ce638faa9c38a31400
F ext/fts5/test/fts5leftjoin.test c0b4cafb9661379e576dc4405c0891d8fcc2782680740513c4d1fc114b43d4ad
F ext/fts5/test/fts5limits.test 8ab67cf5d311c124b6ceb0062d0297767176df4572d955fce79fa43004dff01c
F ext/fts5/test/fts5locale.test 83ba7ee12628b540d3098f39c39c1de0c0440eddff8f7512c8c698d0c4a3ae3c
-F ext/fts5/test/fts5matchinfo.test 7806f6d521bb49bcb54fff88a50f137866f7000c96ccfd28500caa47b63cb0aa
+F ext/fts5/test/fts5matchinfo.test bc9e74157773db7f00aec1e85587f1145956ebdf1672c136f0f04323b2752aa0
F ext/fts5/test/fts5merge.test 2654df0bcdb2d117c2d38b6aeb0168061be01c643f9e9194b36c43a2970e8082
F ext/fts5/test/fts5merge2.test 3ebad1a59d6ad3fb66eff6523a09e95dc6367cbefb3cd73196801dea0425c8e2
F ext/fts5/test/fts5misc.test f4dee7da898d605a6488c5b7afaace3158ed6bb9addff78faa1b37b402b77fb9
@@ -711,7 +711,7 @@ F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36
F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61
F ext/wasm/wasmfs.make 68999f5bd8c489239592d59a420f8c627c99169bbd6fa16a404751f757b9f702
F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
-F main.mk a94af0553cc09c89632573a98de0dad3600c4da9225d8efa62f3b7840d7008c0
+F main.mk 3bc2070edfdc2279d21a469cb74737cdcbd2f535fdaf9c284c1878f8fb5b31a2
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@@ -731,14 +731,14 @@ F src/btmutex.c 30dada73a819a1ef5b7583786370dce1842e12e1ad941e4d05ac29695528daea
F src/btree.c d1ca761240cb8db94f71d977aa83c80e07b589ca0f52cbf122b73fb032dcdf6c
F src/btree.h 18e5e7b2124c23426a283523e5f31a4bff029131b795bb82391f9d2f3136fc50
F src/btreeInt.h 9c0f9ea5c9b5f4dcaea18111d43efe95f2ac276cd86d770dce10fd99ccc93886
-F src/build.c ad72c60b6e01053dea9d1e03f52de747138e4b0de1c73f17501ea307cf40a240
+F src/build.c 67c1db4c5e89a8519fe9b6dafc287f6bc3627696b5b8536dc5e06db570d8c05f
F src/callback.c acae8c8dddda41ee85cfdf19b926eefe830f371069f8aadca3aa39adf5b1c859
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/date.c 9db4d604e699a73e10b8e85a44db074a1f04c0591a77e2abfd77703f50dce1e9
F src/dbpage.c fcb1aafe00872a8aff9a7aa0ef7ff1b01e5817ec7bbd521f8f3e1e674ac8d609
F src/dbstat.c 73362c0df0f40ad5523a6f5501224959d0976757b511299bf892313e79d14f5c
F src/delete.c 03a77ba20e54f0f42ebd8eddf15411ed6bdb06a2c472ac4b6b336521bf7cea42
-F src/expr.c 61c3baab38f1b50eb4696e1f37c8f7ae1d1ecbfc1a35d446cfd1886624784131
+F src/expr.c 6f184da1f36576ad1ecc48a03f14774235373c64f88d462c710834930ee6c145
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
F src/fkey.c 928ed2517e8732113d2b9821aa37af639688d752f4ea9ac6e0e393d713eeb76f
F src/func.c 7686ea382b20e8bfe2ab9de76150c99ee7b6e83523561f3c7787e0f68cb435c2
@@ -748,7 +748,7 @@ F src/hash.h 46b92795a95bfefb210f52f0c316e9d7cdbcdd7e7fcfb0d8be796d3a5767cddf
F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
F src/insert.c d05934dfab2c5c0c480fc6fd2038f11215661de08ea6ff38d2563216bd555c1b
-F src/json.c d0e1c62a9689018fe5876b1a1eb574e4ab7a40e0928e0e5adda8b3860c70e1b8
+F src/json.c c84b0f2bae967341d5a035808d21d0619b23b6e054ceac08e7592cd87f04ed4f
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 7432c944ff197046d67a1207790a1b13eec4548c85a9457eb0896bb3641dfb36
F src/main.c 07f78d917ffcdf327982840cfd8e855fd000527a2ea5ace372ce4febcbd0bf97
@@ -788,7 +788,7 @@ F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
F src/resolve.c 20e1fbe8f840ffc0cd835e33f68a802a22e34faa918d7a269f3de242fda02f99
F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
F src/select.c 1a5956231f7c57571288eaad61e5c37aaf0f3acb5c8a5ea0b896938166b62fa2
-F src/shell.c.in 1e8b9bf369e80cdf9b029142e773038bc12bd38aea1c56df4af6bf7b46cae955
+F src/shell.c.in 2c904da4431fed365e7d5029b8cb2da46cb3e8cf8a09305d1478836a2301ea96
F src/sqlite.h.in 22882ddd3a70751aa8864c81993ee4562ed54c2c508b6270f75e223ffee38e1b
F src/sqlite3.rc 015537e6ac1eec6c7050e17b616c2ffe6f70fca241835a84a4f0d5937383c479
F src/sqlite3ext.h 3f046c04ea3595d6bfda99b781926b17e672fd6d27da2ba6d8d8fc39981dcb54
@@ -856,11 +856,11 @@ F src/upsert.c 215328c3f91623c520ec8672c44323553f12caeb4f01b1090ebdca99fdf7b4f1
F src/utf.c 3a20cbae9688af4c1e3754cc2520189d00762e37f60c2deb0b303360d166bba6
F src/util.c 36fb1150062957280777655976f3f9a75db236cb8207a0770ceae8d5ec17fcd3
F src/vacuum.c d580ceb395c1ae3d59da41cbfea60683ff7dd2b94ddf4d0f5657620159e2eeb7
-F src/vdbe.c 3db56c6704aa937627b5707f137ecb5c0c7c853fc7d4b4ad3d64022a484d433a
+F src/vdbe.c ca31e2e5b3f4cdb2dd3ac5c1be3194d71d01e42df1de217c558acf0acbb7ecd2
F src/vdbe.h 31eddcffc1d14c76c2a20fe4e137e1ee43d44f370896fae14a067052801a3625
F src/vdbeInt.h 5446f60e89b2aa7cdf3ab0ec4e7b01b8732cd9d52d9092a0b8b1bf700768f784
F src/vdbeapi.c 28fab30ed0acc981aecfdcaab0a421503609078e29850eb28494816682baf0a7
-F src/vdbeaux.c fc45bf49e6aa5168e77f3b5c4b500feab14ea995d3e444cb4ae557d8404ca251
+F src/vdbeaux.c a976da0c291f1c38c6911327edb35a82198b70fc567a05cbece51bde83b9bd06
F src/vdbeblob.c b1b4032cac46b41e44b957c4d00aee9851f862dfd85ecb68116ba49884b03dfd
F src/vdbemem.c e67d9c6484d868c879d20c70d00bf4a9058082f1d4058607ca15d50eb3aebc21
F src/vdbesort.c 49e366d0216c782eba287bf602384e4330d2526a22f1275492d2785ce103c79b
@@ -979,7 +979,7 @@ F test/bind2.test 918bc35135f4141809ead7585909cde57d44db90a7a62aef540127148f91aa
F test/bindxfer.test efecd12c580c14df5f4ad3b3e83c667744a4f7e0
F test/bitvec.test 75894a880520164d73b1305c1c3f96882615e142
F test/blob.test e7ac6c7d3a985cc4678c64f325292529a69ae252
-F test/bloom1.test 04f3a17df8912bfdc292c41b59d79f93893fe69799f3089a64451f9112f9658f
+F test/bloom1.test 3b6277a647ac503b5d5df331037b0c01c40e88cc9537b94eaf2d8aa334ed4c8f
F test/boundary1.tcl 6421b2d920d8b09539503a8673339d32f7609eb1
F test/boundary1.test 66d7f4706ccdb42d58eafdb081de07b0eb42d77b
F test/boundary2.tcl e34ef4e930cf1083150d4d2c603e146bd3b76bcb
@@ -1007,7 +1007,7 @@ F test/cast.test a2a3b32df86e3c0601ffa2e9f028a18796305d251801efea807092dbf374a04
F test/cffault.test 9d6b20606afe712374952eec4f8fd74b1a8097ef
F test/changes.test 4377d202a487f66fc2822c1bf57c46798c8b2caf7446f4f701723b1dbb6b86f6
F test/changes2.test 07949edcc732af28cb54276bfb7d99723bccc1e905a423648bf57ac5cb0dc792
-F test/check.test 56e4ed457e9f8683b9fc56f5b964f461f6e8a8dd5a13f3d495408215d66419ed
+F test/check.test 3a7972ccbaad80d496833da8714d69d9d5d4ce9e7211af1cd2a06ae488a7de12
F test/checkfault.test da6cb3d50247169efcb20bdf57863a3ccfa1d27d9e55cd324f0680096970f014
F test/chunksize.test faea11c5d6df9d392252a8dd879e1b1d68c9d3e8b7909cbed8bcec3b60c706f1
F test/cksumvfs.test 6f05dc95847c06a3dc10eee6b5ab1351d78314a52d0db15717c9388f4cb96646
@@ -1736,8 +1736,8 @@ F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d163
F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
F test/tester.tcl c882b91b16a9958faeb931c5a0a22eb475ed3c7610d400f1c1ea898882b1768e
-F test/testrunner.tcl 228732ca62ea60e97785e13528bd36627cf3c115d59364a5181ed7f85fd0031d x
-F test/testrunner_data.tcl 07f2d36a6321bfd196975aca237db6bba1991032809264a1abeb62e697196d20
+F test/testrunner.tcl 614c4a28f7f730acd7bec53e17d76602fb480e0d538b6ec548169e03a093f92d x
+F test/testrunner_data.tcl 8d5fa3851c48bc94e26db0be325202e44f6ca4ed838272b8d5b10c23817621e6
F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
F test/thread002.test c24c83408e35ba5a952a3638b7ac03ccdf1ce4409289c54a050ac4c5f1de7502
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -2157,7 +2157,6 @@ F tool/genfkey.README e550911fa984c8255ebed2ef97824125d83806eb5232582700de949edf
F tool/genfkey.test b6afd7b825d797a1e1274f519ab5695373552ecad5cd373530c63533638a5a4f
F tool/getlock.c f4c39b651370156cae979501a7b156bdba50e7ce
F tool/index_usage.c f62a0c701b2c7ff2f3e21d206f093c123f222dbf07136a10ffd1ca15a5c706c5
-F tool/kvtest-speed.sh 4761a9c4b3530907562314d7757995787f7aef8f
F tool/lemon.c f51a488369046cd4f4212d755a214a57673ded400cbeb01e298cbf63539e1d8c
F tool/lempar.c bdffd3b233a4e4e78056c9c01fadd2bb3fe902435abde3bce3d769fdf0d5cca2
F tool/libvers.c caafc3b689638a1d88d44bc5f526c2278760d9b9
@@ -2177,7 +2176,6 @@ F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07
F tool/mkpragmatab.tcl 3801ce32f8c55fe63a3b279f231fb26c2c1a2ea9a09d2dd599239d87a609acec
F tool/mkshellc.tcl 9ce74de0fa904a2c56a96f8d8b5261246bacb0eaa8d7e184f9e18ff94145ebbc
F tool/mksourceid.c 36aa8020014aed0836fd13c51d6dc9219b0df1761d6b5f58ff5b616211b079b9
-F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 351c55256213154cabb051a3c870ef9f4487de905015141ae50dc7578a901b84
F tool/mksqlite3c.tcl f11b63445c4840509248bd4aa151a81aea25d5415fef71943c8d436eba4f3b3c
F tool/mksqlite3h.tcl 989948c6a26e188e673d7c2f2f093ea3acd816ad6ac65bab596280075c8f3a45
@@ -2193,7 +2191,6 @@ F tool/pagesig.c f98909b4168d9cac11a2de7f031adea0e2f3131faa7515a72807c03ec58eafe
F tool/replace.tcl 511c61acfe563dfb58675efb4628bb158a13d48ff8322123ac447e9d25a82d9a
F tool/restore_jrnl.tcl 1079ecba47cc82fa82115b81c1f68097ab1f956f357ee8da5fc4b2589af6bd98
F tool/rollback-test.c 9fc98427d1e23e84429d7e6d07d9094fbdec65a5
-F tool/run-speed-test.sh df9686c0991ea7c617b2cb5467d89d34b561f198ab91cb87735e27030ede92e8
F tool/showdb.c 3956d71e5193162609a60e8c9edfcf09274c00cfea2b1d221261427adb2b5cca
F tool/showjournal.c 5bad7ae8784a43d2b270d953060423b8bd480818
F tool/showlocks.c 9cc5e66d4ebbf2d194f39db2527ece92077e86ae627ddd233ee48e16e8142564
@@ -2202,17 +2199,11 @@ F tool/showstat4.c 0682ebea7abf4d3657f53c4a243f2e7eab48eab344ed36a94bb75dcd19a5c
F tool/showwal.c 11eca547980a066b081f512636151233350ac679f29ecf4ebfce7f4530230b3d
F tool/soak1.tcl a3892082ed1079671565c044e93b55c3c7f38829aedf53cc597c65d23ffdaddf
F tool/spaceanal.tcl 1f83962090a6b60e1d7bf92495d643e622bef9fe82ea3f2d22350dcbce9a12d0
-F tool/speed-check.sh 2d9e337449f8eb9f5ab4c1ce7433024e334ea03a68d48aa9caee6229c7cf0774
-F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355
-F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e
-F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
-F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
-F tool/speedtest8inst1.c 7ce07da76b5e745783e703a834417d725b7d45fd
F tool/spellsift.tcl 52b4b04dc4333c7ab024f09d9d66ed6b6f7c6eb00b38497a09f338fa55d40618 x
F tool/split-sqlite3c.tcl 07e18a1d8cc3f6b3a4a1f3528e63c9b29a5c8a7bca0b8d394b231da464ce1247
F tool/sqldiff.c 134be7866be19f8beb32043d5aea5657f01aaeae2df8d33d758ff722c78666b9
F tool/sqlite3_analyzer.c.in 14f02cb5ec3c264cd6107d1f1dad77092b1cf440fc196c30b69ae87b56a1a43b
-F tool/sqlite3_rsync.c 9a1cca2ab1271c59b37a6493c15dc1bcd0ab9149197a9125926bc08dd26b83fb
+F tool/sqlite3_rsync.c dcf03373565eab543bd43926ed7e97ade744ab0599115fde1ff8aadf04fa4974
F tool/sqltclsh.c.in 1bcc2e9da58fadf17b0bf6a50e68c1159e602ce057210b655d50bad5aaaef898
F tool/sqltclsh.tcl 862f4cf1418df5e1315b5db3b5ebe88969e2a784525af5fbf9596592f14ed848
F tool/src-verify.c d00f93263aa2fa6ba0cba0106d95458e6effb94fdb5fc634f56834f90c05bbb4
@@ -2230,8 +2221,8 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 49a486c5069de041aedcbde4de178293e0463ae9918ecad7539eedf0ec77a139
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 80e6ddd560b3041fe9164b940d684eeb6f28560a6c48b23ff49095da52e85df8 ea9acb5573f4d71a314e4467d30477a1d01c8db648985750a42b3c047f404c9c
-R e8277f87bf1c61b9f0b07d9f21635d54
+P c68d0d353082a5810a48f01637d642bcdfd9f03dd244e0618fc0d7cf5f7b9b12 6eb2939a6093c0796910645172d80c53055559dd57c012f1dc815d89fbf84447
+R 71f6d3dc7cf4b3e239f1610c654bf049
U drh
-Z e8ead4bda11deb101225583febb05da9
+Z ae0c7f7b4f8ebc4d74a219746691a228
# Remove this line to create a well-formed Fossil manifest.
diff --git a/manifest.uuid b/manifest.uuid
index 8a1e6a0a1d..c2f29d376e 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-c68d0d353082a5810a48f01637d642bcdfd9f03dd244e0618fc0d7cf5f7b9b12
+b17f5beab892028fa3e246858f7b34e9104ffb4edd970804e31e5c71bcd5bbc5
diff --git a/src/build.c b/src/build.c
index 13f5b71330..5bd3aac3ca 100644
--- a/src/build.c
+++ b/src/build.c
@@ -1071,7 +1071,7 @@ int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
int i;
i16 iCol16;
assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
- assert( pIdx->nColumn<=SQLITE_MAX_COLUMN );
+ assert( pIdx->nColumn<=SQLITE_MAX_COLUMN+1 );
iCol16 = iCol;
for(i=0; inColumn; i++){
if( iCol16==pIdx->aiColumn[i] ){
diff --git a/src/expr.c b/src/expr.c
index dd5ea20383..12c94362f7 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -3626,11 +3626,12 @@ void sqlite3CodeRhsOfIN(
sqlite3SelectDelete(pParse->db, pCopy);
sqlite3DbFree(pParse->db, dest.zAffSdst);
if( addrBloom ){
+ /* Remember that location of the Bloom filter in the P3 operand
+ ** of the OP_Once that began this subroutine. tag-202407032019 */
sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2;
if( dest.iSDParm2==0 ){
- sqlite3VdbeChangeToNoop(v, addrBloom);
- }else{
- sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2;
+ /* If the Bloom filter won't actually be used, keep it small */
+ sqlite3VdbeGetOp(v, addrBloom)->p1 = 10;
}
}
if( rc ){
@@ -4077,7 +4078,7 @@ static void sqlite3ExprCodeIN(
if( ExprHasProperty(pExpr, EP_Subrtn) ){
const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
assert( pOp->opcode==OP_Once || pParse->nErr );
- if( pOp->opcode==OP_Once && pOp->p3>0 ){
+ if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */
assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) );
sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse,
rLhs, nVector); VdbeCoverage(v);
@@ -5926,11 +5927,11 @@ void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
- sqlite3VdbeTypeofColumn(v, r1);
+ assert( regFree1==0 || regFree1==r1 );
+ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1);
sqlite3VdbeAddOp2(v, op, r1, dest);
VdbeCoverageIf(v, op==TK_ISNULL);
VdbeCoverageIf(v, op==TK_NOTNULL);
- testcase( regFree1==0 );
break;
}
case TK_BETWEEN: {
@@ -6101,11 +6102,11 @@ void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int jumpIfNull){
case TK_ISNULL:
case TK_NOTNULL: {
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1);
- sqlite3VdbeTypeofColumn(v, r1);
+ assert( regFree1==0 || regFree1==r1 );
+ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1);
sqlite3VdbeAddOp2(v, op, r1, dest);
testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL);
testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL);
- testcase( regFree1==0 );
break;
}
case TK_BETWEEN: {
diff --git a/src/json.c b/src/json.c
index 9c38bde985..1528fdb708 100644
--- a/src/json.c
+++ b/src/json.c
@@ -398,7 +398,7 @@ struct JsonParse {
** Forward references
**************************************************************************/
static void jsonReturnStringAsBlob(JsonString*);
-static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
+static int jsonArgIsJsonb(sqlite3_value *pJson, JsonParse *p);
static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
static void jsonReturnParse(sqlite3_context*,JsonParse*);
static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
@@ -816,11 +816,9 @@ static void jsonAppendSqlValue(
break;
}
default: {
- if( jsonFuncArgMightBeBinary(pValue) ){
- JsonParse px;
- memset(&px, 0, sizeof(px));
- px.aBlob = (u8*)sqlite3_value_blob(pValue);
- px.nBlob = sqlite3_value_bytes(pValue);
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ if( jsonArgIsJsonb(pValue, &px) ){
jsonTranslateBlobToText(&px, 0, p);
}else if( p->eErr==0 ){
sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
@@ -2494,33 +2492,6 @@ static u32 jsonTranslateBlobToPrettyText(
return i;
}
-
-/* Return true if the input pJson
-**
-** For performance reasons, this routine does not do a detailed check of the
-** input BLOB to ensure that it is well-formed. Hence, false positives are
-** possible. False negatives should never occur, however.
-*/
-static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
- u32 sz, n;
- const u8 *aBlob;
- int nBlob;
- JsonParse s;
- if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
- aBlob = sqlite3_value_blob(pJson);
- nBlob = sqlite3_value_bytes(pJson);
- if( nBlob<1 ) return 0;
- if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
- memset(&s, 0, sizeof(s));
- s.aBlob = (u8*)aBlob;
- s.nBlob = nBlob;
- n = jsonbPayloadSize(&s, 0, &sz);
- if( n==0 ) return 0;
- if( sz+n!=(u32)nBlob ) return 0;
- if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
- return sz+n==(u32)nBlob;
-}
-
/*
** Given that a JSONB_ARRAY object starts at offset i, return
** the number of entries in that array.
@@ -3348,10 +3319,7 @@ static int jsonFunctionArgToBlob(
return 0;
}
case SQLITE_BLOB: {
- if( jsonFuncArgMightBeBinary(pArg) ){
- pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
- pParse->nBlob = sqlite3_value_bytes(pArg);
- }else{
+ if( !jsonArgIsJsonb(pArg, pParse) ){
sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
return 1;
}
@@ -3502,27 +3470,46 @@ jsonInsertIntoBlob_patherror:
/*
** If pArg is a blob that seems like a JSONB blob, then initialize
** p to point to that JSONB and return TRUE. If pArg does not seem like
-** a JSONB blob, then return FALSE;
+** a JSONB blob, then return FALSE.
**
-** This routine is only called if it is already known that pArg is a
-** blob. The only open question is whether or not the blob appears
-** to be a JSONB blob.
+** For small BLOBs (having no more than 7 bytes of payload) a full
+** validity check is done. So for small BLOBs this routine only returns
+** true if the value is guaranteed to be a valid JSONB. For larger BLOBs
+** (8 byte or more of payload) only the size of the outermost element is
+** checked to verify that the BLOB is superficially valid JSONB.
+**
+** A full JSONB validation is done on smaller BLOBs because those BLOBs might
+** also be text JSON that has been incorrectly cast into a BLOB.
+** (See tag-20240123-a and https://sqlite.org/forum/forumpost/012136abd5)
+** If the BLOB is 9 bytes are larger, then it is not possible for the
+** superficial size check done here to pass if the input is really text
+** JSON so we do not need to look deeper in that case.
+**
+** Why we only need to do full JSONB validation for smaller BLOBs:
+**
+** The first byte of valid JSON text must be one of: '{', '[', '"', ' ', '\n',
+** '\r', '\t', '-', or a digit '0' through '9'. Of these, only a subset
+** can also be the first byte of JSONB: '{', '[', and digits '3'
+** through '9'. In every one of those cases, the payload size is 7 bytes
+** or less. So if we do full JSONB validation for every BLOB where the
+** payload is less than 7 bytes, we will never get a false positive for
+** JSONB on an input that is really text JSON.
*/
static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
u32 n, sz = 0;
+ u8 c;
+ if( sqlite3_value_type(pArg)!=SQLITE_BLOB ) return 0;
p->aBlob = (u8*)sqlite3_value_blob(pArg);
p->nBlob = (u32)sqlite3_value_bytes(pArg);
- if( p->nBlob==0 ){
- p->aBlob = 0;
- return 0;
- }
- if( NEVER(p->aBlob==0) ){
- return 0;
- }
- if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
+ if( p->nBlob>0
+ && ALWAYS(p->aBlob!=0)
+ && ((c = p->aBlob[0]) & 0x0f)<=JSONB_OBJECT
&& (n = jsonbPayloadSize(p, 0, &sz))>0
&& sz+n==p->nBlob
- && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
+ && ((c & 0x0f)>JSONB_FALSE || sz==0)
+ && (sz>7
+ || (c!=0x7b && c!=0x5b && !sqlite3Isdigit(c))
+ || jsonbValidityCheck(p, 0, p->nBlob, 1)==0)
){
return 1;
}
@@ -4615,21 +4602,17 @@ static void jsonValidFunc(
return;
}
case SQLITE_BLOB: {
- if( jsonFuncArgMightBeBinary(argv[0]) ){
+ JsonParse py;
+ memset(&py, 0, sizeof(py));
+ if( jsonArgIsJsonb(argv[0], &py) ){
if( flags & 0x04 ){
/* Superficial checking only - accomplished by the
- ** jsonFuncArgMightBeBinary() call above. */
+ ** jsonArgIsJsonb() call above. */
res = 1;
}else if( flags & 0x08 ){
/* Strict checking. Check by translating BLOB->TEXT->BLOB. If
** no errors occur, call that a "strict check". */
- JsonParse px;
- u32 iErr;
- memset(&px, 0, sizeof(px));
- px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
- px.nBlob = sqlite3_value_bytes(argv[0]);
- iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
- res = iErr==0;
+ res = 0==jsonbValidityCheck(&py, 0, py.nBlob, 1);
}
break;
}
@@ -4687,9 +4670,7 @@ static void jsonErrorFunc(
UNUSED_PARAMETER(argc);
memset(&s, 0, sizeof(s));
s.db = sqlite3_context_db_handle(ctx);
- if( jsonFuncArgMightBeBinary(argv[0]) ){
- s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
- s.nBlob = sqlite3_value_bytes(argv[0]);
+ if( jsonArgIsJsonb(argv[0], &s) ){
iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
}else{
s.zJson = (char*)sqlite3_value_text(argv[0]);
@@ -5374,9 +5355,8 @@ static int jsonEachFilter(
memset(&p->sParse, 0, sizeof(p->sParse));
p->sParse.nJPRef = 1;
p->sParse.db = p->db;
- if( jsonFuncArgMightBeBinary(argv[0]) ){
- p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
- p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ if( jsonArgIsJsonb(argv[0], &p->sParse) ){
+ /* We have JSONB */
}else{
p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
p->sParse.nJson = sqlite3_value_bytes(argv[0]);
diff --git a/src/shell.c.in b/src/shell.c.in
index ca76e4a6e9..7af8f79b8d 100644
--- a/src/shell.c.in
+++ b/src/shell.c.in
@@ -1243,30 +1243,6 @@ static void shellDtostr(
sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
}
-
-/*
-** SQL function: shell_module_schema(X)
-**
-** Return a fake schema for the table-valued function or eponymous virtual
-** table X.
-*/
-static void shellModuleSchema(
- sqlite3_context *pCtx,
- int nVal,
- sqlite3_value **apVal
-){
- const char *zName;
- char *zFake;
- UNUSED_PARAMETER(nVal);
- zName = (const char*)sqlite3_value_text(apVal[0]);
- zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
- if( zFake ){
- sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
- -1, sqlite3_free);
- free(zFake);
- }
-}
-
/*
** SQL function: shell_add_schema(S,X)
**
@@ -5710,6 +5686,39 @@ static void shellUSleepFunc(
sqlite3_result_int(context, sleep);
}
+/*
+** SQL function: shell_module_schema(X)
+**
+** Return a fake schema for the table-valued function or eponymous virtual
+** table X.
+*/
+static void shellModuleSchema(
+ sqlite3_context *pCtx,
+ int nVal,
+ sqlite3_value **apVal
+){
+ const char *zName;
+ char *zFake;
+ ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
+ FILE *pSavedLog = p->pLog;
+ UNUSED_PARAMETER(nVal);
+ zName = (const char*)sqlite3_value_text(apVal[0]);
+
+ /* Temporarily disable the ".log" when calling shellFakeSchema() because
+ ** shellFakeSchema() might generate failures for some ephemeral virtual
+ ** tables due to missing arguments. Example: fts4aux.
+ ** https://sqlite.org/forum/forumpost/42fe6520b803be51 */
+ p->pLog = 0;
+ zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
+ p->pLog = pSavedLog;
+
+ if( zFake ){
+ sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
+ -1, sqlite3_free);
+ free(zFake);
+ }
+}
+
/* Flags for open_db().
**
** The default behavior of open_db() is to exit(1) if the database fails to
@@ -5853,7 +5862,7 @@ static void open_db(ShellState *p, int openFlags){
shellDtostr, 0, 0);
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
shellAddSchemaName, 0, 0);
- sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
+ sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, p,
shellModuleSchema, 0, 0);
sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
shellPutsFunc, 0, 0);
diff --git a/src/vdbe.c b/src/vdbe.c
index 586b03edd0..ff08b749b9 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -2644,7 +2644,7 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
break;
}
-/* Opcode: Once P1 P2 * * *
+/* Opcode: Once P1 P2 P3 * *
**
** Fall through to the next instruction the first time this opcode is
** encountered on each invocation of the byte-code program. Jump to P2
@@ -2660,6 +2660,12 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
** whether or not the jump should be taken. The bitmask is necessary
** because the self-altering code trick does not work for recursive
** triggers.
+**
+** The P3 operand is not used directly by this opcode. However P3 is
+** used by the code generator as follows: If this opcode is the start
+** of a subroutine and that subroutine uses a Bloom filter, then P3 will
+** be the register that holds that Bloom filter. See tag-202407032019
+** in the source code for implementation details.
*/
case OP_Once: { /* jump */
u32 iAddr; /* Address of this instruction */
@@ -3705,6 +3711,7 @@ case OP_MakeRecord: {
zHdr += sqlite3PutVarint(zHdr, serial_type);
if( pRec->n ){
assert( pRec->z!=0 );
+ assert( pRec->z!=(const char*)sqlite3CtypeMap );
memcpy(zPayload, pRec->z, pRec->n);
zPayload += pRec->n;
}
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index d5cace07bf..840319a884 100644
--- a/src/vdbeaux.c
+++ b/src/vdbeaux.c
@@ -1306,6 +1306,9 @@ void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){
*/
void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){
VdbeOp *pOp = sqlite3VdbeGetLastOp(p);
+#ifdef SQLITE_DEBUG
+ while( pOp->opcode==OP_ReleaseReg ) pOp--;
+#endif
if( pOp->p3==iDest && pOp->opcode==OP_Column ){
pOp->p5 |= OPFLAG_TYPEOFARG;
}
diff --git a/test/bloom1.test b/test/bloom1.test
index f8efcc1846..09553c3b9b 100644
--- a/test/bloom1.test
+++ b/test/bloom1.test
@@ -224,6 +224,18 @@ do_execsql_test 5.3 {
SELECT 0 as c_0
);
} {0}
-
+
+# 2025-04-30 https://sqlite.org/forum/forumpost/792a09cb3df9e69f
+# A continuation of the above.
+#
+do_execsql_test 6.1 {
+ DROP TABLE IF EXISTS t1;
+ CREATE TABLE t1(a);
+ SELECT 111 IN (
+ SELECT 222 FROM (SELECT 333 ORDER BY 1)
+ UNION ALL
+ SELECT 444 FROM (SELECT 555 FROM t1 ORDER BY 1)
+ );
+} 0
finish_test
diff --git a/test/check.test b/test/check.test
index 10d1cf4be6..c3beb2f5d8 100644
--- a/test/check.test
+++ b/test/check.test
@@ -612,4 +612,36 @@ do_catchsql_test 12.81 {
INSERT INTO t1(a) VALUES(456);
} {1 {CHECK constraint failed: a NOT BETWEEN +a AND 999999}}
+#-------------------------------------------------------------------------
+
+reset_db
+
+do_execsql_test 13.1.0 {
+ CREATE TABLE Table0 (Col0 , CHECK(Table0.Col0 NOT NULL ) ) ;
+ REPLACE INTO Table0 VALUES (hex(randomblob(100000)));
+}
+integrity_check 13.1.1
+do_execsql_test 13.1.2 {
+ UPDATE OR REPLACE Table0 SET Col0 = Table0.Col0 ;
+}
+integrity_check 13.1.3
+do_execsql_test 13.1.4 {
+ SELECT length(col0) FROM table0;
+} {200000}
+
+do_execsql_test 13.2.0 {
+ CREATE TABLE t2 (x , CHECK((NOT (x ISNULL) )));
+ REPLACE INTO t2 VALUES (hex(randomblob(100000)));
+}
+do_execsql_test 13.2.1 {
+ SELECT length(x) FROM t2
+} {200000}
+do_execsql_test 13.2.2 {
+ UPDATE OR REPLACE t2 SET x = x;
+}
+do_execsql_test 13.2.3 {
+ SELECT length(x) FROM t2
+} {200000}
+integrity_check 13.2.4
+
finish_test
diff --git a/test/testrunner.tcl b/test/testrunner.tcl
index 60c4627f92..0c6982f426 100755
--- a/test/testrunner.tcl
+++ b/test/testrunner.tcl
@@ -98,6 +98,7 @@ Usage:
--config CONFIGS Only use configs on comma-separate list CONFIGS
--dryrun Write what would have happened to testrunner.log
--explain Write summary to stdout
+ --fuzzdb FILENAME Additional external fuzzcheck database
--jobs NUM Run tests using NUM separate processes
--omit CONFIGS Omit configs on comma-separated list CONFIGS
--status Show the full "status" report while running
@@ -169,7 +170,6 @@ Full documentation here: https://sqlite.org/src/doc/trunk/doc/testrunner.md
proc guess_number_of_cores {} {
if {[catch {number_of_cores} ret]} {
set ret 4
-
if {$::tcl_platform(platform) eq "windows"} {
catch { set ret $::env(NUMBER_OF_PROCESSORS) }
} else {
@@ -532,7 +532,7 @@ proc show_status {db cls} {
(SELECT value FROM config WHERE name='start')
}]
- set total 0
+ set totalw 0
foreach s {"" ready running done failed omit} { set S($s) 0; set W($s) 0; }
set workpending 0
$db eval {
@@ -557,7 +557,7 @@ proc show_status {db cls} {
flush stdout
}
puts [format %-79.79s "Command: \[testrunner.tcl$cmdline\]"]
- puts [format %-79.79s "Summary: [elapsetime $tm], $fin/$total jobs,\
+ puts [format %-79.79s "Summary: [elapsetime $tm], $fin/$totalw jobs,\
$ne errors, $nt tests"]
set srcdir [file dirname [file dirname $TRG(info_script)]]
@@ -812,6 +812,9 @@ for {set ii 0} {$ii < [llength $argv]} {incr ii} {
} elseif {($n>2 && [string match "$a*" --omit]) || $a=="-c"} {
incr ii
set TRG(omitconfig) [lindex $argv $ii]
+ } elseif {($n>2 && [string match "$a*" --fuzzdb])} {
+ incr ii
+ set env(FUZZDB) [lindex $argv $ii]
} elseif {[string match "$a*" --stop-on-error]} {
set TRG(stopOnError) 1
} elseif {[string match "$a*" --stop-on-coredump]} {
@@ -1003,6 +1006,35 @@ proc add_job {args} {
trdb last_insert_rowid
}
+
+# Look to see if $jobcmd matches any of the glob patterns given in
+# $patternlist. Return true if there is a match. Return false
+# if no match is seen.
+#
+# An empty patternlist matches everything
+#
+proc job_matches_any_pattern {patternlist jobcmd} {
+ set bMatch 0
+ if {[llength $patternlist]==0} {return 1}
+ foreach p $patternlist {
+ set p [string trim $p *]
+ if {[string index $p 0]=="^"} {
+ set p [string range $p 1 end]
+ } else {
+ set p "*$p"
+ }
+ if {[string index $p end]=="\$"} {
+ set p [string range $p 0 end-1]
+ } else {
+ set p "$p*"
+ }
+ if {[string match $p $jobcmd]} {
+ set bMatch 1
+ break
+ }
+ }
+ return $bMatch
+}
# Argument $build is either an empty string, or else a list of length 3
@@ -1016,6 +1048,7 @@ proc add_job {args} {
#
proc add_tcl_jobs {build config patternlist {shelldepid ""}} {
global TRG
+ set ntcljob 0
set topdir [file dirname $::testdir]
set testrunner_tcl [file normalize [info script]]
@@ -1033,26 +1066,8 @@ proc add_tcl_jobs {build config patternlist {shelldepid ""}} {
# The ::testspec array is populated by permutations.test
foreach f [dict get $::testspec($config) -files] {
- if {[llength $patternlist]>0} {
- set bMatch 0
- foreach p $patternlist {
- set p [string trim $p *]
- if {[string index $p 0]=="^"} {
- set p [string range $p 1 end]
- } else {
- set p "*$p"
- }
- if {[string index $p end]=="\$"} {
- set p [string range $p 0 end-1]
- } else {
- set p "$p*"
- }
- if {[string match $p "$config [file tail $f]"]} {
- set bMatch 1
- break
- }
- }
- if {$bMatch==0} continue
+ if {![job_matches_any_pattern $patternlist "$config [file tail $f]"]} {
+ continue
}
if {[file pathtype $f]!="absolute"} { set f [file join $::testdir $f] }
@@ -1077,6 +1092,7 @@ proc add_tcl_jobs {build config patternlist {shelldepid ""}} {
set depid [lindex $build 0]
if {$shelldepid!="" && [lsearch $lProp shell]>=0} { set depid $shelldepid }
+ incr ntcljob
add_job \
-displaytype tcl \
-displayname $displayname \
@@ -1084,6 +1100,10 @@ proc add_tcl_jobs {build config patternlist {shelldepid ""}} {
-depid $depid \
-priority $priority
}
+ if {$ntcljob==0 && [llength $build]>0} {
+ set bldid [lindex $build 0]
+ trdb eval {DELETE FROM jobs WHERE rowid=$bldid}
+ }
}
proc add_build_job {buildname target {postcmd ""} {depid ""}} {
@@ -1146,26 +1166,57 @@ proc add_make_job {bld target} {
-priority 1
}
-proc add_fuzztest_jobs {buildname} {
+proc add_fuzztest_jobs {buildname patternlist} {
+ global env TRG
+ # puts buildname=$buildname
- foreach {interpreter scripts} [trd_fuzztest_data] {
+ foreach {interpreter scripts} [trd_fuzztest_data $buildname] {
+ set bldDone 0
set subcmd [lrange $interpreter 1 end]
set interpreter [lindex $interpreter 0]
- set bld [add_build_job $buildname $interpreter]
- foreach {depid dirname displayname} $bld {}
+ if {[string match fuzzcheck* $interpreter]
+ && [info exists env(FUZZDB)]
+ && [file readable $env(FUZZDB)]
+ && $buildname ne "Windows-Win32Heap"
+ && $buildname ne "Windows-Memdebug"
+ } {
+ set TRG(FUZZDB) $env(FUZZDB)
+ set fname [file normalize $env(FUZZDB)]
+ set N [expr {([file size $fname]+4999999)/5000000}]
+ for {set i 0} {$i<$N} {incr i} {
+ lappend scripts [list --slice $i $N $fname]
+ }
+ }
foreach s $scripts {
# Fuzz data files fuzzdata1.db and fuzzdata2.db are larger than
# the others. So ensure that these are run as a higher priority.
- set tail [file tail $s]
- if {$tail=="fuzzdata1.db" || $tail=="fuzzdata2.db"} {
+ if {[llength $s]==1} {
+ set tail [file tail $s]
+ } else {
+ set fname [lindex $s end]
+ set tail [lrange $s 0 end-1]
+ lappend tail [file tail $fname]
+ }
+ if {![job_matches_any_pattern $patternlist "$interpreter $tail"]} {
+ continue
+ }
+ if {!$bldDone} {
+ set bld [add_build_job $buildname $interpreter]
+ foreach {depid dirname displayname} $bld {}
+ set bldDone 1
+ }
+ if {[string match ?-slice* $tail]} {
+ set priority 15
+ } elseif {$tail=="fuzzdata1.db"
+ || $tail=="fuzzdata2.db"
+ || $tail=="fuzzdata8.db"} {
set priority 5
} else {
set priority 1
}
-
add_job \
-displaytype fuzz \
-displayname "$buildname $interpreter $tail" \
@@ -1201,9 +1252,7 @@ proc add_devtest_jobs {lBld patternlist} {
foreach b $lBld {
set bld [add_build_job $b $TRG(testfixture)]
add_tcl_jobs $bld veryquick $patternlist SHELL
- if {$patternlist==""} {
- add_fuzztest_jobs $b
- }
+ add_fuzztest_jobs $b $patternlist
if {[trdb one "SELECT EXISTS (SELECT 1 FROM jobs WHERE depid='SHELL')"]} {
set sbld [add_shell_build_job $b [lindex $bld 1] [lindex $bld 0]]
@@ -1277,13 +1326,11 @@ proc add_jobs_from_cmdline {patternlist} {
add_tcl_jobs $bld $c $patternlist SHELL
}
- if {$patternlist==""} {
- foreach e [trd_extras $TRG(platform) $b] {
- if {$e=="fuzztest"} {
- add_fuzztest_jobs $b
- } else {
- add_make_job $bld $e
- }
+ foreach e [trd_extras $TRG(platform) $b] {
+ if {$e=="fuzztest"} {
+ add_fuzztest_jobs $b $patternlist
+ } elseif {[job_matches_any_pattern $patternlist $e]} {
+ add_make_job $bld $e
}
}
@@ -1603,6 +1650,9 @@ proc run_testset {} {
puts "\nTest database is $TRG(dbname)"
puts "Test log is $TRG(logname)"
+ if {[info exists TRG(FUZZDB)]} {
+ puts "Extra fuzzcheck data taken from $TRG(FUZZDB)"
+ }
trdb eval {
SELECT sum(ntest) AS totaltest,
sum(nerr) AS totalerr
@@ -1655,7 +1705,13 @@ proc explain_layer {indent depid} {
puts "${indent}$displayname in $dirname"
explain_layer "${indent} " $jobid
} elseif {$showtests} {
- set tail [lindex $displayname end]
+ if {[lindex $displayname end-3] eq "--slice"} {
+ set M [lindex $displayname end-2]
+ set N [lindex $displayname end-1]
+ set tail "[lindex $displayname end] (slice $M/$N)"
+ } else {
+ set tail [lindex $displayname end]
+ }
set e1 [lindex $displayname 1]
if {[string match config=* $e1]} {
set cfg [string range $e1 7 end]
diff --git a/test/testrunner_data.tcl b/test/testrunner_data.tcl
index ade126a64d..557c31b803 100644
--- a/test/testrunner_data.tcl
+++ b/test/testrunner_data.tcl
@@ -430,7 +430,7 @@ proc trd_extras {platform bld} {
# Usage:
#
-# trd_fuzztest_data
+# trd_fuzztest_data $buildname
#
# This returns data used by testrunner.tcl to run commands equivalent
# to [make fuzztest]. The returned value is a list, which should be
@@ -450,16 +450,24 @@ proc trd_extras {platform bld} {
# directory containing this file). "fuzzcheck" and "sessionfuzz" have .exe
# extensions on windows.
#
-proc trd_fuzztest_data {} {
+proc trd_fuzztest_data {buildname} {
set EXE ""
set lFuzzDb [glob [file join $::testdir fuzzdata*.db]]
set lSessionDb [glob [file join $::testdir sessionfuzz-data*.db]]
+ set sanBuilds {All-Debug Apple Have-Not Update-Delete-Limit}
if {$::tcl_platform(platform) eq "windows"} {
return [list fuzzcheck.exe $lFuzzDb]
+ } elseif {[lsearch $sanBuilds $buildname]>=0} {
+ return [list [trd_get_bin_name fuzzcheck] $lFuzzDb \
+ [trd_get_bin_name fuzzcheck-asan] $lFuzzDb \
+ [trd_get_bin_name fuzzcheck-ubsan] $lFuzzDb \
+ {sessionfuzz run} $lSessionDb]
+ } else {
+ return [list [trd_get_bin_name fuzzcheck] $lFuzzDb \
+ {sessionfuzz run} $lSessionDb]
}
- return [list [trd_get_bin_name fuzzcheck] $lFuzzDb {sessionfuzz run} $lSessionDb]
}
diff --git a/tool/kvtest-speed.sh b/tool/kvtest-speed.sh
deleted file mode 100644
index 5f2c8345be..0000000000
--- a/tool/kvtest-speed.sh
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/bin/bash
-#
-# A script for running speed tests using kvtest.
-#
-# The test database must be set up first. Recommended
-# command-line:
-#
-# ./kvtest init kvtest.db --count 100K --size 12K --variance 5K
-
-if test "$1" = ""
-then
- echo "Usage: $0 OUTPUTFILE [OPTIONS]"
- exit
-fi
-NAME=$1
-shift
-OPTS="-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_DIRECT_OVERFLOW_READ -DUSE_PREAD"
-KVARGS="--count 100K --stats"
-gcc -g -Os -I. $OPTS $* kvtest.c sqlite3.c -o kvtest
-
-# First run using SQL
-rm cachegrind.out.[1-9][0-9]*
-valgrind --tool=cachegrind ./kvtest run kvtest.db $KVARGS 2>&1 | tee summary-kvtest-$NAME.txt
-mv cachegrind.out.[1-9][0-9]* cachegrind.out.sql-$NAME
-cg_anno.tcl cachegrind.out.sql-$NAME >cout-kvtest-sql-$NAME.txt
-
-# Second run using the sqlite3_blob object
-valgrind --tool=cachegrind ./kvtest run kvtest.db $KVARGS --blob-api 2>&1 | tee -a summary-kvtest-$NAME.txt
-mv cachegrind.out.[1-9][0-9]* cachegrind.out.$NAME
-cg_anno.tcl cachegrind.out.$NAME >cout-kvtest-$NAME.txt
-
-# Diff the sqlite3_blob API analysis for non-trunk runs.
-if test "$NAME" != "trunk"; then
- fossil test-diff --tk cout-kvtest-trunk.txt cout-kvtest-$NAME.txt &
-fi
diff --git a/tool/mkspeedsql.tcl b/tool/mkspeedsql.tcl
deleted file mode 100644
index 04bafc04c1..0000000000
--- a/tool/mkspeedsql.tcl
+++ /dev/null
@@ -1,237 +0,0 @@
-# 2008 October 9
-#
-# 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.
-#
-#*************************************************************************
-# This file generates SQL text used for performance testing.
-#
-# $Id: mkspeedsql.tcl,v 1.1 2008/10/09 17:57:34 drh Exp $
-#
-
-# Set a uniform random seed
-expr srand(0)
-
-# The number_name procedure below converts its argment (an integer)
-# into a string which is the English-language name for that number.
-#
-# Example:
-#
-# puts [number_name 123] -> "one hundred twenty three"
-#
-set ones {zero one two three four five six seven eight nine
- ten eleven twelve thirteen fourteen fifteen sixteen seventeen
- eighteen nineteen}
-set tens {{} ten twenty thirty forty fifty sixty seventy eighty ninety}
-proc number_name {n} {
- if {$n>=1000} {
- set txt "[number_name [expr {$n/1000}]] thousand"
- set n [expr {$n%1000}]
- } else {
- set txt {}
- }
- if {$n>=100} {
- append txt " [lindex $::ones [expr {$n/100}]] hundred"
- set n [expr {$n%100}]
- }
- if {$n>=20} {
- append txt " [lindex $::tens [expr {$n/10}]]"
- set n [expr {$n%10}]
- }
- if {$n>0} {
- append txt " [lindex $::ones $n]"
- }
- set txt [string trim $txt]
- if {$txt==""} {set txt zero}
- return $txt
-}
-
-# Create a database schema.
-#
-puts {
- PRAGMA page_size=1024;
- PRAGMA cache_size=8192;
- PRAGMA locking_mode=EXCLUSIVE;
- CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
- CREATE TABLE t2(a INTEGER, b INTEGER, c TEXT);
- CREATE INDEX i2a ON t2(a);
- CREATE INDEX i2b ON t2(b);
- SELECT name FROM sqlite_master ORDER BY 1;
-}
-
-
-# 50000 INSERTs on an unindexed table
-#
-set t1c_list {}
-puts {BEGIN;}
-for {set i 1} {$i<=50000} {incr i} {
- set r [expr {int(rand()*500000)}]
- set x [number_name $r]
- lappend t1c_list $x
- puts "INSERT INTO t1 VALUES($i,$r,'$x');"
-}
-puts {COMMIT;}
-
-# 50000 INSERTs on an indexed table
-#
-puts {BEGIN;}
-for {set i 1} {$i<=50000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts "INSERT INTO t2 VALUES($i,$r,'[number_name $r]');"
-}
-puts {COMMIT;}
-
-
-# 50 SELECTs on an integer comparison. There is no index so
-# a full table scan is required.
-#
-for {set i 0} {$i<50} {incr i} {
- set lwr [expr {$i*100}]
- set upr [expr {($i+10)*100}]
- puts "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
-}
-
-# 50 SELECTs on an LIKE comparison. There is no index so a full
-# table scan is required.
-#
-for {set i 0} {$i<50} {incr i} {
- puts "SELECT count(*), avg(b) FROM t1 WHERE c LIKE '%[number_name $i]%';"
-}
-
-# Create indices
-#
-puts {BEGIN;}
-puts {
- CREATE INDEX i1a ON t1(a);
- CREATE INDEX i1b ON t1(b);
- CREATE INDEX i1c ON t1(c);
-}
-puts {COMMIT;}
-
-# 5000 SELECTs on an integer comparison where the integer is
-# indexed.
-#
-set sql {}
-for {set i 0} {$i<5000} {incr i} {
- set lwr [expr {$i*100}]
- set upr [expr {($i+10)*100}]
- puts "SELECT count(*), avg(b) FROM t1 WHERE b>=$lwr AND b<$upr;"
-}
-
-# 100000 random SELECTs against rowid.
-#
-for {set i 1} {$i<=100000} {incr i} {
- set id [expr {int(rand()*50000)+1}]
- puts "SELECT c FROM t1 WHERE rowid=$id;"
-}
-
-# 100000 random SELECTs against a unique indexed column.
-#
-for {set i 1} {$i<=100000} {incr i} {
- set id [expr {int(rand()*50000)+1}]
- puts "SELECT c FROM t1 WHERE a=$id;"
-}
-
-# 50000 random SELECTs against an indexed column text column
-#
-set nt1c [llength $t1c_list]
-for {set i 0} {$i<50000} {incr i} {
- set r [expr {int(rand()*$nt1c)}]
- set c [lindex $t1c_list $i]
- puts "SELECT c FROM t1 WHERE c='$c';"
-}
-
-
-# Vacuum
-puts {VACUUM;}
-
-# 5000 updates of ranges where the field being compared is indexed.
-#
-puts {BEGIN;}
-for {set i 0} {$i<5000} {incr i} {
- set lwr [expr {$i*2}]
- set upr [expr {($i+1)*2}]
- puts "UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr;"
-}
-puts {COMMIT;}
-
-# 50000 single-row updates. An index is used to find the row quickly.
-#
-puts {BEGIN;}
-for {set i 0} {$i<50000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts "UPDATE t1 SET b=$r WHERE a=$i;"
-}
-puts {COMMIT;}
-
-# 1 big text update that touches every row in the table.
-#
-puts {
- UPDATE t1 SET c=a;
-}
-
-# Many individual text updates. Each row in the table is
-# touched through an index.
-#
-puts {BEGIN;}
-for {set i 1} {$i<=50000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts "UPDATE t1 SET c='[number_name $r]' WHERE a=$i;"
-}
-puts {COMMIT;}
-
-# Delete all content in a table.
-#
-puts {DELETE FROM t1;}
-
-# Copy one table into another
-#
-puts {INSERT INTO t1 SELECT * FROM t2;}
-
-# Delete all content in a table, one row at a time.
-#
-puts {DELETE FROM t1 WHERE 1;}
-
-# Refill the table yet again
-#
-puts {INSERT INTO t1 SELECT * FROM t2;}
-
-# Drop the table and recreate it without its indices.
-#
-puts {BEGIN;}
-puts {
- DROP TABLE t1;
- CREATE TABLE t1(a INTEGER, b INTEGER, c TEXT);
-}
-puts {COMMIT;}
-
-# Refill the table yet again. This copy should be faster because
-# there are no indices to deal with.
-#
-puts {INSERT INTO t1 SELECT * FROM t2;}
-
-# Select 20000 rows from the table at random.
-#
-puts {
- SELECT rowid FROM t1 ORDER BY random() LIMIT 20000;
-}
-
-# Delete 20000 random rows from the table.
-#
-puts {
- DELETE FROM t1 WHERE rowid IN
- (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000);
-}
-puts {SELECT count(*) FROM t1;}
-
-# Delete 20000 more rows at random from the table.
-#
-puts {
- DELETE FROM t1 WHERE rowid IN
- (SELECT rowid FROM t1 ORDER BY random() LIMIT 20000);
-}
-puts {SELECT count(*) FROM t1;}
diff --git a/tool/run-speed-test.sh b/tool/run-speed-test.sh
deleted file mode 100644
index 9c76465a26..0000000000
--- a/tool/run-speed-test.sh
+++ /dev/null
@@ -1,90 +0,0 @@
-#!/bin/bash
-#
-# This is a template for a script used for day-to-day size and
-# performance monitoring of SQLite. Typical usage:
-#
-# sh run-speed-test.sh trunk # Baseline measurement of trunk
-# sh run-speed-test.sh x1 # Measure some experimental change
-# fossil test-diff --tk cout-trunk.txt cout-x1.txt # View changes
-#
-# There are multiple output files, all with a base name given by
-# the first argument:
-#
-# summary-$BASE.txt # Copy of standard output
-# cout-$BASE.txt # cachegrind output
-# explain-$BASE.txt # EXPLAIN listings (only with --explain)
-#
-if test "$1" = ""
-then
- echo "Usage: $0 OUTPUTFILE [OPTIONS]"
- exit
-fi
-NAME=$1
-shift
-CC_OPTS="-DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_MEMSYS5"
-SPEEDTEST_OPTS="--shrink-memory --reprepare --heap 10000000 64"
-SIZE=5
-doExplain=0
-while test "$1" != ""; do
- case $1 in
- --reprepare)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --autovacuum)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --utf16be)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --stats)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --without-rowid)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --nomemstat)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --wal)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --journal wal"
- ;;
- --size)
- shift; SIZE=$1
- ;;
- --explain)
- doExplain=1
- ;;
- --heap)
- CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_MEMSYS5"
- shift;
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --heap $1 64"
- ;;
- *)
- CC_OPTS="$CC_OPTS $1"
- ;;
- esac
- shift
-done
-SPEEDTEST_OPTS="$SPEEDTEST_OPTS --size $SIZE"
-echo "NAME = $NAME" | tee summary-$NAME.txt
-echo "SPEEDTEST_OPTS = $SPEEDTEST_OPTS" | tee -a summary-$NAME.txt
-echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt
-rm -f cachegrind.out.* speedtest1 speedtest1.db sqlite3.o
-gcc -g -Os -Wall -I. $CC_OPTS -c sqlite3.c
-size sqlite3.o | tee -a summary-$NAME.txt
-if test $doExplain -eq 1; then
- gcc -g -Os -Wall -I. $CC_OPTS \
- -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
- ./shell.c ./sqlite3.c -o sqlite3 -ldl -lpthread
-fi
-SRC=./speedtest1.c
-gcc -g -Os -Wall -I. $CC_OPTS $SRC ./sqlite3.o -o speedtest1 -ldl -lpthread
-ls -l speedtest1 | tee -a summary-$NAME.txt
-valgrind --tool=cachegrind ./speedtest1 speedtest1.db \
- $SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt
-size sqlite3.o | tee -a summary-$NAME.txt
-wc sqlite3.c
-cg_anno.tcl cachegrind.out.* >cout-$NAME.txt
-if test $doExplain -eq 1; then
- ./speedtest1 --explain $SPEEDTEST_OPTS | ./sqlite3 >explain-$NAME.txt
-fi
diff --git a/tool/speed-check.sh b/tool/speed-check.sh
deleted file mode 100644
index fd122a12db..0000000000
--- a/tool/speed-check.sh
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/bin/bash
-#
-# This is a template for a script used for day-to-day size and
-# performance monitoring of SQLite. Typical usage:
-#
-# sh speed-check.sh trunk # Baseline measurement of trunk
-# sh speed-check.sh x1 # Measure some experimental change
-# fossil xdiff --tk cout-trunk.txt cout-x1.txt # View changes
-#
-# There are multiple output files, all with a base name given by
-# the first argument:
-#
-# summary-$BASE.txt # Copy of standard output
-# cout-$BASE.txt # cachegrind output
-# explain-$BASE.txt # EXPLAIN listings (only with --explain)
-#
-if test "$1" = ""
-then
- echo "Usage: $0 OUTPUTFILE [OPTIONS]"
- exit
-fi
-NAME=$1
-shift
-#CC_OPTS="-DSQLITE_ENABLE_RTREE -DSQLITE_ENABLE_MEMSYS5"
-CC_OPTS="-DSQLITE_ENABLE_MEMSYS5"
-CC=gcc
-SPEEDTEST_OPTS="--shrink-memory --reprepare --stats --heap 10000000 64"
-SIZE=5
-LEAN_OPTS="-DSQLITE_THREADSAFE=0"
-LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_MEMSTATUS=0"
-LEAN_OPTS="$LEAN_OPTS -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1"
-LEAN_OPTS="$LEAN_OPTS -DSQLITE_LIKE_DOESNT_MATCH_BLOBS"
-LEAN_OPTS="$LEAN_OPTS -DSQLITE_MAX_EXPR_DEPTH=0"
-LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DECLTYPE"
-LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_DEPRECATED"
-LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_PROGRESS_CALLBACK"
-LEAN_OPTS="$LEAN_OPTS -DSQLITE_OMIT_SHARED_CACHE"
-LEAN_OPTS="$LEAN_OPTS -DSQLITE_USE_ALLOCA"
-BASELINE="trunk"
-doExplain=0
-doCachegrind=1
-doVdbeProfile=0
-doWal=1
-doDiff=1
-while test "$1" != ""; do
- case $1 in
- --nodiff)
- doDiff=0
- ;;
- --reprepare)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --autovacuum)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --utf16be)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --stats)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --without-rowid)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --strict)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --nomemstat)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --multithread)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --singlethread)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --serialized)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS $1"
- ;;
- --temp)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --temp 6"
- ;;
- --legacy)
- doWal=0
- CC_OPTS="$CC_OPTS -DSPEEDTEST_OMIT_HASH"
- ;;
- --verify)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --verify"
- ;;
- --wal)
- doWal=1
- ;;
- --size)
- shift; SIZE=$1
- ;;
- --cachesize)
- shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --cachesize $1"
- ;;
- --stmtcache)
- shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --stmtcache $1"
- ;;
- --checkpoint)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --checkpoint"
- ;;
- --explain)
- doExplain=1
- ;;
- --vdbeprofile)
- rm -f vdbe_profile.out
- CC_OPTS="$CC_OPTS -DVDBE_PROFILE"
- doCachegrind=0
- doVdbeProfile=1
- ;;
- --lean)
- CC_OPTS="$CC_OPTS $LEAN_OPTS"
- ;;
- --clang)
- CC=clang
- ;;
- --icc)
- CC=/home/drh/intel/bin/icc
- ;;
- --gcc7)
- CC=gcc-7
- ;;
- --heap)
- CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_MEMSYS5"
- shift;
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --heap $1 64"
- ;;
- --lookaside)
- shift;
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --lookaside $1 $2"
- shift;
- ;;
- --repeat)
- CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_RCACHE"
- shift;
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --repeat $1"
- ;;
- --mmap)
- shift;
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --mmap $1"
- ;;
- --rtree)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset rtree"
- CC_OPTS="$CC_OPTS -DSQLITE_ENABLE_RTREE"
- ;;
- --persist)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --persist"
- ;;
- --orm)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset orm"
- ;;
- --cte)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset cte"
- ;;
- --fp)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset fp"
- ;;
- --parsenumber)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --testset parsenumber"
- ;;
- --stmtscanstatus)
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --stmtscanstatus"
- ;;
- -*)
- CC_OPTS="$CC_OPTS $1"
- ;;
- *)
- BASELINE=$1
- ;;
- esac
- shift
-done
-if test $doWal -eq 1; then
- SPEEDTEST_OPTS="$SPEEDTEST_OPTS --journal wal"
-fi
-SPEEDTEST_OPTS="$SPEEDTEST_OPTS --size $SIZE"
-echo "NAME = $NAME" | tee summary-$NAME.txt
-echo "SPEEDTEST_OPTS = $SPEEDTEST_OPTS" | tee -a summary-$NAME.txt
-echo "CC_OPTS = $CC_OPTS" | tee -a summary-$NAME.txt
-rm -f cachegrind.out.* speedtest1 speedtest1.db sqlite3.o
-if test $doVdbeProfile -eq 1; then
- rm -f vdbe_profile.out
-fi
-$CC -g -Os -Wall -I. $CC_OPTS -c sqlite3.c
-size sqlite3.o | tee -a summary-$NAME.txt
-if test $doExplain -eq 1; then
- $CC -g -Os -Wall -I. $CC_OPTS \
- -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
- ./shell.c ./sqlite3.c -o sqlite3 -ldl -lpthread
-fi
-SRC=./speedtest1.c
-$CC -g -Os -Wall -I. $CC_OPTS $SRC ./sqlite3.o -o speedtest1 -ldl -lpthread
-ls -l speedtest1 | tee -a summary-$NAME.txt
-if test $doCachegrind -eq 1; then
- valgrind --tool=cachegrind ./speedtest1 speedtest1.db \
- $SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt
-else
- ./speedtest1 speedtest1.db $SPEEDTEST_OPTS 2>&1 | tee -a summary-$NAME.txt
-fi
-size sqlite3.o | tee -a summary-$NAME.txt
-wc sqlite3.c
-if test $doCachegrind -eq 1; then
- cg_anno.tcl cachegrind.out.* >cout-$NAME.txt
- echo '*****************************************************' >>cout-$NAME.txt
- sed 's/^[0-9=-]\{9\}/==00000==/' summary-$NAME.txt >>cout-$NAME.txt
-fi
-if test $doExplain -eq 1; then
- ./speedtest1 --explain $SPEEDTEST_OPTS | ./sqlite3 >explain-$NAME.txt
-fi
-if test $doVdbeProfile -eq 1; then
- tclsh ../sqlite/tool/vdbe_profile.tcl >vdbeprofile-$NAME.txt
- open vdbeprofile-$NAME.txt
-fi
-if test "$NAME" != "$BASELINE" -a $doVdbeProfile -ne 1 -a $doDiff -ne 0; then
- fossil test-diff --tk -c 20 cout-$BASELINE.txt cout-$NAME.txt
-fi
diff --git a/tool/speedtest.tcl b/tool/speedtest.tcl
deleted file mode 100644
index ef39dc5461..0000000000
--- a/tool/speedtest.tcl
+++ /dev/null
@@ -1,275 +0,0 @@
-#!/usr/bin/tclsh
-#
-# Run this script using TCLSH to do a speed comparison between
-# various versions of SQLite and PostgreSQL and MySQL
-#
-
-# Run a test
-#
-set cnt 1
-proc runtest {title} {
- global cnt
- set sqlfile test$cnt.sql
- puts "Test $cnt: $title
"
- incr cnt
- set fd [open $sqlfile r]
- set sql [string trim [read $fd [file size $sqlfile]]]
- close $fd
- set sx [split $sql \n]
- set n [llength $sx]
- if {$n>8} {
- set sql {}
- for {set i 0} {$i<3} {incr i} {append sql [lindex $sx $i]
\n}
- append sql "... [expr {$n-6}] lines omitted
\n"
- for {set i [expr {$n-3}]} {$i<$n} {incr i} {
- append sql [lindex $sx $i]
\n
- }
- } else {
- regsub -all \n [string trim $sql]
sql
- }
- puts ""
- puts "$sql"
- puts "
"
- set format {%s | %.3f |
}
- set delay 1000
-# exec sync; after $delay;
-# set t [time "exec psql drh <$sqlfile" 1]
-# set t [expr {[lindex $t 0]/1000000.0}]
-# puts [format $format PostgreSQL: $t]
- exec sync; after $delay;
- set t [time "exec mysql -f drh <$sqlfile" 1]
- set t [expr {[lindex $t 0]/1000000.0}]
- puts [format $format MySQL: $t]
-# set t [time "exec ./sqlite232 s232.db <$sqlfile" 1]
-# set t [expr {[lindex $t 0]/1000000.0}]
-# puts [format $format {SQLite 2.3.2:} $t]
-# set t [time "exec ./sqlite-100 s100.db <$sqlfile" 1]
-# set t [expr {[lindex $t 0]/1000000.0}]
-# puts [format $format {SQLite 2.4 (cache=100):} $t]
- exec sync; after $delay;
- set t [time "exec ./sqlite248 s2k.db <$sqlfile" 1]
- set t [expr {[lindex $t 0]/1000000.0}]
- puts [format $format {SQLite 2.4.8:} $t]
- exec sync; after $delay;
- set t [time "exec ./sqlite248 sns.db <$sqlfile" 1]
- set t [expr {[lindex $t 0]/1000000.0}]
- puts [format $format {SQLite 2.4.8 (nosync):} $t]
- exec sync; after $delay;
- set t [time "exec ./sqlite2412 s2kb.db <$sqlfile" 1]
- set t [expr {[lindex $t 0]/1000000.0}]
- puts [format $format {SQLite 2.4.12:} $t]
- exec sync; after $delay;
- set t [time "exec ./sqlite2412 snsb.db <$sqlfile" 1]
- set t [expr {[lindex $t 0]/1000000.0}]
- puts [format $format {SQLite 2.4.12 (nosync):} $t]
-# set t [time "exec ./sqlite-t1 st1.db <$sqlfile" 1]
-# set t [expr {[lindex $t 0]/1000000.0}]
-# puts [format $format {SQLite 2.4 (test):} $t]
- puts "
"
-}
-
-# Initialize the environment
-#
-expr srand(1)
-catch {exec /bin/sh -c {rm -f s*.db}}
-set fd [open clear.sql w]
-puts $fd {
- drop table t1;
- drop table t2;
-}
-close $fd
-catch {exec psql drh =1000} {
- set txt "[number_name [expr {$n/1000}]] thousand"
- set n [expr {$n%1000}]
- } else {
- set txt {}
- }
- if {$n>=100} {
- append txt " [lindex $::ones [expr {$n/100}]] hundred"
- set n [expr {$n%100}]
- }
- if {$n>=20} {
- append txt " [lindex $::tens [expr {$n/10}]]"
- set n [expr {$n%10}]
- }
- if {$n>0} {
- append txt " [lindex $::ones $n]"
- }
- set txt [string trim $txt]
- if {$txt==""} {set txt zero}
- return $txt
-}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd "CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100));"
-for {set i 1} {$i<=1000} {incr i} {
- set r [expr {int(rand()*100000)}]
- puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
-}
-close $fd
-runtest {1000 INSERTs}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-puts $fd "CREATE TABLE t2(a INTEGER, b INTEGER, c VARCHAR(100));"
-for {set i 1} {$i<=25000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts $fd "INSERT INTO t2 VALUES($i,$r,'[number_name $r]');"
-}
-puts $fd "COMMIT;"
-close $fd
-runtest {25000 INSERTs in a transaction}
-
-
-
-set fd [open test$cnt.sql w]
-for {set i 0} {$i<100} {incr i} {
- set lwr [expr {$i*100}]
- set upr [expr {($i+10)*100}]
- puts $fd "SELECT count(*), avg(b) FROM t2 WHERE b>=$lwr AND b<$upr;"
-}
-close $fd
-runtest {100 SELECTs without an index}
-
-
-
-set fd [open test$cnt.sql w]
-for {set i 1} {$i<=100} {incr i} {
- puts $fd "SELECT count(*), avg(b) FROM t2 WHERE c LIKE '%[number_name $i]%';"
-}
-close $fd
-runtest {100 SELECTs on a string comparison}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd {CREATE INDEX i2a ON t2(a);}
-puts $fd {CREATE INDEX i2b ON t2(b);}
-close $fd
-runtest {Creating an index}
-
-
-
-set fd [open test$cnt.sql w]
-for {set i 0} {$i<5000} {incr i} {
- set lwr [expr {$i*100}]
- set upr [expr {($i+1)*100}]
- puts $fd "SELECT count(*), avg(b) FROM t2 WHERE b>=$lwr AND b<$upr;"
-}
-close $fd
-runtest {5000 SELECTs with an index}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-for {set i 0} {$i<1000} {incr i} {
- set lwr [expr {$i*10}]
- set upr [expr {($i+1)*10}]
- puts $fd "UPDATE t1 SET b=b*2 WHERE a>=$lwr AND a<$upr;"
-}
-puts $fd "COMMIT;"
-close $fd
-runtest {1000 UPDATEs without an index}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-for {set i 1} {$i<=25000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts $fd "UPDATE t2 SET b=$r WHERE a=$i;"
-}
-puts $fd "COMMIT;"
-close $fd
-runtest {25000 UPDATEs with an index}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-for {set i 1} {$i<=25000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts $fd "UPDATE t2 SET c='[number_name $r]' WHERE a=$i;"
-}
-puts $fd "COMMIT;"
-close $fd
-runtest {25000 text UPDATEs with an index}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-puts $fd "INSERT INTO t1 SELECT * FROM t2;"
-puts $fd "INSERT INTO t2 SELECT * FROM t1;"
-puts $fd "COMMIT;"
-close $fd
-runtest {INSERTs from a SELECT}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd {DELETE FROM t2 WHERE c LIKE '%fifty%';}
-close $fd
-runtest {DELETE without an index}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd {DELETE FROM t2 WHERE a>10 AND a<20000;}
-close $fd
-runtest {DELETE with an index}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd {INSERT INTO t2 SELECT * FROM t1;}
-close $fd
-runtest {A big INSERT after a big DELETE}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd {BEGIN;}
-puts $fd {DELETE FROM t1;}
-for {set i 1} {$i<=3000} {incr i} {
- set r [expr {int(rand()*100000)}]
- puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
-}
-puts $fd {COMMIT;}
-close $fd
-runtest {A big DELETE followed by many small INSERTs}
-
-
-
-set fd [open test$cnt.sql w]
-puts $fd {DROP TABLE t1;}
-puts $fd {DROP TABLE t2;}
-close $fd
-runtest {DROP TABLE}
diff --git a/tool/speedtest16.c b/tool/speedtest16.c
deleted file mode 100644
index 993cc19268..0000000000
--- a/tool/speedtest16.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
-** Performance test for SQLite.
-**
-** This program reads ASCII text from a file named on the command-line.
-** It converts each SQL statement into UTF16 and submits it to SQLite
-** for evaluation. A new UTF16 database is created at the beginning of
-** the program. All statements are timed using the high-resolution timer
-** built into Intel-class processors.
-**
-** To compile this program, first compile the SQLite library separately
-** will full optimizations. For example:
-**
-** gcc -c -O6 -DSQLITE_THREADSAFE=0 sqlite3.c
-**
-** Then link against this program. But to do optimize this program
-** because that defeats the hi-res timer.
-**
-** gcc speedtest16.c sqlite3.o -ldl -I../src
-**
-** Then run this program with a single argument which is the name of
-** a file containing SQL script that you want to test:
-**
-** ./a.out database.db test.sql
-*/
-#include
-#include
-#include
-#include
-#include
-#include "sqlite3.h"
-
-#define ISSPACE(X) isspace((unsigned char)(X))
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-#include "hwtime.h"
-
-/*
-** Convert a zero-terminated ASCII string into a zero-terminated
-** UTF-16le string. Memory to hold the returned string comes
-** from malloc() and should be freed by the caller.
-*/
-static void *asciiToUtf16le(const char *z){
- int n = strlen(z);
- char *z16;
- int i, j;
-
- z16 = malloc( n*2 + 2 );
- for(i=j=0; i<=n; i++){
- z16[j++] = z[i];
- z16[j++] = 0;
- }
- return (void*)z16;
-}
-
-/*
-** Timers
-*/
-static sqlite_uint64 prepTime = 0;
-static sqlite_uint64 runTime = 0;
-static sqlite_uint64 finalizeTime = 0;
-
-/*
-** Prepare and run a single statement of SQL.
-*/
-static void prepareAndRun(sqlite3 *db, const char *zSql){
- void *utf16;
- sqlite3_stmt *pStmt;
- const void *stmtTail;
- sqlite_uint64 iStart, iElapse;
- int rc;
-
- printf("****************************************************************\n");
- printf("SQL statement: [%s]\n", zSql);
- utf16 = asciiToUtf16le(zSql);
- iStart = sqlite3Hwtime();
- rc = sqlite3_prepare16_v2(db, utf16, -1, &pStmt, &stmtTail);
- iElapse = sqlite3Hwtime() - iStart;
- prepTime += iElapse;
- printf("sqlite3_prepare16_v2() returns %d in %llu cycles\n", rc, iElapse);
- if( rc==SQLITE_OK ){
- int nRow = 0;
- iStart = sqlite3Hwtime();
- while( (rc=sqlite3_step(pStmt))==SQLITE_ROW ){ nRow++; }
- iElapse = sqlite3Hwtime() - iStart;
- runTime += iElapse;
- printf("sqlite3_step() returns %d after %d rows in %llu cycles\n",
- rc, nRow, iElapse);
- iStart = sqlite3Hwtime();
- rc = sqlite3_finalize(pStmt);
- iElapse = sqlite3Hwtime() - iStart;
- finalizeTime += iElapse;
- printf("sqlite3_finalize() returns %d in %llu cycles\n", rc, iElapse);
- }
- free(utf16);
-}
-
-int main(int argc, char **argv){
- void *utf16;
- sqlite3 *db;
- int rc;
- int nSql;
- char *zSql;
- int i, j;
- FILE *in;
- sqlite_uint64 iStart, iElapse;
- sqlite_uint64 iSetup = 0;
- int nStmt = 0;
- int nByte = 0;
-
- if( argc!=3 ){
- fprintf(stderr, "Usage: %s FILENAME SQL-SCRIPT\n"
- "Runs SQL-SCRIPT as UTF16 against a UTF16 database\n",
- argv[0]);
- exit(1);
- }
- in = fopen(argv[2], "r");
- fseek(in, 0L, SEEK_END);
- nSql = ftell(in);
- zSql = malloc( nSql+1 );
- fseek(in, 0L, SEEK_SET);
- nSql = fread(zSql, 1, nSql, in);
- zSql[nSql] = 0;
-
- printf("SQLite version: %d\n", sqlite3_libversion_number());
- unlink(argv[1]);
- utf16 = asciiToUtf16le(argv[1]);
- iStart = sqlite3Hwtime();
- rc = sqlite3_open16(utf16, &db);
- iElapse = sqlite3Hwtime() - iStart;
- iSetup = iElapse;
- printf("sqlite3_open16() returns %d in %llu cycles\n", rc, iElapse);
- free(utf16);
- for(i=j=0; jTest $cnt: $title"
- incr cnt
- set fd [open $sqlfile r]
- set sql [string trim [read $fd [file size $sqlfile]]]
- close $fd
- set sx [split $sql \n]
- set n [llength $sx]
- if {$n>8} {
- set sql {}
- for {set i 0} {$i<3} {incr i} {append sql [lindex $sx $i]
\n}
- append sql "... [expr {$n-6}] lines omitted
\n"
- for {set i [expr {$n-3}]} {$i<$n} {incr i} {
- append sql [lindex $sx $i]
\n
- }
- } else {
- regsub -all \n [string trim $sql]
sql
- }
- puts ""
- puts "$sql"
- puts "
"
- set format {%s | %.3f |
}
- set delay 1000
- exec sync; after $delay;
- set t [time "exec psql drh <$sqlfile" 1]
- set t [expr {[lindex $t 0]/1000000.0}]
- puts [format $format PostgreSQL: $t]
- exec sync; after $delay;
- set t [time "exec mysql -f drh <$sqlfile" 1]
- set t [expr {[lindex $t 0]/1000000.0}]
- puts [format $format MySQL: $t]
-# set t [time "exec ./sqlite232 s232.db <$sqlfile" 1]
-# set t [expr {[lindex $t 0]/1000000.0}]
-# puts [format $format {SQLite 2.3.2:} $t]
-# set t [time "exec ./sqlite-100 s100.db <$sqlfile" 1]
-# set t [expr {[lindex $t 0]/1000000.0}]
-# puts [format $format {SQLite 2.4 (cache=100):} $t]
- exec sync; after $delay;
- set t [time "exec ./sqlite240 s2k.db <$sqlfile" 1]
- set t [expr {[lindex $t 0]/1000000.0}]
- puts [format $format {SQLite 2.4:} $t]
- exec sync; after $delay;
- set t [time "exec ./sqlite240 sns.db <$sqlfile" 1]
- set t [expr {[lindex $t 0]/1000000.0}]
- puts [format $format {SQLite 2.4 (nosync):} $t]
-# set t [time "exec ./sqlite-t1 st1.db <$sqlfile" 1]
-# set t [expr {[lindex $t 0]/1000000.0}]
-# puts [format $format {SQLite 2.4 (test):} $t]
- puts "
"
-}
-
-# Initialize the environment
-#
-expr srand(1)
-catch {exec /bin/sh -c {rm -f s*.db}}
-set fd [open clear.sql w]
-puts $fd {
- drop table t1;
- drop table t2;
-}
-close $fd
-catch {exec psql drh =1000} {
- set txt "[number_name [expr {$n/1000}]] thousand"
- set n [expr {$n%1000}]
- } else {
- set txt {}
- }
- if {$n>=100} {
- append txt " [lindex $::ones [expr {$n/100}]] hundred"
- set n [expr {$n%100}]
- }
- if {$n>=20} {
- append txt " [lindex $::tens [expr {$n/10}]]"
- set n [expr {$n%10}]
- }
- if {$n>0} {
- append txt " [lindex $::ones $n]"
- }
- set txt [string trim $txt]
- if {$txt==""} {set txt zero}
- return $txt
-}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-puts $fd "CREATE TABLE t1(a INTEGER, b INTEGER, c VARCHAR(100));"
-for {set i 1} {$i<=25000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
-}
-puts $fd "COMMIT;"
-close $fd
-runtest {25000 INSERTs in a transaction}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "DELETE FROM t1;"
-close $fd
-runtest {DELETE everything}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-for {set i 1} {$i<=25000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
-}
-puts $fd "COMMIT;"
-close $fd
-runtest {25000 INSERTs in a transaction}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "DELETE FROM t1;"
-close $fd
-runtest {DELETE everything}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-for {set i 1} {$i<=25000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
-}
-puts $fd "COMMIT;"
-close $fd
-runtest {25000 INSERTs in a transaction}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "DELETE FROM t1;"
-close $fd
-runtest {DELETE everything}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-for {set i 1} {$i<=25000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
-}
-puts $fd "COMMIT;"
-close $fd
-runtest {25000 INSERTs in a transaction}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "DELETE FROM t1;"
-close $fd
-runtest {DELETE everything}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "BEGIN;"
-for {set i 1} {$i<=25000} {incr i} {
- set r [expr {int(rand()*500000)}]
- puts $fd "INSERT INTO t1 VALUES($i,$r,'[number_name $r]');"
-}
-puts $fd "COMMIT;"
-close $fd
-runtest {25000 INSERTs in a transaction}
-
-
-set fd [open test$cnt.sql w]
-puts $fd "DELETE FROM t1;"
-close $fd
-runtest {DELETE everything}
-
-
-set fd [open test$cnt.sql w]
-puts $fd {DROP TABLE t1;}
-close $fd
-runtest {DROP TABLE}
diff --git a/tool/speedtest8.c b/tool/speedtest8.c
deleted file mode 100644
index 051fc89819..0000000000
--- a/tool/speedtest8.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
-** Performance test for SQLite.
-**
-** This program reads ASCII text from a file named on the command-line
-** and submits that text to SQLite for evaluation. A new database
-** is created at the beginning of the program. All statements are
-** timed using the high-resolution timer built into Intel-class processors.
-**
-** To compile this program, first compile the SQLite library separately
-** will full optimizations. For example:
-**
-** gcc -c -O6 -DSQLITE_THREADSAFE=0 sqlite3.c
-**
-** Then link against this program. But to do optimize this program
-** because that defeats the hi-res timer.
-**
-** gcc speedtest8.c sqlite3.o -ldl -I../src
-**
-** Then run this program with a single argument which is the name of
-** a file containing SQL script that you want to test:
-**
-** ./a.out test.db test.sql
-*/
-#include
-#include
-#include
-#include
-#include
-
-#if defined(_MSC_VER)
-#include
-#else
-#include
-#include
-#include
-#endif
-
-#include "sqlite3.h"
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-#include "hwtime.h"
-
-/*
-** Timers
-*/
-static sqlite_uint64 prepTime = 0;
-static sqlite_uint64 runTime = 0;
-static sqlite_uint64 finalizeTime = 0;
-
-/*
-** Prepare and run a single statement of SQL.
-*/
-static void prepareAndRun(sqlite3 *db, const char *zSql, int bQuiet){
- sqlite3_stmt *pStmt;
- const char *stmtTail;
- sqlite_uint64 iStart, iElapse;
- int rc;
-
- if (!bQuiet){
- printf("***************************************************************\n");
- }
- if (!bQuiet) printf("SQL statement: [%s]\n", zSql);
- iStart = sqlite3Hwtime();
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &stmtTail);
- iElapse = sqlite3Hwtime() - iStart;
- prepTime += iElapse;
- if (!bQuiet){
- printf("sqlite3_prepare_v2() returns %d in %llu cycles\n", rc, iElapse);
- }
- if( rc==SQLITE_OK ){
- int nRow = 0;
- iStart = sqlite3Hwtime();
- while( (rc=sqlite3_step(pStmt))==SQLITE_ROW ){ nRow++; }
- iElapse = sqlite3Hwtime() - iStart;
- runTime += iElapse;
- if (!bQuiet){
- printf("sqlite3_step() returns %d after %d rows in %llu cycles\n",
- rc, nRow, iElapse);
- }
- iStart = sqlite3Hwtime();
- rc = sqlite3_finalize(pStmt);
- iElapse = sqlite3Hwtime() - iStart;
- finalizeTime += iElapse;
- if (!bQuiet){
- printf("sqlite3_finalize() returns %d in %llu cycles\n", rc, iElapse);
- }
- }
-}
-
-int main(int argc, char **argv){
- sqlite3 *db;
- int rc;
- int nSql;
- char *zSql;
- int i, j;
- FILE *in;
- sqlite_uint64 iStart, iElapse;
- sqlite_uint64 iSetup = 0;
- int nStmt = 0;
- int nByte = 0;
- const char *zArgv0 = argv[0];
- int bQuiet = 0;
-#if !defined(_MSC_VER)
- struct tms tmsStart, tmsEnd;
- clock_t clkStart, clkEnd;
-#endif
-
-#ifdef HAVE_OSINST
- extern sqlite3_vfs *sqlite3_instvfs_binarylog(char *, char *, char *);
- extern void sqlite3_instvfs_destroy(sqlite3_vfs *);
- sqlite3_vfs *pVfs = 0;
-#endif
-
- while (argc>3)
- {
-#ifdef HAVE_OSINST
- if( argc>4 && (strcmp(argv[1], "-log")==0) ){
- pVfs = sqlite3_instvfs_binarylog("oslog", 0, argv[2]);
- sqlite3_vfs_register(pVfs, 1);
- argv += 2;
- argc -= 2;
- continue;
- }
-#endif
-
- /*
- ** Increasing the priority slightly above normal can help with
- ** repeatability of testing. Note that with Cygwin, -5 equates
- ** to "High", +5 equates to "Low", and anything in between
- ** equates to "Normal".
- */
- if( argc>4 && (strcmp(argv[1], "-priority")==0) ){
-#if defined(_MSC_VER)
- int new_priority = atoi(argv[2]);
- if(!SetPriorityClass(GetCurrentProcess(),
- (new_priority<=-5) ? HIGH_PRIORITY_CLASS :
- (new_priority<=0) ? ABOVE_NORMAL_PRIORITY_CLASS :
- (new_priority==0) ? NORMAL_PRIORITY_CLASS :
- (new_priority<5) ? BELOW_NORMAL_PRIORITY_CLASS :
- IDLE_PRIORITY_CLASS)){
- printf ("error setting priority\n");
- exit(2);
- }
-#else
- struct sched_param myParam;
- sched_getparam(0, &myParam);
- printf ("Current process priority is %d.\n", (int)myParam.sched_priority);
- myParam.sched_priority = atoi(argv[2]);
- printf ("Setting process priority to %d.\n", (int)myParam.sched_priority);
- if (sched_setparam (0, &myParam) != 0){
- printf ("error setting priority\n");
- exit(2);
- }
-#endif
- argv += 2;
- argc -= 2;
- continue;
- }
-
- if( argc>3 && strcmp(argv[1], "-quiet")==0 ){
- bQuiet = -1;
- argv++;
- argc--;
- continue;
- }
-
- break;
- }
-
- if( argc!=3 ){
- fprintf(stderr, "Usage: %s [options] FILENAME SQL-SCRIPT\n"
- "Runs SQL-SCRIPT against a UTF8 database\n"
- "\toptions:\n"
-#ifdef HAVE_OSINST
- "\t-log \n"
-#endif
- "\t-priority : set priority of task\n"
- "\t-quiet : only display summary results\n",
- zArgv0);
- exit(1);
- }
-
- in = fopen(argv[2], "r");
- fseek(in, 0L, SEEK_END);
- nSql = ftell(in);
- zSql = malloc( nSql+1 );
- fseek(in, 0L, SEEK_SET);
- nSql = fread(zSql, 1, nSql, in);
- zSql[nSql] = 0;
-
- printf("SQLite version: %d\n", sqlite3_libversion_number());
- unlink(argv[1]);
-#if !defined(_MSC_VER)
- clkStart = times(&tmsStart);
-#endif
- iStart = sqlite3Hwtime();
- rc = sqlite3_open(argv[1], &db);
- iElapse = sqlite3Hwtime() - iStart;
- iSetup = iElapse;
- if (!bQuiet) printf("sqlite3_open() returns %d in %llu cycles\n", rc, iElapse);
- for(i=j=0; j=6 && memcmp(&zSql[i], ".crash",6)==0 ) exit(1);
- nStmt++;
- nByte += n;
- prepareAndRun(db, &zSql[i], bQuiet);
- }
- zSql[j] = ';';
- i = j+1;
- }
- }
- }
- iStart = sqlite3Hwtime();
- sqlite3_close(db);
- iElapse = sqlite3Hwtime() - iStart;
-#if !defined(_MSC_VER)
- clkEnd = times(&tmsEnd);
-#endif
- iSetup += iElapse;
- if (!bQuiet) printf("sqlite3_close() returns in %llu cycles\n", iElapse);
-
- printf("\n");
- printf("Statements run: %15d stmts\n", nStmt);
- printf("Bytes of SQL text: %15d bytes\n", nByte);
- printf("Total prepare time: %15llu cycles\n", prepTime);
- printf("Total run time: %15llu cycles\n", runTime);
- printf("Total finalize time: %15llu cycles\n", finalizeTime);
- printf("Open/Close time: %15llu cycles\n", iSetup);
- printf("Total time: %15llu cycles\n",
- prepTime + runTime + finalizeTime + iSetup);
-
-#if !defined(_MSC_VER)
- printf("\n");
- printf("Total user CPU time: %15.3g secs\n", (tmsEnd.tms_utime - tmsStart.tms_utime)/(double)CLOCKS_PER_SEC );
- printf("Total system CPU time: %15.3g secs\n", (tmsEnd.tms_stime - tmsStart.tms_stime)/(double)CLOCKS_PER_SEC );
- printf("Total real time: %15.3g secs\n", (clkEnd -clkStart)/(double)CLOCKS_PER_SEC );
-#endif
-
-#ifdef HAVE_OSINST
- if( pVfs ){
- sqlite3_instvfs_destroy(pVfs);
- printf("vfs log written to %s\n", argv[0]);
- }
-#endif
-
- return 0;
-}
diff --git a/tool/speedtest8inst1.c b/tool/speedtest8inst1.c
deleted file mode 100644
index ceaeca0f16..0000000000
--- a/tool/speedtest8inst1.c
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
-** Performance test for SQLite.
-**
-** This program reads ASCII text from a file named on the command-line
-** and submits that text to SQLite for evaluation. A new database
-** is created at the beginning of the program. All statements are
-** timed using the high-resolution timer built into Intel-class processors.
-**
-** To compile this program, first compile the SQLite library separately
-** will full optimizations. For example:
-**
-** gcc -c -O6 -DSQLITE_THREADSAFE=0 sqlite3.c
-**
-** Then link against this program. But to do optimize this program
-** because that defeats the hi-res timer.
-**
-** gcc speedtest8.c sqlite3.o -ldl -I../src
-**
-** Then run this program with a single argument which is the name of
-** a file containing SQL script that you want to test:
-**
-** ./a.out test.db test.sql
-*/
-#include
-#include
-#include
-#include
-#include
-#include
-#include "sqlite3.h"
-
-#define ISSPACE(X) isspace((unsigned char)(X))
-
-#include "test_osinst.c"
-
-/*
-** Prepare and run a single statement of SQL.
-*/
-static void prepareAndRun(sqlite3_vfs *pInstVfs, sqlite3 *db, const char *zSql){
- sqlite3_stmt *pStmt;
- const char *stmtTail;
- int rc;
- char zMessage[1024];
- zMessage[1023] = '\0';
-
- sqlite3_uint64 iTime;
-
- sqlite3_snprintf(1023, zMessage, "sqlite3_prepare_v2: %s", zSql);
- sqlite3_instvfs_binarylog_marker(pInstVfs, zMessage);
-
- iTime = sqlite3Hwtime();
- rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &stmtTail);
- iTime = sqlite3Hwtime() - iTime;
- sqlite3_instvfs_binarylog_call(pInstVfs,BINARYLOG_PREPARE_V2,iTime,rc,zSql);
-
- if( rc==SQLITE_OK ){
- int nRow = 0;
-
- sqlite3_snprintf(1023, zMessage, "sqlite3_step loop: %s", zSql);
- sqlite3_instvfs_binarylog_marker(pInstVfs, zMessage);
- iTime = sqlite3Hwtime();
- while( (rc=sqlite3_step(pStmt))==SQLITE_ROW ){ nRow++; }
- iTime = sqlite3Hwtime() - iTime;
- sqlite3_instvfs_binarylog_call(pInstVfs, BINARYLOG_STEP, iTime, rc, zSql);
-
- sqlite3_snprintf(1023, zMessage, "sqlite3_finalize: %s", zSql);
- sqlite3_instvfs_binarylog_marker(pInstVfs, zMessage);
- iTime = sqlite3Hwtime();
- rc = sqlite3_finalize(pStmt);
- iTime = sqlite3Hwtime() - iTime;
- sqlite3_instvfs_binarylog_call(pInstVfs, BINARYLOG_FINALIZE, iTime, rc, zSql);
- }
-}
-
-static int stringcompare(const char *zLeft, const char *zRight){
- int ii;
- for(ii=0; zLeft[ii] && zRight[ii]; ii++){
- if( zLeft[ii]!=zRight[ii] ) return 0;
- }
- return( zLeft[ii]==zRight[ii] );
-}
-
-static char *readScriptFile(const char *zFile, int *pnScript){
- sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
- sqlite3_file *p;
- int rc;
- sqlite3_int64 nByte;
- char *zData = 0;
- int flags = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_DB;
-
- p = (sqlite3_file *)malloc(pVfs->szOsFile);
- rc = pVfs->xOpen(pVfs, zFile, p, flags, &flags);
- if( rc!=SQLITE_OK ){
- goto error_out;
- }
-
- rc = p->pMethods->xFileSize(p, &nByte);
- if( rc!=SQLITE_OK ){
- goto close_out;
- }
-
- zData = (char *)malloc(nByte+1);
- rc = p->pMethods->xRead(p, zData, nByte, 0);
- if( rc!=SQLITE_OK ){
- goto close_out;
- }
- zData[nByte] = '\0';
-
- p->pMethods->xClose(p);
- free(p);
- *pnScript = nByte;
- return zData;
-
-close_out:
- p->pMethods->xClose(p);
-
-error_out:
- free(p);
- free(zData);
- return 0;
-}
-
-int main(int argc, char **argv){
-
- const char zUsageMsg[] =
- "Usage: %s options...\n"
- " where available options are:\n"
- "\n"
- " -db DATABASE-FILE (database file to operate on)\n"
- " -script SCRIPT-FILE (script file to read sql from)\n"
- " -log LOG-FILE (log file to create)\n"
- " -logdata (log all data to log file)\n"
- "\n"
- " Options -db, -script and -log are compulsory\n"
- "\n"
- ;
-
- const char *zDb = 0;
- const char *zScript = 0;
- const char *zLog = 0;
- int logdata = 0;
-
- int ii;
- int i, j;
- int rc;
-
- sqlite3_vfs *pInstVfs; /* Instrumentation VFS */
-
- char *zSql = 0;
- int nSql;
-
- sqlite3 *db;
-
- for(ii=1; iiiSize==0 ) HashInit(pCx, 160);
+ if( eType==SQLITE_BLOB ){
+ HashUpdate(pCx, sqlite3_value_blob(argv[0]), nByte);
+ }else{
+ HashUpdate(pCx, sqlite3_value_text(argv[0]), nByte);
+ }
+}
+static void agghashFinal(sqlite3_context *context){
+ HashContext *pCx = (HashContext*)sqlite3_aggregate_context(context, 0);
+ if( pCx ){
+ sqlite3_result_blob(context, HashFinal(pCx), 160/8, SQLITE_TRANSIENT);
+ }
+}
+
/* Register the hash function */
static int hashRegister(sqlite3 *db){
- return sqlite3_create_function(db, "hash", 1,
+ int rc;
+ rc = sqlite3_create_function(db, "hash", 1,
SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
0, hashFunc, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "agghash", 1,
+ SQLITE_UTF8 | SQLITE_INNOCUOUS | SQLITE_DETERMINISTIC,
+ 0, 0, agghashStep, agghashFinal);
+ }
+ return rc;
}
/* End of the hashing logic
@@ -836,6 +934,25 @@ static void logError(SQLiteRsync *p, const char *zFormat, ...){
p->nErr++;
}
+/*
+** Append text to the debugging mesage file, if an that file is
+** specified.
+*/
+static void debugMessage(SQLiteRsync *p, const char *zFormat, ...){
+ if( p->zDebugFile ){
+ if( p->pDebug==0 ){
+ p->pDebug = fopen(p->zDebugFile, "wb");
+ }
+ if( p->pDebug ){
+ va_list ap;
+ va_start(ap, zFormat);
+ vfprintf(p->pDebug, zFormat, ap);
+ va_end(ap);
+ fflush(p->pDebug);
+ }
+ }
+}
+
/* Read a single big-endian 32-bit unsigned integer from the input
** stream. Return 0 on success and 1 if there are any errors.
@@ -1190,6 +1307,13 @@ static void closeDb(SQLiteRsync *p){
** nPage, and szPage. Then enter a loop responding to message from
** the replica:
**
+** REPLICA_BEGIN iProtocol
+**
+** An optional message sent by the replica in response to the
+** prior ORIGIN_BEGIN with a counter-proposal for the protocol
+** level. If seen, try to reduce the protocol level to what is
+** requested and send a new ORGIN_BEGIN.
+**
** REPLICA_ERROR size text
**
** Report an error from the replica and quit
@@ -1200,30 +1324,42 @@ static void closeDb(SQLiteRsync *p){
**
** REPLICA_HASH hash
**
-** The argument is the 20-byte SHA1 hash for the next page
-** page hashes appear in sequential order with no gaps.
+** The argument is the 20-byte SHA1 hash for the next page or
+** block of pages. Hashes appear in sequential order with no gaps,
+** unless there is an intervening REPLICA_CONFIG message.
+**
+** REPLICA_CONFIG pgno cnt
+**
+** Set counters used by REPLICA_HASH. The next hash will start
+** on page pgno and all subsequent hashes will cover cnt pages
+** each. Note that for a multi-page hash, the hash value is
+** actually a hash of the individual page hashes.
**
** REPLICA_READY
**
** The replica has sent all the hashes that it intends to send.
** This side (the origin) can now start responding with page
-** content for pages that do not have a matching hash.
+** content for pages that do not have a matching hash or with
+** ORIGIN_DETAIL messages with requests for more detail.
*/
static void originSide(SQLiteRsync *p){
int rc = 0;
int c = 0;
unsigned int nPage = 0;
- unsigned int iPage = 0;
+ unsigned int iHash = 1; /* Pgno for next hash to receive */
+ unsigned int nHash = 1; /* Number of pages per hash received */
+ unsigned int mxHash = 0; /* Maximum hash value received */
unsigned int lockBytePage = 0;
unsigned int szPg = 0;
- sqlite3_stmt *pCkHash = 0;
- sqlite3_stmt *pInsHash = 0;
+ sqlite3_stmt *pCkHash = 0; /* Verify hash on a single page */
+ sqlite3_stmt *pCkHashN = 0; /* Verify a multi-page hash */
+ sqlite3_stmt *pInsHash = 0; /* Record a bad hash */
char buf[200];
p->isReplica = 0;
if( p->bCommCheck ){
infoMsg(p, "origin zOrigin=%Q zReplica=%Q isRemote=%d protocol=%d",
- p->zOrigin, p->zReplica, p->isRemote, PROTOCOL_VERSION);
+ p->zOrigin, p->zReplica, p->isRemote, p->iProtocol);
writeByte(p, ORIGIN_END);
fflush(p->pOut);
}else{
@@ -1237,9 +1373,11 @@ static void originSide(SQLiteRsync *p){
}
hashRegister(p->db);
runSql(p, "BEGIN");
- runSqlReturnText(p, buf, "PRAGMA journal_mode");
- if( sqlite3_stricmp(buf,"wal")!=0 ){
- reportError(p, "Origin database is not in WAL mode");
+ if( p->bWalOnly ){
+ runSqlReturnText(p, buf, "PRAGMA journal_mode");
+ if( sqlite3_stricmp(buf,"wal")!=0 ){
+ reportError(p, "Origin database is not in WAL mode");
+ }
}
runSqlReturnUInt(p, &nPage, "PRAGMA page_count");
runSqlReturnUInt(p, &szPg, "PRAGMA page_size");
@@ -1247,13 +1385,15 @@ static void originSide(SQLiteRsync *p){
if( p->nErr==0 ){
/* Send the ORIGIN_BEGIN message */
writeByte(p, ORIGIN_BEGIN);
- writeByte(p, PROTOCOL_VERSION);
+ writeByte(p, p->iProtocol);
writePow2(p, szPg);
writeUint32(p, nPage);
fflush(p->pOut);
+ if( p->zDebugFile ){
+ debugMessage(p, "-> ORIGIN_BEGIN %u %u %u\n", p->iProtocol,szPg,nPage);
+ }
p->nPage = nPage;
p->szPage = szPg;
- p->iProtocol = PROTOCOL_VERSION;
lockBytePage = (1<<30)/szPg + 1;
}
}
@@ -1266,11 +1406,24 @@ static void originSide(SQLiteRsync *p){
** that is larger than what it knows about. The replica sends back
** a counter-proposal of an earlier protocol which the origin can
** accept by resending a new ORIGIN_BEGIN. */
- p->iProtocol = readByte(p);
- writeByte(p, ORIGIN_BEGIN);
- writeByte(p, p->iProtocol);
- writePow2(p, p->szPage);
- writeUint32(p, p->nPage);
+ u8 newProtocol = readByte(p);
+ if( p->zDebugFile ){
+ debugMessage(p, "<- REPLICA_BEGIN %d\n", (int)newProtocol);
+ }
+ if( newProtocol < p->iProtocol ){
+ p->iProtocol = newProtocol;
+ writeByte(p, ORIGIN_BEGIN);
+ writeByte(p, p->iProtocol);
+ writePow2(p, p->szPage);
+ writeUint32(p, p->nPage);
+ fflush(p->pOut);
+ if( p->zDebugFile ){
+ debugMessage(p, "-> ORIGIN_BEGIN %d %d %u\n", p->iProtocol,
+ p->szPage, p->nPage);
+ }
+ }else{
+ reportError(p, "Invalid REPLICA_BEGIN reply");
+ }
break;
}
case REPLICA_MSG:
@@ -1278,25 +1431,73 @@ static void originSide(SQLiteRsync *p){
readAndDisplayMessage(p, c);
break;
}
+ case REPLICA_CONFIG: {
+ readUint32(p, &iHash);
+ readUint32(p, &nHash);
+ if( p->zDebugFile ){
+ debugMessage(p, "<- REPLICA_CONFIG %u %u\n", iHash, nHash);
+ }
+ break;
+ }
case REPLICA_HASH: {
+ int bMatch = 0;
if( pCkHash==0 ){
- runSql(p, "CREATE TEMP TABLE badHash(pgno INTEGER PRIMARY KEY)");
+ runSql(p, "CREATE TEMP TABLE badHash("
+ " pgno INTEGER PRIMARY KEY,"
+ " sz INT)");
pCkHash = prepareStmt(p,
- "SELECT pgno FROM sqlite_dbpage('main')"
- " WHERE pgno=?1 AND hash(data)!=?2"
+ "SELECT hash(data)==?3 FROM sqlite_dbpage('main')"
+ " WHERE pgno=?1"
);
if( pCkHash==0 ) break;
- pInsHash = prepareStmt(p, "INSERT INTO badHash VALUES(?)");
+ pInsHash = prepareStmt(p, "INSERT INTO badHash VALUES(?1,?2)");
if( pInsHash==0 ) break;
}
p->nHashSent++;
- iPage++;
- sqlite3_bind_int64(pCkHash, 1, iPage);
readBytes(p, 20, buf);
- sqlite3_bind_blob(pCkHash, 2, buf, 20, SQLITE_STATIC);
- rc = sqlite3_step(pCkHash);
- if( rc==SQLITE_ROW ){
- sqlite3_bind_int64(pInsHash, 1, sqlite3_column_int64(pCkHash, 0));
+ if( nHash>1 ){
+ if( pCkHashN==0 ){
+ pCkHashN = prepareStmt(p,
+ "WITH c(n) AS "
+ " (VALUES(?1) UNION ALL SELECT n+1 FROM c WHERE n2)"
+ "SELECT agghash(hash(data))==?3"
+ " FROM c CROSS JOIN sqlite_dbpage('main') ON pgno=n"
+ );
+ if( pCkHashN==0 ) break;
+ }
+ sqlite3_bind_int64(pCkHashN, 1, iHash);
+ sqlite3_bind_int64(pCkHashN, 2, iHash + nHash - 1);
+ sqlite3_bind_blob(pCkHashN, 3, buf, 20, SQLITE_STATIC);
+ rc = sqlite3_step(pCkHashN);
+ if( rc==SQLITE_ROW ){
+ bMatch = sqlite3_column_int(pCkHashN,0);
+ }else if( rc==SQLITE_ERROR ){
+ reportError(p, "SQL statement [%s] failed: %s",
+ sqlite3_sql(pCkHashN), sqlite3_errmsg(p->db));
+ }
+ sqlite3_reset(pCkHashN);
+ }else{
+ sqlite3_bind_int64(pCkHash, 1, iHash);
+ sqlite3_bind_blob(pCkHash, 3, buf, 20, SQLITE_STATIC);
+ rc = sqlite3_step(pCkHash);
+ if( rc==SQLITE_ERROR ){
+ reportError(p, "SQL statement [%s] failed: %s",
+ sqlite3_sql(pCkHash), sqlite3_errmsg(p->db));
+ }else if( rc==SQLITE_ROW && sqlite3_column_int(pCkHash,0) ){
+ bMatch = 1;
+ }
+ sqlite3_reset(pCkHash);
+ }
+ if( p->zDebugFile ){
+ debugMessage(p, "<- REPLICA_HASH %u %u %s %08x...\n",
+ iHash, nHash,
+ bMatch ? "match" : "fail",
+ *(unsigned int*)buf
+ );
+ }
+ if( !bMatch ){
+ sqlite3_bind_int64(pInsHash, 1, iHash);
+ sqlite3_bind_int64(pInsHash, 2, nHash);
rc = sqlite3_step(pInsHash);
if( rc!=SQLITE_DONE ){
reportError(p, "SQL statement [%s] failed: %s",
@@ -1304,42 +1505,74 @@ static void originSide(SQLiteRsync *p){
}
sqlite3_reset(pInsHash);
}
- else if( rc!=SQLITE_DONE ){
- reportError(p, "SQL statement [%s] failed: %s",
- sqlite3_sql(pCkHash), sqlite3_errmsg(p->db));
- }
- sqlite3_reset(pCkHash);
+ if( iHash+nHash>mxHash ) mxHash = iHash+nHash;
+ iHash += nHash;
break;
}
case REPLICA_READY: {
+ int nMulti = 0;
sqlite3_stmt *pStmt;
- sqlite3_finalize(pCkHash);
- sqlite3_finalize(pInsHash);
- pCkHash = 0;
- pInsHash = 0;
- if( iPage+1nPage ){
- runSql(p, "WITH RECURSIVE c(n) AS"
- " (VALUES(%d) UNION ALL SELECT n+1 FROM c WHERE n<%d)"
- " INSERT INTO badHash SELECT n FROM c",
- iPage+1, p->nPage);
+ if( p->zDebugFile ){
+ debugMessage(p, "<- REPLICA_READY\n");
}
- runSql(p, "DELETE FROM badHash WHERE pgno=%d", lockBytePage);
- pStmt = prepareStmt(p,
- "SELECT pgno, data"
- " FROM badHash JOIN sqlite_dbpage('main') USING(pgno)");
+ p->nRound++;
+ pStmt = prepareStmt(p,"SELECT pgno, sz FROM badHash WHERE sz>1");
if( pStmt==0 ) break;
- while( sqlite3_step(pStmt)==SQLITE_ROW && p->nErr==0 && p->nWrErr==0 ){
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
unsigned int pgno = (unsigned int)sqlite3_column_int64(pStmt,0);
- const void *pContent = sqlite3_column_blob(pStmt, 1);
- writeByte(p, ORIGIN_PAGE);
+ unsigned int cnt = (unsigned int)sqlite3_column_int64(pStmt,1);
+ writeByte(p, ORIGIN_DETAIL);
writeUint32(p, pgno);
- writeBytes(p, szPg, pContent);
- p->nPageSent++;
+ writeUint32(p, cnt);
+ nMulti++;
+ if( p->zDebugFile ){
+ debugMessage(p, "-> ORIGIN_DETAIL %u %u\n", pgno, cnt);
+ }
}
sqlite3_finalize(pStmt);
- writeByte(p, ORIGIN_TXN);
- writeUint32(p, nPage);
- writeByte(p, ORIGIN_END);
+ if( nMulti ){
+ runSql(p, "DELETE FROM badHash WHERE sz>1");
+ writeByte(p, ORIGIN_READY);
+ if( p->zDebugFile ) debugMessage(p, "-> ORIGIN_READY\n");
+ }else{
+ sqlite3_finalize(pCkHash);
+ sqlite3_finalize(pCkHashN);
+ sqlite3_finalize(pInsHash);
+ pCkHash = 0;
+ pInsHash = 0;
+ if( mxHashnPage ){
+ runSql(p, "WITH RECURSIVE c(n) AS"
+ " (VALUES(%d) UNION ALL SELECT n+1 FROM c WHERE n<%d)"
+ " INSERT INTO badHash SELECT n, 1 FROM c",
+ mxHash, p->nPage);
+ }
+ runSql(p, "DELETE FROM badHash WHERE pgno=%d", lockBytePage);
+ pStmt = prepareStmt(p,
+ "SELECT pgno, data"
+ " FROM badHash JOIN sqlite_dbpage('main') USING(pgno)");
+ if( pStmt==0 ) break;
+ while( sqlite3_step(pStmt)==SQLITE_ROW
+ && p->nErr==0
+ && p->nWrErr==0
+ ){
+ unsigned int pgno = (unsigned int)sqlite3_column_int64(pStmt,0);
+ const void *pContent = sqlite3_column_blob(pStmt, 1);
+ writeByte(p, ORIGIN_PAGE);
+ writeUint32(p, pgno);
+ writeBytes(p, szPg, pContent);
+ p->nPageSent++;
+ if( p->zDebugFile ){
+ debugMessage(p, "-> ORIGIN_PAGE %u\n", pgno);
+ }
+ }
+ sqlite3_finalize(pStmt);
+ writeByte(p, ORIGIN_TXN);
+ writeUint32(p, nPage);
+ if( p->zDebugFile ){
+ debugMessage(p, "-> ORIGIN_TXN %u\n", nPage);
+ }
+ writeByte(p, ORIGIN_END);
+ }
fflush(p->pOut);
break;
}
@@ -1356,6 +1589,103 @@ static void originSide(SQLiteRsync *p){
closeDb(p);
}
+/*
+** Send a REPLICA_HASH message for each entry in the sendHash table.
+** The sendHash table looks like this:
+**
+** CREATE TABLE sendHash(
+** fpg INTEGER PRIMARY KEY, -- Page number of the hash
+** npg INT -- Number of pages in this hash
+** );
+**
+** If iHash is page number for the next page that the origin will
+** be expecting, and nHash is the number of pages that the origin will
+** be expecting in the hash that follows. Send a REPLICA_CONFIG message
+** if either of these values if not correct.
+*/
+static void sendHashMessages(
+ SQLiteRsync *p, /* The replica-side of the sync */
+ unsigned int iHash, /* Next page expected by origin */
+ unsigned int nHash /* Next number of pages expected by origin */
+){
+ sqlite3_stmt *pStmt;
+ pStmt = prepareStmt(p,
+ "SELECT if(npg==1,"
+ " (SELECT hash(data) FROM sqlite_dbpage('replica') WHERE pgno=fpg),"
+ " (WITH RECURSIVE c(n) AS"
+ " (SELECT fpg UNION ALL SELECT n+1 FROM c WHERE nnErr==0 && p->nWrErr==0 ){
+ const unsigned char *a = sqlite3_column_blob(pStmt, 0);
+ unsigned int pgno = (unsigned int)sqlite3_column_int64(pStmt, 1);
+ unsigned int npg = (unsigned int)sqlite3_column_int64(pStmt, 2);
+ if( pgno!=iHash || npg!=nHash ){
+ writeByte(p, REPLICA_CONFIG);
+ writeUint32(p, pgno);
+ writeUint32(p, npg);
+ if( p->zDebugFile ){
+ debugMessage(p, "-> REPLICA_CONFIG %u %u\n", pgno, npg);
+ }
+ }
+ if( a==0 ){
+ if( p->zDebugFile ){
+ debugMessage(p, "# Oops: No hash for %u %u\n", pgno, npg);
+ }
+ }else{
+ writeByte(p, REPLICA_HASH);
+ writeBytes(p, 20, a);
+ if( p->zDebugFile ){
+ debugMessage(p, "-> REPLICA_HASH %u %u (%08x...)\n",
+ pgno, npg, *(unsigned int*)a);
+ }
+ }
+ p->nHashSent++;
+ iHash = pgno + npg;
+ nHash = npg;
+ }
+ sqlite3_finalize(pStmt);
+ runSql(p, "DELETE FROM sendHash");
+ writeByte(p, REPLICA_READY);
+ fflush(p->pOut);
+ p->nRound++;
+ if( p->zDebugFile ) debugMessage(p, "-> REPLICA_READY\n", iHash);
+}
+
+/*
+** Make entries in the sendHash table to send hashes for
+** npg (mnemonic: Number of PaGes) pages starting with fpg
+** (mnemonic: First PaGe).
+*/
+static void subdivideHashRange(
+ SQLiteRsync *p, /* The replica-side of the sync */
+ unsigned int fpg, /* First page of the range */
+ unsigned int npg /* Number of pages */
+){
+ unsigned int nChunk; /* How many pages to request per hash */
+ sqlite3_uint64 iEnd; /* One more than the last page */
+ if( npg<=30 ){
+ nChunk = 1;
+ }else if( npg<=1000 ){
+ nChunk = 30;
+ }else{
+ nChunk = 1000;
+ }
+ iEnd = fpg;
+ iEnd += npg;
+ runSql(p,
+ "WITH RECURSIVE c(n) AS"
+ " (VALUES(%u) UNION ALL SELECT n+%u FROM c WHERE n<%llu)"
+ "REPLACE INTO sendHash(fpg,npg)"
+ " SELECT n, min(%llu-n,%u) FROM c",
+ fpg, nChunk, iEnd-nChunk, iEnd, nChunk
+ );
+}
+
/*
** Run the replica-side protocol. The protocol is passive in the sense
** that it only response to message from the origin side.
@@ -1366,15 +1696,35 @@ static void originSide(SQLiteRsync *p){
** each page in the origin database (sent as a single-byte power-of-2),
** and the number of pages in the origin database.
** This procedure checks compatibility, and if everything is ok,
-** it starts sending hashes of pages already present back to the origin.
+** it starts sending hashes back to the origin using REPLICA_HASH
+** and/or REPLICA_CONFIG message, followed by a single REPLICA_READY.
+** REPLICA_CONFIG is only sent if the protocol is 2 or greater.
**
-** ORIGIN_ERROR size text
+** ORIGIN_ERROR size text
**
-** Report the received error and quit.
+** Report an error and quit.
**
-** ORIGIN_PAGE pgno content
+** ORIGIN_DETAIL pgno cnt
**
-** Update the content of the given page.
+** The origin reports that a multi-page hash starting at pgno and
+** spanning cnt pages failed to match. The origin is requesting
+** details (more REPLICA_HASH message with a smaller cnt). The
+** replica must wait on ORIGIN_READY before sending its reply.
+**
+** ORIGIN_READY
+**
+** After sending one or more ORIGIN_DETAIL messages, the ORIGIN_READY
+** is sent by the origin to indicate that it has finished sending
+** requests for detail and is ready for the replicate to reply
+** with a new round of REPLICA_CONFIG and REPLICA_HASH messages.
+**
+** ORIGIN_PAGE pgno content
+**
+** Once the origin believes it knows exactly which pages need to be
+** updated in the replica, it starts sending those pages using these
+** messages. These messages will only appear immediately after
+** REPLICA_READY. The origin never mixes ORIGIN_DETAIL and
+** ORIGIN_PAGE messages in the same batch.
**
** ORIGIN_TXN pgno
**
@@ -1389,15 +1739,17 @@ static void replicaSide(SQLiteRsync *p){
int c;
sqlite3_stmt *pIns = 0;
unsigned int szOPage = 0;
+ char eJMode = 0; /* Journal mode prior to sync */
char buf[65536];
p->isReplica = 1;
if( p->bCommCheck ){
infoMsg(p, "replica zOrigin=%Q zReplica=%Q isRemote=%d protocol=%d",
- p->zOrigin, p->zReplica, p->isRemote, PROTOCOL_VERSION);
+ p->zOrigin, p->zReplica, p->isRemote, p->iProtocol);
writeByte(p, REPLICA_END);
fflush(p->pOut);
}
+ if( p->iProtocol<=0 ) p->iProtocol = PROTOCOL_VERSION;
/* Respond to message from the origin. The origin will initiate the
** the conversation with an ORIGIN_BEGIN message.
@@ -1413,22 +1765,31 @@ static void replicaSide(SQLiteRsync *p){
unsigned int nOPage = 0;
unsigned int nRPage = 0, szRPage = 0;
int rc = 0;
- sqlite3_stmt *pStmt = 0;
+ u8 iProtocol;
closeDb(p);
- p->iProtocol = readByte(p);
+ iProtocol = readByte(p);
szOPage = readPow2(p);
readUint32(p, &nOPage);
+ if( p->zDebugFile ){
+ debugMessage(p, "<- ORIGIN_BEGIN %d %d %u\n", iProtocol, szOPage,
+ nOPage);
+ }
if( p->nErr ) break;
- if( p->iProtocol>PROTOCOL_VERSION ){
+ if( iProtocol>p->iProtocol ){
/* If the protocol version on the origin side is larger, send back
** a REPLICA_BEGIN message with the protocol version number of the
** replica side. This gives the origin an opportunity to resend
** a new ORIGIN_BEGIN with a reduced protocol version. */
writeByte(p, REPLICA_BEGIN);
- writeByte(p, PROTOCOL_VERSION);
+ writeByte(p, p->iProtocol);
+ fflush(p->pOut);
+ if( p->zDebugFile ){
+ debugMessage(p, "-> REPLICA_BEGIN %u\n", p->iProtocol);
+ }
break;
}
+ p->iProtocol = iProtocol;
p->nPage = nOPage;
p->szPage = szOPage;
rc = sqlite3_open(":memory:", &p->db);
@@ -1453,20 +1814,30 @@ static void replicaSide(SQLiteRsync *p){
closeDb(p);
break;
}
+ runSql(p,
+ "CREATE TABLE sendHash("
+ " fpg INTEGER PRIMARY KEY," /* The page number of hash to send */
+ " npg INT" /* Number of pages in this hash */
+ ")"
+ );
hashRegister(p->db);
if( runSqlReturnUInt(p, &nRPage, "PRAGMA replica.page_count") ){
break;
}
if( nRPage==0 ){
runSql(p, "PRAGMA replica.page_size=%u", szOPage);
- runSql(p, "PRAGMA replica.journal_mode=WAL");
runSql(p, "SELECT * FROM replica.sqlite_schema");
}
runSql(p, "BEGIN IMMEDIATE");
runSqlReturnText(p, buf, "PRAGMA replica.journal_mode");
if( strcmp(buf, "wal")!=0 ){
- reportError(p, "replica is not in WAL mode");
- break;
+ if( p->bWalOnly && nRPage>0 ){
+ reportError(p, "replica is not in WAL mode");
+ break;
+ }
+ eJMode = 1; /* Non-WAL mode prior to sync */
+ }else{
+ eJMode = 2; /* WAL-mode prior to sync */
}
runSqlReturnUInt(p, &nRPage, "PRAGMA replica.page_count");
runSqlReturnUInt(p, &szRPage, "PRAGMA replica.page_size");
@@ -1475,26 +1846,43 @@ static void replicaSide(SQLiteRsync *p){
"replica is %d bytes", szOPage, szRPage);
break;
}
-
- pStmt = prepareStmt(p,
- "SELECT hash(data) FROM sqlite_dbpage('replica')"
- " WHERE pgno<=min(%d,%d)"
- " ORDER BY pgno", nRPage, nOPage);
- while( sqlite3_step(pStmt)==SQLITE_ROW && p->nErr==0 && p->nWrErr==0 ){
- const unsigned char *a = sqlite3_column_blob(pStmt, 0);
- writeByte(p, REPLICA_HASH);
- writeBytes(p, 20, a);
- p->nHashSent++;
+ if( p->iProtocol<2 || nRPage<=100 ){
+ runSql(p,
+ "WITH RECURSIVE c(n) AS"
+ "(VALUES(1) UNION ALL SELECT n+1 FROM c WHERE n<%d)"
+ "INSERT INTO sendHash(fpg, npg) SELECT n, 1 FROM c",
+ nRPage);
+ }else{
+ runSql(p,"INSERT INTO sendHash VALUES(1,1)");
+ subdivideHashRange(p, 2, nRPage);
}
- sqlite3_finalize(pStmt);
- writeByte(p, REPLICA_READY);
- fflush(p->pOut);
+ sendHashMessages(p, 1, 1);
runSql(p, "PRAGMA writable_schema=ON");
break;
}
+ case ORIGIN_DETAIL: {
+ unsigned int fpg, npg;
+ readUint32(p, &fpg);
+ readUint32(p, &npg);
+ if( p->zDebugFile ){
+ debugMessage(p, "<- ORIGIN_DETAIL %u %u\n", fpg, npg);
+ }
+ subdivideHashRange(p, fpg, npg);
+ break;
+ }
+ case ORIGIN_READY: {
+ if( p->zDebugFile ){
+ debugMessage(p, "<- ORIGIN_READY\n");
+ }
+ sendHashMessages(p, 0, 0);
+ break;
+ }
case ORIGIN_TXN: {
unsigned int nOPage = 0;
readUint32(p, &nOPage);
+ if( p->zDebugFile ){
+ debugMessage(p, "<- ORIGIN_TXN %u\n", nOPage);
+ }
if( pIns==0 ){
/* Nothing has changed */
runSql(p, "COMMIT");
@@ -1522,6 +1910,9 @@ static void replicaSide(SQLiteRsync *p){
unsigned int pgno = 0;
int rc;
readUint32(p, &pgno);
+ if( p->zDebugFile ){
+ debugMessage(p, "<- ORIGIN_PAGE %u\n", pgno);
+ }
if( p->nErr ) break;
if( pIns==0 ){
pIns = prepareStmt(p,
@@ -1531,6 +1922,11 @@ static void replicaSide(SQLiteRsync *p){
}
readBytes(p, szOPage, buf);
if( p->nErr ) break;
+ if( pgno==1 && eJMode==2 && buf[18]==1 ){
+ /* Do not switch the replica out of WAL mode if it started in
+ ** WAL mode */
+ buf[18] = buf[19] = 2;
+ }
p->nPageSent++;
sqlite3_bind_int64(pIns, 1, pgno);
sqlite3_bind_blob(pIns, 2, buf, szOPage, SQLITE_STATIC);
@@ -1619,9 +2015,9 @@ static char *hostSeparator(const char *zIn){
zIn++;
}
return zPath;
-
}
+
/*
** Parse command-line arguments. Dispatch subroutines to do the
** requested work.
@@ -1664,16 +2060,19 @@ int main(int argc, char const * const *argv){
sqlite3_int64 tmEnd;
sqlite3_int64 tmElapse;
const char *zRemoteErrFile = 0;
+ const char *zRemoteDebugFile = 0;
#define cli_opt_val cmdline_option_value(argc, argv, ++i)
memset(&ctx, 0, sizeof(ctx));
+ ctx.iProtocol = PROTOCOL_VERSION;
for(i=1; iPROTOCOL_VERSION ){
+ ctx.iProtocol = PROTOCOL_VERSION;
+ }
+ continue;
+ }
+ if( strcmp(z, "-ssh")==0 ){
zSsh = cli_opt_val;
continue;
}
- if( strcmp(z, "--exe")==0 ){
+ if( strcmp(z, "-exe")==0 ){
zExe = cli_opt_val;
continue;
}
- if( strcmp(z, "--logfile")==0 ){
+ if( strcmp(z, "-wal-only")==0 ){
+ ctx.bWalOnly = 1;
+ continue;
+ }
+ if( strcmp(z, "-version")==0 ){
+ printf("%s\n", sqlite3_sourceid());
+ return 0;
+ }
+ if( strcmp(z, "-help")==0 || strcmp(z, "--help")==0
+ || strcmp(z, "-?")==0
+ ){
+ printf("%s", zUsage);
+ return 0;
+ }
+ if( strcmp(z, "-logfile")==0 ){
/* DEBUG OPTION: --logfile FILENAME
** Cause all local output traffic to be duplicated in FILENAME */
const char *zLog = cli_opt_val;
@@ -1701,48 +2123,51 @@ int main(int argc, char const * const *argv){
}
continue;
}
- if( strcmp(z, "--errorfile")==0 ){
+ if( strcmp(z, "-errorfile")==0 ){
/* DEBUG OPTION: --errorfile FILENAME
** Error messages on the local side are written into FILENAME */
ctx.zErrFile = cli_opt_val;
continue;
}
- if( strcmp(z, "--remote-errorfile")==0 ){
+ if( strcmp(z, "-remote-errorfile")==0 ){
/* DEBUG OPTION: --remote-errorfile FILENAME
** Error messages on the remote side are written into FILENAME on
** the remote side. */
zRemoteErrFile = cli_opt_val;
continue;
}
- if( strcmp(z, "-help")==0 || strcmp(z, "--help")==0
- || strcmp(z, "-?")==0
- ){
- printf("%s", zUsage);
+ if( strcmp(z, "-debugfile")==0 ){
+ /* DEBUG OPTION: --debugfile FILENAME
+ ** Debugging messages on the local side are written into FILENAME */
+ ctx.zDebugFile = cli_opt_val;
+ continue;
+ }
+ if( strcmp(z, "-remote-debugfile")==0 ){
+ /* DEBUG OPTION: --remote-debugfile FILENAME
+ ** Error messages on the remote side are written into FILENAME on
+ ** the remote side. */
+ zRemoteDebugFile = cli_opt_val;
+ continue;
+ }
+ if( strcmp(z,"-commcheck")==0 ){ /* DEBUG ONLY */
+ /* Run a communication check with the remote side. Do not attempt
+ ** to exchange any database connection */
+ ctx.bCommCheck = 1;
+ continue;
+ }
+ if( strcmp(z,"-arg-escape-check")==0 ){ /* DEBUG ONLY */
+ /* Test the append_escaped_arg() routine by using it to render a
+ ** copy of the input command-line, assuming all arguments except
+ ** this one are filenames. */
+ sqlite3_str *pStr = sqlite3_str_new(0);
+ int k;
+ for(k=0; k/dev/null", 0);
+ }
+ zCmd = sqlite3_str_finish(pStr);
+ if( ctx.eVerbose>=2 ) printf("%s\n", zCmd);
+ if( popen2(zCmd, &ctx.pIn, &ctx.pOut, &childPid, 0) ){
+ if( iRetry>=1 ){
+ fprintf(stderr, "Could not start auxiliary process: %s\n", zCmd);
+ return 1;
+ }
+ if( ctx.eVerbose>=2 ){
+ printf("ssh FAILED. Retry with a PATH= argument...\n");
+ }
+ continue;
+ }
+ replicaSide(&ctx);
+ if( ctx.nHashSent==0 && iRetry==0 ) continue;
+ break;
}
- if( zRemoteErrFile ){
- append_escaped_arg(pStr, "--errorfile", 0);
- append_escaped_arg(pStr, zRemoteErrFile, 1);
- }
- append_escaped_arg(pStr, zDiv, 1);
- append_escaped_arg(pStr, file_tail(ctx.zReplica), 1);
- zCmd = sqlite3_str_finish(pStr);
- if( ctx.eVerbose>=2 ) printf("%s\n", zCmd);
- if( popen2(zCmd, &ctx.pIn, &ctx.pOut, &childPid, 0) ){
- fprintf(stderr, "Could not start auxiliary process: %s\n", zCmd);
- return 1;
- }
- replicaSide(&ctx);
}else if( (zDiv = hostSeparator(ctx.zReplica))!=0 ){
/* Local ORIGIN and remote REPLICA */
- sqlite3_str *pStr = sqlite3_str_new(0);
- append_escaped_arg(pStr, zSsh, 1);
- sqlite3_str_appendf(pStr, " -e none");
+ int iRetry;
*(zDiv++) = 0;
- append_escaped_arg(pStr, ctx.zReplica, 0);
- append_escaped_arg(pStr, zExe, 1);
- append_escaped_arg(pStr, "--replica", 0);
- if( ctx.bCommCheck ){
- append_escaped_arg(pStr, "--commcheck", 0);
- if( ctx.eVerbose==0 ) ctx.eVerbose = 1;
+ for(iRetry=0; 1 /*exit-by-break*/; iRetry++){
+ sqlite3_str *pStr = sqlite3_str_new(0);
+ append_escaped_arg(pStr, zSsh, 1);
+ sqlite3_str_appendf(pStr, " -e none");
+ append_escaped_arg(pStr, ctx.zReplica, 0);
+ if( iRetry==1 ) add_path_argument(pStr);
+ append_escaped_arg(pStr, zExe, 1);
+ append_escaped_arg(pStr, "--replica", 0);
+ if( ctx.bCommCheck ){
+ append_escaped_arg(pStr, "--commcheck", 0);
+ if( ctx.eVerbose==0 ) ctx.eVerbose = 1;
+ }
+ if( zRemoteErrFile ){
+ append_escaped_arg(pStr, "--errorfile", 0);
+ append_escaped_arg(pStr, zRemoteErrFile, 1);
+ }
+ if( zRemoteDebugFile ){
+ append_escaped_arg(pStr, "--debugfile", 0);
+ append_escaped_arg(pStr, zRemoteDebugFile, 1);
+ }
+ if( ctx.bWalOnly ){
+ append_escaped_arg(pStr, "--wal-only", 0);
+ }
+ append_escaped_arg(pStr, file_tail(ctx.zOrigin), 1);
+ append_escaped_arg(pStr, zDiv, 1);
+ if( ctx.eVerbose<2 && iRetry==0 ){
+ append_escaped_arg(pStr, "2>/dev/null", 0);
+ }
+ zCmd = sqlite3_str_finish(pStr);
+ if( ctx.eVerbose>=2 ) printf("%s\n", zCmd);
+ if( popen2(zCmd, &ctx.pIn, &ctx.pOut, &childPid, 0) ){
+ if( iRetry>=1 ){
+ fprintf(stderr, "Could not start auxiliary process: %s\n", zCmd);
+ return 1;
+ }else if( ctx.eVerbose>=2 ){
+ printf("ssh FAILED. Retry with a PATH= argument...\n");
+ }
+ continue;
+ }
+ originSide(&ctx);
+ if( ctx.nHashSent==0 && iRetry==0 ) continue;
+ break;
}
- if( zRemoteErrFile ){
- append_escaped_arg(pStr, "--errorfile", 0);
- append_escaped_arg(pStr, zRemoteErrFile, 1);
- }
- append_escaped_arg(pStr, file_tail(ctx.zOrigin), 1);
- append_escaped_arg(pStr, zDiv, 1);
- zCmd = sqlite3_str_finish(pStr);
- if( ctx.eVerbose>=2 ) printf("%s\n", zCmd);
- if( popen2(zCmd, &ctx.pIn, &ctx.pOut, &childPid, 0) ){
- fprintf(stderr, "Could not start auxiliary process: %s\n", zCmd);
- return 1;
- }
- originSide(&ctx);
}else{
/* Local ORIGIN and REPLICA */
sqlite3_str *pStr = sqlite3_str_new(0);
@@ -1866,6 +2334,10 @@ int main(int argc, char const * const *argv){
append_escaped_arg(pStr, "--errorfile", 0);
append_escaped_arg(pStr, zRemoteErrFile, 1);
}
+ if( zRemoteDebugFile ){
+ append_escaped_arg(pStr, "--debugfile", 0);
+ append_escaped_arg(pStr, zRemoteDebugFile, 1);
+ }
append_escaped_arg(pStr, ctx.zOrigin, 1);
append_escaped_arg(pStr, ctx.zReplica, 1);
zCmd = sqlite3_str_finish(pStr);
@@ -1909,6 +2381,11 @@ int main(int argc, char const * const *argv){
printf("%s\n", zMsg);
sqlite3_free(zMsg);
}
+ if( ctx.eVerbose>=3 ){
+ printf("hashes: %lld hash-rounds: %d"
+ " page updates: %d protocol: %d\n",
+ ctx.nHashSent, ctx.nRound, ctx.nPageSent, ctx.iProtocol);
+ }
}
sqlite3_free(zCmd);
if( pIn!=0 && pOut!=0 ){