Your first build
A zut workspace is a directory tree with BUILD files. Each BUILD file
declares targets; a target is an instance of a rule (like rust_binary)
with some attributes.
A minimal example
Let’s build a Rust binary that links a Zig static library. Create this layout:
myproject/
BUILD
src/main.rs
greet.zig
greet.zig — a tiny C-ABI function:
export fn zut_add(a: i32, b: i32) callconv(.c) i32 {
return a + b;
}
src/main.rs — calls into it:
extern "C" { fn zut_add(a: i32, b: i32) -> i32; }
fn main() {
println!("2 + 3 = {}", unsafe { zut_add(2, 3) });
}
BUILD — wire them together using the built-in prelude:
load("@builtin//:rust.bzl", "rust_binary")
load("@builtin//:zig.bzl", "zig_library")
zig_library(
name = "greet",
src = "greet.zig",
libname = "greet",
)
rust_binary(
name = "hello",
crate_root = "src/main.rs",
srcs = ["src/main.rs"],
edition = "2021",
out = "hello",
native_deps = [":greet"],
)
Build it
$ zut build //:hello
zut build //:hello
sandbox: namespace (recursive ro host, net+pid isolated)
toolchain: rust[1.xx] zig[0.16.0]
jobs: 8
libgreet.a [zig] built
hello [rustc] built
-> zut-out/hello
done — 2 built, 0 cached
$ ./zut-out/hello
2 + 3 = 5
The label //:hello means “the target named hello in the BUILD file at the
workspace root”. A target in a subdirectory foo/bar is //foo/bar:name.
Incrementality in action
Run the same command again, unchanged:
$ zut build //:hello
...
up to date — 2 action(s), all cached
Nothing rebuilds: zut hashed the action inputs, found the outputs already in its
content-addressed store, and served them. Now touch only the Rust source and
rebuild — only the rustc action re-runs; the Zig library is still a cache hit,
because its inputs didn’t change.
Build state lives under .zut/ in your workspace (the CAS, the action cache,
and a work directory). Delete .zut/ to start cold; it is safe to .gitignore.