Hey!
Billgo

EN

Zig 构建系统实用入门

2025 年 5 月 2 日

Zig 的构建系统一开始可能看起来有点陌生,但它其实是 Zig 最强大的特性之一。通过把 Zig 本身作为配置语言,build.zig 让你可以用完整的程序化方式控制编译、链接、测试等流程,而不需要额外工具或专用 DSL。

第 1 步:什么是 build.zig

任何包含多个文件或自定义构建步骤的 Zig 项目,都会使用 build.zig 脚本。这个脚本使用 Zig 的 std.Build API 来定义应用应该如何构建。

第 2 步:最小 build.zig 示例

下面是一个用于 Zig 可执行程序的简单构建脚本:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const mode = b.standardReleaseOptions();

    const exe = b.addExecutable(.{
        .name = "hello",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = mode,
    });

    b.installArtifact(exe);
}

这段代码为 src/main.zig 定义了构建配置,并包含目标平台和优化模式等选项。

第 3 步:添加库

如果你想构建静态库或动态库,而不是可执行文件,可以这样写:

const lib = b.addStaticLibrary(.{
    .name = "mylib",
    .root_source_file = .{ .path = "src/lib.zig" },
    .target = target,
    .optimize = mode,
});

然后可以把这个库链接到可执行文件:

exe.linkLibrary(lib);

第 4 步:添加测试

你也可以直接在构建系统中定义并运行测试:

const tests = b.addTest(.{
    .root_source_file = .{ .path = "src/main.zig" },
    .target = target,
    .optimize = mode,
});
b.installArtifact(tests);

然后运行:

zig build test

第 5 步:自定义步骤

可以使用 b.step() 把任意逻辑注入构建流水线:

const my_step = b.step("message", "Print a message");
my_step.makeFn = fn (step: *std.Build.Step, _: *std.Progress) anyerror!void {
    std.debug.print("Custom step executed!\n", .{});
    return;
};

使用下面的命令运行:

zig build message

build.zig 的优缺点

优点

  • 使用 Zig 编写,不需要额外构建语言。
  • 拥有完整的程序化控制能力。
  • 很容易集成测试、库和自定义步骤。
  • 内置交叉编译和 release 模式。

缺点

  • 学习曲线比 makecargo 更陡。
  • 高级特性的文档相对有限。
  • 对非常小的脚本来说可能有些过重。

总结

一旦熟悉了 build.zig,你会在每个新的 Zig 项目中自然地使用它的灵活性。无论你是在构建命令行工具、跨平台库,还是复杂的多阶段程序,Zig 的构建系统都能给你明确的控制能力,而不是隐藏在魔法背后。