🎓 A1Learner
🔍
0 of 46 lessons completed

Zig Build Tools

Source generation is a build step that writes source code, which the same build then compiles. The program you write never contains the generated lines by hand.

Generate a greeting

A small generator program writes a source file with a greeting constant. The main program imports that module and prints the greeting.

const std = @import("std");
pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    const gen_exe = b.addExecutable(.{ .name = "gen", .root_source_file = b.path("src/gen.zig"), .target = target, .optimize = optimize });
    const gen_run = b.addRunArtifact(gen_exe);
    const generated = gen_run.addOutputFileArg("generated.zig");
    const exe = b.addExecutable(.{ .name = "app", .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize });
    exe.root_module.addImport("generated", b.createModule(.{ .root_source_file = generated }));
    b.installArtifact(exe);
    const run_step = b.step("run", "Run the app");
    const run = b.addRunArtifact(exe);
    run_step.dependOn(&run.step);
}

Generate a number

The generator writes a source file with a number constant. The main program imports that module and prints the number.

const std = @import("std");
pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    const gen_exe = b.addExecutable(.{ .name = "gen", .root_source_file = b.path("src/gen.zig"), .target = target, .optimize = optimize });
    const gen_run = b.addRunArtifact(gen_exe);
    const generated = gen_run.addOutputFileArg("generated.zig");
    const exe = b.addExecutable(.{ .name = "app", .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize });
    exe.root_module.addImport("generated", b.createModule(.{ .root_source_file = generated }));
    b.installArtifact(exe);
    const run_step = b.step("run", "Run the app");
    const run = b.addRunArtifact(exe);
    run_step.dependOn(&run.step);
}

Generate two values

One generated file can hold more than one constant. The main program prints both values from the same module.

const std = @import("std");
pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    const gen_exe = b.addExecutable(.{ .name = "gen", .root_source_file = b.path("src/gen.zig"), .target = target, .optimize = optimize });
    const gen_run = b.addRunArtifact(gen_exe);
    const generated = gen_run.addOutputFileArg("generated.zig");
    const exe = b.addExecutable(.{ .name = "app", .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize });
    exe.root_module.addImport("generated", b.createModule(.{ .root_source_file = generated }));
    b.installArtifact(exe);
    const run_step = b.step("run", "Run the app");
    const run = b.addRunArtifact(exe);
    run_step.dependOn(&run.step);
}

Example

const std = @import("std");
pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    const gen_exe = b.addExecutable(.{ .name = "gen", .root_source_file = b.path("src/gen.zig"), .target = target, .optimize = optimize });
    const gen_run = b.addRunArtifact(gen_exe);
    const generated = gen_run.addOutputFileArg("generated.zig");
    const exe = b.addExecutable(.{ .name = "app", .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize });
    exe.root_module.addImport("generated", b.createModule(.{ .root_source_file = generated }));
    b.installArtifact(exe);
    const run_step = b.step("run", "Run the app");
    const run = b.addRunArtifact(exe);
    run_step.dependOn(&run.step);
}

Key points

  • Source generation: source generation is a build step that writes source code for the same build to compile.
  • Generator: a generator is a program that writes source code.
  • Output file argument: an output file argument is the file path a run step hands to its program.
  • Generated module: a generated module is a module whose source file comes from a generator step.
  • Shared options: shared options are one target value and one optimize value used by every program in the build file.
  • Import link: an import link is the module link that lets the main program import the generated module.

The generator program receives its output path as an argument and writes the source file there. The build file links that file as a module named generated for the main program. One build command runs the generator, compiles the main program, and runs it, printing the generated values.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What does the generator step produce in this lesson?