Skip to content

Zig Build System

Zig has a powerful built-in build system. This chapter will cover how to use build.zig to configure and manage complex build tasks.

Build System Basics

Simple Build Script

The most basic build.zig file:

zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    // Get target platform and optimization options
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    // Create executable
    const exe = b.addExecutable(.{
        .name = "my-app",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    // Install executable
    b.installArtifact(exe);
    
    // Create run step
    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());
    
    // Pass command line arguments
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }
    
    // Register run step
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
}

Build Options

zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    // Custom build options
    const enable_logging = b.option(bool, "enable-logging", "Enable logging") orelse true;
    const max_connections = b.option(u32, "max-connections", "Maximum connections") orelse 100;
    const server_port = b.option(u16, "port", "Server port") orelse 8080;
    
    const exe = b.addExecutable(.{
        .name = "server",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    // Add build options as compile-time constants
    const options = b.addOptions();
    options.addOption(bool, "enable_logging", enable_logging);
    options.addOption(u32, "max_connections", max_connections);
    options.addOption(u16, "server_port", server_port);
    
    exe.addOptions("build_options", options);
    
    b.installArtifact(exe);
    
    // Example using build options
    std.debug.print("Build configuration:\n");
    std.debug.print("  Logging: {}\n", .{enable_logging});
    std.debug.print("  Max connections: {}\n", .{max_connections});
    std.debug.print("  Port: {}\n", .{server_port});
}

Multi-Target Builds

Executables and Libraries

zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    // Static library
    const lib = b.addStaticLibrary(.{
        .name = "mylib",
        .root_source_file = .{ .path = "src/lib.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    b.installArtifact(lib);
    
    // Shared library
    const shared_lib = b.addSharedLibrary(.{
        .name = "mylib",
        .root_source_file = .{ .path = "src/lib.zig" },
        .target = target,
        .optimize = optimize,
        .version = .{ .major = 1, .minor = 0, .patch = 0 },
    });
    
    b.installArtifact(shared_lib);
    
    // Executable (linking static library)
    const exe = b.addExecutable(.{
        .name = "myapp",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    exe.linkLibrary(lib);
    b.installArtifact(exe);
    
    // Test executable
    const test_exe = b.addTest(.{
        .root_source_file = .{ .path = "src/lib.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    const run_tests = b.addRunArtifact(test_exe);
    const test_step = b.step("test", "Run library tests");
    test_step.dependOn(&run_tests.step);
}

C/C++ Integration

Compiling C Source Files

zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    const exe = b.addExecutable(.{
        .name = "mixed-app",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    // Link C standard library
    exe.linkLibC();
    
    // Add C source files
    exe.addCSourceFiles(&[_][]const u8{
        "src/c/helper.c",
        "src/c/math_utils.c",
        "src/c/string_utils.c",
    }, &[_][]const u8{
        "-std=c99",
        "-Wall",
        "-Wextra",
        "-O2",
    });
    
    // Add C++ source files
    exe.addCSourceFiles(&[_][]const u8{
        "src/cpp/algorithm.cpp",
        "src/cpp/data_structure.cpp",
    }, &[_][]const u8{
        "-std=c++17",
        "-Wall",
        "-Wextra",
        "-O2",
    });
    
    // Link C++ standard library
    exe.linkLibCpp();
    
    // Add include paths
    exe.addIncludePath(.{ .path = "src/c" });
    exe.addIncludePath(.{ .path = "src/cpp" });
    exe.addIncludePath(.{ .path = "include" });
    
    // Add system libraries
    if (target.isWindows()) {
        exe.linkSystemLibrary("ws2_32");
        exe.linkSystemLibrary("advapi32");
    } else {
        exe.linkSystemLibrary("pthread");
        exe.linkSystemLibrary("m");
    }
    
    b.installArtifact(exe);
}

Test Configuration

Unit Tests

zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});
    
    // Main program
    const exe = b.addExecutable(.{
        .name = "app",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    b.installArtifact(exe);
    
    // Unit tests
    const unit_tests = b.addTest(.{
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    const run_unit_tests = b.addRunArtifact(unit_tests);
    
    // Module tests
    const test_files = [_][]const u8{
        "src/utils/math.zig",
        "src/utils/string.zig",
        "src/config.zig",
        "src/logger.zig",
    };
    
    for (test_files) |test_file| {
        const module_test = b.addTest(.{
            .root_source_file = .{ .path = test_file },
            .target = target,
            .optimize = optimize,
        });
        
        run_unit_tests.step.dependOn(&b.addRunArtifact(module_test).step);
    }
    
    // Integration tests
    const integration_tests = b.addTest(.{
        .root_source_file = .{ .path = "tests/integration.zig" },
        .target = target,
        .optimize = optimize,
    });
    
    integration_tests.addModule("app", b.addModule("app", .{
        .source_file = .{ .path = "src/lib.zig" },
    }));
    
    const run_integration_tests = b.addRunArtifact(integration_tests);
    
    // Test steps
    const test_step = b.step("test", "Run all tests");
    test_step.dependOn(&run_unit_tests.step);
    test_step.dependOn(&run_integration_tests.step);
    
    // Separate test steps
    const unit_test_step = b.step("test-unit", "Run unit tests");
    unit_test_step.dependOn(&run_unit_tests.step);
    
    const integration_test_step = b.step("test-integration", "Run integration tests");
    integration_test_step.dependOn(&run_integration_tests.step);
}

Cross-Compilation

Multi-Platform Builds

zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const optimize = b.standardOptimizeOption(.{});
    
    // Define target platforms
    const targets = [_]std.zig.CrossTarget{
        .{ .cpu_arch = .x86_64, .os_tag = .linux },
        .{ .cpu_arch = .x86_64, .os_tag = .windows },
        .{ .cpu_arch = .x86_64, .os_tag = .macos },
        .{ .cpu_arch = .aarch64, .os_tag = .linux },
        .{ .cpu_arch = .aarch64, .os_tag = .macos },
    };
    
    // Create build target for each platform
    for (targets) |target| {
        const target_name = b.fmt("{s}-{s}", .{ 
            @tagName(target.cpu_arch.?), 
            @tagName(target.os_tag.?) 
        });
        
        const exe = b.addExecutable(.{
            .name = b.fmt("app-{s}", .{target_name}),
            .root_source_file = .{ .path = "src/main.zig" },
            .target = target,
            .optimize = optimize,
        });
        
        // Platform-specific configuration
        if (target.os_tag == .windows) {
            exe.linkSystemLibrary("ws2_32");
        } else {
            exe.linkSystemLibrary("pthread");
        }
        
        const install_exe = b.addInstallArtifact(exe, .{
            .dest_dir = .{ .override = .{ .custom = target_name } },
        });
        
        const target_step = b.step(
            b.fmt("build-{s}", .{target_name}),
            b.fmt("Build for {s}", .{target_name})
        );
        target_step.dependOn(&install_exe.step);
    }
    
    // Build all platforms
    const build_all_step = b.step("build-all", "Build for all platforms");
    for (targets) |target| {
        const target_name = b.fmt("{s}-{s}", .{ 
            @tagName(target.cpu_arch.?), 
            @tagName(target.os_tag.?) 
        });
        const target_step = b.step(b.fmt("build-{s}", .{target_name}), "");
        build_all_step.dependOn(target_step);
    }
}

Summary

This chapter covered various aspects of the Zig build system:

  • ✅ Basic build scripts and build options
  • ✅ Multi-target builds and dependency management
  • ✅ C/C++ integration and external library linking
  • ✅ Test configuration and benchmarking
  • ✅ Custom build steps and code generation
  • ✅ Cross-compilation and multi-platform support
  • ✅ Advanced build techniques and best practices

Zig's build system provides powerful and flexible project management capabilities. By properly using these features, you can build complex, cross-platform applications.

In the next chapter, we'll learn about Zig's style guide.

Content is for learning and research only.