The Zig Build System is under apreciated

I’ve managed to turn some of my company’s C++ projects to fully cross-compile by setting zig as the C and C++ compiler. There’s a bit of work involved to create a CmakeToolchain file for each architecture you want to target and always some work to fix the code to be cross compilable but it has been a very nice experience so far and amazingly useful.

However there are some issues I haven’t dared to tackle yet (for example cross-compiling a Qt project).

Has anyone used the zig package manager in conjunction with a cmake project using zigc and zigcc? Is it be possible? (I know that one option is to write a build.zig file for the project instead and dump cmake but I’m trying a gradual approach first as some upstream projects rely on cmake)

5 Likes

I worked on ZigROS a bit last year, and for most projects it wasn’t too bad to create a build.zig for the C/++ project so long as you only cared about a specific build with specific options, but for complex projects targeting multiple OS’s with a lot of build-time options (for example, OpenCV), what I found was easier was to build that project separately and “install” the library / libraries to a local directory for the Zig build system to then reference.

For OpenCV, for example, my approach to cross-compilation was to start from a minimal alpine Docker image, install Zig, clone the OpenCV repo at the commit I needed, then use command-line args to CMake to use zig as the compiler, linker, and archiver for the CMake build. This would generate a fully static library with zero system dependencies for both x86 and aarch64. I would then place the libraries into build_deps/{x86_64,aarch64} directories in my zig-based repo (actually I had the Zig build system fetch the prebuilt libraries over the network from a private package cache), and it was easy to link those libraries from there. This works really well for dependencies you aren’t actively maintaining, but if the CMake-based project is under active development, it might be a bit cumbersome.

3 Likes

Well changing from using Cmake to using Zig is not really an option, for multiple reasons. 3 of them being:

  • Changing build systems is a political decision, not a technical decision
  • Time to make the change
  • Upstream projects are using cmake, so we need to provide a very easy cmake integration

So using a build.zig file is out of the question. However it’s possible to use parts of zig and with that show little by little how zig makes everyone’s life easier. Are there any integrations points between the zig package manager and zigc / zigcc? Can I have a build.zig.zon file with my dependencies and use zig fetch to download by dependencies and then trigger the build with cmake?

1 Like

Doesn’t really make sense to me, or at least you don’t describe why that wouldn’t be an option.

You could have a build.zig that just contains code to invoke cmake commands, where the build.zig.zon alongside that describes dependencies that use the pristine-tar-ball strategy (basically that those dependencies don’t include their own build.zig or build.zig.zon).

3 Likes

Doesn’t really make sense to me, or at least you don’t describe why that wouldn’t be an option.

I’d rather focus the thread on if it’s possible to use the zig package manager for C++ and use other build systems, this is not a XY problem, I just don’t want to go too into detail on the pitfalls of build.zig for C++ as I know the biggest issues are already in the Zig tracker.

But since you asked, there are 2 dealbreaker issues with build.zig that make it a non-viable option:

  1. Because upstream projects use cmake, many projects, it’s not possible to port all of them to use build.zig;
  2. Because build.zig does not create a correct compile_commands.json file for LSP usage, while zigc and zigcc do. This single issue makes the build.zig approach effectively worthless, since it’s a massive drawback to the local development experience.

You could have a build.zig that just contains code to invoke cmake commands, where the build.zig.zon alongside that describes dependencies that use the pristine-tar-ball strategy (basically that those dependencies don’t include their own build.zig or build.zig.zon).

Ah I see where the confusion comes from. I meant upstream projects use cmake, not downstream, currently they use git submodules / vcpkg / Conan for defining their dependencies and cmake to then build them, I was hoping to use the zig’s way to track dependencies instead of those

1 Like

In theory, you could use std.Build.Dependency.path to fetch the root directory of the upstream dependency, fetch its CMakeLists file and call cmake with that information, though I’ve never done this myself.
This’d let you fetch and build upstream dependencies fully integrated with a build.zig file, while still using cmake.

