0.14.0
2025/3/5,0.14.0
发布,历时 9 个月,有 251 位贡献者,一共进行了 3467 次提交!
除了大量的构建系统升级、语言变更和目标支持增强之外,此版本还朝着我们的两项长期目标迈进——增量编译和更快的 x86 后端 - 都专注于 减少编辑/编译/调试周期延迟。
目标支持
此版本的 Zig 的一个重要特性是改进目标支持状况,Zig 可以正确交叉编译和运行的目标列表已增加很多。 Zig 可以为其构建程序的目标见下表,zig-bootstrap README 涵盖了 Zig 编译器本身可以轻松交叉编译以运行的目标。
针对特性目标修复的完整列表太长,不在列出,但简言之,如果过去尝试针对 arm/thumb
、mips/mips64
、powerpc/powerpc64
、riscv32/riscv64
或 s390x
并遇到工具链问题、缺少标准库支持或看似无意义的崩溃,那么现在大概率 Zig 0.14.0
可以正常工作。
目标三重变化
Zig 对理解的目标三重做了一些更改:
arm-windows-gnu
已被替换为thumb-windows-gnu
,以反映 Windows 仅支持 Thumb-2 模式的事实。armeb-windows-gnu
和aarch64_be-windows-gnu
已被移除,因为 Windows 不支持大端。- 添加了
thumb[eb]-linux-musleabi[hf]
以使用 musl libc 针对纯 Thumb 模式。 - 添加了
mips[el]-linux-musleabi
以使用软浮点 ABI 和 musl libc 针对 32 位 mips。 mips[el]-linux-musl
已重命名为mips[el]-linux-musleabihf
以明确它们针对硬浮点 ABI。mips64[el]-linux-musl
已重命名为mips64[el]-linux-muslabi64
以与mips64[el]-linux-gnuabi64
保持一致。- 添加了
mips64[el]-linux-muslabin32
以使用 32 位指针和 musl libc 针对 64 位 mips。 - 添加了
powerpc-linux-musleabi
以使用软浮点 ABI 和 musl libc 针对 32 位 powerpc。 powerpc-linux-musl
已重命名为powerpc-linux-musleabihf
以明确它针对硬浮点 ABI。- 添加了
x86_64-linux-muslx32
以使用 32 位指针和 musl libc 针对 64 位 x86。
分层系统
Zig 对各种目标的支持水平大致分为四个层级,其中 Tier 1 是最高的。请注意,目前,即使是一些 Tier 1 目标也可能有一些禁用的测试,因为我们正在努力实现 100% 的测试通过率。
Tier 1
- 所有非实验性语言功能都已知可以正常工作。
- 编译器可以在不依赖 LLVM 的情况下为此目标生成机器代码,同时在功能支持方面与 LLVM 相当。
- 即使在交叉编译时,libc 也可用于此目标。
Tier 2
- 标准库的跨平台抽象具有此目标的实现。
- 此目标具有调试信息功能,因此在断言失败和崩溃时会生成堆栈跟踪。
- CI 机器会在每次提交到主分支时自动构建和测试此目标。
Tier 3
- 编译器可以通过依赖外部后端(如 LLVM)为此目标生成机器代码。
- 链接器可以为此目标生成目标文件、库和可执行文件。
Tier 4
- 编译器可以通过依赖外部后端(如 LLVM)为此目标生成汇编源代码。
- 此目标可能被 LLVM 视为实验性目标,在这种情况下,需要从源代码构建 LLVM 和 Zig 才能使用它。
支持列表
在下表中,🟢 表示完全支持,🔴 表示不支持,🟡 表示部分支持,例如仅支持某些子目标,或存在一些显著的已知问题。❔ 表示状态大致未知,通常是因为该目标很少被使用。将鼠标悬停在其他图标上以查看详细信息。
目标 | 层级 | 语言特性 | 标准库 | 代码生成 | 链接器 | 调试信息 | libc | CI |
---|---|---|---|---|---|---|---|---|
x86_64-linux | 1 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🟢 | 🟢 |
aarch64[_be]-linux | 2 | 🟢 | 🟢 | 🖥️🛠️ | 🟢 | 🟢 | 🟢 | 🟢 |
aarch64-macos | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
aarch64-windows | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
arm[eb]-linux | 2 | 🟢 | 🟢 | 🖥️🛠️ | 🟢 | 🟢 | 🟢 | 🟢 |
powerpc-linux | 2 | 🟢 | 🟢 | 🖥️ | 🟡 | 🟢 | 🟢 | 🟡 |
powerpc64-linux | 2 | 🟢 | 🟢 | 🖥️ | 🟡 | 🟢 | 🟡 | 🟡 |
powerpc64le-linux | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
wasm32-wasi | 2 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🟢 | 🟢 |
x86-linux | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
x86-windows | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
x86_64-macos | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
x86_64-windows | 2 | 🟢 | 🟢 | 🖥️ | 🟢 | 🟢 | 🟢 | 🟢 |
aarch64-freebsd | 3 | ❔ | ❔ | 🖥️🛠️ | 🟢 | 🟢 | 🔴 | 🔴 |
aarch64[_be]-netbsd | 3 | ❔ | ❔ | 🖥️🛠️ | 🟢 | ❔ | 🔴 | 🔴 |
aarch64-openbsd | 3 | ❔ | ❔ | 🖥️🛠️ | 🟢 | ❔ | 🔴 | 🔴 |
hexagon-linux | 3 | 🟡 | 🟡 | 🖥️ | 🟢 | 🔴 | 🔴 | 🔴 |
loongarch64-linux | 3 | 🟡 | 🟡 | 🖥️ | 🟢 | 🔴 | 🟢 | 🔴 |
mips[el]-linux | 3 | 🟢 | 🟢 | 🖥️ | 🟢 | 🔴 | 🟢 | 🔴 |
mips64[el]-linux | 3 | 🟢 | 🟢 | 🖥️ | 🟡 | 🔴 | 🟢 | 🔴 |
riscv32-linux | 3 | 🟢 | 🟢 | 🖥️ | 🟢 | 🔴 | 🟢 | 🟢 |
riscv64-linux | 3 | 🟢 | 🟢 | 🖥️🛠️ | 🟢 | 🔴 | 🟢 | 🟢 |
s390x-linux | 3 | 🟢 | 🟢 | 🖥️ | 🟢 | 🔴 | 🟢 | 🟢 |
sparc64-linux | 3 | ❔ | 🟢 | 🖥️🛠️ | 🟢 | ❔ | 🟢 | 🔴 |
sparc64-solaris | 3 | ❔ | ❔ | 🖥️🛠️ | 🟢 | ❔ | 🔴 | 🔴 |
wasm64-wasi | 3 | ❔ | ❔ | 🖥️⚡ | 🟢 | ❔ | 🔴 | 🔴 |
x86_64-dragonfly | 3 | 🟢 | ❔ | 🖥️⚡ | 🟢 | ❔ | 🔴 | 🔴 |
x86_64-freebsd | 3 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🔴 | 🔴 |
x86_64-illumos | 3 | 🟢 | ❔ | 🖥️⚡ | 🟢 | ❔ | 🔴 | 🔴 |
x86_64-netbsd | 3 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🔴 | 🔴 |
x86_64-openbsd | 3 | 🟢 | 🟢 | 🖥️⚡ | 🟢 | 🟢 | 🔴 | 🔴 |
x86_64-solaris | 3 | 🟢 | ❔ | 🖥️⚡ | 🟢 | ❔ | 🔴 | 🔴 |
arc-linux | 4 | ❔ | 🟢 | 📄 | 🔴 | ❔ | 🟢 | 🔴 |
csky-linux | 4 | ❔ | 🟢 | 📄 | 🔴 | ❔ | 🟢 | 🔴 |
m68k-linux | 4 | ❔ | 🔴 | 🖥️ | 🔴 | ❔ | 🟢 | 🔴 |
m68k-netbsd | 4 | ❔ | 🔴 | 🖥️ | 🔴 | ❔ | 🔴 | 🔴 |
sparc-linux | 4 | ❔ | 🔴 | 🖥️ | 🔴 | ❔ | 🟢 | 🔴 |
xtensa-linux | 4 | ❔ | 🔴 | 📄 | 🔴 | ❔ | 🔴 | 🔴 |
附加平台
Zig 还对以下目标具有不同程度的支持,这些目标不完全适用于分层系统:
目标 | 目标 | 目标 |
---|---|---|
aarch64-driverkit | aarch64-ios | amdgcn-amdhsa |
aarch64[_be]-freestanding | aarch64-tvos | arc-freestanding |
aarch64-uefi | aarch64-visionos | arm[eb]-freestanding |
aarch64-watchos | avr-freestanding | bpf(eb,el)-freestanding |
csky-freestanding | hexagon-freestanding | kalimba-freestanding |
lanai-freestanding | loongarch(32,64)-freestanding | loongarch(32,64)-uefi |
m68k-freestanding | mips[64][el]-freestanding | msp430-freestanding |
nvptx[64]-cuda | nvptx[64]-nvcl | powerpc[64][le]-freestanding |
propeller-freestanding | riscv(32,64)-freestanding | riscv(32,64)-uefi |
s390x-freestanding | sparc[64]-freestanding | spirv(32,64)-opencl |
spirv(32,64)-vulkan | ve-freestanding | wasm(32,64)-emscripten |
wasm(32,64)-freestanding | x86-elfiamcu | x86[_64]-freestanding |
x86[_64]-uefi | x86_64-driverkit | x86_64-ios |
x86_64-tvos | x86_64-visionos | x86_64-watchos |
xcore-freestanding | xtensa-freestanding |
编译器
未分类的更改:
-fno-omit-frame-pointer
现在是 x86 目标上ReleaseSmall
的默认设置- 管道:更早地生成产生链接器输入的作业
多线程后端支持
编译器的一些后端(例如 x86 后端)现在支持在与前端不同的线程中运行代码生成。作为一个数据点,这使得编译器在一台计算机上自我构建的速度从 12.8 秒加快到 8.57 秒。
增量编译
虽然此功能尚未准备好默认启用,但可以通过传递给 zig build
的 -fincremental
标志选择加入。建议与文件系统监视结合使用,因为编译器状态序列化尚未实现。
此功能的完整性因使用的链接器后端而异。它们中的任何一个都尚未普遍准备好使用,但与 -fno-emit-bin
结合使用效果很好。
鼓励用户创建一个仅检查编译错误的构建选项,并通过以下方式尝试增量编译:
const no_bin = b.option(bool, "no-bin", "skip emitting binary") orelse false;
if (no_bin) {
b.getInstallStep().dependOn(&exe.step);
} else {
b.installArtifact(exe);
}
在进行大型重构并且希望快速获得编译错误反馈时:
$ zig build -Dno-bin -fincremental --watch
Build Summary: 3/3 steps succeeded
install success
└─ zig build-exe zig Debug native success 14s
└─ options success
Build Summary: 3/3 steps succeeded
install success
└─ zig build-exe zig Debug native success 63ms
watching 119 directories, 1 processes
上面的例子中,对于 50 万行代码库,需要 14 秒才能生成编译错误。但是,由于我们使用了 --watch
和 -fincremental
,因此在进行编辑并保存后,仅需 63 毫秒即可对更改的代码进行重新分析。
那些在构建脚本中添加 -Dno-bin
选项的用户也可以享受类似的工作流程。
未来,这也将能够生成一个完全可用的二进制文件,可以像平常一样进行测试和调试。
该功能尚不兼容 usingnamespace
,建议尽可能避免使用 usingnamespace
,根据社区讨论,在未来很可能删除 usingnamespace
。
x86 后端
x86 后端现在通过了 1923 个测试中的 1884(98%)。目前还未作为默认后端使用,但在开发过程中,它通常比 LLVM 后端更好,因为它具有显著更快的编译速度,并且具有更好的调试器支持。
该后端接近完成,预计在下一个发布周期使用,作为调试默认的默认后端。建议用户现在就在 0.14.0
版本中尝试它。可以使用-fno-llvm
或在构建脚本中 use_llvm = false
来启用它。
导入 ZON
现在可以在编译时导入 ZON:
const foo: Foo = @import("foo.zon");
目前,这需要已知的结果类型,计划未来取消此限制。
有关在运行时导入 ZON 的信息,请参阅升级指南中的 ZON 解析和序列化。
标记器:简化和规范一致性
我们用模糊测试器测试了 tokenizer,结果它立刻就崩溃了。检查后,对这个实现不满意。
此提交移除了几个机制:
- 移除了“无效字节”编译错误注释。
- 通过使恢复总是在换行处发生,而不是其他地方,大大简化了标记器恢复。
- 移除了 UTF-8 验证。
- 将一些字符验证逻辑移动到
std.zig.parseCharLiteral
。
移除 UTF-8 验证是对 #663 的回退,然而,现有实现存在问题。当重新添加此功能时,必须在检查其与同一文件上的独立 Unicode 验证实现匹配的属性时进行模糊测试。同时,模糊测试还应检查该提案的其他属性,例如源代码中不存在 ASCII 控制字符,\r
总是跟随 \n
,等等。
此提交中包含的其他更改:
- 弃用
std.unicode.utf8Decode
及其 WTF-8 对应函数。此函数的 API 很尴尬,太容易被误用。 - 使
utf8Decode2
及其相关函数使用数组作为参数,消除运行时断言,转而使用类型系统。
在此提交之后,由模糊测试发现的崩溃(即 "\x07\xd5\x80\xc3=o\xda|a\xfc{\x9a\xec\x91\xdf\x0f\\\x1a^\xbe;\x8c\xbf\xee\xea"
)不再导致崩溃。没有必要添加此单元测试,因为简化的逻辑消除了这种性质的崩溃。
Benchmark 1 (100 runs): before/zig ast-check /home/andy/dev/zig/src/Sema.zig
测量项 | 平均值 ± 标准差 | 最小值 … 最大值 | 异常值比例 | 变化率 |
---|---|---|---|---|
wall_time | 50.0ms ± 2.04ms | 48.7ms … 57.4ms | 14 (14%) | 0% |
peak_rss | 60.0MB ± 147KB | 59.4MB … 60.2MB | 3 (3%) | 0% |
cpu_cycles | 232M ± 745K | 230M … 234M | 3 (3%) | 0% |
instructions | 522M ± 24.3 | 522M … 522M | 1 (1%) | 0% |
cache_references | 6.55M ± 120K | 6.39M … 7.45M | 2 (2%) | 0% |
cache_misses | 205K ± 3.47K | 198K … 215K | 1 (1%) | 0% |
branch_misses | 2.86M ± 10.3K | 2.80M … 2.87M | 9 (9%) | 0% |
Benchmark 2 (104 runs): after/zig ast-check /home/andy/dev/zig/src/Sema.zig
测量项 | 平均值 ± 标准差 | 最小值 … 最大值 | 异常值比例 | 变化率 |
---|---|---|---|---|
wall_time | 48.3ms ± 250us | 48.1ms … 50.0ms | 10 (10%) | ⚡- 3.3% ± 0.8% |
peak_rss | 62.4MB ± 142KB | 62.1MB … 62.6MB | 0 (0%) | 💩+ 4.1% ± 0.1% |
cpu_cycles | 227M ± 637K | 226M … 230M | 7 (7%) | ⚡- 1.9% ± 0.1% |
instructions | 501M ± 44.8 | 501M … 501M | 7 (7%) | ⚡- 4.0% ± 0.0% |
cache_references | 6.65M ± 141K | 6.45M … 7.67M | 4 (4%) | + 1.5% ± 0.5% |
cache_misses | 208K ± 3.79K | 201K … 226K | 3 (3%) | + 1.3% ± 0.5% |
branch_misses | 2.84M ± 8.62K | 2.81M … 2.86M | 1 (1%) | - 0.4% ± 0.1% |
在所有函数中包含错误跟踪
具体见 PR #22572。
链接器
将输入文件解析移至前端
将 GNU ld
脚本处理移至前端,以加入相关的库查找逻辑,使得库符合与所有其他库相同的搜索标准。
此更改是必要的,以便编译器在编译开始时了解所有链接器输入,从而使链接和编译可以同时开始。在 flush()
期间发现链接器输入为时已晚。此分支完全移除了传递给 ELF 链接代码的 lib_dirs
。
不幸的是,这意味着在针对 ELF 时,需要对所有 .so
文件进行文件系统访问,以确定它们是否是链接器脚本,因此我引入了一个选择性加入的 CLI 标志来启用 .so
脚本。当遇到 GNU ld 脚本时,错误消息会通知用户有关 CLI 标志的信息,该标志将立即解决他们的问题,即传递 -fallow-so-scripts
给 zig build
。这意味着那些没有奇怪库的用户,或者至少避免链接这些库的用户,不必支付这些文件系统访问的成本。
所有目标文件、归档文件和共享目标文件的解析现在都在并行编译管道中进行,而不是在最后形成瓶颈。
这朝着增量编译迈出了一步。
Wasm 链接器重构
目标:
- 使用 wasm 链接器和后端时编译更快
- 通过直接将内存中的链接器状态复制到磁盘来启用保存编译器状态
- 更有效的编译器内存利用
- 为 wasm 链接器代码引入整数类型安全
- 生成更好的 WebAssembly 代码
- 完全参与增量编译
- 在编译器管道中尽可能多地完成工作,而不是在最后形成瓶颈,同时继续进行垃圾收集
- 避免不必要的堆分配
- 避免不必要的间接函数调用
所有这些目标都已实现。演示和性能数据点在报告中可用。
虽然它通过了之前的相同测试套件,但链接器尚未完成,尚未默认启用,因此不会消除对 LLD 的依赖。
模糊测试器
Zig 0.14.0
集成了一个模糊测试器。它处于 alpha
状态,这意味着使用它需要参与开发。
为构建运行器添加了 --fuzz
CLI 选项。当使用此选项时,它会重新构建包含至少一个模糊测试的任何单元测试二进制文件,并使用 -ffuzz
,然后告诉它开始模糊测试,这会进行进程内模糊测试。
这仅包含模糊测试器逻辑的基本实现,实际上只是一些早期的实验,但已经使这个测试用例在我们的机器上在 65 毫秒内失败:
test "fuzz example" {
const input_bytes = std.testing.fuzzInput(.{});
try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input_bytes));
}
--fuzz
标志使构建系统生成一个 HTTP 服务器,该服务器提供一个模糊测试器 Web UI,显示模糊测试器探索不同输入时实时更新的代码覆盖率。
$ zig build test --fuzz
info: web interface listening at http://127.0.0.1:38239/
info: hint: pass --port 38239 to use this same port next time
[0/1] Fuzzing
└─ foo.bar.example
Bug 修复
在该版本中关闭的 Issue 列表
工具链
UBSan 运行时
Zig 现在提供了一个用于 UBSan 的运行时库,在 Debug 模式下编译时默认启用。
简言之,默认情况下,当在 C 代码中触发未定义行为时,它现在看起来像这样:
$ zig run test.c -lc
thread 208135 panic: signed integer overflow: 2147483647 + 2147483647 cannot be represented in type 'int'
/home/david/Code/zig/build/test.c:4:14: 0x1013e41 in foo (test.c)
return x + y;
^
/home/david/Code/zig/build/test.c:8:18: 0x1013e63 in main (test.c)
int result = foo(0x7fffffff, 0x7fffffff);
^
../sysdeps/nptl/libc_start_call_main.h:58:16: 0x7fca4c42e1c9 in __libc_start_call_main (../sysdeps/x86/libc-start.c)
../csu/libc-start.c:360:3: 0x7fca4c42e28a in __libc_start_main_impl (../sysdeps/x86/libc-start.c)
???:?:?: 0x1013de4 in ??? (???)
???:?:?: 0x0 in ??? (???)
fish: Job 1, 'zig run test.c -lc' terminated by signal SIGABRT (Abort)
如果默认设置未能正确检测是否应省略或包含运行时库,可以分别使用 -fno-ubsan-rt
和 -fubsan-rt
覆盖默认设置。相关 Issue。
compiler_rt
优化的 memcpy
相关 PR #18912
LLVM 19
此版本的 Zig 升级到 LLVM 19.1.7。这涵盖了 Clang (zig cc
/zig c++
)、libc++、libc++abi、libunwind 和 libtsan。
musl 1.2.5
Zig 附带了 musl 的源代码。
当选择 musl C ABI 时,Zig 从源代码为所选目标构建静态 musl。
Zig 还支持针对动态链接的 musl,这对于使用它作为系统 libc 的 Linux 发行版(如 Alpine Linux)非常有用。
此版本保持 v1.2.5,但:
- 应用了 Rich Felker 的 CVE-2025-26519 缓解补丁。
- 应用了许多正在上游处理的特定目标补丁。
- 更新了动态链接时提供的 libc 存根符号列表。
- 修复了一些特殊 libc 函数(如软浮点目标上的
fma
)中的堆栈溢出问题。 - 此外,Zig 不再附带 musl 的
memcpy
文件。相反,Zig 提供了优化的memcpy
。
glibc 2.41
在交叉编译时,现在可以使用 glibc 版本 2.40 和 2.41。
一些显著的修复:
- 修复了 Zig 构建的可执行文件未定义
_IO_stdin_used
,导致它们在多个目标上无法使用的问题。 - 修复了某些目标(例如 powerpc 和 s390x 上的
lgammal
)导致重复存根符号的边缘情况。 - 更改了存根符号,使它们不再具有相同的地址。这导致 LLVM 在某些目标上合并它们,导致代码严重损坏。
- 应用了一个针对 LLVM 错误的解决方法,该错误导致在针对 mips r6 时
j $ra
被错误地汇编。 - 应用了一个针对 LLVM 的 s390x 汇编器缺少语法支持的解决方法。
Linux 6.13.4 头文件
此版本包括 Linux 内核版本 6.13.4 的头文件。
Darwin libSystem 15.1
此版本包括 Xcode SDK 版本 15.1 的 Darwin libSystem 符号。
MinGW-w64
此版本将捆绑的 MinGW-w64 版本提升到提交 3839e21b08807479a31d5a9764666f82ae2f0356
。
其他更改:
- Zig 现在捆绑了
winpthreads
库。 - Zig 现在支持为
thumb-windows-gnu
进行交叉编译。
wasi-libc
此版本将捆绑的 wasi-libc 版本提升到提交 d03829489904d38c624f6de9983190f1e5e7c9c5
。
路线图
0.15.0
版本周期的主要目标将是编译速度。
一些即将到来的里程碑:
- 使 x86 后端成为调试模式的默认后端。
- 增强链接器实现,消除对 LLD 的依赖并支持增量编译。
- 增强集成的模糊测试器,使其与 AFL 和其他最先进的模糊测试器竞争。
这里的想法是,优先提高编译速度将增加编译器本身的开发速度,从而在接下来的发布周期中修复更多的错误并完成更多的功能。
为了提高编译速度,可能会对语法进行变动。