{"id":51063596,"url":"https://github.com/deblasis/ziobuild","last_synced_at":"2026-06-23T04:30:29.532Z","repository":{"id":355426839,"uuid":"1224771232","full_name":"deblasis/ziobuild","owner":"deblasis","description":"Declarative build.zig DSL. Collapse 80+ line build files into a dozen calls.","archived":false,"fork":false,"pushed_at":"2026-05-12T18:11:11.000Z","size":122,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-12T18:17:56.008Z","etag":null,"topics":["build-system","build-zig","cross-compile","dsl","zig"],"latest_commit_sha":null,"homepage":null,"language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/deblasis.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":["deblasis"],"ko_fi":"deblasis","custom":"deblasis.eth"}},"created_at":"2026-04-29T15:58:49.000Z","updated_at":"2026-05-12T18:11:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/deblasis/ziobuild","commit_stats":null,"previous_names":["deblasis/ziobuild"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/deblasis/ziobuild","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deblasis%2Fziobuild","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deblasis%2Fziobuild/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deblasis%2Fziobuild/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deblasis%2Fziobuild/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/deblasis","download_url":"https://codeload.github.com/deblasis/ziobuild/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/deblasis%2Fziobuild/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34675970,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-23T02:00:07.161Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build-system","build-zig","cross-compile","dsl","zig"],"created_at":"2026-06-23T04:30:28.735Z","updated_at":"2026-06-23T04:30:29.527Z","avatar_url":"https://github.com/deblasis.png","language":"Zig","funding_links":["https://github.com/sponsors/deblasis","https://ko-fi.com/deblasis","deblasis.eth"],"categories":[],"sub_categories":[],"readme":"# ziobuild\n\nDeclarative `build.zig` DSL. Comptime helpers that wrap `std.Build` and collapse 80+ line build files into a dozen calls. Nothing is hidden: every helper that produces an artifact returns the underlying `*std.Build.Step.Compile` so you can drop down to raw `std.Build` whenever you want.\n\n## The pitch\n\nA typical project has one app, internal modules, tests, examples, and a release matrix. Vanilla `build.zig` for a multi-module project:\n\n```zig\nconst std = @import(\"std\");\n\npub fn build(b: *std.Build) void {\n    const target = b.standardTargetOptions(.{});\n    const optimize = b.standardOptimizeOption(.{});\n\n    const mylib_mod = b.addModule(\"mylib\", .{\n        .root_source_file = b.path(\"src/lib.zig\"),\n        .target = target,\n    });\n\n    const exe_mod = b.createModule(.{\n        .root_source_file = b.path(\"src/main.zig\"),\n        .target = target,\n        .optimize = optimize,\n    });\n    exe_mod.addImport(\"mylib\", mylib_mod);\n\n    const exe = b.addExecutable(.{ .name = \"myapp\", .root_module = exe_mod });\n    b.installArtifact(exe);\n    // ... 40+ more lines for run, test, examples, releases\n}\n```\n\nWith ziobuild:\n\n```zig\nconst std = @import(\"std\");\nconst zb = @import(\"ziobuild\");\n\npub fn build(b: *std.Build) void {\n    const ctx = zb.init(b, .{ .name = \"myapp\" });\n\n    _ = ctx.module(\"mylib\", .{ .root = \"src/lib.zig\" });\n\n    const app = ctx.app(.{\n        .root = \"src/main.zig\",\n        .mod_imports = \u0026.{\"mylib\"},\n    });\n    _ = ctx.tests(.{\n        .root = \"src/main.zig\",\n        .mod_imports = \u0026.{\"mylib\"},\n    });\n    _ = ctx.examples(\"examples/*/main.zig\");\n    _ = ctx.releases(.{\n        .of = app,\n        .targets = \u0026.{ .linux_x64, .darwin_arm64, .windows_x64 },\n    });\n    ctx.help();\n}\n```\n\n14 lines. Same artifacts, same step graph.\n\n## Install\n\n`build.zig.zon`:\n\n```zig\n.dependencies = .{\n    .ziobuild = .{\n        .url = \"https://github.com/deblasis/ziobuild/archive/refs/tags/v0.4.0.tar.gz\",\n        .hash = \"...\",  // zig prints the right value on first fetch\n    },\n},\n```\n\n`build.zig`:\n\n```zig\nconst zb = @import(\"ziobuild\");\n```\n\n## What's new in v0.4\n\n- **`Expr` \u0026 `ctx.patch()`**: composable build-time expressions and conditional dependency patching.\n- **`ctx.overlay()`**: replace files in a dependency's source tree without git.\n- **`Expr.envVar()`**: branch on environment variables (e.g., `CI`, `TARGET`).\n\n## What's new in v0.3\n\n- **Deferred resolution**: modules can be declared in any order.\n- **`Dep.mod`**: renamed from `module_registry` -- shorter, cleaner.\n- **`mod_imports`**: shorthand `[]const []const u8` for the common case of importing modules by name.\n- **`import_all`**: import ALL registered modules in one flag.\n- **`ctx.finalize()`**: explicit resolution trigger (usually unnecessary since `help()` auto-finalizes).\n\n## Quickstart\n\n### Simple project\n\n```zig\nconst std = @import(\"std\");\nconst zb = @import(\"ziobuild\");\n\npub fn build(b: *std.Build) void {\n    const ctx = zb.init(b, .{ .name = \"myapp\" });\n    const app = ctx.app(.{ .root = \"src/main.zig\" });\n    _ = ctx.tests(.{ .root = \"src/main.zig\" });\n    _ = ctx.examples(\"examples/*/main.zig\");\n    _ = ctx.releases(.{\n        .of = app,\n        .targets = \u0026.{ .linux_x64, .darwin_arm64, .windows_x64 },\n    });\n    ctx.help();\n}\n```\n\n### Multi-module project (order-independent)\n\n```zig\nconst std = @import(\"std\");\nconst zb = @import(\"ziobuild\");\n\npub fn build(b: *std.Build) void {\n    const ctx = zb.init(b, .{ .name = \"myapp\" });\n\n    // Order doesn't matter -- deferred resolution\n    _ = ctx.module(\"core\", .{ .root = \"src/core.zig\" });\n    _ = ctx.module(\"utils\", .{\n        .root = \"src/utils.zig\",\n        .mod_imports = \u0026.{\"core\"},\n    });\n\n    const app = ctx.app(.{\n        .root = \"src/main.zig\",\n        .mod_imports = \u0026.{ \"core\", \"utils\" },\n    });\n\n    _ = ctx.testModules(.{});\n    _ = ctx.examples(\"examples/*/main.zig\");\n    _ = ctx.releases(.{\n        .of = app,\n        .targets = \u0026.{ .linux_x64, .darwin_arm64, .windows_x64 },\n    });\n    ctx.help();\n}\n```\n\n### Aggregator module that imports everything\n\n```zig\n_ = ctx.module(\"cli\", .{\n    .root = \"src/cli.zig\",\n    .import_all = true,  // gets core, utils, etc. automatically\n});\n```\n\n## API\n\n### `zb.init(b, opts) -\u003e Context`\n\nEntry point. `opts.name` is the default executable name.\n\n### `ctx.module(name, opts) -\u003e *Module`\n\nRegister a named module. **Order-independent** (deferred resolution).\n\nThree ways to declare imports:\n\n| Field | Type | Description |\n|---|---|---|\n| `imports` | `[]const Dep` | Full control -- `Dep.mod`, `Dep.zon_dep`, `Dep.direct` |\n| `mod_imports` | `[]const []const u8` | Shorthand: each string imports that module by name |\n| `import_all` | `bool` | Import ALL registered modules (self excluded) |\n\n### `ctx.app(opts) -\u003e *Compile`\n\nBuild an executable. Also supports `mod_imports` and `import_all`.\n\n### `ctx.lib(opts) -\u003e *Compile`\n\nBuild a library. Also supports `mod_imports` and `import_all`.\n\n### `ctx.tests(opts) -\u003e *Compile`\n\nDeclare a test compile. Also supports `mod_imports` and `import_all`.\n\n### `ctx.testModules(opts) -\u003e []const *Compile`\n\nCreate a test compile for every registered module and aggregate under a single step.\n\n### `ctx.examples(pattern) -\u003e []const *Compile`\n\nGlob-walk and register one executable per match. Use `examplesWithImports` for imports.\n\n### `ctx.releases(opts) -\u003e []const *Compile`\n\nBuild one executable per release target. Presets: `.linux_x64`, `.linux_arm64`, `.darwin_x64`, `.darwin_arm64`, `.windows_x64`, `.windows_arm64`.\n\n### `ctx.help()`\n\nPrint a tidy step table. Also triggers deferred import resolution -- call this last.\n\n### `ctx.finalize()`\n\nExplicit resolution trigger. Only needed if you don't call `help()`.\n\n### `ctx.patch(dep_name, opts)`\n\nRegister a conditional patch for a dependency. `opts.file` is the patch path (relative to build root), `opts.when` is an `Expr`, and `opts.strip` is the `-p` level (defaults to 1). Requires `git`.\n\n### `ctx.overlay(dep_name, opts)`\n\nRegister a conditional file overlay for a dependency. `opts.dir` is the overlay directory (relative to build root), `opts.when` is an `Expr`. Files from `dir` are copied into the dep's source tree. No git required.\n\n### `Expr`\n\nComposable build-time expression. See [Expressions \u0026 Conditional Patching](#expressions--conditional-patching).\n\n## Dependency resolution: the `Dep` type\n\n```zig\npub const Dep = union(enum) {\n    mod: []const u8,       // resolved from ctx.module() registry\n    zon_dep: []const u8,   // resolved from build.zig.zon\n    direct: struct {       // a pre-built *Module\n        name: []const u8,\n        module: *std.Build.Module,\n    },\n};\n```\n\n### Deferred resolution\n\nImports are resolved lazily, not at registration time. **Modules can be declared in any order.** Resolution is incremental — calling `ensureResolved()` (via `help()`, `testModules()`, `releases()`, or `finalize()`) at any point only resolves entries registered so far; later registrations are processed on the next call. This means `testModules()` can safely be called before `app()` without skipping the app's imports.\n\n### `mod_imports` shorthand\n\n```zig\n.mod_imports = \u0026.{\"core\", \"utils\", \"models\"}\n// equivalent to:\n// .imports = \u0026.{ .{ .mod = \"core\" }, .{ .mod = \"utils\" }, .{ .mod = \"models\" } }\n```\n\n### `import_all` flag\n\nImport ALL registered modules by name. Self-import excluded for `ctx.module()`.\n\n## Build option helpers\n\n```zig\nconst emit_bench = zb.boolOption(b, \"emit-bench\", false, \"Emit benchmark artifacts\");\nconst mode = zb.enumOption(b, enum { native, wasm }, \"runtime\", .native, \"App runtime mode\");\nconst count = zb.intOption(b, u32, \"count\", 10, \"Number of items\");\nconst name = zb.stringOption(b, \"name\", null, \"Override name\");\n```\n\n## Expressions \u0026 Conditional Patching\n\nziobuild provides a composable `Expr` type for build-time predicates and a `ctx.patch()` API that conditionally applies `.patch` files to dependency source trees.\n\n### The `Expr` type\n\nLeaf constructors create primitive predicates:\n\n| Constructor | Evaluates against |\n|---|---|\n| `Expr.zigVersion(.gte, \"0.16.0\")` | Zig compiler version |\n| `Expr.targetOs(.linux)` | Resolved target OS |\n| `Expr.targetArch(.x86_64)` | Resolved target arch |\n| `Expr.optimizeMode(.ReleaseFast)` | Context optimize mode |\n| `Expr.envVar(\"CI\", \"true\")` | Environment variable |\n| `Expr.literal(true)` | Always true/false (also useful with pre-resolved build options) |\n\nCombinators compose them:\n\n```zig\nconst needs_fix = Expr.zigVersion(.gte, \"0.16.0\")\n    .andAlso(Expr.targetOs(.linux), b.allocator);\n\nconst is_dev = Expr.optimizeMode(.Debug)\n    .orElse(Expr.optimizeMode(.ReleaseSafe), b.allocator);\n\nconst not_windows = Expr.targetOs(.windows).not(b.allocator);\n```\n\nEvaluate against the current build:\n\n```zig\nif (needs_fix.evaluate(b, ctx.target, ctx.optimize)) {\n    // conditional build logic\n}\n```\n\nVersion comparison operators: `lt`, `lte`, `eq`, `gte`, `gt`, `neq`.\n\n### Conditional patching\n\nApply `.patch` files to dependencies when a condition holds:\n\n```zig\nctx.patch(\"my_dep\", .{\n    .file = \"patches/my_dep/fix-zig-0.16.patch\",\n    .when = zb.Expr.zigVersion(.gte, \"0.16.0\"),\n    .strip = 1,  // -p1 (default)\n});\n```\n\nMultiple patches per dep — applied in registration order:\n\n```zig\nctx.patch(\"my_dep\", .{\n    .file = \"patches/my_dep/fix-zig-0.16.patch\",\n    .when = zb.Expr.zigVersion(.gte, \"0.16.0\"),\n});\nctx.patch(\"my_dep\", .{\n    .file = \"patches/my_dep/fix-linux.patch\",\n    .when = zb.Expr.targetOs(.linux),\n});\n```\n\nComposed conditions:\n\n```zig\nctx.patch(\"my_dep\", .{\n    .file = \"patches/my_dep/fix-linux-0.16.patch\",\n    .when = zb.Expr.zigVersion(.gte, \"0.16.0\")\n        .andAlso(zb.Expr.targetOs(.linux), b.allocator),\n});\n```\n\n**How it works:** Patches are applied at dependency resolution time using `git apply`. The operation is idempotent — if a patch is already applied, it is silently skipped. If a patch conflicts with the source (neither forward nor reverse applies), the build fails with a clear error message. Requires `git` on `$PATH`.\n\n### File overlays (no git)\n\nReplace files in a dependency's source tree without `git`. Copy files from an overlay directory into the dep:\n\n```zig\nctx.overlay(\"my_dep\", .{\n    .dir = \"overlays/my_dep\",\n    .when = zb.Expr.targetOs(.windows),\n});\n```\n\nThe overlay directory structure mirrors the dependency's source tree. Every file in `overlays/my_dep/` overwrites the corresponding file in the dep. No git required — uses direct file copies.\n\n```text\noverlays/my_dep/\n  src/root.zig        ← replaces dep's src/root.zig\n  src/platform.zig    ← replaces dep's src/platform.zig\n```\n\n**Filesystem convention:** Store patches in `patches/\u003cdep-name\u003e/` at your project root. Patches are standard unified diffs.\n\nSee `examples/conditional_patching/` for a complete working example.\n\n## Drop down to raw `std.Build`\n\nEvery helper returns the underlying `*Compile`. Use it.\n\n```zig\nconst app = ctx.app(.{ .root = \"src/main.zig\" });\napp.root_module.addCSourceFile(.{ .file = b.path(\"src/foo.c\") });\napp.linkLibC();\n```\n\n## Migration from v0.3 to v0.4\n\n- New: `Expr` type for composable build-time predicates.\n- New: `ctx.patch()` for conditional dependency patching (requires git).\n- New: `ctx.overlay()` for conditional file overlays (no git needed).\n- New: `Expr.envVar()` for environment-based conditions.\n- Removed: `Expr.buildOptionBool` / `Expr.buildOptionString` (broken — use `Expr.literal(resolved_value)` or plain Zig conditionals).\n\n## Migration from v0.2 to v0.3\n\n- `Dep.module_registry` renamed to `Dep.mod`.\n- Modules can now be declared in any order (deferred resolution).\n- New: `mod_imports` and `import_all` fields on `module`, `app`, `tests`, `lib`.\n- `ctx.resolveDeps()` removed from public API (internal, called automatically).\n- New: `ctx.finalize()`.\n\n## License\n\nMIT. Copyright Alessandro De Blasis.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeblasis%2Fziobuild","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdeblasis%2Fziobuild","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdeblasis%2Fziobuild/lists"}