# Build System Tricks

**URL:** <https://ziggit.dev/t/build-system-tricks/3531>\
**Category:** Docs\
**Tags:** build-system, zig-master\
**Created:** [March 11, 2024, 8:51am UTC](https://ziggit.dev/t/build-system-tricks/3531 "2024-03-11T08:51:10Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![tensorush](https://ziggit.dev/letter_avatar_proxy/v4/letter/t/6a8cbe/32.png) [@tensorush](https://ziggit.dev/u/tensorush)\
**Post date:** [March 11, 2024, 8:51am UTC](https://ziggit.dev/t/build-system-tricks/3531/1 "2024-03-11T08:51:10Z")

</div>

This community-driven doc is an attempt to expand on the [official Zig Build System documentation](https://ziglang.org/learn/build-system/) as well as the past community contributions by [xq](https://zig.news/xq/series/2), [Sobeston](https://zig.guide/build-system/build-modes/) and others.

Here you’ll find a list of tricks on how to use every part of the build system while making sure to conveniently name and lay out your build steps.

## I. Basic Usage

### 1) Declare commonly used information upfront

```zig
// Resolve a query for a custom target
const my_target = b.resolveTargetQuery(.{
    .cpu_arch = .wasm32,
    .os_tag = .wasi,
});

// Get default install step (called with `zig build` or `zig build install`)
const install_step = b.getInstallStep();

// Resolve target triplet of "cpu-os-abi" (native is default)
const target = b.standardTargetOptions(.{});

// Resolve optimization mode (debug is default)
const optimize = b.standardOptimizeOption(.{});

// Specify common lazy paths relative to the build script
const root_source_file = b.path("src/main.zig");

// Define single semantic version for your library, examples, etc.
const version = std.SemanticVersion{ .major = 0, .minor = 1, .patch = 0 };

```

### 2) Declare user options upfront

```zig
const is_enabled = b.option(bool, "is_enabled", "Enable some capability") orelse false;

const options = b.addOptions();
options.addOption(bool, "is_enabled", is_enabled);

```

### 3) Declare dependencies as well as their artifacts and modules upfront

```zig
// Declare package dependency called "johnny" with your info
const johny_dep = b.dependency("johnny", .{
    .target = target,
    .optimize = optimize,
    // configure custom options
    .dep_option_foo = true,
    // pass string options as `[]const u8`
    .dep_option_bar = @as([]const u8, "bar_cfg_string"),
});

// Declare dependency's artifact with the name from its build script
const johnny_art = johnny_dep.artifact("johnny");

// Bundle artifact
b.installArtifact(johnny_art);
exe.step.dependOn(&johnny_art.step);

// Declare dependency's module with the name from its build script
const johnny_mod = johnny_dep.module("johnny");

```

### 4) Declare your library’s module and expose it to your users

```zig
const mod = b.addModule("my_mod", .{
    .target = target,
    .optimize = optimize,
    .root_source_file = root_source_file,
});

```

### 5) Declare every major `Compile` or `Run` step upfront

```zig
const exe_step = b.step("exe", "Run executable");

const exe = b.addExecutable(.{
    .name = "my_exe",
    .version = version,
    .root_module = b.createModule(.{
        .target = target,
        .optimize = optimize,
        .root_source_file = root_source_file,
    }),
});

// Add dependency module as the root module's import
exe.root_module.addImport("my_johnny_import", johnny_mod);

// Add user options as the root module's import
exe.root_module.addOptions("config", options);

```

### 6) Add build artifacts to depend on

```zig
// Add install artifact and depend the default step on it in one go
b.installArtifact(exe);

// Declare a separate run or install artifact step
const exe_run = b.addRunArtifact(exe);
const exe_install = b.addInstallArtifact(exe, .{});

// Pass CLI arguments to the run artifact
if (b.args) |args| {
    exe_run.addArgs(args);
}

// Optionally change the directory path to run the artifact in from current to custom
exe_run.setCwd(b.path("my-exe/"));

// Depend your pre-declared custom step (`zig build exe`) on the build artifact step
exe_step.dependOn(&exe_run.step);

// Depend the default step (`zig build`) on your custom step
install_step.dependOn(exe_step);

```

### 7) Add private modules for use only within your project

```zig
// Create private module
const hello_mod = b.createModule(.{
    .target = target,
    .optimize = optimize,
    .root_source_file = root_source_file,
});
// Add private module as import
exe.root_module.addImport("hello", hello_mod);

// Create and add private module in one go
exe.root_module.addAnonymousImport("hello", .{
    .target = target,
    .optimize = optimize,
    .root_source_file = root_source_file,
});

```

### 8) Handle failing cases

> #### Note: The build system prefers to avoid returning errors, instead choosing to panic. You should avoid using errors in the build script, as well.

- 
#### Depend on a fail step (mostly used to explicitly require an option for a step)

```zig
step.dependOn(&b.addFail("Requires -Doption").step);

```

- 
#### Declare a dummy root module (mostly used to enable running auxiliary build logic for unsupported targets)

```zig
// Public root module on Darwin or
// private dummy root module on non-Darwin
const root_mod = if (target.result.os.tag.isDarwin())
    b.addModule("my_mod", .{
        .target = target,
        .optimize = optimize,
        .root_source_file = root_source_file,
    })
else
    b.createModule(.{
        .target = target,
        .optimize = optimize,
        .root_source_file = root_source_file,
    });

```

- 
#### Log an error message if build can proceed

```zig
std.log.err("Unsupported OS: {s}!", .{@tagName(target.result.os.tag)});

```

- 
#### Panic with a message if build cannot proceed

```zig
// Either a simple string
@panic("Unsupported target!");
// Or with formatted arguments
std.debug.panic("Unsupported CPU: {s}!", .{@tagName(target.result.cpu.arch)});

```

### 9) Import public functions from dependency’s build script

```zig
// Assuming the dependency is declared as `.dep` in `build.zig.zon`
@import("dep").functionFromDependentBuildScript();

```

## II. Extra Steps

### 1) Emit documentation into `docs/` installed with `zig-out/` prefix

```zig
const docs_step = b.step("doc", "Emit documentation");

const docs_install = b.addInstallDirectory(.{
    .install_dir = .prefix,
    .install_subdir = "docs",
    .source_dir = lib.getEmittedDocs(),
});

docs_step.dependOn(&docs_install.step);
install_step.dependOn(docs_step);

```

### 2) Put static strings as global constants after the `build` function

```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    ...
}

const EXAMPLES_DIR = "examples/";

const EXAMPLE_NAMES = .{
    "example1",
    "example2",
};

```

### 3) Run your library’s example suite

```zig
const examples_step = b.step("example", "Run example suite");

inline for (EXAMPLE_NAMES) |EXAMPLE_NAME| {
    const example = b.addExecutable(.{
        .name = EXAMPLE_NAME,
        .version = version,
        .root_module = b.createModule(.{
            .target = target,
            .optimize = optimize,
            .root_source_file = b.path(EXAMPLES_DIR ++ EXAMPLE_NAME ++ "/main.zig"),
        }),
    });
    example.root_module.addImport("my_import", lib_mod);

    const example_run = b.addRunArtifact(example);
    examples_step.dependOn(&example_run.step);
}

install_step.dependOn(examples_step);

```

### 4) Run your test suite exposed with `std.testing.refAllDecls`

```zig
const tests_step = b.step("test", "Run tests");

const tests = b.addTest(.{
    .version = version,
    .root_module = b.createModule(.{
        .target = target,
        .root_source_file = root_source_file,
    }),
});

const tests_run = b.addRunArtifact(tests);
tests_step.dependOn(&tests_run.step);
install_step.dependOn(tests_step);

```

### 5) Generate code coverage report with the `kcov` system dependency

```zig
const cov_step = b.step("cov", "Generate code coverage");

const cov_run = b.addSystemCommand(&.{ "kcov", "--clean", "--include-pattern=src/", "kcov-output/" });
cov_run.addArtifactArg(tests);

cov_step.dependOn(&cov_run.step);
install_step.dependOn(cov_step);

```

### 6) Check formatting

```zig
const fmt_step = b.step("fmt", "Check formatting");

const fmt = b.addFmt(.{
    .paths = &.{
        "src/",
        "build.zig",
        "build.zig.zon",
    },
    .check = true,
});

fmt_step.dependOn(&fmt.step);
install_step.dependOn(fmt_step);

```

### 7) System tool tricks

```zig
// Check if a system tool exists
const echo_program = try b.findProgram(&.{"echo"}, &.{"check/this/path/", "and/this/one"});

// Get string output from a system tool
const string = b.run(&.{echo_program, "hello/world"});
const some_path = b.fmt("/my/other/path/{s}", .{string});

// Specify a system command as a build dependency
// Note: currently will not run as nothing depends on it!
const sys_command_build_dependency = b.addSystemCommand(&.{ echo_program, "I'm generating some text!" });

// Add a dependency on this command that takes the command's stdout and puts it in a file `zig-out/output.txt`
// Note: Now this does something!
install_step.dependOn(&b.addInstallFile(sys_command_build_dependency.captureStdOut(), "output.txt").step);

```

### 8) Code generation tricks

```zig
// Generating a .zig file from a system tool for use in build
const generate_zig_file_cmd = b.addSystemCommand(&.{ "python", "really_complex_zig_generator.py" });

// Passes "output_file.zig" as an arg to our really_complex_zig_generator.py script
const generated_zig_file = generate_zig_file_cmd.addOutputFileArg("output_file.zig");

// Making this .zig file importable in our main code
exe.root_module.addAnonymousImport("generated", .{
    .root_source_file = generated_zig_file,
});

// Let's do another step of processing! This time "additional_processing.py" takes in our previously generated .zig file,
// and generates an additional .zig file
const additional_generation_cmd = b.addSystemCommand(&.{ "python", "additional_processing.py" });

// Passes "output_file.zig" to the python script + establishes that this depends on the previous step completing
additional_generation_cmd.addFileArg(generate_zig_file_cmd);

// Same as before, passes "another_output_file.zig" as an arg
const additional_generated_zig_file = additional_generation_cmd.addOutputFileArg("another_output_file.zig");

// Making this additional .zig file importable in our main code
exe.root_module.addAnonymousImport("additionalGenerated", .{
    .root_source_file = additional_generated_zig_file,
});

// Generating a .zig file from another zig program in the workspace
// Assume another_exe was created with b.addExecutable(...), and takes the file path to generate as an arg
const another_exe_run_artifact = b.addRunArtifact(another_exe);
const zig_generated_zig_file = another_exe_run_artifact.addOutputFileArg("zig_generated_zig.zig");
exe.root_module.addAnonymousImport("zigGenerated", .{
    .root_source_file = zig_generated_zig_file,
});

```

### 9) Clean `zig-out` and `zig-cache` directories

```zig
const clean_step = b.step("clean", "Clean up");

clean_step.dependOn(&b.addRemoveDirTree(b.install_path).step);
if (@import("builtin").os.tag != .windows) {
    clean_step.dependOn(&b.addRemoveDirTree(b.pathFromRoot("zig-cache")).step);
}

```

## III. C Dependencies

### 1) Add C-specific stuff to a `Compile` step

```zig
lib.addCSourceFiles(.{
    .root = C_ROOT_DIR,
    .files = &(C_CORE_FILES ++ C_LIB_FILES),
    .flags = &C_FLAGS,
});
lib.addIncludePath(b.path(C_LIB_DIR));
lib.defineCMacro("MY_C_MACRO", null);
lib.linkSystemLibrary("readline");
lib.linkLibCpp();
lib.linkLibC();

```

Static string data goes after the `build` function to avoid cluttering the main script path.

```zig
const C_ROOT_DIR = "deps/my-c-lib/";

const C_CORE_FILES = .{
    "file1.c",
    "file2.c",
};

const C_LIB_FILES = .{
    "libfile1.c",
    "libfile2.c",
};

const C_FLAGS = .{
    "-Wall",
    "-O2",
};

```

### 2) Link dynamic and static libraries

```zig
// If you are compiling multiple objects that have the same
// library dependencies, make a helper function to attach them in one go.
fn linkLibraries(b: *std.Build, obj: *std.Build.Step.Compile) void {
    // Link a static library (archive file)
    obj.addObjectFile(b.path("src/lib/mp_kernels.a"));

    // Enable searching for libraries prefixed with "lib",
    // such as "libcudart.so" or "libfoo.so"
    obj.addLibraryPath(b.path("deps/cuda/lib64"));

    // Perform a search for libraries in specified paths.
    // Note that the "lib" prefix has been omitted, such as in "cudart",
    // which actually has the name "libcudart.so"
    obj.linkSystemLibrary("cudart");
    obj.linkSystemLibrary("nvrtc");
    obj.linkSystemLibrary("cuda");

    // Link system libc
    obj.linkLibC();
}

```

### 3) Dependence on the Zig Compiler version

```zig
const builtin = @import("builtin");

comptime {
    const required_zig = "0.12.0-dev.3302";
    const current_zig = builtin.zig_version;
    const min_zig = std.SemanticVersion.parse(required_zig) catch unreachable;
    if (current_zig.order(min_zig) == .lt) {
        const error_message =
            \\Sorry, it looks like your version of zig is too old. :-(
            \\
            \\(Insert your program name here) requires development build {}
            \\
            \\Please download a development ("master") build from
            \\
            \\https://ziglang.org/download/
            \\
            \\
        ;
        @compileError(std.fmt.comptimePrint(error_message, .{min_zig}));
    }
}

```

### 4) Code generation tricks

For the following examples, assume the following files exist:

`cmake_style_cfg_header.h.in`:

```c
#cmakedefine SOME_VAR
static const char *variable = "@SOME_STRING@";

```

`autoconf_style_cfg_header.h.in`:

```c
// Autoconf should fill in this #define for us
#undef SOME_VAR

```

```zig
// See previous "Code generation tricks" section for using system commands to generate files
// Assuming instead of generating a .zig file we generate a .c file:
// exe.root_module.addAnonymousImport("generated", .{
// .root_source_file = generated_zig_file,
// });
// Becomes:
exe.addCSourceFile(.{ .file = generated_c_file, .flags = &.{"-fyour", "-fflags"} });

// Generating a header file in the style of autoconf
// See: https://www.gnu.org/software/autoconf/manual/autoconf-2.68/html_node/Header-Templates.html#Header-Templates
const autoconf_cfg_header_cmd = b.addConfigHeader(.{ .style = .{ .autoconf = b.path("autoconf_style_cfg_header.h.in") } }, .{ .SOME_VAR = 12345 });
const autoconf_cfg_header_out = b.addInstallFile(autoconf_cfg_header_cmd.getOutput(), "autoconf_style_cfg_header.h");
install_step.dependOn(&autoconf_cfg_header_out.step);

// Generating a header file in the style of Cmake
// See: https://cmake.org/cmake/help/v3.30/command/configure_file.html
const cmake_cfg_header_cmd = b.addConfigHeader(.{ .style = .{ .cmake = b.path("cmake_style_cfg_header.h.in") } }, .{ .SOME_VAR = true, .SOME_STRING = "hello_world", .SOME_OTHER_STRING = "from_zig" });
const cmake_cfg_header_out = b.addInstallFile(cmake_cfg_header_cmd.getOutput(), "cmake_style_cfg_header.h");
install_step.dependOn(&cmake_cfg_header_out.step);

// Generating a header from scratch, with no "template" input file
const scratch_cfg_header_cmd = b.addConfigHeader(.{ .style = .blank }, .{ .SOME_VAR = true, .SOME_STRING = "hello_world", .SOME_OTHER_STRING = "from_zig" });
const scratch_cfg_header_out = b.addInstallFile(scratch_cfg_header_cmd.getOutput(), "scratch_style_cfg_header.h");
install_step.dependOn(&scratch_cfg_header_out.step);

```

The generated headers content should be:

`zig-out/autoconf_style_cfg_header.h`:

```c
/* This file was generated by ConfigHeader using the Zig Build System. */
// Autoconf should fill in this #define for us
#define SOME_VAR 12345

```

`zig-out/cmake_style_cfg_header.h`:

```c
/* This file was generated by ConfigHeader using the Zig Build System. */
#define SOME_VAR 
static const char *variable = "hello_world";
static const char *some_other_variable = "from_zig";

```

`zig-out/scratch_style_cfg_header.h`:

```c
/* This file was generated by ConfigHeader using the Zig Build System. */
#ifndef CONFIG_H
#define CONFIG_H
#define SOME_VAR 1
#define SOME_STRING "hello_world"
#define SOME_OTHER_STRING "from_zig"
#endif /* CONFIG_H */

```

## IV. The Build Graph

The Zig build system models the build as a directed acyclic graph (DAG). We will now discuss how this graph is constructed.

The core of this graph is `std.Build.Step`. Every `Step` has a name and a list of dependencies, which are other `Step`s. These `Step`s and the dependencies between them form the build graph.

The most basic way to create a new step is via the `std.Build.step` method. This method creates a new standalone `Step`, meaning that it does not depend on any other `Step`s, and no other `Step`s depend on it. You can add this step as a dependency of another step via the dependent step’s `std.Build.Step.dependOn` method. You can also use `dependOn` to add dependencies to _this_ step.

The `std.Build.step` method is generic in the sense that it’s agnostic to the contents and purpose of the `Step` that it creates. However, in many cases there are helper methods to create `Step`s for various purposes. For instance, the `std.Build.installArtifact` method creates a `std.Build.Step.InstallArtifact` and adds its step as a dependency of the top-level “install” step. This ensures that some artifact will be installed to `zig-out` when `zig build install` or `zig build` (since “install” is the default step) are run. It’s also possible to use the `std.Build.addInstallArtifact` method to create an `InstallArtifact` step that _isn’t_ tied to the top-level “install” step, which gives you the ability to install an artifact as part of some other build step.

Another helper method to be aware of is `std.Build.addExecutable`, which was shown in the previous section. This helper method creates a `std.Build.Step.Compile`, which is one of the arguments required by the `std.Build.installArtifact` method. This is why in examples you’ll frequently see both `addExecutable` and `installArtifact`. This may give you the impression that “executable” and “artifact” are different kinds of entities that you need to know about in order to write your `build.zig`, but it really just boils down to `Step`s.

## P. S.

Since this doc isn’t constantly tested and kept in sync with Zig master, you might find it more useful to choose and generate an entire codebase template (not just a build script) using Liza:

> [@Liza: Zig codebase initializer](https://ziggit.dev/t/liza-zig-codebase-initializer/3486):
>
> Hi everyone! I’ve been reusing a few Zig project patterns when staring a new repo, so I decided to combine them into a “templated” alternative to zig init. Hope you find it useful! Feedback and questions are welcome as always!

---

<div class="post-metadata">

**Author:** ![tensorush](https://ziggit.dev/letter_avatar_proxy/v4/letter/t/6a8cbe/32.png) [@tensorush](https://ziggit.dev/u/tensorush)\
**Post date:** [March 11, 2024, 10:20am UTC](https://ziggit.dev/t/build-system-tricks/3531/2 "2024-03-11T10:20:44Z")

</div>

Phew, first draft is out! This is pretty much all that I’ve used myself so far. Tried to structure everything as it tends to occur top-down in my build scripts, so pretty opinionated. I’m not too familiar with the codegen and the C stuff, so I left some todos to pick up from for later. Feel free to change anything.

---

<div class="post-metadata">

**Author:** ![dimdin](https://ziggit.dev/user_avatar/ziggit.dev/dimdin/32/1457_2.png) [@dimdin](https://ziggit.dev/u/dimdin)\
**Post date:** [March 11, 2024, 10:43am UTC](https://ziggit.dev/t/build-system-tricks/3531/3 "2024-03-11T10:43:31Z")

</div>

Thank you for all these cases.

I added a cleaning step.

II.1) when emitting docs for lib, there is something unexpected. Because of `lib.getEmittedDocs()` you get documentation when you build `lib`, the problem with this is the delay, so I am redeclaring the lib for documentation only.

---

<div class="post-metadata">

**Author:** ![tensorush](https://ziggit.dev/letter_avatar_proxy/v4/letter/t/6a8cbe/32.png) [@tensorush](https://ziggit.dev/u/tensorush)\
**Post date:** [March 11, 2024, 10:53am UTC](https://ziggit.dev/t/build-system-tricks/3531/4 "2024-03-11T10:53:44Z")

</div>

Thanks, nice addition from you, too!

Yeah, the docs can only be emitted from some `Compile` thing, which isn’t too convenient, but kinda makes sense if the idea is to generate the docs only for publicly exposed things defined in files that are actually accessible from some `Compile`’s entry point.

---

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [March 11, 2024, 4:08pm UTC](https://ziggit.dev/t/build-system-tricks/3531/5 "2024-03-11T16:08:32Z")

</div>

Hey, great stuff @tensorush, thanks for your help! I added a blurb about dynamic and static library linking but anyone please feel free to edit it if you have a better suggestion.

Thanks again.

---

<div class="post-metadata">

**Author:** ![pierrelgol](https://ziggit.dev/user_avatar/ziggit.dev/pierrelgol/32/10233_2.png) [@pierrelgol](https://ziggit.dev/u/pierrelgol)\
**Post date:** [March 11, 2024, 5:43pm UTC](https://ziggit.dev/t/build-system-tricks/3531/6 "2024-03-11T17:43:09Z")

</div>

Wouah thanks for this posts, it’s really helpful, Now I would like to ask in case anyone knows, basically I would like to start an open source tester for school projects in my school, which would require compiling C files, but since I can’t know the name for sure of those files, is there a way to specify a list of potential paths to look for c files to compile ? or something along those lines ? since I can know in advance the name of the folders (as they are required to be named like the project.)

---

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [March 11, 2024, 6:17pm UTC](https://ziggit.dev/t/build-system-tricks/3531/7 "2024-03-11T18:17:49Z")

</div>

Yes, I’ve seen that done. You can probe for “likely” install paths by checking to see if they exist.

I actually opted to not do this in Metaphor at the moment because CUDA can basically show up anywhere depending on how people have their system setup. I’m making it a requirement to symlink/copy it to a dependency folder that I’ll reference directly.

Now, are you talking about _systems libraries_ as in shared objects? That’s much more doable but it’s kind of the same process. You probe for likely directories and then add them to the compile step. It will tell you if the search failed.

---

<div class="post-metadata">

**Author:** ![castholm](https://ziggit.dev/user_avatar/ziggit.dev/castholm/32/1330_2.png) [@castholm](https://ziggit.dev/u/castholm)\
**Post date:** [March 11, 2024, 7:29pm UTC](https://ziggit.dev/t/build-system-tricks/3531/8 "2024-03-11T19:29:21Z")

</div>

I know that this is a wiki post that anyone can edit, but I don’t really like silently editing other people’s posts and think it’s more constructive to discuss improvements out in the open, so I’ll provide some suggestions and offer some constructive criticism instead.

First off, you should probably clarify which Zig version this is meant for because it’s currently mixing from different ones. `b.resolveTargetQuery` does not exist on 0.11. `std.zig.system.NativeTargetInfo` does not exist on master, and the `ResolvedTarget` returned by `b.standardTargetOptions` already has both the query and the resolved target.

> Declare [things] upfront  
> Put static strings as global constants after the `build` function

I don’t think this is useful as prescriptive advice. I think the doc should focus on teaching the reader how to accomplish specific tasks, not how they should organize their code. Subjectively, I also think there is great value in a concise build script.

> `std.Build.LazyPath.relative("src/main.zig")`

This might be a matter of taste but there is no real reason to use `LazyPath.relative`. If you peek at its implementation all it does is assert that the path is not absolute then returns `.{ .path = "src/main.zig" }` and I believe it is mostly a leftover from an earlier iteration of the build system. The union init is what `zig init` generates and in documentation I think that should be preferred over `LazyPath.relative` because the latter might falsely give the user the impression that it’s doing something special.

> 2) Declare user options upfront

“Options” is an unfortunately overloaded term in the current iteration of the build system, so it might be useful to explain the difference between project-specific build options that are exposed as -`Doption=value` on the command line, and `Step.Options` which generates code.

> `EXAMPLES_DIR ++ EXAMPLE_NAME ++ "/main.zig"`

Use `b.pathJoin` to safely join path segments. In general it might be useful to mention that `b` exposes some string utility functions like `b.fmt` that let you accomplish common tasks without needing to manage memory.

> 8) Clean `zig-out` and `zig-cache` directories

The default `zig build uninstall` step should already delete the contents of `zig-out`. I also don’t know the exact implementation details but I would guess that deleting `zig-cache` while the build runner itself is running might not be a good idea and that it’s better/safer to manually delete `zig-cache`.

> `lib.addCSourceFiles(&(C_CORE_FILES ++ C_LIB_FILES), C_FLAGS)`

`addCSourceFiles` uses a slightly different API on master, which is a bit friendlier with regard to common path segments:

```zig
exe.addCSourceFiles(.{
    .root = "src/c/", // this is optional, but useful
    .files = &.{ "alfa.c", "bravo.c" },
    .flags = &.{ "-Wall", "-O2" },
});

```

---

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [March 11, 2024, 7:38pm UTC](https://ziggit.dev/t/build-system-tricks/3531/9 "2024-03-11T19:38:44Z")

</div>

@castholm, this is exactly why we made this an open forum - thanks for your constructive feedback. I’ll tag it with the version but I’m sure we can work your suggestions in. I do think you should feel at liberty to edit these if you have more compact/elegant solutions.

---

<div class="post-metadata">

**Author:** ![dimdin](https://ziggit.dev/user_avatar/ziggit.dev/dimdin/32/1457_2.png) [@dimdin](https://ziggit.dev/u/dimdin)\
**Post date:** [March 11, 2024, 7:41pm UTC](https://ziggit.dev/t/build-system-tricks/3531/10 "2024-03-11T19:41:01Z")

</div>

> [@castholm](#):
>
> > 1. Clean `zig-out` and `zig-cache` directories
> 
> The default `zig build uninstall` step should already delete the contents of `zig-out`.

Unfortunately `uninstall` is a task that does nothing.

> [@castholm](#):
>
> > I also don’t know the exact implementation details but I would guess that deleting `zig-cache` while the build runner itself is running might not be a good idea and that it’s better/safer to manually delete `zig-cache`.

It works perfectly and I prefer it from manually deleting recursively the directory.

---

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [March 11, 2024, 7:43pm UTC](https://ziggit.dev/t/build-system-tricks/3531/11 "2024-03-11T19:43:06Z")

</div>

Also, just to be clear, the tags for adding versions can be appended when you either make the topic or if you edit the header of the topic. They’re not on the article itself. If you do it this way, you’ll see them show up when you search for tags like “12” (you’ll get zig version 12 tags in that case).

---

<div class="post-metadata">

**Author:** ![castholm](https://ziggit.dev/user_avatar/ziggit.dev/castholm/32/1330_2.png) [@castholm](https://ziggit.dev/u/castholm)\
**Post date:** [March 11, 2024, 8:00pm UTC](https://ziggit.dev/t/build-system-tricks/3531/12 "2024-03-11T20:00:47Z")

</div>

> [@dimdin](#):
>
> It works perfectly and I prefer it from manually deleting recursively the directory.

This might be different depending on OS, it doesn’t work on Windows and fails with the error

> unable to recursively delete path ‘C:.…\zig-cache’: FileBusy

When you run `zig build`, zig compiles a build runner executable that it puts in `zig-cache` then runs, so it makes sense that the cache directory is off limits.

---

<div class="post-metadata">

**Author:** ![pierrelgol](https://ziggit.dev/user_avatar/ziggit.dev/pierrelgol/32/10233_2.png) [@pierrelgol](https://ziggit.dev/u/pierrelgol)\
**Post date:** [March 11, 2024, 8:15pm UTC](https://ziggit.dev/t/build-system-tricks/3531/13 "2024-03-11T20:15:02Z")

</div>

So basically the school I’m in is project based, all in C for the first year, there is a wide variety of subject but they start as pretty small projects, (like re implementing printf. all the way to building your own kernel, anyway, the school doesn’t allow you to use any external dependency, at most you are allowed a few functions from libc but that’s it, the rest has to be made from scratch. There are no official structure for testing and validating, students are correcting each other’s projects, by following some general broad testing guidelines.

A lot of projects have very popular automated testing suite made by members of the community, but they suffer a lot from a lack of precision, or portability (some wont’ compile or be usable on MacOS, or Windows) but there is a big lack of a true “ecosystem” and on my spare time I’d love to start using Zig, as way to build that ecosystem for my fellow comrade. Because Zig is the best language at using C in my opinion, and it can be used as the tool chain to provide cross compilation and also cross platform testing.

So as a rule of thumb projects are expected to be very much “self contained” with next to no external dependency except for libc. But as I’ve said while the projects are usually named the same the individual files not necessarily which is why I’d like to see if there is a way of achieving a very easy path that requires minimum efforts from the user to compile their projects and test them across platform ?, I’ve recently tried to play with the walking-directory thingy but couldn’t figure a way to use it to find .c files and compile them together. So any advice or guidance would be very much appreciated 🙂

---

<div class="post-metadata">

**Author:** ![dimdin](https://ziggit.dev/user_avatar/ziggit.dev/dimdin/32/1457_2.png) [@dimdin](https://ziggit.dev/u/dimdin)\
**Post date:** [March 11, 2024, 8:18pm UTC](https://ziggit.dev/t/build-system-tricks/3531/14 "2024-03-11T20:18:17Z")

</div>

Yes, you are right. It works everywhere but windows.

I remember the early days of Windows. Ιn order to uninstall a program, the uninstaller had to be deleted somehow. But the uninstaller was running and for some reason windows doesn’t allow deleting a running executable. The solution was to create a batch file, in a hidden console window, that tries to delete the uninstaller in a loop, when the uninstaller was deleted, the batch file deleted itself because it was a text file and not executable.  
Maybe I can provide a similar solution for erasing zig-cache on windows 🙂

---

<div class="post-metadata">

**Author:** ![gonzo](https://ziggit.dev/user_avatar/ziggit.dev/gonzo/32/54_2.png) [@gonzo](https://ziggit.dev/u/gonzo)\
**Post date:** [March 12, 2024, 7:57am UTC](https://ziggit.dev/t/build-system-tricks/3531/15 "2024-03-12T07:57:25Z")

</div>

This is awesome, thanks @tensorush for all the hard work!

---

<div class="post-metadata">

**Author:** ![chrboesch](https://ziggit.dev/user_avatar/ziggit.dev/chrboesch/32/438_2.png) [@chrboesch](https://ziggit.dev/u/chrboesch)\
**Post date:** [March 15, 2024, 3:00pm UTC](https://ziggit.dev/t/build-system-tricks/3531/16 "2024-03-15T15:00:03Z")

</div>

Very good! 👍

I have added a compiler dependency that is often used while Zig is still under heavy development.

---

<div class="post-metadata">

**Author:** ![dimdin](https://ziggit.dev/user_avatar/ziggit.dev/dimdin/32/1457_2.png) [@dimdin](https://ziggit.dev/u/dimdin)\
**Post date:** [April 12, 2024, 12:12pm UTC](https://ziggit.dev/t/build-system-tricks/3531/17 "2024-04-12T12:12:01Z")

</div>

I changed the deprecated [std.Build.LazyPath.relative](https://ziglang.org/documentation/master/std/#std.Build.LazyPath.relative) to [std.Build.path](https://ziglang.org/documentation/master/std/#std.Build.path) in

```zig
const root_source_file = b.path("src/main.zig");

```

---

<div class="post-metadata">

**Author:** ![nyc](https://ziggit.dev/user_avatar/ziggit.dev/nyc/32/1167_2.png) [@nyc](https://ziggit.dev/u/nyc)\
**Post date:** [April 13, 2024, 2:22pm UTC](https://ziggit.dev/t/build-system-tricks/3531/18 "2024-04-13T14:22:18Z")

</div>

This is fucking gold. Absolute gold. Thank you thank you thank you.

---

<div class="post-metadata">

**Author:** ![FObersteiner](https://ziggit.dev/user_avatar/ziggit.dev/fobersteiner/32/1177_2.png) [@FObersteiner](https://ziggit.dev/u/FObersteiner)\
**Post date:** [May 9, 2024, 2:21pm UTC](https://ziggit.dev/t/build-system-tricks/3531/19 "2024-05-09T14:21:03Z")

</div>

should we add [Pass build option to a dependency?](https://ziggit.dev/t/pass-build-option-to-a-dependency/4196) here? Not 100% sure where to put it, under `I 3)` maybe?

---

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [May 9, 2024, 2:24pm UTC](https://ziggit.dev/t/build-system-tricks/3531/20 "2024-05-09T14:24:24Z")

</div>

I’d say go ahead and add it. Section 3 seems appropriate.

[Next page](https://ziggit.dev/t/build-system-tricks/3531.md?page=2)
