zig
toolchain (build, build-exe, run, test, fmt, version, env, targets) sodownstream pipelines can build, test, format, and cross-compile Zig without
re-inventing toolchain pinning and container plumbing.
Toolchain version is pinned via New(version) or inferred from the source's
build.zig.zon `minimum_zig_version`; falls back to a module-pinned default.
There is no canonical official Zig image, so the base container downloads
the official release tarball from ziglang.org (via dag.HTTP, SHA256-verified
against download/index.json) and unpacks it onto a minimal alpine base with
`zig` on PATH and a shared zig-cache cache volume mounted.
Installation
dagger install github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3Entrypoint
Return Type
Zig !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| version | String | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
func (m *MyModule) Example() *dagger.Zig {
return dag.
Zig()
}@function
def example() -> dagger.Zig:
return (
dag.zig()
)@func()
example(): Zig {
return dag
.zig()
}Types
Zig 🔗
Zig wraps the Zig toolchain as Dagger functions. Construct via New(); call Container() for the prepared base container, or use the per-CLI helpers (Build, BuildExe, Run, Test, Fmt, …) which reuse the same backing container.
version() 🔗
Version is the pinned Zig toolchain version (e.g. “0.14.1”). Empty means infer from the supplied source’s build.zig.zon
minimum_zig_version; falls back to a module-pinned default.
Return Type
String ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
versionfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Zig().
Version(ctx)
}@function
async def example() -> str:
return await (
dag.zig()
.version()
)@func()
async example(): Promise<string> {
return dag
.zig()
.version()
}build() 🔗
Build runs zig build [-Doptimize=<optimize>] [-Dtarget=<target>] [steps...]
[args...] against the supplied source and returns the zig-out install
directory.
optimize, when non-empty, must be one of Debug, ReleaseSafe, ReleaseFast, ReleaseSmall and is rejected otherwise. Empty optimize and empty target build for the host.
steps and args are both appended to the zig build command line and are
interpreted by the build system (steps name build steps; args are additional
build-system arguments/options) — neither is forwarded to a built program. To
pass arguments to the program itself, use Run, which inserts the --
separator zig build run expects.
Return Type
Directory !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | No description provided |
| optimize | String | - | No description provided |
| target | String | - | No description provided |
| steps | [String ! ] | - | No description provided |
| args | [String ! ] | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
build --source DIR_PATHfunc (m *MyModule) Example(source *dagger.Directory) *dagger.Directory {
return dag.
Zig().
Build(source)
}@function
def example(source: dagger.Directory) -> dagger.Directory:
return (
dag.zig()
.build(source)
)@func()
example(source: Directory): Directory {
return dag
.zig()
.build(source)
}buildExe() 🔗
BuildExe runs zig build-exe <root> [-O <optimize>] [-target <target>]
--name <name> [args...] for a single entry file and returns the produced
executable.
root is required (the single entry .zig file). optimize, when non-empty, must be one of Debug, ReleaseSafe, ReleaseFast, ReleaseSmall. name defaults to the basename of root with any trailing “.zig” stripped.
Note the flag spelling differs from Build: build-exe uses -O / -target / –name (compiler flags), not the -Doptimize= / -Dtarget= build-system options.
Return Type
File !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | No description provided |
| root | String ! | - | No description provided |
| optimize | String | - | No description provided |
| target | String | - | No description provided |
| name | String | - | No description provided |
| args | [String ! ] | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
build-exe --source DIR_PATH --root stringfunc (m *MyModule) Example(source *dagger.Directory, root string) *dagger.File {
return dag.
Zig().
Buildexe(source, root)
}@function
def example(source: dagger.Directory, root: str) -> dagger.File:
return (
dag.zig()
.buildexe(source, root)
)@func()
example(source: Directory, root: string): File {
return dag
.zig()
.buildExe(source, root)
}cc() 🔗
Cc cross-compiles C source with zig cc, a clang frontend that bundles libc
and headers for every supported target, so cross-compilation needs no sysroot
setup. files is required (the C source files, relative to source). target,
when non-empty, sets clang’s -target triple (e.g. “x86_64-windows-gnu”).
outputName names the produced artifact (default “a.out”); extra clang flags
pass through via args. Returns the artifact as a *dagger.File.
outputName (CLI: –output-name) is named so to avoid colliding with the Dagger CLI’s top-level –output/-o flag — same precedent as go’s Ci.WithBuild.
Return Type
File !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | No description provided |
| files | [String ! ] ! | - | No description provided |
| target | String | - | No description provided |
| outputName | String ! | "a.out" | No description provided |
| args | [String ! ] | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
cc --source DIR_PATH --files string1 --files string2 --output-name stringfunc (m *MyModule) Example(source *dagger.Directory, files []string, outputName string) *dagger.File {
return dag.
Zig().
Cc(source, files, outputName)
}@function
def example(source: dagger.Directory, files: List[str], outputname: str) -> dagger.File:
return (
dag.zig()
.cc(source, files, outputname)
)@func()
example(source: Directory, files: string[], outputName: string): File {
return dag
.zig()
.cc(source, files, outputName)
}ci() 🔗
Ci returns a new pipeline builder bound to the supplied source.
Return Type
Ci !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
ci --source DIR_PATHfunc (m *MyModule) Example(source *dagger.Directory) *dagger.ZigCi {
return dag.
Zig().
Ci(source)
}@function
def example(source: dagger.Directory) -> dagger.ZigCi:
return (
dag.zig()
.ci(source)
)@func()
example(source: Directory): ZigCi {
return dag
.zig()
.ci(source)
}container() 🔗
Container returns the prepared base container with the zig toolchain on PATH, the shared zig-cache cache volume mounted, source mounted at /src, and the working directory set to /src. Use this as an escape hatch when a Zig command isn’t covered by the typed helpers.
The toolchain is downloaded from ziglang.org at the version from New() or,
when New(“”) was used, from source/build.zig.zon’s minimum_zig_version
(falling back to the module-pinned default). The signature takes ctx +
returns error because version inference and the tarball download require
async I/O.
Return Type
Container !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
container --source DIR_PATHfunc (m *MyModule) Example(source *dagger.Directory) *dagger.Container {
return dag.
Zig().
Container(source)
}@function
def example(source: dagger.Directory) -> dagger.Container:
return (
dag.zig()
.container(source)
)@func()
example(source: Directory): Container {
return dag
.zig()
.container(source)
}cxx() 🔗
Cxx cross-compiles C++ source with zig c++ (the C++ frontend; bundles
libc++). Same parameters and semantics as Cc.
Return Type
File !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | No description provided |
| files | [String ! ] ! | - | No description provided |
| target | String | - | No description provided |
| outputName | String ! | "a.out" | No description provided |
| args | [String ! ] | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
cxx --source DIR_PATH --files string1 --files string2 --output-name stringfunc (m *MyModule) Example(source *dagger.Directory, files []string, outputName string) *dagger.File {
return dag.
Zig().
Cxx(source, files, outputName)
}@function
def example(source: dagger.Directory, files: List[str], outputname: str) -> dagger.File:
return (
dag.zig()
.cxx(source, files, outputname)
)@func()
example(source: Directory, files: string[], outputName: string): File {
return dag
.zig()
.cxx(source, files, outputName)
}env() 🔗
Env runs zig env in a source-less base container and returns its stdout
(JSON).
Return Type
String ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
envfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Zig().
Env(ctx)
}@function
async def example() -> str:
return await (
dag.zig()
.env()
)@func()
async example(): Promise<string> {
return dag
.zig()
.env()
}fmt() 🔗
Fmt runs zig fmt --check . against the supplied source, returning a non-nil
error that lists the offending files when any are unformatted, and nil when
the tree is clean — so CI fails fast on formatting violations.
zig fmt --check exits non-zero and prints the offending paths to stdout, so
the exec is run allowing any exit code and the paths are surfaced in the
returned error. Fmt returns only error (not the file list as a string)
because a Dagger function’s non-error return value is dropped at the GraphQL
boundary whenever it also returns a non-nil error: a (string, error)
signature would leave the file list unreachable on exactly the failure path
that needs it.
Return Type
Void !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
fmt --source DIR_PATHfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory) {
return dag.
Zig().
Fmt(ctx, source)
}@function
async def example(source: dagger.Directory) -> None:
return await (
dag.zig()
.fmt(source)
)@func()
async example(source: Directory): Promise<void> {
return dag
.zig()
.fmt(source)
}objCopy() 🔗
ObjCopy runs zig objcopy -O <format> <input> <output> in the toolchain
container, converting an ELF (e.g. from BuildExe) into a flashable artifact,
and returns the result as a *dagger.File. This is the post-build step every
embedded flow needs before flashing.
format selects the output target: “binary” (a raw .bin image) or “hex” (Intel
HEX). Any other value is rejected with a clear error — notably UF2 is out of
scope here, since zig objcopy can’t emit it.
outputName names the produced artifact and defaults to the input’s basename with its extension replaced by the format’s (.bin or .hex). Extra flags (e.g. –only-section, –pad-to) pass through via args.
outputName (CLI: –output-name) is named so to avoid colliding with the Dagger CLI’s top-level –output/-o flag — same precedent as Cc/Cxx.
Return Type
File !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| input | File ! | - | No description provided |
| format | String ! | "binary" | No description provided |
| outputName | String ! | "" | No description provided |
| args | [String ! ] | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
obj-copy --input file:path --format string --output-name stringfunc (m *MyModule) Example(input *dagger.File, format string, outputName string) *dagger.File {
return dag.
Zig().
Objcopy(input, format, outputName)
}@function
def example(input: dagger.File, format: str, outputname: str) -> dagger.File:
return (
dag.zig()
.objcopy(input, format, outputname)
)@func()
example(input: File, format: string, outputName: string): File {
return dag
.zig()
.objCopy(input, format, outputName)
}run() 🔗
Run runs zig build run [-- args...] against the supplied source and
returns the program’s stdout.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | No description provided |
| args | [String ! ] | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
run --source DIR_PATHfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory) string {
return dag.
Zig().
Run(ctx, source)
}@function
async def example(source: dagger.Directory) -> str:
return await (
dag.zig()
.run(source)
)@func()
async example(source: Directory): Promise<string> {
return dag
.zig()
.run(source)
}size() 🔗
Size reports the per-section byte totals of an ELF input (e.g. a freestanding BuildExe output) so CI can gate Flash/Ram against a target’s budget.
Zig ships no size subcommand, so this is computed in pure Go via debug/elf:
the input is exported and its section headers are read directly — no helper
container (per the established runtime-I/O pattern). A non-ELF input returns a
clear error.
Return Type
SectionSizes !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| input | File ! | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
size --input file:pathfunc (m *MyModule) Example(input *dagger.File) *dagger.ZigSectionSizes {
return dag.
Zig().
Size(input)
}@function
def example(input: dagger.File) -> dagger.ZigSectionSizes:
return (
dag.zig()
.size(input)
)@func()
example(input: File): ZigSectionSizes {
return dag
.zig()
.size(input)
}targets() 🔗
Targets runs zig targets in a source-less base container and returns its
stdout (the supported architecture/OS/ABI matrix).
Return Type
String ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
targetsfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Zig().
Targets(ctx)
}@function
async def example() -> str:
return await (
dag.zig()
.targets()
)@func()
async example(): Promise<string> {
return dag
.zig()
.targets()
}test() 🔗
Test runs zig build test when root is empty, else zig test <root>,
against the supplied source and returns the combined stdout.
Return Type
String !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| source | Directory ! | - | No description provided |
| root | String | - | No description provided |
| args | [String ! ] | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
test --source DIR_PATHfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory) string {
return dag.
Zig().
Test(ctx, source)
}@function
async def example(source: dagger.Directory) -> str:
return await (
dag.zig()
.test(source)
)@func()
async example(source: Directory): Promise<string> {
return dag
.zig()
.test(source)
}toolVersion() 🔗
ToolVersion runs zig version in a source-less base container and returns
the trimmed output (e.g. “0.14.1”).
Return Type
String ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
tool-versionfunc (m *MyModule) Example(ctx context.Context) string {
return dag.
Zig().
Toolversion(ctx)
}@function
async def example() -> str:
return await (
dag.zig()
.toolversion()
)@func()
async example(): Promise<string> {
return dag
.zig()
.toolVersion()
}Ci 🔗
Ci is a chained builder for a standardized Zig CI pipeline. Construct via Zig.Ci(source); enable check stages via the With* methods; call Run to execute checks-then-build, or Check to run only the parallel checks.
Stage 1 runs the enabled static checks in parallel (Fmt, Test); errors are aggregated. Stage 2 builds the source and Run returns the produced zig-out directory. Downstream consumers compose that directory into their own pipelines (package, sign, publish, …).
check() 🔗
Check runs the enabled check stages (Fmt, Test) in parallel via github.com/dagger/dagger/util/parallel and returns the aggregated error. Use when callers want to run the checks independently of the build (for example multi-target pipelines that share one check run across N target builds).
Return Type
Void ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
ci --source DIR_PATH \
checkfunc (m *MyModule) Example(ctx context.Context, source *dagger.Directory) {
return dag.
Zig().
Ci(source).
Check(ctx)
}@function
async def example(source: dagger.Directory) -> None:
return await (
dag.zig()
.ci(source)
.check()
)@func()
async example(source: Directory): Promise<void> {
return dag
.zig()
.ci(source)
.check()
}run() 🔗
Run executes the pipeline: stage 1 (Check) → stage 2 (build). Returns the produced zig-out directory. On stage-1 failure, returns the aggregated error from Check and a nil directory (stage 2 is skipped).
Return Type
Directory ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
ci --source DIR_PATH \
runfunc (m *MyModule) Example(source *dagger.Directory) *dagger.Directory {
return dag.
Zig().
Ci(source).
Run()
}@function
def example(source: dagger.Directory) -> dagger.Directory:
return (
dag.zig()
.ci(source)
.run()
)@func()
example(source: Directory): Directory {
return dag
.zig()
.ci(source)
.run()
}withBuild() 🔗
WithBuild configures the build stage parameters (forwarded to Zig.Build). optimize, when non-empty, must be one of Debug, ReleaseSafe, ReleaseFast, ReleaseSmall; target sets -Dtarget; steps names build steps. Build is always executed by Run regardless of whether this method is called.
Return Type
Ci !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| optimize | String | - | No description provided |
| target | String | - | No description provided |
| steps | [String ! ] | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
ci --source DIR_PATH \
with-buildfunc (m *MyModule) Example(source *dagger.Directory) *dagger.ZigCi {
return dag.
Zig().
Ci(source).
Withbuild()
}@function
def example(source: dagger.Directory) -> dagger.ZigCi:
return (
dag.zig()
.ci(source)
.withbuild()
)@func()
example(source: Directory): ZigCi {
return dag
.zig()
.ci(source)
.withBuild()
}withFmt() 🔗
WithFmt enables the zig fmt --check check stage.
Return Type
Ci ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
ci --source DIR_PATH \
with-fmtfunc (m *MyModule) Example(source *dagger.Directory) *dagger.ZigCi {
return dag.
Zig().
Ci(source).
Withfmt()
}@function
def example(source: dagger.Directory) -> dagger.ZigCi:
return (
dag.zig()
.ci(source)
.withfmt()
)@func()
example(source: Directory): ZigCi {
return dag
.zig()
.ci(source)
.withFmt()
}withTest() 🔗
WithTest enables the test check stage. root maps onto Zig.Test’s optional
root: empty runs zig build test; non-empty runs zig test <root>.
Return Type
Ci !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| root | String | - | No description provided |
Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
ci --source DIR_PATH \
with-testfunc (m *MyModule) Example(source *dagger.Directory) *dagger.ZigCi {
return dag.
Zig().
Ci(source).
Withtest()
}@function
def example(source: dagger.Directory) -> dagger.ZigCi:
return (
dag.zig()
.ci(source)
.withtest()
)@func()
example(source: Directory): ZigCi {
return dag
.zig()
.ci(source)
.withTest()
}SectionSizes 🔗
SectionSizes is the per-section footprint of an ELF, in bytes. Flash and Ram are the budget-relevant rollups: Flash is what the image occupies in flash, Ram what it claims at runtime.
text() 🔗
SHF_ALLOC, read-only: code (.text) + constants (.rodata)
Return Type
Integer ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
size --input file:path \
textfunc (m *MyModule) Example(ctx context.Context, input *dagger.File) int {
return dag.
Zig().
Size(input).
Text(ctx)
}@function
async def example(input: dagger.File) -> int:
return await (
dag.zig()
.size(input)
.text()
)@func()
async example(input: File): Promise<number> {
return dag
.zig()
.size(input)
.text()
}data() 🔗
SHF_ALLOC|SHF_WRITE, PROGBITS (initialized data)
Return Type
Integer ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
size --input file:path \
datafunc (m *MyModule) Example(ctx context.Context, input *dagger.File) int {
return dag.
Zig().
Size(input).
Data(ctx)
}@function
async def example(input: dagger.File) -> int:
return await (
dag.zig()
.size(input)
.data()
)@func()
async example(input: File): Promise<number> {
return dag
.zig()
.size(input)
.data()
}bss() 🔗
SHF_ALLOC|SHF_WRITE, NOBITS (zero-init data)
Return Type
Integer ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
size --input file:path \
bssfunc (m *MyModule) Example(ctx context.Context, input *dagger.File) int {
return dag.
Zig().
Size(input).
Bss(ctx)
}@function
async def example(input: dagger.File) -> int:
return await (
dag.zig()
.size(input)
.bss()
)@func()
async example(input: File): Promise<number> {
return dag
.zig()
.size(input)
.bss()
}flash() 🔗
Text + Data (consumes flash)
Return Type
Integer ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
size --input file:path \
flashfunc (m *MyModule) Example(ctx context.Context, input *dagger.File) int {
return dag.
Zig().
Size(input).
Flash(ctx)
}@function
async def example(input: dagger.File) -> int:
return await (
dag.zig()
.size(input)
.flash()
)@func()
async example(input: File): Promise<number> {
return dag
.zig()
.size(input)
.flash()
}ram() 🔗
Data + Bss (consumes RAM)
Return Type
Integer ! Example
dagger -m github.com/z5labs/devex/daggerverse/zig@bc5cee36080549722c6d3bf02152aa7d46d2dcf3 call \
size --input file:path \
ramfunc (m *MyModule) Example(ctx context.Context, input *dagger.File) int {
return dag.
Zig().
Size(input).
Ram(ctx)
}@function
async def example(input: dagger.File) -> int:
return await (
dag.zig()
.size(input)
.ram()
)@func()
async example(input: File): Promise<number> {
return dag
.zig()
.size(input)
.ram()
}