diff --git a/ext/wasm/GNUmakefile b/ext/wasm/GNUmakefile index a9b4af209a..d08e46bc35 100644 --- a/ext/wasm/GNUmakefile +++ b/ext/wasm/GNUmakefile @@ -14,10 +14,6 @@ # by the target name. Rebuild is necessary for all components to get # the desired optimization level. # -# quick, q = do just build the essentials for testing -# (sqlite3.js/wasm, tester1) for faster development-mode -# turnaround. -# # dist = create end user deliverables. Add dist.build=oX to build # with a specific optimization level, where oX is one of the # above-listed o? or qo? target names. @@ -37,25 +33,61 @@ # - InfoZip for 'dist' zip file ######################################################################## default: all -#default: quick MAKEFILE = $(lastword $(MAKEFILE_LIST)) -MAKEFILE.fiddle = fiddle.make CLEAN_FILES = DISTCLEAN_FILES = config.make MAKING_CLEAN = $(if $(filter %clean,$(MAKECMDGOALS)),1,0) -.PHONY: clean distclean -clean: - -rm -f $(CLEAN_FILES) -distclean: clean - -rm -f $(DISTCLEAN_FILES) +# +# dir.X = various directory names. +# +# dir.top = the top dir of the canonical build tree, where +# sqlite3.[ch] live. +# +dir.top = ../.. +dir.wasm = $(patsubst %/,%,$(dir $(MAKEFILE))) +dir.api = api +dir.jacc = jaccwabyt +dir.common = common +dir.fiddle = fiddle +dir.fiddle.debug = fiddle-debug +dir.tool = $(dir.top)/tool +# dir.dout = output dir for deliverables +dir.dout = $(dir.wasm)/jswasm +# dir.tmp = output dir for intermediary build files, as opposed to +# end-user deliverables. +dir.tmp = $(dir.wasm)/bld +dir.wasmfs = $(dir.dout) -######################################################################## +# +# Emoji for log messages. +# +emo.bug = ๐Ÿž +emo.compile = โณ +emo.roadblock = ๐Ÿšง +emo.disk = ๐Ÿ’พ +emo.done = โœ… +emo.fire = ๐Ÿ”ฅ +emo.folder = ๐Ÿ“ +emo.garbage = ๐Ÿ—‘ +emo.lock = ๐Ÿ”’ +emo.magic = ๐Ÿง™ +emo.megaphone = ๐Ÿ“ฃ +emo.mute = ๐Ÿ”‡ +emo.stop = ๐Ÿ›‘ +emo.strip = ๐Ÿ’ˆ +emo.test = ๐Ÿงช +emo.tool = ๐Ÿ”จ +emo.wasm-opt = ๐Ÿงผ +# ๐Ÿ‘ท๐Ÿช„๐Ÿงฎ๐Ÿงซ๐Ÿงฝ๐Ÿฟโ›ฝ๐Ÿšง๐ŸŽฑ๐Ÿชš๐Ÿ†๐Ÿงผ + +# # Special-case builds for which we require certain pre-conditions # which, if not met, may cause warnings or fatal errors in the build. # This also affects the default optimization level flags. The fiddle # targets are in this list because they are used for generating # sqlite.org/fiddle. +# OPTIMIZED_TARGETS = dist snapshot fiddle fiddle.debug ifeq (1,$(MAKING_CLEAN)) @@ -67,7 +99,7 @@ ifeq (1,$(MAKING_CLEAN)) else # Include config.make and perform some bootstrapping... ifeq (,$(wildcard ./config.make)) - $(error Missing config.make (gets generated by the configure script if the EMSDK is found)) + $(error Missing config.make. It gets generated by the configure script if the EMSDK is found) endif include ./config.make ifeq (,$(bin.bash)) @@ -79,22 +111,24 @@ else emcc.version = $(shell $(bin.emcc) --version | sed -n 1p | sed -e 's/^.* \([3-9][^ ]*\) .*$$/\1/;') $(info using emcc version [$(emcc.version)]) ifeq (,$(bin.wasm-strip)) - #################################################################### + # # We need wasm-strip for release builds (see below for why) but # not strictly for non-release builds. - $(info WARNING: *******************************************************************) - $(info WARNING: Builds using -Oz will minify WASM-exported names, breaking) - $(info WARNING: _All The Things_. The workaround for that is to build) - $(info WARNING: with -g3 (which explodes the file size) and then strip the debug) - $(info WARNING: info after compilation, using wasm-strip, to shrink the wasm file.) - $(info WARNING: wasm-strip was not found in the PATH so we cannot strip those.) - $(info WARNING: If this build uses any optimization level higher than -O1 then) - $(info WARNING: the ***resulting JS code WILL NOT BE USABLE***.) - $(info WARNING: wasm-strip is part of the wabt package:) - $(info WARNING: https://github.com/WebAssembly/wabt) - $(info WARNING: on Ubuntu-like systems it can be installed with:) - $(info WARNING: sudo apt install wabt) - $(info WARNING: *******************************************************************) + # + achtung = $(emo.fire)WARNING + $(info $(achtung): *******************************************************************) + $(info $(achtung): Builds using -Oz will minify WASM-exported names, breaking) + $(info $(achtung): _All The Things_. The workaround for that is to build) + $(info $(achtung): with -g3 (which explodes the file size) and then strip the debug) + $(info $(achtung): info after compilation, using wasm-strip, to shrink the wasm file.) + $(info $(achtung): wasm-strip was not found in the PATH so we cannot strip those.) + $(info $(achtung): If this build uses any optimization level higher than -O1 then) + $(info $(achtung): the ***resulting JS code WILL NOT BE USABLE***.) + $(info $(achtung): wasm-strip is part of the wabt package:) + $(info $(achtung): https://github.com/WebAssembly/wabt) + $(info $(achtung): on Ubuntu-like systems it can be installed with:) + $(info $(achtung): sudo apt install wabt) + $(info $(achtung): *******************************************************************) ifneq (,$(filter $(OPTIMIZED_TARGETS),$(MAKECMDGOALS))) $(error Cannot make release-quality binary because wasm-strip is not available.) endif @@ -108,73 +142,132 @@ else endif endif # ^^^ end of are-we-MAKING_CLEAN -maybe-wasm-strip = $(bin.wasm-strip) -######################################################################## -# JS_BUILD_NAMES exists for documentation purposes only. It enumerates -# the core build styles: # -# - sqlite3 = canonical library build +# Common vars and $(call)/$(eval)able utilities. # -# - sqlite3-wasmfs = WASMFS-capable library build +# The "b." prefix on some APIs is for "build". It was initially used +# only for features specific to each distinct js/wasm build. That's no +# longer the case, but the naming convention has stuck. # -JS_BUILD_NAMES = sqlite3 sqlite3-wasmfs -######################################################################## -# JS_BUILD_MODES exists for documentation purposes only. It enumerates -# the various "flavors" of build, each of which requires slight -# customization of the output: -# -# - vanilla = plain-vanilla JS for use in browsers. This is the -# canonical build mode. -# -# - esm = ES6 module, a.k.a. ESM, for use in browsers. -# -# - bundler-friendly = esm slightly tweaked for "bundler" -# tools. Bundlers are invariably based on node.js, so these builds -# are intended to be read at build-time by node.js but with a final -# target of browsers. All bundler-friendly builds are UNTESTED. -# They are provided primarily for use by the community-supported -# subproject which hosts npm builds of sqlite3, and they are -# actively supported only to the extent needed by that subproject. -# -# - node = for use by node.js for node.js, as opposed to by node.js on -# behalf of browser-side code (use bundler-friendly for that). Note -# that persistent storage (OPFS) is not available in these builds. -# These builds are UNTESTED and UNSUPPORTED! -# -JS_BUILD_MODES = vanilla esm bunder-friendly node +loud ?= 0 +ifeq (1,$(loud)) + $(info $(emo.megaphone) Emitting loud build info. Pass loud=0 to disable it.) + b.cmd@ = + loud.if = 1 +else + $(info $(emo.mute) Eliding loud build info. Pass loud=1 to enable it.) + b.cmd@ = @ + loud.if = +endif -######################################################################## -# dir.top = the top dir of the canonical build tree, where -# sqlite3.[ch] live. -dir.top = ../.. -# Maintenance reminder: some Emscripten flags require absolute paths -# but we want relative paths for most stuff simply to reduce -# noise. The $(abspath...) GNU make function can transform relative -# paths to absolute. -dir.wasm = $(patsubst %/,%,$(dir $(MAKEFILE))) -dir.api = api -dir.jacc = jaccwabyt -dir.common = common -dir.fiddle = fiddle -dir.fiddle-debug = fiddle-debug -dir.tool = $(dir.top)/tool -# dir.dout = output dir for deliverables -dir.dout = $(dir.wasm)/jswasm -# dir.tmp = output dir for intermediary build files, as opposed to -# end-user deliverables. -dir.tmp = $(dir.wasm)/bld -dir.wasmfs = $(dir.dout) +# +# logtag.X value for log context labeling. logtag.OTHERX can be +# assigned to customize it for a given X. This tag is used by +# b.call.X, b.eval.X, etc. for logging. There motivation for this is +# adding a build-specific prefix to messages so that the output of +# parallel builds is easier to sort through. We use emoji for the +# prefixes because it's far easier for my eyes to sort through than +# using only each build's name as the prefix. +# +# Each distinct build sets up its own logtag.BUILDNAME. +# +logtag.@ = [$@] +logtag.filter = [$(emo.disk) $@] +logtag.test = [$(emo.test) $@] +logtag.cp = [$(emo.disk) $@] -MKDIR.bld = $(dir.tmp) -$(MKDIR.bld): - @mkdir -p $@ $(dir.dout) +# +# $(call b.echo,LOGTAG,msg) +# +b.echo = echo $(logtag.$(1)) $(2) -CLEAN_FILES += *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ $(dir.fiddle)/*~ \ - $(dir.fiddle-debug)/* $(dir.dout)/* $(dir.tmp)/* +# +# $(call b.mkdir@) +# +# $1 = optional LOGTAG +# +b.mkdir@ = if [ ! -d $(dir $@) ]; then \ + echo '[$(emo.folder)+] $(if $(1),$(logtag.$(1)),[$(dir $@)])'; \ + mkdir -p $(dir $@) || exit; fi -######################################################################## +# +# $(call b.cp,@,src,dest) +# +# $1 = build name, $2 = src file(s). $3 = dest dir +b.cp = $(call b.mkdir@); \ + echo '$(logtag.$(1)) $(emo.disk) $(2) ==> $3'; \ + cp -p $(2) $(3) || exit + +# +# $(eval $(call b.eval.c-pp,@,src,dest,-Dx=y...)) +# +# $1 = build name +# $2 = Input file(s): cat $(2) | $(bin.c-pp) ... +# $3 = Output file: $(bin.c-pp) -o $(3) +# $4 = optional $(bin.c-pp) -D... flags */ +define b.eval.c-pp +$(3): $$(MAKEFILE_LIST) $$(bin.c-pp) $(2) + @$$(call b.mkdir@); \ + $$(call b.echo,$(1),$$(emo.disk)$$(emo.lock) $$(bin.c-pp) $(4) $(if $(loud.if),$(2))); \ + rm -f $(3); \ + $$(bin.c-pp) -o $(3) $(4) $(2) || exit; \ + chmod -w $(3) +CLEAN_FILES += $(3) +endef + +c-pp.D.64bit = -D64bit + +# +# $(call b.strip-js-emcc-bindings) +# +# $1 = an optional log message prefix +# +# Our JS code installs bindings of each sqlite3_...() WASM export. The +# generated Emscripten JS file does the same using its own framework, +# but we don't use those results and can speed up lib init, and reduce +# memory cost a bit, by stripping them out. Emscripten code-generation +# changes can "break" this, causing this to be a no-op, but (probably) +# the worst that can happen in that case is that it doesn't actually +# strip anything, leading to slightly larger JS files. +# +# This is intended to be used in makefile targets which generate an +# Emscripten module and where $@ is the module's .js/.mjs file. +b.strip-js-emcc-bindings = \ + sed -i -e '/^.*= \(_sqlite3\|_fiddle\)[^=]*=.*createExportWrapper/d' \ + -e '/^var \(_sqlite3\|_fiddle\)[^=]*=.*makeInvalidEarlyAccess/d' $@ || exit; \ + echo '$(1) $(emo.garbage) (Probably) /createExportWrapper()/d and /makeInvalidEarlyAccess()/d' + + +# +# bin.version-info = binary to output various sqlite3 version info for +# embedding in the JS files and in building the distribution zip file. +# It must NOT be in $(dir.tmp) because we need it to survive the +# cleanup process for the dist build to work properly. +# +# Slight caveat: this uses the version info from the in-tree +# sqlite3.c/h, which may diff from a user-provided $(sqlite3.c). The +# end result is that the generated JS files may have static version +# info from $(bin.version-info) which differ from their runtime-emitted +# version info (e.g. from sqlite3_libversion()). +bin.version-info = $(dir.top)/version-info +$(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile + $(MAKE) -C $(dir.top) version-info + +# +# bin.stripcomments is used for stripping C/C++-style comments from JS +# files. The JS files contain large chunks of documentation which we +# don't need for all builds. That app's -k flag is of particular +# importance here, as it allows us to retain the opening comment +# block(s), which contain the license header and version info. +bin.stripccomments = $(dir.tool)/stripccomments +$(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE) + $(CC) -o $@ $< +DISTCLEAN_FILES += $(bin.stripccomments) + + +# # Set up sqlite3.c and sqlite3.h... # # To build with SEE (https://sqlite.org/see), either put sqlite3-see.c @@ -206,18 +299,11 @@ else endif endif -########################################################################@ -# It's important that sqlite3.h be built to completion before any -# other parts of the build run, thus we use .NOTPARALLEL to disable -# parallel build of that file and its dependants. -.NOTPARALLEL: $(sqlite3.h) -$(sqlite3.h): - $(MAKE) -C $(dir.top) sqlite3.c -$(sqlite3.c): $(sqlite3.h) -######################################################################## +# # barebones=1 disables all "extraneous" stuff from sqlite3-wasm.c, the # goal being to create a WASM file with only the core APIs. +# ifeq (1,$(barebones)) wasm-bare-bones = 1 $(info ==============================================================) @@ -229,6 +315,24 @@ else endif # undefine barebones # relatively new gmake feature, not ubiquitous +# +# It's important that sqlite3.h be built to completion before any +# other parts of the build run, thus we use .NOTPARALLEL to disable +# parallel build of that file and its dependants. However, that makes +# the whole build non-parallelizable because everything has a dep on +# sqlite3.h/c. The alternative is to force the user to run (make +# sqlite3.c) from the top of the tree before running this build. +# +#.NOTPARALLEL: $(sqlite3.h) +# +$(sqlite3.h): + @echo "$(sqlite3.h) is out of date. "; \ + echo "To avoid problems with parallel builds, we're exiting now. Please do:"; \ + echo " $(MAKE) -C $(dir.top) sqlite3.c"; \ + echo "and try again."; exit 1 +# $(MAKE) -C $(dir.top) sqlite3.c +$(sqlite3.c): $(sqlite3.h) + # Common options for building sqlite3-wasm.c and speedtest1.c. # Explicit ENABLEs... SQLITE_OPT.common = \ @@ -312,7 +416,6 @@ endif # deprecated and alternatives are in place, but this crash behavior # can be used to find errant uses of sqlite3_js_vfs_create_file() # in client code. - ######################################################################## # The following flags are hard-coded into sqlite3-wasm.c and cannot be # modified via the build process: @@ -354,33 +457,6 @@ ifneq (,$(sqlite3_wasm_extra_init.c)) cflags.wasm_extra_init = -DSQLITE_WASM_EXTRA_INIT endif -######################################################################### -# bin.version-info = binary to output various sqlite3 version info for -# embedding in the JS files and in building the distribution zip file. -# It must NOT be in $(dir.tmp) because we need it to survive the -# cleanup process for the dist build to work properly. -# -# Slight caveat: this uses the version info from the in-tree -# sqlite3.c/h, which may diff from a user-provided $(sqlite3.c). The -# end result is that the generated JS files may have static version -# info from $(bin.version-info) which differ from their runtime-emitted -# version info (e.g. from sqlite3_libversion()). -bin.version-info = $(dir.top)/version-info -.NOTPARALLEL: $(bin.version-info) -$(bin.version-info): $(dir.tool)/version-info.c $(sqlite3.h) $(dir.top)/Makefile - $(MAKE) -C $(dir.top) version-info - -######################################################################### -# bin.stripcomments is used for stripping C/C++-style comments from JS -# files. The JS files contain large chunks of documentation which we -# don't need for all builds. That app's -k flag is of particular -# importance here, as it allows us to retain the opening comment -# block(s), which contain the license header and version info. -bin.stripccomments = $(dir.tool)/stripccomments -$(bin.stripccomments): $(bin.stripccomments).c $(MAKEFILE) - $(CC) -o $@ $< -DISTCLEAN_FILES += $(bin.stripccomments) - # # If $(WASM_CUSTOM_INSTANTIATE) is 1 then mkwasmbuilds will add # -Dcustom-Module.instantiateWasm to some of the builds. This is @@ -391,11 +467,10 @@ DISTCLEAN_FILES += $(bin.stripccomments) WASM_CUSTOM_INSTANTIATE = 0 ######################################################################## -# SQLITE.CALL.C-PP.FILTER: a $(call)able to transform $(1) to $(2) via: +# $(bin.c-pp): a minimal text file preprocessor. Like C's but much +# less so. # -# ./c-pp -f $(1) -o $(2) $(3) -# -# Historical notes: +# Historical notes about preprocessing files in this project: # # - We first attempted to use gcc and/or clang to preprocess JS files # in the same way we would normally do C files, but C-specific quirks @@ -417,11 +492,11 @@ WASM_CUSTOM_INSTANTIATE = 0 # builds but is maintained as a standalone project: # https://fossil.wanderinghorse.net/r/c-pp # -# Note that the SQLITE_... build flags used here have NO EFFECT on the -# JS/WASM build. They are solely for use with $(bin.c-pp) itself. +# The SQLITE_... build flags used here have NO EFFECT on the JS/WASM +# build. They are solely for use with $(bin.c-pp) itself. # # -D... flags which should be included in all invocations should be -# appended to $(SQLITE.CALL.C-PP.FILTER.global). +# appended to $(b.eval.c-pp.flags). bin.c-pp = ./c-pp $(bin.c-pp): c-pp.c $(sqlite3.c) # $(MAKEFILE) $(CC) -O0 -o $@ c-pp.c $(sqlite3.c) '-DCMPP_DEFAULT_DELIM="//#"' -I$(dir.top) \ @@ -429,22 +504,10 @@ $(bin.c-pp): c-pp.c $(sqlite3.c) # $(MAKEFILE) -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_WAL -DSQLITE_THREADSAFE=0 \ -DSQLITE_TEMP_STORE=3 DISTCLEAN_FILES += $(bin.c-pp) -SQLITE.CALL.C-PP.FILTER.global ?= +b.eval.c-pp.flags ?= ifeq (1,$(SQLITE_C_IS_SEE)) - SQLITE.CALL.C-PP.FILTER.global += -Denable-see + b.eval.c-pp.flags += -Denable-see endif -define SQLITE.CALL.C-PP.FILTER -# Create $2 from $1 using $(bin.c-pp) -# $1 = Input file: c-pp -f $(1).js -# $2 = Output file: c-pp -o $(2).js -# $3 = optional c-pp -D... flags -$(2): $(1) $$(MAKEFILE_LIST) $$(bin.c-pp) - @mkdir -p $$(dir $$@) - $$(bin.c-pp) -f $(1) -o $$@ $(3) $(SQLITE.CALL.C-PP.FILTER.global) -CLEAN_FILES += $(2) -endef -# /end SQLITE.CALL.C-PP.FILTER -######################################################################## # cflags.common = C compiler flags for all builds cflags.common = -I. -I$(dir $(sqlite3.c)) @@ -460,17 +523,6 @@ emcc.MEMORY64 ?= 0 # 1.0.34) or will fail to strip with "tables may not be 64-bit". ######################################################################## -ifneq (0,$(emcc.MEMORY64)) - emcc.WASM_BIGINT = 1 - # -sMEMORY64=1+ assumes -sWASM_BIGINT=1, so we'll make it explicit - - # SQLITE.CALL.C-PP.FILTER.global += -D64bit - # ^^ We no longer need build-time code filtering for 64-bit but if - # we ever do again, just uncomment that line or, if it's just needed - # in specific builds, update that build's -D flags in - # mkwasmbuilds.c. -endif - # emcc_opt = optimization-related flags. These are primarily used by # the various oX targets. build times for -O levels higher than 0 are # painful at dev-time. @@ -488,22 +540,6 @@ else emcc_opt ?= -Oz endif -# Our JS code installs bindings of each sqlite3_...() WASM export. The -# generated Emscripten JS file does the same using its own framework, -# but we don't use those results and can speed up lib init, and reduce -# memory cost a bit, by stripping them out. Emscripten-side changes -# can "break" this, causing this to be a no-op, but the worst that can -# happen in that case is that it doesn't actually strip anything, -# leading to slightly larger JS files. -# -# This snippet is intended to be used in makefile targets which -# generate an Emscripten module and where $@ is the module's .js/.mjs -# file. -SQLITE.strip-createExportWrapper = \ - sed -i -e '/^.*= \(_sqlite3\|_fiddle\)[^=]*=.*createExportWrapper/d' \ - -e '/^var \(_sqlite3\|_fiddle\)[^=]*=.*makeInvalidEarlyAccess/d' $@ || exit; \ - echo '(Probably) stripped out createExportWrapper() and makeInvalidEarlyAccess() parts.' - # When passing emcc_opt from the CLI, += and re-assignment have no # effect, so emcc_opt+=-g3 doesn't work. So... emcc_opt_full = $(emcc_opt) -g3 @@ -546,101 +582,10 @@ ifeq (0,$(wasm-bare-bones)) EXPORTED_FUNCTIONS.api.in += $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-extras endif EXPORTED_FUNCTIONS.api = $(dir.tmp)/EXPORTED_FUNCTIONS.api -$(EXPORTED_FUNCTIONS.api): $(MKDIR.bld) $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE) +$(EXPORTED_FUNCTIONS.api): $(EXPORTED_FUNCTIONS.api.in) $(sqlite3.c) $(MAKEFILE) + @$(call b.mkdir@) cat $(EXPORTED_FUNCTIONS.api.in) > $@ -######################################################################## -# sqlite3-license-version.js = generated JS file with the license -# header and version info. -sqlite3-license-version.js = $(dir.tmp)/sqlite3-license-version.js -# sqlite3-api-build-version.js = generated JS file which populates the -# sqlite3.version object using $(bin.version-info). -sqlite3-api-build-version.js = $(dir.tmp)/sqlite3-api-build-version.js -# sqlite3-api.jses = the list of JS files which make up -# $(sqlite3-api.js.in), in the order they need to be assembled. -sqlite3-api.jses = $(sqlite3-license-version.js) -# sqlite3-api-prologue.js: initial bootstrapping bits: -sqlite3-api.jses += $(dir.api)/sqlite3-api-prologue.js -# whwhasm.js and jaccwabyt.js: Low-level utils, mostly replacing -# Emscripten glue: -sqlite3-api.jses += $(dir.common)/whwasmutil.js -sqlite3-api.jses += $(dir.jacc)/jaccwabyt.js -# sqlite3-api-glue Glues the previous part together with sqlite: -sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.c-pp.js -# $(sqlite3-api-build-version.js) = library version info -sqlite3-api.jses += $(sqlite3-api-build-version.js) -# sqlite3-api-oo1 = the oo1 API: -sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.c-pp.js -# sqlite3-api-worker = the Worker1 API: -sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.c-pp.js -# sqlite3-vfs-helper = helper APIs for VFSes: -sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.c-pp.js -ifeq (0,$(wasm-bare-bones)) - # sqlite3-vtab-helper = helper APIs for VTABLEs: - sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js -endif -# sqlite3-vfs-opfs = the first OPFS VFS: -sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js -# sqlite3-vfs-opfs-sahpool = the second OPFS VFS: -sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js -# sqlite3-api-cleanup.js = "finalizes" the build and cleans up -# any extraneous global symbols which are needed temporarily -# by the previous files. -sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js - -######################################################################## -# SOAP.js is an external API file which is part of our distribution -# but not part of the sqlite3-api.js amalgamation. It's a component of -# the first OPFS VFS and necessarily an external file. -SOAP.js = $(dir.api)/sqlite3-opfs-async-proxy.js -SOAP.js.bld = $(dir.dout)/$(notdir $(SOAP.js)) -# -# $(sqlite3-api.ext.jses) = API-related files which are standalone files, -# not part of the amalgamation. -# -sqlite3-api.ext.jses = $(SOAP.js.bld) -$(SOAP.js.bld): $(SOAP.js) - cp $< $@ - -######################################################################## -# $(sqlite3-api*.*js) contain the core library code but not the -# Emscripten-related glue which deals with loading sqlite3.wasm. In -# theory they can be used by arbitrary build environments and WASM -# loaders, but in practice that breaks down because the WASM loader -# has to be able to provide all of the necessary "imports" to -# sqlite3.wasm, and that list of imports is unknown until sqlite3.wasm -# is compiled, at which point Emscripten sets up the imports -# appropriately. Abstractly speaking, it's impossible for other build -# environments to know exactly which imports are needed and provide -# them. Tools like wasm-objdump can be used to find the list of -# imports but it's questionable whether a non-Emscripten tool could -# realistically use that info to provide proper implementations. -# -# Sidebar: some of the imports are used soley by the Emscripten glue, -# which the sqlite3 JS code does not rely on. -# -# 2025-09-04: the most significant WASM imports sqlite3.wasm relies on -# are (A) sqlite3_malloc/sqlite3_free() (the memory-management APIs -# used by the whole library) and (B) the libc proxies for POSIX I/O -# emulation (arguably one of Emscripten's most convenient features -# when building C code for WASM). If we eliminate the POSIX I/O -# emulation then we could hypothetically eliminate all, or maybe all -# but one, Emscripten import dependency. We would either need to add -# our own POSIX emulation layer (not a trivial effort) or restrict the -# library to use of only in-memory VFSes and the JS-side VFSes. -# -# sqlite3-api.js.in = the amalgamated sqlite3-api.js before it gets -# preprocessed. It contains all of $(sqlite3-api.jses) but none of the -# Emscripten-specific headers and footers. -sqlite3-api.js.in = $(dir.tmp)/sqlite3-api.c-pp.js -$(sqlite3-api.js.in): $(MKDIR.bld) $(sqlite3-api.jses) $(MAKEFILE) - @echo "Making $@ ..." - @for i in $(sqlite3-api.jses); do \ - echo "/* BEGIN FILE: $$i */"; \ - cat $$i; \ - echo "/* END FILE: $$i */"; \ - done > $@ - ######################################################################## # emcc flags for .c/.o/.wasm/.js. emcc.flags = @@ -649,18 +594,22 @@ emcc.flags += -v # -v is _very_ loud but also informative about what it's doing endif - -######################################################################## +# # emcc flags for .c/.o. +# emcc.cflags = emcc.cflags += -std=c99 -fPIC # -------------^^^^^^^^ we need c99 for $(sqlite3-wasm.c), primarily # for variadic macros and snprintf() to implement # sqlite3__wasm_enum_json(). emcc.cflags += -I. -I$(dir.top) -######################################################################## + +# # emcc flags specific to building .js/.wasm files... +# emcc.jsflags = -fPIC +#emcc.jsflags += -Wno-gcc-install-dir-libstdcxx +#emcc is not passing ^^^ this on to clang emcc.jsflags += --no-entry emcc.jsflags += -sWASM_BIGINT=$(emcc.WASM_BIGINT) emcc.jsflags += -sMODULARIZE @@ -691,19 +640,7 @@ emcc.jsflags += -sSTRICT_JS=0 # -sSTRICT=1 Causes failures about unknown symbols which the build # tools should be installing, e.g. __syscall_geteuid32 -# emcc -sENVIRONMENT values for the various build modes: -emcc.environment.vanilla = web,worker -emcc.environment.vanilla64 = $(emcc.environment.vanilla) -emcc.environment.bundler-friendly = $(emcc.environment.vanilla) -emcc.environment.esm = $(emcc.environment.vanilla) -emcc.environment.esm64 = $(emcc.environment.vanilla) -emcc.environment.node = node -# Adding ",node" to the list for the other builds causes Emscripten to -# generate code which confuses node: it cannot reliably determine -# whether the build is for a browser or for node. We neither build nor -# test node builds on a regular basis. They are fully unsupported. - -######################################################################## +# # -sINITIAL_MEMORY: How much memory we need to start with is governed # at least in part by whether -sALLOW_MEMORY_GROWTH is enabled. If so, # we can start with less. If not, we need as much as we'll ever @@ -719,6 +656,12 @@ emcc.environment.node = node # but also says (in its changelog): "Note that it is currently not # supported in all configurations (#21071)." # https://github.com/emscripten-core/emscripten/blob/main/ChangeLog.md +# +# 2025-09-25: it turns out that _this_ WASM's heap size is not +# affected by Emscripten's in-memory virtual filesystem, so we don't +# strictly need a lot of heap. Resizing the heap is slow, though, so +# we want to start off with some room to grow. +# emcc.jsflags += -sALLOW_MEMORY_GROWTH emcc.INITIAL_MEMORY.128 = 134217728 emcc.INITIAL_MEMORY.96 = 100663296 @@ -726,9 +669,10 @@ emcc.INITIAL_MEMORY.64 = 67108864 emcc.INITIAL_MEMORY.32 = 33554432 emcc.INITIAL_MEMORY.16 = 16777216 emcc.INITIAL_MEMORY.8 = 8388608 -emcc.INITIAL_MEMORY ?= 8 +emcc.INITIAL_MEMORY.4 = 4194304 +emcc.INITIAL_MEMORY ?= 8 ifeq (,$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY))) -$(error emcc.INITIAL_MEMORY must be one of: 8, 16, 32, 64, 96, 128 (megabytes)) +$(error emcc.INITIAL_MEMORY must be one of: 4, 8, 16, 32, 64, 96, 128 (megabytes)) endif emcc.jsflags += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY)) # /INITIAL_MEMORY @@ -793,69 +737,14 @@ emcc.jsflags += -sLLD_REPORT_UNDEFINED # -g3 debugging info, _huge_. ######################################################################## -######################################################################## -# $(sqlite3-api-build-version.js) injects the build version info into -# the bundle in JSON form. -$(sqlite3-api-build-version.js): $(MKDIR.bld) $(bin.version-info) $(MAKEFILE) - @echo "Making $@..." - @{ \ - echo 'globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){'; \ - echo -n ' sqlite3.version = '; \ - $(bin.version-info) --json; \ - echo ';'; \ - echo '});'; \ - } > $@ - -######################################################################## -# $(sqlite3-license-version.js) contains the license header and -# in-comment build version info. -# -# Maintenance reminder: there are awk binaries out there which do not -# support -e SCRIPT. -$(sqlite3-license-version.js): $(MKDIR.bld) $(sqlite3.h) \ - $(dir.api)/sqlite3-license-version-header.js $(MAKEFILE) - @echo "Making $@..."; { \ - cat $(dir.api)/sqlite3-license-version-header.js; \ - echo '/*'; \ - echo '** This code was built from sqlite3 version...'; \ - echo "**"; \ - awk '/define SQLITE_VERSION/{$$1=""; print "**" $$0}' $(sqlite3.h); \ - awk '/define SQLITE_SOURCE_ID/{$$1=""; print "**" $$0}' $(sqlite3.h); \ - echo "**"; \ - echo "** with the help of Emscripten SDK version $(emcc.version)."; \ - echo '*/'; \ - } > $@ - -######################################################################## -# --post-js and --pre-js are emcc flags we use to append/prepend JS to -# the generated emscripten module file. These rules set up the core -# pre/post files for use by the various builds. --pre-js is used to -# inject code which needs to run as part of the pre-WASM-load phase. -# --post-js injects code which runs after the WASM module is loaded -# and includes the entirety of the library plus some -# Emscripten-specific post-bootstrapping code. -pre-js.js.in = $(dir.api)/pre-js.c-pp.js -post-js.js.in = $(dir.tmp)/post-js.c-pp.js -post-jses.js = \ - $(dir.api)/post-js-header.js \ - $(sqlite3-api.js.in) \ - $(dir.api)/post-js-footer.js -$(post-js.js.in): $(MKDIR.bld) $(post-jses.js) $(MAKEFILE) - @echo "Making $@..." - @for i in $(post-jses.js); do \ - echo "/* BEGIN FILE: $$i */"; \ - cat $$i; \ - echo "/* END FILE: $$i */"; \ - done > $@ - # Undocumented Emscripten feature: if the target file extension is # "mjs", it defaults to ES6 module builds: # https://github.com/emscripten-core/emscripten/issues/14383 sqlite3.wasm = $(dir.dout)/sqlite3.wasm sqlite3-wasm.c = $(dir.api)/sqlite3-wasm.c -sqlite3-wasm.cfiles = $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c) -sqlite3-wasmfs.cfiles = $(sqlite3-wasm.cfiles) +sqlite3-wasm.c.in = $(sqlite3-wasm.c) $(sqlite3_wasm_extra_init.c) +sqlite3-wasmfs.c.in = $(sqlite3-wasm.c.in) # sqlite3-wasm.o vs sqlite3-wasm.c: building against the latter # (predictably) results in a slightly faster binary. We're close # enough to the target speed requirements that the 500ms makes a @@ -863,12 +752,12 @@ sqlite3-wasmfs.cfiles = $(sqlite3-wasm.cfiles) # of building a shared copy of sqlite3-wasm.o to link against. ######################################################################## -######################################################################## -# SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT is used by mkwasmbuilds.c and the +# +# b.call.patch-export-default is used by mkwasmbuilds.c and the # wasmfs build. $1 is 1 if the build mode needs this workaround # (modes: esm, bundler-friendly, node) and 0 if not (vanilla). $2 must # be 0 for all builds except sqlite3-wasmfs.mjs, in which case it must -# be 1. +# be 1. $(3) is an optional log prefix, defaulting to $(logtag.@) # # Reminder for ESM builds: even if we use -sEXPORT_ES6=0, emcc _still_ # adds: @@ -885,12 +774,16 @@ sqlite3-wasmfs.cfiles = $(sqlite3-wasm.cfiles) # # Maintenance reminder: Mac sed works differently than GNU sed, so we # use awk instead of sed for this. -define SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT +# +define b.call.patch-export-default if [ x1 = x$(1) ]; then \ - echo "Fragile workaround for emscripten/issues/18237. See SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT."; \ + echo "$(if $(3),$(3),$(logtag.@)) $(emo.bug) Fragile" \ + "workaround for emscripten/issues/18237." \ + "See b.call.patch-export-default."; \ {\ - awk '/^export default/ && !f{f=1; next} 1' $@ > $@.tmp && mv $@.tmp $@; \ - } || exit $$?; \ + awk '/^export default/ && !f{f=1; next} 1' $@ \ + > $@.tmp && mv $@.tmp $@; \ + } || exit; \ if [ x1 = x$(2) ]; then \ if ! grep -q '^export default' $@; then \ echo "Cannot find export default." 1>&2; \ @@ -901,17 +794,6 @@ fi endef # -# We define these to assist in deps handling to avoid concurrent -# builds (more notes on that below). -# -sqlite3.js = $(dir.dout)/sqlite3.js -sqlite3.mjs = $(dir.dout)/sqlite3.mjs -sqlite3.wasm = $(dir.dout)/sqlite3.wasm -sqlite3-64bit.js = $(dir.dout)/sqlite3-64bit.js -sqlite3-64bit.mjs = $(dir.dout)/sqlite3-64bit.mjs -sqlite3-64bit.wasm = $(dir.dout)/sqlite3-64bit.wasm -EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle - # The various -D... values used by *.c-pp.js include: # # -Dtarget=es6-module: for all ESM module builds @@ -925,273 +807,19 @@ EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle # as string literals so that bundlers' static-analysis tools can # find those files and include them in their bundles. # -# -Dtarget=es6-module -Dtarget=es6-bundler-friendly -Dtarget=node: is -# intended for use by node.js for node.js, as opposed to by -# node.js on behalf of a browser. Mixing -sENVIRONMENT=web and -# -sENVIRONMENT=node leads to ambiguity and confusion on node's -# part, as it's unable to reliably determine whether the target is -# a browser or node. +# -Dtarget=es6-module -Dtarget=node: is intended for use by node.js +# for node.js, as opposed to by node.js on behalf of a +# browser. Mixing -sENVIRONMENT=web and -sENVIRONMENT=node leads to +# ambiguity and confusion on node's part, as it's unable to +# reliably determine whether the target is a browser or node. # -# We repeat: all node.js builds are 100% untested and unsupported. +# To repeat: all node.js builds are 100% untested and unsupported. # ######################################################################## -######################################################################## -# We have to ensure that we do not build $(sqlite3*.*js) in parallel -# for any builds which result in the creation of $(sqlite3.wasm). We -# have no way to build just a .[m]js file without also building the -# .wasm file because the generated .[m]js file has to include info -# about the imports needed by the wasm file, so they have to be built -# together. i.e. we're building $(sqlite3.wasm) multiple times, but -# that's unavoidable (and harmless, but is a significant waste of -# build time). -$(sqlite3.wasm): $(sqlite3.js) -$(sqlite3.mjs): $(sqlite3.js) -$(sqlite3-64bit.wasm): $(sqlite3-64bit.js) -$(sqlite3-64bit.mjs): $(sqlite3-64bit.js) -$(dir.dout)/sqlite3-bundler-friendly.mjs: $(sqlite3.mjs) -$(dir.dout)/sqlite3-node.mjs: $(sqlite3.mjs) -#CLEAN_FILES += $(sqlite3.wasm) - -# This block MUST come between the above definitions of -# sqlite3-...js/mjs and the $(eval) calls below this block which use -# SQLITE.CALL.C-PP.FILTER. -######################################################################## -# bin.mkwb is used for generating much of the makefile code for the -# various wasm builds. It used to be generated in this makefile via a -# difficult-to-read/maintain block of $(eval)'d code. Attempts were -# made to generate it from tcl and bash (shell) but having to escape -# the $ references in those languages made it just as illegible as the -# native makefile code. Somewhat surprisingly, moving that code generation -# to C makes it slightly less illegible than the previous 3 options. # -# Maintenance note: the various $(c-pp.D.XYZ) vars are defined via -# $(bin.mkwb). -bin.mkwb = ./mkwasmbuilds -ifneq (1,$(MAKING_CLEAN)) -$(bin.mkwb): $(bin.mkwb).c $(MAKEFILE) - $(CC) -g -std=c99 -o $@ $< -DWASM_CUSTOM_INSTANTIATE=$(WASM_CUSTOM_INSTANTIATE) - -.wasmbuilds.make: $(bin.mkwb) - @rm -f $@ - $(bin.mkwb) > $@ - @chmod -w $@ --include .wasmbuilds.make -endif -CLEAN_FILES += .wasmbuilds.make $(bin.mkwb) - -######################################################################## -# We need separate copies of certain supplementary JS files for the -# bundler-friendly build. Concretely, any supplemental JS files which -# themselves use importScripts() or Workers or URL() constructors -# which refer to other in-tree (m)JS files require a bundler-friendly -# copy. Bundler-friendly builds replace certain references to string -# vars/expressions with string literals, as bundler tools are static -# code analyzers and cannot cope with the former. -# -# Most of what follows is the generation of those copies. -$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1.c-pp.js,\ - $(dir.dout)/sqlite3-worker1.js)) -$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1.c-pp.js,\ - $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs,\ - $(c-pp.D.sqlite3-bundler-friendly))) -$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\ - $(dir.dout)/sqlite3-worker1-promiser.js)) -$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\ - $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js,\ - $(c-pp.D.sqlite3-bundler-friendly))) -$(eval $(call SQLITE.CALL.C-PP.FILTER,$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\ - $(dir.dout)/sqlite3-worker1-promiser.mjs,\ - -Dtarget=es6-module -Dtarget=es6-bundler-friendly)) -$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.js)) -$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.js,demo-worker1-promiser.mjs,\ - -Dtarget=es6-module)) -$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser.html)) -$(eval $(call SQLITE.CALL.C-PP.FILTER,demo-worker1-promiser.c-pp.html,demo-worker1-promiser-esm.html,\ - -Dtarget=es6-module)) - -$(dir.dout)/sqlite3-bundler-friendly.mjs: \ - $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs \ - $(dir.dout)/sqlite3-worker1-promiser-bundler-friendly.js - -demo-worker1-promiser.html: $(dir.dout)/sqlite3-worker1-promiser.js demo-worker1-promiser.js -demo-worker1-promiser-esm.html: $(sqlite3-worker1-promiser.mjs) demo-worker1-promiser.mjs -all: demo-worker1-promiser.html demo-worker1-promiser-esm.html - -sqlite3-api.ext.jses += \ - $(dir.dout)/sqlite3-worker1-promiser.mjs \ - $(dir.dout)/sqlite3-worker1-promiser.js \ - $(dir.dout)/sqlite3-worker1-bundler-friendly.mjs \ - $(dir.dout)/sqlite3-worker1.js -all quick: $(sqlite3-api.ext.jses) -q: quick -64bit: # populated by $(bin.mkwb) - -######################################################################## -# batch-runner.js is part of one of the test apps which reads in SQL -# dumps generated by $(speedtest1) and executes them. -dir.sql = sql -speedtest1 = ../../speedtest1 -speedtest1.c = ../../test/speedtest1.c -speedtest1.sql = $(dir.sql)/speedtest1.sql -speedtest1.cliflags = --size 10 --big-transactions -$(speedtest1): - $(MAKE) -C ../.. speedtest1 -$(speedtest1.sql): $(speedtest1) $(MAKEFILE) - $(speedtest1) $(speedtest1.cliflags) --script $@ -batch-runner.list: $(MAKEFILE) $(speedtest1.sql) $(dir.sql)/000-mandelbrot.sql - bash split-speedtest1-script.sh $(dir.sql)/speedtest1.sql - ls -1 $(dir.sql)/*.sql | grep -v speedtest1.sql | sort > $@ -clean-batch: - rm -f batch-runner.list $(dir.sql)/speedtest1*.sql -# ^^^ we don't do this along with 'clean' because we clean/rebuild on -# a regular basis with different -Ox flags and rebuilding the batch -# pieces each time is an unnecessary time sink. -batch: batch-runner.list -#all: batch -# end batch-runner.js -######################################################################## -# Wasmified speedtest1 is our primary benchmarking tool. -# -# emcc.speedtest1.common = emcc flags used by multiple builds of speedtest1 -# emcc.speedtest1 = emcc flags used by main build of speedtest1 -emcc.speedtest1.common = $(emcc_opt_full) -emcc.speedtest1 = -I. -I$(dir $(sqlite3.canonical.c)) -emcc.speedtest1 += -sENVIRONMENT=web -emcc.speedtest1 += -sALLOW_MEMORY_GROWTH -emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY)) -emcc.speedtest1.common += -sINVOKE_RUN=0 -emcc.speedtest1.common += --no-entry -emcc.speedtest1.common += -sABORTING_MALLOC -emcc.speedtest1.common += -sSTRICT_JS=0 -emcc.speedtest1.common += -sMODULARIZE -emcc.speedtest1.common += -Wno-limited-postlink-optimizations -emcc.speedtest1.common += -Wno-unused-main -# ^^^^ -Wno-unused-main is for emcc 3.1.52+. speedtest1 has a wasm_main() which is -# exported and called by the JS code. -EXPORTED_FUNCTIONS.speedtest1 = $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1) -emcc.speedtest1.common += -sSTACK_SIZE=512KB -emcc.speedtest1.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1) -emcc.speedtest1.common += $(emcc.exportedRuntimeMethods) -emcc.speedtest1.common += -sALLOW_TABLE_GROWTH -emcc.speedtest1.common += -sDYNAMIC_EXECUTION=0 -emcc.speedtest1.common += --minify 0 -emcc.speedtest1.common += -sEXPORT_NAME=$(sqlite3.js.init-func) -emcc.speedtest1.common += -sWASM_BIGINT=$(emcc.WASM_BIGINT) -emcc.speedtest1.common += -sMEMORY64=$(emcc.MEMORY64) - -speedtest1.exit-runtime0 = -sEXIT_RUNTIME=0 -speedtest1.exit-runtime1 = -sEXIT_RUNTIME=1 -# Re -sEXIT_RUNTIME=1 vs 0: if it's 1 and speedtest1 crashes, we get -# this error from Emscripten: -# -# > native function `free` called after runtime exit (use -# NO_EXIT_RUNTIME to keep it alive after main() exits)) -# -# If it's 0 and it crashes, we get: -# -# > stdio streams had content in them that was not flushed. you should -# set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline -# when you printf etc. -# -# and pending output is not flushed because it didn't end with a -# newline (by design). The lesser of the two evils seems to be -# -sEXIT_RUNTIME=1 but we need EXIT_RUNTIME=0 for the worker-based app -# which runs speedtest1 multiple times. - -$(EXPORTED_FUNCTIONS.speedtest1): $(MKDIR.bld) $(EXPORTED_FUNCTIONS.api.core) - @echo "Making $@ ..." - @{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.core); } > $@ -speedtest1.js = $(dir.dout)/speedtest1.js -emcc.flags.speedtest1-vanilla = $(cflags.common) -DSQLITE_SPEEDTEST1_WASM -speedtest1.cfiles = $(speedtest1.c) $(sqlite3-wasm.c) -$(speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \ - $(pre-post-speedtest1-vanilla.deps) \ - $(EXPORTED_FUNCTIONS.speedtest1) - @echo "Building $@ ..." - $(bin.emcc) \ - $(emcc.speedtest1) \ - $(emcc.speedtest1.common) \ - $(emcc.flags.speedtest1-vanilla) $(pre-post-speedtest1-vanilla.flags) \ - $(SQLITE_OPT) \ - -USQLITE_WASM_BARE_BONES \ - -USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c) \ - $(speedtest1.exit-runtime0) \ - -o $@ $(speedtest1.cfiles) -lm - @chmod -x $(basename $@).wasm - @$(maybe-wasm-strip) $(basename $@).wasm - @$(SQLITE.strip-createExportWrapper) - @ls -la $@ $(speedtest1.wasm) - -speedtest1: $(speedtest1.js) -all: speedtest1 -# end speedtest1.js -######################################################################## - -######################################################################## -# tester1 is the main unit and regression test application and needs -# to be able to run in 4 separate modes to cover the primary -# client-side use cases: -# -# 1) Load sqlite3 in the main UI thread of a conventional script. -# 2) Load sqlite3 in a conventional Worker thread. -# 3) Load sqlite3 as an ES6 module (ESM) in the main thread. -# 4) Load sqlite3 as an ESM worker. (Not all browsers support this.) -# -# To that end, we require two separate builds of tester1.js: -# -# tester1.js: cases 1 and 2 -# tester1.mjs: cases 3 and 4 -# -# To create those, we filter tester1.c-pp.js with $(bin.c-pp)... -$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.js,tester1.js)) -$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.js,tester1.mjs,$(c-pp.D.sqlite3-esm))) -$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.html,tester1.html)) -$(eval $(call SQLITE.CALL.C-PP.FILTER,tester1.c-pp.html,tester1-esm.html,$(c-pp.D.sqlite3-esm))) -tester1: tester1.js tester1.mjs tester1.html tester1-esm.html -# We do not include $(dir.dout)/sqlite3-bundler-friendly.mjs in this -# because bundlers are client-specific. -all quick: tester1 -quick: $(sqlite3.js) - -######################################################################## -# Convenience rules to rebuild with various -Ox levels. Much -# experimentation shows -O2 to be the clear winner in terms of speed. -# Note that build times with anything higher than -O0 are somewhat -# painful. - -.PHONY: o0 o1 o2 o3 os oz -emcc-opt-extra = -#ifeq (1,$(wasm-bare-bones)) -#emcc-opt-extra += -flto -# ^^^^ -flto can have a considerably performance boost at -O0 but -# doubles the build time and seems to have negligible, if any, effect -# on higher optimization levels. -# -# -flto does not shrink the size of bare-bones builds by any measurable -# amount. -#endif -o0: clean - $(MAKE) -e "emcc_opt=-O0" -o1: clean - $(MAKE) -e "emcc_opt=-O1 $(emcc-opt-extra)" -o2: clean - $(MAKE) -j2 -e "emcc_opt=-O2 $(emcc-opt-extra)" -o3: clean - $(MAKE) -e "emcc_opt=-O3 $(emcc-opt-extra)" -os: clean - @echo "WARNING: -Os can result in a build with mysteriously missing pieces!" - $(MAKE) -e "emcc_opt=-Os $(emcc-opt-extra)" -oz: clean - $(MAKE) -j2 -e "emcc_opt=-Oz $(emcc-opt-extra)" - -######################################################################## -# Sub-makes... - -# https://sqlite.org/fiddle application... -include $(MAKEFILE.fiddle) - # Only add wasmfs if wasmfs.enable=1 or we're running (dist)clean +# ifneq (,$(filter wasmfs,$(MAKECMDGOALS))) wasmfs.enable ?= 1 else @@ -1218,18 +846,481 @@ $(info This platform does not support the WASMFS build.) HAVE_WASMFS = 0 else HAVE_WASMFS = 1 -include wasmfs.make endif endif # /wasmfs ######################################################################## +# +# Inputs/outputs for the sqlite3-api.js family. +# +# sqlite3-api.jses = the list of JS files which make up +# sqlite3-api.js, in the order they need to be assembled. +sqlite3-api.jses = $(sqlite3-license-version.js) +sqlite3-api.jses += $(dir.api)/sqlite3-api-prologue.js +sqlite3-api.jses += $(dir.common)/whwasmutil.js +sqlite3-api.jses += $(dir.jacc)/jaccwabyt.js +sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.c-pp.js +sqlite3-api.jses += $(sqlite3-api-build-version.js) +sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.c-pp.js +sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.c-pp.js +sqlite3-api.jses += $(dir.api)/sqlite3-vfs-helper.c-pp.js +ifeq (0,$(wasm-bare-bones)) + sqlite3-api.jses += $(dir.api)/sqlite3-vtab-helper.c-pp.js +endif +sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs.c-pp.js +sqlite3-api.jses += $(dir.api)/sqlite3-vfs-opfs-sahpool.c-pp.js +sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js + +# +# $(sqlite3-license-version.js) contains the license header and +# in-comment build version info. +# +# Maintenance reminder: there are awk binaries out there which do not +# support -e SCRIPT. +# +sqlite3-license-version.js = $(dir.tmp)/sqlite3-license-version.js +$(sqlite3-license-version.js): $(bin.version-info) \ + $(dir.api)/sqlite3-license-version-header.js + @echo '$(logtag.@) $(emo.disk)'; { \ + $(call b.mkdir@); \ + cat $(dir.api)/sqlite3-license-version-header.js || exit $$?; \ + echo '/*'; \ + echo '** This code was built from sqlite3 version...'; \ + echo '**'; \ + awk '/define SQLITE_VERSION/{$$1=""; print "**" $$0}' $(sqlite3.h); \ + awk '/define SQLITE_SOURCE_ID/{$$1=""; print "**" $$0}' $(sqlite3.h); \ + echo '**'; echo '** Emscripten SDK: $(emcc.version)'; \ + echo '**'; \ + echo '*/'; \ + } > $@ + +# +# $(sqlite3-api-build-version.js) injects the build version info into +# the bundle in JSON form. +# +sqlite3-api-build-version.js = $(dir.tmp)/sqlite3-api-build-version.js +$(sqlite3-api-build-version.js): $(bin.version-info) $(MAKEFILE) + @echo '$(logtag.@) $(emo.disk)'; { \ + $(call b.mkdir@); \ + echo 'globalThis.sqlite3ApiBootstrap.initializers.push(function(sqlite3){'; \ + echo -n ' sqlite3.version = '; \ + $(bin.version-info) --json; \ + echo ';'; \ + echo '});'; \ + } > $@ + +# +# extern-post-js* and extern-pre-js* are files for use with +# Emscripten's --extern-pre-js and --extern-post-js flags. +# +extern-pre-js.js = $(dir.api)/extern-pre-js.js +extern-post-js.in.js = $(dir.api)/extern-post-js.c-pp.js + +# +# Emscripten flags for --[extern-][pre|post]-js=... for the +# various builds. +# pre-post-jses.*.deps = lists of dependencies for the +# --[extern-][pre/post]-js files. +# +pre-post-jses.common.deps = $(extern-pre-js.js) $(sqlite3-license-version.js) + +# --post-js and --pre-js are emcc flags we use to append/prepend JS to +# the generated emscripten module file. These rules set up the core +# pre/post files for use by the various builds. --pre-js is used to +# inject code which needs to run as part of the pre-WASM-load phase. +# --post-js injects code which runs after the WASM module is loaded +# and includes the entirety of the library plus some +# Emscripten-specific post-bootstrapping code. +pre-js.in.js = $(dir.api)/pre-js.c-pp.js +post-js.in.js = $(dir.tmp)/post-js.c-pp.js +post-jses.js = \ + $(dir.api)/post-js-header.js \ + $(sqlite3-api.jses) \ + $(dir.api)/post-js-footer.js +$(post-js.in.js): $(MKDIR.bld) $(post-jses.js) $(MAKEFILE) + @$(call b.echo,@,$(emo.disk)); \ + for i in $(post-jses.js); do \ + echo "/* BEGIN FILE: $$i */"; \ + cat $$i || exit $$?; \ + echo "/* END FILE: $$i */"; \ + done > $@ + +# +# speedtest1 decls needed before the $(bin.mkws)-generated makefile +# is included. +# +bin.speedtest1 = ../../speedtest1 +speedtest1.c = ../../test/speedtest1.c +speedtest1.c.in = $(speedtest1.c) $(sqlite3-wasm.c) +EXPORTED_FUNCTIONS.speedtest1 = $(abspath $(dir.tmp)/EXPORTED_FUNCTIONS.speedtest1) + +# +# fiddle build flags +# +# Flags specifically for debug builds of fiddle. Performance suffers +# greatly in debug builds. ######################################################################## -# Push files to public wasm-testing.sqlite.org server +# shell.c and its build flags... +# +# We should ideally collect these from ../../configure and past +# them in ./config.make. The problem with that is that SHELL_OPT is +# generated at make-time, not configure-time. +ifneq (1,$(MAKING_CLEAN)) + make-np-0 = make -C $(dir.top) -n -p + make-np-1 = sed -e 's/(TOP)/(dir.top)/g' + # Extract SHELL_OPT and SHELL_DEP from the top-most makefile and import + # them as vars here... + $(eval $(shell $(make-np-0) | grep -e '^SHELL_OPT ' | $(make-np-1))) + $(eval $(shell $(make-np-0) | grep -e '^SHELL_DEP ' | $(make-np-1))) + # ^^^ can't do that in 1 invocation b/c newlines get stripped + ifeq (,$(SHELL_OPT)) + $(error Could not parse SHELL_OPT from $(dir.top)/Makefile.) + endif + ifeq (,$(SHELL_DEP)) + $(error Could not parse SHELL_DEP from $(dir.top)/Makefile.) + endif +$(dir.top)/shell.c: $(SHELL_DEP) $(dir.tool)/mkshellc.tcl $(sqlite3.c) + $(MAKE) -C $(dir.top) shell.c +endif +# /shell.c +######################################################################## + +EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle +$(EXPORTED_FUNCTIONS.fiddle): $(fiddle.EXPORTED_FUNCTIONS.in) $(MAKEFILE_LIST) + @$(b.call.mkdir@) + @sort -u $(fiddle.EXPORTED_FUNCTIONS.in) > $@ + @echo $(logtag.@) $(emo.disk) + +emcc.flags.fiddle = \ + $(emcc.cflags) $(emcc_opt_full) \ + --minify 0 \ + -sALLOW_TABLE_GROWTH \ + -sMEMORY64=$(emcc.MEMORY64) \ + -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.8) \ + -sABORTING_MALLOC \ + -sSTRICT_JS=0 \ + -sENVIRONMENT=web,worker \ + -sMODULARIZE \ + -sDYNAMIC_EXECUTION=0 \ + -sWASM_BIGINT=$(emcc.WASM_BIGINT) \ + -sEXPORT_NAME=$(sqlite3.js.init-func) \ + -Wno-limited-postlink-optimizations \ + $(emcc.exportedRuntimeMethods),FS \ + -sEXPORTED_FUNCTIONS=@$(abspath $(EXPORTED_FUNCTIONS.fiddle)) \ + $(SQLITE_OPT.full-featured) \ + $(SQLITE_OPT.common) \ + $(SHELL_OPT) \ + -UHAVE_READLINE -UHAVE_EDITLINE -UHAVE_LINENOISE \ + -USQLITE_HAVE_ZLIB \ + -USQLITE_WASM_BARE_BONES \ + -DSQLITE_SHELL_FIDDLE + +clean: clean-fiddle +clean-fiddle: + rm -f $(dir.fiddle)/fiddle-module.js \ + $(dir.fiddle)/*.wasm \ + $(dir.fiddle)/sqlite3-opfs-*.js \ + $(dir.fiddle)/*.gz \ + EXPORTED_FUNCTIONS.fiddle + rm -fr $(dir.fiddle-debug) + +emcc.flags.fiddle.debug = $(emcc.flags.fiddle) \ + -DSQLITE_DEBUG \ + -DSQLITE_ENABLE_SELECTTRACE \ + -DSQLITE_ENABLE_WHERETRACE + +fiddle.EXPORTED_FUNCTIONS.in = \ + EXPORTED_FUNCTIONS.fiddle.in \ + $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-core \ + $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-extras + +fiddle.c.in = $(dir.top)/shell.c $(sqlite3-wasm.c) + +# +# bin.mkwb is used for generating much of the makefile code for the +# various wasm builds. It used to be generated in this makefile via a +# difficult-to-read/maintain block of $(eval)'d code. Attempts were +# made to generate it from tcl and bash (shell) but having to escape +# the $ references in those languages made it just as illegible as the +# native makefile code. Somewhat surprisingly, moving that code generation +# to C makes it slightly less illegible than the previous 3 options. +# +# Maintenance notes: +# +# - Ordering of this block within this file is fragile. The generated +# makefile sets up many vars which are useful for the other targets. +# +# - Vars which are used by $(bin.mkwb) in dependency lists and such +# need to be defined before this is included. Those used in recipies +# do not. +# +bin.mkwb = ./mkwasmbuilds +ifneq (1,$(MAKING_CLEAN)) +$(bin.mkwb): $(bin.mkwb).c $(MAKEFILE) + $(CC) -g -std=c99 -o $@ $< -DWASM_CUSTOM_INSTANTIATE=$(WASM_CUSTOM_INSTANTIATE) + +.wasmbuilds.make: $(bin.mkwb) + @rm -f $@ + $(bin.mkwb) > $@ + @chmod -w $@ +-include .wasmbuilds.make +endif +CLEAN_FILES += .wasmbuilds.make $(bin.mkwb) + +# +# $(sqlite3.ext.js) = API-related files which are standalone +# files, not part of the amalgamation. This list holds +# the name of each such _output_ file. +# +sqlite3.ext.js = + +######################################################################## +# We need separate copies of certain supplementary JS files for the +# bundler-friendly build. Concretely, any supplemental JS files which +# themselves use importScripts() or Workers or URL() constructors +# which refer to other in-tree (m)JS files require a bundler-friendly +# copy. Bundler-friendly builds replace certain references to string +# vars/expressions with string literals, as bundler tools are static +# code analyzers and cannot cope with the former. + +# +# sqlite3-worker1*.* +# TODO: 64-bit +# +define gen-worker1 +# $1 = X.ext part of sqlite3-worker1X.ext +# $2 = $(c-pp.D.NAME) +$(call b.eval.c-pp,filter,$(dir.api)/sqlite3-worker1.c-pp.js,\ + $(dir.dout)/sqlite3-worker1$(1),$(2)) +sqlite3.ext.js += $(dir.dout)/sqlite3-worker1$(1) +all: $(dir.dout)/sqlite3-worker1$(1) +endef + +$(eval $(call gen-worker1,.js,$(c-pp.D.vanilla))) +$(eval $(call gen-worker1,.mjs,$(c-pp.D.esm))) +$(eval $(call gen-worker1,-bundler-friendly.mjs,$(c-pp.D.bundler))) + +# +# sqlite3-worker1-promiser*.* +# TODO: 64-bit +# +define gen-promiser +# $1 = X.ext part of sqlite3-worker1-promiserX.ext +# $2 = $(c-pp.D.NAME) +$(call b.eval.c-pp,filter,$(dir.api)/sqlite3-worker1-promiser.c-pp.js,\ + $(dir.dout)/sqlite3-worker1-promiser$(1),$(2)) +sqlite3.ext.js += $(dir.dout)/sqlite3-worker1-promiser$(1) +all: $(dir.dout)/sqlite3-worker1-promiser$(1) +endef + +$(eval $(call gen-promiser,.js,$(c-pp.D.vanilla))) +$(eval $(call gen-promiser,.mjs,$(c-pp.D.esm))) +$(eval $(call gen-promiser,-bundler-friendly.mjs,$(c-pp.D.bundler))) + +# +# demo1-worker-*.*: +# $1 = .js or .mjs +# $2 = .html or -esm.html +# $3 = -D... flags for $(bin.c-pp) +# +define gen-dwp +$(call b.eval.c-pp,test,demo-worker1-promiser.c-pp.js,demo-worker1-promiser$(1),$(3)) +$(call b.eval.c-pp,test,demo-worker1-promiser.c-pp.html,demo-worker1-promiser$(2),$(3)) +demos: demo-worker1-promiser$(1) demo-worker1-promiser$(2) +endef +$(eval $(call gen-dwp,.js,.html,$(c-pp.D.vanilla))) +$(eval $(call gen-dwp,.mjs,-esm.html,$(c-pp.D.esm))) +all: demos +# End worker/promiser generation +####################################################################### + +# +# "SOAP" is a static file which is not part of the amalgamation but +# gets copied into the build output folder and into each of the fiddle +# builds. +# +sqlite3.ext.js += $(dir.dout)/sqlite3-opfs-async-proxy.js +$(dir.dout)/sqlite3-opfs-async-proxy.js: $(dir.api)/sqlite3-opfs-async-proxy.js + @$(call b.cp,@,$<,$@) + +# +# Add a dep of $(sqlite3.ext.js) on every individual build's JS file. +# The primary purpose of this is to force them to be copied early in +# the build process, which is sometimes a time-saver during +# development, allowing the developer to reload a test page while +# other parts of the build are still running. Another reason is that +# we don't otherwise have a great place to attach them such that +# they're always copied when we need them. +# +$(foreach B,$(b.names),$(eval $(out.$(B).js): $(sqlite3.ext.js))) + +# +# speedtest1 is our primary benchmarking tool. +# +# emcc.speedtest1.common = emcc flags used by multiple builds of speedtest1 +# emcc.speedtest1 = emcc flags used by the main build of speedtest1 +# +# These flags get applied via $(bin.mkwb). +emcc.speedtest1.common = $(emcc_opt_full) +emcc.speedtest1 = -I. -I$(dir $(sqlite3.canonical.c)) +emcc.speedtest1 += -sENVIRONMENT=web +emcc.speedtest1 += -sALLOW_MEMORY_GROWTH +emcc.speedtest1 += -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.$(emcc.INITIAL_MEMORY)) +emcc.speedtest1.common += -sINVOKE_RUN=0 +emcc.speedtest1.common += --no-entry +emcc.speedtest1.common += -sABORTING_MALLOC +emcc.speedtest1.common += -sSTRICT_JS=0 +emcc.speedtest1.common += -sMODULARIZE +emcc.speedtest1.common += -Wno-limited-postlink-optimizations +emcc.speedtest1.common += -Wno-unused-main +# ^^^^ -Wno-unused-main is for emcc 3.1.52+. speedtest1 has a wasm_main() which is +# exported and called by the JS code. +emcc.speedtest1.common += -sSTACK_SIZE=512KB +emcc.speedtest1.common += -sEXPORTED_FUNCTIONS=@$(EXPORTED_FUNCTIONS.speedtest1) +emcc.speedtest1.common += $(emcc.exportedRuntimeMethods) +emcc.speedtest1.common += -sALLOW_TABLE_GROWTH +emcc.speedtest1.common += -sDYNAMIC_EXECUTION=0 +emcc.speedtest1.common += --minify 0 +emcc.speedtest1.common += -sEXPORT_NAME=$(sqlite3.js.init-func) +emcc.speedtest1.common += -sWASM_BIGINT=$(emcc.WASM_BIGINT) +emcc.speedtest1.common += -sMEMORY64=$(emcc.MEMORY64) +speedtest1.exit-runtime0 = -sEXIT_RUNTIME=0 +speedtest1.exit-runtime1 = -sEXIT_RUNTIME=1 +# Re -sEXIT_RUNTIME=1 vs 0: if it's 1 and speedtest1 crashes, we get +# this error from Emscripten: +# +# > native function `free` called after runtime exit (use +# NO_EXIT_RUNTIME to keep it alive after main() exits)) +# +# If it's 0 and it crashes, we get: +# +# > stdio streams had content in them that was not flushed. you should +# set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline +# when you printf etc. +# +# and pending output is not flushed because it didn't end with a +# newline (by design). The lesser of the two evils seems to be +# -sEXIT_RUNTIME=1 but we need EXIT_RUNTIME=0 for the worker-based app +# which runs speedtest1 multiple times. + +$(EXPORTED_FUNCTIONS.speedtest1): $(EXPORTED_FUNCTIONS.api.core) + @echo "Making $@ ..." + @$(call b.mkdir@) + @{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.core); } > $@ +speedtest1: $(out.speedtest1.js) +# end speedtest1.js +######################################################################## + +# +# tester1 is the main unit and regression test application and needs +# to be able to run in 4 separate modes to cover the primary +# client-side use cases: +# +# 1) Load sqlite3 in the main UI thread of a conventional script. +# 2) Load sqlite3 in a conventional Worker thread. +# 3) Load sqlite3 as an ES6 module (ESM) in the main thread. +# 4) Load sqlite3 as an ESM worker. (Not all browsers support this.) +# +# To that end, we require two separate builds of tester1.js: +# +# tester1.js: cases 1 and 2 +# tester1.mjs: cases 3 and 4 +# +# Then we need those again in 64-bit builds, which require a 64-bit +# pair of js/wasm files. +# +# To create those, we filter tester1.c-pp.js/html with $(bin.c-pp)... + +# tester1.js variants: +define gen-tester1.js +# $1 = build name to have dep on +# $2 = suffix for tester1SUFFIX JS +# $3 = c-pp flags +$(call b.eval.c-pp,test,tester1.c-pp.js,tester1$(2),$(3)) +tester1$(2): $(sqlite3.ext.js) $(out.$(1).wasm) +tester1: tester1$(2) +tester1-$(1): tester1$(2) +endef + +$(eval $(call gen-tester1.js,vanilla,.js,$(c-pp.D.vanilla))) +$(eval $(call gen-tester1.js,vanilla64,-64bit.js,$(c-pp.D.vanilla64))) +$(eval $(call gen-tester1.js,esm,.mjs,$(c-pp.D.esm))) +$(eval $(call gen-tester1.js,esm64,-64bit.mjs,$(c-pp.D.esm64))) + +# tester1.html variants: +define gen-tester1.html +# $1 = build name to have a dep on +# $2 = file suffix: empty, -64bit, -esm, esm-64bit +# $3 = c-pp -D flags. +$(call b.eval.c-pp,test,tester1.c-pp.html,tester1$(2).html,$(3)) +tester1$(2).html: tester1-$(1) +tester1: tester1$(2).html +endef + +$(eval $(call gen-tester1.html,vanilla,,$(c-pp.D.vanilla))) +$(eval $(call gen-tester1.html,vanilla64,-64bit,$(c-pp.D.vanilla64))) +$(eval $(call gen-tester1.html,esm,-esm,$(c-pp.D.esm64))) +$(eval $(call gen-tester1.html,esm64,-esm-64bit,$(c-pp.D.esm64))) + +# tester1-worker.html variants: +# There is no ESM variant of this file. Instead, that page accepts a +# ?esm URL flag to switch to ESM mode. +$(eval $(call b.eval.c-pp,test,tester1-worker.c-pp.html,tester1-worker.html)) +$(eval $(call b.eval.c-pp,test,tester1-worker.c-pp.html,tester1-worker-64bit.html,$(c-pp.D.64bit))) +tester: tester1-worker.html tester1-worker-64bit.html + +all: tester1 +# end tester1 +######################################################################## + +# +# Convenience rules to rebuild with various -Ox levels. Much +# experimentation shows -O2 to be the clear winner in terms of speed. +# -Oz results are significantly smaller and only slightly slower than +# -O2 (very roughly 10% in highly unscientific tests), so -Oz is the +# shipping configuration. +# +# Achtung: build times with anything higher than -O0 are somewhat +# painful, which is why -O0 is the default. +.PHONY: o0 o1 o2 o3 os oz +emcc-opt-extra = +#ifeq (1,$(wasm-bare-bones)) +#emcc-opt-extra += -flto +# ^^^^ -flto can have a considerably performance boost at -O0 but +# doubles the build time and seems to have negligible, if any, effect +# on higher optimization levels. +# +# -flto does not shrink the size of bare-bones builds by any measurable +# amount. +#endif +o0: clean + $(MAKE) -e "emcc_opt=-O0" +o1: clean + $(MAKE) -e "emcc_opt=-O1 $(emcc-opt-extra)" +o2: clean + $(MAKE) -e "emcc_opt=-O2 $(emcc-opt-extra)" +o3: clean + $(MAKE) -e "emcc_opt=-O3 $(emcc-opt-extra)" +os: clean + @echo "$(emo.fire)WARNING$(emo.fire): -Os can result in a build with mysteriously missing pieces!" + $(MAKE) -e "emcc_opt=-Os $(emcc-opt-extra)" +oz: clean + $(MAKE) -e "emcc_opt=-Oz $(emcc-opt-extra)" + +# +# Push files to the public wasm-testing.sqlite.org server. +# +# Ideally only -Oz builds should be pushed, so the practice has become: +# +# make clean +# make -j4 for-testing +# make push-testing +# wasm-testing.include = *.js *.mjs *.html \ ./tests \ - $(dir.dout) $(dir.common) $(dir.fiddle) $(dir.fiddle-debug) $(dir.jacc) -wasm-testing.exclude = sql/speedtest1.sql + $(dir.dout) $(dir.common) $(dir.fiddle) $(dir.fiddle.debug) $(dir.jacc) +wasm-testing.exclude = sql/speedtest1.sql jswasm/*/* wasm-testing.dir = /jail/sites/wasm-testing wasm-testing.dest ?= wasm-testing:$(wasm-testing.dir) # ---------------------^^^^^^^^^^^^ ssh alias @@ -1242,6 +1333,18 @@ push-testing: ssh wasm-testing 'cd $(wasm-testing.dir) && bash .gzip' || \ echo "SSH failed: it's likely that stale content will be served via old gzip files." +# build everything needed by push-testing with -Oz +.PHONY: for-testing +for-testing: emcc_opt=-Oz +for-testing: loud=1 +for-testing.deps = \ + tester1 demos \ + b-vanilla b-vanilla64 \ + b-esm b-esm64 \ + b-fiddle b-fiddle.debug \ + speedtest1 +for-testing: $(for-testing.deps) + ######################################################################## # If we find a copy of https://sqlite.org/wasm checked out, copy # certain files over to it, applying some automated edits... @@ -1256,12 +1359,12 @@ update-docs: exit 127 else wasm.docs.jswasm = $(wasm.docs.home)/jswasm -update-docs: $(bin.stripccomments) $(sqlite3.js) $(sqlite3.wasm) - @echo "Copying files to the /wasm docs. Be sure to use an -Oz build for this!" - cp $(sqlite3.wasm) $(wasm.docs.jswasm)/. - $(bin.stripccomments) -k -k < $(sqlite3.js) \ +update-docs: $(bin.stripccomments) $(out.sqlite3.js) $(out.sqlite3.wasm) + @echo "Copying files to the /wasm docs. Be sure to use an -Oz build for this!"; + cp -p $(sqlite3.wasm) $(wasm.docs.jswasm)/. + $(bin.stripccomments) -k -k < $(out.vanilla.js) \ | sed -e '/^[ \t]*$$/d' > $(wasm.docs.jswasm)/sqlite3.js - cp demo-123.js demo-123.html demo-123-worker.html $(wasm.docs.home) + cp -p demo-123.js demo-123.html demo-123-worker.html $(wasm.docs.home)/. sed -n -e '/EXTRACT_BEGIN/,/EXTRACT_END/p' \ module-symbols.html > $(wasm.docs.home)/module-symbols.html endif @@ -1277,3 +1380,117 @@ endif # Run local web server for the test/demo pages. httpd: althttpd -max-age 1 -enable-sab 1 -page index.html + +######################################################################## +# fiddle_remote is the remote destination for the fiddle app. It must +# be a [user@]HOST:/path for rsync. The target "should probably" +# contain a symlink of index.html -> fiddle.html. +fiddle_remote ?= +ifeq (,$(fiddle_remote)) +ifneq (,$(wildcard /home/stephan)) + fiddle_remote = wh:www/wasm-testing/fiddle/. +else ifneq (,$(wildcard /home/drh)) + #fiddle_remote = if appropriate, add that user@host:/path here +endif +endif +push-fiddle: fiddle + @if [ x = "x$(fiddle_remote)" ]; then \ + echo "fiddle_remote must be a [user@]HOST:/path for rsync"; \ + exit 1; \ + fi + rsync -va fiddle/ $(fiddle_remote) +# end fiddle remote push +######################################################################## + +.PHONY: clean distclean +clean: + -rm -f $(CLEAN_FILES) + -rm -fr $(dir.dout) $(dir.tmp) + +distclean: clean + -rm -f $(DISTCLEAN_FILES) + +CLEAN_FILES += *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~ $(dir.fiddle)/*~ \ + +######################################################################## +# Explanation of, and some commentary on, various emcc build flags +# follows. Full docs for these can be found at: +# +# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js +# +# -sENVIRONMENT=web: elides bootstrap code related to non-web JS +# environments like node.js. Removing this makes the output a tiny +# tick larger but hypothetically makes it more portable to +# non-browser JS environments. +# +# -sMODULARIZE: changes how the generated code is structured to avoid +# declaring a global Module object and instead installing a function +# which loads and initializes the module. The function is named... +# +# -sEXPORT_NAME=jsFunctionName (see -sMODULARIZE) +# +# -sEXPORTED_RUNTIME_METHODS=@/absolute/path/to/file: a file +# containing a list of emscripten-supplied APIs, one per line, which +# must be exported into the generated JS. Must be an absolute path! +# +# -sEXPORTED_FUNCTIONS=@/absolute/path/to/file: a file containing a +# list of C functions, one per line, which must be exported via wasm +# so they're visible to JS. C symbols names in that file must all +# start with an underscore for reasons known only to the emcc +# developers. e.g., _sqlite3_open_v2 and _sqlite3_finalize. Must be +# an absolute path! +# +# -sSTRICT_JS ensures that the emitted JS code includes the 'use +# strict' option. Note that -sSTRICT is more broadly-scoped and +# results in build errors. +# +# -sALLOW_TABLE_GROWTH is required for (at a minimum) the UDF-binding +# feature. Without it, JS functions cannot be made to proxy C-side +# callbacks. +# +# -sABORTING_MALLOC causes the JS-bound _malloc() to abort rather than +# return 0 on OOM. If set to 0 then all code which uses _malloc() +# must, just like in C, check the result before using it, else +# they're likely to corrupt the JS/WASM heap by writing to its +# address of 0. It is, as of this writing, enabled in Emscripten by +# default but we enable it explicitly in case that default changes. +# +# -sDYNAMIC_EXECUTION=0 disables eval() and the Function constructor. +# If the build runs without these, it's preferable to use this flag +# because certain execution environments disallow those constructs. +# This flag is not strictly necessary, however. +# +# --no-entry: for compiling library code with no main(). If this is +# not supplied and the code has a main(), it is called as part of the +# module init process. Note that main() is #if'd out of shell.c +# (renamed) when building in wasm mode. +# +# --pre-js/--post-js=FILE relative or absolute paths to JS files to +# prepend/append to the emcc-generated bootstrapping JS. It's +# easier/faster to develop with separate JS files (reduces rebuilding +# requirements) but certain configurations, namely -sMODULARIZE, may +# require using at least a --pre-js file. They can be used +# individually and need not be paired. +# +# -O0..-O3 and -Oz: optimization levels affect not only C-style +# optimization but whether or not the resulting generated JS code +# gets minified. -O0 compiles _much_ more quickly than -O3 or -Oz, +# and doesn't minimize any JS code, so is recommended for +# development. -O3 or -Oz are recommended for deployment, but +# primarily because -Oz will shrink the wasm file notably. JS-side +# minification makes little difference in terms of overall +# distributable size. +# +# --minify 0: supposedly disables minification of the generated JS +# code, regardless of optimization level, but that's not quite true: +# search the main makefile for wasm-strip for details. Minification +# of the JS has minimal overall effect in the larger scheme of things +# and results in JS files which can neither be edited nor viewed as +# text files in Fossil (which flags them as binary because of their +# extreme line lengths). Interestingly, whether or not the comments +# in the generated JS file get stripped is unaffected by this setting +# and depends entirely on the optimization level. Higher optimization +# levels reduce the size of the JS considerably even without +# minification. +# +######################################################################## diff --git a/ext/wasm/api/extern-post-js.c-pp.js b/ext/wasm/api/extern-post-js.c-pp.js index e26e8bef66..ddba6917bd 100644 --- a/ext/wasm/api/extern-post-js.c-pp.js +++ b/ext/wasm/api/extern-post-js.c-pp.js @@ -69,6 +69,7 @@ const toExportForESM = EmscriptenModule /* see post-js-header/footer.js */, !!ff.__isUnderTest ); + sIMS.debugModule("sqlite3InitModule() sqlite3 =",s); //const rv = s.asyncPostInit(); //delete s.asyncPostInit; //#if wasmfs diff --git a/ext/wasm/api/sqlite3-api-cleanup.js b/ext/wasm/api/sqlite3-api-cleanup.js index a9e0047055..d0fc4e80f4 100644 --- a/ext/wasm/api/sqlite3-api-cleanup.js +++ b/ext/wasm/api/sqlite3-api-cleanup.js @@ -36,7 +36,6 @@ if( 'undefined' === typeof EmscriptenModule/*from post-js-header.js*/ ){ throw new Error("sqlite3-api-cleanup.js expects to be running in the "+ "context of its Emscripten module loader."); } - try{ /* Config options for sqlite3ApiBootstrap(). */ const bootstrapConfig = Object.assign( @@ -60,6 +59,8 @@ try{ bootstrapConfig.wasmPtrIR = 'number'===(typeof bootstrapConfig.exports.sqlite3_libversion()) ? 'i32' :'i64'; + const sIMS = sqlite3InitScriptInfo; + sIMS.debugModule("Bootstrapping lib config", sIMS); /** For purposes of the Emscripten build, call sqlite3ApiBootstrap(). diff --git a/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js b/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js index 2edabe3e62..29e64e94cf 100644 --- a/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js +++ b/ext/wasm/api/sqlite3-worker1-promiser.c-pp.js @@ -252,10 +252,12 @@ globalThis.sqlite3Worker1Promiser = function callee(config = callee.defaultConfi globalThis.sqlite3Worker1Promiser.defaultConfig = { worker: function(){ -//#if target=es6-module +//#if target=es6-bundler-friendly return new Worker(new URL("sqlite3-worker1-bundler-friendly.mjs", import.meta.url),{ type: 'module' }); +//#elif target=es6-module + return new Worker(new URL("sqlite3-worker1.js", import.meta.url)); //#else let theJs = "sqlite3-worker1.js"; if(this.currentScript){ diff --git a/ext/wasm/api/sqlite3-worker1.c-pp.js b/ext/wasm/api/sqlite3-worker1.c-pp.js index 74de9ec7ef..243128fe1d 100644 --- a/ext/wasm/api/sqlite3-worker1.c-pp.js +++ b/ext/wasm/api/sqlite3-worker1.c-pp.js @@ -34,6 +34,8 @@ */ //#if target=es6-bundler-friendly import {default as sqlite3InitModule} from './sqlite3-bundler-friendly.mjs'; +//#elif target=es6-module + return new Worker(new URL("sqlite3.js", import.meta.url)); //#else "use strict"; { diff --git a/ext/wasm/batch-runner-sahpool.html b/ext/wasm/batch-runner-sahpool.html deleted file mode 100644 index ad7e7b5408..0000000000 --- a/ext/wasm/batch-runner-sahpool.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - sqlite3-api batch SQL runner for the SAHPool VFS - - -
sqlite3-api batch SQL runner for the SAHPool VFS
-
- - - - -
-
- - - - diff --git a/ext/wasm/batch-runner-sahpool.js b/ext/wasm/batch-runner-sahpool.js deleted file mode 100644 index dfa5044a9e..0000000000 --- a/ext/wasm/batch-runner-sahpool.js +++ /dev/null @@ -1,341 +0,0 @@ -/* - 2023-11-30 - - 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. - - *********************************************************************** - - A basic batch SQL runner for the SAHPool VFS. This file must be run in - a worker thread. This is not a full-featured app, just a way to get some - measurements for batch execution of SQL for the OPFS SAH Pool VFS. -*/ -'use strict'; - -const wMsg = function(msgType,...args){ - postMessage({ - type: msgType, - data: args - }); -}; -const toss = function(...args){throw new Error(args.join(' '))}; -const warn = (...args)=>{ wMsg('warn',...args); }; -const error = (...args)=>{ wMsg('error',...args); }; -const log = (...args)=>{ wMsg('stdout',...args); } -let sqlite3; -const urlParams = new URL(globalThis.location.href).searchParams; -const cacheSize = (()=>{ - if(urlParams.has('cachesize')) return +urlParams.get('cachesize'); - return 200; -})(); - - -/** Throws if the given sqlite3 result code is not 0. */ -const checkSqliteRc = (dbh,rc)=>{ - if(rc) toss("Prepare failed:",sqlite3.capi.sqlite3_errmsg(dbh)); -}; - -const sqlToDrop = [ - "SELECT type,name FROM sqlite_schema ", - "WHERE name NOT LIKE 'sqlite\\_%' escape '\\' ", - "AND name NOT LIKE '\\_%' escape '\\'" -].join(''); - -const clearDbSqlite = function(db){ - // This would be SO much easier with the oo1 API, but we specifically want to - // inject metrics we can't get via that API, and we cannot reliably (OPFS) - // open the same DB twice to clear it using that API, so... - const rc = sqlite3.wasm.exports.sqlite3_wasm_db_reset(db.handle); - log("reset db rc =",rc,db.id, db.filename); -}; - -const App = { - db: undefined, - cache:Object.create(null), - log: log, - warn: warn, - error: error, - metrics: { - fileCount: 0, - runTimeMs: 0, - prepareTimeMs: 0, - stepTimeMs: 0, - stmtCount: 0, - strcpyMs: 0, - sqlBytes: 0 - }, - fileList: undefined, - execSql: async function(name,sql){ - const db = this.db; - const banner = "========================================"; - this.log(banner, - "Running",name,'('+sql.length,'bytes)'); - const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; - let pStmt = 0, pSqlBegin; - const metrics = db.metrics = Object.create(null); - metrics.prepTotal = metrics.stepTotal = 0; - metrics.stmtCount = 0; - metrics.malloc = 0; - metrics.strcpy = 0; - if(this.gotErr){ - this.error("Cannot run SQL: error cleanup is pending."); - return; - } - // Run this async so that the UI can be updated for the above header... - const endRun = ()=>{ - metrics.evalSqlEnd = performance.now(); - metrics.evalTimeTotal = (metrics.evalSqlEnd - metrics.evalSqlStart); - this.log("metrics:",JSON.stringify(metrics, undefined, ' ')); - this.log("prepare() count:",metrics.stmtCount); - this.log("Time in prepare_v2():",metrics.prepTotal,"ms", - "("+(metrics.prepTotal / metrics.stmtCount),"ms per prepare())"); - this.log("Time in step():",metrics.stepTotal,"ms", - "("+(metrics.stepTotal / metrics.stmtCount),"ms per step())"); - this.log("Total runtime:",metrics.evalTimeTotal,"ms"); - this.log("Overhead (time - prep - step):", - (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); - this.log(banner,"End of",name); - this.metrics.prepareTimeMs += metrics.prepTotal; - this.metrics.stepTimeMs += metrics.stepTotal; - this.metrics.stmtCount += metrics.stmtCount; - this.metrics.strcpyMs += metrics.strcpy; - this.metrics.sqlBytes += sql.length; - }; - - const runner = function(resolve, reject){ - ++this.metrics.fileCount; - metrics.evalSqlStart = performance.now(); - const stack = wasm.scopedAllocPush(); - try { - let t, rc; - let sqlByteLen = sql.byteLength; - const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); - t = performance.now(); - pSqlBegin = wasm.scopedAlloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed"); - metrics.malloc = performance.now() - t; - metrics.byteLength = sqlByteLen; - let pSql = pSqlBegin; - const pSqlEnd = pSqlBegin + sqlByteLen; - t = performance.now(); - wasm.heap8().set(sql, pSql); - wasm.poke(pSql + sqlByteLen, 0); - //log("SQL:",wasm.cstrToJs(pSql)); - metrics.strcpy = performance.now() - t; - let breaker = 0; - while(pSql && wasm.peek8(pSql)){ - wasm.pokePtr(ppStmt, 0); - wasm.pokePtr(pzTail, 0); - t = performance.now(); - rc = capi.sqlite3_prepare_v2( - db.handle, pSql, sqlByteLen, ppStmt, pzTail - ); - metrics.prepTotal += performance.now() - t; - checkSqliteRc(db.handle, rc); - pStmt = wasm.peekPtr(ppStmt); - pSql = wasm.peekPtr(pzTail); - sqlByteLen = pSqlEnd - pSql; - if(!pStmt) continue/*empty statement*/; - ++metrics.stmtCount; - t = performance.now(); - rc = capi.sqlite3_step(pStmt); - capi.sqlite3_finalize(pStmt); - pStmt = 0; - metrics.stepTotal += performance.now() - t; - switch(rc){ - case capi.SQLITE_ROW: - case capi.SQLITE_DONE: break; - default: checkSqliteRc(db.handle, rc); toss("Not reached."); - } - } - resolve(this); - }catch(e){ - if(pStmt) capi.sqlite3_finalize(pStmt); - this.gotErr = e; - reject(e); - }finally{ - capi.sqlite3_exec(db.handle,"rollback;",0,0,0); - wasm.scopedAllocPop(stack); - } - }.bind(this); - const p = new Promise(runner); - return p.catch( - (e)=>this.error("Error via execSql("+name+",...):",e.message) - ).finally(()=>{ - endRun(); - }); - }, - - /** - Loads batch-runner.list and populates the selection list from - it. Returns a promise which resolves to nothing in particular - when it completes. Only intended to be run once at the start - of the app. - */ - loadSqlList: async function(){ - const infile = 'batch-runner.list'; - this.log("Loading list of SQL files:", infile); - let txt; - try{ - const r = await fetch(infile); - if(404 === r.status){ - toss("Missing file '"+infile+"'."); - } - if(!r.ok) toss("Loading",infile,"failed:",r.statusText); - txt = await r.text(); - }catch(e){ - this.error(e.message); - throw e; - } - App.fileList = txt.split(/\n+/).filter(x=>!!x); - this.log("Loaded",infile); - }, - - /** Fetch ./fn and return its contents as a Uint8Array. */ - fetchFile: async function(fn, cacheIt=false){ - if(cacheIt && this.cache[fn]) return this.cache[fn]; - this.log("Fetching",fn,"..."); - let sql; - try { - const r = await fetch(fn); - if(!r.ok) toss("Fetch failed:",r.statusText); - sql = new Uint8Array(await r.arrayBuffer()); - }catch(e){ - this.error(e.message); - throw e; - } - this.log("Fetched",sql.length,"bytes from",fn); - if(cacheIt) this.cache[fn] = sql; - return sql; - }/*fetchFile()*/, - - /** - Converts this.metrics() to a form which is suitable for easy conversion to - CSV. It returns an array of arrays. The first sub-array is the column names. - The 2nd and subsequent are the values, one per test file (only the most recent - metrics are kept for any given file). - */ - metricsToArrays: function(){ - const rc = []; - Object.keys(this.dbs).sort().forEach((k)=>{ - const d = this.dbs[k]; - const m = d.metrics; - delete m.evalSqlStart; - delete m.evalSqlEnd; - const mk = Object.keys(m).sort(); - if(!rc.length){ - rc.push(['db', ...mk]); - } - const row = [k.split('/').pop()/*remove dir prefix from filename*/]; - rc.push(row); - row.push(...mk.map((kk)=>m[kk])); - }); - return rc; - }, - - metricsToBlob: function(colSeparator='\t'){ - const ar = [], ma = this.metricsToArrays(); - if(!ma.length){ - this.error("Metrics are empty. Run something."); - return; - } - ma.forEach(function(row){ - ar.push(row.join(colSeparator),'\n'); - }); - return new Blob(ar); - }, - - /** - Fetch file fn and eval it as an SQL blob. This is an async - operation and returns a Promise which resolves to this - object on success. - */ - evalFile: async function(fn){ - const sql = await this.fetchFile(fn); - return this.execSql(fn,sql); - }/*evalFile()*/, - - /** - Fetches the handle of the db associated with - this.e.selImpl.value, opening it if needed. - */ - initDb: function(){ - const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; - const stack = wasm.scopedAllocPush(); - let pDb = 0; - const d = Object.create(null); - d.filename = "/batch.db"; - try{ - const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; - const ppDb = wasm.scopedAllocPtr(); - const rc = capi.sqlite3_open_v2(d.filename, ppDb, oFlags, this.PoolUtil.vfsName); - pDb = wasm.peekPtr(ppDb) - if(rc) toss("sqlite3_open_v2() failed with code",rc); - capi.sqlite3_exec(pDb, "PRAGMA cache_size="+cacheSize, 0, 0, 0); - this.log("cache_size =",cacheSize); - }catch(e){ - if(pDb) capi.sqlite3_close_v2(pDb); - throw e; - }finally{ - wasm.scopedAllocPop(stack); - } - d.handle = pDb; - this.log("Opened db:",d.filename,'@',d.handle); - return d; - }, - - closeDb: function(){ - if(this.db.handle){ - this.sqlite3.capi.sqlite3_close_v2(this.db.handle); - this.db.handle = undefined; - } - }, - - run: async function(sqlite3){ - delete this.run; - this.sqlite3 = sqlite3; - const capi = sqlite3.capi, wasm = sqlite3.wasm; - this.log("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); - this.log("WASM heap size =",wasm.heap8().length); - let timeStart; - sqlite3.installOpfsSAHPoolVfs({ - clearOnInit: true, initialCapacity: 4, - name: 'batch-sahpool', - verbosity: 2 - }).then(PoolUtil=>{ - App.PoolUtil = PoolUtil; - App.db = App.initDb(); - }) - .then(async ()=>this.loadSqlList()) - .then(async ()=>{ - timeStart = performance.now(); - for(let i = 0; i < App.fileList.length; ++i){ - const fn = App.fileList[i]; - await App.evalFile(fn); - if(App.gotErr) throw App.gotErr; - } - }) - .then(()=>{ - App.metrics.runTimeMs = performance.now() - timeStart; - App.log("total metrics:",JSON.stringify(App.metrics, undefined, ' ')); - App.log("Reload the page to run this again."); - App.closeDb(); - App.PoolUtil.removeVfs(); - }) - .catch(e=>this.error("ERROR:",e)); - }/*run()*/ -}/*App*/; - -let sqlite3Js = 'sqlite3.js'; -if(urlParams.has('sqlite3.dir')){ - sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js; -} -importScripts(sqlite3Js); -globalThis.sqlite3InitModule().then(async function(sqlite3_){ - log("Done initializing. Running batch runner..."); - sqlite3 = sqlite3_; - App.run(sqlite3_); -}); diff --git a/ext/wasm/batch-runner.html b/ext/wasm/batch-runner.html deleted file mode 100644 index 5258f9597e..0000000000 --- a/ext/wasm/batch-runner.html +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - sqlite3-api batch SQL runner - - -
sqlite3-api batch SQL runner
- -
-
-
Initializing app...
-
- On a slow internet connection this may take a moment. If this - message displays for "a long time", intialization may have - failed and the JavaScript console may contain clues as to why. -
-
-
Downloading...
-
- -
-

- This page is for batch-running extracts from the output - of speedtest1 --script, as well as other standalone SQL - scripts. -

-

ACHTUNG: this file requires a generated input list - file. Run "make batch" from this directory to generate it. -

- - -
-
-
- - - - - - - - - - - -
- -
- - - - -
- - - - - - diff --git a/ext/wasm/batch-runner.js b/ext/wasm/batch-runner.js deleted file mode 100644 index e7a322b7f0..0000000000 --- a/ext/wasm/batch-runner.js +++ /dev/null @@ -1,604 +0,0 @@ -/* - 2022-08-29 - - 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. - - *********************************************************************** - - A basic batch SQL runner for sqlite3-api.js. This file must be run in - main JS thread and sqlite3.js must have been loaded before it. -*/ -'use strict'; -(function(){ - const toss = function(...args){throw new Error(args.join(' '))}; - const warn = console.warn.bind(console); - let sqlite3; - const urlParams = new URL(self.location.href).searchParams; - const cacheSize = (()=>{ - if(urlParams.has('cachesize')) return +urlParams.get('cachesize'); - return 200; - })(); - - /** Throws if the given sqlite3 result code is not 0. */ - const checkSqliteRc = (dbh,rc)=>{ - if(rc) toss("Prepare failed:",sqlite3.capi.sqlite3_errmsg(dbh)); - }; - - const sqlToDrop = [ - "SELECT type,name FROM sqlite_schema ", - "WHERE name NOT LIKE 'sqlite\\_%' escape '\\' ", - "AND name NOT LIKE '\\_%' escape '\\'" - ].join(''); - - const clearDbWebSQL = function(db){ - db.handle.transaction(function(tx){ - const onErr = (e)=>console.error(e); - const callback = function(tx, result){ - const rows = result.rows; - let i, n; - i = n = rows.length; - while(i--){ - const row = rows.item(i); - const name = JSON.stringify(row.name); - const type = row.type; - switch(type){ - case 'index': case 'table': - case 'trigger': case 'view': { - const sql2 = 'DROP '+type+' '+name; - tx.executeSql(sql2, [], ()=>{}, onErr); - break; - } - default: - warn("Unhandled db entry type:",type,'name =',name); - break; - } - } - }; - tx.executeSql(sqlToDrop, [], callback, onErr); - db.handle.changeVersion(db.handle.version, "", ()=>{}, onErr, ()=>{}); - }); - }; - - const clearDbSqlite = function(db){ - // This would be SO much easier with the oo1 API, but we specifically want to - // inject metrics we can't get via that API, and we cannot reliably (OPFS) - // open the same DB twice to clear it using that API, so... - const rc = sqlite3.wasm.exports.sqlite3_wasm_db_reset(db.handle); - App.logHtml("reset db rc =",rc,db.id, db.filename); - }; - - const E = (s)=>document.querySelector(s); - const App = { - e: { - output: E('#test-output'), - selSql: E('#sql-select'), - btnRun: E('#sql-run'), - btnRunNext: E('#sql-run-next'), - btnRunRemaining: E('#sql-run-remaining'), - btnExportMetrics: E('#export-metrics'), - btnClear: E('#output-clear'), - btnReset: E('#db-reset'), - cbReverseLog: E('#cb-reverse-log-order'), - selImpl: E('#select-impl'), - fsToolbar: E('#toolbar') - }, - db: Object.create(null), - dbs: Object.create(null), - cache:{}, - metrics: { - fileCount: 0, - runTimeMs: 0, - prepareTimeMs: 0, - stepTimeMs: 0, - stmtCount: 0, - strcpyMs: 0, - sqlBytes: 0 - }, - log: console.log.bind(console), - warn: console.warn.bind(console), - cls: function(){this.e.output.innerHTML = ''}, - logHtml2: function(cssClass,...args){ - const ln = document.createElement('div'); - if(cssClass) ln.classList.add(cssClass); - ln.append(document.createTextNode(args.join(' '))); - this.e.output.append(ln); - //this.e.output.lastElementChild.scrollIntoViewIfNeeded(); - }, - logHtml: function(...args){ - console.log(...args); - if(1) this.logHtml2('', ...args); - }, - logErr: function(...args){ - console.error(...args); - if(1) this.logHtml2('error', ...args); - }, - - execSql: async function(name,sql){ - const db = this.getSelectedDb(); - const banner = "========================================"; - this.logHtml(banner, - "Running",name,'('+sql.length,'bytes) using',db.id); - const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; - let pStmt = 0, pSqlBegin; - const metrics = db.metrics = Object.create(null); - metrics.prepTotal = metrics.stepTotal = 0; - metrics.stmtCount = 0; - metrics.malloc = 0; - metrics.strcpy = 0; - this.blockControls(true); - if(this.gotErr){ - this.logErr("Cannot run SQL: error cleanup is pending."); - return; - } - // Run this async so that the UI can be updated for the above header... - const endRun = ()=>{ - metrics.evalSqlEnd = performance.now(); - metrics.evalTimeTotal = (metrics.evalSqlEnd - metrics.evalSqlStart); - this.logHtml(db.id,"metrics:",JSON.stringify(metrics, undefined, ' ')); - this.logHtml("prepare() count:",metrics.stmtCount); - this.logHtml("Time in prepare_v2():",metrics.prepTotal,"ms", - "("+(metrics.prepTotal / metrics.stmtCount),"ms per prepare())"); - this.logHtml("Time in step():",metrics.stepTotal,"ms", - "("+(metrics.stepTotal / metrics.stmtCount),"ms per step())"); - this.logHtml("Total runtime:",metrics.evalTimeTotal,"ms"); - this.logHtml("Overhead (time - prep - step):", - (metrics.evalTimeTotal - metrics.prepTotal - metrics.stepTotal)+"ms"); - this.logHtml(banner,"End of",name); - this.metrics.prepareTimeMs += metrics.prepTotal; - this.metrics.stepTimeMs += metrics.stepTotal; - this.metrics.stmtCount += metrics.stmtCount; - this.metrics.strcpyMs += metrics.strcpy; - this.metrics.sqlBytes += sql.length; - }; - - let runner; - if('websql'===db.id){ - const who = this; - runner = function(resolve, reject){ - /* WebSQL cannot execute multiple statements, nor can it execute SQL without - an explicit transaction. Thus we have to do some fragile surgery on the - input SQL. Since we're only expecting carefully curated inputs, the hope is - that this will suffice. PS: it also can't run most SQL functions, e.g. even - instr() results in "not authorized". */ - if('string'!==typeof sql){ // assume TypedArray - sql = new TextDecoder().decode(sql); - } - sql = sql.replace(/-- [^\n]+\n/g,''); // comment lines interfere with our split() - const sqls = sql.split(/;+\n/); - const rxBegin = /^BEGIN/i, rxCommit = /^COMMIT/i; - try { - const nextSql = ()=>{ - let x = sqls.shift(); - while(sqls.length && !x) x = sqls.shift(); - return x && x.trim(); - }; - const who = this; - const transaction = function(tx){ - try { - let s; - /* Try to approximate the spirit of the input scripts - by running batches bound by BEGIN/COMMIT statements. */ - for(s = nextSql(); !!s; s = nextSql()){ - if(rxBegin.test(s)) continue; - else if(rxCommit.test(s)) break; - //console.log("websql sql again",sqls.length, s); - ++metrics.stmtCount; - const t = performance.now(); - tx.executeSql(s,[], ()=>{}, (t,e)=>{ - console.error("WebSQL error",e,"SQL =",s); - who.logErr(e.message); - //throw e; - return false; - }); - metrics.stepTotal += performance.now() - t; - } - }catch(e){ - who.logErr("transaction():",e.message); - throw e; - } - }; - const n = sqls.length; - const nextBatch = function(){ - if(sqls.length){ - console.log("websql sqls.length",sqls.length,'of',n); - db.handle.transaction(transaction, (e)=>{ - who.logErr("Ignoring and contiuing:",e.message) - //reject(e); - return false; - }, nextBatch); - }else{ - resolve(who); - } - }; - metrics.evalSqlStart = performance.now(); - nextBatch(); - }catch(e){ - //this.gotErr = e; - console.error("websql error:",e); - who.logErr(e.message); - //reject(e); - } - }.bind(this); - }else{/*sqlite3 db...*/ - runner = function(resolve, reject){ - ++this.metrics.fileCount; - metrics.evalSqlStart = performance.now(); - const stack = wasm.scopedAllocPush(); - try { - let t; - let sqlByteLen = sql.byteLength; - const [ppStmt, pzTail] = wasm.scopedAllocPtr(2); - t = performance.now(); - pSqlBegin = wasm.scopedAlloc( sqlByteLen + 1/*SQL + NUL*/) || toss("alloc(",sqlByteLen,") failed"); - metrics.malloc = performance.now() - t; - metrics.byteLength = sqlByteLen; - let pSql = pSqlBegin; - const pSqlEnd = pSqlBegin + sqlByteLen; - t = performance.now(); - wasm.heap8().set(sql, pSql); - wasm.poke(pSql + sqlByteLen, 0); - metrics.strcpy = performance.now() - t; - let breaker = 0; - while(pSql && wasm.peek(pSql,'i8')){ - wasm.pokePtr(ppStmt, 0); - wasm.pokePtr(pzTail, 0); - t = performance.now(); - let rc = capi.sqlite3_prepare_v3( - db.handle, pSql, sqlByteLen, 0, ppStmt, pzTail - ); - metrics.prepTotal += performance.now() - t; - checkSqliteRc(db.handle, rc); - pStmt = wasm.peekPtr(ppStmt); - pSql = wasm.peekPtr(pzTail); - sqlByteLen = pSqlEnd - pSql; - if(!pStmt) continue/*empty statement*/; - ++metrics.stmtCount; - t = performance.now(); - rc = capi.sqlite3_step(pStmt); - capi.sqlite3_finalize(pStmt); - pStmt = 0; - metrics.stepTotal += performance.now() - t; - switch(rc){ - case capi.SQLITE_ROW: - case capi.SQLITE_DONE: break; - default: checkSqliteRc(db.handle, rc); toss("Not reached."); - } - } - resolve(this); - }catch(e){ - if(pStmt) capi.sqlite3_finalize(pStmt); - //this.gotErr = e; - reject(e); - }finally{ - capi.sqlite3_exec(db.handle,"rollback;",0,0,0); - wasm.scopedAllocPop(stack); - } - }.bind(this); - } - let p; - if(1){ - p = new Promise(function(res,rej){ - setTimeout(()=>runner(res, rej), 0)/*give UI a chance to output the "running" banner*/; - }); - }else{ - p = new Promise(runner); - } - return p.catch( - (e)=>this.logErr("Error via execSql("+name+",...):",e.message) - ).finally(()=>{ - endRun(); - this.blockControls(false); - }); - }, - - clearDb: function(){ - const db = this.getSelectedDb(); - if('websql'===db.id){ - this.logErr("TODO: clear websql db."); - return; - } - if(!db.handle) return; - const capi = this.sqlite3, wasm = this.sqlite3.wasm; - //const scope = wasm.scopedAllocPush( - this.logErr("TODO: clear db"); - }, - - /** - Loads batch-runner.list and populates the selection list from - it. Returns a promise which resolves to nothing in particular - when it completes. Only intended to be run once at the start - of the app. - */ - loadSqlList: async function(){ - const sel = this.e.selSql; - sel.innerHTML = ''; - this.blockControls(true); - const infile = 'batch-runner.list'; - this.logHtml("Loading list of SQL files:", infile); - let txt; - try{ - const r = await fetch(infile); - if(404 === r.status){ - toss("Missing file '"+infile+"'."); - } - if(!r.ok) toss("Loading",infile,"failed:",r.statusText); - txt = await r.text(); - const warning = E('#warn-list'); - if(warning) warning.remove(); - }catch(e){ - this.logErr(e.message); - throw e; - }finally{ - this.blockControls(false); - } - const list = txt.split(/\n+/); - let opt; - if(0){ - opt = document.createElement('option'); - opt.innerText = "Select file to evaluate..."; - opt.value = ''; - opt.disabled = true; - opt.selected = true; - sel.appendChild(opt); - } - list.forEach(function(fn){ - if(!fn) return; - opt = document.createElement('option'); - opt.value = fn; - opt.innerText = fn.split('/').pop(); - sel.appendChild(opt); - }); - this.logHtml("Loaded",infile); - }, - - /** Fetch ./fn and return its contents as a Uint8Array. */ - fetchFile: async function(fn, cacheIt=false){ - if(cacheIt && this.cache[fn]) return this.cache[fn]; - this.logHtml("Fetching",fn,"..."); - let sql; - try { - const r = await fetch(fn); - if(!r.ok) toss("Fetch failed:",r.statusText); - sql = new Uint8Array(await r.arrayBuffer()); - }catch(e){ - this.logErr(e.message); - throw e; - } - this.logHtml("Fetched",sql.length,"bytes from",fn); - if(cacheIt) this.cache[fn] = sql; - return sql; - }/*fetchFile()*/, - - /** Disable or enable certain UI controls. */ - blockControls: function(disable){ - //document.querySelectorAll('.disable-during-eval').forEach((e)=>e.disabled = disable); - this.e.fsToolbar.disabled = disable; - }, - - /** - Converts this.metrics() to a form which is suitable for easy conversion to - CSV. It returns an array of arrays. The first sub-array is the column names. - The 2nd and subsequent are the values, one per test file (only the most recent - metrics are kept for any given file). - */ - metricsToArrays: function(){ - const rc = []; - Object.keys(this.dbs).sort().forEach((k)=>{ - const d = this.dbs[k]; - const m = d.metrics; - delete m.evalSqlStart; - delete m.evalSqlEnd; - const mk = Object.keys(m).sort(); - if(!rc.length){ - rc.push(['db', ...mk]); - } - const row = [k.split('/').pop()/*remove dir prefix from filename*/]; - rc.push(row); - row.push(...mk.map((kk)=>m[kk])); - }); - return rc; - }, - - metricsToBlob: function(colSeparator='\t'){ - const ar = [], ma = this.metricsToArrays(); - if(!ma.length){ - this.logErr("Metrics are empty. Run something."); - return; - } - ma.forEach(function(row){ - ar.push(row.join(colSeparator),'\n'); - }); - return new Blob(ar); - }, - - downloadMetrics: function(){ - const b = this.metricsToBlob(); - if(!b) return; - const url = URL.createObjectURL(b); - const a = document.createElement('a'); - a.href = url; - a.download = 'batch-runner-js-'+((new Date().getTime()/1000) | 0)+'.csv'; - this.logHtml("Triggering download of",a.download); - document.body.appendChild(a); - a.click(); - setTimeout(()=>{ - document.body.removeChild(a); - URL.revokeObjectURL(url); - }, 500); - }, - - /** - Fetch file fn and eval it as an SQL blob. This is an async - operation and returns a Promise which resolves to this - object on success. - */ - evalFile: async function(fn){ - const sql = await this.fetchFile(fn); - return this.execSql(fn,sql); - }/*evalFile()*/, - - /** - Clears all DB tables in all _opened_ databases. Because of - disparities between backends, we cannot simply "unlink" the - databases to clean them up. - */ - clearStorage: function(onlySelectedDb=false){ - const list = onlySelectedDb - ? [('boolean'===typeof onlySelectedDb) - ? this.dbs[this.e.selImpl.value] - : onlySelectedDb] - : Object.values(this.dbs); - for(let db of list){ - if(db && db.handle){ - this.logHtml("Clearing db",db.id); - db.clear(); - } - } - }, - - /** - Fetches the handle of the db associated with - this.e.selImpl.value, opening it if needed. - */ - getSelectedDb: function(){ - if(!this.dbs.memdb){ - for(let opt of this.e.selImpl.options){ - const d = this.dbs[opt.value] = Object.create(null); - d.id = opt.value; - switch(d.id){ - case 'virtualfs': - d.filename = 'file:/virtualfs.sqlite3?vfs=unix-none'; - break; - case 'memdb': - d.filename = ':memory:'; - break; - case 'wasmfs-opfs': - d.filename = 'file:'+( - this.sqlite3.capi.sqlite3_wasmfs_opfs_dir() - )+'/wasmfs-opfs.sqlite3b'; - break; - case 'websql': - d.filename = 'websql.db'; - break; - default: - this.logErr("Unhandled db selection option (see details in the console).",opt); - toss("Unhandled db init option"); - } - } - }/*first-time init*/ - const dbId = this.e.selImpl.value; - const d = this.dbs[dbId]; - if(d.handle) return d; - if('websql' === dbId){ - d.handle = self.openDatabase('batch-runner', '0.1', 'foo', 1024 * 1024 * 50); - d.clear = ()=>clearDbWebSQL(d); - d.handle.transaction(function(tx){ - tx.executeSql("PRAGMA cache_size="+cacheSize); - App.logHtml(dbId,"cache_size =",cacheSize); - }); - }else{ - const capi = this.sqlite3.capi, wasm = this.sqlite3.wasm; - const stack = wasm.scopedAllocPush(); - let pDb = 0; - try{ - const oFlags = capi.SQLITE_OPEN_CREATE | capi.SQLITE_OPEN_READWRITE; - const ppDb = wasm.scopedAllocPtr(); - const rc = capi.sqlite3_open_v2(d.filename, ppDb, oFlags, null); - pDb = wasm.peekPtr(ppDb) - if(rc) toss("sqlite3_open_v2() failed with code",rc); - capi.sqlite3_exec(pDb, "PRAGMA cache_size="+cacheSize, 0, 0, 0); - this.logHtml(dbId,"cache_size =",cacheSize); - }catch(e){ - if(pDb) capi.sqlite3_close_v2(pDb); - }finally{ - wasm.scopedAllocPop(stack); - } - d.handle = pDb; - d.clear = ()=>clearDbSqlite(d); - } - d.clear(); - this.logHtml("Opened db:",dbId,d.filename); - console.log("db =",d); - return d; - }, - - run: function(sqlite3){ - delete this.run; - this.sqlite3 = sqlite3; - const capi = sqlite3.capi, wasm = sqlite3.wasm; - this.logHtml("Loaded module:",capi.sqlite3_libversion(), capi.sqlite3_sourceid()); - this.logHtml("WASM heap size =",wasm.heap8().length); - this.loadSqlList(); - if(capi.sqlite3_wasmfs_opfs_dir()){ - E('#warn-opfs').classList.remove('hidden'); - }else{ - E('#warn-opfs').remove(); - E('option[value=wasmfs-opfs]').disabled = true; - } - if('function' === typeof self.openDatabase){ - E('#warn-websql').classList.remove('hidden'); - }else{ - E('option[value=websql]').disabled = true; - E('#warn-websql').remove(); - } - const who = this; - if(this.e.cbReverseLog.checked){ - this.e.output.classList.add('reverse'); - } - this.e.cbReverseLog.addEventListener('change', function(){ - who.e.output.classList[this.checked ? 'add' : 'remove']('reverse'); - }, false); - this.e.btnClear.addEventListener('click', ()=>this.cls(), false); - this.e.btnRun.addEventListener('click', function(){ - if(!who.e.selSql.value) return; - who.evalFile(who.e.selSql.value); - }, false); - this.e.btnRunNext.addEventListener('click', function(){ - ++who.e.selSql.selectedIndex; - if(!who.e.selSql.value) return; - who.evalFile(who.e.selSql.value); - }, false); - this.e.btnReset.addEventListener('click', function(){ - who.clearStorage(true); - }, false); - this.e.btnExportMetrics.addEventListener('click', function(){ - who.logHtml2('warning',"Triggering download of metrics CSV. Check your downloads folder."); - who.downloadMetrics(); - //const m = who.metricsToArrays(); - //console.log("Metrics:",who.metrics, m); - }); - this.e.selImpl.addEventListener('change', function(){ - who.getSelectedDb(); - }); - this.e.btnRunRemaining.addEventListener('click', async function(){ - let v = who.e.selSql.value; - const timeStart = performance.now(); - while(v){ - await who.evalFile(v); - if(who.gotError){ - who.logErr("Error handling script",v,":",who.gotError.message); - break; - } - ++who.e.selSql.selectedIndex; - v = who.e.selSql.value; - } - const timeTotal = performance.now() - timeStart; - who.logHtml("Run-remaining time:",timeTotal,"ms ("+(timeTotal/1000/60)+" minute(s))"); - who.clearStorage(); - App.metrics.runTimeMs = timeTotal; - who.logHtml("Total metrics:",JSON.stringify(App.metrics,undefined,' ')); - }, false); - }/*run()*/ - }/*App*/; - - self.sqlite3TestModule.initSqlite3().then(function(sqlite3_){ - sqlite3 = sqlite3_; - self.App = App /* only to facilitate dev console access */; - App.run(sqlite3); - }); -})(); diff --git a/ext/wasm/c-pp.c b/ext/wasm/c-pp.c index 0968a68690..791bc319ce 100644 --- a/ext/wasm/c-pp.c +++ b/ext/wasm/c-pp.c @@ -278,6 +278,8 @@ void fatalv(char const *zFmt, va_list va){ vfprintf(stderr, zFmt, va); } fputc('\n', stderr); + fflush(stdout); + fflush(stderr); exit(1); } @@ -1417,15 +1419,16 @@ void cmpp_process_keyword(CmppTokenizer * const t){ void cmpp_process_file(const char * zName){ FileWrapper fw = FileWrapper_empty; CmppTokenizer ct = CmppTokenizer_empty; - FileWrapper_open(&fw, zName, "r"); FileWrapper_slurp(&fw); g_debug(1,("Read %u byte(s) from [%s]\n", fw.nContent, fw.zName)); - ct.zName = zName; - ct.zBegin = fw.zContent; - ct.zEnd = fw.zContent + fw.nContent; - while(cmpp_next_keyword_line(&ct)){ - cmpp_process_keyword(&ct); + if( fw.zContent ){ + ct.zName = zName; + ct.zBegin = fw.zContent; + ct.zEnd = fw.zContent + fw.nContent; + while(cmpp_next_keyword_line(&ct)){ + cmpp_process_keyword(&ct); + } } FileWrapper_close(&fw); if(0!=ct.level.ndx){ @@ -1438,13 +1441,13 @@ void cmpp_process_file(const char * zName){ static void usage(int isErr){ FILE * const fOut = isErr ? stderr : stdout; fprintf(fOut, - "Usage: %s [flags] [infile]\n" + "Usage: %s [flags] [infile...]\n" "Flags:\n", g.zArgv0); #define arg(F,D) fprintf(fOut," %s\n %s\n",F, D) arg("-f|--file FILE","Read input from FILE (default=- (stdin)).\n" - " Alternately, the first non-flag argument is assumed to " - "be the input file."); + " Alternately, non-flag arguments are assumed to " + "be the input files."); arg("-o|--outfile FILE","Send output to FILE (default=- (stdout))"); arg("-DXYZ","Define XYZ to true"); arg("-UXYZ","Undefine XYZ (equivalent to false)"); @@ -1459,13 +1462,16 @@ int main(int argc, char const * const * argv){ int rc = 0; int i; int inclCount = 0; - const char * zInfile = 0; + int nFile = 0; + char const *zFileList[128] = {0}; #define M(X) (0==strcmp(X,zArg)) #define ISFLAG(X) else if(M(X)) #define ISFLAG2(X,Y) else if(M(X) || M(Y)) #define ARGVAL \ if(i+1>=argc) fatal("Missing value for flag '%s'", zArg); \ zArg = argv[++i] + + memset(zFileList, 0, sizeof(zFileList)); g.zArgv0 = argv[0]; atexit(cmpp_atexit); cmpp_initdb(); @@ -1497,8 +1503,11 @@ int main(int argc, char const * const * argv){ ISFLAG2("f","file"){ ARGVAL; do_infile: - if(zInfile) fatal("Cannot use -i more than once."); - zInfile = zArg; + if( nFile>=sizeof(zFileList)/sizeof(zFileList[0]) ){ + fatal("Too many file arguments. Max is %d.", + (int)(sizeof(zFileList)/sizeof(zFileList[0]))); + } + zFileList[nFile++] = zArg; } ISFLAG2("d","delimiter"){ ARGVAL; @@ -1508,17 +1517,21 @@ int main(int argc, char const * const * argv){ } ISFLAG("debug"){ ++g.doDebug; - }else if(!zInfile && '-'!=argv[i][0]){ + }else if('-'!=*zArg){ goto do_infile; }else{ fatal("Unhandled flag: %s", argv[i]); } } - if(!zInfile) zInfile = "-"; + if(!nFile){ + zFileList[nFile++] = "-"; + } if(!g.out.zName) g.out.zName = "-"; if(!inclCount) db_include_dir_add("."); FileWrapper_open(&g.out, g.out.zName, "w"); - cmpp_process_file(zInfile); + for(i = 0; i < nFile; ++i){ + cmpp_process_file(zFileList[i]); + } FileWrapper_close(&g.out); end: return rc ? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/ext/wasm/fiddle.make b/ext/wasm/fiddle.make deleted file mode 100644 index 0ee4468c24..0000000000 --- a/ext/wasm/fiddle.make +++ /dev/null @@ -1,186 +0,0 @@ -#!/do/not/make -#^^^ help emacs select edit mode -# -# Intended to include'd by ./GNUmakefile. -####################################################################### - -######################################################################## -# shell.c and its build flags... -ifneq (1,$(MAKING_CLEAN)) - make-np-0 = make -C $(dir.top) -n -p - make-np-1 = sed -e 's/(TOP)/(dir.top)/g' - # Extract SHELL_OPT and SHELL_DEP from the top-most makefile and import - # them as vars here... - $(eval $(shell $(make-np-0) | grep -e '^SHELL_OPT ' | $(make-np-1))) - $(eval $(shell $(make-np-0) | grep -e '^SHELL_DEP ' | $(make-np-1))) - # ^^^ can't do that in 1 invocation b/c newlines get stripped - ifeq (,$(SHELL_OPT)) - $(error Could not parse SHELL_OPT from $(dir.top)/Makefile.) - endif - ifeq (,$(SHELL_DEP)) - $(error Could not parse SHELL_DEP from $(dir.top)/Makefile.) - endif -$(dir.top)/shell.c: $(SHELL_DEP) $(dir.tool)/mkshellc.tcl $(sqlite3.c) - $(MAKE) -C $(dir.top) shell.c -endif -# /shell.c -######################################################################## - -EXPORTED_FUNCTIONS.fiddle = $(dir.tmp)/EXPORTED_FUNCTIONS.fiddle -fiddle.emcc-flags = \ - $(emcc.cflags) $(emcc_opt_full) \ - --minify 0 \ - -sALLOW_TABLE_GROWTH \ - -sMEMORY64=$(emcc.MEMORY64) \ - -sINITIAL_MEMORY=$(emcc.INITIAL_MEMORY.8) \ - -sABORTING_MALLOC \ - -sSTRICT_JS=0 \ - -sENVIRONMENT=web,worker \ - -sMODULARIZE \ - -sDYNAMIC_EXECUTION=0 \ - -sWASM_BIGINT=$(emcc.WASM_BIGINT) \ - -sEXPORT_NAME=$(sqlite3.js.init-func) \ - -Wno-limited-postlink-optimizations \ - $(emcc.exportedRuntimeMethods),FS \ - -sEXPORTED_FUNCTIONS=@$(abspath $(EXPORTED_FUNCTIONS.fiddle)) \ - $(SQLITE_OPT.full-featured) \ - $(SQLITE_OPT.common) \ - $(SHELL_OPT) \ - -UHAVE_READLINE -UHAVE_EDITLINE -UHAVE_LINENOISE \ - -USQLITE_HAVE_ZLIB \ - -USQLITE_WASM_BARE_BONES \ - -DSQLITE_SHELL_FIDDLE - -# Flags specifically for debug builds of fiddle. Performance suffers -# greatly in debug builds. -fiddle.emcc-flags.debug = $(fiddle.emcc-flags) \ - -DSQLITE_DEBUG \ - -DSQLITE_ENABLE_SELECTTRACE \ - -DSQLITE_ENABLE_WHERETRACE - -fiddle.EXPORTED_FUNCTIONS.in = \ - EXPORTED_FUNCTIONS.fiddle.in \ - $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-core \ - $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-extras - -$(EXPORTED_FUNCTIONS.fiddle): $(MKDIR.bld) $(fiddle.EXPORTED_FUNCTIONS.in) \ - $(MAKEFILE.fiddle) - sort -u $(fiddle.EXPORTED_FUNCTIONS.in) > $@ - -fiddle.cses = $(dir.top)/shell.c $(sqlite3-wasm.c) - -clean: clean-fiddle -clean-fiddle: - rm -f $(dir.fiddle)/fiddle-module.js \ - $(dir.fiddle)/*.wasm \ - $(dir.fiddle)/sqlite3-opfs-*.js \ - $(dir.fiddle)/*.gz \ - EXPORTED_FUNCTIONS.fiddle - rm -fr $(dir.fiddle-debug) -.PHONY: fiddle fiddle.debug -all: fiddle - -######################################################################## -# fiddle_remote is the remote destination for the fiddle app. It must -# be a [user@]HOST:/path for rsync. The target "should probably" -# contain a symlink of index.html -> fiddle.html. -fiddle_remote ?= -ifeq (,$(fiddle_remote)) -ifneq (,$(wildcard /home/stephan)) - fiddle_remote = wh:www/wasm-testing/fiddle/. -else ifneq (,$(wildcard /home/drh)) - #fiddle_remote = if appropriate, add that user@host:/path here -endif -endif -push-fiddle: fiddle - @if [ x = "x$(fiddle_remote)" ]; then \ - echo "fiddle_remote must be a [user@]HOST:/path for rsync"; \ - exit 1; \ - fi - rsync -va fiddle/ $(fiddle_remote) -# end fiddle remote push -######################################################################## - - -######################################################################## -# Explanation of the emcc build flags follows. Full docs for these can -# be found at: -# -# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js -# -# -sENVIRONMENT=web: elides bootstrap code related to non-web JS -# environments like node.js. Removing this makes the output a tiny -# tick larger but hypothetically makes it more portable to -# non-browser JS environments. -# -# -sMODULARIZE: changes how the generated code is structured to avoid -# declaring a global Module object and instead installing a function -# which loads and initializes the module. The function is named... -# -# -sEXPORT_NAME=jsFunctionName (see -sMODULARIZE) -# -# -sEXPORTED_RUNTIME_METHODS=@/absolute/path/to/file: a file -# containing a list of emscripten-supplied APIs, one per line, which -# must be exported into the generated JS. Must be an absolute path! -# -# -sEXPORTED_FUNCTIONS=@/absolute/path/to/file: a file containing a -# list of C functions, one per line, which must be exported via wasm -# so they're visible to JS. C symbols names in that file must all -# start with an underscore for reasons known only to the emcc -# developers. e.g., _sqlite3_open_v2 and _sqlite3_finalize. Must be -# an absolute path! -# -# -sSTRICT_JS ensures that the emitted JS code includes the 'use -# strict' option. Note that -sSTRICT is more broadly-scoped and -# results in build errors. -# -# -sALLOW_TABLE_GROWTH is required for (at a minimum) the UDF-binding -# feature. Without it, JS functions cannot be made to proxy C-side -# callbacks. -# -# -sABORTING_MALLOC causes the JS-bound _malloc() to abort rather than -# return 0 on OOM. If set to 0 then all code which uses _malloc() -# must, just like in C, check the result before using it, else -# they're likely to corrupt the JS/WASM heap by writing to its -# address of 0. It is, as of this writing, enabled in Emscripten by -# default but we enable it explicitly in case that default changes. -# -# -sDYNAMIC_EXECUTION=0 disables eval() and the Function constructor. -# If the build runs without these, it's preferable to use this flag -# because certain execution environments disallow those constructs. -# This flag is not strictly necessary, however. -# -# --no-entry: for compiling library code with no main(). If this is -# not supplied and the code has a main(), it is called as part of the -# module init process. Note that main() is #if'd out of shell.c -# (renamed) when building in wasm mode. -# -# --pre-js/--post-js=FILE relative or absolute paths to JS files to -# prepend/append to the emcc-generated bootstrapping JS. It's -# easier/faster to develop with separate JS files (reduces rebuilding -# requirements) but certain configurations, namely -sMODULARIZE, may -# require using at least a --pre-js file. They can be used -# individually and need not be paired. -# -# -O0..-O3 and -Oz: optimization levels affect not only C-style -# optimization but whether or not the resulting generated JS code -# gets minified. -O0 compiles _much_ more quickly than -O3 or -Oz, -# and doesn't minimize any JS code, so is recommended for -# development. -O3 or -Oz are recommended for deployment, but -# primarily because -Oz will shrink the wasm file notably. JS-side -# minification makes little difference in terms of overall -# distributable size. -# -# --minify 0: supposedly disables minification of the generated JS -# code, regardless of optimization level, but that's not quite true: -# search the main makefile for wasm-strip for details. Minification -# of the JS has minimal overall effect in the larger scheme of things -# and results in JS files which can neither be edited nor viewed as -# text files in Fossil (which flags them as binary because of their -# extreme line lengths). Interestingly, whether or not the comments -# in the generated JS file get stripped is unaffected by this setting -# and depends entirely on the optimization level. Higher optimization -# levels reduce the size of the JS considerably even without -# minification. -# -######################################################################## diff --git a/ext/wasm/index.html b/ext/wasm/index.html index fb8a585604..d77a655e62 100644 --- a/ext/wasm/index.html +++ b/ext/wasm/index.html @@ -49,20 +49,23 @@
  • WASMFS-specific tests which require that diff --git a/ext/wasm/mkwasmbuilds.c b/ext/wasm/mkwasmbuilds.c index 6d1786094d..6a44386d41 100644 --- a/ext/wasm/mkwasmbuilds.c +++ b/ext/wasm/mkwasmbuilds.c @@ -33,70 +33,97 @@ #define pf printf #define ps puts -/* -** Valid build names. Each build is a combination of one of these and -** one of JS_BUILD_MODES, but only certain combinations are legal. -** This macro and JS_BUILD_MODES exist solely for documentation -** purposes: they are not expanded into code anywhere. -*/ -#define JS_BUILD_NAMES sqlite3 sqlite3-wasmfs -/* -** Valid build modes. For the "sqlite3-wasmfs" build, only "esm" (ES6 -** Module) is legal. -*/ -#define JS_BUILD_MODES \ - vanilla vanilla64 esm esm64 bundler-friendly bundler-friendly64 node - /* Separator to help eyeballs find the different output sections */ -static const char * zBanner = - "\n########################################################################\n"; +#define zBanner \ + "\n########################################################################\n" /* -** Flags for use with BuildDef::flags and the 3rd argument to -** mk_pre_post(). +** Flags for use with BuildDef::flags. ** ** Maintenance reminder: do not combine flags within this enum, -** e.g. LIBMODE_BUNDLER_FRIENDLY=0x02|LIBMODE_ESM, as that will lead +** e.g. F_BUNDLER_FRIENDLY=0x02|F_ESM, as that will lead ** to breakage in some of the flag checks. */ -enum LibModeFlags { +enum { /* Indicates an ESM module build. */ - LIBMODE_ESM = 0x01, + F_ESM = 0x01, /* Indicates a "bundler-friendly" build mode. */ - LIBMODE_BUNDLER_FRIENDLY = 0x02, + F_BUNDLER_FRIENDLY = 1<<1, /* Indicates that this build is unsupported. Such builds are not ** added to the 'all' target. The unsupported builds exist primarily ** for experimentation's sake. */ - LIBMODE_UNSUPPORTED = 0x04, + F_UNSUPPORTED = 1<<2, /* Elide this build from the 'all' target. */ - LIBMODE_NOT_IN_ALL = 0x08, - LIBMODE_64BIT = 0x10, + F_NOT_IN_ALL = 1<<3, + /* If it's a 64-bit build. */ + F_64BIT = 1<<4, /* Indicates a node.js-for-node.js build (untested and ** unsupported). */ - LIBMODE_NODEJS = 0x20, + F_NODEJS = 1<<5, /* Indicates a wasmfs build (untested and unsupported). */ - LIBMODE_WASMFS = 0x40 + F_WASMFS = 1<<6, + + /** + Which compiled files from $(dir.dout)/buildName/*.{js,mjs,wasm} + to copy to $(dir.dout) after creating them. + */ + CP_JS = 1 << 30, + CP_WASM = 1 << 31, + CP_ALL = CP_JS | CP_WASM }; /* -** Info needed for building one combination of JS_BUILD_NAMES and -** JS_BUILD_MODE, noting that only a subset of those combinations are -** legal/sensical. +** Info needed for building one concrete JS/WASM combination.. */ struct BuildDef { - const char *zName; /* Name from JS_BUILD_NAMES */ - const char *zMode; /* Name from JS_BUILD_MODES */ - int flags; /* Flags from LibModeFlags */ - const char *zJsOut; /* Name of generated sqlite3.js/.mjs */ - /* TODO?: dynamically determine zJsOut based on zName, zMode, and - flags. */ - const char *zWasmOut; /* zJsOut w/ .wasm extension if it needs to - be renamed. Do we still need this? */ + /** + Base name of output JS and WASM files. + */ + const char *zBaseName; + /* + ** A glyph to use in log messages for this build, intended to help + ** the eyes distinguish the build lines more easily in parallel + ** builds. + ** + ** The convention for 32- vs 64-bit pairs is to give them similar + ** emoji, e.g. a cookie for 32-bit and a donut or cake for 64. + ** Alternately, the same emoji a "64" suffix, excep that that throws + ** off the output alignment in parallel builds ;). + */ + const char *zEmo; + /* + ** If the build needs its x.wasm renamed in its x.{js,mjs} then this + ** must hold the base name to rename it to. Typically "sqlite3" or + ** "sqlite3-64bit". This is the case for builds which are named + ** something like sqlite3-foo-bar but can use the vanilla + ** sqlite3.wasm file. In such cases we don't need the extra + ** sqlite3-foo-bar.wasm which Emscripten (necessarily) creates when + ** compiling the module, so we patch (at build-time) the JS file to + ** use this name instead sqlite3-foo-bar. + */ + const char *zDotWasm; const char *zCmppD; /* Extra -D... flags for c-pp */ - const char *zEmcc; /* Extra flags for emcc */ + const char *zEmcc; /* Full flags for emcc. Normally NULL for default. */ + const char *zEmccExtra; /* Extra flags for emcc */ + const char *zDeps; /* Extra deps */ + const char *zEnv; /* emcc -sENVIRONMENT=... value */ + /* + ** Makefile code "ifeq (...)". If set, this build is enclosed in a + ** $zIfCond/endif block. + */ + const char *zIfCond; /* makefile "ifeq (...)" or similar */ + int flags; /* Flags from LibModeFlags */ }; typedef struct BuildDef BuildDef; +/* +** WASM_CUSTOM_INSTANTIATE changes how the JS pieces load the .wasm +** file from the .js file. When set, our JS takes over that step from +** Emscripten. Both modes are functionally equivalent but +** customization gives us access to wasm module state which we don't +** otherwise have. That said, the library also does not _need_ that +** state, so we don't _need_ to customize that step. +*/ #if !defined(WASM_CUSTOM_INSTANTIATE) # define WASM_CUSTOM_INSTANTIATE 0 #elif (WASM_CUSTOM_INSTANTIATE+0)==0 @@ -105,20 +132,30 @@ typedef struct BuildDef BuildDef; #endif #if WASM_CUSTOM_INSTANTIATE +/* c-pp -D... flags for the custom instantiateWasm(). */ #define C_PP_D_CUSTOM_INSTANTIATE " -Dcustom-Module.instantiateWasm " #else #define C_PP_D_CUSTOM_INSTANTIATE #endif -/* List of distinct library builds. See next comment block. */ -#define BuildDefs_map(E) \ - E(canonical) \ - E(canonical64) \ - E(esm) \ - E(esm64) \ - E(bundler) \ - E(bundler64) \ - E(node) \ +/* +** List of distinct library builds. Each one has to be set up in +** oBuildDefs. See the next comment block. +** +** Many makefile vars use these identifiers for naming stuff, e.g.: +** +** out.NAME.js = output file NAME.js or NAME.mjs +** out.NAME.wasm = output file NAME.wasm +** logtag.NAME = Used for decorating log output +** +** etc. +***/ +#define BuildDefs_map(E) \ + E(vanilla) E(vanilla64) \ + E(esm) E(esm64) \ + E(bundler) E(bundler64) \ + E(speedtest1) \ + E(node) E(node64) \ E(wasmfs) /* @@ -137,83 +174,179 @@ struct BuildDefs { typedef struct BuildDefs BuildDefs; const BuildDefs oBuildDefs = { - .canonical = { - .zName ="sqlite3", .zMode = "vanilla", - .flags = 0, - .zJsOut ="$(sqlite3.js)", - .zWasmOut = 0, - .zCmppD = 0, - .zEmcc =0 + /* + ** The canonical build, against which all others are compared and + ** contrasted. This is the one we post downloads for. + */ + .vanilla = { + /* This one's zBaseName and zEnv MUST be non-NULL so it can be + ** used as a default for all others. */ + .zEmo = "๐Ÿฆ", + .zBaseName = "sqlite3", + .zDotWasm = 0, + .zCmppD = 0, + .zEmcc = 0, + .zEmccExtra = 0, + .zEnv = "web,worker", + .zDeps = 0, + .zIfCond = 0, + .flags = CP_ALL }, - .canonical64 = { - .zName="sqlite3", .zMode= "vanilla64", - .flags= LIBMODE_NOT_IN_ALL | LIBMODE_64BIT, - .zJsOut="$(sqlite3-64bit.js)", - .zWasmOut=0, - .zCmppD=0, - .zEmcc="-sMEMORY64=1" + /* The canonical build in 64-bit. */ + .vanilla64 = { + .zEmo = "๐Ÿจ", + .zBaseName = "sqlite3-64bit", + .zDotWasm = 0, + .zCmppD = 0, + .zEmcc = 0, + .zEmccExtra = "-sMEMORY64=1 -sWASM_BIGINT=1", + .zEnv = 0, + .zDeps = 0, + .zIfCond = 0, + .flags = CP_ALL | F_64BIT }, + /* The canonical esm build. */ .esm = { - .zName="sqlite3", .zMode= "esm", - .flags= LIBMODE_ESM, - .zJsOut="$(sqlite3.mjs)", - .zWasmOut=0, - .zCmppD= "-Dtarget=es6-module", - .zEmcc=0 + .zEmo = "๐Ÿฌ", + .zBaseName = "sqlite3", + .zDotWasm = 0, + .zCmppD = "-Dtarget=es6-module", + .zEmcc = 0, + .zEmccExtra = 0, + .zEnv = 0, + .zDeps = 0, + .zIfCond = 0, + .flags = CP_JS | F_ESM }, + /* The canonical esm build in 64-bit. */ .esm64 = { - .zName="sqlite3", .zMode= "esm64", - .flags= LIBMODE_NOT_IN_ALL | LIBMODE_64BIT, - .zJsOut="$(sqlite3-64bit.mjs)", - .zWasmOut=0, - .zCmppD=0, - .zEmcc="-sMEMORY64=1" + .zEmo = "๐Ÿซ", + .zBaseName = "sqlite3-64bit", + .zDotWasm = 0, + .zCmppD = "-Dtarget=es6-module", + .zEmcc = 0, + .zEmccExtra = "-sMEMORY64=1 -sWASM_BIGINT=1", + .zEnv = 0, + .zDeps = 0, + .zIfCond = 0, + .flags = CP_JS | F_ESM | F_64BIT }, + /* speedtest1, our primary benchmarking tool */ + .speedtest1 = { + .zEmo = "๐Ÿ›ผ", + .zBaseName = "speedtest1", + .zDotWasm = 0, + .zCmppD = 0, + .zEmcc = + "$(emcc.speedtest1)" + " $(emcc.speedtest1.common)" + " $(pre-post.speedtest1.flags)" + " $(cflags.common)" + " -DSQLITE_SPEEDTEST1_WASM" + " $(SQLITE_OPT)" + " -USQLITE_WASM_BARE_BONES" + " -USQLITE_C -DSQLITE_C=$(sqlite3.canonical.c)" + " $(speedtest1.exit-runtime0)" + " $(speedtest1.c.in)" + " -lm", + .zEmccExtra = 0, + .zEnv = 0, + .zDeps = + "$(speedtest1.c.in)" + " $(EXPORTED_FUNCTIONS.speedtest1)", + .zIfCond = 0, + .flags = CP_ALL + }, + + /* + ** Core bundler-friendly build. Untested and "not really" supported, + ** but required by the downstream npm subproject. + ** + ** Testing these requires special-purpose node-based tools and + ** custom test apps, none of which we have/use. So we can pass them + ** off as-is to the npm subproject and they spot failures pretty + ** quickly ;). + */ .bundler = { - /* Core bundler-friendly build. Untested and "not really" - ** supported, but required by the downstream npm subproject. - ** Testing these would require special-purpose node-based tools and - ** custom test apps. Or we can pass them off as-is to the npm - ** subproject and they spot failures pretty quickly ;). */ - .zName="sqlite3", .zMode= "bundler-friendly", - .flags=LIBMODE_BUNDLER_FRIENDLY | LIBMODE_ESM, - .zJsOut="$(dir.dout)/sqlite3-bundler-friendly.mjs", - .zWasmOut=0, - .zCmppD="$(c-pp.D.sqlite3-esm) -Dtarget=es6-bundler-friendly", - .zEmcc=0 + .zEmo = "๐Ÿ‘›", + .zBaseName = "sqlite3-bundler-friendly", + .zDotWasm = "sqlite3", + .zCmppD = "$(c-pp.D.esm) -Dtarget=es6-bundler-friendly", + .zEmcc = 0, + .zEmccExtra = 0, + .zEnv = 0, + .zDeps = 0, + .zIfCond = 0, + .flags = CP_JS | F_BUNDLER_FRIENDLY | F_ESM + //| F_NOT_IN_ALL }, + /* 64-bit bundler-friendly. */ .bundler64 = { - .zName="sqlite3", .zMode= "bundler-friendly64", - .flags= LIBMODE_BUNDLER_FRIENDLY | LIBMODE_ESM, - .zJsOut="$(dir.dout)/sqlite3-bundler-friendly-64bit.mjs", - .zWasmOut=0, - .zCmppD="$(c-pp.D.sqlite3-esm) -Dtarget=es6-bundler-friendly", - .zEmcc="-sMEMORY64=1" + .zEmo = "๐Ÿ“ฆ", + .zBaseName = "sqlite3-bundler-friendlyu", + .zDotWasm = "sqlite3-64bit", + .zCmppD = "$(c-pp.D.bundler)", + .zEmcc = 0, + .zEmccExtra = "-sMEMORY64=1", + .zEnv = 0, + .zDeps = 0, + .zIfCond = 0, + .flags = CP_JS | F_ESM | F_BUNDLER_FRIENDLY | F_64BIT | F_NOT_IN_ALL }, - /* Entirely unsupported. */ + /* + ** We neither build node builds on a regular basis nor test them at + ** all. They are fully unsupported. Also, our JS targets only + ** browsers. + */ .node = { - .zName="sqlite3", .zMode= "node", - .flags= LIBMODE_UNSUPPORTED | LIBMODE_NODEJS, - .zJsOut="$(dir.dout)/sqlite3-node.mjs", - .zWasmOut="sqlite3-node.wasm", - .zCmppD="$(c-pp.D.sqlite3-bundler-friendly) -Dtarget=node", - .zEmcc=0 + .zEmo = "๐ŸŸ", + .zBaseName = "sqlite3-node", + .zDotWasm = 0, + .zCmppD = "-Dtarget=node $(c-pp.D.bundler)", + .zEmcc = 0, + .zEmccExtra = 0, + .zEnv = "node" + /* Adding ",node" to the zEnv list for the other builds causes + ** Emscripten to generate code which confuses node: it cannot + ** reliably determine whether the build is for a browser or for + ** node. */, + .zDeps = 0, + .zIfCond = 0, + .flags = CP_ALL | F_UNSUPPORTED | F_NODEJS + }, + + /* 64-bit node. */ + .node64 = { + .zEmo = "๐Ÿ”", + .zBaseName = "sqlite3-node-64bit", + .zDotWasm = 0, + .zCmppD = "-Dtarget=node $(c-pp.D.bundler)", + .zEmcc = 0, + .zEmccExtra = 0, + .zEnv = "node", + .zDeps = 0, + .zIfCond = 0, + .flags = CP_ALL | F_UNSUPPORTED | F_NODEJS | F_64BIT }, /* Entirely unsupported. */ .wasmfs = { - .zName="sqlite3-wasmfs", .zMode= "esm" , - .flags= LIBMODE_UNSUPPORTED | LIBMODE_WASMFS | LIBMODE_ESM, - .zJsOut="$(dir.wasmfs)/sqlite3-wasmfs.mjs", - .zWasmOut="sqlite3-wasmfs.wasm", - .zCmppD="$(c-pp.D.sqlite3-bundler-friendly) -Dwasmfs", - .zEmcc="-sEXPORT_ES6 -sUSE_ES6_IMPORT_META" + .zEmo = "๐Ÿ’ฟ", + .zBaseName = "sqlite3-wasmfs", + .zDotWasm = 0, + .zCmppD = "$(c-pp.D.bundler)", + .zEmcc = 0, + .zEmccExtra = "-sEXPORT_ES6 -sUSE_ES6_IMPORT_META", + .zEnv = 0, + .zDeps = 0, + .zIfCond = "ifeq (1,$(HAVE_WASMFS))", + .flags = CP_ALL | F_UNSUPPORTED | F_WASMFS } }; @@ -227,42 +360,47 @@ static void mk_prologue(void){ char const * aRequiredVars[] = { "dir.top", "dir.api", "dir.dout", "dir.tmp", - "sqlite3-license-version.js", - "MAKEFILE", "MAKEFILE_LIST", - /* Fiddle... */ - "dir.fiddle", "dir.fiddle-debug", - "MAKEFILE.fiddle", - "EXPORTED_FUNCTIONS.fiddle", - /* Some core JS files... */ - "sqlite3.js", "sqlite3.mjs", - "sqlite3-64bit.js", "sqlite3-64bit.mjs", + "dir.fiddle", "dir.fiddle.debug", /*"just-testing",*/ 0 }; char const * zVar; int i; - pf("%s# Build setup sanity checks...\n", zBanner); + ps(zBanner "# Build setup sanity checks..."); for( i = 0; (zVar = aRequiredVars[i]); ++i ){ pf("ifeq (,$(%s))\n", zVar); pf(" $(error build process error: expecting make var $$(%s) to " "have been set up by now)\n", zVar); ps("endif"); } - pf("%s", zBanner); - ps("# extern-post-js* and extern-pre-js* are files for use with"); - ps("# Emscripten's --extern-pre-js and --extern-post-js flags."); - ps("extern-pre-js.js = $(dir.api)/extern-pre-js.js"); - ps("extern-post-js.js.in = $(dir.api)/extern-post-js.c-pp.js"); - ps("# Emscripten flags for --[extern-][pre|post]-js=... for the"); - ps("# various builds."); - ps("pre-post-common.flags = --extern-pre-js=$(sqlite3-license-version.js)"); - ps("# pre-post-jses.deps.* = a list of dependencies for the\n" - "# --[extern-][pre/post]-js files."); - ps("pre-post-jses.deps.common = $(extern-pre-js.js) $(sqlite3-license-version.js)"); + + ps(zBanner + /** $1 = build name */ + "b.call.wasm-strip = " + "echo '[$(emo.b.$(1)) $(out.$(1).wasm)] $(emo.strip) wasm-strip'; " + "$(bin.wasm-strip) $(out.$(1).wasm)\n" + ); + + pf(zBanner + "define b.do.emcc\n" + /* $1 = build name */ + "$(bin.emcc) -o $@ $(emcc_opt_full) $(emcc.flags) " + "$(emcc.jsflags) -sENVIRONMENT=$(emcc.environment.$(1)) " + " $(pre-post.$(1).flags) " + " $(emcc.flags.$(1)) " + " $(cflags.common) $(cflags.$(1)) " + " $(SQLITE_OPT) " + " $(cflags.wasm_extra_init) $(sqlite3-wasm.c.in)\n" + "endef\n" + ); { - /* SQLITE.CALL.WASM-OPT = shell code to run $(1) (source wasm file - ** name) through $(bin.wasm-opt) */ + /* b.do.wasm-opt + ** + ** $1 = build name + ** + ** Runs $(out.$(1).wasm) through $(bin.wasm-opt) + */ const char * zOptFlags = /* ** Flags for wasm-opt. It has many, many, MANY "passes" options @@ -305,275 +443,552 @@ static void mk_prologue(void){ /*"--safe-heap "*/ /*"--vacuum " saves nothing */ ; - ps("ifeq (,$(bin.wasm-opt))"); - ps("define SQLITE.CALL.WASM-OPT"); - ps("echo 'wasm-opt not available for $(1)'"); - ps("endef"); - ps("else"); - ps("define SQLITE.CALL.WASM-OPT"); - pf("echo -n 'Before wasm-opt:'; ls -l $(1);\\\n" - "\trm -f wasm-opt-tmp.wasm;\\\n" - /* It's very likely that the set of wasm-opt flags varies from - ** version to version, so we'll ignore any errors here. */ - "\tif $(bin.wasm-opt) $(1) -o wasm-opt-tmp.wasm \\\n" - "\t\t%s; then \\\n" - "\t\tmv wasm-opt-tmp.wasm $(1); \\\n" - "\t\techo -n 'After wasm-opt: '; \\\n" - "\t\tls -l $(1); \\\n" - "\telse \\\n" - "\t\techo 'WARNING: ignoring wasm-opt failure for $(1)'; \\\n" - "\tfi\n", - zOptFlags - ); - ps("endef"); + ps(zBanner + "# post-compilation WASM file optimization"); + + /* b.do.wasm-opt $1 = build name*/ + ps("ifeq (,$(bin.wasm-opt))"); { + ps("b.do.wasm-opt = echo '$(logtag.$(1)) wasm-opt not available'"); + } + ps("else"); { + ps("define b.do.wasm-opt"); + pf( + "echo '[$(emo.b.$(1)) $(out.$(1).wasm)] $(emo.wasm-opt) $(bin.wasm-opt)';\\\n" + "\ttmpfile=$(dir.dout.$(1))/wasm-opt-tmp.$(1).wasm; \\\n" + "\trm -f $$tmpfile; \\\n" + "\tif $(bin.wasm-opt) $(out.$(1).wasm) " + "-o $$tmpfile \\\n" + "\t\t%s; then \\\n" + "\t\tmv $$tmpfile $(out.$(1).wasm); \\\n" +#if 0 + "\t\techo -n 'After wasm-opt: '; \\\n" + "\t\tls -l $(1); \\\n" +#endif + /* It's very likely that the set of wasm-opt flags varies from + ** version to version, so we'll ignore any errors here. */ + "\telse \\\n" + "\t\trm -f $$tmpfile; \\\n" + "\t\techo '$(logtag.$(1)) $(emo.fire) ignoring wasm-opt failure'; \\\n" + "\tfi\n", + zOptFlags + ); + ps("endef"); + } ps("endif"); } + + ps("more: all"); } /* ** Emits makefile code for setting up values for the --pre-js=FILE, ** --post-js=FILE, and --extern-post-js=FILE emcc flags, as well as -** populating those files. +** populating those files. This is necessary for any builds which +** embed the library's JS parts of this build (as opposed to parts +** which do not use the library-level code). */ -static void mk_pre_post(const char *zName /* build name */, - const char *zMode /* build mode */, - const char *zCmppD /* optional -D flags for c-pp for the - ** --pre/--post-js files. */, - const char *zWasmOut){ +static void mk_pre_post(char const *zBuildName){ /* Very common printf() args combo. */ -#define zNM zName, zMode - pf("%s# Begin --pre/--post flags for %s-%s\n", zBanner, zNM); - if( zCmppD && *zCmppD ){ - pf("c-pp.D.%s-%s = %s\n", zNM, zCmppD ? zCmppD : ""); - } - pf("pre-post-%s-%s.flags ?=\n", zNM); + pf("%s# Begin --pre/--post flags for %s\n", zBanner, zBuildName); - /* --pre-js=... */ - pf("pre-js.js.%s-%s = $(dir.tmp)/pre-js.%s-%s.js\n", - zNM, zNM); - pf("$(pre-js.js.%s-%s): $(MAKEFILE_LIST) " - "$(sqlite3-license-version.js)\n", zNM); - if( 0==WASM_CUSTOM_INSTANTIATE || 0==zWasmOut ){ - pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(pre-js.js.in)," - "$(pre-js.js.%s-%s)," - C_PP_D_CUSTOM_INSTANTIATE "$(c-pp.D.%s-%s)))\n", zNM, zNM); + ps("# --pre-js=..."); + pf("pre-js.%s.js = $(dir.tmp)/pre-js.%s.js\n", + zBuildName, zBuildName); + + if( 0==WASM_CUSTOM_INSTANTIATE ){ + pf("$(eval $(call b.eval.c-pp," + "%s," + "$(pre-js.in.js)," + "$(pre-js.%s.js)," + "$(c-pp.D.%s)" + "))", + zBuildName, zBuildName, zBuildName); }else{ +#if 0 + fixme; /* This part is needed for builds which have to rename the wasm file - in zJsOut so that the loader can find it. */ - pf("pre-js.js.%s-%s.intermediary = " - "$(dir.tmp)/pre-js.%s-%s.intermediary.js\n", - zNM, zNM); - pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(pre-js.js.in)," - "$(pre-js.js.%s-%s.intermediary)," - C_PP_D_CUSTOM_INSTANTIATE "$(c-pp.D.%s-%s)))\n", zNM, zNM); - pf("$(pre-js.js.%s-%s): $(pre-js.js.%s-%s.intermediary)\n", zNM, zNM); - pf("\tcp $(pre-js.js.%s-%s.intermediary) $@\n", zNM); - pf("\t@echo 'sIMS.wasmFilename = \"%s\";' >> $@\n", zWasmOut) + in zDotWasm so that the loader can find it. */ + pf("pre-js.%s.js.intermediary = " + "$(dir.tmp)/pre-js.%s.intermediary.js\n", + zBuildName, zBuildName); + pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(pre-js.in.js)," + "$(pre-js.%s.js.intermediary)," + C_PP_D_CUSTOM_INSTANTIATE "$(c-pp.D.%s)))\n", + zBuildName, zBuildName); + pf("$(pre-js.%s.js): $(pre-js.%s.js.intermediary)\n", + zBuildName, zBuildName); + pf("\tcp $(pre-js.%s.js.intermediary) $@\n", zBuildName); + pf("\t@echo 'sIMS.wasmFilename = \"$(out.%s.wasm)\";' >> $@\n", + zBuildName) /* see api/pre-js.c-pp.js:Module.instantiateModule() */; +#endif } - /* --post-js=... */ - pf("post-js.js.%s-%s = $(dir.tmp)/post-js.%s-%s.js\n", zNM, zNM); - pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(post-js.js.in)," - "$(post-js.js.%s-%s),$(c-pp.D.%s-%s)))\n", zNM, zNM); + ps("\n# --post-js=..."); + pf("post-js.%s.js = $(dir.tmp)/post-js.%s.js\n", + zBuildName, zBuildName); + pf("post-js.%s.in =" + " $(dir.api)/post-js-header.js" + " $(sqlite3-api.%s.js)" + " $(dir.api)/post-js-footer.js\n", + zBuildName, zBuildName); - /* --extern-post-js=... */ - pf("extern-post-js.js.%s-%s = $(dir.tmp)/extern-post-js.%s-%s.js\n", - zNM, zNM); - pf("$(eval $(call SQLITE.CALL.C-PP.FILTER,$(extern-post-js.js.in)," - "$(extern-post-js.js.%s-%s)," - C_PP_D_CUSTOM_INSTANTIATE "$(c-pp.D.%s-%s)))\n", zNM, zNM); + pf("$(eval $(call b.eval.c-pp," + "%s," + "$(post-js.%s.in)," + "$(post-js.%s.js)," + "$(c-pp.D.%s)" + "))\n", + zBuildName, zBuildName, zBuildName, zBuildName); + pf("$(post-js.%s.js): $(post-js.%s.in)\n", + zBuildName, zBuildName); + + ps("\n# --extern-post-js=..."); + pf("extern-post-js.%s.js = $(dir.tmp)/extern-post-js.%s.js\n", + zBuildName, zBuildName); + pf("$(eval $(call b.eval.c-pp," + "%s," + "$(extern-post-js.in.js)," + "$(extern-post-js.%s.js)," + C_PP_D_CUSTOM_INSTANTIATE "$(c-pp.D.%s)))\n", + zBuildName, zBuildName, zBuildName); + + ps("\n# --pre/post misc..."); /* Combined flags for use with emcc... */ - pf("pre-post-common.flags.%s-%s = " - "$(pre-post-common.flags) " - "--post-js=$(post-js.js.%s-%s) " - "--extern-post-js=$(extern-post-js.js.%s-%s)\n", zNM, zNM, zNM); + pf("pre-post.%s.flags = " + "--extern-pre-js=$(sqlite3-license-version.js) " + "--pre-js=$(pre-js.%s.js) " + "--post-js=$(post-js.%s.js) " + "--extern-post-js=$(extern-post-js.%s.js)\n", + zBuildName, zBuildName, zBuildName, zBuildName); - pf("pre-post-%s-%s.flags += $(pre-post-common.flags.%s-%s) " - "--pre-js=$(pre-js.js.%s-%s)\n", zNM, zNM, zNM); /* Set up deps... */ - pf("pre-post-jses.%s-%s.deps = $(pre-post-jses.deps.common) " - "$(post-js.js.%s-%s) $(extern-post-js.js.%s-%s)\n", - zNM, zNM, zNM); - pf("pre-post-%s-%s.deps = $(pre-post-jses.%s-%s.deps) " - "$(dir.tmp)/pre-js.%s-%s.js\n", - zNM, zNM, zNM); - pf("# End --pre/--post flags for %s-%s%s", zNM, zBanner); -#undef zNM + pf("pre-post.%s.deps = " + "$(pre-post-jses.common.deps) " + "$(post-js.%s.js) $(extern-post-js.%s.js) " + "$(dir.tmp)/pre-js.%s.js\n", + zBuildName, zBuildName, zBuildName, zBuildName); + pf("# End --pre/--post flags for %s%s", zBuildName, zBanner); } -/* -** Emits rules for the fiddle builds. +static char const * BuildDef_jsext(const BuildDef * pB){ + return (F_ESM & pB->flags) ? ".mjs" : ".js"; +} + +static char const * BuildDef_basename(const BuildDef * pB){ + return pB->zBaseName ? pB->zBaseName : oBuildDefs.vanilla.zBaseName; +} + +static void emit_compile_start(char const *zBuildName){ + pf("\t@$(call b.mkdir@);" + " $(call b.echo,%s,$(emo.compile) building ...)\n", + zBuildName); +} + +/** + Emit rules for sqlite3-api.${zBuildName}.js. zCmppD is optional + flags for $(bin.c-pp). */ -static void mk_fiddle(void){ - int i = 0; - - mk_pre_post("fiddle-module","vanilla", 0, "fiddle-module.wasm"); - for( ; i < 2; ++i ){ - /* 0==normal, 1==debug */ - const char *zTail = i ? ".debug" : ""; - const char *zDir = i ? "$(dir.fiddle-debug)" : "$(dir.fiddle)"; - - pf("%s# Begin fiddle%s\n", zBanner, zTail); - pf("fiddle-module.js%s = %s/fiddle-module.js\n", zTail, zDir); - pf("$(fiddle-module.js%s):%s $(MAKEFILE_LIST) $(MAKEFILE.fiddle) " - "$(EXPORTED_FUNCTIONS.fiddle) " - "$(fiddle.cses) $(pre-post-fiddle-module-vanilla.deps) " - "$(SOAP.js)\n", - zTail, (i ? " $(fiddle-module.js)" : "")); - if( 1==i ){/*fiddle.debug*/ - pf("\t@test -d \"$(dir $@)\" || mkdir -p \"$(dir $@)\"\n"); - } - pf("\t$(bin.emcc) -o $@ $(fiddle.emcc-flags%s) " - "$(pre-post-fiddle-module-vanilla.flags) $(fiddle.cses)\n", - zTail); - ps("\t@chmod -x $(basename $@).wasm"); - ps("\t@$(maybe-wasm-strip) $(basename $@).wasm"); - ps("\t@$(SQLITE.strip-createExportWrapper)"); - pf("\t@cp -p $(SOAP.js) $(dir $@)\n"); - if( 1==i ){/*fiddle.debug*/ - pf("\tcp -p $(dir.fiddle)/index.html " - "$(dir.fiddle)/fiddle.js " - "$(dir.fiddle)/fiddle-worker.js " - "$(dir $@)\n"); - } - /* Compress fiddle files. We handle each file separately, rather - than compressing them in a loop in the previous target, to help - avoid that hand-edited files, like fiddle-worker.js, do not end - up with stale .gz files (which althttpd will then serve instead - of the up-to-date uncompressed one). */ - pf("%s/fiddle-module.js.gz: %s/fiddle-module.js\n", zDir, zDir); - ps("\tgzip < $< > $@"); - pf("%s/fiddle-module.wasm.gz: %s/fiddle-module.wasm\n", zDir, zDir); - ps("\tgzip < $< > $@"); - pf("fiddle%s: %s/fiddle-module.js.gz %s/fiddle-module.wasm.gz\n", - i ? "-debug" : "", zDir, zDir); - if( 0==i ){ - ps("fiddle: $(fiddle-module.js)"); - }else{ - ps("fiddle-debug: $(fiddle-module.js.debug)"); - } - pf("# End fiddle%s%s", zTail, zBanner); - } +static void emit_api_js(char const *zBuildName, + char const *zCmppD){ + pf("c-pp.D.%s = %s\n" + "sqlite3-api.%s.js = $(dir.tmp)/sqlite3-api.%s.js\n", + zBuildName, zCmppD ? zCmppD: "", + zBuildName, zBuildName); + pf("$(eval $(call b.eval.c-pp," + "%s," + "$(sqlite3-api.jses)," + "$(sqlite3-api.%s.js)," + "$(c-pp.D.%s)" + "))\n", + zBuildName, zBuildName, zBuildName); + pf("$(out.%s.js): $(sqlite3-api.%s.js)\n", + zBuildName, zBuildName); } /* ** Emits makefile code for one build of the library. */ -static void mk_lib_mode(const BuildDef * pB){ - const char * zWasmOut = "$(basename $@).wasm" - /* The various targets named X.js or X.mjs (pB->zJsOut) also generate - ** X.wasm, and we need that part of the name to perform some - ** post-processing after Emscripten generates X.wasm. */; - assert( pB->zName ); - assert( pB->zMode ); - assert( pB->zJsOut ); -/* Very common printf() args combo. */ -#define zNM pB->zName, pB->zMode +static void mk_lib_mode(const char *zBuildName, const BuildDef * pB){ + const char * zJsExt = BuildDef_jsext(pB); + char const * const zBaseName = BuildDef_basename(pB); - pf("%s# Begin build [%s-%s]. flags=0x%02x\n", zBanner, zNM, pB->flags); - pf("# zJsOut=%s\n# zCmppD=%s\n# zWasmOut=%s\n", pB->zJsOut, - pB->zCmppD ? pB->zCmppD : "", - pB->zWasmOut ? pB->zWasmOut : ""); - pf("$(info Setting up build [%s-%s]: %s)\n", zNM, pB->zJsOut); - mk_pre_post(zNM, pB->zCmppD, pB->zWasmOut); - pf("\nemcc.flags.%s.%s ?=\n", zNM); - if( pB->zEmcc && pB->zEmcc[0] ){ - pf("emcc.flags.%s.%s += %s\n", zNM, pB->zEmcc); + assert( oBuildDefs.vanilla.zEnv ); + assert( zBaseName ); + + pf("%s# Begin build [%s%s]. flags=0x%02x\n", zBanner, + pB->zEmo, zBuildName, pB->flags); + pf("# zCmppD=%s\n# zBaseName=%s\n", + pB->zCmppD ? pB->zCmppD : "", zBaseName); + + pf("b.names += %s\n" + "emo.b.%s = %s\n", + zBuildName, zBuildName, pB->zEmo); + + pf("logtag.%s ?= [%s [%s] $@]:\n", + zBuildName, pB->zEmo, zBuildName); + + if( pB->zIfCond ){ + pf("%s\n", pB->zIfCond ); } - /* target pB->zJsOut */ - pf("%s: $(MAKEFILE_LIST) $(sqlite3-wasm.cfiles) $(EXPORTED_FUNCTIONS.api) " - "$(bin.mkwb) " - "$(pre-post-%s-%s.deps) " - "$(sqlite3-api.ext.jses)" - /* ^^^ maintenance reminder: we set these as deps so that they - ** get copied into place early. That allows the developer to - ** reload the base-most test pages while the later-stage builds - ** are still compiling, which is especially helpful when running - ** builds with long build times (like -Oz). */ - "\n", - pB->zJsOut, zNM); - pf("\t@echo \"Building $@ ...\"\n"); - if( LIBMODE_UNSUPPORTED & pB->flags ){ - ps("\t@echo 'ACHTUNG: $@ is an unsupported build. " - "Use at your own risk.'"); + pf("$(info $(logtag.%s) Setting up target b-%s)\n", + zBuildName, zBuildName ); + + pf("dir.dout.%s ?= $(dir.dout)/%s\n", zBuildName, zBuildName); + + pf("out.%s.base ?= $(dir.dout.%s)/%s\n", + zBuildName, zBuildName, zBaseName); + pf("out.%s.js ?= $(dir.dout.%s)/%s%s\n", + zBuildName, zBuildName, zBaseName, zJsExt); + pf("out.%s.wasm ?= $(dir.dout.%s)/%s.wasm\n", + //"$(basename $@).wasm" + zBuildName, zBuildName, zBaseName); + + pf("dir.dout.%s ?= $(dir.dout)/%s\n", zBuildName, zBuildName); + pf("out.%s.base ?= $(dir.dout.%s)/%s\n", + zBuildName, zBuildName, zBaseName); + + if( pB->zDeps ){ + pf("deps.%s += %s\n", zBuildName, pB->zDeps); } - pf("\t$(bin.emcc) -o $@ $(emcc_opt_full) $(emcc.flags) \\\n"); - pf("\t\t$(emcc.jsflags) -sENVIRONMENT=$(emcc.environment.%s) \\\n", - pB->zMode); - pf("\t\t$(pre-post-%s-%s.flags) \\\n", zNM); - pf("\t\t$(emcc.flags.%s) $(emcc.flags.%s.%s) \\\n", pB->zName, zNM); - pf("\t\t$(cflags.common) $(SQLITE_OPT) \\\n" - "\t\t$(cflags.%s) $(cflags.%s.%s) \\\n" - "\t\t$(cflags.wasm_extra_init) $(sqlite3-wasm.cfiles)\n", pB->zName, zNM); - if( (LIBMODE_ESM & pB->flags) || (LIBMODE_NODEJS & pB->flags) ){ - /* TODO? Replace this $(call) with the corresponding makefile - ** code. OTOH, we also use this $(call) in the speedtest1-wasmfs - ** build, which is not part of the rules emitted by this - ** program. */ - pf("\t@$(call SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT,1,%d)\n", - (LIBMODE_WASMFS & pB->flags) ? 1 : 0); + + pf("c-pp.D.%s ?= %s\n", zBuildName, pB->zCmppD ? pB->zCmppD : ""); + pf("emcc.environment.%s ?= %s\n", zBuildName, + pB->zEnv ? pB->zEnv : oBuildDefs.vanilla.zEnv); + if( pB->zEmccExtra ){ + pf("emcc.flags.%s = %s\n", zBuildName, pB->zEmccExtra); } - pf("\t@chmod -x %s\n", zWasmOut); - pf("\t@$(maybe-wasm-strip) %s\n", zWasmOut); - pf("\t@$(call SQLITE.CALL.WASM-OPT,%s)\n", zWasmOut); - ps("\t@$(SQLITE.strip-createExportWrapper)"); - /* - ** The above $(bin.emcc) call will write pB->zJsOut, a.k.a. $@, and - ** will create a like-named .wasm file (pB->zWasmOut). That .wasm - ** file name gets hard-coded into $@ so we need to, for some cases, - ** patch zJsOut to use the name sqlite3.wasm instead. The resulting - ** .wasm file is identical for all builds for which pB->zEmcc is - ** empty. - */ - if( (LIBMODE_BUNDLER_FRIENDLY & pB->flags) ){ - pf("\t@echo 'Patching $@ for %s.wasm...'; \\\n", pB->zName); - pf("\t\trm -f %s; \\\n", zWasmOut); - pf("\t\tsed -i -e 's/%s-%s.wasm/%s.wasm/g' $@ || exit;\n", - /* ^^^^^^ reminder: Mac/BSD sed has no -i flag but this - ** build process explicitly requires a Linux system. */ - zNM, pB->zName); - pf("\t@ls -la $@\n"); - if( LIBMODE_BUNDLER_FRIENDLY & pB->flags ){ + + emit_api_js(zBuildName, pB->zCmppD); + if( pB->flags & F_64BIT ){ + pf("c-pp.D.%s += $(c-pp.D.64bit)\n", zBuildName); + } + mk_pre_post(zBuildName); + + { /* build it... */ + pf(zBanner + "$(out.%s.js): $(MAKEFILE_LIST) $(sqlite3-wasm.c.in)" + " $(EXPORTED_FUNCTIONS.api) $(deps.%s)" + " $(bin.mkwb) $(pre-post.%s.deps)" + "\n", + zBuildName, zBuildName, zBuildName); + + emit_compile_start(zBuildName); + + if( F_UNSUPPORTED & pB->flags ){ + pf("\t@echo '$(logtag.%s) $(emo.fire)$(emo.fire)$(emo.fire): " + "unsupported build. Use at your own risk.'\n", zBuildName); + } + + /* emcc ... */ + { + pf("\t$(b.cmd@)$(bin.emcc) -o $@ "); + if( pB->zEmcc ){ + pf("%s $(emcc.flags.%s)\n", + pB->zEmcc, zBuildName); + }else{ + pf("$(emcc_opt_full) $(emcc.flags)" + " $(emcc.jsflags)" + " -sENVIRONMENT=$(emcc.environment.%s)" + " $(pre-post.%s.flags)" + " $(emcc.flags.%s)" + " $(cflags.common)" + " $(cflags.%s)" + " $(SQLITE_OPT)" + " $(cflags.wasm_extra_init) $(sqlite3-wasm.c.in)\n", + zBuildName, zBuildName, zBuildName, zBuildName + ); + } + } + + { /* Post-compilation transformations and copying to + $(dir.dout)... */ + if( (F_ESM & pB->flags) || (F_NODEJS & pB->flags) ){ + pf("\t@$(call b.call.patch-export-default,1,%d,$(logtag.%s))\n", + (F_WASMFS & pB->flags) ? 1 : 0, + zBuildName + ); + } + + pf("\t@chmod -x $(out.%s.wasm)\n", zBuildName + /* althttpd will automatically try to execute wasm files + if they have the +x bit set. Why that bit is set + at all is a mystery. */); + pf("\t@$(call b.call.wasm-strip,%s)\n", zBuildName); + + pf("\t@$(call b.do.wasm-opt,%s)\n", zBuildName); + pf("\t@$(call b.strip-js-emcc-bindings,$(logtag.%s))\n", zBuildName); + + if( CP_JS & pB->flags && !(pB->zDotWasm/*handled below*/) ){ + pf("\t@$(call b.cp,%s,$@,$(dir.dout))\n", + zBuildName + ); + } + if( CP_WASM & pB->flags ){ + pf("\t@$(call b.cp,%s,$(basename $@).wasm,$(dir.dout))\n", + zBuildName + //"\techo '[%s $(out.%s.wasm)] $(emo.disk) $(dir.dout)/$(notdir $(out.%s.wasm))' + //pB->zEmo, zBuildName + ); + } + /* + ** $(bin.emcc) will write out $@ and will create a like-named + ** .wasm file. The resulting .wasm and .js/.mjs files are + ** identical across all builds which have the same pB->zEmccExtra. + ** + ** We copy one or both of those files to $(dir.dout) (the top-most + ** build target dir), but: that .wasm file name gets hard-coded + ** into $@ so we need, for some cases, to patch the name to + ** pB->zDotWasm when copying to $(dir.dout). + */ + if( pB->zDotWasm && (CP_JS & pB->flags) ){ + pf("\t@echo '$(logtag.%s) $(emo.disk) " + "s/\"%s.wasm\"/\"%s.wasm\"/g " + "in $(dir.dout)/$(notdir $@)'; \\\n" + "sed " + "-e 's/\"%s.wasm\"/\"%s.wasm\"/g' " + "-e \"s/'%s.wasm'/'%s.wasm'/g\" " + "$@ > $(dir.dout)/$(notdir $@)\n", + zBuildName, + zBaseName, pB->zDotWasm, + zBaseName, pB->zDotWasm, + zBaseName, pB->zDotWasm); + } + /* Avoid a 3rd occurrence of the bug fixed by 65798c09a00662a3, ** which was (in two cases) caused by makefile refactoring and ** not recognized until after a release was made with the broken ** sqlite3-bundler-friendly.mjs (which is used by the npm ** subproject but is otherwise untested/unsupported): */ pf("\t@if grep -e '^ *importScripts(' $@; " - "then echo 'ERROR: bug fixed in 65798c09a00662a3 has re-appeared'; " - "exit 1; fi;\n"); + "then echo '$(logtag.%s) $(emo.bug)$(emo.fire): " + "bug fixed in 65798c09a00662a3 has re-appeared'; " + "exit 1; fi;\n", zBuildName); } - }else{ - pf("\t@ls -la %s $@\n", zWasmOut); + } + pf("\t@$(call b.echo,%s,$(emo.done) done!)\n", zBuildName); + + pf("\n%dbit: $(out.%s.js)\n" + "$(out.%s.wasm): $(out.%s.js)\n" + "b-%s: $(out.%s.js) $(out.%s.wasm)\n", + (F_64BIT & pB->flags) ? 64 : 32, zBuildName, + zBuildName, zBuildName, + zBuildName, zBuildName, zBuildName); + + if( CP_JS & pB->flags ){ + pf("$(dir.dout)/%s%s: $(out.%s.js)\n", + pB->zBaseName, zJsExt, zBuildName + ); + } + if( CP_WASM & pB->flags ){ + pf("$(dir.dout)/%s.wasm: $(out.%s.wasm)\n", + pB->zBaseName, zBuildName + ); } - if( LIBMODE_64BIT & pB->flags ){ - pf("64bit: %s\n", pB->zJsOut); - }else if( 0==(LIBMODE_NOT_IN_ALL & pB->flags) - && 0==(LIBMODE_UNSUPPORTED & pB->flags) ){ - pf("all: %s\n", pB->zJsOut); + pf("%s: $(out.%s.js)\n", + 0==((F_UNSUPPORTED | F_NOT_IN_ALL) & pB->flags) + ? "all" : "more", zBuildName); + + if( pB->zIfCond ){ + pf("else\n" + "$(info $(logtag.%s) $(emo.stop) disabled by condition: %s)\n" + "endif\n", + zBuildName, pB->zIfCond); } - pf("# End build [%s-%s]%s", zNM, zBanner); -#undef zNM + pf("# End build [%s]%s", zBuildName, zBanner); } -int main(void){ + +static void emit_gz(char const *zBuildName, + char const *zOutDotSExt){ + pf("\n$(out.%s.%s).gz: $(out.%s.%s)\n" + "\t@$(call b.echo,%s,$(emo.disk))\n" + "\t@gzip < $< > $@\n", + zBuildName, zOutDotSExt, + zBuildName, zOutDotSExt, + zBuildName); +} + +/* +** Emits rules for the fiddle builds. +*/ +static void mk_fiddle(void){ + for(int i = 0; i < 2; ++i ){ + /* 0==normal, 1==debug */ + int const isDebug = i>0; + const char * const zBuildName = i ? "fiddle.debug" : "fiddle"; + + pf(zBanner "# Begin build %s\n", zBuildName); + if( isDebug ){ + pf("emo.%s = $(emo.fiddle)$(emo.bug)\n", zBuildName); + }else{ + pf("emo.fiddle = ๐ŸŽป\n"); + } + pf("logtag.%s = [$(emo.%s) [%s] $@]:\n", + zBuildName, zBuildName, zBuildName); + pf("$(info $(logtag.%s) Setting up target b-%s)\n", + zBuildName, zBuildName ); + + pf("dir.%s = %s\n" + "out.%s.js = $(dir.%s)/fiddle-module.js\n" + "out.%s.wasm = $(dir.%s)/fiddle-module.wasm\n" + "$(out.%s.wasm): $(out.%s.js)\n", + zBuildName, zBuildName, + zBuildName, zBuildName, + zBuildName, zBuildName, + zBuildName, zBuildName); + + emit_api_js(zBuildName, 0); + mk_pre_post(zBuildName); + + {/* emcc */ + pf("$(out.%s.js): $(MAKEFILE_LIST) " + "$(EXPORTED_FUNCTIONS.fiddle) " + "$(fiddle.c.in) " + "$(pre-post.%s.deps)\n", + zBuildName, zBuildName); + emit_compile_start(zBuildName); + pf("\t$(b.cmd@)$(bin.emcc) -o $@" + " $(emcc.flags.%s)" /* set in fiddle.make */ + " $(pre-post.%s.flags)" + " $(fiddle.c.in)" + "\n", + zBuildName, zBuildName); + pf("\t@chmod -x $(out.%s.wasm)\n", zBuildName); + pf("\t@$(call b.call.wasm-strip,%s)\n", zBuildName); + pf("\t@$(call b.strip-js-emcc-bindings,$(logtag.%s))\n", + zBuildName); + pf("\t@$(call b.cp," + "%s," + "$(dir.api)/sqlite3-opfs-async-proxy.js," + "$(dir $@))\n", zBuildName); + if( isDebug ){ + pf("\t@$(call b.cp,%s," + "$(dir.fiddle)/index.html " + "$(dir.fiddle)/fiddle.js " + "$(dir.fiddle)/fiddle-worker.js," + "$(dir $@)" + ")\n", + zBuildName); + } + pf("\t@$(call b.echo,%s,$(emo.done) done!)\n", zBuildName); + } + + pf("\n%s: $(out.%s.wasm)\n", isDebug ? "more" : "all", zBuildName); + + /* Compress fiddle files. We handle each file separately, rather + than compressing them in a loop in the previous target, to help + avoid that hand-edited files, like fiddle-worker.js, do not end + up with stale .gz files (which althttpd will then serve instead + of the up-to-date uncompressed one). */ + emit_gz(zBuildName, "js"); + emit_gz(zBuildName, "wasm"); + + pf("\n%s: $(out.%s.js).gz $(out.%s.wasm).gz\n" + "b-%s: %s\n", + zBuildName, zBuildName, zBuildName, + zBuildName, zBuildName); + if( isDebug ){ + ps("fiddle-debug: fiddle.debug"); /* older name */ + }else{ + ps("all: b-fiddle"); + } + pf("# End %s" zBanner, zBuildName); + } +} + +#if 0 +static void mk_speedtest1(void){ + char const *zBuildName = "speedtest1"; + pf(zBanner "# Begin build %s\n", zBuildName); + pf("emo.%s =" + "๐Ÿ›ผ" // roller skates + /*"๐ŸŽ" //racecar doesn't show up well in my emacs or terminal */ + "\n", + zBuildName); + pf("logtag.%s = [$(emo.%s) [%s] $@]:\n" + "$(info $(logtag.%s) Setting up target speedtest1)\n" + "all: %s\n", + zBuildName, zBuildName, zBuildName, + zBuildName, zBuildName ); + + pf("dir.dout.%s ?= $(dir.dout)\n", zBuildName); + pf("out.%s.js = $(dir.dout)/speedtest1.js\n" + "out.%s.wasm = $(dir.dout)/speedtest1.wasm\n", + zBuildName, zBuildName); + + emit_api_js(zBuildName, 0); + mk_pre_post(zBuildName); + + /* Main rules are in the makefile */ + +#if 0 + mk_pre_post("speedtest1-vanilla"); + ps(zBanner "ifeq (1,$(HAVE_WASMFS))"); + mk_pre_post("speedtest1-wasmfs"); + ps("endif\n# ^^^ HAVE_WASMFS" zBanner); +#endif +#if 0 + mk_pre_post(0, "speedtest1","vanilla", 0, "speedtest1.wasm"); + mk_pre_post(0, "speedtest1-wasmfs", "esm", + "$(c-pp.D.sqlite3-bundler-friendly) -Dwasmfs", + "speetest1-wasmfs.wasm"); +#endif +} +#endif + +int main(int argc, char const ** argv){ int rc = 0; const BuildDef *pB; pf("# What follows was GENERATED by %s. Edit at your own risk.\n", __FILE__); - mk_prologue(); -#define E(N) mk_lib_mode(&oBuildDefs.N); - BuildDefs_map(E) + + if(argc>1){ + /* + ** Only emit the rules for the given list of builds, sans prologue + ** (unless the arg "prologue" is given). Intended only for + ** debugging, not actual makefile generation. + */ + for( int i = 1; i < argc; ++i ){ + char const * const zArg = argv[i]; +#define E(N) if(0==strcmp(#N, zArg)) {mk_lib_mode(# N, &oBuildDefs.N);} else /**/ + BuildDefs_map(E) if( 0==strcmp("prologue",zArg) ){ + mk_prologue(); + }else { + fprintf(stderr,"Unkown build name: %s\n", zArg); + rc = 1; + break; + } #undef E - mk_fiddle(); - mk_pre_post("speedtest1","vanilla", 0, "speedtest1.wasm"); - mk_pre_post("speedtest1-wasmfs", "esm", - "$(c-pp.D.sqlite3-bundler-friendly) -Dwasmfs", - "speetest1-wasmfs.wasm"); + } + }else{ + /* + ** Emit the whole shebang... + */ + mk_prologue(); +#define E(N) mk_lib_mode(# N, &oBuildDefs.N); + BuildDefs_map(E) +#undef E +#if 0 + pf(zBanner + "$(dir.dout)/sqlite3.js: $(out.vanilla.js)\n" + "$(dir.dout)/sqlite3.mjs: $(out.esm.js)\n" + "$(dir.dout)/sqlite3.wasm: $(out.vanilla.wasm)\n" + "$(dir.dout)/sqlite3-64bit.js: $(out.vanilla64.js)\n" + "$(dir.dout)/sqlite3-64bit.mjs: $(out.esm64.js)\n" + "$(dir.dout)/sqlite3-64bit.wasm: $(out.vanilla64.wasm)\n" + "b-vanilla: $(dir.dout)/sqlite3.wasm\n" + "b-vanilla64: $(dir.dout)/sqlite3-64bit.wasm\n" + "b-esm: $(dir.dout)/sqlite3.mjs\n" + "b-esm64: $(dir.dout)/sqlite3-64bit.mjs\n" + ); +#endif + mk_fiddle(); + //mk_speedtest1(); + } return rc; } diff --git a/ext/wasm/speedtest1-worker.html b/ext/wasm/speedtest1-worker.html index ba0d22fb61..c207e51824 100644 --- a/ext/wasm/speedtest1-worker.html +++ b/ext/wasm/speedtest1-worker.html @@ -170,9 +170,10 @@ const urlParams = new URL(self.location.href).searchParams; const W = new Worker( - "speedtest1-worker.js?sqlite3.dir=jswasm"+ - (urlParams.has('opfs-verbose') ? '&opfs-verbose' : '')+ - (urlParams.has('opfs-disable') ? '&opfs-disable' : '') + "speedtest1-worker.js?sqlite3.dir=jswasm"+ + '&sqlite3.debugModule'+ + (urlParams.has('opfs-verbose') ? '&opfs-verbose' : '')+ + (urlParams.has('opfs-disable') ? '&opfs-disable' : '') ); const mPost = function(msgType,payload){ W.postMessage({type: msgType, data: payload}); diff --git a/ext/wasm/tester1-worker.html b/ext/wasm/tester1-worker.c-pp.html similarity index 69% rename from ext/wasm/tester1-worker.html rename to ext/wasm/tester1-worker.c-pp.html index e768c3d6c3..dc84bc15e9 100644 --- a/ext/wasm/tester1-worker.html +++ b/ext/wasm/tester1-worker.c-pp.html @@ -4,13 +4,25 @@ - - - sqlite3 tester #1: Worker thread + + + sqlite3 tester #1: Worker thread +//#if 64bit + (64-bit WASM) +//#else + (32-bit WASM) +//#endif + -

    sqlite3 tester #1: Worker thread

    +

    sqlite3 tester #1: Worker thread +//#if 64bit + (64-bit WASM) +//#else + (32-bit WASM) +//#endif +

    Variants: conventional UI thread, conventional worker, @@ -35,23 +47,42 @@ logTarget.append(ln); }; const cbReverse = document.querySelector('#cb-log-reverse'); + const cbReverseKey = 'tester1:cb-log-reverse'; const cbReverseIt = ()=>{ logTarget.classList[cbReverse.checked ? 'add' : 'remove']('reverse'); + localStorage.setItem(cbReverseKey, cbReverse.checked ? 1 : 0); }; cbReverse.addEventListener('change',cbReverseIt,true); + if(localStorage.getItem(cbReverseKey)){ + cbReverse.checked = !!(+localStorage.getItem(cbReverseKey)); + } cbReverseIt(); const urlParams = new URL(self.location.href).searchParams; const workerArgs = []; - if(urlParams.has('esm')){ - logHtml('warning',"Attempting to run an ES6 Worker Module, "+ - "which is not supported by all browsers! "+ - "e.g. Firefox (as of 2023-05) cannot do this."); - workerArgs.push("tester1.mjs",{type:"module"}); + const baseName = +//#if 64bit + 'tester1-64bit' +//#else + 'tester1' +//#endif + ; + if(urlParams.has('esm')){ + logHtml('warning',"Attempting to run an ES6 Worker Module, "+ + "which is not supported by all browsers!."); + workerArgs.push(baseName+'.mjs', {type:"module"}); + const bitness = +//#if 64bit + 64 +//#else + 32 +//#endif + ; document.querySelectorAll('title,#color-target').forEach((e)=>{ - e.innerText = "sqlite3 tester #1: ES6 Worker Module"; + e.innerText = + "sqlite3 tester #1: ES6 Worker Module ("+bitness+"-bit WASM)"; }); }else{ - workerArgs.push("tester1.js?sqlite3.dir=jswasm"); + workerArgs.push(baseName+'.js?sqlite3.dir=jswasm'); } const w = new Worker(...workerArgs); w.onmessage = function({data}){ diff --git a/ext/wasm/tester1.c-pp.html b/ext/wasm/tester1.c-pp.html index bbdd8b2233..0fae73de2b 100644 --- a/ext/wasm/tester1.c-pp.html +++ b/ext/wasm/tester1.c-pp.html @@ -8,9 +8,13 @@ sqlite3 tester #1: //#if target=es6-module -ES6 Module in UI thread +ES6 Module in +//#endif + UI thread +//#if 64bit + (64-bit WASM) //#else -UI thread + (32-bit WASM) //#endif @@ -32,10 +36,19 @@ UI thread document.querySelector('title').innerHTML; })(); //#if target=es6-module +//#if 64bit + +//#else +//#endif +//#else +//#if 64bit + + //#else //#endif +//#endif target=es6-module diff --git a/ext/wasm/tester1.c-pp.js b/ext/wasm/tester1.c-pp.js index 0ed6404582..ce33b55260 100644 --- a/ext/wasm/tester1.c-pp.js +++ b/ext/wasm/tester1.c-pp.js @@ -44,7 +44,13 @@ ./c-pp -f tester1.c-pp.js -o tester1-esm.mjs -Dtarget=es6-module */ //#if target=es6-module -import {default as sqlite3InitModule} from './jswasm/sqlite3.mjs'; +import {default as sqlite3InitModule} from +//#if 64bit +'./jswasm/sqlite3-64bit.mjs' +//#else +'./jswasm/sqlite3.mjs' +//#endif +; globalThis.sqlite3InitModule = sqlite3InitModule; //#else 'use strict'; @@ -108,16 +114,15 @@ globalThis.sqlite3InitModule = sqlite3InitModule; logTarget.append(ln); }; const cbReverse = document.querySelector('#cb-log-reverse'); - //cbReverse.setAttribute('checked','checked'); const cbReverseKey = 'tester1:cb-log-reverse'; const cbReverseIt = ()=>{ logTarget.classList[cbReverse.checked ? 'add' : 'remove']('reverse'); - //localStorage.setItem(cbReverseKey, cbReverse.checked ? 1 : 0); + localStorage.setItem(cbReverseKey, cbReverse.checked ? 1 : 0); }; cbReverse.addEventListener('change', cbReverseIt, true); - /*if(localStorage.getItem(cbReverseKey)){ + if(localStorage.getItem(cbReverseKey)){ cbReverse.checked = !!(+localStorage.getItem(cbReverseKey)); - }*/ + } cbReverseIt(); }else{ /* Worker thread */ console.log("Running in a Worker thread."); @@ -138,6 +143,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; }else{ postMessage({type:'test-result', payload:{pass}}); } + TestUtil.checkHeapSize(true); }; const log = (...args)=>{ //console.log(...args); @@ -298,6 +304,7 @@ globalThis.sqlite3InitModule = sqlite3InitModule; logClass(['faded','one-test-summary'], TestUtil.counter - tc, 'assertion(s) in', roundMs(then-now),'ms'); + TestUtil.checkHeapSize(); } logClass(['green','group-end'], "#"+this.number+":", @@ -348,6 +355,14 @@ globalThis.sqlite3InitModule = sqlite3InitModule; reportFinalTestStatus(false); } }.bind(this)); + }, + + checkHeapSize: function(force=false){ + const heapSize = SQLite3.wasm.heap8().byteLength; + if( force || heapSize !== TestUtil.lastHeapSize ){ + TestUtil.lastHeapSize = heapSize; + log('WASM heap size:', heapSize,'bytes'); + } } }/*TestUtil*/; const T = TestUtil; @@ -3807,7 +3822,11 @@ globalThis.sqlite3InitModule = sqlite3InitModule; are simply lost, and such scripts see the globalThis.location of _this_ script. */ +//#if 64bit + let sqlite3Js = 'sqlite3-64bit.js'; +//#else let sqlite3Js = 'sqlite3.js'; +//#endif const urlParams = new URL(globalThis.location.href).searchParams; if(urlParams.has('sqlite3.dir')){ sqlite3Js = urlParams.get('sqlite3.dir') + '/' + sqlite3Js; @@ -3840,7 +3859,6 @@ globalThis.sqlite3InitModule = sqlite3InitModule; }else{ logClass('warning',"BigInt/int64 support is disabled."); } - log("WASM pointer size:",wasm.ptr.size,"bytes"); if(haveWasmCTests()){ log("sqlite3__wasm_test_...() APIs are available."); }else{ @@ -3848,6 +3866,8 @@ globalThis.sqlite3InitModule = sqlite3InitModule; } log("registered vfs list =",capi.sqlite3_js_vfs_list().join(', ')); SQLite3 = sqlite3; + log("WASM pointer size:",wasm.ptr.size,"bytes."); + TestUtil.checkHeapSize(); TestUtil.runTests(sqlite3); }); })(self); diff --git a/ext/wasm/wasmfs.make b/ext/wasm/wasmfs.make index 0d1fb4043a..b694b8eb92 100644 --- a/ext/wasm/wasmfs.make +++ b/ext/wasm/wasmfs.make @@ -97,7 +97,7 @@ $(speedtest1-wasmfs.mjs): $(speedtest1.cfiles) $(sqlite3-wasmfs.js) \ $(emcc.flags.sqlite3-wasmfs) \ $(emcc.flags.speedtest1-wasmfs) \ -o $@ $(speedtest1.cfiles) -lm - @$(call SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT,1,1) + @$(call SQLITE.CALL.xJS.ESM-EXPORT-DEFAULT,1,1,$(logtag.wasmfs)) $(maybe-wasm-strip) $(speedtest1-wasmfs.wasm) chmod -x $(speedtest1-wasmfs.wasm) ls -la $@ $(speedtest1-wasmfs.wasm) diff --git a/manifest b/manifest index dee7291603..59053023f2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\stranslation\sof\s"\v"\sin\sJSON5.\n[forum:/forumpost/28e21085f9c6a4e7|Forum\spost\s28e21085f9]. -D 2025-09-25T15:06:57.956 +C Overhaul\sthe\swasm\sbuild\sto\s(A)\ssupport\smore\scoexisting\svariants,\se.g.\s32/64-bit\sof\sboth\svanilla\sand\sesm,\sand\s(B)\sbuild\seach\svariant\sto\sits\sown\ssubdir\sso\sthat\sthey\scan\sbuild\sin\sparallel.\sIt\scan,\swith\smake\s-j4,\snow\sbuild\sall\snew\svariants\sin\shalf\sthe\stime\sit\spreviously\stook\sfor\sjust\sthe\s32-bit\sbuilds.\sThe\snew\sbuild\slogging\soutput,\sthough\sunconventional,\sserves\stwo\spurposes:\s(A)\simprove\smy\sbuild-time\ssituational\sawareness\sand\s(B)\sit\shelp\sdemystify\ssome\sof\sthe\sbuild\ssteps\sand\soutput\sfiles. +D 2025-09-25T15:17:58.460 F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea @@ -578,7 +578,7 @@ F ext/session/sqlite3session.c b3de195ce668cace9b324599bf6255a70290cbfb5451e826e F ext/session/sqlite3session.h 7404723606074fcb2afdc6b72c206072cdb2b7d8ba097ca1559174a80bc26f7a F ext/session/test_session.c 8766b5973a6323934cb51248f621c3dc87ad2a98f023c3cc280d79e7d78d36fb F ext/wasm/EXPORTED_FUNCTIONS.fiddle.in 27450c8b8c70875a260aca55435ec927068b34cef801a96205adb81bdcefc65c -F ext/wasm/GNUmakefile 39beef47c945a140f93435998f07df70e7f0157e4c198f6197dc3b8236fec7ca +F ext/wasm/GNUmakefile 473695f6c5d9fcfa4099ee1d447b3a965d387e44f213d34ed18f7abce84a7852 F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a F ext/wasm/README.md 66ace67ae98a45e4116f2ca5425b716887bcee4d64febee804ff6398e1ae9ec7 F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff @@ -591,12 +591,12 @@ F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-extras cb4fa8842c875b6ee99381523792975 F ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-see fb29e62082a658f0d81102488414d422c393c4b20cc2f685b216bc566237957b F ext/wasm/api/EXPORTED_RUNTIME_METHODS.sqlite3-api 1ec3c73e7d66e95529c3c64ac3de2470b0e9e7fbf7a5b41261c367cf4f1b7287 F ext/wasm/api/README.md f4c0d67caaee21a77b8938c30b5f79667bfc9d0c95d01b51df77ea35ee773884 -F ext/wasm/api/extern-post-js.c-pp.js d3748c6bbdcef15d8e494e0558aad370b9c24242eb020042b3a73b4950562f5b +F ext/wasm/api/extern-post-js.c-pp.js 6bf4407c028fac7d387821420cd25f6d68ad80f7d526e86539b6a3012230917f F ext/wasm/api/extern-pre-js.js cc61c09c7a24a07dbecb4c352453c3985170cec12b4e7e7e7a4d11d43c5c8f41 F ext/wasm/api/post-js-footer.js e617e5f81a907362de152576323155f02d24642e625fc05fb801b86b6a269444 F ext/wasm/api/post-js-header.js 79d078aec33d93b640a19c574b504d88bb2446432f38e2fbb3bb8e36da436e70 F ext/wasm/api/pre-js.c-pp.js 664551f490d296e0f4590d3a029787ab0782b9a1fa5954d73bde4fb2c6bfc709 -F ext/wasm/api/sqlite3-api-cleanup.js f91a2afdef19c350bce99784fff20310d4d060520001059822aa36c4ce80dc56 +F ext/wasm/api/sqlite3-api-cleanup.js e643a96c5323e051e29046bfd6412750917ca2c955e31c9192fb924ecc8ed004 F ext/wasm/api/sqlite3-api-glue.c-pp.js 12f5b36775fab1e7bf5385689fded2b2a9f77360562515e9849acb5e66602e2d F ext/wasm/api/sqlite3-api-oo1.c-pp.js db4c8ebb03bac60db32ce03f8c615b00f4e4ad53e7d5de5e63d2780cba052caa F ext/wasm/api/sqlite3-api-prologue.js 259c72c6a33ba1be2297c568cbc2ad53437d1d4b2613d7772c56a3aa00bf435d @@ -608,13 +608,9 @@ F ext/wasm/api/sqlite3-vfs-opfs-sahpool.c-pp.js e2c0bd6917b697137035d775ed3300e6 F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js f8e762aeb568e0fd050ab991c5f3420dca9c14630386e4e18d42c0624b8ff7cd F ext/wasm/api/sqlite3-vtab-helper.c-pp.js 9097074724172e31e56ce20ccd7482259cf72a76124213cbc9469d757676da86 F ext/wasm/api/sqlite3-wasm.c ff2dc011e17b06186b8b35e408626d7ace69a362b92c197a34d78bef25c7105a -F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 4ad256b4ff7f839ad18931ed35d46cced544207bd2209665ec552e193f7f4544 -F ext/wasm/api/sqlite3-worker1.c-pp.js 5e8706c2c4af2a57fbcdc02f4e7ef79869971bc21bb8ede777687786ce1c92d5 -F ext/wasm/batch-runner-sahpool.html e9a38fdeb36a13eac7b50241dfe7ae066fe3f51f5c0b0151e7baee5fce0d07a7 -F ext/wasm/batch-runner-sahpool.js 54a3ac228e6c4703fe72fb65c897e19156263a51fe9b7e21d2834a45e876aabd -F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8 -F ext/wasm/batch-runner.js 05ec254f5dbfe605146d9640b3db17d6ef8c3fbef6aa8396051ca72bb5884e3f -F ext/wasm/c-pp.c 75e23ad9ca3a3771e70d401ba79c0bacdf7b2169d1c17f9e2639d81067e5ab95 +F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 8fb6adfbae6270344f43f2652e63780df3f86521755bde8f92cf6b809ba7891d +F ext/wasm/api/sqlite3-worker1.c-pp.js 69483df1df2d0988e708390f7b1feda769c16e9e9efd4683557f8e7197099cc0 +F ext/wasm/c-pp.c 7692739ac435120c37b9de993f152c90e5dbf6a340ca6331de81d7b8b06b5307 F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51 F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15 F ext/wasm/common/testing.css e97549bab24126c24e0daabfe2de9bb478fb0a69fdb2ddd0a73a992c091aad6f @@ -631,21 +627,20 @@ F ext/wasm/demo-worker1.html 2c178c1890a2beb5a5fecb1453e796d067a4b8d3d2a04d65ca2 F ext/wasm/demo-worker1.js 08720227e98fa5b44761cf6e219269cee3e9dd0421d8d91459535da776950314 F ext/wasm/dist.make 57f5da2f0de5a297b5a0bc39ffec736380050578240ab24d864c2ff1b3634a3b F ext/wasm/example_extra_init.c 2347cd69d19d839ef4e5e77b7855103a7fe3ef2af86f2e8c95839afd8b05862f -F ext/wasm/fiddle.make 7ed14ba851d331b62ea9ddfcdb62184ff853c5620c3856631310ca0f9633ef93 F ext/wasm/fiddle/fiddle-worker.js 7798af02e672e088ff192716f80626c8895e19301a65b8af6d5d12b2d13d2451 F ext/wasm/fiddle/fiddle.js 84fd75967e0af8b69d3dd849818342227d0f81d13db92e0dcbc63649b31a4893 F ext/wasm/fiddle/index.html a27b8127ef9ecf19612da93b2a6a73bdb3777b5c56b5450bb7200a94bc108ff9 F ext/wasm/index-dist.html 56132399702b15d70c474c3f1952541e25cb0922942868f70daf188f024b3730 -F ext/wasm/index.html bcaa00eca521b372a6a62c7e7b17a870b0fcdf3e418a5921df1fd61e5344080d +F ext/wasm/index.html 1b329fb63e057c02a17ce178308d6b06aac62d92af7dd6d821fb0e183e0f1557 F ext/wasm/jaccwabyt/jaccwabyt.js bbac67bc7a79dca34afe6215fd16b27768d84e22273507206f888c117e2ede7d F ext/wasm/jaccwabyt/jaccwabyt.md 167fc0b624c9bc2c477846e336de9403842d81b1a24fc4d3b24317cb9eba734f -F ext/wasm/mkwasmbuilds.c fc6044341e6cdc8825ffc07db505c92f83cb62cb7c8d271f293f4dc8a8887468 +F ext/wasm/mkwasmbuilds.c 3f27681fd3b32a78560feb3bb54f0f46a68f75024f65d9c5d4247f6bd08a04ad F ext/wasm/module-symbols.html dc476b403369b26a1a23773e13b80f41b9a49f0825e81435fe3600a7cfbbe337 F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96 F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63 F ext/wasm/speedtest1-wasmfs.html 0e9d335a9b5b5fafe6e1bc8dc0f0ca7e22e6eb916682a2d7c36218bb7d67379d F ext/wasm/speedtest1-wasmfs.mjs c77c7231338ed5c0e1ce16aa29106df8e5b5cf11a48319c49433490a8d3ded30 -F ext/wasm/speedtest1-worker.html d24d1e06caf3dcd83430c8c3d87761ff7555fd06eaeaf2fc02ce49cf45f0d032 +F ext/wasm/speedtest1-worker.html 068d4190f304fa1c34e6501a1b3a4c32fe8d8dac93c2d0f53d667a1cb386eedc F ext/wasm/speedtest1-worker.js 5b7eba7cdb5239768e1ed61edb046df8e0092e6c9d6e0bc76e51536022bdccb9 F ext/wasm/speedtest1.html e2a0e0bd12243ca34b11235bf9f3c229f4574ea1125f2ecf2bf0589853d6f9c8 F ext/wasm/split-speedtest1-script.sh a3e271938d4d14ee49105eb05567c6a69ba4c1f1293583ad5af0cd3a3779e205 x @@ -653,9 +648,9 @@ F ext/wasm/sql/000-mandelbrot.sql 775337a4b80938ac8146aedf88808282f04d02d983d826 F ext/wasm/sql/001-sudoku.sql 35b7cb7239ba5d5f193bc05ec379bcf66891bce6f2a5b3879f2f78d0917299b5 F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555e685bce3da8c3f F ext/wasm/test-opfs-vfs.js 1618670e466f424aa289859fe0ec8ded223e42e9e69b5c851f809baaaca1a00c -F ext/wasm/tester1-worker.html ebc4b820a128963afce328ecf63ab200bd923309eb939f4110510ab449e9814c -F ext/wasm/tester1.c-pp.html 1c1bc78b858af2019e663b1a31e76657b73dc24bede28ca92fbe917c3a972af2 -F ext/wasm/tester1.c-pp.js a2bc1a96dbe55e3b14ace35e8561893e84221d63794a9bc2ab5a5f1b1d6bc5a1 +F ext/wasm/tester1-worker.c-pp.html b240b59cd313427434190d5795f57986b9add1a08df53921311fc0b8221b672a w ext/wasm/tester1-worker.html +F ext/wasm/tester1.c-pp.html 93fbedb6b15eaad9b03df0524c702a81896e358785655ae2c751e30ef8202e54 +F ext/wasm/tester1.c-pp.js 2b2aea6d9938fcfad0e3fd8ae094bced45212b807812b60a0dbe970f7f143f82 F ext/wasm/tests/opfs/concurrency/index.html 657578a6e9ce1e9b8be951549ed93a6a471f4520a99e5b545928668f4285fb5e F ext/wasm/tests/opfs/concurrency/test.js d08889a5bb6e61937d0b8cbb78c9efbefbf65ad09f510589c779b7cc6a803a88 F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2 @@ -664,7 +659,7 @@ F ext/wasm/tests/opfs/sahpool/digest.html 206d08a34dc8bd570b2581d3d9ab3ecad3201b F ext/wasm/tests/opfs/sahpool/index.html be736567fd92d3ecb9754c145755037cbbd2bca01385e2732294b53f4c842328 F ext/wasm/tests/opfs/sahpool/sahpool-pausing.js f264925cfc82155de38cecb3d204c36e0f6991460fff0cb7c15079454679a4e2 F ext/wasm/tests/opfs/sahpool/sahpool-worker.js bd25a43fc2ab2d1bafd8f2854ad3943ef673f7c3be03e95ecf1612ff6e8e2a61 -F ext/wasm/wasmfs.make 411dd94b40406572caddf88392a1ccc4deed0f88d260516e59ca6e0c887ee861 +F ext/wasm/wasmfs.make 5de02751b3e9e79b81a52ab4fe0ed6aa6a23311a90db58fea98c1c4e2845e562 F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0 F main.mk fd45a3578989f38bbeb2564a63883f6f6c077105c9f1361c3eac411d31f3afbd F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271 @@ -1650,7 +1645,7 @@ F test/speed4p.explain 6b5f104ebeb34a038b2f714150f51d01143e59aa F test/speed4p.test 377a0c48e5a92e0b11c1c5ebb1bc9d83a7312c922bc0cb05970ef5d6a96d1f0c F test/speedtest.md ee958457ae1b729d9715ae33c0320600000bf1d9ddea1a88dcf79f56729d6fad F test/speedtest.tcl 405411356fbc54af15987b7ffeec330a49138f71584220fb8fe1948b2f7ac907 x -F test/speedtest1.c a9b002a7bfed99ba3166c2a9b8ae45a95b4c2d37f891e1637c022f9e1d15e3f9 +F test/speedtest1.c 6c01252e66f46de0b6b8d5316e03521e2151782104f3608c10262aa5dce85721 F test/spellfix.test 951a6405d49d1a23d6b78027d3877b4a33eeb8221dcab5704b499755bb4f552e F test/spellfix2.test dfc8f519a3fc204cb2dfa8b4f29821ae90f6f8c3 F test/spellfix3.test 0f9efaaa502a0e0a09848028518a6fb096c8ad33 @@ -2175,8 +2170,9 @@ F tool/version-info.c 3b36468a90faf1bbd59c65fd0eb66522d9f941eedd364fabccd7227350 F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7 F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f -P 4a1bb874f12adda26e91970b64e700cb904c126412989be4debac9a5647d6f69 -R 16252f4eed24f0ab043779bc0feb3096 -U drh -Z 2a05a8a00bda16fec363a8f421bf356f +P 2914e8fc7b10e8b42c9a0fbd0e71f495714cee3cae67b8c238d37b1e7ec22359 75079401753778e3bc2f7fa307de9217e8d1ec395079fc1009060f707be32943 +R bf2a14e22103418be159da49f21a135f +T +closed 75079401753778e3bc2f7fa307de9217e8d1ec395079fc1009060f707be32943 Closed\sby\sintegrate-merge. +U stephan +Z c8ddfc47b782e73f61a2d31043443056 # Remove this line to create a well-formed Fossil manifest. diff --git a/manifest.uuid b/manifest.uuid index 92c06a3445..4a4bec61d6 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -2914e8fc7b10e8b42c9a0fbd0e71f495714cee3cae67b8c238d37b1e7ec22359 +2f4be98614b49def2c2951887796c736269ef3bb7ba5b045cae5f748ae165a83 diff --git a/test/speedtest1.c b/test/speedtest1.c index 968f2cb00b..a127f62e9f 100644 --- a/test/speedtest1.c +++ b/test/speedtest1.c @@ -572,9 +572,8 @@ char *speedtest1_once(const char *zFormat, ...){ } rc = sqlite3_reset(pStmt); if( rc!=SQLITE_OK ){ - fprintf(stderr, "%s\nError code %d: %s\n", - sqlite3_sql(pStmt), rc, sqlite3_errmsg(g.db)); - exit(1); + fatal_error("%s\nError code %d: %s\n", + sqlite3_sql(pStmt), rc, sqlite3_errmsg(g.db)); } sqlite3_finalize(pStmt); } @@ -666,9 +665,8 @@ void speedtest1_run(void){ sqlite3_prepare_v2(g.db, sqlite3_sql(g.pStmt), -1, &pNew, 0); rc = sqlite3_finalize(g.pStmt); if( rc!=SQLITE_OK ){ - fprintf(stderr, "%s\nError code %d: %s\n", - sqlite3_sql(pNew), rc, sqlite3_errmsg(g.db)); - exit(1); + fatal_error("%s\nError code %d: %s\n", + sqlite3_sql(pNew), rc, sqlite3_errmsg(g.db)); } g.pStmt = pNew; }else @@ -676,9 +674,8 @@ void speedtest1_run(void){ { rc = sqlite3_reset(g.pStmt); if( rc!=SQLITE_OK ){ - fprintf(stderr, "%s\nError code %d: %s\n", - sqlite3_sql(g.pStmt), rc, sqlite3_errmsg(g.db)); - exit(1); + fatal_error("%s\nError code %d: %s\n", + sqlite3_sql(g.pStmt), rc, sqlite3_errmsg(g.db)); } } speedtest1_shrink_memory(); @@ -2152,7 +2149,7 @@ void testset_rtree(int p1, int p2){ } speedtest1_end_test(); } - + n = g.szTest*200; speedtest1_begin_test(120, "%d one-dimensional overlap slice queries", n); speedtest1_prepare("SELECT count(*) FROM rt1 WHERE y1>=?1 AND y0<=?2"); @@ -2181,7 +2178,6 @@ void testset_rtree(int p1, int p2){ } speedtest1_end_test(); } - n = g.szTest*200; speedtest1_begin_test(125, "%d custom geometry callback queries", n);