zig のビルドシステム
zig-0.13.0
zig build とは何か
コマンドラインから zig build
するとビルドシステムが実行されます。
Step 引数を指定しない場合は、デフォルトの Step
が発動します。
Step の内容は build.zig
により記述されます。
zig init で生成される build.zig を読む
$ mkdir zig_hello
$ cd zig_hello
$ zig init
$ fd
build.zig
build.zig.zon
src\
src\main.zig
src\root.zig
Step 確認
> zig build --help
Usage: zig.exe build [steps] [options]
Steps:
# 👇 zig build(step 引数無し) とするとデフォルトの `install` Step が実行される
install (default) Copy build artifacts to prefix path
uninstall Remove build artifacts from prefix path
# 👇 b.step("run", "Run the app") で登録
run Run the app
# 👇 b.step("test", "Run unit tests") で登録
test Run unit tests
build.zig の内容
lib
src/root.zig
を root とした lib をビルドする。
const lib = b.addStaticLibrary(.{
.name = "hello_build",
// In this case the main source file is merely a path, however, in more
// complicated build scripts, this could be a generated file.
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
// This declares intent for the library to be installed into the standard
// location when the user invokes the "install" step (the default step when
// running `zig build`).
b.installArtifact(lib); // 👈 install step に artifact を追加する。zig-out へ
exe
src/main.zig
の pub fn main
をエントリーポイントとした exe をビルドする。
const exe = b.addExecutable(.{
.name = "hello_build",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// This declares intent for the executable to be installed into the
// standard location when the user invokes the "install" step (the default
// step when running `zig build`).
b.installArtifact(exe); // 👈 install step に artifact を追加する。zig-out へ
run
exe をビルドする。
exe を実行する。
zig build の引数を exe に渡す。
// This *creates* a Run step in the build graph, to be executed when another
// step is evaluated that depends on it. The next line below will establish
// such a dependency.
const run_cmd = b.addRunArtifact(exe); // 👈 Run Step 追加
// By making the run step depend on the install step, it will be run from the
// installation directory rather than directly from within the cache directory.
// This is not necessary, however, if the application depends on other installed
// files, this ensures they will be present and in the expected location.
run_cmd.step.dependOn(b.getInstallStep()); // 👈 Run Step は Install step に依存
// This allows the user to pass arguments to the application in the build
// command itself, like this: `zig build run -- arg1 arg2 etc`
if (b.args) |args| {
run_cmd.addArgs(args); // 👈Run Step にコマンドライン引数追加
}
// This creates a build step. It will be visible in the `zig build --help` menu,
// and can be selected like this: `zig build run`
// This will evaluate the `run` step rather than the default, which is "install".
const run_step = b.step("run", "Run the app"); // 👈 zig build に `run` を登録
run_step.dependOn(&run_cmd.step); // 👈 zig build run がRun Step に依存
test
lib と exe を両方ビルドしてテストする。
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const lib_unit_tests = b.addTest(.{
.root_source_file = b.path("src/root.zig"),
.target = target,
.optimize = optimize,
});
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
const exe_unit_tests = b.addTest(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
// Similar to creating the run step earlier, this exposes a `test` step to
// the `zig build --help` menu, providing a way for the user to request
// running the unit tests.
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);
test_step.dependOn(&run_exe_unit_tests.step);
最小まで削る
exe のビルドに必要な最小まで削ってみる。
build.zig
src\main.zig
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// *std.Build.Step.Compile を作る
const exe = b.addExecutable(.{
.name = "zig_hello",
.target = target,
.optimize = optimize,
// std.Build.LazyPath
.root_source_file = b.path("src/main.zig"), // zig のエントリーポイント
});
// ビルド対象に追加して zig-out に出てくるようにする
b.installArtifact(exe);
}
zig build-system
Step.Compile 詳細
@import の登録
zig の @import は2種類ある。
@import("path_to.zig")
と
@import("pkg_name")
@import の返り値は struct(type) であり、
const std = @import("std");
のように使う。
@import("relative_path_to.zig")
@import
を記述する .zig
からの相対パスで指定する。
@import("pkg_name") の登録
// module を作る
const tinygizmo = b.createModule(.{
.root_source_file = b.path("tinygizmo/main.zig"),
});
// module を @import できるように登録する
exe.root_module.addImport("tinygizmo", tinygizmo);
c のライブラリとリンクする
TODO: raylib の例
build.zig
は cmake
と似ていて、
まずCのコンパイルを通す(@cImport できるようにする)。
次にリンクを解決するという2段階で考えます(cやcppをコンパイルしてリンクする)。
c のエントリーポイント
普通に使える。
#include <iostream>
int main(int argc, char **argv) {
std::cout << "hello" << std::endl;
return 0;
}
const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "cpp_entry",
// .root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
exe.addCSourceFile(.{
.file = b.path("main.cpp"),
});
exe.linkLibCpp(); // #include <iostream> に必要
b.installArtifact(exe);
}
zon 経由の外部依存
build.zig.zon の dependencies を使う
c/c++ のソースを download する
sokol で imgui と cimgui のソースを得る例。
ここで docking
ブランチを指定することもできた。
sokol-zig を使う例
zon の dependency を追加
.dependencies = .{
.sokol = .{
.url = "https://github.com/floooh/sokol-zig/archive/refs/heads/master.zip",
},
hash
> zig build
C:\sokol_sample\build.zig.zon:27:20: error: dependency is missing hash field
.url = "https://github.com/floooh/sokol-zig/archive/refs/heads/master.zip",
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
note: expected .hash = "1220ce851df99fd0ddeb6f20e75cdd35dd4e24404f7de7a2aa4cc4f37de58f039cab",
とりあえずビルドすると hash
値が表示されるので、これを zon に追記します。
.dependencies = .{
.sokol = .{
.url = "https://github.com/floooh/sokol-zig/archive/refs/heads/master.zip",
.hash = "1220ce851df99fd0ddeb6f20e75cdd35dd4e24404f7de7a2aa4cc4f37de58f039cab",
},
}
import の追加
// zon の dependency sokol
const sokol_dep = b.dependency("sokol", .{
.target = target,
.optimize = optimize,
});
// zon の dependency から "sokol" import を取り出して、 exe に追加する。
exe.root_module.addImport("sokol", sokol_dep.module("sokol"));
import できるようになります。
const sokol = @import("sokol");
を src/main.zig に上書きしたら動きました。
target
ターゲットを変更することでクロスコンパイルできる。
target の指定方法
コマンドラインから
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
に対して、以下のようにする。
$ zig build -Dtarget=wasm32-emscripten
build.zig にハードコーディングする
const target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
}),
wasm32-freestanding
を zig-0.13.0
向けにしてみた。
const std = @import("std");
pub fn build(b: *std.Build) void {
// addSharedLibrary ではなく
// exe に entry = .disabled を指定する
const exe = b.addExecutable(.{
.name = "wasmtest",
.root_source_file = b.path("src/main.zig"),
// -target wasm32-freestanding
.target = b.resolveTargetQuery(.{
.cpu_arch = .wasm32,
.os_tag = .freestanding,
}),
.optimize = b.standardOptimizeOption(.{}),
.version = .{ .major = 0, .minor = 0, .patch = 1 },
});
exe.rdynamic = true;
// 👇
exe.entry = .disabled;
b.installArtifact(exe);
}
wasm32-emscripten
Discussion