Zig ELF 链接器改进开发日志
摘要
新的 Zig ELF 链接器现在支持外部库和 C 源码的快速增量编译,在 x86_64 Linux 上能够实现毫秒级重建。
暂无内容
查看缓存全文
缓存时间: 2026/05/30 19:28
# 开发日志 ⚡ Zig 编程语言
来源:https://ziglang.org/devlog/2026/
此页面收录了 main 分支 Zig 近期变更的精选列表。
本页面包含 2026 年的条目。其他年份请访问开发日志归档页面 (https://ziglang.org/devlog/)。
2026 年 5 月 30 日
## ELF 链接器改进 (https://ziglang.org/devlog/2026/#2026-05-30)
作者:Matthew Lugg
过去几周我一直在改进我们新的 ELF 链接器,该链接器在 Zig 0.16.0 中首次亮相。在 0.16.0 发布时,这个链接器实现还处于相当早期的阶段,仅支持链接纯 Zig 代码,不涉及任何外部库(包括 libc)——这就是它当时(并且现在仍然)默认禁用的原因(可通过 `-fnew-linker` 启用)。不过,自首次发布以来,我们已经取得了相当大的进展!
这是一个不错的里程碑——根据我最近的 PR (https://codeberg.org/ziglang/zig/pulls/35533),新的 ELF 链接器已经能够构建启用了 LLVM 和 LLD 库的自托管 Zig 编译器,这需要大量底层特性的支持。
```
[mlugg@nebula master]$ # 使用新链接器构建 Zig 编译器:
[mlugg@nebula master]$ zig build -Dno-lib -Dnew-linker -Denable-llvm
[mlugg@nebula master]$ # 用那个编译器构建带有 LLVM 和 LLD 的项目:
[mlugg@nebula master]$ ./zig-out/bin/zig build-exe ~/hello.zig -fllvm -flld
[mlugg@nebula master]$ ./hello
Hello, World!
[mlugg@nebula master]$
```
当然,ELF 链接器本身未必是世界上最令人兴奋的东西,因此这个新链接器的核心卖点是支持快速增量编译。经过最近的增强,现在(在 x86_64 Linux 上)可以在链接外部库、C 源码等情况下执行增量重构建——而且没有任何额外的性能开销!以下是我在 Andrew 的俄罗斯方块克隆 (https://github.com/andrewrk/tetris) 上测试的片段:
对 Andrew 的俄罗斯方块克隆进行一些简单修改,每次构建大约只需 30 毫秒。哦,快速增量重构建在 Zig 编译器本身上也运行良好:
```
[mlugg@nebula master]$ zig build -Dno-lib -Denable-llvm -fincremental --watch
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 36s
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 244ms
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 228ms
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 288ms
Build Summary: 4/4 steps succeeded
install success
└─ install zig success
└─ compile exe zig Debug native success 283ms
```
目前这个链接器实现最大的缺失功能是尚不支持为 Zig 代码生成 DWARF 调试信息——这绝对是我接下来的首要任务。但即使没有这个支持,即时重构建的实用性也已经非常惊人,例如在你大量使用打印调试的任何场景中。
如果你正在使用 Zig 的 master 分支并且运行在 x86_64 Linux 上,不妨尝试使用新的 ELF 链接器进行增量编译——如果之前它对你的项目不工作的话!我预计许多代码库已经可以和它很好地配合,从而解锁毫秒级重构建的能力。当然,如果你遇到任何 bug,请务必提交 issue (https://codeberg.org/ziglang/zig/issues)。
如果你目前仍坚持使用 Zig 的标签发布版,也请放心——正如 Andrew 在上一期开发日志中提到的,Zig 0.17.0 即将发布,所以你很快也能尝试这个功能了!
2026 年 5 月 26 日
## 构建系统重构 (https://ziglang.org/devlog/2026/#2026-05-26)
作者:Andrew Kelley
大分支刚刚合并:将 maker 进程与 configurer 进程分离 (https://codeberg.org/ziglang/zig/pulls/35428)
这篇开发日志本质上是对即将发布的版本说明的预览,但同时也是一种提前通知,希望帮助测试新功能并提供反馈,以指导 Zig 项目的未来发展方向。
之前,`build.zig` 文件与构建系统的实现一起被编译成一个臃肿的进程,运行在 Debug 模式下。在 `build.zig` 逻辑完成构建图的内存构建后,“构建运行器”代码会执行它。
现在,`build.zig` 文件被编译成一个小的进程(“configurer”),运行在 Debug 模式下。在该逻辑完成构建图的内存构建后,它会序列化为一个二进制配置文件。父进程 `zig build` 知道这个文件,并会缓存它以便下次使用。在等待所有这些的同时,它会异步编译构建图执行进程(“maker”),运行在 Release 模式下。一旦配置文件可用且 maker 进程编译完成,就会执行 maker 进程,并将配置文件传递给它。由于全局缓存的存在,maker 进程每个 `zig 版本` 只需编译一次。然后 maker 进程执行构建图,该构建图包含在序列化的配置文件中。
此更改的主要动机是让 `zig build` 更快,体现在三个方面:
1. 只有用户的 `build.zig` 逻辑会在每次更改时重新编译,而不是整个构建系统都重新编译。随着我们引入 `--watch`、`--fuzz` 和 `--webui`,这一点变得越来越有价值。构建系统可以增加更多功能,而不会让 `zig build` 花费更长时间。
2. 现在构建系统可以在知道不会有任何变化时完全跳过重新运行 `build.zig` 逻辑,例如如果你向 `zig build` 命令行添加 `-freference-trace`,它现在会避免冗余重新运行你的 `build.zig` 逻辑,而是使用上次相同的配置。
3. 现在实际执行构建图的进程是在启用优化的情况下编译的。
为了演示第 2 点和第 3 点,以下是运行 `zig build --help` 前后的差异:
```
Benchmark 1 (34 runs): master/zig build -h
measurement mean ± σ min ... max outliers delta
wall_time 150ms ± 5.52ms 145ms ... 165ms 4 (12%) 0%
peak_rss 84.8MB ± 275KB 84.2MB ... 85.1MB 0 ( 0%) 0%
cpu_cycles 593M ± 4.01M 588M ... 608M 2 ( 6%) 0%
instructions 995M ± 52.5K 995M ... 995M 0 ( 0%) 0%
cache_references 25.8M ± 165K 25.4M ... 26.1M 0 ( 0%) 0%
cache_misses 651K ± 20.1K 619K ... 697K 0 ( 0%) 0%
branch_misses 918K ± 7.44K 906K ... 935K 0 ( 0%) 0%
Benchmark 2 (348 runs): branch/zig build -h
measurement mean ± σ min ... max outliers delta
wall_time 14.3ms ± 744us 13.2ms ... 23.3ms 8 ( 2%) ⚡- 90.4% ± 0.4%
peak_rss 78.5MB ± 562KB 77.1MB ... 81.4MB 7 ( 2%) ⚡- 7.4% ± 0.2%
cpu_cycles 24.1M ± 821K 22.8M ... 27.1M 3 ( 1%) ⚡- 95.9% ± 0.1%
instructions 43.7M ± 23.8K 43.7M ... 43.8M 56 (16%) ⚡- 95.6% ± 0.0%
cache_references 1.46M ± 14.6K 1.40M ... 1.50M 19 ( 5%) ⚡- 94.3% ± 0.1%
cache_misses 142K ± 4.87K 127K ... 157K 2 ( 1%) ⚡- 78.1% ± 0.4%
branch_misses 126K ± 1.37K 120K ... 129K 12 ( 3%) ⚡- 86.3% ± 0.1%
```
之所以效果显著,是因为以前每次执行 `zig build` 命令时都会运行 `build.zig` 逻辑,而现在构建系统使用的是缓存的、序列化的配置。
除了性能之外,我预计像 ZLS 这样的第三方工具也能从使用序列化配置文件而不是维护构建运行器的分支中受益。
这个变更集对 Zig 构建系统的内部机制进行了大量重构,但从 API 角度来看,除了上述 PR 中提到的几点例外,大部分是非破坏性的。
对大多数人来说,我猜测他们遇到的主要破坏性变更如下:
```
if (b.args) |args| {
run_cmd.addArgs(args);
}
```
⬇️
```
run_cmd.addPassthruArgs();
```
这移除了构建脚本的一项能力,因为它们无法再观察那些参数。作为交换,当更改这些参数时,构建脚本无需重新从源码构建。
如果你希望影响 Zig 的发展方向,现在正是升级你的项目到开发版本并尝试这些更改的好时机。我们将在未来几周内发布 0.17.0。但是,如果你没有时间,并且发现 0.17.0 破坏了你的构建,也别担心,0.17.1 标签仍有很多机会进行修复。
2026 年 4 月 8 日
## 使用 LLVM 进行增量编译 (https://ziglang.org/devlog/2026/#2026-04-08)
作者:Matthew Lugg
在上个月合并了我的类型解析更改 (https://ziglang.org/devlog/2026/#2026-03-10) 之后,我花了一些时间在个人项目上,但最近我还是抽出时间对 LLVM 代码生成后端做了一些改进。这涉及多个不同目标的增强,但一个不错的用户可见变化是我成功让增量编译在 LLVM 后端下工作了。
遗憾的是,这无法加速令人头疼的 LLVM Emit Object 阶段——那段时间完全取决于 LLVM。然而,增量编译确实有助于最小化在 Zig 编译器本身代码上花费的时间,这意味着如果你的代码有编译错误(因此会跳过“LLVM Emit Object”),你通常能很快得到这些错误。(当然,它在成功构建时也会带来一点速度提升。)
该支持现在已在 master 分支构建中可用,并将包含在 0.16.0 版本中(我们很快就会打标签)。
对于尚未尝试过的人,特别是如果你正在使用 Zig 的 master 分支,请务必通过向 `zig build` 传递 `-fincremental --watch` 来尝试增量编译!Zig 核心团队在将近一年的时间里已经从增量编译中受益,我们也听到了用户的积极反馈。该功能目前相对稳定,人们常常惊讶于只需将编译错误从几秒缩短到几毫秒就能省下多少时间。
我个人并没有真正使用过 LLVM 后端的增量编译,但现在 CI 中所有的增量测试覆盖都已为 LLVM 后端启用,并且我收到了用户的积极反馈,所以绝对值得一试。一如既往,如果你在增量编译中遇到 bug,请尽可能报告它们!
谢谢,希望你觉得这有用 :)
2026 年 3 月 10 日
## 类型解析重新设计,附带语言变化 (https://ziglang.org/devlog/2026/#2026-03-10)
作者:Matthew Lugg
今天,经过两(或者说三)个月的工作后,我合并了一个 30,000 行的 PR (https://codeberg.org/ziglang/zig/pulls/31403)。这个分支的目标是将 Zig 编译器内部的类型解析逻辑重构成更合理、更直接的设计。对我个人来说,这是一个非常令人兴奋的变化,因为它让我清理了大量编译器内部代码,同时也带来了一些你可能感兴趣的用户可见变化。
首先,Zig 编译器现在对类型字段的解析更加惰性:如果类型从未被初始化,那么 Zig 就无需关心该类型“长什么样”。这对于那些同时充当命名空间的类型(现代 Zig 中的常见模式)非常重要。例如,当使用 `std.Io.Writer` 时,你肯定不希望编译器也拉入 `std.Io` 中的一堆代码!这里有一个简单的例子:
```
const Foo = struct {
bad_field: @compileError("我是邪恶字段,哈哈哈"),
const something = 123;
};
comptime {
_ = Foo.something; // `Foo` 仅用作命名空间
}
```
以前,这段代码会触发编译错误。现在,它可以正常编译,因为 Zig 实际上从未查看 `@compileError` 调用。
另一个改进是“依赖循环”的错误体验。以前在 Zig 中遇到依赖循环编译错误的人都知道,错误消息完全没用——但现在情况变了!如果你遇到一个(现在出现的概率也比以前略低),你会得到一条详细的错误消息,准确告诉你依赖循环来自哪里。来看看这个:
```
const Foo = struct { inner: Bar };
const Bar = struct { x: u32 align(@alignOf(Foo)) };
comptime {
_ = @as(Foo, undefined);
}
```
```
$ zig build-obj repro.zig
error: 长度为 2 的依赖循环
repro.zig:1:29: note: 类型 'repro.Foo' 依赖于此处声明的字段类型 'repro.Bar'
const Foo = struct { inner: Bar };
^~~
repro.zig:2:44: note: 类型 'repro.Bar' 依赖于此处的对齐查询类型 'repro.Foo'
const Bar = struct { x: u32 align(@alignOf(Foo)) };
^~~
note: 消除其中任意一个依赖即可打破循环
```
当然,依赖循环可能比这复杂得多,但在我测试的每个案例中,错误消息都提供了足够的信息,让人轻松看出问题所在。
此外,这个 PR 对 Zig 编译器的“增量编译”功能做了很大改进。简单来说,它修复了大量已知错误,尤其是“过度分析”问题(即增量更新做了比必要更多的工作,有时甚至多很多)应该基本被消除了——从而在许多情况下显著加速增量编译!如果你还没试过,不妨尝试一下增量编译 (https://ziglang.org/download/0.15.1/release-notes.html#Incremental-Compilation):这真的是一种很棒的开发体验。这绝对是我最兴奋的改进,也是推动这个变化的主要原因之一。
这个 PR 还带来了许多其他变化——几十个 bug 修复、一些小的语言变化(大多是相当小众的),以及编译器性能改进。这里无法一一列举,但如果你有兴趣了解更多,可以在 Codeberg 上查看这个 PR (https://codeberg.org/ziglang/zig/pulls/31403)——当然,如果你遇到任何 bug,请提交 issue。祝编码愉快!
2026 年 2 月 13 日
## io_uring 和 Grand Central Dispatch 的 std.Io 实现已合并 (https://ziglang.org/devlog/2026/#2026-02-13)
作者:Andrew Kelley
随着 0.16.0 发布周期接近尾声,Jacob 一直在努力工作,将 `std.Io.Evented` 更新到最新的 API 变化:
- io_uring 实现 (https://codeberg.org/ziglang/zig/pulls/31158)
- Grand Central Dispatch 实现 (https://codeberg.org/ziglang/zig/pulls/31198)
两者都基于用户空间堆栈切换,有时称为“纤程”、“有栈协程”或“绿色线程”。
它们现在**可用于实验**,通过使用 `std.Io.Evented` 构建应用程序。它们应被视为**实验性**,因为在可靠和稳健使用之前还有一些重要的后续工作要做:
- 更好的错误处理 (https://codeberg.org/ziglang/zig/issues/31199)
- 移除此项目
- 诊断在编译器中使用 `IoMode.evented` 时出现的意外性能下降
- 还有几个函数尚未实现 (https://codeberg.org/ziglang/zig/issues/31200)
- 需要更多的测试覆盖率
相似文章
Zig 构建速度正在提升
Zig 0.15 相比 0.14 在编译时性能有显著提升,构建脚本编译时间从约 7 秒降至约 1.7 秒,完整构建时间从 41 秒降至 32 秒,且仍使用 LLVM。本文重点介绍了自托管后端和增量编译方面的进展。
Zig ELF 二进制文件代码高尔夫 (2025)
深入技术探讨如何缩小 Zig ELF 二进制文件的大小,从 2180K 缩减至 500 字节以下,通过去除调试信息、切换到 ReleaseSmall 以及使用 freestanding 目标。
构建系统重构
Zig 构建系统已经重构,将配置器和制造器进程分离,支持缓存、发布模式编译,并且'zig build'命令速度提升高达90%。这一变化提高了性能,并允许构建系统在不减速的情况下增加功能。
用 Zig 写一个 C 编译器
一位开发者记录了用 Zig 语言、按照 Nora Sandler 的教程系列构建名为 paella 的 C 编译器的全过程。
Zig 0.16.0 发布说明:"Juicy Main"
Zig 0.16.0 发布了一个名为 'Juicy Main' 的新特性,它为 main() 函数提供了依赖注入功能,方便地访问分配器、IO、环境变量和命令行参数。