There’s also always the option of creating a allyourcodebase-style repo for the upstream dependency, where you create a build.zig file for the upstream dependency and a build.zig.zon file that can fetch the upstream dependency in its unmodified form.
Then, in your final build, you depend on your “allyourcodebase” repositories, and now you can build your entire dependency tree with Zig and only Zig.

3 Likes

Does zig’s build system support fetching build.zig.zon dependencies without running zig build?

I know this sounds like an odd question but I haven’t found any information on if it’s possible to declare a build.zig.zon for my project’s dependencies but not call zig build but instead call some version of zig fetch ./build.zig.zonand get all the dependencies in this file installed

In sum, I’m looking at the possibility of having a different consumer for my zon file. Like Fetching dependencies without build.zig.zon? - #6 by andrewrk

1 Like

zig build —fetch fetches the dependency tree and exits.

1 Like

Ended up discovering it just before, zig build --fetch=all almost does what I want, the only issues is that it needs the existence of a build.zig file to run. It would be nice to be able to have it be independent as it doesn’t seem like running the build process is a necessary requirement when fetching all dependencies but it can easily be bypassed by generating an “fake” build.zig at configuration time.

Waiting patiently for Implemented the JSON compilation database support by jonathanmarvens · Pull Request #18391 · ziglang/zig · GitHub, as that would enable using zig build for us.

looks to me like that pull request is unlikely to be merged…

Fortunately it’s still on the radar Zig the Build System - Compiling LLVM with Zig - #4 by andrewrk

2 Likes

We have a big C++ codebase at Tuple. All our code/dependencies (outside of WebRTC) used CMake. We were on macOS/windows, for linux I just created a build.zig file and put all the include paths/source files directly in there. Instead of patching together hundreds of cmake files across all our projects, I pretty much have just 1 build.zig file that compiled everything in one unit. I could split them into libraries but…meh.

We still have the cmake files for macos/windows, but, it’s so easy for me to just maintain the build.zig that I’m ok with having both. I’ll probably move windows over to the build.zig eventually to, then, the macOS people can have fun with their cmake :slight_smile:

8 Likes

s…surely the macOS port would be easier than windows

I dont understand why you would want this, dependencies are practically unusable without the build system.

1 Like

To be quite honest I’m definitely at the “Your scientists were so preoccupied with whether or not they could that they didn’t stop to think if they should” phase.

I’ve worked on quite a few enterprise C++ projects, where dependency management has always been a nightmare. Most times even setting up a single developer machine wasn’t straightforward. I’ve know of many different ways people use to define dependencies, but none of them spark joy:

  • Submodules
  • Mono-repos
  • Vcpkg
  • Conan
  • Bazel
  • Nix (almost, almost there just no windows)

I already have a project that enables cross-compilation on all major targets, with no extra work*. You just plug and play that project in almost any cmake project and it just works, with a very familiar cmake interface that people are used to.

With that in mind and the fact I think all other dependency management options in C++ are subpar, I think experimenting with zig’s package manager makes sense.

1 Like

I find that its equally over and under appreciated.

It definitely does a lot of things right and excluding some gaps that have been overlooked its very good for writing Zig Projects.

Once you are trying to use build.zig with a C/C++ project it tends to crumple a bit but if a projects build architecture is fairly simple then its not impossible.

If a projects build architecture is not simple like for SDL3 then it becomes a matter of trade-offs, some things are simply not representable in build.zig and the best you can really do is pick the specific options you want or need and pray that it keeps working.

Has anyone used the zig package manager in conjunction with a cmake project using zigc and zigcc? Is it be possible?

I have written an experimental and incomplete cmake trace interpreter for 0.15.2 that is intended to be ran in build.zig:

It invokes cmake with the relevant setup to make it use zig and then parses a more manageable json trace which contains all relevant and pre-parsed information to integrate relevant cmake info into Zig’s build system.

1 Like