container 构建指南:从零编译、测试到与 Containerization 联调的完整开发流程
本文以 BUILDING.md 为核心,系统讲解如何在 Apple silicon Mac 上从源码构建 container 项目、运行单元与集成测试、重新生成 gRPC 协议代码,以及如何通过 Swift Package Manager 将本地的 Containerization、container-builder-shim 仓库挂接进构建流程。读完本篇,你将掌握从 make all 到 make install 的完整产物布局、隔离测试数据目录的用法、本地依赖编辑与回滚的规范操作,以及基于 launchd 标签对 XPC 助手进程挂接调试器的技巧。
环境要求
根据 BUILDING.md 的明确说明,构建 container 需要满足以下条件:
- Mac(Apple silicon 芯片);
- macOS 15 起步,推荐 macOS 26;
- Xcode 26,并将其设置为命令行工具所使用的 active developer directory。
这里有一个与源码一致的重要佐证:Package.swift 声明了 swift-tools-version: 6.2 与 platforms: [.macOS("15")],即 Swift 工具链版本和 macOS 部署目标分别对应上述要求。从依赖清单看,项目锁定了对核心依赖的精确版本约束,例如:
.package(url: "https://github.com/apple/containerization.git", exact: Version(stringLiteral: scVersion)), // scVersion = "0.43.0"
.package(url: "https://github.com/grpc/grpc-swift-2.git", from: "2.3.0"),
.package(url: "https://github.com/apple/swift-protobuf.git", from: "1.36.0"),
即 Package.swift 第 25、26 行的 builderShimVersion = "0.13.1" 与 scVersion = "0.43.0" 决定了 builder shim 与 Containerization 的配套版本。
macOS 26 上的已知限制(vmnet 框架 bug):如果 container 的助手应用位于你的 Documents 或 Desktop 目录下,网络创建会失败。规避方式有两种——使用 make install 后直接运行 /usr/local 下的 container 二进制;或者如果你偏好直接使用 make all 在项目 bin 和 libexec 目录中生成的二进制,就把项目放到其他位置(如 ~/projects/container),直到该问题修复。
编译、测试与安装
在隔离的数据目录中构建并测试
BUILDING.md 给出的标准开发循环是:
rm -rf test-data
make APP_ROOT=test-data all test integration
这行命令的意图是:构建 container 及其后台服务,并在一个隔离的应用数据目录(test-data)中运行基础测试与集成测试,避免污染你日常使用的 ~/Library/Application Support/com.apple.container 数据。结合 Makefile 可以看出每一步的底层行为:
make all:默认目标,等价于all: container init-block。container目标先执行swift build(见 Makefile 第 101–104 行),再以SUDO=(不使用 sudo)和DEST_DIR=<项目根目录>调用install,把产物安装到项目自己的bin/与libexec/下;init-block目标调用 scripts/install-init.sh:当containerization处于 Swift 包的 edit 模式(本地路径依赖)时,会自动构建并导入 init 镜像(详见后文「本地依赖联调」一节)。
make test:先执行共享构建阶段build-tests(swift build --build-tests),再运行swift test --skip-build,并显式跳过TestCLI与IntegrationTests——即只跑单元测试。注意默认构建还附带-Xswiftc -warnings-as-errors -Xswiftc -enable-testing参数(由 Makefile 第 16–18 行的BUILD_CONFIGURATION/WARNINGS_AS_ERRORS变量控制),也就是警告会被当作错误,测试编译选项默认开启。make integration:执行流程定义在 Makefile 的RUN_INTEGRATION宏(第 281–313 行)中,依次完成:- 停止 apiserver(
bin/container system stop+scripts/ensure-container-stopped.sh); - 清空
APP_ROOT下的应用数据(若设置PRESERVE_KERNELS=true则保留kernels目录); - 以
bin/container --debug system start --timeout 60 --enable-kernel-install --app-root "$(APP_ROOT)"启动系统; - 分三阶段跑集成测试:
ImageWarmup/预热、非*Serial命名的套件并发执行(并行宽度默认取物理核数)、*Serial命名的套件串行执行。测试通过环境变量CONTAINER_APP_ROOT、CONTAINER_CLI_PATH、CLITEST_SCRATCH_ROOT等与隔离目录关联。
- 停止 apiserver(
构建成功后,bin/ 与 libexec/ 中的产物布局可从 Makefile 的 staging 目录规则(第 127–158 行)得到印证:CLI 与 API 服务位于 bin/,各插件位于 libexec/container/plugins/<plugin>/bin/ 与各自的 config.toml 资源文件,插件包括:
| 二进制 | 插件目录 | 职责(从目录结构推断) |
|---|---|---|
container |
bin/ |
命令行入口 |
container-apiserver |
bin/ |
常驻 API 服务 |
container-runtime-linux |
plugins/container-runtime-linux/ |
Linux 容器运行时 |
container-network-vmnet |
plugins/container-network-vmnet/ |
基于 vmnet 的网络 |
container-core-images |
plugins/container-core-images/ |
核心镜像服务 |
machine-apiserver |
plugins/machine-apiserver/ |
轻量虚拟机 API |
k8s |
plugins/k8s/ |
Kubernetes 支持 |
这与 Package.swift 中定义的 7 个可执行 target(container、container-apiserver、container-core-images、container-network-vmnet、container-runtime-linux、machine-apiserver、k8s)一一对应。
安装到 /usr/local
make install
该目标需要管理员密码。从 Makefile 第 115–125 行可见其内部流程:先通过 installer-pkg 目标把产物暂存到 bin/<configuration>/staging/,对每个二进制做 ad-hoc 签名(codesign --force --sign -,其中 container-runtime-linux 与 container-network-vmnet 分别附带 signing/container-runtime-linux.entitlements 与 signing/container-network-vmnet.entitlements 权限),再用 pkgbuild 生成 unsigned pkg(container-installer-unsigned.pkg),最后以 sudo installer -pkg ... -target / 安装。因此最终的二进制会落到 /usr/local/bin 与 /usr/local/libexec。
发布版(release)构建
BUILDING.md 说明 release 构建比 debug 构建有更好的性能,用法是:
BUILD_CONFIGURATION=release make all test integration
BUILD_CONFIGURATION=release make install
BUILD_CONFIGURATION 变量会同时传给 swift build -c <配置> 与 swift test -c <配置>,并决定产物输出目录 bin/<configuration>/。
重新生成 gRPC 协议代码
container 通过 gRPC 与负责从 Dockerfile 创建镜像的 builder 虚拟机通信,并且依赖特定版本的 grpc-swift 与 swift-protobuf。如果你修改了 container-builder-shim 项目中的 gRPC API,需要安装工具并重新生成本项目中的 gRPC 代码:
make protos
Protobuf.Makefile 揭示了 make protos 的完整步骤:
- 从 protobuf 官方 release 下载并解压
protoc26.1(universal 二进制)到.local/bin/protoc@26.1/; - 构建本项目内的两个 protoc 插件:
protoc-gen-swift与protoc-gen-grpc-swift-2; - 若
.local/container-builder-shim尚不存在,则以--depth 1克隆该仓库的BUILDER_SHIM_VERSION标签——该版本直接解析自 Package.swift 第 25 行的let builderShimVersion = "0.13.1"; - 以
pkg/api/Builder.proto为输入,把生成物写入 Sources/ContainerBuild 目录(即Builder.pb.swift与Builder.grpc.swift的来源):
$(PROTOC) $(LOCAL_DIR)/container-builder-shim/pkg/api/Builder.proto \
--plugin=protoc-gen-grpc-swift=$(BUILD_BIN_DIR)/protoc-gen-grpc-swift-2 \
--plugin=protoc-gen-swift=$(BUILD_BIN_DIR)/protoc-gen-swift \
--proto_path=$(LOCAL_DIR)/container-builder-shim/pkg/api \
--grpc-swift_out="Sources/ContainerBuild" \
--grpc-swift_opt=Visibility=Public \
--swift_out="Sources/ContainerBuild" \
--swift_opt=Visibility=Public \
-I.
生成后还会自动调用 make update-licenses 补齐许可证头。对应的构建端实现位于 Sources/ContainerBuild/Builder.swift 及生成的 Builder.grpc.swift。
使用本地 Containerization 副本开发
当你的改动需要同时修改 Containerization 项目(或反过来)时,按 BUILDING.md 的流程操作:
-
把 Containerization 仓库克隆到
container克隆目录的同级位置,并按其 README 的「prepare to build package」指引准备好构建环境。 -
进入
container项目目录:cd container -
若
container服务已在运行,先停止:bin/container system stop -
用 Swift Package Manager 把依赖切换到本地
containerization路径并更新Package.resolved:/usr/bin/swift package edit --path ../containerization containerization /usr/bin/swift package update containerization使用 Xcode 的注意事项:不要运行
swift package edit,而是临时把 Package.swift 中的版本化依赖:.package(url: "https://github.com/apple/containerization.git", exact: Version(stringLiteral: scVersion)),替换为本地路径依赖:
.package(path: "../containerization"),注意:如果你已经(无论有意还是意外)运行过
swift package edit,必须先按下一节的方法恢复正常依赖,否则修改后的Package.swift不生效,项目可能构建失败。 -
如果你希望
container使用 Containerization 中vminit子项目的改动,在运行时配置文件~/.config/container/config.toml中设置 init 镜像:[vminit] image = "vminit:latest"这个配置项有明确的源码对应:Sources/ContainerPersistence/ContainerSystemConfig.swift 第 147–165 行的
VminitConfig定义了默认值——当 Containerization 版本为latest时默认镜像是vminit:latest,否则是ghcr.io/apple/containerization/vminit:<tag>。把它覆盖为本地构建的vminit:latest后,运行时才会消费你本地改出的 init 镜像。 -
构建:
make clean all此处
make all的init-block环节尤为关键:scripts/install-init.sh 会通过swift package show-dependencies --format json探测containerization依赖状态,一旦发现版本是unspecified(即处于本地 edit 模式),就会在本地 Containerization 目录执行make init构建 init 镜像、用cctl images save导出为vminit:latest,停止系统后重新加载该镜像(bin/container i load -i /tmp/init.tar)。也就是说,本地 Containerization 联调时的 init 镜像构建是make all自动完成的。 -
重启服务:
bin/container system stop bin/container system start
回滚到 Package.swift 中的正式依赖
-
如果之前使用了本地 init 文件系统,从
~/.config/container/config.toml中移除init覆盖(若[vminit]段没有别的镜像设置则整段删除)。 -
用 Swift Package Manager 恢复正常依赖并更新
Package.resolved(Xcode 用户则改回Package.swift,不要使用swift package unedit):/usr/bin/swift package unedit containerization /usr/bin/swift package update containerization -
重新构建:
make clean all -
重启服务:
bin/container system stop bin/container system start
使用本地 container-builder-shim 副本开发
要测试需要改动 container-builder-shim 项目的变更:
-
克隆 container-builder-shim 仓库并进入其目录。
-
完成改动后,构建自定义 builder 镜像、将其设为
~/.config/container/config.toml中的活动 builder 镜像,并删除现有buildkit容器以确保新镜像生效:container build -t builder . container rm -f buildkit在
~/.config/container/config.toml中添加:[build] image = "builder:latest"这一配置项对应 Sources/ContainerPersistence/ContainerSystemConfig.swift 第 80–113 行的
BuildConfig:默认image为ghcr.io/apple/container-builder-shim/builder:<builderShimVersion>(当前为 0.13.1),另有rosetta(默认true)、cpus(默认 2)、memory(默认 2048MB)三个可调参数,覆盖image后构建流程就会使用你本地打标签的builder:latest。 -
照常运行构建命令:
container build ...
注意:如果你的修改把 builder 镜像改坏了,务必先重新构建并正确打好标签,再去尝试构建 container-builder-shim,否则会陷入用坏镜像构建坏镜像的循环。
调试 XPC 助手进程
container 的后台服务(runtime、network、apiserver 等)是 launchd 托管的 XPC 助手进程,可以用 launchd 服务标签挂接调试器。
-
查找 launchd 服务标签:
% container system start % container run -d --name test debian:bookworm sleep infinity test % launchctl list | grep container 27068 0 com.apple.container.container-network-vmnet.default 27072 0 com.apple.container.container-core-images 26980 0 com.apple.container.apiserver 27331 0 com.apple.container.container-runtime-linux.test -
设置环境变量
CONTAINER_DEBUG_LAUNCHD_LABEL为要调试的服务标签后重启系统。标签以该值为前缀的服务将等待调试器:% export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux.test % container system start # 只有 com.apple.container.container-runtime-linux.test 等待调试器% export CONTAINER_DEBUG_LAUNCHD_LABEL=com.apple.container.container-runtime-linux % container system start # 所有以 com.apple.container.container-runtime-linux 开头的服务都等待调试器前缀匹配的行为在源码中有直接实现:Sources/ContainerPlugin/LaunchPlist.swift 第 66–79 行的
getWaitForDebugger会检查label == debugTarget || label.starts(with: debugTarget + "."),命中则在生成的 launchd plist 中写入WaitForDebugger = true(对应waitForDebugger字段与WaitForDebuggerplist 键)。 -
触发服务启动的命令(如
container run)会挂起,此时再附上调试器:% container run -it --name test debian:bookworm ⠧ [6/6] Starting container [0s] # 服务在等待调试器,命令挂起
安装 pre-commit 钩子
make pre-commit
该目标在 git commit 时保证改动具备正确的格式与许可证头。对照 Makefile 第 395–404 行,它会把 scripts/pre-commit.fmt 复制到 .git/hooks/,并在既有 pre-commit 钩子中追加一行对 hooks/pre-commit.fmt 的引用(支持 PRECOMMIT_NOFMT 环境变量),同时通过 scripts/ensure-hawkeye-exists.sh 确保 hawkeye 工具就绪。日常格式化与校验也可以直接用 make fmt(swift-format + 更新许可证头)和 make check(格式 lint + 许可证头检查)两个目标完成。
附:常用 make 目标速查
结合 Makefile 与 Protobuf.Makefile,与日常开发相关的目标汇总如下(BUILDING.md 只覆盖了其中一部分):
| 目标 | 作用 |
|---|---|
all |
构建全部产物并安装到项目 bin/、libexec/,同时构建 init-block |
build / cli |
仅编译 / 仅构建 CLI 并拷贝到 bin/container |
test |
运行单元测试(跳过 TestCLI 与 IntegrationTests) |
integration |
在隔离数据目录中启动系统并运行集成测试(warmup → 并发 → 串行三阶段) |
install |
签名、打包 pkg 并通过 sudo installer 安装到 /usr/local |
release |
以 release 配置执行 all |
protos |
下载 protoc、构建插件、重新生成 gRPC 代码 |
pre-commit |
安装格式与许可证头检查的 pre-commit 钩子 |
fmt / check |
应用 / 校验 swift-format 格式与许可证头 |
install-kernel |
停止系统后以 --enable-kernel-install 启动以安装内核 |
clean |
清理 bin/、libexec/ 与 swift package clean |
coverage / coverage-unit / coverage-integration |
代码覆盖率收集与报告(llvm-cov) |
小结
container 的开发工作流可以归纳为四条主线:日常开发用 make APP_ROOT=test-data all test integration 在隔离目录完成构建与验证;交付安装用 make install 走签名与 pkg 安装链路;协议变更用 make protos 对齐 container-builder-shim 版本(0.13.1);跨仓库联调则依赖 Swift 包的 edit/unedit 机制,并配合 ~/.config/container/config.toml 中的 [vminit] 与 [build] 镜像覆盖、scripts/install-init.sh 的自动 init 镜像构建,以及 CONTAINER_DEBUG_LAUNCHD_LABEL 提供的 XPC 进程断点能力。所有命令与配置项均可在 BUILDING.md、Makefile、Protobuf.Makefile 与 Package.swift 中逐项查证。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00