*ฅ^•ﻌ•^ฅ* ✨✨  HWisnu's blog  ✨✨ о ฅ^•ﻌ•^ฅ

New Zig 0.14 compiler features -fincremental --watch

Introduction

The two big feature release in the new Zig 0.14 are incremental compilation and x86 Backend (no-LLVM).

Incremental Compilation + File System Watch

Read here on incremental compilation
Read here on File System Watch
(sorry I'm too lazy to repeat stuff covered by other resources)

x86 Backend

Read here
(sorry again...LOL)

build.zig

Here's my build.zig: build-zig-01

Note: pay attention to the purple arrows and rectangle.

The full code:

const std = @import("std");
const ctPrint = std.fmt.comptimePrint;

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

    // List of Zig files in the programs/ directory
    const programs = [_][]const u8{
        "main_add",
        "test_zigMaster_014",
    };

    // Loop through each program and create exec
    inline for (programs) |program| {
        // Comptime program path
        const program_path = comptime ctPrint("programs/{s}.zig", .{program});

        const exe = b.addExecutable(.{
            .name = program,
            .root_source_file = b.path(program_path),
            .target = target,
            .optimize = optimize,
            .use_llvm = false,
        });

        b.installArtifact(exe);

        const run_cmd = b.addRunArtifact(exe);
        run_cmd.step.dependOn(b.getInstallStep());

        if (b.args) |args| {
            run_cmd.addArgs(args);
        }

        // Comptime step name and description
        const run_step_name = comptime ctPrint("run-{s}", .{program});
        const run_step_desc = comptime ctPrint("Run the {s} program", .{program});

        const run_step = b.step(run_step_name, run_step_desc);
        run_step.dependOn(&run_cmd.step);
    }

    const test_step = b.step("test", "Run tests for all programs");
    inline for (programs) |program| {
        // Comptime program path
        const program_path = comptime ctPrint("programs/{s}.zig", .{program});

        const test_exe = b.addTest(.{
            .root_source_file = b.path(program_path),
            .target = target,
            .optimize = optimize,
            .use_llvm = false,
        });

        const run_test_cmd = b.addRunArtifact(test_exe);
        test_step.dependOn(&run_test_cmd.step);
    }
}

Seeing -fincremental --watch in action

zig-build-watch-01

Key Highlights:

  1. Purple arrow: run the command zig build -fincremental --watch
  2. Yellow rectangle: the initial build ~ note the time taken around 700ms each for my two Zig files.
  3. Green rectangle: incremental build which is done automatically whenever you make changes and save the file. -> note it took only around 50ms --> bwazzingly fwast!

More evidence of the speedup from incremental compilation

Conclusion

So there you go, it's quite amazing isn't it? What's more, these features are not fully baked yet, so next Zig releases 0.15 / 0.16 there will be even more performance improvements. Combination of these features are crucial for those working on something requiring rapid iteration (especially gamedevs).

My 10yr old daughter is working on a game using Godot and currently it takes 20 seconds for each debug compile..these 20 second delays are annoying and really break the momentum. I wonder if Mach (the Zig game engine) compiles in subsecond with this incremental compilation, we might as well migrate to Mach? We'll see...