对c/c++有所了解的话,都知道宏的概念。c/c++当中使用宏来实现条件编译时非常常用的一种手段。不过zig语言是不支持宏的,但是其仍然支持条件编译,依赖的便是编译时运行的能力。

看一下ghostty的main函数入口,可以发现

const entrypoint = switch (build_config.exe_entrypoint) {
    .ghostty => @import("main_ghostty.zig"),
    .helpgen => @import("helpgen.zig"),
    .mdgen_ghostty_1 => @import("build/mdgen/main_ghostty_1.zig"),
    .mdgen_ghostty_5 => @import("build/mdgen/main_ghostty_5.zig"),
    .webgen_config => @import("build/webgen/main_config.zig"),
    .webgen_actions => @import("build/webgen/main_actions.zig"),
    .webgen_commands => @import("build/webgen/main_commands.zig"),
    .bench_parser => @import("bench/parser.zig"),
    .bench_stream => @import("bench/stream.zig"),
    .bench_codepoint_width => @import("bench/codepoint-width.zig"),
    .bench_grapheme_break => @import("bench/grapheme-break.zig"),
    .bench_page_init => @import("bench/page-init.zig"),
};
 
/// The main entrypoint for the program.
pub const main = entrypoint.main;

其main函数并不是固定的,可以根据构建的选项不同可以构建出不同的程序出来。结合zig的构建系统和编译时运行来看是如何实现条件编译的。

这里的关键在于build_config的信息是如何在编译时获得的,追踪进去可以发现

pub fn fromOptions() Config {
    const options = @import("build_options");
    return .{
        // Unused at runtime.
        .optimize = undefined,
        .target = undefined,
        .env = undefined,
 
        .version = options.app_version,
        .flatpak = options.flatpak,
        .app_runtime = std.meta.stringToEnum(apprt.Runtime, @tagName(options.app_runtime)).?,
        .font_backend = std.meta.stringToEnum(font.Backend, @tagName(options.font_backend)).?,
        .renderer = std.meta.stringToEnum(rendererpkg.Impl, @tagName(options.renderer)).?,
        .exe_entrypoint = std.meta.stringToEnum(ExeEntrypoint, @tagName(options.exe_entrypoint)).?,
        .wasm_target = std.meta.stringToEnum(WasmTarget, @tagName(options.wasm_target)).?,
        .wasm_shared = options.wasm_shared,
    };
}

这里的核心在于@import("build_options")这行代码,因为这里倒入的模块build_options时编译器在编译的时候根据编译选项自动生成的模块。因此上面的这个函数会在编译时运行,同理调用该函数的模块同样也在编译期上下文中,所以编译这块代码的时候可以获取到编译选项的值,从而之行对应的逻辑,也就相当于条件编译了。

可以在build.zig为入口找到其添加构建选项的地方,上面的那些选项一定是在这个过程中添加的,比如exe_entrypoint这个选项

step.addOption(ExeEntrypoint, "exe_entrypoint", self.exe_entrypoint);

和cmake构建选项的作用类似,不过zig的构建系统和zig的代码很好地结合在了一起