Skip to content

0.14.0

2025/3/5,0.14.0 发布,历时 9 个月,有 251 位贡献者,一共进行了 3467 次提交!

除了大量的构建系统升级、语言变更和目标支持增强之外,此版本还朝着我们的两项长期目标迈进——增量编译和更快的 x86 后端 - 都专注于 减少编辑/编译/调试周期延迟

目标支持

此版本的 Zig 的一个重要特性是改进目标支持状况,Zig 可以正确交叉编译和运行的目标列表已增加很多。 Zig 可以为其构建程序的目标见下表,zig-bootstrap README 涵盖了 Zig 编译器本身可以轻松交叉编译以运行的目标。

针对特性目标修复的完整列表太长,不在列出,但简言之,如果过去尝试针对 arm/thumbmips/mips64powerpc/powerpc64riscv32/riscv64s390x 并遇到工具链问题、缺少标准库支持或看似无意义的崩溃,那么现在大概率 Zig 0.14.0 可以正常工作。

目标三重变化

Zig 对理解的目标三重做了一些更改:

  • arm-windows-gnu 已被替换为 thumb-windows-gnu,以反映 Windows 仅支持 Thumb-2 模式的事实。
  • armeb-windows-gnuaarch64_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 才能使用它。

支持列表

在下表中,🟢 表示完全支持,🔴 表示不支持,🟡 表示部分支持,例如仅支持某些子目标,或存在一些显著的已知问题。❔ 表示状态大致未知,通常是因为该目标很少被使用。将鼠标悬停在其他图标上以查看详细信息。

目标层级语言特性标准库代码生成链接器调试信息libcCI
x86_64-linux1🟢🟢🖥️⚡🟢🟢🟢🟢
aarch64[_be]-linux2🟢🟢🖥️🛠️🟢🟢🟢🟢
aarch64-macos2🟢🟢🖥️🟢🟢🟢🟢
aarch64-windows2🟢🟢🖥️🟢🟢🟢🟢
arm[eb]-linux2🟢🟢🖥️🛠️🟢🟢🟢🟢
powerpc-linux2🟢🟢🖥️🟡🟢🟢🟡
powerpc64-linux2🟢🟢🖥️🟡🟢🟡🟡
powerpc64le-linux2🟢🟢🖥️🟢🟢🟢🟢
wasm32-wasi2🟢🟢🖥️⚡🟢🟢🟢🟢
x86-linux2🟢🟢🖥️🟢🟢🟢🟢
x86-windows2🟢🟢🖥️🟢🟢🟢🟢
x86_64-macos2🟢🟢🖥️🟢🟢🟢🟢
x86_64-windows2🟢🟢🖥️🟢🟢🟢🟢
aarch64-freebsd3🖥️🛠️🟢🟢🔴🔴
aarch64[_be]-netbsd3🖥️🛠️🟢🔴🔴
aarch64-openbsd3🖥️🛠️🟢🔴🔴
hexagon-linux3🟡🟡🖥️🟢🔴🔴🔴
loongarch64-linux3🟡🟡🖥️🟢🔴🟢🔴
mips[el]-linux3🟢🟢🖥️🟢🔴🟢🔴
mips64[el]-linux3🟢🟢🖥️🟡🔴🟢🔴
riscv32-linux3🟢🟢🖥️🟢🔴🟢🟢
riscv64-linux3🟢🟢🖥️🛠️🟢🔴🟢🟢
s390x-linux3🟢🟢🖥️🟢🔴🟢🟢
sparc64-linux3🟢🖥️🛠️🟢🟢🔴
sparc64-solaris3🖥️🛠️🟢🔴🔴
wasm64-wasi3🖥️⚡🟢🔴🔴
x86_64-dragonfly3🟢🖥️⚡🟢🔴🔴
x86_64-freebsd3🟢🟢🖥️⚡🟢🟢🔴🔴
x86_64-illumos3🟢🖥️⚡🟢🔴🔴
x86_64-netbsd3🟢🟢🖥️⚡🟢🟢🔴🔴
x86_64-openbsd3🟢🟢🖥️⚡🟢🟢🔴🔴
x86_64-solaris3🟢🖥️⚡🟢🔴🔴
arc-linux4🟢📄🔴🟢🔴
csky-linux4🟢📄🔴🟢🔴
m68k-linux4🔴🖥️🔴🟢🔴
m68k-netbsd4🔴🖥️🔴🔴🔴
sparc-linux4🔴🖥️🔴🟢🔴
xtensa-linux4🔴📄🔴🔴🔴

