引言
自 Zig 0.11 起,官方引入了 build.zig.zon 格式与 zig fetch 命令,标志着 Zig 从「手写构建脚本」时代迈入「声明式包管理」时代。build.zig.zon 类似 Node.js 的 package.json 或 Rust 的 Cargo.toml,但语法是简洁的 Zig 结构体字面量;zig fetch 负责从 Git 仓库、HTTP tarball 或本地路径拉取依赖,缓存在 Zig 全局缓存目录中,并在 build.zig 中暴露模块引用。
本文覆盖:Zon 文件格式、zig fetch 缓存机制、Git 与 tarball 依赖、路径依赖、版本锁定策略、常用第三方库盘点、本地包开发流程,以及 zyg 等社区包管理器的补充角色。
前置:/zig-build-system/(build.zig 基本结构)、/zig-comptime-programming/(编译期模块导入)。
目录
- 1. build.zig.zon 格式详解
- 2. zig fetch 缓存与全局存储
- 3. Git 依赖与 tarball 依赖
- 4. 路径依赖:本地包开发
- 5. 版本锁定与依赖解析
- 6. 常用第三方库盘点
- 7. zyg 与社区包管理器
- 8. 工程实践:多包工作区
- 9. 速查表
- 10. 一句话记忆
- 相关阅读
- 延伸阅读
1. build.zig.zon 格式详解
build.zig.zon 位于项目根目录,描述包名、版本、依赖列表和路径映射。
1.1 最小 Zon 文件
.{
.name = "myserver",
.version = "0.1.0",
.dependencies = .{},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
"LICENSE",
},
}
字段说明:
| 字段 | 必需 | 说明 |
|---|---|---|
name | 是 | 包名,用于模块导入标识 |
version | 是 | 语义化版本,如 “0.1.0” |
dependencies | 否 | 依赖表,键为别名,值为 fetch 来源 |
paths | 是 | 发布包时包含的文件白名单 |
1.2 带依赖的 Zon 文件
.{
.name = "zig-webapp",
.version = "0.2.0",
.dependencies = .{
.ziglyph = .{
.url = "https://github.com/jecolon/ziglyph/archive/refs/tags/v0.1.0.tar.gz",
.hash = "1220a3b2c...",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}
url指向源码压缩包或 Git archive。hash是 Zig 专用的多哈希(multihash),用于验证下载内容与锁定版本。
1.3 hash 的获取方式
首次添加依赖时,先不写 hash,执行 zig build 让 Zig 报错并给出期望的哈希值:
$ zig build
note: expected .hash = "1220f3d9ab..."
将打印出的 hash 填入 build.zig.zon 即可锁定该版本。hash 一旦写死,后续下载相同 URL 时 Zig 会校验匹配,防止供应链投毒。
2. zig fetch 缓存与全局存储
2.1 缓存目录
zig fetch 将下载的包缓存在 Zig 全局缓存中:
# Linux/macOS
$ ls ~/.cache/zig/p/
# Windows
$ ls %LOCALAPPDATA%\zig\p\
每个依赖按内容哈希命名目录,内容寻址保证:相同内容的包无论来源如何,只存一份。
2.2 手动 fetch
$ zig fetch https://github.com/jecolon/ziglyph/archive/refs/tags/v0.1.0.tar.gz
1220a3b2c...
命令输出 hash 字符串,直接粘贴进 build.zig.zon 的 .hash 字段即可。
2.3 build.zig 中引用依赖
依赖下载后,需要在 build.zig 中声明为模块,才能让源码通过 @import 使用:
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// 声明 ziglyph 模块,让源码可以通过 @import("ziglyph") 访问
const ziglyph = b.dependency("ziglyph", .{});
exe.root_module.addImport("ziglyph", ziglyph.module("ziglyph"));
b.installArtifact(exe);
}
源码中即可使用:
const ziglyph = @import("ziglyph");
pub fn main() !void {
const s = "Hello, Zig!";
std.debug.print("upper: {s}\n", .{ziglyph.toUpper(s)});
}
b.dependency("ziglyph", .{})的名称必须与build.zig.zon中.dependencies的键名一致。
3. Git 依赖与 tarball 依赖
3.1 tarball 依赖(推荐)
GitHub/GitLab 自动为 tag 生成 tarball:
.dependencies = .{
.ziglyph = .{
.url = "https://github.com/jecolon/ziglyph/archive/refs/tags/v0.1.0.tar.gz",
.hash = "1220f3d9ab...",
},
}
tarball 是内容哈希锁定的基础:tar 包的内容与 Git commit 一一对应,无中心化 registry 也能保证可复现构建。
3.2 Git 子模块风格依赖
目前 Zig 官方不支持直接写 git URL + branch,但社区有两种变通方案:
方案 A:git archive 导出 tarball
git archive --format=tar.gz HEAD > mypkg.tar.gz
zig fetch file:///path/to/mypkg.tar.gz
方案 B:手动 clone 后用 path 依赖
.dependencies = .{
.mypkg = .{
.path = "../mypkg",
},
}
3.3 私有仓库
私有 Git 仓库的 tarball 需要认证:
# 使用 curl + token 下载后本地 fetch
$ curl -L -H "Authorization: token $GITHUB_TOKEN" \
https://api.github.com/repos/org/private/tarball/main \
-o private.tar.gz
$ zig fetch file://$(pwd)/private.tar.gz
将输出的 hash 填入 .hash,url 可替换为内部镜像地址。
4. 路径依赖:本地包开发
4.1 本地路径依赖
开发多包工作区时,一个包引用另一个本地包:
// myapp/build.zig.zon
.{
.name = "myapp",
.version = "0.1.0",
.dependencies = .{
.shared = .{
.path = "../shared",
},
},
.paths = .{ "build.zig", "build.zig.zon", "src" },
}
// myapp/build.zig
const shared = b.dependency("shared", .{});
exe.root_module.addImport("shared", shared.module("shared"));
路径依赖不参与 zig fetch 的下载/缓存流程:Zig 直接读取本地目录,适合 monorepo 或并行开发场景。
4.2 开发中切换远程/本地
当包还未发布或正在调试 fork 版本时,可用路径依赖临时覆盖远程版本:
.{
.dependencies = .{
.some_lib = if (@hasDecl(@This(), "dev_mode"))
.{ .path = "../../fork/some_lib" }
else
.{ .url = "...tarball...", .hash = "..." },
},
}
更实用的办法是在 CI 中替换 build.zig.zon:构建前 sed 替换 url 为 path,测试完再恢复。
5. 版本锁定与依赖解析
5.1 hash 即锁定
Zig 没有 package-lock.json 或 Cargo.lock 这种独立锁定文件。锁定信息直接内联在 build.zig.zon 的 hash 字段中:hash 唯一确定内容版本,任何内容变化都会导致 hash 不匹配。
5.2 传递依赖
如果 ziglyph 自己也依赖其他包,Zig 会自动递归解析。每个包的 build.zig.zon 中的依赖被独立下载到缓存,同名不同版本的包可以共存(按 hash 区分)。
5.3 hash 冲突处理
当上游发布了安全补丁但 URL 不变(罕见但可能),hash 会变化。此时 zig build 报错提示新的 hash,手动替换即可。出于安全考虑,Zig 不会自动接受内容变化——这是供应链安全的基本防线。
6. 常用第三方库盘点
Zig 生态正在快速发展,以下是经过验证的常用库:
| 库名 | 用途 | URL |
|---|---|---|
| ziglyph | Unicode 处理、大小写转换、正则 | github.com/jecolon/ziglyph |
| zig-regex | 正则表达式引擎 | github.com/tiehuis/zig-regex |
| zap | HTTP 服务器/客户端 | github.com/zigzap/zap |
| migu | SQLite 包装器 | github.com/vrischmann/zig-sqlite |
| zig-cli | CLI 参数解析 | github.com/sam701/zig-cli |
| ** SDL.zig** | SDL2 绑定 | github.com/MasterQ32/SDL.zig |
| zstd.zig | Zstd 压缩 | github.com/Snektron/zstd.zig |
| bearssl-zig | TLS/SSL 绑定 | github.com/MasterQ32/bearssl-zig |
6.1 选型建议
□ 文本处理 → ziglyph + zig-regex
□ Web 服务 → std.http.Server / zap
□ 数据存储 → zig-sqlite (SQLite) / libpq-zig (PostgreSQL)
□ CLI 工具 → zig-cli / std.process.args
□ 图形窗口 → SDL.zig / mach-glfw
□ 压缩 → zstd.zig / std.compress
7. zyg 与社区包管理器
7.1 zyg
zyg 是社区实现的 Zig 包管理器,提供类似 npm/cradle 的命令行体验:
$ zyg add ziglyph # 自动写入 build.zig.zon 并 fetch
$ zyg update # 批量更新依赖
$ zyg search sqlite # 搜索包索引
zyg 不是必需的:所有操作都可通过 zig fetch + 手动编辑 Zon 文件完成。但在频繁添加依赖时,zyg 可以减少样板操作。
7.2 包注册中心状态
截至目前,Zig 官方没有中心化包注册中心(类似 crates.io)。依赖通过 URL 直接指向源码仓库。这带来灵活性(不依赖第三方服务),但也增加了发现成本。社区正在探索基于 git 或 ipfs 的去中心化索引方案。
8. 工程实践:多包工作区
8.1 Monorepo 结构
workspace/
myapp/
build.zig
build.zig.zon
src/main.zig
shared/
build.zig
build.zig.zon
src/lib.zig
tests/
integration_tests.zig
myapp 通过 path = "../shared" 依赖 shared:
// myapp/build.zig.zon 局部
.dependencies = .{
.shared = .{ .path = "../shared" },
}
8.2 共享工具链版本
确保所有子包使用相同的 Zig 版本:在工作区根放一个 .zig-version 文件:
0.13.0
CI 读取该文件安装对应 Zig 版本,防止编译器版本差异导致 API 不兼容。
9. 速查表
| 需求 | 命令/配置 |
|---|---|
| 初始化项目 | zig init-exe 自动生成 build.zig.zon |
| 添加 tarball 依赖 | 写 .url + .hash 进 build.zig.zon |
| 获取 hash | zig fetch <url> 或先不写 hash 让 zig build 提示 |
| 添加本地依赖 | .path = "../mypkg" |
| build.zig 中使用 | b.dependency("name", .{}) + addImport |
| 源码中导入 | @import("name") |
| 多包工作区 | path 依赖 + monorepo 目录结构 |
| 锁定版本 | hash 字段内联锁定,无额外 lock 文件 |
10. 一句话记忆
build.zig.zon 声明依赖名+URL+hash,zig fetch 下载内容寻址缓存,build.zig 中用 b.dependency + addImport 暴露模块,源码 @import 直接消费——无中心化 registry,hash 即锁定,路径依赖支持 monorepo。
相关阅读
- /zig-build-system/ — build.zig 构建系统入门
- /zig-comptime-programming/ — 编译期编程与模块系统
- /zig-c-interoperability/ — C 库绑定与外部依赖集成
延伸阅读
- /zig-http-server/ — 使用第三方 HTTP 库构建 Web 服务
- /zig-json-serialization/ — 数据序列化与配置解析
- /zig-testing-quality/ — 依赖隔离与测试策略
- [[zig]] — Zig 系统编程专题
// 完整示例:使用 build.zig.zon 管理 ziglyph 依赖
// ===== build.zig.zon =====
// .{
// .name = "demo",
// .version = "0.1.0",
// .dependencies = .{
// .ziglyph = .{
// .url = "https://github.com/jecolon/ziglyph/archive/refs/tags/v0.1.0.tar.gz",
// .hash = "1220f3d9ab...", // 执行 zig fetch 获取真实 hash
// },
// },
// .paths = .{"build.zig", "build.zig.zon", "src"},
// }
// ===== build.zig =====
// const std = @import("std");
// pub fn build(b: *std.Build) void {
// const target = b.standardTargetOptions(.{});
// const optimize = b.standardOptimizeOption(.{});
// const exe = b.addExecutable(.{
// .name = "demo",
// .root_source_file = b.path("src/main.zig"),
// .target = target,
// .optimize = optimize,
// });
// const ziglyph = b.dependency("ziglyph", .{});
// exe.root_module.addImport("ziglyph", ziglyph.module("ziglyph"));
// b.installArtifact(exe);
// }
// ===== src/main.zig =====
const std = @import("std");
const ziglyph = @import("ziglyph");
pub fn main() !void {
const s = "Zig 包管理";
std.debug.print("source: {s}\n", .{s});
_ = ziglyph;
}
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。