PPysolate SpineREAD THE CODE · UNDERSTAND THE SYSTEM
课程与章节
CODE READING / 28

Makefile:选择构建任务

Makefile

本章目录
配套饭点图解 · 1 节(按需展开)

先回答一个问题:从“手里只有源码”到“能读本地站点、能做离线验收”,每个命令究竟交付什么?根目录 Makefile 不是一个自动推导完整依赖图的构建系统。它提供几个动作入口,把工作交给 Python、Bash、Go 和 pnpm;各入口之间的关键交接需要读者自己看清。

make setup-build  →  build/inputs/
                         ↓
make guest        →  dist/spine.wasm
                         ↓
make check        →  Go/Python 检查结果
make site         →  site/dist/
                         ↓
make read         →  127.0.0.1 上的静态阅读服务

make offline      →  在 Linux/Ubuntu 禁网子进程中消费上述已有输入

setup-build 和 guest 是两个阶段,不是同一个目标的别名:前者允许下载并准备本地工具链,后者只使用已经存在的输入生成 Guest。site 负责生成静态站点,read 只读取已生成的目录。

1. 先看目标、依赖和 recipe

Makefile · L1–L58 · 打开完整源码

# Small local entrypoints. Only setup-build downloads toolchain inputs.
.PHONY: setup setup-build guest check-all bench-analyze bench-smoke bench-full
setup:
	python3 tools/setup-guest.py

check-all: check
	python3 -m unittest discover -s tools -p 'test_setup_guest.py'
	cd learning/go-patterns && go test ./... -count=1 && go vet ./...
	python3 -m unittest discover -s experiments/memory -p 'test_*.py'
	python3 experiments/memory/run.py test
	cd experiments/reusable-code && go test ./... -count=1 && go run . offline

bench-analyze:
	python3 experiments/memory/run.py analyze

bench-smoke:
	python3 experiments/memory/run.py smoke

bench-full:
	python3 experiments/memory/run.py full

.PHONY: setup-build guest
setup-build:
	python3 tools/setup-build-inputs.py
guest:
	bash build-guest.sh
.PHONY: walkthrough demo explain prefix prepared prepared-cow check

walkthrough:
	@bash demos/core.sh all

demo:
	@go run ./cmd/demo -source examples/order.py

explain:
	@go run ./cmd/demo -source examples/order.py -plm -show-transformed

prefix:
	@go run ./cmd/demo -source examples/order.py -prefix -show-transformed

prepared:
	@go run ./cmd/demo -source examples/order.py -prepared-copy -prefix

prepared-cow:
	@go run ./cmd/demo -source examples/order.py -prepared-cow -prefix

check:
	go test ./... -count=1 -v
	PYTHONPATH=guest python3 -m unittest guest/test_plm.py guest/test_prefix.py
	go vet ./...

.PHONY: site read offline
site:
	cd site && pnpm build
read:
	@bash tools/read-local.sh
offline:
	@bash tools/offline-check.sh

Makefile 中的目标名是调用入口,冒号右边是 Make 层面的 prerequisite,缩进的命令是 recipe。check-all 声明了 prerequisite check,因此先执行核心检查,再检查教学与实验模块。setup 从仓库解压 Guest;bench-analyze 复算历史数据,bench-smoke / bench-full 采集 Linux 内存数据。其余入口如 setup-build、guest、site、read 和 offline 没有写 prerequisite;.PHONY 只说明这些名字代表动作,不要拿同名文件的时间戳决定是否运行。也就是说,make guest 不会因为 setup-build 写在上面就自动先跑它,make read 也不会因为 site 存在就自动重建站点。

这条规则解释了构建链里的显式边界:

入口 直接调用者 消费的输入 主要交付物 网络预期
setup-build Python 准备脚本 锁文件和远程归档 build/inputs、CPython 交叉构建输出 首次或缓存失效时可联网
guest build-guest.sh build/inputs、C/Python 源文件 dist/spine.wasm 脚本不下载
check Go、Python、vet Go module cache、Guest artifact、测试源码 检查结果 正常不应需要网络
site pnpm build site/ 源码和 Node 依赖 site/dist 依赖须先准备
offline 离隔离子进程 上面已经存在的输入 禁网条件下的验收结果 子进程被阻断
read tools/read-local.sh site/dist/index.html 本地 HTTP 服务 不下载、不重建

