第14章: ビルドシステム

学習目標

この章を終えると、以下ができるようになります:

  • build.zigを作成し、プロジェクトをビルドできる
  • 依存関係を管理できる
  • クロスコンパイルでマルチプラットフォームビルドができる
  • カスタムビルドステップを追加できる
  • build.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 = "myapp",
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        });
    
        // インストールステップに追加
        b.installArtifact(exe);
    
        // 実行ステップの作成
        const run_cmd = b.addRunArtifact(exe);
        run_cmd.step.dependOn(b.getInstallStep());
    
        // コマンドライン引数を渡す
        if (b.args) |args| {
            run_cmd.addArgs(args);
        }
    
        // "zig build run" でビルド&実行
        const run_step = b.step("run", "Run the app");
        run_step.dependOn(&run_cmd.step);
    }
    

    ライブラリのビルド

    const std = @import("std");
    
    pub fn build(b: *std.Build) void {
        const target = b.standardTargetOptions(.{});
        const optimize = b.standardOptimizeOption(.{});
    
        // 静的ライブラリの作成
        const lib = b.addStaticLibrary(.{
            .name = "mylib",
            .root_source_file = b.path("src/lib.zig"),
            .target = target,
            .optimize = optimize,
        });
    
        b.installArtifact(lib);
    
        // 動的ライブラリの作成
        const dylib = b.addSharedLibrary(.{
            .name = "mylib",
            .root_source_file = b.path("src/lib.zig"),
            .target = target,
            .optimize = optimize,
        });
    
        b.installArtifact(dylib);
    }
    

    テストのビルド

    const std = @import("std");
    
    pub fn build(b: *std.Build) void {
        const target = b.standardTargetOptions(.{});
        const optimize = b.standardOptimizeOption(.{});
    
        // ユニットテスト
        const unit_tests = b.addTest(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        });
    
        const run_unit_tests = b.addRunArtifact(unit_tests);
    
        // "zig build test" でテストを実行
        const test_step = b.step("test", "Run unit tests");
        test_step.dependOn(&run_unit_tests.step);
    }
    

    ビルドモードと最適化

    最適化レベル

    const std = @import("std");
    
    pub fn build(b: *std.Build) void {
        const target = b.standardTargetOptions(.{});
    
        // 最適化レベルの選択
        // zig build -Doptimize=Debug
        // zig build -Doptimize=ReleaseSafe
        // zig build -Doptimize=ReleaseFast
        // zig build -Doptimize=ReleaseSmall
        const optimize = b.standardOptimizeOption(.{});
    
        const exe = b.addExecutable(.{
            .name = "myapp",
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
        });
    
        b.installArtifact(exe);
    }
    

    各最適化レベルの特徴:

  • Debug: デバッグ情報付き、最適化なし、安全性チェックあり
  • ReleaseSafe: 最適化あり、安全性チェックあり
  • ReleaseFast: 最大速度、安全性チェックなし
  • ReleaseSmall: 最小サイズ、安全性チェックなし

カスタムビルドオプション

const std = @import("std");

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

    // カスタムオプションの追加
    const enable_logging = b.option(bool, "enable-logging", "Enable logging") orelse false;
    const max_connections = b.option(u32, "max-connections", "Maximum connections") orelse 100;

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

    // ビルドオプションをコードに渡す
    const options = b.addOptions();
    options.addOption(bool, "enable_logging", enable_logging);
    options.addOption(u32, "max_connections", max_connections);

    exe.root_module.addOptions("config", options);

    b.installArtifact(exe);
}

使用例:

# カスタムオプションを指定
zig build -Denable-logging=true -Dmax-connections=200

コード内で使用:

const config = @import("config");

pub fn main() void {
    if (config.enable_logging) {
        std.debug.print("Logging enabled\n", .{});
    }
    std.debug.print("Max connections: {}\n", .{config.max_connections});
}

クロスコンパイル

ターゲットの指定

const std = @import("std");

pub fn build(b: *std.Build) void {
    // デフォルトはネイティブ
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

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

    b.installArtifact(exe);
}

クロスコンパイルの実行:

# Linux x86_64向け
zig build -Dtarget=x86_64-linux

# Windows x86_64向け
zig build -Dtarget=x86_64-windows

# macOS ARM64向け
zig build -Dtarget=aarch64-macos

# WebAssembly向け
zig build -Dtarget=wasm32-freestanding

マルチターゲットビルド

const std = @import("std");

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

    // 各ターゲット用のビルド
    const targets = [_]std.Target.Query{
        .{ .cpu_arch = .x86_64, .os_tag = .linux },
        .{ .cpu_arch = .x86_64, .os_tag = .windows },
        .{ .cpu_arch = .aarch64, .os_tag = .macos },
    };

    for (targets) |target_query| {
        const target = b.resolveTargetQuery(target_query);

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

        // ターゲット名をサフィックスとして追加
        const target_output = b.addInstallArtifact(exe, .{
            .dest_dir = .{
                .override = .{
                    .custom = try target.result.zigTriple(b.allocator),
                },
            },
        });

        b.getInstallStep().dependOn(&target_output.step);
    }
}

依存関係の管理

ローカルパッケージの使用

