CMakePkg — link CMake targets from build.zig

I like Zig build system, it is easy to write, extensible and, of course, written in Zig (what makes it enjoyable to work with).

But many libraries I depend on, are too complex to replace their build system with build.zig, for example Qt6. You can always link library by name, but in case with Qt6 there’s so many things, include directories, defines, compiler flags, internal libraries (for example, that’s Qt6 compiler flags on my system: -I/usr/include/qt6/QtCore -I/usr/include/qt6 -DQT_CORE_LIB -DQT_NO_DEBUG -I/usr/lib/qt6/mkspecs/linux-g++).

And this is how CMakePkg started: at first it was a CLI utility (my initial use case was a Makefile), and now it’s a build.zig plugin.
The idea is pretty simple: use CMake to extract include directories, compiler flags, libraries, link options, the whole dependency tree, and turn it into pkgconf-ish output of flags. Solution I come up with is way too hacky, I had to write a simple CMake generator expression engine in CMake language.
I can’t recommend anyone to seriously use it, as I just want to share what I crafted :grin:, there’s no documentation, there could be bugs.

You can find Qt6 example at yataro/cmake-pkg-qt-example - Codeberg.org, assuming you have Qt6 and Zig 0.15.2 installed on your system, just run zig build run to start demo program.

The penalties of CMakePkg is that CMake configuration step is triggered on every zig build run, but it’s only a few seconds. This is fixed, now subsequent configurations will happen much faster (from 1.4s to 0.1s on my system), CMakePkg build plugin now uses deterministic CMake configuration directory, which will leverage CMake configuration cache.

This was my first big Zig project, I started it more than a year ago, after long hiatus I upgraded it to Zig 0.15.2, and added build.zig plugin. I can say that upgrade from Zig 0.13.0 to 0.15.2 was simple and didn’t took long (I used Zig almost every day since then).

12 Likes

Very impressive and useful project, but man I hate how cmake creeps into non-C/C++ ecosystems mainly just because the cmake build scripts are usually so overengineered that they are too hard to replicate in another build system :slight_smile:

3 Likes

It doesn’t help that CMake lets you do a ton of things that many other build systems typically don’t permit without external scripts or a lot of extra work. Which is partially why it is such a lock-in build system. I have a strong suspicion that someone is going to want CMake Zig language/compiler integration one of these days. (Which, really, wouldn’t exactly be the worst idea ever…)

I have a strong suspicion that someone is going to want CMake Zig language/compiler integration one of these days.

I nearly wrote something about a hypotethical ‘translateCMake()’ build step similar to ‘translateC()’ but bit my tongue :wink:

1 Like

I’m happy to see this discussion. I use a terrible project at work and I’m trying to change it’s clunky CMake build into something using zig build. This is purely a labour of love on my part so I can learn zig and, frankly, how to better structure libraries.

Are there any zig libraries or idioms specific to building C/C++ (alone or mixed with zig) you have found useful? For example, I have a struct that has a *Step.Compile and []LazyPath so you can pass some options into in a function and get back include file paths against which dependents should be compiled. I’m still feeling my way around the build system to get a feel for the different ways to do stuff (esp to with include paths and generated files).

One problem I’ve seen in build tools built out of a general purpose language like Python or Lua is that people may choose it because it’s a language which they use well. And then they invent a whole bunch of helpers (which I have already started doing) and it’s kind of a pain because there aren’t kind of standard functions you can look up and say “Ok, I know what that does…” and move on. Instead I may have to learn a bunch of auxiliary functions first. So…

I think it would be really cool if people started sharing useful idioms and libraries.

I’ve been searching around and so far have not found anything like that.

I don’t think there’s something applicable to what you are looking for. The “true” Zig way is to also compile dependencies with build.zig, so I don’t think there’s a pattern different from addLibrary, linkLibrary, etc.
I think that the complexity of source project directly affects need of auxiliary functions and helpers, so I can’t say something specific. Some CMake projects require nothing but basic std.Build calls.
I think you can write a build.zig plugin designed to provide helpers when porting from CMake, but I think it’s more sane to follow build.zig patterns and rules, and even hypotethical translateCMake() floooh mentioned sounds more interesting (and I guess crazy) to implement and use :grin:!

Oh, that it were only so easy in this case :). But I take your point. I’m trying not to deviate much at all from whatever build.zig idioms I find in the wild. However, now and then it is difficult to resist the urge to reduce work and write a helper or two.

I think the biggest problem with the Zig build system is that so much uses CMake and people want to use CMake for their projects. They don’t want to have to completely swap out their build system just to use Zig. It would be nice if build.zig were “the” go-to build system, but alas it isn’t, and translating CMake idioms to something that build.zig can do is from my experience a non-trivial thing to do. Someone I know would prefer that Zig be as flexible as a C/C++ compiler, and the build system be abstracted away from Zig specifically so that integrating Zig into any arbitrary build system would be easier. I have no idea how this would be done though, and it would require things like extensively documenting the build API, stabilizing it, and writing docs that say something like “This is how x translates to y on the zig command line”.

I like build.zig myself, but my biggest problem is the hump of adapting to it. CMake is one of those build systems where getting up and running takes 1-2 minutes at best. Much of the Zig build API is undocumented, as are common patterns, so it requires a lot of spelunking through the Zig stdlib to figure out how things interact. I’m hoping however that this is just a problem of the present and something that will get fixed in future.

It is valid to be annoyed by the lack of documentation and examples, or it even be a dealbreaker. But it is just a result of zigs WIP status, and will be resolved before 1.0.

Most criticisms and suggestions for the build system here have been pretty vague, or not constructive. Which is fine, but I do encourage you to figure out what the exact problems are, and be more precise with your suggestions, so that the zig team can make the best build system possible.

Lastly, this topic is for appreciating what @yataro built, don’t get too off-topic, if you want to continue those conversations consider making a new topic(s). I think one for porting or dealing with cmake would be well received, as it keeps poping up now and then.

2 Likes

This is pretty neat. IMO there should definitely be a zig build integration if possible.

How do you hook find_package with, e.g., toolchain files? Or is that not yet possible?

I have a couple of small helper functions in the sokol-zig build.zig, like abstracting the target platforms:

…or various build*() functions which take an options struct and return a build step, like here:

…or here:

Also as for cmake vs build.zig, the sokol shader compiler has both (although the cmake file uses plenty of helper functions to cut down on the line count).

E.g. the cmake file hierarchy:

…and the corresponding build.zig file which essentially does the same thing as those cmake files, just with the Zig build system: sokol-tools/build.zig at master · floooh/sokol-tools · GitHub

Thanks! I’ll study your examples. I’m a big fan your your chip emulator stuff, btw…

1 Like

Luckily CMake is working together with others to get the “Common Package Specification” (CPS) up to scratch with the goal that multiple buildsystems can work together, but I guess it will take a while into it becomes usable enough (and even longer until it becomes somewhat widely used).

That way I guess Zig (or a 3rd party) could add a CMake step which just invokes a (system installed) CMake, builds the thing and then consumes the CPS to generate the required information.

This still poses a problem. Especially on OSes like Windows where the CMake you would want is the one that MSVC ships with, for example. Hopefully they can resolve all of these problems though.

CPS is just a way of specifying a way for buildsystems to use (or tell others) how to use stuff. That’s it.

It does not tell anybody how these files are generated.

So if you need to use the MSVC provided CMake executable (for whatever reason; it’s news to me that they even do that), the CPS file gets generated by that one.

It’s essentially an actually portable alternative to pkgconfig files which works on Windows and others too.