# Writers Block Adding New Build System Feature

**URL:** <https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717>\
**Category:** Help\
**Tags:** build-system\
**Created:** [September 25, 2026, 10:26pm UTC](https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717 "2026-09-25T22:26:32Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![haydenridd](https://ziggit.dev/user_avatar/ziggit.dev/haydenridd/32/2375_2.png) [@haydenridd](https://ziggit.dev/u/haydenridd)\
**Post date:** [September 25, 2026, 10:26pm UTC](https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717/1 "2026-09-25T22:26:32Z")

</div>

A couple months back I decided to take a crack at a implementing JSON compilation database support into Zig’s build system (`compile_commands.json`) since it seemed useful and there was a dead PR from years ago that got part of the way there. See my [WIP PR](https://codeberg.org/ziglang/zig/pulls/35462) for some context/history.

See [this](https://clang.llvm.org/docs/JSONCompilationDatabase.html) for some info on the artifact I’m trying to generate, but the general idea is as follows:

- Compilation calls to clang with the flag `-MJ[path/to/artifact.json]` for a given C/C++ file spit out a JSON fragment that looks something like:

```json
{
 "directory": "/home/user/llvm/build",
 "arguments": ["/usr/bin/clang++", "-Irelative", "-DSOMEDEF=With spaces, quotes and \\-es.", "-c", "-o", "file.o", "file.cc"],
 "file": "file.cc"
},

```

For better or worse, lots of tooling has been built around these files to help IDEs understand how a file was compiled so it can intelligently do syntax highlighting that’s dependent on `#defines`, etc.

I’ve made good progress generating these files for individual `Step.Compile` steps. For instance, `build.zig` snippet for an executable with some C files:

```Zig
const exe = b.addExecutable(.{
    .name = "cdb_exe",
    .root_module = b.createModule(
        .{
            .root_source_file = b.path("main.zig"),
            .target = b.graph.host,
            .optimize = optimize,
        },
    ),
});
exe.root_module.addCSourceFile(.{
    .file = b.path("foo.c"),
    .flags = &.{"-Wall"},
});
exe.root_module.addCSourceFiles(.{
    .files = &.{ "bar.c", "baz.c" },
    .flags = &.{ "-Wall", "-Werror" },
});
const cc_exe = exe.getCompileCommandsJson();

```

Where `cc_exe` is the `LazyPath` of the generated compilation db the user can do with what they please (add to install artifacts, perform further processing, etc.). In my branch on that PR, see `test/standalone/compdb_single` for a working example of how to use this new feature.

However, generally when people want to generate a `compile_commands.json`, it’s for IDE tooling. So what they really want is a single file that encompasses _every_ compiled C/C++ source file across _every_ invocation of the compiler when you build your project. For instance, if you’re compiling one source file, but into two different artifacts (exe, lib, etc.) with different compiler args, you would expect two entries in your database:

```json
// You compile it with some_arg1 for exe1
{
 "directory": "/home/user/llvm/build",
 "arguments": ["/usr/bin/clang++", "some_arg1", "-c", "-o", "file.o", "file.cc"],
 "file": "file.cc"
},
// You compile it with some_arg2 for exe2
{
 "directory": "/home/user/llvm/build",
 "arguments": ["/usr/bin/clang++", "some_arg2", "-c", "-o", "file.o", "file.cc"],
 "file": "file.cc"
}

```

Although it’s a little odd to have every compilation command for every artifact in one “file”, this matches how current build systems (CMake) do it.

This is the part I’m stuck on. Mechanically what needs to happen is:

- The user can get access to a top level merged compilation database `LazyPath` via `const merged_db = b.getCompileCommandsJson();`
- This creates some sort of top level maker step let’s call `MergeAllCompdbFiles`
- Every time the user adds a `Step.Compile` to their build graph (either directly or through including a dependency that has `Compile` steps), the `some_compile_step.getCompileCommandsJson()` `LazyPath` is added as a dependency to `MergeAllCompdbFiles`
- After all `Maker.Step.Compile` complete, `MergeAllCompdbFiles` `Maker` step runs generating the overall merged compilation db

Anybody have a good starting point or suggestions for where this quasi-global behavior can get added? I actually had this behavior implemented pre build system rework where I kept track of this top level step (and its dependencies) in the `std.Build` struct. Now that `Configure` and `Maker` phases are separated this approach no longer works, although arguably it was a bit hacky to begin with.

---

<div class="post-metadata">

**Author:** ![alanza](https://ziggit.dev/user_avatar/ziggit.dev/alanza/32/6006_2.png) [@alanza](https://ziggit.dev/u/alanza)\
**Post date:** [September 25, 2026, 11:58pm UTC](https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717/2 "2026-09-25T23:58:43Z")

</div>

is it fine to have each step just append to the file? (or perhaps better, to a string that is eventually flushed to the file?)

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [September 26, 2026, 3:43am UTC](https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717/3 "2026-09-26T03:43:31Z")

</div>

i snooped around, is there a reason not to add this global state to the maker struct? where after executing the graph it generates the complete compile commands (if requested).

---

<div class="post-metadata">

**Author:** ![iceghost](https://ziggit.dev/user_avatar/ziggit.dev/iceghost/32/460_2.png) [@iceghost](https://ziggit.dev/u/iceghost)\
**Post date:** [September 26, 2026, 4:07am UTC](https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717/4 "2026-09-26T04:07:35Z")

</div>

Adding `-MJ` support to each `Step.Compile` is already valuable enough IMO. I don’t mind implementing the merging step in my build script.

(In fact, if `Step.Compile.getCompileCommandsJson()` returns the concatenated fragments without the `[...]` delimiters, I think it would be easier to stitch multiple compile commands JSON files together and just add those delimiters once, not having to strip before merging)

---

<div class="post-metadata">

**Author:** ![vulpesx](https://ziggit.dev/user_avatar/ziggit.dev/vulpesx/32/3989_2.png) [@vulpesx](https://ziggit.dev/u/vulpesx)\
**Post date:** [September 26, 2026, 8:33am UTC](https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717/5 "2026-09-26T08:33:41Z")

</div>

I nerd snipped myself into _trying_ a poc (I didn’t get very far), but I better understand the issue now:

Depending implicitly on _all_ compile steps is troublesome.

It wasn’t clear if you intend to merge the compile db for just the package (and maybe its dependencies too) that ore quests it, or for the entire build graph.  
Both have the aforementioned issue, but the latter has the additional issue of dependencies referring to a generated file from the root.

There needs to be some special casing somewhere. The best solution I came up with is similar to what you tried before (which I think could still work), the difference I suggest is to only merge a caller given set of artifacts compile db’s instead of _all_ of them.

This neatly uses the existing systems to solve the issues, dependencies are explicit and properly defined, and it allows the developer to have better control. And yeah, it is ultimately a convenience feature that they could implement themselves.

Edit: I have a working implementation I’ll send a PR for your PR if you want it, but I do need to improve the test (I just copied and modified your existing one) as it can certainly be fooled

---

<div class="post-metadata">

**Author:** ![haydenridd](https://ziggit.dev/user_avatar/ziggit.dev/haydenridd/32/2375_2.png) [@haydenridd](https://ziggit.dev/u/haydenridd)\
**Post date:** [September 26, 2026, 10:13pm UTC](https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717/6 "2026-09-26T22:13:53Z")

</div>

Ahh unfortunately not really, at least not without some pretty invasive hacks to the build system. It’s a bit hard to explain, but the way I have it set up with `Step.WriteFile` and `Configuration.OptionalGeneratedFileIndex` makes the generated artifacts (fragment temporary files generated by compilation + eventual merged file for a single `Step.Compile`) play nicely with each other through rebuilds/partial builds. A better way of putting it: when you _dont_ use the idiomatic way of generating files within the build system you get all sorts of issues with stale file content getting included in subsequent builds, etc.

---

<div class="post-metadata">

**Author:** ![haydenridd](https://ziggit.dev/user_avatar/ziggit.dev/haydenridd/32/2375_2.png) [@haydenridd](https://ziggit.dev/u/haydenridd)\
**Post date:** [September 26, 2026, 10:15pm UTC](https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717/7 "2026-09-26T22:15:14Z")

</div>

I’m kind of leaning towards this since the more I look at it the more I don’t really think there’s a non-invasive way to get the “automatic” collating of _every_ `Step.Compile`.

---

<div class="post-metadata">

**Author:** ![haydenridd](https://ziggit.dev/user_avatar/ziggit.dev/haydenridd/32/2375_2.png) [@haydenridd](https://ziggit.dev/u/haydenridd)\
**Post date:** [September 26, 2026, 10:20pm UTC](https://ziggit.dev/t/writers-block-adding-new-build-system-feature/17717/8 "2026-09-26T22:20:32Z")

</div>

Yup you hit the nail on the head with this:

> Depending implicitly on _all_ compile steps is troublesome.

I wanted it for the entire build graph. Meaning any dependencies you added with C/C++ source file containing `Step.Compile` would automatically have their output JSON db merged into one master db.

The more I think about it the more I agree with @iceghost. I’m not sure if it’s the Zig build system’s responsibility to be able to automatically spit out a single file for ALL `Step.Compile` in a build graph. Presumably even with dependencies you always have access to the underlying `Step.Compile` and so if it was important to you, you could merge them yourself using the already working `step.getCompileCommandsJson()`.

Note that this original issue provides some context to _why_ you might want a merged compile commands db for your entire project:

> <https://github.com/ziglang/zig/issues/17365>
>
> The \[JSON Compilation Database\](https://clang.llvm.org/docs/JSONCompilationDatab…ase.html) is a file format that stores information about files compiled in a build.
> 
> This database can be then consumed by tooling like \[clangd\](https://clangd.llvm.org/) to provide auto-completion and refactorings to your C/C++ code base.
> 
> It would be very nice to expose the clang command line flag \`-MJ argument\` from the zig compiler to support generating this file.
> 
> I can imagine this in a flag like \`-femit-clang-compile-db=\<path\>\`
> 
> The optimal use case would be that all \`std.Build.Step.Compile\` collect information about their builds and \`zig build\` will then emit a merged version of the \`compile\_commands.json\` emitted by each individual compilation.

But sure send me your fork! Curious to take a look regardless to see what you came up with.