const std = @import("std");

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

    // ローカルライブラリの追加
    const mylib = b.addStaticLibrary(.{
        .name = "mylib",
        .root_source_file = b.path("libs/mylib/lib.zig"),
        .target = target,
        .optimize = optimize,
    });

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

    // ライブラリをリンク
    exe.linkLibrary(mylib);

    b.installArtifact(exe);
}

モジュールの追加

const std = @import("std");

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

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

    // モジュールの追加
    const utils_module = b.addModule("utils", .{
        .root_source_file = b.path("src/utils.zig"),
    });

    exe.root_module.addImport("utils", utils_module);

    b.installArtifact(exe);
}

使用例:

const utils = @import("utils");

pub fn main() void {
    utils.doSomething();
}

外部ライブラリのリンク

const std = @import("std");

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

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

    // システムライブラリのリンク
    exe.linkSystemLibrary("c");
    exe.linkSystemLibrary("sqlite3");

    // インクルードパスの追加
    exe.addIncludePath(b.path("include"));

    // ライブラリパスの追加
    exe.addLibraryPath(b.path("lib"));

    b.installArtifact(exe);
}

カスタムビルドステップ

ファイル生成ステップ

const std = @import("std");

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

    // カスタムステップ: バージョンファイルの生成
    const version_step = b.addWriteFiles();
    const version_file = version_step.add("version.txt", "1.0.0");

    // 実行ファイルのビルド
    const exe = b.addExecutable(.{
        .name = "myapp",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    // バージョンファイル生成に依存
    exe.step.dependOn(&version_step.step);

    b.installArtifact(exe);
    b.installFile(version_file, "version.txt");
}

カスタムコマンドの実行

const std = @import("std");

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

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

    b.installArtifact(exe);

    // カスタムステップ: ドキュメント生成
    const docs_cmd = b.addSystemCommand(&[_][]const u8{
        "zig", "test", "-femit-docs", "src/main.zig",
    });

    const docs_step = b.step("docs", "Generate documentation");
    docs_step.dependOn(&docs_cmd.step);

    // カスタムステップ: フォーマット
    const fmt_cmd = b.addFmt(.{
        .paths = &[_][]const u8{ "src", "build.zig" },
        .check = false,
    });

    const fmt_step = b.step("fmt", "Format source code");
    fmt_step.dependOn(&fmt_cmd.step);
}

実行:

# ドキュメント生成
zig build docs

# フォーマット
zig build fmt

実践的なbuild.zigの例

完全なプロジェクト構成

const std = @import("std");

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

    // ビルドオプション
    const enable_tests = b.option(bool, "enable-tests", "Enable test builds") orelse true;
    const enable_docs = b.option(bool, "enable-docs", "Enable documentation") orelse false;

    // ライブラリのビルド
    const lib = b.addStaticLibrary(.{
        .name = "mylib",
        .root_source_file = b.path("src/lib.zig"),
        .target = target,
        .optimize = optimize,
    });

    b.installArtifact(lib);

    // 実行ファイルのビルド
    const exe = b.addExecutable(.{
        .name = "myapp",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    exe.linkLibrary(lib);
    b.installArtifact(exe);

    // 実行ステップ
    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());

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

    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);

    // テストステップ
    if (enable_tests) {
        const lib_tests = b.addTest(.{
            .root_source_file = b.path("src/lib.zig"),
            .target = target,
            .optimize = optimize,
        });

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

        const run_lib_tests = b.addRunArtifact(lib_tests);
        const run_exe_tests = b.addRunArtifact(exe_tests);

        const test_step = b.step("test", "Run unit tests");
        test_step.dependOn(&run_lib_tests.step);
        test_step.dependOn(&run_exe_tests.step);
    }

    // ドキュメントステップ
    if (enable_docs) {
        const docs_cmd = b.addSystemCommand(&[_][]const u8{
            "zig", "test", "-femit-docs", "src/lib.zig",
        });

        const docs_step = b.step("docs", "Generate documentation");
        docs_step.dependOn(&docs_cmd.step);
    }

    // クリーンステップ
    const clean_step = b.step("clean", "Clean build artifacts");
    const rm_cmd = b.addRemoveDirTree(b.install_path);
    clean_step.dependOn(&rm_cmd.step);
}

使用例:

# デバッグビルド
zig build

# リリースビルド
zig build -Doptimize=ReleaseFast

# テスト実行
zig build test

# ドキュメント生成
zig build docs -Denable-docs=true

# クロスコンパイル
zig build -Dtarget=x86_64-windows

# ビルドしてすぐ実行
zig build run

まとめ

この章では、Zigのビルドシステムについて学びました:

  • build.zigの基本: 実行ファイル、ライブラリ、テストのビルド
  • 最適化: Debug、ReleaseSafe、ReleaseFast、ReleaseSmall
  • クロスコンパイル: マルチプラットフォームビルド
  • 依存関係: ローカルパッケージ、モジュール、外部ライブラリ
  • カスタムステップ: ファイル生成、コマンド実行
  • これでZig Foundationsコースは完了です!

    参考資料

  • Zig Language Reference - Build System: https://ziglang.org/documentation/master/#Build-System
  • Zig Build System: https://ziglang.org/learn/build-system/
  • Ziglearn - Build System: https://ziglearn.org/chapter-3/