附加平台

Zig 还对以下目标具有不同程度的支持,这些目标不完全适用于分层系统:

目标目标目标
aarch64-driverkitaarch64-iosamdgcn-amdhsa
aarch64[_be]-freestandingaarch64-tvosarc-freestanding
aarch64-uefiaarch64-visionosarm[eb]-freestanding
aarch64-watchosavr-freestandingbpf(eb,el)-freestanding
csky-freestandinghexagon-freestandingkalimba-freestanding
lanai-freestandingloongarch(32,64)-freestandingloongarch(32,64)-uefi
m68k-freestandingmips[64][el]-freestandingmsp430-freestanding
nvptx[64]-cudanvptx[64]-nvclpowerpc[64][le]-freestanding
propeller-freestandingriscv(32,64)-freestandingriscv(32,64)-uefi
s390x-freestandingsparc[64]-freestandingspirv(32,64)-opencl
spirv(32,64)-vulkanve-freestandingwasm(32,64)-emscripten
wasm(32,64)-freestandingx86-elfiamcux86[_64]-freestanding
x86[_64]-uefix86_64-driverkitx86_64-ios
x86_64-tvosx86_64-visionosx86_64-watchos
xcore-freestandingxtensa-freestanding

编译器

未分类的更改:

  • -fno-omit-frame-pointer 现在是 x86 目标上 ReleaseSmall 的默认设置
  • 管道:更早地生成产生链接器输入的作业

多线程后端支持

编译器的一些后端(例如 x86 后端)现在支持在与前端不同的线程中运行代码生成。作为一个数据点,这使得编译器在一台计算机上自我构建的速度从 12.8 秒加快到 8.57 秒。

增量编译

虽然此功能尚未准备好默认启用,但可以通过传递给 zig build-fincremental 标志选择加入。建议与文件系统监视结合使用,因为编译器状态序列化尚未实现。

此功能的完整性因使用的链接器后端而异。它们中的任何一个都尚未普遍准备好使用,但与 -fno-emit-bin 结合使用效果很好。

鼓励用户创建一个仅检查编译错误的构建选项,并通过以下方式尝试增量编译:

zig
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);
}

在进行大型重构并且希望快速获得编译错误反馈时:

sh
$ 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:

zig
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_time50.0ms ± 2.04ms48.7ms … 57.4ms14 (14%)0%
peak_rss60.0MB ± 147KB59.4MB … 60.2MB3 (3%)0%
cpu_cycles232M ± 745K230M … 234M3 (3%)0%
instructions522M ± 24.3522M … 522M1 (1%)0%
cache_references6.55M ± 120K6.39M … 7.45M2 (2%)0%
cache_misses205K ± 3.47K198K … 215K1 (1%)0%
branch_misses2.86M ± 10.3K2.80M … 2.87M9 (9%)0%

Benchmark 2 (104 runs): after/zig ast-check /home/andy/dev/zig/src/Sema.zig

