Appearance
构建与部署 — 练习
练习 1:分析 Makefile 构建目标链
阅读 Makefile 中的 WASM 相关目标,回答以下问题:
make wasm的完整依赖链是什么?从敲入命令到生成pokeemerald.wasm,中间经历了哪些步骤?$(WASM_C_OBJS)的编译规则中,为什么需要先经过$(PREPROC)预处理器?预处理器的输出为什么通过管道传给 Clang?wasm-ld的--export-all标志会将所有全局符号导出。这对最终 WASM 文件大小有什么影响?为什么项目仍然使用它?
参考答案
完整依赖链:
make wasm └─ generated (生成预处理文件和资源索引) └─ wasm-assets (运行 generate_wasm_assets.py) └─ $(WASM_C_OBJS) (编译所有 .c 文件为 .wasm.o) └─ $(WASM_DATA_OBJS) (转换汇编数据文件为 .wasm.o) └─ $(WASM) (wasm-ld 链接所有 .o → pokeemerald.wasm)具体步骤:预处理(生成头文件、资源索引)→ 资源扫描(Python 脚本)→ C 编译(Clang)→ 汇编数据转换(Python 脚本)→ 链接(wasm-ld)
预处理器的作用:
$(PREPROC)是 pret/pokeemerald 的自定义预处理器,处理INCBIN/INCGFX等 GBA 特有宏- 它将 C 源码中的资源引用(如
INCBIN("graphics/pokemon/bulbasaur/anim_front.4bpp"))展开为 WASM 可识别的数据声明 - 通过管道传递(
-E $< | $(PREPROC) | $(WASM_CC) -x c)是因为预处理器的输出需要直接喂给 Clang 编译,不需要中间文件
--export-all的影响:- 导出所有全局符号会增加 WASM 文件中的导出表大小,但由于符号名只是元数据,对文件大小的实际影响较小(通常增加几十 KB)
- 项目使用它的原因是为了支持 自动化测试和调试:
app.js中的automationApi()需要访问gSaveBlock1Ptr、gPlayerAvatar、gObjectEvents等全局变量,以及VarGet()等函数 wasm_replay.mjs也依赖导出的符号进行自动回放测试
练习 2:修改部署配置
假设你需要将 pokeemerald-wasm 部署到一个新的域名 emerald.example.com,并且希望添加一个 /health 端点返回服务状态。
- 需要修改
wrangler.toml的哪些配置? - 如果 WASM 文件超过了 Cloudflare Workers 的免费套餐大小限制(25MB),你会如何优化?
- 为什么
build_wrangler_site.mjs在每次打包前要rm -rf dist/cloudflare?如果去掉这行会有什么问题?
参考答案
修改
wrangler.toml:toml[[routes]] pattern = "emerald.example.com" custom_domain = true需要将
pattern改为新域名,并确保域名 DNS 指向 Cloudflare。添加/health端点需要使用 Workers 的路由功能,但由于当前项目仅使用静态资源托管([assets]),不执行 Worker 代码,需要额外添加一个 Worker 脚本来处理 API 路由。WASM 文件大小优化策略:
- 使用
wasm-opt(Binaryen 工具包)进行后优化:wasm-opt -Oz pokeemerald.wasm -o pokeemerald.opt.wasm - 使用
wasm-snip移除未使用的函数 - 在链接时使用
-strip-all或-strip-debug去除调试信息 - 启用 WASM 压缩(如 Brotli/Gzip),Cloudflare 默认支持自动压缩
- 考虑使用
wasm-split进行延迟加载
- 使用
rm -rf dist/cloudflare的作用:- 确保每次打包都是干净的,不会残留上次的旧文件
- 如果去掉这行,当文件名变更时(如删除了某个资源),旧文件会留在 dist 目录中,可能被意外部署
- 对于 Cloudflare Workers 的静态资源部署,多余的文件会浪费部署包的空间
练习 3:设计 CI/CD 流水线
假设你要为 pokeemerald-wasm 设计一个 GitHub Actions CI/CD 流水线,请回答以下问题:
- 构建阶段需要安装哪些工具?列出完整的依赖列表
- 如何缓存构建产物以加速 CI?哪些目录适合缓存?
- 部署到 Cloudflare Workers 的 GitHub Actions 步骤应该怎么写?
参考答案
枇建依赖:
- Clang(支持 wasm32 目标):
apt install clang或使用 LLVM 预编译包 - wasm-ld:通常随 LLVM/Clang 安装,或来自 Rust 工具链(
~/.rustup/toolchains/*/gcc-ld/wasm-ld) - Node.js:运行
server.mjs和build_wrangler_site.mjs - uv(Python 包管理器):运行
generate_wasm_assets.py和wasm_asm_data.py - GNU Make:执行 Makefile
- Wrangler CLI:
npm install -g wrangler,用于部署
- Clang(支持 wasm32 目标):
缓存策略:
build/wasm/:缓存编译中间文件(.wasm.o),只有源文件变更时才重新编译node_modules/:如果添加 npm 依赖~/.cache/uv/:Python 依赖缓存- Clang 编译缓存:如果启用了
ccache - 缓存 key 应包含 Makefile 的 hash 和源文件的 hash,确保依赖变更时缓存失效
部署步骤示例:
yaml- name: Deploy to Cloudflare Workers env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} run: | npm install -g wrangler make wrangler-site wrangler deploy需要在 GitHub 仓库的 Secrets 中配置
CLOUDFLARE_API_TOKEN。
拓展挑战
- 在本地运行
make serve-wasm,使用浏览器开发者工具的 Network 面板分析 WASM 文件的加载时间和大小 - 修改
build_wrangler_site.mjs,添加 WASM 文件的 Brotli 压缩版本,并修改index.html使其优先加载压缩版本 - 研究 Cloudflare Workers 的
[assets]配置选项,添加合理的缓存策略(如 WASM 文件缓存 1 天,HTML 文件不缓存) - 设计一个
make serve-wasm-hot目标,在源文件变更时自动重新编译并热重载浏览器页面