libSQL SQLite 的 WASM/JS 构建指南:从 Emscripten 环境搭建到浏览器部署
【免费下载链接】libsqllibSQL is a fork of SQLite that is both Open Source, and Open Contributions.项目地址: https://gitcode.com/GitHub_Trending/li/libsql
本指南以 libSQL 仓库中 libsql-sqlite3/ext/wasm/README.md 为主体,系统讲解如何在本地编译 SQLite 的 WebAssembly 版本、通过 HTTP 服务器在浏览器中运行其测试与演示页面,并借助 SSH 隧道在远程开发机上完成联调。读完本文,你将掌握 Emscripten SDK 的安装与激活、make构建流程、althttpd的正确启动参数,以及SharedArrayBuffer/OPFS 等高级特性对响应头和网络环境的硬性要求。
一、目录定位:sqlite3 构建体系中的 WASM 部件
libsql-sqlite3/ext/wasm/目录承载了 sqlite3 构建体系中的 Web Assembly 部分,是 SQLite 官方 JS/WASM 发行版的同源实现。这里不仅能产出可供浏览器直接加载的sqlite3.js、sqlite3.mjs与sqlite3.wasm三件套,还附带一整套测试页、演示应用(demo)和基准测试工具(speedtest1),以及一个基于 sqlite3 shell 的 WASM 构建的在线 SQL 编辑器 Fiddle(见 fiddle/index.html,页面标题即为 "libSQL Fiddle")。
构建该目录必须依赖 Emscripten,并且要求构建环境已经针对 Emscripten 完成初始化配置,因此本文第一件事就是完成 Emscripten SDK 的安装与环境激活。
二、第一步:安装 Emscripten SDK
Emscripten 的安装过程只需一次性完成。对于 Linux 环境,官方推荐的流程是先克隆emsdk仓库,再通过其自带的安装脚本下载并激活最新版 SDK:
# 安装 git(如尚未安装) $ sudo apt install git # 克隆 emscripten 仓库 $ git clone https://github.com/emscripten-core/emsdk.git $ cd emsdk # 下载并安装最新的 SDK 工具 $ ./emsdk install latest # 将 "latest" SDK 设为当前用户的活动版本 $ ./emsdk activate latest其中install负责下载工具链,activate负责写入版本切换信息,两者配合即完成全局安装。上述步骤只需要执行一次。
升级 SDK
当需要更新 Emscripten 时,在emsdk目录内重复拉取与安装动作即可:
$ git pull $ ./emsdk install latest $ ./emsdk activate latest三、第二步:每个 Shell 会话的环境激活
与一次性安装不同,每一个需要使用emcc编译器的终端会话都必须执行一次环境激活脚本,把PATH及其它环境变量注入当前终端:
# 在当前终端激活 PATH 及其它环境变量: $ source ./emsdk_env.sh $ which emcc /path/to/emsdk/upstream/emscripten/emcc如果希望省去每次手动 source 的麻烦,也可以把上述source语句追加到登录 shell 的资源文件中(~/.bashrc或等效文件)。which emcc的输出路径中包含upstream/emscripten/emcc即表示激活成功。
构建系统对emcc的依赖可以在 GNUmakefile 中看到:makefile 会优先用which emcc定位编译器,找不到时再回退到$EMSDK_HOME/upstream/emscripten/emcc,两者皆无则直接以Cannot find emcc in path.报错终止。因此环境未激活时执行 make,首先就会在这里失败。
四、第三步:构建 WASM 产物
env脚本必须在编译应用前完成 source。构建有两种等价方式:
方式一:在 sqlite3 构建树的顶层执行:
$ make fiddle方式二:直接进入 ext/wasm 目录执行:
$ cd ext/wasm $ make两种方式都会生成一批测试与演示应用所需的目标文件,这些页面统一通过index.html索引访问。
构建目标与产物形态(源码级解读)
从 GNUmakefile 的头部注释可以看到,这个 makefile 不是 canonical 构建流程的一部分,而是 sqlite 项目维护 JS/WASM 组件时使用的开发构建,目标包括:
| 目标 | 说明 |
|---|---|
default/all | 开发模式(dev mode)全量构建,默认优化级别为-O0 |
o0o1o2o3osoz | 以目标名对应的-Ox级别执行完整清理重建,所有组件都需要重建才能获得期望的优化级别 |
quick/q | 只为测试构建核心产物(sqlite3.js/wasm、tester1),加快开发期周转 |
dist | 产出面向终端用户的发行物,可用dist.build=oX指定优化级别 |
snapshot | 与dist类似,但 zip 文件名明确标注为预发布/快照构建 |
clean | 清理 |
关键的产出物被写入jswasm/目录(对应 makefile 中的dir.dout),按构建模式分为sqlite3.js(vanilla JS)、sqlite3.mjs(ES6 Module)、*-bundler-friendly.*(面向 node.js 生态打包工具的变体)以及sqlite3-node.mjs(node 专用)等。从 makefile 的JS_BUILD_MODES := vanilla esm bunder-friendly node(GNUmakefile)可以看出官方维护四种构建风格,其中 node 模式不提供 OPFS 持久化存储。
关于优化级别,makefile 注释给出了一条重要的实践经验(GNUmakefile):-O3、-Os、-Oz都会混淆 WASM 导出符号名,从而破坏模块可用性;解决办法是配合-g3编译、再用 wabt 工具包中的wasm-strip剥离调试信息。libSQL 在此基础上还额外引入了wasm-opt(binaryen)对生成的.wasm做-Oz后处理瘦身(GNUmakefile),这是相对上游 SQLite 的本地增强。
自定义 C 扩展代码
构建系统支持通过sqlite3_wasm_extra_init.c注入自定义 C 代码:只要该文件存在于 wasm 构建目录,make 就会把它编入sqlite3.wasm并定义SQLITE_EXTRA_INIT=sqlite3_wasm_extra_init。该函数签名必须为:
int sqlite3_wasm_extra_init(const char *)sqlite3 库会在sqlite3_initialize()过程中以NULL参数调用它一次,返回值非 0 会导致库初始化失败。仓库中的 example_extra_init.c 给出了最小实现:仅向 stderr 打印一条日志并返回 0。文件路径也可用make sqlite3_wasm_extra_init.c=my_custom_stuff.c覆盖。
导出函数清单与编译宏
emcc的-sEXPORTED_FUNCTIONS参数指向由 makefile 拼接生成的导出清单,核心部分来自 api/EXPORTED_FUNCTIONS.sqlite3-core,其中列出了_malloc、_free、_sqlite3_open_v2、_sqlite3_bind_text、_sqlite3_exec等数百个 C 层符号,full-featured 构建还会追加EXPORTED_FUNCTIONS.sqlite3-extras,SEE 构建追加EXPORTED_FUNCTIONS.sqlite3-see。
同时 makefile 定义了一组SQLITE_OPT.common编译宏(GNUmakefile),例如SQLITE_THREADSAFE=0、SQLITE_ENABLE_MATH_FUNCTIONS、SQLITE_USE_URI=1,并强制SQLITE_OMIT_DEPRECATED、SQLITE_OMIT_UTF16、SQLITE_OMIT_LOAD_EXTENSION、SQLITE_OMIT_SHARED_CACHE(这些 OMIT 被硬编码在 api/sqlite3-wasm.c 中,无法通过构建参数移除)。full-featured 构建还开启 FTS5、RTREE、SESSION、PREUPDATE_HOOK 等扩展;若以barebones=1构建,则会切换到精简模式,换来更小的.wasm体积。初始内存可通过emcc.INITIAL_MEMORY在 8/16/32/64/96/128 MB 档位间选择,默认 16 MB,并启用了ALLOW_MEMORY_GROWTH。
JS 胶水文件的拼装原理
最终sqlite3.js并非单一源文件,而是由 api/README.md 描述的多个文件按固定顺序拼接而成:sqlite3-api-prologue.js(API 对象引导)→common/whwasmutil.js(半第三方 WASM 工具库,替代大量 Emscripten 胶水)→jaccwabyt/jaccwabyt.js(JS 与 C 结构体的双向绑定层)→sqlite3-api-glue.c-pp.js→ 版本信息 →sqlite3-api-oo1.c-pp.js(OO API #1 高层对象封装)→sqlite3-api-worker1.c-pp.js(Worker 线程 API)→ VFS/VTab 辅助 → 两个 OPFS VFS 实现(sqlite3-vfs-opfs.c-pp.js与sqlite3-vfs-opfs-sahpool.c-pp.js)→sqlite3-api-cleanup.js(清理全局符号并触发引导)。扩展名为.c-pp.js的文件需经仓库自带的 c-pp.c 预处理器处理,以在同一份源码中为 vanilla JS、ESM、node 三种目标切换代码段。
五、第四步:通过 HTTP 服务器访问演示页面
构建完成后,由于XMLHttpRequest 的安全限制,WASM 内容无法在浏览器直接以file://URL 打开 HTML 文件时加载,因此必须经由 HTTP 服务器提供。官方推荐的服务器是 althttpd:
$ cd ext/wasm $ althttpd --enable-sab --max-age 1 --page index.html该命令会打开系统浏览器并运行索引页,从索引页可以访问全部测试与演示应用。index.html(libsql-sqlite3/ext/wasm/index.html)中列出了完整清单,主要包括:
- 核心测试:
tester1(主线程单元测试与回归测试)、tester1-worker(Worker 中运行同套测试)、tester1-esm(ES6 模块方式加载)、tester1-worker?esm(Worker Module 加载,注意并非所有浏览器都允许在 Worker 线程中加载模块); - 高层演示:
fiddle(sqlite3 shell 的 WASM 前端)、demo-123(主线程最小示例)、demo-123-worker(Worker 线程版)、demo-jsstorage(用 kvvfs 把数据库持久化到localStorage/sessionStorage)、demo-worker1与demo-worker1-promiser(Worker1 API 的 Promise 封装演示); - 基准测试:
speedtest1(sqlite3 官方基准工具的主线程版,可用?vfs=kvvfs、?vfs=opfs、?vfs=opfs-sahpool切换 VFS); - 其它:
module-symbols(模块导出符号概览)、test-opfs-vfs(基于 SharedArrayBuffer 与 Atomics 的 OPFS VFS 代理测试)、tests/opfs/concurrency/index.html(多 Worker 并发测试)。
althttpd 版本要求与 COOP/COEP 响应头
使用 althttpd 提供服务时,必须使用 2022-09-26 或更新的版本,因为只有新版本才识别--enable-sab标志。该标志让 althttpd 在响应中额外输出两个 HTTP 响应头,用于启用 JavaScript 的SharedArrayBuffer与AtomicsAPI:
Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp这两个头是 OPFS 相关功能的前置条件。如 README-dist.txt 所述,核心库在没有这两个头时也能运行,但 OPFS 存储等特性将不可用。index.html的警告清单也补充说明:OPFS 相关页面要求 2023 年 2 月之后发布的浏览器(Chromium 系约 v102 起部分可用),且服务器必须输出 COOP/COEP 头。
六、在远程机器上测试(SSH 场景)
以下是开发者在 2023-07-19 验证过的远程联调流程,适用于通过 SSH 访问的远程开发机:
- 远程端:安装 git、emsdk 与 althttpd(同样要求 2022-09-26 之后的版本);
- 远程端:安装 SQLite 源码树,进入
ext/wasm目录; - 远程端:执行
make构建 WASM; - 远程端:启动服务:
$ althttpd --enable-sab --port 8080 --popup- 本地端:建立 SSH 端口转发隧道:
$ ssh -L 8180:localhost:8080 remote- 本地端:在浏览器中访问
http://localhost:8180/index.html。
为什么必须用 SSH 隧道
SharedArrayBuffer的启用条件相当严格:浏览器要求响应中同时携带两条额外的 Cross-Origin 头,并且请求必须来自localhost(或经由 SSL 连接)。由于本场景中 Web 服务器与浏览器不在同一台机器上,localhost条件无法直接满足,因此必须借助 SSH 把远程端口隧道到本地localhost,使浏览器看到的请求源变为localhost,从而满足 SAB 的启用前提。这正是第 4 步使用--popup(弹出提示)、第 5 步将远端 8080 端口映射为本地 8180 端口的原因。
七、常见问题速查
| 现象 | 原因与解法 |
|---|---|
make报Cannot find emcc in path. | 当前 Shell 未执行source ./emsdk_env.sh,重新激活环境即可 |
| 双击 HTML 打开页面但 WASM 无法加载 | 浏览器禁止从file://URL 加载 WASM,必须通过 HTTP 服务器访问 |
| OPFS 相关页面/测试不可用 | 服务器未输出 COOP/COEP 头;确认 althttpd ≥ 2022-09-26 并使用--enable-sab |
| 远程访问时 SAB 报错 | 请求源不是localhost且非 SSL,使用ssh -L隧道转发到本地 |
| 优化构建后导出符号全部损坏 | -O2及以上会混淆符号名,需配合-g3+wasm-strip(wabt 包,Ubuntu 可用sudo apt install wabt),或在发行构建前安装wasm-opt |
八、进一步阅读
- ext/wasm/README.md:本文的原始依据文档;
- ext/wasm/README-dist.txt:WASM/JS 发行包交付物清单(
jswasm/sqlite3.js、sqlite3.mjs、sqlite3.wasm及 bundler-friendly 变体); - ext/wasm/GNUmakefile:完整构建规则、优化级别、编译宏与目标说明;
- ext/wasm/api/README.md:
sqlite3-*.js系列文件的分层结构与拼装顺序; - ext/wasm/api/EXPORTED_FUNCTIONS.sqlite3-core:WASM 导出的 C API 符号清单;
- ext/wasm/index.html:全部测试与演示页面的入口索引。
【免费下载链接】libsqllibSQL is a fork of SQLite that is both Open Source, and Open Contributions.项目地址: https://gitcode.com/GitHub_Trending/li/libsql
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考