xref: /sqlite-3.40.0/ext/wasm/GNUmakefile (revision f815011a)
1# This GNU makefile exists primarily to simplify/speed up development
2# of the sqlite3 WASM components. It is not part of the canonical
3# build process.
4#
5# Maintenance notes: the fiddle build is currently performed in the
6# top-level ../../Makefile.in. It may be moved into this file at some
7# point, as GNU Make has been deemed acceptable for the WASM-related
8# components (whereas POSIX Make is required for the more conventional
9# components).
10SHELL := $(shell which bash 2>/dev/null)
11all:
12emcc_opt ?= -O0
13
14.PHONY: fiddle
15ifneq (,$(wildcard /home/stephan))
16  fiddle_opt ?= $(emcc_opt)
17else
18  fiddle_opt = -Os
19endif
20fiddle:
21	$(MAKE) -C ../.. fiddle -e emcc_opt=$(fiddle_opt)
22all: fiddle
23
24clean:
25	$(MAKE) -C ../../ clean-fiddle
26	-rm -f $(CLEAN_FILES)
27
28MAKEFILE := $(lastword $(MAKEFILE_LIST))
29dir.top := ../..
30# Reminder: some Emscripten flags require absolute paths
31dir.wasm := $(patsubst %/,%,$(dir $(abspath $(MAKEFILE))))
32dir.api := api
33dir.jacc := jaccwabyt
34dir.common := common
35CLEAN_FILES := *~ $(dir.jacc)/*~ $(dir.api)/*~ $(dir.common)/*~
36
37sqlite3.c := $(dir.top)/sqlite3.c
38$(sqlite3.c):
39	$(MAKE) -C $(dir.top) sqlite3.c
40
41SQLITE_OPT = \
42  -DSQLITE_ENABLE_FTS4 \
43  -DSQLITE_ENABLE_RTREE \
44  -DSQLITE_ENABLE_EXPLAIN_COMMENTS \
45  -DSQLITE_ENABLE_UNKNOWN_SQL_FUNCTION \
46  -DSQLITE_ENABLE_STMTVTAB \
47  -DSQLITE_ENABLE_DBPAGE_VTAB \
48  -DSQLITE_ENABLE_DBSTAT_VTAB \
49  -DSQLITE_ENABLE_BYTECODE_VTAB \
50  -DSQLITE_ENABLE_OFFSET_SQL_FUNC \
51  -DSQLITE_OMIT_LOAD_EXTENSION \
52  -DSQLITE_OMIT_DEPRECATED \
53  -DSQLITE_OMIT_UTF16 \
54  -DSQLITE_OMIT_SHARED_CACHE \
55  -DSQLITE_THREADSAFE=0 \
56  -DSQLITE_TEMP_STORE=3 \
57  -DSQLITE_OS_KV_OPTIONAL=1
58#SQLITE_OPT += -DSQLITE_ENABLE_MEMSYS5
59# ^^^ MEMSYS5 is hypothetically useful for non-Emscripten builds but
60# requires adding more infrastructure and fixing one spot in the
61# sqlite3 internals which calls malloc() early on.
62
63# SQLITE_OMIT_LOAD_EXTENSION: if this is true, sqlite3_vfs::xDlOpen
64# and friends may be NULL.
65
66.PHONY: release
67release:
68	$(MAKE) 'emcc_opt=-Os -g3 -flto'
69# ^^^^^ target-specific vars, e.g.:
70#   release: emcc_opt=...
71# apparently only work for file targets, not PHONY targets?
72#
73# ^^^ -flto improves runtime speed at -O0 considerably but doubles
74# build time.
75#
76# ^^^^ -O3, -Oz, -Os minify symbol names and there appears to be no
77# way around that except to use -g3, but -g3 causes the binary file
78# size to absolutely explode (approx. 5x larger). This minification
79# utterly breaks the resulting module, making it unsable except as
80# self-contained/self-referential-only code, as ALL of the exported
81# symbols get minified names.
82#
83# However, we have an option for using -Oz or -Os:
84#
85# Build with (-Os -g3) or (-Oz -g3) then use wasm-strip, from the wabt
86# tools package (https://github.com/WebAssembly/wabt), to strip the
87# debugging symbols. That results in a small build with unmangled
88# symbol names. -Oz gives ever-so-slightly better compression than
89# -Os: not quite 1% in some completely unscientific tests. Runtime
90# speed for the unit tests is all over the place either way so it's
91# difficult to say whether -Os gives any speed benefit over -Oz.
92########################################################################
93
94# Emscripten SDK home dir and related binaries...
95EMSDK_HOME ?= $(word 1,$(wildcard $(HOME)/src/emsdk $(HOME)/emsdk))
96emcc.bin ?= $(word 1,$(wildcard $(shell which emcc) $(EMSDK_HOME)/upstream/emscripten/emcc))
97ifeq (,$(emcc.bin))
98  $(error Cannot find emcc.)
99endif
100
101wasm-strip ?= $(shell which wasm-strip 2>/dev/null)
102ifeq (,$(filter clean,$(MAKECMDGOALS)))
103ifeq (,$(wasm-strip))
104  $(info WARNING: *******************************************************************)
105  $(info WARNING: builds using -O2/-O3/-Os/-Oz will minify WASM-exported names,)
106  $(info WARNING: breaking _All The Things_. The workaround for that is to build)
107  $(info WARNING: with -g3 (which explodes the file size) and then strip the debug)
108  $(info WARNING: info after compilation, using wasm-strip, to shrink the wasm file.)
109  $(info WARNING: wasm-strip was not found in the PATH so we cannot strip those.)
110  $(info WARNING: If this build uses any optimization level higher than -O1 then)
111  $(info WARNING: the ***resulting WASM binary WILL NOT BE USABLE***.)
112  $(info WARNING: wasm-strip is part of the wabt package:)
113  $(info WARNING:    https://github.com/WebAssembly/wabt)
114  $(info WARNING: on Ubuntu-like systems it can be installed with:)
115  $(info WARNING:    sudo apt install wabt)
116  $(info WARNING: *******************************************************************)
117endif
118endif # 'make clean' check
119
120
121ifeq (,$(wasm-strip))
122  maybe-wasm-strip = echo "not wasm-stripping"
123else
124  maybe-wasm-strip = $(wasm-strip)
125endif
126
127ifeq (release,$(filter release,$(MAKECMDGOALS)))
128  ifeq (,$(wasm-strip))
129    $(error Cannot make release-quality binary because wasm-strip is not available. \
130            See notes in the warning above)
131  endif
132else
133  $(info Development build. Use '$(MAKE) release' for a smaller release build.)
134endif
135
136EXPORTED_FUNCTIONS.api.in := $(dir.api)/EXPORTED_FUNCTIONS.sqlite3-api \
137    $(dir.jacc)/jaccwabyt_test.exports
138
139EXPORTED_FUNCTIONS.api: $(EXPORTED_FUNCTIONS.api.in) $(MAKEFILE)
140	cat $(EXPORTED_FUNCTIONS.api.in) > $@
141CLEAN_FILES += EXPORTED_FUNCTIONS.api
142
143sqlite3-api.jses := $(dir.api)/sqlite3-api-prologue.js
144sqlite3-api.jses += $(dir.common)/whwasmutil.js
145sqlite3-api.jses += $(dir.jacc)/jaccwabyt.js
146sqlite3-api.jses += $(dir.api)/sqlite3-api-glue.js
147sqlite3-api.jses += $(dir.api)/sqlite3-api-oo1.js
148sqlite3-api.jses += $(dir.api)/sqlite3-api-worker1.js
149sqlite3-api.jses += $(dir.api)/sqlite3-api-opfs.js
150sqlite3-api.jses += $(dir.api)/sqlite3-api-cleanup.js
151
152sqlite3-api.js := sqlite3-api.js
153CLEAN_FILES += $(sqlite3-api.js)
154$(sqlite3-api.js): $(sqlite3-api.jses) $(MAKEFILE)
155	@echo "Making $@..."
156	@for i in $(sqlite3-api.jses); do \
157		echo "/* BEGIN FILE: $$i */"; \
158		cat $$i; \
159		echo "/* END FILE: $$i */"; \
160	done > $@
161
162post-js.js := post-js.js
163CLEAN_FILES += $(post-js.js)
164post-jses := \
165  $(dir.api)/post-js-header.js \
166  $(sqlite3-api.js) \
167  $(dir.api)/post-js-footer.js
168
169$(post-js.js): $(post-jses) $(MAKEFILE)
170	@echo "Making $@..."
171	@for i in $(post-jses); do \
172		echo "/* BEGIN FILE: $$i */"; \
173		cat $$i; \
174		echo "/* END FILE: $$i */"; \
175	done > $@
176
177
178########################################################################
179# emcc flags for .c/.o/.wasm/.js.
180emcc.flags =
181#emcc.flags += -v # _very_ loud but also informative about what it's doing
182# -g is needed to keep -O2 and higher from creating broken JS via
183# minification.
184emcc.flags += -g
185
186########################################################################
187# emcc flags for .c/.o.
188emcc.cflags :=
189emcc.cflags += -std=c99 -fPIC
190# -------------^^^^^^^^ we currently need c99 for WASM-specific sqlite3 APIs.
191emcc.cflags += -I. -I$(dir.top) # $(SQLITE_OPT)
192
193########################################################################
194# emcc flags specific to building the final .js/.wasm file...
195emcc.jsflags := -fPIC
196emcc.jsflags += --minify 0
197emcc.jsflags += --no-entry
198emcc.jsflags += -sMODULARIZE
199emcc.jsflags += -sSTRICT_JS
200emcc.jsflags += -sDYNAMIC_EXECUTION=0
201emcc.jsflags += -sNO_POLYFILL
202emcc.jsflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.api
203emcc.exportedRuntimeMethods := \
204    -sEXPORTED_RUNTIME_METHODS=FS,wasmMemory,allocateUTF8OnStack
205    # FS ==> stdio/POSIX I/O proxies
206    # wasmMemory ==> required by our code for use with -sIMPORTED_MEMORY
207    # allocateUTF8OnStack => for kvvfs internals
208emcc.jsflags += $(emcc.exportedRuntimeMethods)
209emcc.jsflags += -sUSE_CLOSURE_COMPILER=0
210emcc.jsflags += -sIMPORTED_MEMORY
211emcc.environment := -sENVIRONMENT=web
212emcc.jsflags += -sALLOW_MEMORY_GROWTH
213# emcc: warning: USE_PTHREADS + ALLOW_MEMORY_GROWTH may run non-wasm code
214#       slowly, see https://github.com/WebAssembly/design/issues/1271
215#       [-Wpthreads-mem-growth]
216emcc.jsflags += -sINITIAL_MEMORY=13107200
217#emcc.jsflags += -sINITIAL_MEMORY=64225280
218# ^^^^ 64MB is not enough for WASMFS/OPFS test runs using batch-runner.js
219emcc.jsflags += $(emcc.environment)
220#emcc.jsflags += -sTOTAL_STACK=4194304
221emcc.jsflags += -sEXPORT_NAME=sqlite3InitModule
222emcc.jsflags += -sGLOBAL_BASE=4096 # HYPOTHETICALLY keep func table indexes from overlapping w/ heap addr.
223emcc.jsflags += --post-js=$(post-js.js)
224#emcc.jsflags += -sSTRICT # fails due to missing __syscall_...()
225#emcc.jsflags += -sALLOW_UNIMPLEMENTED_SYSCALLS
226#emcc.jsflags += -sFILESYSTEM=0 # only for experimentation. sqlite3 needs the FS API
227#emcc.jsflags += -sABORTING_MALLOC
228emcc.jsflags += -sALLOW_TABLE_GROWTH
229emcc.jsflags += -Wno-limited-postlink-optimizations
230# ^^^^^ it likes to warn when we have "limited optimizations" via the -g3 flag.
231#emcc.jsflags += -sSTANDALONE_WASM # causes OOM errors, not sure why
232# https://lld.llvm.org/WebAssembly.html
233emcc.jsflags += -sERROR_ON_UNDEFINED_SYMBOLS=0
234emcc.jsflags += -sLLD_REPORT_UNDEFINED
235#emcc.jsflags += --allow-undefined
236emcc.jsflags += --import-undefined
237#emcc.jsflags += --unresolved-symbols=import-dynamic --experimental-pic
238#emcc.jsflags += --experimental-pic --unresolved-symbols=ingore-all --import-undefined
239#emcc.jsflags += --unresolved-symbols=ignore-all
240enable_bigint ?= 1
241ifneq (0,$(enable_bigint))
242emcc.jsflags += -sWASM_BIGINT
243endif
244emcc.jsflags += -sMEMORY64=0
245# ^^^^ MEMORY64=1 fails to load, erroring with:
246#  invalid memory limits flags 0x5
247#    (enable via --experimental-wasm-memory64)
248#
249# ^^^^ MEMORY64=2 builds and loads but dies when we do things like:
250#
251#  new Uint8Array(heapWrappers().HEAP8U.buffer, ptr, n)
252#
253# because ptr is now a BigInt, so is invalid for passing to arguments
254# which have strict must-be-a-Number requirements.
255########################################################################
256
257
258########################################################################
259# -sSINGLE_FILE:
260# https://github.com/emscripten-core/emscripten/blob/main/src/settings.js#L1704
261# -sSINGLE_FILE=1 would be really nice but we have to build with -g
262# for -O2 and higher to work (else minification breaks the code) and
263# cannot wasm-strip the binary before it gets encoded into the JS
264# file. The result is that the generated JS file is, because of the -g
265# debugging info, _huge_.
266########################################################################
267
268########################################################################
269# Maintenance reminder: the output .js and .wasm files of emcc must be
270# in _this_ dir, rather than a subdir, or else parts of the generated
271# code get confused and cannot load property (namely, the
272# sqlite3.worker.js generated in conjunction with -sWASMFS).
273sqlite3.js := sqlite3.js
274sqlite3.wasm := sqlite3.wasm
275sqlite3-wasm.o := $(dir.api)/sqlite3-wasm.o
276$(sqlite3-wasm.o): emcc.cflags += $(SQLITE_OPT)
277$(sqlite3-wasm.o): $(dir.top)/sqlite3.c
278sqlite3-wasm.c := $(dir.api)/sqlite3-wasm.c
279jaccwabyt_test.c := $(dir.jacc)/jaccwabyt_test.c
280# ^^^ FIXME (how?): jaccwabyt_test.c is only needed for the test apps,
281# so we don't really want to include it in release builds. However, we
282# want to test the release builds with those apps, so we cannot simply
283# elide that file in release builds. That component is critical to the
284# VFS bindings so needs to be tested along with the core APIs.
285define WASM_C_COMPILE
286$(1).o := $$(subst .c,.o,$(1))
287sqlite3.wasm.obj += $$($(1).o)
288$$($(1).o): $$(MAKEFILE) $(1)
289	$$(emcc.bin) $$(emcc_opt) $$(emcc.flags) $$(emcc.cflags) -c $(1) -o $$@
290CLEAN_FILES += $$($(1).o)
291endef
292$(foreach c,$(sqlite3-wasm.c) $(jaccwabyt_test.c),$(eval $(call WASM_C_COMPILE,$(c))))
293$(sqlite3.js): $(MAKEFILE) $(sqlite3.wasm.obj) \
294    EXPORTED_FUNCTIONS.api \
295    $(post-js.js)
296	@echo "Building $@ ..."
297	$(emcc.bin) -o $(sqlite3.js) $(emcc_opt) $(emcc.flags) $(emcc.jsflags) $(sqlite3.wasm.obj)
298	chmod -x $(sqlite3.wasm)
299	$(maybe-wasm-strip) $(sqlite3.wasm)
300	@ls -la $@ $(sqlite3.wasm)
301
302CLEAN_FILES += $(sqlite3.js) $(sqlite3.wasm)
303all: $(sqlite3.js)
304wasm: $(sqlite3.js)
305# End main Emscripten-based module build
306########################################################################
307
308########################################################################
309# batch-runner.js...
310dir.sql := sql
311speedtest1 := ../../speedtest1
312speedtest1.c := ../../test/speedtest1.c
313speedtest1.sql := $(dir.sql)/speedtest1.sql
314speedtest1.cliflags := --size 1 --big-transactions
315#  --------------------^^^^^^^^ small size is needed for batch-runner-kvvfs.html
316$(speedtest1):
317	$(MAKE) -C ../.. speedtest1
318$(speedtest1.sql): $(speedtest1) $(MAKEFILE)
319	$(speedtest1) $(speedtest1.cliflags) --script $@
320batch-runner.list: $(MAKEFILE) $(speedtest1.sql) $(dir.sql)/000-mandelbrot.sql
321	bash split-speedtest1-script.sh $(dir.sql)/speedtest1.sql
322	ls -1 $(dir.sql)/*.sql | grep -v speedtest1.sql | sort > $@
323clean-batch:
324	rm -f batch-runner.list $(dir.sql)/speedtest1*.sql
325# ^^^ we don't do this along with 'clean' because we clean/rebuild on
326# a regular basis with different -Ox flags and rebuilding the batch
327# pieces each time is an unnecessary time sink.
328batch: batch-runner.list
329all: batch
330# end batch-runner.js
331########################################################################
332# speedtest1.js...
333# speedtest1-common.eflags = emcc flags used by multiple builds of speedtest1
334# speedtest1.eflags = emcc flags used by main build of speedtest1
335speedtest1-common.eflags := -g $(emcc_opt)
336speedtest1.eflags :=
337speedtest1.eflags += -sENVIRONMENT=web
338speedtest1-common.eflags += -sINVOKE_RUN=0
339speedtest1-common.eflags += --no-entry
340#speedtest1-common.eflags += -flto
341speedtest1-common.eflags += -sABORTING_MALLOC
342speedtest1-common.eflags += -sINITIAL_MEMORY=128450560
343speedtest1-common.eflags += -sSTRICT_JS
344speedtest1-common.eflags += -sMODULARIZE
345speedtest1-common.eflags += -Wno-limited-postlink-optimizations
346speedtest1-common.eflags += -sEXPORTED_FUNCTIONS=@$(dir.wasm)/EXPORTED_FUNCTIONS.speedtest1
347speedtest1-common.eflags += $(emcc.exportedRuntimeMethods)
348speedtest1-common.eflags += -sALLOW_TABLE_GROWTH
349speedtest1-common.eflags += -sDYNAMIC_EXECUTION=0
350speedtest1-common.eflags += --minify 0
351speedtest1-common.eflags += -sEXPORT_NAME=sqlite3Speedtest1InitModule
352speedtest1-common.eflags += --post-js=$(post-js.js)
353ifneq (0,$(enable_bigint))
354speedtest1-common.eflags += -sWASM_BIGINT
355endif
356speedtest1.exit-runtime0 := -sEXIT_RUNTIME=0
357speedtest1.exit-runtime1 := -sEXIT_RUNTIME=1
358# Re -sEXIT_RUNTIME=1 vs 0: if it's 1 and speedtest1 crashes, we get
359# this error from emscripten:
360#
361# > native function `free` called after runtime exit (use
362# NO_EXIT_RUNTIME to keep it alive after main() exits))
363#
364# If it's 0 and it crashes, we get:
365#
366# > stdio streams had content in them that was not flushed. you should
367# set EXIT_RUNTIME to 1 (see the FAQ), or make sure to emit a newline
368# when you printf etc.
369#
370# and pending output is not flushed because it didn't end with a
371# newline (by design). The lesser of the two evils seems to be
372# -sEXIT_RUNTIME=1 but we need EXIT_RUNTIME=0 for the worker-based app
373# which runs speedtest1 multiple times.
374
375EXPORTED_FUNCTIONS.speedtest1: EXPORTED_FUNCTIONS.api
376	{ echo _wasm_main; cat EXPORTED_FUNCTIONS.api; } > $@
377CLEAN_FILES += EXPORTED_FUNCTIONS.speedtest1
378speedtest1.js := speedtest1.js
379speedtest1.wasm := $(subst .js,.wasm,$(speedtest1.js))
380speedtest1.cflags := \
381  -I. -I.. -I$(dir.top) \
382  -DSQLITE_SPEEDTEST1_WASM
383speedtest1.cs := $(speedtest1.c) $(sqlite3-wasm.c) $(jaccwabyt_test.c)
384$(speedtest1.js): emcc.cflags+=
385# speedtest1 notes re. sqlite3-wasm.o vs sqlite3-wasm.c: building against
386# the latter (predictably) results in a slightly faster binary, but we're
387# close enough to the target speed requirements that the 500ms makes a
388# difference.
389$(speedtest1.js): $(MAKEFILE) $(speedtest1.cs) $(post-js.js) \
390    EXPORTED_FUNCTIONS.speedtest1
391	@echo "Building $@ ..."
392	$(emcc.bin) \
393        $(speedtest1.eflags) $(speedtest1-common.eflags) $(speedtest1.cflags) \
394        $(SQLITE_OPT) \
395        $(speedtest1.exit-runtime0) \
396        '-DSQLITE_DEFAULT_UNIX_VFS="unix-none"' \
397        -o $@ $(speedtest1.cs) -lm
398	$(maybe-wasm-strip) $(speedtest1.wasm)
399	ls -la $@ $(speedtest1.wasm)
400
401speedtest1: $(speedtest1.js)
402all: speedtest1
403CLEAN_FILES += $(speedtest1.js) $(speedtest1.wasm)
404# end speedtest1.js
405########################################################################
406
407########################################################################
408# fiddle_remote is the remote destination for the fiddle app. It
409# must be a [user@]HOST:/path for rsync.
410# Note that the target "should probably" contain a symlink of
411# index.html -> fiddle.html.
412fiddle_remote ?=
413ifeq (,$(fiddle_remote))
414ifneq (,$(wildcard /home/stephan))
415  fiddle_remote = wh:www/wh/sqlite3/.
416else ifneq (,$(wildcard /home/drh))
417  #fiddle_remote = if appropriate, add that user@host:/path here
418endif
419endif
420$(fiddle_files): default
421push-fiddle: $(fiddle_files)
422	@if [ x = "x$(fiddle_remote)" ]; then \
423		echo "fiddle_remote must be a [user@]HOST:/path for rsync"; \
424		exit 1; \
425	fi
426	rsync -va fiddle/ $(fiddle_remote)
427# end fiddle remote push
428########################################################################
429
430########################################################################
431# Convenience rules to rebuild with various -Ox levels. Much
432# experimentation shows -O2 to be the clear winner in terms of speed.
433# Note that build times with anything higher than -O0 are somewhat
434# painful.
435.PHONY: o0 o1 o2 o3 os oz
436o0:
437	$(MAKE) clean; $(MAKE) -e emcc_opt=-O0
438o1:
439	$(MAKE) clean; $(MAKE) -e emcc_opt=-O1
440o2:
441	$(MAKE) clean; $(MAKE) -e emcc_opt=-O2
442o3:
443	$(MAKE) clean; $(MAKE) -e emcc_opt=-O3
444os:
445	$(MAKE) clean; $(MAKE) -e emcc_opt=-Os
446oz:
447	$(MAKE) clean; $(MAKE) -e emcc_opt=-Oz
448
449########################################################################
450# Sub-makes...
451
452########################################################################
453# Some platforms do not support the WASMFS build. Raspberry Pi OS is one
454# of them. As such platforms are discovered, add their (uname -m) name
455# to PLATFORMS_WITH_NO_WASMFS to exclude the wasmfs build parts.
456PLATFORMS_WITH_NO_WASMFS := aarch64 # add any others here
457THIS_ARCH := $(shell /usr/bin/uname -m)
458ifneq (,$(filter $(THIS_ARCH),$(PLATFORMS_WITH_NO_WASMFS)))
459$(info This platform does not support the WASMFS build.)
460else
461include wasmfs.make
462endif
463