Appearance
构建与部署 — 代码走读
Makefile — WASM 构建目标
完整 WASM 构建规则
makefile
# Makefile (第 251-260 行)
wasm: generated wasm-assets $(WASM)
wasm-assets: $(GFX)
uv run python tools/generate_wasm_assets.py
$(WASM_C_OBJS): | generated wasm-assets
$(WASM): Makefile $(WASM_C_OBJS) $(WASM_DATA_OBJS)
@test -n "$(WASM_LD)" || { echo "wasm-ld not found; set WASM_LD=/path/to/wasm-ld"; exit 1; }
$(WASM_LD) --no-entry --allow-undefined --initial-memory=268435456 --max-memory=268435456 \
--export=AgbMain --export=WasmRunFrame --export-all -o $@ $(filter %.o,$^)C 文件编译规则
makefile
# Makefile (第 262-264 行)
$(WASM_OBJ_DIR)/%.o: $(C_SUBDIR)/%.c
@mkdir -p $(dir $@)
$(WASM_CC) --target=wasm32-unknown-unknown -DMODERN=1 -DWASM=1 \
-I include/wasm -I include -iquote include \
-E $< | $(PREPROC) -i -g $(ASSETS_DIR_NAME) $< charmap.txt | \
$(WASM_CC) --target=wasm32-unknown-unknown -x c -O2 \
-Wno-incompatible-library-redeclaration \
-Wno-unknown-attributes -Wno-ignored-attributes \
-Wno-parentheses -Wno-pointer-to-int-cast -Wno-int-to-pointer-cast \
-Wno-builtin-requires-header -Wno-gnu-alignof-expression \
-Wno-unknown-escape-sequence -Wno-excess-initializers \
-c - -o $@注意编译管道的三阶段:
大量的 -Wno-* 标志是为了抑制 Clang 对 GBA 时代代码风格的警告(如不兼容的库函数重声明、指针与整数转换等)。
汇编数据转换规则
makefile
# Makefile (第 266-269 行)
$(WASM_OBJ_DIR)/%.o: $(DATA_ASM_SUBDIR)/%.s tools/wasm_asm_data.py | generated
@mkdir -p $(dir $@) $(WASM_BUILD_DIR)
uv run python tools/wasm_asm_data.py $< $(WASM_BUILD_DIR)/$*.wasm.s
$(WASM_CC) --target=wasm32-unknown-unknown -c $(WASM_BUILD_DIR)/$*.wasm.s -o $@辅助构建目标
makefile
# Makefile (第 271-278 行)
clean-wasm:
rm -rf $(WASM_BUILD_DIR)
serve-wasm: $(WASM)
node web/server.mjs
wrangler-site: wasm
node tools/build_wrangler_site.mjsweb/server.mjs — 开发服务器
javascript
// web/server.mjs — 完整文件
import { createReadStream, existsSync, statSync } from 'node:fs';
import { extname, join, normalize, resolve } from 'node:path';
import { createServer } from 'node:http';
const root = resolve(process.cwd());
const requestedPort = Number(process.env.PORT || 8000);
const types = new Map([
['.html', 'text/html; charset=utf-8'],
['.js', 'text/javascript; charset=utf-8'],
['.css', 'text/css; charset=utf-8'],
['.wasm', 'application/wasm'],
]);
function fileFor(url) {
const pathname = new URL(url, `http://localhost:${requestedPort}`).pathname;
const relative = pathname === '/' ? 'web/index.html' : pathname.slice(1);
const file = resolve(root, normalize(relative));
return file.startsWith(root) ? file : null; // 路径安全:防止目录遍历
}
createServer((req, res) => {
const file = fileFor(req.url);
if (!file || !existsSync(file) || !statSync(file).isFile()) {
res.writeHead(404).end('not found');
return;
}
res.writeHead(200, {
'Content-Type': types.get(extname(file)) || 'application/octet-stream',
'Cache-Control': 'no-store', // 开发时不缓存
});
createReadStream(file).pipe(res);
}).listen(requestedPort, function () {
const { port } = this.address();
console.log(`pokeemerald wasm server: http://localhost:${port}`);
});tools/build_wrangler_site.mjs — 打包脚本
javascript
// tools/build_wrangler_site.mjs — 完整文件
#!/usr/bin/env node
import { copyFile, mkdir, rm } from 'node:fs/promises';
import { dirname } from 'node:path';
const output = 'dist/cloudflare';
const files = [
['web/index.html', `${output}/index.html`],
['web/style.css', `${output}/web/style.css`],
['web/app.js', `${output}/web/app.js`],
['build/wasm/pokeemerald.wasm', `${output}/build/wasm/pokeemerald.wasm`],
];
await rm(output, { recursive: true, force: true }); // 清空旧文件
for (const [source, destination] of files) {
await mkdir(dirname(destination), { recursive: true }); // 创建目录结构
await copyFile(source, destination); // 复制文件
}
console.log(`prepared Cloudflare static assets in ${output}`);wrangler.toml — Cloudflare Workers 配置
toml
name = "pokeemerald-wasm"
account_id = "9fc9d21b2d12f99cdb4da40d8d127877"
compatibility_date = "2026-05-29"
workers_dev = false # 不使用 *.workers.dev 子域
preview_urls = false # 禁用预览 URL
[build]
command = "make wrangler-site" # 部署前自动构建
watch_dir = ["web", "src", "include", "data", "graphics", "sound", "tools", "Makefile"]
[assets]
directory = "./dist/cloudflare" # 静态资源目录
html_handling = "auto-trailing-slash"
not_found_handling = "none"
[[routes]]
pattern = "pokeemerald.com" # 自定义域名
custom_domain = true关键配置/函数索引
| 文件 | 条目 | 职责 |
|---|---|---|
| Makefile | wasm 目标 | 完整 WASM 构建(编译 + 链接) |
| Makefile | wasm-assets 目标 | 运行资源扫描 Python 脚本 |
| Makefile | serve-wasm 目标 | 构建并启动开发服务器 |
| Makefile | wrangler-site 目标 | 构建 + 打包为 Cloudflare 格式 |
| Makefile | clean-wasm 目标 | 清理 WASM 构建产物 |
| Makefile | $(WASM) 链接规则 | wasm-ld 链接,导出符号 |
| Makefile | $(WASM_OBJ_DIR)/%.o (C) | Clang 编译 C 文件(含 PREPROC 管道) |
| Makefile | $(WASM_OBJ_DIR)/%.o (asm) | 汇编数据转换 + 编译 |
| server.mjs | fileFor() | URL 路径解析 + 安全检查 |
| server.mjs | createServer() | HTTP 服务器,静态文件服务 |
| build_wrangler_site.mjs | files 数组 | 定义 4 个需要打包的文件映射 |
| build_wrangler_site.mjs | rm() | 清空旧 dist 目录 |
| wrangler.toml | [build] | 构建命令和监听目录配置 |
| wrangler.toml | [assets] | 静态资源目录和 HTML 处理策略 |
| wrangler.toml | [[routes]] | 自定义域名绑定 |