Skip to content

构建与部署 — 代码走读

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.mjs

web/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

关键配置/函数索引

文件条目职责
Makefilewasm 目标完整 WASM 构建(编译 + 链接)
Makefilewasm-assets 目标运行资源扫描 Python 脚本
Makefileserve-wasm 目标构建并启动开发服务器
Makefilewrangler-site 目标构建 + 打包为 Cloudflare 格式
Makefileclean-wasm 目标清理 WASM 构建产物
Makefile$(WASM) 链接规则wasm-ld 链接,导出符号
Makefile$(WASM_OBJ_DIR)/%.o (C)Clang 编译 C 文件(含 PREPROC 管道)
Makefile$(WASM_OBJ_DIR)/%.o (asm)汇编数据转换 + 编译
server.mjsfileFor()URL 路径解析 + 安全检查
server.mjscreateServer()HTTP 服务器,静态文件服务
build_wrangler_site.mjsfiles 数组定义 4 个需要打包的文件映射
build_wrangler_site.mjsrm()清空旧 dist 目录
wrangler.toml[build]构建命令和监听目录配置
wrangler.toml[assets]静态资源目录和 HTML 处理策略
wrangler.toml[[routes]]自定义域名绑定