表中的“交付物”是阶段之间传递的状态。比如 setup-build 成功后,guest 才有机会找到 clang、CPython 静态库和 wasi-vfs;guest 成功后,check 才能走真实 Guest 测试;site 成功后,read 才有首页可提供。

2. 一次真实的构建顺序

make setup-build 调用 python3 tools/setup-build-inputs.py。Python 脚本负责平台检查、锁文件读取、归档下载与 SHA-256 校验、解压和 CPython WASI 构建;它将输入放到默认的 build/inputs,也可由 SPINE_BUILD_INPUTS 指定另一处目录。

随后执行 make guest,Make 调用 bash build-guest.sh。这个脚本检查同一输入根目录中的 clang 和 CPython 主静态库,然后编译 guest/runtime.c,链接 CPython/WASI 静态库,打包标准库和项目 Python 文件,验证 Wasm,最后才写入 dist/spine.wasm。输入根目录如果前后不一致,准备阶段的成果不会自动出现在 Guest 构建使用的位置。

make check 是检查阶段,不是 Guest 构建阶段。它顺序执行 go test ./... -count=1 -v、带 PYTHONPATH=guest 的两个 Python 单测和 go vet ./...。Go recipe 的非零退出会阻止下一条 recipe 继续执行;因此前一步失败不会被后续检查掩盖。它仍然消费当前文件树和已有 dist/spine.wasm,不会把 guest/plm.py 等文件自动重新打进 Wasm。

make site 在该条 recipe 的 shell 中进入 site/ 并执行 pnpm build,产物写入 site/dist。make read 只是启动 tools/read-local.sh,它检查首页后使用 Python 标准库 HTTP server 读取这个目录。

3. 演示目标和重建判断

walkthrough 是当前面试主入口,调用 demos/core.sh all,依次展示普通调用、PLM、prefix 与隔离。它编排现有实现和真实测试,不修改核心 runtime;详见 核心演示。

demo、explain、prefix、prepared 和 prepared-cow 都是 go run ./cmd/demo 的不同参数组合。-source examples/order.py 传入的是运行时用户程序;-plm、-prefix、-prepared-copy 和 -prepared-cow 选择运行方式或展示方式,不会触发 Guest 重新链接。

因此改动后的处理可以按输入归类:

  • 改 Go Host:通常重新执行对应的 go run、定向 Go 测试或 go vet 即可;
  • 改 examples/*.py 或 -source 指向的用户程序:这是运行时输入,不需要重建 Guest;
  • 改 guest/runtime.c、guest/bootstrap.py、guest/plm.py、guest/prefix.py 或链接参数:重新执行 make guest,再做真实 Guest 检查;
  • 改站点文档或布局:执行 make site,需要阅读时再执行 make read;
  • 只改讲义且不改变发布站点输入:不需要下载工具链或重建大型 Wasm。

这些判断来自实际调用路径,而不是目标名的相似性。

4. 离线入口放在哪里

make offline 只调用 tools/offline-check.sh。脚本在隔离子进程中运行网络探针、make check、站点构建和几个 demo;它不把 offline 声明成 offline: check site,也不在禁网环境中调用 setup-build。完整离线检查假定 Go module cache、Node/pnpm 依赖、dist/spine.wasm 和 Guest 构建输入都已在线准备。

在 Linux/Ubuntu 路径中,真正的 socket 阻断由 user/network namespace 完成;Go 的 GOPROXY=off 等变量只是生态内的约束。若缺依赖,离线流程应明确失败,而不是删除隔离或临时联网把缺口补上。

5. 读完后应能回答

  1. 为什么 make guest 不会自动调用 make setup-build?
  2. 改 guest/plm.py 后,为什么 go run 和 make guest 分别证明不同的事情?
  3. 为什么 make read 成功只证明已有 site/dist 能被服务,不能证明站点是最新的?

下一步读构建输入,跟随锁文件和准备脚本进入真实的 Linux x86_64 本地工具链;再读Guest 构建看输入怎样变成 Wasm。本文覆盖当前 Makefile 全部非空源码行,未运行构建、测试、站点服务器或离线检查。