第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);
}
各最適化レベルの特徴:
カスタムビルドオプション
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 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/
これでZig Foundationsコースは完了です!