第 2 章拆解 — 构建、测试与发布:把 Zig 项目当成基础设施
构建、测试与发布:把 Zig 项目当成基础设施
这一章讲什么: 构建系统通常是系统编程书里最干的部分——Makefile 和 shell 脚本,和写的代码完全脱节。 Zig 的处理是:构建过程本身就是一段 Zig 程序(
build.zig),依赖清单是另一个文件(build.zig.zon)。 围绕这两个文件,这一章走完「格式化字符串 → 选分配器 → 写测试 → 测泄漏 → 出文档 → 引依赖 → 造库 → 发 GitHub → 基准对比」的整条链。主线例子:zuuid,一个 UUID 生成器。先看它的 16 个随机字节怎么变成规范的版本 4 UUID, 再看同一份代码怎么从单文件工具拆成库、被别人当依赖引用、最后被拿去和系统自带的
uuidgen(1)赛跑。
1. 顶层全景:一条「工具 → 库 → 包」的流水线
单文件工具(zuuid.zig,能跑)
│ 把「生成逻辑」和「命令行界面」拆开
▼
库(src/root.zig,可测、可被 import)
│ GitHub 托管 + build.zig.zon 标明指纹、版本、最低 Zig 版本
▼
包(别人在 build.zig.zon 里写你的 URL + 提交哈希 + 哈希值)
│
▼
消费者项目(zuuid-cli:zig build run 一条命令编译、链接、运行)
图说:每一步都靠两个文件推进——build.zig 管「怎么编译」(Zig 代码),
build.zig.zon 管「需要什么」(声明式清单)。这一章真正想立的命题在结尾才明说:可复现构建、依赖隔离、测试纪律、内存泄漏验证、文档生成、基准测试、包发布,这些不是锦上添花,是基础设施软件的运行地基——「没法隔离测试的缓存服务器是负债;没有内存验证的并发索引是定时炸弹」1。
2. 核心原理
2.1 std.fmt:一个格式串,四种进制,两种落地方式
Zig 的字符串格式化集中在 std.fmt 一个命名空间下,而且在编译期就校验格式占位符和数据类型对不对得上,一整类运行时错误直接消失2。核心占位符:
| 占位符 | 干什么 | 例(对数字 255) |
|---|---|---|
{d} {x} {b} {o} | 十进制 / 十六进制 / 二进制 / 八进制 | 255 / ff / 11111111 / 377 |
{d:.2} | 保留两位小数 | 3.14(对 π) |
{d: >10.2} | 十位宽、右对齐、两位小数 | 左边补空格的 3.14 |
{s} | 把字节按 UTF-8 文本打印 | Alice |
{any} | 通用:递归打印任何类型的默认表示 | 字符串会打成字节数组 { 65, 108, … } |
图说:最后两行是那组容易踩的对比——{s} 把字节当文本,{any} 把字节当数值序列;书上 User{ .name = "Alice" } 被 {any} 打印成 .{ .name = { 65, 108, 105, 99, 101 }, .id = 101 },技术上完全正确(那正是 Alice 五个字母的 ASCII 码),但几乎从不是你要的3。经验法则:字符串用 {s},其余想看内容用 {any}。
格式化结果有两种落地方式,取舍清清楚楚:
std.fmt.allocPrint(allocator, …):算好精确长度、在堆上申请一块刚好够的内存——快,但要记得defer free;std.fmt.bufPrint(&buffer, …):写进你自己准备的栈数组(例子里是 64 字节),零堆分配、不用 free——更快,但输出超过容量就报error.NoSpaceLeft,截断处理是你的责任4。
2.2 分配器不是设计偏好,是架构决策
Zig 取消了「全局默认分配器」。每个需要动态内存的函数都必须显式接收一个 std.mem.Allocator 参数;它只认这个接口,背后是系统堆、arena 还是栈上数组,它不知道也不关心——这个决定权在调用方,因为调用方才是唯一掌握足够上下文做对选择的人5。
接口本身简单到只有三个函数指针:申请(alloc)、调整大小(resize)、释放(free)。代价是每次分配多一次间接调用;买到的是「用内存的代码」和「管内存的代码」彻底解耦——也因此,你能写出包在任何分配器外面的日志分配器(记录每次申请释放)和故障注入分配器(第 N 次申请故意返回失败,系统性地测试每条失败路径)6。
书上一段 createNumbers(allocator) 函数连跑三次,分别喂三种分配器,输出一模一样——把内存引擎换了三遍,数据生成代码一行没动,这是这套接口设计成功的终极证明7。三种各司其职:
| 分配器 | 交换什么 | 什么时候用它 |
|---|---|---|
| DebugAllocator(0.16 前的旧名 GeneralPurposeAllocator) | 用速度换正确性:跟踪每次分配,deinit() 时报告泄漏;debug 构建里用哨兵值毒化已释放内存,抓二次释放和释放后使用 | 开发期默认 |
| ArenaAllocator | 用「单个对象不能释放」换吞吐:内部一大块内存只前进指针,无锁无元数据;arena.deinit() 一次扫回全部 | 有天然批量生命周期的负载:解析配置树用完就扔、每个请求建一堆对象响应完一起清 |
| FixedBufferAllocator | 把「子分配器」整个去掉:给你已有的(通常是栈上的)数组当撞针分配器,实现就是一个指针加长度 | 内存上限已知且小:嵌入式、热循环、序列化临时缓冲 |
图说:arena 那一行是重点——请求式系统(如 Web 服务)里「整棵对象图随请求结束瞬间销毁」,arena 把「我有没有漏 free 某个节点」这类问题整个消灭掉,因为这个问题根本不再被问起8。
DebugAllocator 还有两个细节值得记住:defer _ = gpa.deinit() 里的 _ = 只是压制「返回值没用」的编译警告,泄漏报告照样写 stderr;想在测试里把泄漏变成失败,就写 defer std.debug.assert(gpa.deinit() == .ok)9。
2.3 std.testing:测试住在源码里,泄漏算失败
Zig 的测试不是外部框架,是语言内置:test "名字" { … } 块就写在实现代码同一个文件里,只有执行 zig test 时才编译运行,生产二进制里被剥得干干净净10。断言语句三个就够用:expect(真值)、expectEqual(相等)、expectError(预期某个错误)。
测第 1 章的 zwhich 时冒出一个 0.16 特有的问题:main() 的 init.io 是运行时送的,测试块可没有运行时——书给的答案是 var t: std.Io.Threaded = .init_single_threaded 手动造一个最小 std.Io 实例11。测文件系统操作则用 testing.tmpDir(.{}) 开一个一次性隔离沙盒目录,defer tmp.cleanup() 保证不管测试通过、失败还是崩掉,整个目录被递归删除、不留痕迹12。
最有说服力的是可执行位那组断言:先用 fchmod 把测试文件设成 0o755(人人可执行),断言 isExecutable() 为真;再改成 0o644(主人可写、无人能执行),断言为假——同一个文件,翻转权限位,函数结论跟着翻转13。
泄漏检测是这节的真正主角:std.testing.allocator 跟踪测试期间每一次分配,测试块结束时强制审计,还有内存没还就整个测试判失败14。书上的实录极传神:
All 1 tests passed.
1 errors were logged.
1 tests leaked memory.
error: the following test command failed with exit code 1:图说:数学全对——第 10 个斐波那契数确实是 34——但内务错了。Zig 把资源泄漏当成和逻辑错误同级的缺陷:数学对、家务烂,测试照样红15。书给出的理由值得背下来:漏几个字节的斐波那契函数无害,但同样模式放在长跑的服务器里,就是一个「几小时几天内慢慢耗尽内存、直到操作系统杀掉进程」的慢性事故16。
2.4 文档:/// 注释直接变成网站
Zig 把文档生成做进了编译器:给声明写三斜杠注释(///,双斜杠 // 编译器直接忽略),然后跑
zig build-exe -femit-docs zwhich.zig编译器把所有三斜杠注释和函数签名抽出来,合成一个可搜索的静态 HTML 网站,文档永远和实现同步,因为它就是从实现里长出来的17。注释内容按 Markdown 解析:第一段当列表页摘要,后面的段落进详情页,还可以嵌 ```zig 代码块,让文档自带语法高亮的用法示例18。
一个反直觉的坑:生成的页面必须起一个本地 HTTP 服务(python3 -m http.server 8000)才能看——因为浏览器出于安全禁止 file:// 协议的页面发起 JavaScript fetch19。
2.5 build.zig.zon:用 URL 和哈希钉死「我依赖谁」
依赖清单 build.zig.zon(ZON 格式,长得和 Zig 的结构体字面量一样)声明的是 what:每个依赖不是一个模糊的名字,而是精确的 URL + 提交哈希 + 内容哈希20。
.url用git+https://…#<提交哈希>的写法,把依赖钉死在历史的某一个精确快照上,上游 main 分支怎么变都伤不到你;.hash是下载内容本身的密码学校验——哪怕 Git 服务器被攻陷、对同一个提交返回了不一样的数据,Zig 也会拒绝;.fingerprint是 64 位标识,帮全局缓存快速认出这个项目,不是内容校验。
图说:这三个字段合起来保证「今天能编译的项目,将来以同样的方式编译」——这就是可复现构建的定义21。
书还演示了 Zig 的报错驱动工作流:故意写一份缺字段的清单,zig build 会接连报「缺 fingerprint,建议值是多少」「URL 缺明确的 ref,建议补上哪个提交」「缺 hash,期望值是多少」——编译器替你把清单补全,你只要照抄建议值22。
2.6 build.zig:构建图是一段程序
build.zig 回答 how。它的骨架就这几行,但每一行都在向一张「构建步骤的有向无环图」加一个节点:
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{}); // 注册 -Dtarget=… 跨编译目标
const optimize = b.standardOptimizeOption(.{}); // 注册 -Doptimize=ReleaseFast 等优化档
const dep = b.dependency("包名", .{ .target = target, .optimize = optimize });
const exe_mod = b.createModule(.{ .root_source_file = b.path("src/main.zig"), … });
exe_mod.addImport("zuuid", dep.module("zuuid")); // 源码里的 @import("zuuid") 指向谁
const exe = b.addExecutable(.{ .name = "zuuid-cli", .root_module = exe_mod });
b.installArtifact(exe); // 编完拷到 zig-out/bin
… // 再注册一个 zig build run 步骤
}图说:standardTargetOptions() 和 standardOptimizeOption() 这两行是跨编译的入口——同一个构建脚本,换 -Dtarget= 参数就能给别的操作系统和 CPU 出二进制;addImport 把「源码里 import 用的名字」和「依赖实际暴露的模块名」解耦,两个名字可以不一样23。好处书上说得直白:过去要记一长串 -I… -L… -lsqlite3 的命令,现在全存在 build.zig 里,zig build run -- 参数 一条命令编译、链接、带参数运行24。
2.7 zuuid:16 个随机字节怎么变成版本 4 UUID
UUID(通用唯一标识符)是一个 128 位的数,设计上让分布式系统各自生成、几乎不可能撞车;版本 4 是完全靠随机数造的那版,数据库主键、事务 ID 都爱用它25。zuuid 的生成循环短到可以逐行走:
第 1 步 var uuid: [16]u8 = undefined;
第 2 步 io.randomSecure(&uuid) // 16 字节全部来自操作系统随机源
第 3 步 uuid[6] = (uuid[6] & 0x0f) | 0x40 // 第 7 字节高 4 位钉成 0100 → 版本号 4
第 4 步 uuid[8] = (uuid[8] & 0x3f) | 0x80 // 第 9 字节最高 2 位钉成 10 → 变体标识
第 5 步 按 8-4-4-4-12 分组打印成十六进制,单数位补零图说:书上真实输出 44cb2754-b58d-428c-8c95-d6d299fb650c——看第 3 组 428c 的开头是 4(版本),第 4 组 8c95 的开头是 8(二进制 1000,最高两位正是 10,变体)。第 3、4 步那两个按位操作就是干这个的:先与掩码清掉要控制的位,再或上规定值26。
第 2 步的选择也经过推敲:io.random() 内部是伪随机数发生器,快但种子之外算法确定;io.randomSecure() 每次都真调操作系统随机源(getrandom 或 arc4random),可能失败(所以带 try),用于数据库 ID、会话令牌这类场景是对的取舍27。
2.8 从工具到库:一次有名字的架构动作
zuuid 单文件版把两件事揉在一起:生成 UUID 的逻辑,和把它打印到屏幕的命令行界面。书把这次重构的原则说得很明白:这两件事不是同一件事——生成逻辑不依赖 stdin/stdout/参数,是可复用、可单测、可基准测试的单元;CLI 只是它的一个消费者28。
拆完之后走完整生命周期:zig init 建骨架 → 把生成算法填进 src/root.zig → 测试搬进去(zig build test)→ 修掉 fingerprint 报错 → 推到 GitHub → 在另一个项目 zuuid-cli 的 build.zig.zon 里引用它(就是第 2.5 节的报错驱动流程)→ zig build run 跑起来29。
收尾是一场真基准:用 hyperfine 把 ReleaseFast 模式的 zuuid-cli 和系统自带 uuidgen(1) 各跑几千次,zuuid 平均 856.1 微秒对 uuidgen 的 1.4 毫秒,快 1.64 倍30。这组数字是「Zig 值得用在工具链上」主张的全章唯一硬证据。
2.9 测试过滤与覆盖率:两个调试放大器
zig test 文件 --test-filter "math":按测试名模式匹配,只跑带 "math" 的那几个——改某个函数时不被无关测试的噪音和延迟打扰31;- 覆盖率:先
-femit-bin编出独立测试二进制,再用kcov跑它,生成把执行到的指令映射回源码行号的可视化报告——书上例子是maybeDouble()的else分支被标红:「翻倍」逻辑被验证了,「不翻倍」的行为还纯粹是理论32。过滤告诉你「选中的测试过了」,覆盖率告诉你「哪些代码根本没被跑到」。
3. 加厚:三个容易略过的好细节
3.1 计时器与微基准的写法
分配器基准用的是 0.16 的新计时习惯:std.Io.Timestamp.now(io, .awake) 取单调钟的当前时刻(.awake 表示「只算真实流逝的时间,不含进程睡着的部分」——性能测量要的就是这个),两个时刻相减用 .durationTo() 得 Duration,再取 .nanoseconds 变成可以累加求平均的整数33。每次试验重新打开文件、计时只包申请不包释放——把「要一块内存」的代价单独隔离,不让清理的波动混进数字34。
arena 的专用基准函数揭示了为什么接口统一还要单开一路:标准循环靠 free() 回收,而 arena 设计上忽略单个 free,标准循环会把内存攒到爆;arena 的回收姿势是 arena.reset(.retain_capacity)——把内部指针倒回起点、内存不还操作系统,第二轮起每轮复用同一块预分配内存,这才是游戏帧、请求处理器里「帧分配器」的真实用法35。
3.2 zuuid 打印的两位补零
UUID 十六进制打印有个细节:{x:0>2} 里的 0>2 是「宽度 2、右对齐、不足补零」——单数位十六进制必须补前导零,否则 16 字节会打印成不足 32 字符、分组全错位;长格式串用编译期 ++ 拼接换行,零运行时代价36。大写的 -u 版只是把 {x} 换成 {X},一个开关两条格式路径37。
3.3 hyperfine 跑的是什么
zuuid 那场 1.64 倍的对比,比的是单次冷执行:每次运行都从进程启动算到退出(856 µs 对 1.4 ms,各跑了几千次取平均)。它证明的是「Zig 写的命令行工具和系统自带工具同场不落下风」,不是吞吐量——真要比吞吐,得让进程活着连发请求38。读任何「快了多少倍」之前,先问这句倍率量的到底是什么形状。
3.4 构建步骤是一张有向无环图
build.zig 的每一行都在往一张图里加节点:standardTargetOptions() 注册 -Dtarget= 命令行旗标并解析成交叉编译约束;b.dependency() 查清单、把同一套 target/optimize 传进依赖(外库与主程序必须同架构同优化档编译,防不匹配);addImport 把源码里的 import 名映射到依赖实际暴露的模块名,两个名字可以不同;installArtifact 注册"副作用"步骤把二进制拷进 zig-out/bin;addRunArtifact 造一个虚拟运行命令,b.args 把 zig build run -- 参数 转发给程序本身39。图的好处:zig build run 一条命令把"编译 → 安装 → 带参运行"整条依赖链走一遍,每步只在脏的时候重跑。
3.5 测试过滤与覆盖率各管一半
--test-filter "math" 按测试名模式匹配,只跑带 "math" 的那几个——改某个函数时不被无关测试的噪音和延迟打扰40。覆盖率走另一条路:-femit-bin 先编出独立测试二进制,再用 kcov 跑它,生成把执行指令映射回源码行的可视化报告——书上的例子是 maybeDouble() 的 else 分支被标红:「翻倍」逻辑被验证了,「不翻倍」的行为还纯粹是理论41。过滤告诉你"选中的过了",覆盖告诉你"哪些根本没被跑到"——两半拼起来才是完整的验证画面。
4. 作者的判断与证据
- 判断: 「分配器策略是架构决策,不是实现细节」——写在调用点的选择(arena/调试/栈缓冲)在代码里可见、在评审里可议、改起来不碰解析器本身42。证据:同一份
createNumbers()换三种分配器输出不变。 - 判断: 泄漏是「无声杀手」,资源泄漏应当与逻辑错误同级处理43。证据:
std.testing.allocator的强制审计 + 书上「TCP 代理每连接漏一块缓冲区,几小时后 OS 杀进程」的场景推演。 - 判断: zuuid 比 uuidgen 快得多30。证据:hyperfine 双基准,3358 次对 2015 次运行。局限:只测了单进程冷启动级别的微基准,没测吞吐场景。
- 推测: 「构建系统是基础设施」这个框是全章的价值观陈述,不是可证命题;支撑它的是结尾那串排比(负债、定时炸弹、维护负担),修辞多于论证。
4. 边界与局限
- 覆盖率工具是外置的:kcov、hyperfine 都要另外装,Zig 自己只出测试二进制32。
- 跨平台条件编译是显式标注的:fchmod 那组断言包在
if (os != .windows)里,Windows 用访问控制列表而不是权限位——书正确处理了,但也意味着这套测试在 Windows 上覆盖更少13。 - 依赖管理是「去中心化」的:没有 crates.io 那样的中央注册表,依赖就是 URL+哈希,上游仓库消失或改历史会让老项目装不上21。
- zuuid 的实现是教学级:没有处理时钟回拨、没有 v1/v5,只做了 v425。
5. 可带走的
- 字符串格式化:
{s}给人看,{any}给调试看;栈缓冲用bufPrint,堆上用allocPrint且必须defer free。 - 分配器三选一的决策树:开发期 DebugAllocator(报泄漏)、批量生命周期 ArenaAllocator(免逐个 free)、上限已知 FixedBufferAllocator(零开销)。
- 想把分配器换策略?函数只收
std.mem.Allocator,调用点换实参,一行的事。 - 测试块和实现住同一个文件;
std.testing.allocator让「数学对、内存漏」的测试直接红。 - 测文件系统用
testing.tmpDir沙盒,defer cleanup保证不留痕。 - 文档从
///注释长出来,-femit-docs一键出网站;本地看要起 HTTP 服务,file://打不开。 build.zig.zon三件套:URL 钉提交、hash 验内容、fingerprint 认项目;报错驱动,缺什么编译器告诉你。build.zig五步:目标/优化档 → 依赖 → 模块 → 工件 → 步骤;zig build run -- 参数一次跑通。- 学一个库的生命周期:工具拆库 → 测试进库 → GitHub 托管 → 别人 zon 引用 → hyperfine 对上系统工具。
--test-filter缩小循环,kcov 找红色死区——过滤管「跑哪些」,覆盖管「漏了谁」。
6. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
{any} 与 {s} 的区别 | Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution | text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:361(搜「technically correct」) |
| allocPrint / bufPrint 取舍 | Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution | text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:322(搜「NoSpaceLeft」) |
| 分配器接口与调用方决定权 | Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution | text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:475(搜「only party with enough context」) |
| 日志/故障注入分配器 | Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution | text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:617(搜「logging allocator」) |
| 三种分配器同一函数 | Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution | text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:1037(搜「swapped the underlying」) |
| 测试块住在源码里 | Writing unit tests with std.testing | text/23-fm-writing-unit-tests-with-std-testing.txt:29(搜「same source files」) |
| tmpDir 沙盒 | Writing tests for zwhich | text/24-fm-writing-tests-for-zwhich.txt:120(搜「strictly isolated temporary directory」) |
| 0o755/0o644 可执行位翻转 | Writing tests for zwhich | text/24-fm-writing-tests-for-zwhich.txt:262(搜「0o755」) |
| 泄漏测试实录 | Detecting memory leaks automatically during tests | text/25-fm-detecting-memory-leaks-automatically-during-test.txt:111(搜「All 1 tests passed」) |
| 泄漏=临界缺陷 | Detecting memory leaks automatically during tests | text/25-fm-detecting-memory-leaks-automatically-during-test.txt:141(搜「critical defects」) |
| /// 注释生成文档 | Generating documentation (-femit-docs) | text/26-fm-generating-documentation-femit-docs.txt:15(搜「triple-slash」) |
| file:// 看不了文档 | Generating documentation (-femit-docs) | text/26-fm-generating-documentation-femit-docs.txt:261(搜「security reasons」) |
| zon 的 URL+哈希钉死 | Managing dependencies with build.zig.zon | text/28-fm-managing-dependencies-with-build-zig-zon.txt:110(搜「cryptographic integrity check」) |
| build.zig 五步骨架 | Understanding build.zig | text/29-fm-understanding-build-zig.txt:76(搜「Directed Acyclic Graph」) |
| UUID v4 定义 | Compiling a library to create an executable binary | text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:176(搜「constructed almost entirely from random numbers」) |
| 两个按位操作钉版本与变体 | Compiling a library to create an executable binary | text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:392(搜「precise bitwise operations」) |
| randomSecure 与 random 之别 | Compiling a library to create an executable binary | text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:390(搜「PRNG seeded from a secure source」) |
| 工具拆库的原则 | Compiling a library to create an executable binary | text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:453(搜「two distinct concerns」) |
| zuuid 对 uuidgen 基准 | Benchmarking zuuid against uuidgen | text/35-fm-benchmarking-zuuid-against-uuidgen.txt:24(搜「1.4 ms」) |
| 测试过滤 | Test filtering and coverage analysis | text/36-fm-test-filtering-and-coverage-analysis.txt:48(搜「test-filter」) |
| kcov 覆盖死区 | Test filtering and coverage analysis | text/36-fm-test-filtering-and-coverage-analysis.txt:81(搜「else」) |
| 「基础设施」价值观收尾 | Test filtering and coverage analysis | text/36-fm-test-filtering-and-coverage-analysis.txt:165(搜「operational foundations」) |
本章拆解依据原书第 2 章(含各小节)。每条出处可用 node scripts/book-verify.mjs systems-programming-with-zig 回核。
Footnotes
-
出处:「Test filtering and coverage analysis」第 165 段(
text/36-fm-test-filtering-and-coverage-analysis.txt:165,搜「operational foundations」)。 ↩ -
出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 83 段(
text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:83,搜「compile-time logic」)。 ↩ -
出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 361 段(
text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:361,搜「technically correct」)。 ↩ -
出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 322 段(
text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:322,搜「NoSpaceLeft」)。 ↩ -
出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 475 段(
text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:475,搜「only party with enough context」)。 ↩ -
出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 617 段(
text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:617,搜「logging allocator」)。 ↩ -
出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 1037 段(
text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:1037,搜「swapped the underlying」)。 ↩ -
出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 561 段(
text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:561,搜「natural batch lifetime」)。 ↩ -
出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 892 段(
text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:892,搜「assert that no leaks occurred」)。 ↩ -
出处:「Writing unit tests with std.testing」第 29 段(
text/23-fm-writing-unit-tests-with-std-testing.txt:29,搜「same source files」)。 ↩ -
出处:「Writing tests for zwhich」第 56 段(
text/24-fm-writing-tests-for-zwhich.txt:56,搜「std.Io.Threaded」)。 ↩ -
出处:「Writing tests for zwhich」第 120 段(
text/24-fm-writing-tests-for-zwhich.txt:120,搜「strictly isolated temporary directory」)。 ↩ -
出处:「Writing tests for zwhich」第 262 段(
text/24-fm-writing-tests-for-zwhich.txt:262,搜「0o755」)。 ↩ ↩2 -
出处:「Detecting memory leaks automatically during tests」第 15 段(
text/25-fm-detecting-memory-leaks-automatically-during-test.txt:15,搜「mandatory audit」)。 ↩ -
出处:「Detecting memory leaks automatically during tests」第 111 段(
text/25-fm-detecting-memory-leaks-automatically-during-test.txt:111,搜「All 1 tests passed」)。 ↩ -
出处:「Detecting memory leaks automatically during tests」第 155 段(
text/25-fm-detecting-memory-leaks-automatically-during-test.txt:155,搜「slow-motion failure」)。 ↩ -
出处:「Generating documentation (-femit-docs)」第 15 段(
text/26-fm-generating-documentation-femit-docs.txt:15,搜「triple-slash」)。 ↩ -
出处:「Generating documentation (-femit-docs)」第 66 段(
text/26-fm-generating-documentation-femit-docs.txt:66,搜「Markdown」)。 ↩ -
出处:「Generating documentation (-femit-docs)」第 261 段(
text/26-fm-generating-documentation-femit-docs.txt:261,搜「security reasons」)。 ↩ -
出处:「Managing dependencies with build.zig.zon」第 110 段(
text/28-fm-managing-dependencies-with-build-zig-zon.txt:110,搜「cryptographic integrity check」)。 ↩ -
出处:「Managing dependencies with build.zig.zon」第 100 段(
text/28-fm-managing-dependencies-with-build-zig-zon.txt:100,搜「always fetch that exact snapshot」)。 ↩ ↩2 -
出处:「Using our zuuid library」第 48 段(
text/34-fm-using-our-zuuid-library.txt:48,搜「error-driven workflow」)。 ↩ -
出处:「Understanding build.zig」第 76 段(
text/29-fm-understanding-build-zig.txt:76,搜「Directed Acyclic Graph」)。 ↩ -
出处:「Compiling a library to create an executable binary」第 94 段(
text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:94,搜「avoid retyping」)。 ↩ -
出处:「Compiling a library to create an executable binary」第 176 段(
text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:176,搜「constructed almost entirely from random numbers」)。 ↩ ↩2 -
出处:「Compiling a library to create an executable binary」第 392 段(
text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:392,搜「precise bitwise operations」)。 ↩ -
出处:「Compiling a library to create an executable binary」第 390 段(
text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:390,搜「PRNG seeded from a secure source」)。 ↩ -
出处:「Compiling a library to create an executable binary」第 453 段(
text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:453,搜「two distinct concerns」)。 ↩ -
出处:「Using GitHub」第 17 段(
text/31-fm-using-github.txt:17,搜「zig init」)与「Using our zuuid library」第 341 段(text/34-fm-using-our-zuuid-library.txt:341,搜「zig build run」)。 ↩ -
出处:「Benchmarking zuuid against uuidgen」第 24 段(
text/35-fm-benchmarking-zuuid-against-uuidgen.txt:24,搜「1.4 ms」)。 ↩ ↩2 -
出处:「Test filtering and coverage analysis」第 48 段(
text/36-fm-test-filtering-and-coverage-analysis.txt:48,搜「test-filter」)。 ↩ -
出处:「Test filtering and coverage analysis」第 79 段(
text/36-fm-test-filtering-and-coverage-analysis.txt:79,搜「flag the」)。 ↩ ↩2 -
出处:「Reading Little-Endian integers from raw bytes」第 742 段(
text/41-fm-reading-little-endian-integers-from-raw-bytes.txt:742,搜「std.Io.Timestamp.now」)。 ↩ -
出处:「Reading Little-Endian integers from raw bytes」第 810 段(
text/41-fm-reading-little-endian-integers-from-raw-bytes.txt:810,搜「cost of asking」)。 ↩ -
出处:「Reading Little-Endian integers from raw bytes」第 836 段(
text/41-fm-reading-little-endian-integers-from-raw-bytes.txt:836,搜「retain_capacity」)。 ↩ -
出处:「Compiling a library to create an executable binary」第 422 段(
text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:422,搜「leading zero」)。 ↩ -
出处:「Compiling a library to create an executable binary」第 412 段(
text/30-fm-compiling-a-library-to-create-an-executable-bina.txt:412,搜「uppercase」)。 ↩ -
出处:「Benchmarking zuuid against uuidgen」第 25 段(
text/35-fm-benchmarking-zuuid-against-uuidgen.txt:25,搜「2015 runs」)。 ↩ -
出处:「Understanding build.zig」第 100 段(
text/29-fm-understanding-build-zig.txt:100,搜「register command-line flags」)。 ↩ -
出处:「Test filtering and coverage analysis」第 48 段(
text/36-fm-test-filtering-and-coverage-analysis.txt:48,搜「test-filter」)。 ↩ -
出处:「Test filtering and coverage analysis」第 79 段(
text/36-fm-test-filtering-and-coverage-analysis.txt:79,搜「flag the」)。 ↩ -
出处:「Chapter 2: Systems Infrastructure: Builds, Testing, and Distribution」第 627 段(
text/22-ch02-chapter-2-systems-infrastructure-builds-testing-.txt:627,搜「architectural decision」)。 ↩ -
出处:「Writing tests for zwhich」第 382 段(
text/24-fm-writing-tests-for-zwhich.txt:382,搜「silent killers」)。 ↩