Check the presence of kernel macros in build.zig

I am converting the build system of a C library to zig. The library has a configure script that does something like this:

##########################################
# check for __kernel_rwf_t
__kernel_rwf_t="no"
cat > $TMPC << EOF
#include <linux/fs.h>
int main(int argc, char **argv)
{
  __kernel_rwf_t x;
  x = 0;
  return x;
}
EOF
if compile_prog "" "" "__kernel_rwf_t"; then
  __kernel_rwf_t="yes"
fi
print_config "__kernel_rwf_t" "$__kernel_rwf_t"

It’s creating a C file and compile it to check if a particular macro is define in some kernel header.
How would you handle this in a build.zig ?

You can use @hasDecl to check if a symbol is available:

const linux = @cImport({
    @cInclude("linux/fs.h")
});
const have_kernel_rwf_t: bool = @hasDecl(linux, "__kernel_rwf_t");

Can you import C headers in build.zig? zig now complains:

note: libc headers not available; compilation does not link against libc

Or did you mean to put that in a zig file and compile it from build.zig and check the output result?

There are various ways of doing this and I am trying to figure out what is the “correct” way.

The result of configure is an .h file that have a HAVE_KERNEL_RWF_T define, or something similar.
In your C code you can #ifdef to select code with or without the HAVE_ feature.
In zig you don’t need any configure step.
In your Zig code you can use compile-time if statements to select the code with or without the feature.

C code: (config.h is generated from configure step)

#include "config.h"

#ifdef HAVE_KERNEL_RWF_T
    foo();
#else
    bar();
#endif

zig code: (no configure step)

const fs = @cImport({
    @cInclude("linux/fs.h")
});

if (@hasDecl(fs, "__kernel_rwf_t")) {
    foo();
} else {
    bar();
}
1 Like

What I am trying to do is “zig” a C library by replacing it’s original build system with zig build. I’ll edit the original question, because the word “converting” is wrong in this context.

Modifying the original library source code is not desirable as I only import it as a dependency.

build.zig is used to declare and configure build artifacts (steps).

You can use addCMacro for C #define e.g.:

exe.root_module.addCMacro("HAVE_KERNEL_RWF_T", "1");

where exe is a build artifact from addExecutable, or addStaticLibrary, etc.

Also there is addConfigHeader for handling config.h, see: @andrewrk nasm/build.zig for example usage.

Finally zig-build explained - part 2 is an excellent guide for building C projects.

Yes, I mainly followed Andrew’s ffmpeg build.zig but my original problem still remains: How to check the presence of a macro in a kernel header in build.zig. The guide you provide do not talk about that (and it’s a little out of date).

Since zig cannot test the presence of the macro in build.zig you can use an external process to detect it’s presence.

Zig ships with the file that you’re talking about:

andy@ark ~/d/zig (master)> find lib/libc/include/ -name fs.h
lib/libc/include/any-linux-any/linux/fs.h

It’s in the any-linux-any directory which means that Zig has determined that this file is the same on every target.

If you look at the file, __kernel_rwf_t is unconditionally defined.

So, the answer to this question that this configure script is inefficiently trying to ask is “yes” and you may simply and accurately hard-code it in build.zig.

Under different conditions, it may be necessary to do something more complicated, but in this instance it is actually correct to delete that logic since that value is always defined.

Stuff like this is one of many reasons why zig build tends to be faster than autoconf.

5 Likes