测量项平均值 ± 标准差最小值 … 最大值异常值比例变化率
wall_time48.3ms ± 250us48.1ms … 50.0ms10 (10%)⚡- 3.3% ± 0.8%
peak_rss62.4MB ± 142KB62.1MB … 62.6MB0 (0%)💩+ 4.1% ± 0.1%
cpu_cycles227M ± 637K226M … 230M7 (7%)⚡- 1.9% ± 0.1%
instructions501M ± 44.8501M … 501M7 (7%)⚡- 4.0% ± 0.0%
cache_references6.65M ± 141K6.45M … 7.67M4 (4%)+ 1.5% ± 0.5%
cache_misses208K ± 3.79K201K … 226K3 (3%)+ 1.3% ± 0.5%
branch_misses2.84M ± 8.62K2.81M … 2.86M1 (1%)- 0.4% ± 0.1%

在所有函数中包含错误跟踪

具体见 PR #22572

链接器

将输入文件解析移至前端

将 GNU ld 脚本处理移至前端,以加入相关的库查找逻辑,使得库符合与所有其他库相同的搜索标准。

此更改是必要的,以便编译器在编译开始时了解所有链接器输入,从而使链接和编译可以同时开始。在 flush() 期间发现链接器输入为时已晚。此分支完全移除了传递给 ELF 链接代码的 lib_dirs

不幸的是,这意味着在针对 ELF 时,需要对所有 .so 文件进行文件系统访问,以确定它们是否是链接器脚本,因此我引入了一个选择性加入的 CLI 标志来启用 .so 脚本。当遇到 GNU ld 脚本时,错误消息会通知用户有关 CLI 标志的信息,该标志将立即解决他们的问题,即传递 -fallow-so-scriptszig build。这意味着那些没有奇怪库的用户,或者至少避免链接这些库的用户,不必支付这些文件系统访问的成本。

所有目标文件、归档文件和共享目标文件的解析现在都在并行编译管道中进行,而不是在最后形成瓶颈。

这朝着增量编译迈出了一步。

Wasm 链接器重构

目标:

  • 使用 wasm 链接器和后端时编译更快
  • 通过直接将内存中的链接器状态复制到磁盘来启用保存编译器状态
  • 更有效的编译器内存利用
  • 为 wasm 链接器代码引入整数类型安全
  • 生成更好的 WebAssembly 代码
  • 完全参与增量编译
  • 在编译器管道中尽可能多地完成工作,而不是在最后形成瓶颈,同时继续进行垃圾收集
  • 避免不必要的堆分配
  • 避免不必要的间接函数调用

所有这些目标都已实现。演示和性能数据点在报告中可用。

虽然它通过了之前的相同测试套件,但链接器尚未完成,尚未默认启用,因此不会消除对 LLD 的依赖。

模糊测试器

Zig 0.14.0 集成了一个模糊测试器。它处于 alpha 状态,这意味着使用它需要参与开发。

为构建运行器添加了 --fuzz CLI 选项。当使用此选项时,它会重新构建包含至少一个模糊测试的任何单元测试二进制文件,并使用 -ffuzz,然后告诉它开始模糊测试,这会进行进程内模糊测试。

这仅包含模糊测试器逻辑的基本实现,实际上只是一些早期的实验,但已经使这个测试用例在我们的机器上在 65 毫秒内失败:

zig
test "fuzz example" {
    const input_bytes = std.testing.fuzzInput(.{});
    try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input_bytes));
}

--fuzz 标志使构建系统生成一个 HTTP 服务器,该服务器提供一个模糊测试器 Web UI,显示模糊测试器探索不同输入时实时更新的代码覆盖率。

sh
$ 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

asciinema demo

vedio demo and screenshots

Bug 修复

在该版本中关闭的 Issue 列表

工具链

UBSan 运行时

Zig 现在提供了一个用于 UBSan 的运行时库,在 Debug 模式下编译时默认启用。

简言之,默认情况下,当在 C 代码中触发未定义行为时,它现在看起来像这样:

sh
$ 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 和其他最先进的模糊测试器竞争。

这里的想法是,优先提高编译速度将增加编译器本身的开发速度,从而在接下来的发布周期中修复更多的错误并完成更多的功能。

为了提高编译速度,可能会对语法进行变动。