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)
+
#
# 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
$(eval $(call gen-dwp,.mjs,-esm.html,$(c-pp.D.esm)))
all: demos
-
#
# $(sqlite3.ext.in) = API-related files which are standalone
# files, not part of the amalgamation. This list holds
$(dir.dout)/sqlite3-opfs-async-proxy.js: $(dir.api)/sqlite3-opfs-async-proxy.js
@$(call b.call.cp,@,$<,$@)
-
-#
-# Copy "external" files - those which are not part of the amalgamation
-# - to $(dir.dout).
-#
-#define gen-extjs
-#sqlite3.ext.js += $(dir.dout)/$(notdir $(1))
-#$(dir.dout)/$(notdir $(1)): $(1)
-# @$(call b.cp,@,$(1),$@)
-#endef
-#sqlite3.ext.js =
-#$(foreach X,$(sqlite3.ext.js.in),$(eval $(call gen-extjs,$(X))))
-
#
# Add a dep of $(sqlite3.ext.js) on every build's JS file.
# The primary purpose of this is to force them to be copied early
#
$(foreach B,$(b.names),$(eval $(out.$(B).js): $(sqlite3.ext.js)))
-########################################################################
-# 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
+#
+# 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.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 += -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
@echo "Making $@ ..."
@$(call b.call.mkdir@)
@{ echo _wasm_main; cat $(EXPORTED_FUNCTIONS.api.core); } > $@
-emcc.flags.speedtest1 = $(cflags.common) -DSQLITE_SPEEDTEST1_WASM
-speedtest1.cfiles = $(speedtest1.c) $(sqlite3-wasm.c)
-$(out.speedtest1.js): $(MAKEFILE) $(speedtest1.cfiles) \
- $(pre-post.speedtest1.deps) \
- $(EXPORTED_FUNCTIONS.speedtest1)
- @$(call b.echo,speedtest1,$(emo.compile) building ...)
- @$(call b.call.mkdir@)
- $(b.cmd@)$(bin.emcc) \
- $(emcc.speedtest1) \
- $(emcc.speedtest1.common) \
- $(emcc.flags.speedtest1) $(pre-post.speedtest1.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
- @$(call b.call.wasm-strip,speedtest1)
- @$(call b.do.wasm-opt,speedtest1)
- @$(call b.call.strip-emcc-js-cruft,$(logtag.speedtest1))
- @ls -la $@ $(speedtest1.wasm)
-
speedtest1: $(out.speedtest1.js)
# end speedtest1.js
########################################################################
$(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: tester
+all: tester1
########################################################################
# Convenience rules to rebuild with various -Ox levels. Much
+++ /dev/null
-<!doctype html>
-<html lang="en-us">
- <head>
- <meta charset="utf-8">
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
- <link rel="stylesheet" href="common/testing.css"/>
- <title>sqlite3-api batch SQL runner for the SAHPool VFS</title>
- </head>
- <body>
- <header id='titlebar'><span>sqlite3-api batch SQL runner for the SAHPool VFS</span></header>
- <div>
- <span class='input-wrapper'>
- <input type='checkbox' class='disable-during-eval' id='cb-reverse-log-order' checked></input>
- <label for='cb-reverse-log-order' id='lbl-reverse-log-order'>Reverse log order</label>
- </span>
- </div>
- <div id='test-output' class='reverse'></div>
- <script>
- (function(){
- const E = (sel)=>document.querySelector(sel);
-
- const eOut = E('#test-output');
- const log2 = function(cssClass,...args){
- let ln;
- if(1 || cssClass){
- ln = document.createElement('div');
- if(cssClass) ln.classList.add(cssClass);
- ln.append(document.createTextNode(args.join(' ')));
- }else{
- // This doesn't work with the "reverse order" option!
- ln = document.createTextNode(args.join(' ')+'\n');
- }
- eOut.append(ln);
- };
- const log = (...args)=>{
- //console.log(...args);
- log2('', ...args);
- };
- const logErr = function(...args){
- console.error(...args);
- log2('error', ...args);
- };
- const logWarn = function(...args){
- console.warn(...args);
- log2('warning', ...args);
- };
-
- const cbReverseLog = E('#cb-reverse-log-order');
- const lblReverseLog = E('#lbl-reverse-log-order');
- if(cbReverseLog.checked){
- lblReverseLog.classList.add('warning');
- eOut.classList.add('reverse');
- }
- cbReverseLog.addEventListener('change', function(){
- if(this.checked){
- eOut.classList.add('reverse');
- lblReverseLog.classList.add('warning');
- }else{
- eOut.classList.remove('reverse');
- lblReverseLog.classList.remove('warning');
- }
- }, false);
-
- const w = new Worker('batch-runner-sahpool.js?sqlite3.dir=jswasm');
- w.onmessage = function(msg){
- msg = msg.data;
- switch(msg.type){
- case 'stdout': log(...msg.data); break;
- case 'warn': logWarn(...msg.data); break;
- case 'error': logErr(...msg.data); break;
- default:
- logErr("Unhandled worker message type:",msg);
- break;
- }
- };
- })();
- </script>
- <style>
- #test-output {
- white-space: break-spaces;
- overflow: auto;
- }
- </style>
- </body>
-</html>
+++ /dev/null
-/*
- 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_);
-});
+++ /dev/null
-<!doctype html>
-<html lang="en-us">
- <head>
- <meta charset="utf-8">
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
- <link rel="stylesheet" href="common/emscripten.css"/>
- <link rel="stylesheet" href="common/testing.css"/>
- <title>sqlite3-api batch SQL runner</title>
- </head>
- <body>
- <header id='titlebar'><span>sqlite3-api batch SQL runner</span></header>
- <!-- emscripten bits -->
- <figure id="module-spinner">
- <div class="spinner"></div>
- <div class='center'><strong>Initializing app...</strong></div>
- <div class='center'>
- 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.
- </div>
- </figure>
- <div class="emscripten" id="module-status">Downloading...</div>
- <div class="emscripten">
- <progress value="0" max="100" id="module-progress" hidden='1'></progress>
- </div><!-- /emscripten bits -->
- <p>
- This page is for batch-running extracts from the output
- of <tt>speedtest1 --script</tt>, as well as other standalone SQL
- scripts.
- </p>
- <p id='warn-list' class='warning'>ACHTUNG: this file requires a generated input list
- file. Run "make batch" from this directory to generate it.
- </p>
- <p id='warn-opfs' class='warning hidden'>WARNING: if the WASMFS/OPFS layer crashes, this page may
- become completely unresponsive and need to be closed and reloaded to recover.
- </p>
- <p id='warn-websql' class='warning hidden'>WARNING: WebSQL's limited API requires that
- this app split up SQL batches into separate statements for execution. That will
- only work so long as semicolon characters are <em>only</em> used to terminate
- SQL statements, and not used within string literals or the like.
- </p>
- <hr>
- <fieldset id='toolbar'>
- <div>
- <select class='disable-during-eval' id='sql-select'>
- <option disabled selected>Populated via script code</option>
- </select>
- <button class='disable-during-eval' id='sql-run'>Run selected SQL</button>
- <button class='disable-during-eval' id='sql-run-next'>Run next...</button>
- <button class='disable-during-eval' id='sql-run-remaining'>Run all remaining...</button>
- <button class='disable-during-eval' id='export-metrics' disabled>Export metrics (WIP)<br>(broken by refactoring)</button>
- <button class='disable-during-eval' id='db-reset'>Reset db</button>
- <button id='output-clear'>Clear output</button>
- <span class='input-wrapper flex-col'>
- <label for='select-impl'>Storage impl:</label>
- <select id='select-impl'>
- <option value='virtualfs'>Virtual filesystem</option>
- <option value='memdb'>:memory:</option>
- <option value='wasmfs-opfs'>WASMFS OPFS</option>
- <option value='websql'>WebSQL</option>
- </select>
- </span>
- </fieldset>
- </div>
- <hr>
- <span class='input-wrapper'>
- <input type='checkbox' class='disable-during-eval' id='cb-reverse-log-order' checked></input>
- <label for='cb-reverse-log-order'>Reverse log order (newest first)</label>
- </span>
- <div id='test-output'></div>
- <script src="jswasm/sqlite3.js"></script>
- <script src="common/SqliteTestUtil.js"></script>
- <script src="batch-runner.js"></script>
- <style>
- .flex-col {
- display: flex;
- flex-direction: column;
- }
- #toolbar > div {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- }
- #toolbar > div > * {
- margin: 0.25em;
- }
- </style>
- </body>
-</html>
+++ /dev/null
-/*
- 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);
- });
-})();
*/
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
E(vanilla) E(vanilla64) \
E(esm) E(esm64) \
E(bundler) E(bundler64) \
+ E(speedtest1) \
E(node) E(node64) \
E(wasmfs)
.zDotWasm = 0,
.zCmppD = 0,
.zEmcc = 0,
+ .zEmccExtra = 0,
.zEnv = "web,worker",
+ .zDeps = 0,
.zIfCond = 0,
.flags = CP_ALL
},
.zBaseName = "sqlite3-64bit",
.zDotWasm = 0,
.zCmppD = 0,
- .zEmcc = "-sMEMORY64=1 -sWASM_BIGINT=1",
+ .zEmcc = 0,
+ .zEmccExtra = "-sMEMORY64=1 -sWASM_BIGINT=1",
.zEnv = 0,
+ .zDeps = 0,
.zIfCond = 0,
.flags = CP_ALL | F_64BIT
},
.zDotWasm = 0,
.zCmppD = "-Dtarget=es6-module",
.zEmcc = 0,
+ .zEmccExtra = 0,
.zEnv = 0,
+ .zDeps = 0,
.zIfCond = 0,
.flags = CP_JS | F_ESM
},
.zBaseName = "sqlite3-64bit",
.zDotWasm = 0,
.zCmppD = "-Dtarget=es6-module",
- .zEmcc = "-sMEMORY64=1 -sWASM_BIGINT=1",
+ .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.
.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
.zBaseName = "sqlite3-bundler-friendlyu",
.zDotWasm = "sqlite3-64bit",
.zCmppD = "$(c-pp.D.bundler)",
- .zEmcc = "-sMEMORY64=1",
+ .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
},
.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
},
.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
},
.zBaseName = "sqlite3-wasmfs",
.zDotWasm = 0,
.zCmppD = "$(c-pp.D.bundler)",
- .zEmcc = "-sEXPORT_ES6 -sUSE_ES6_IMPORT_META",
+ .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
}
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",
//"$(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);
- 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);
+
+ if( pB->zDeps ){
+ pf("deps.%s += %s\n", zBuildName, pB->zDeps);
+ }
+
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);
- pf("emcc.flags.%s ?= %s\n", zBuildName, pB->zEmcc ? pB->zEmcc : "");
+ if( pB->zEmccExtra ){
+ pf("emcc.flags.%s = %s\n", zBuildName, pB->zEmccExtra);
+ }
emit_api_js(zBuildName, pB->zCmppD);
if( pB->flags & F_64BIT ){
{ /* build it... */
pf(zBanner
"$(out.%s.js): $(MAKEFILE_LIST) $(sqlite3-wasm.cfiles)"
- " $(EXPORTED_FUNCTIONS.api)"
+ " $(EXPORTED_FUNCTIONS.api) $(deps.%s)"
" $(bin.mkwb) $(pre-post.%s.deps)"
"\n",
- zBuildName, zBuildName);
+ zBuildName, zBuildName, zBuildName);
emit_compile_start(zBuildName);
pf("\t@echo '$(logtag.%s) $(emo.fire)$(emo.fire)$(emo.fire): "
"unsupported build. Use at your own risk.'\n", zBuildName);
}
- pf("\t$(b.cmd@)$(call b.do.emcc,%s)\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.cfiles)\n",
+ zBuildName, zBuildName, zBuildName, zBuildName
+ );
+ }
+ }
{ /* Post-compilation transformations and copying to
$(dir.dout)... */
/*
** $(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->zEmcc.
+ ** 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
}
}
+#if 0
static void mk_speedtest1(void){
char const *zBuildName = "speedtest1";
pf(zBanner "# Begin build %s\n", zBuildName);
"$(c-pp.D.sqlite3-bundler-friendly) -Dwasmfs",
"speetest1-wasmfs.wasm");
#endif
-
}
+#endif
-int main(void){
+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();
+
+ if(argc>1){
+ /*
+ ** Only emit the rules for the given list of builds, sans
+ ** prologue. Only for debugging, not 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) {
+ fprintf(stderr,"Unkown build name: %s\n", zArg);
+ rc = 1;
+ break;
+ }
+#undef E
+ }
+ }else{
+ /*
+ ** Emit the whole shebang...
+ */
+ mk_prologue();
#define E(N) mk_lib_mode(# N, &oBuildDefs.N);
- BuildDefs_map(E)
+ BuildDefs_map(E)
#undef E
- 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"
- );
- mk_fiddle();
- mk_speedtest1();
+#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;
}
-C Wasm\sbuild\sdeps\sfixes.
-D 2025-09-25T00:53:30.968
+C Consolidate\sthe\sspeedtest1\swasm\sbuild\sinto\sthe\snew\smkwasmbuilds.c\smodel.\sRemove\sthe\slong-unused\sbatch-runner\sJS\stools.
+D 2025-09-25T02:02:48.595
F .fossil-settings/binary-glob 61195414528fb3ea9693577e1980230d78a1f8b0a54c78cf1b9b24d0a409ed6a x
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
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 3e4798dd38252e361b4baab2cba60ef329621a3d1955db0c53b004f3a37e8630
+F ext/wasm/GNUmakefile a5abc4ad90d9426e80a24778fedfb5947d827e22f53ae4cff99ccc8e52bcfdd7
F ext/wasm/README-dist.txt f01081a850ce38a56706af6b481e3a7878e24e42b314cfcd4b129f0f8427066a
F ext/wasm/README.md 66ace67ae98a45e4116f2ca5425b716887bcee4d64febee804ff6398e1ae9ec7
F ext/wasm/SQLTester/GNUmakefile e0794f676d55819951bbfae45cc5e8d7818dc460492dc317ce7f0d2eca15caff
F ext/wasm/api/sqlite3-wasm.c ff2dc011e17b06186b8b35e408626d7ace69a362b92c197a34d78bef25c7105a
F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js 8fb6adfbae6270344f43f2652e63780df3f86521755bde8f92cf6b809ba7891d
F ext/wasm/api/sqlite3-worker1.c-pp.js 69483df1df2d0988e708390f7b1feda769c16e9e9efd4683557f8e7197099cc0
-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 7692739ac435120c37b9de993f152c90e5dbf6a340ca6331de81d7b8b06b5307
F ext/wasm/common/SqliteTestUtil.js 7adaeffef757d8708418dc9190f72df22367b531831775804b31598b44f6aa51
F ext/wasm/common/emscripten.css 11bd104b6c0d597c67d40cc8ecc0a60dae2b965151e3b6a37fa5708bac3acd15
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 238cc9a9888dec4a9b48e9b608982cc5b7f4b307438d9266e0d4fcca939d697d
+F ext/wasm/mkwasmbuilds.c 29e55190b6bdb2b9a52966e34144c3af3948331fd65a713910c9c7231d75f4b6
F ext/wasm/module-symbols.html dc476b403369b26a1a23773e13b80f41b9a49f0825e81435fe3600a7cfbbe337
F ext/wasm/scratchpad-wasmfs.html a3d7388f3c4b263676b58b526846e9d02dfcb4014ff29d3a5040935286af5b96
F ext/wasm/scratchpad-wasmfs.mjs 66034b9256b218de59248aad796760a1584c1dd842231505895eff00dbd57c63
F tool/warnings-clang.sh bbf6a1e685e534c92ec2bfba5b1745f34fb6f0bc2a362850723a9ee87c1b31a7
F tool/warnings.sh 1ad0169b022b280bcaaf94a7fa231591be96b514230ab5c98fbf15cd7df842dd
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 18beeb26bfa48f806866e0e330024535adabcf668071abc4b6251eb39de2701d
-R b0e1a74c5137f4633645ae07790f221a
+P fd0c649047f4e444ea682138a3555a973839627150e1124bb9d9ced17880a9e5
+R 4644e40acad7ec2826869dd780c99c6c
U stephan
-Z 436e3963d5b7b3aa761dea055937d61e
+Z 003460e9650f2a4bf650b04d84b0f4a7
# Remove this line to create a well-formed Fossil manifest.
-fd0c649047f4e444ea682138a3555a973839627150e1124bb9d9ced17880a9e5
+980c033c05bf37c0e8f5e82486ee99ba1294cc9c9e2087aaf83b64e5d0118b5f