@cImport not handling #ifdefs correctly and including unix libraries even on windows

I tried using rgfw, which is a single header library written in c, using Zig’s C interop, in a fairly recent (0.16.0-dev.747+493ad58ff) zig master version.

const std = @import("std");

const c = @cImport({
    @cDefine("RGFW_IMPLEMENTATION", {});
    @cInclude("RGFW.h");
});

pub fn main() !void {
    const window: *c.RGFW_window = c.RGFW_createWindow("a window", 0, 0, 800, 600, c.RGFW_windowCenter);

    _ = window;

    while (true) {  }
}

This compiles and works on Linux, but when compiling on my windows vm, this happens:

error: translation failure
C:\zig\lib\include\xmmintrin.h:3197:27: error: function-like macro '__building_module' is not defined
#if defined(__SSE2__) && !__building_module(_Builtin_intrinsics)
                          ^
C:\Users\vm\zig\app\src\RGFW.h:4673:10: error: 'poll.h' not found
#include <poll.h>
         ^

However compiling an equivalent program in C directly with clang works (even on the VM), meaning it isn’t a problem with RGFW being ‘malformed’.

#define RGFW_IMPLEMENTATION
#include "RGFW.h"

int main() {
    RGFW_window* win = RGFW_createWindow("a window", 0, 0, 800, 600, RGFW_windowCenter);

    while (true) {

    }
}

Does anyone know a workaround for this?

This seems to be a regression made in the newest versions, (see Some cimports have regressed and are now failing with `use of unknown builtin` · Issue #201 · ziglang/translate-c · GitHub)

You can either downgrade to the stable 1.15.2 or implement all the functions yourself binding style.

I made a bindings project for RGFW that does exactly this but its a bit outdated at the moment as I am going through a full rewrite (check it out here if you wish), but the basic idea goes as follows;

zig fetch --save=rgfw git+https://github.com/ColleagueRiley/RGFW.git

then put the following in your build.zig

const rgfw_dep = b.dependency("rgfw", .{ .target = target, .optimize = optimize });

...

exe.addCSourceFile(.{
    .language = .c,
    .file = rgfw_dep.path("RGFW.h"),
});

Then you can define whatever functions you need like so;

pub extern fn RGFW_createWindow(name: [*c]const u8, x: i32, y: i32, width: i32, height: i32, flags: Window.Flags) *Window;

The Window type in particular is quite the complex beast but if you don’t care about the internals i’m sure you can ignore some of it with opaque{}

I would avoid applying @cImport to the implementation part of any header only library, because it is unnecessary and increases the chances that you will have problems with the c translation. Instead just use @cImport with the header (without the implementation define set) and then add the implementation by adding it as a c source (for example by adding the c source to the module via the build system).

1 Like

Hi i am trying to work with rgfw aswell. Do you have to addCSourceFile in order for you to make a C include in the main file? I am relatively new.

I assume you’re trying to import just the header definitions using @cInclude as @Sze suggested.
In which case, and also in the case of my previous example, you would have to use exe.root_module.addCMacro("RGFW_IMPLEMENTATION", ""); so that RGFW includes the full implementation through the build system.

I believe @cInclude searches your projects include locations for the file so you would have to add that to your build.zig with exe.addIncludePath(rgfw_dep.path("")); as well.

Updated version of what goes in your build.zig:

const rgfw_dep = b.dependency("rgfw", .{ .target = target, .optimize = optimize });

...

exe.addIncludePath(rgfw_dep.path(""));
exe.root_module.addCMacro("RGFW_IMPLEMENTATION", "");
exe.addCSourceFile(.{
    .language = .c,
    .file = rgfw_dep.path("RGFW.h"),
});

Also main.zig:

const c = @cImport({
    @cInclude("RGFW.h");
});

pub fn main() !void {
    const window = c.RGFW_createWindow("Window Title", 0, 0, 800, 600, c.RGFW_windowCenter);
    
    ...
}
1 Like