Zig build system not detecting when C headers have changed?

So we’re stumbling over a strange issue in the sokol-zig bindings where the underlying C library might not get recompiled when the sokol-zig dependency is updated, and the Zig caches need to be deleted to trigger a complete rebuild:

The only way I can think of how this can happen is when the Zig build system doesn’t consider included C headers when deciding whether to compile a C source file or not.

E.g. What happens on a sokol-zig update is that C headers are being updated, but the C source files including those header files remain unchanged (e.g. if the Zig build system only checks the C source files, but not included headers for being updated it wouldn’t notice a change).

Can anybody confirm whether or whether not this is the case? (but that would be a pretty big oversight, and should cause problems will all sorts of C code…)

1 Like

I’ve been having the same issue. I’m using a sort of unity build setup, for one of my dependencies thankfully it’s not code I edit frequently. It is still a pain to have to delete the cache every time I change it though.

The two obvious solutions I can come up with are:

  • Recursively add files to the cache invalidation thing through #include s
  • Add an option to the addCSourceFile(s) functions to add explicit headers / files that trigger a rebuild when changed

checking for cache invalidation recursively would require parsing the C source file each time a compile is requested, making sure that header order and macros definitions have not changed since the last compilation of the source code.

This is a non trivial amount of work to do at each compilation, just in order to save on compilation time.

I think having explicit header dependencies as an argument to addCSourceFile would be great both in term of performance and simplicity of the build system.

That would be the easiest way out, but also failure prone because it’s easy to miss include dependencies, especially in non-trivial C/C++ projects.

I think the build system doesn’t need to do its own parsing, since the Clang frontend can help with discovering all dependencies of a C/C++/ObjC source: for instance clang -M source.c outputs a ‘Makefile rule’ which lists all headers the source file depends on, and I bet this is also optimized enough to not matter for the overall build time.

PS: and having to accept slightly slower build times once C/C++/ObjC sources are involved is IMHO an acceptable compromise - but still - I bet compared to the actual compilation time it doesn’t matter much, and Ninja has shown that a "no-op build’ even for large C/C++ projects can be very fast.

1 Like

That should not be the case, Zig uses a depfile to track all dependencies correctly (including indirect ones). If your setup doesn’t have any other flaw, then you’re experiencing a bug.

1 Like

Good to know, thanks! I think I have the build set up correctly, at the same time I haven’t stumbled over this problem for quite a while (despite regular sokol updates). I’ll keep an eye out for it.

PS: it definitely works as expected within the sokol-zig project (e.g. when manually changing one of the C headers that change is picked up by the build system). It’s harder to check though whether this is true when sokol-zig is used as dependency (it also works as expected when doing zig fetch from a local sokol-zig) - maybe it was a temporary fluke.