How do I build my project with a C dependency?

Helllo,

I’m trying to build my project with a C dependency called blip-buf, this library has just two files (blip_buf.c and blip_buf.h). I’ve been trying to build it with this code:

    const blip_buf_lib = b.addLibrary(
        .{ .name = "blip_buf", .linkage = .static, .root_module = b.createModule(
            .{
                .target = target,
                .optimize = optimize,
                .link_libc = true,
            },
        ) },
    );
    blip_buf_lib.addCSourceFile(.{ .file = b.path("third-party/blip_buf-1.1.0/blip_buf.c") });
    blip_buf_lib.addIncludePath(b.path("third-party/blip_buf-1.1.0"));
    b.installArtifact(blip_buf_lib);
    mod.linkLibrary(blip_buf_lib);

But I’m getting this error

src/root.zig:18:15: error: C import failed
pub const c = @cImport({
              ^~~~~~~~
referenced by:
    c: src/main.zig:4:15
    main: src/main.zig:53:10
    4 reference(s) hidden; use '-freference-trace=6' to see all references
.zig-cache/o/14771798dce5c649d8f7da9bebf86288/cimport.h:3:10: error: 'blip_buf.h' file not found
#include <blip_buf.h>

What am I missing here? You can see my full build.zig here.

You need to add the include path to the module as well.
blip_buf_lib.root_module.addIncludePath(b.path("third-party/blip_buf-1.1.0"));

1 Like

I’m still getting the same error.

        const blip_buf_lib = b.addLibrary(
        .{ .name = "blip_buf", .linkage = .static, .root_module = b.createModule(
            .{
                .target = target,
                .optimize = optimize,
                .link_libc = true,
            },
        ) },
    );
    blip_buf_lib.addCSourceFile(.{ .file = b.path("third-party/blip_buf-1.1.0/blip_buf.c") });
    blip_buf_lib.addIncludePath(b.path("third-party/blip_buf-1.1.0"));
    blip_buf_lib.root_module.addIncludePath(b.path("third-party/blip_buf-1.1.0"));

    b.installArtifact(blip_buf_lib);
    mod.linkLibrary(blip_buf_lib);

My bad I failed to notice you haven’t provided a root_source_file for the module.

Create a file that @cImport your c header this acts as an importable module for the exe then add the import to the exe. some thing like this

// blip_buf.zig
pub const c = @cImport({
    @cInclude("blip_buf.h");
});

    const blip_buf_lib = b.addLibrary(
        .{ .name = "blip_buf", .linkage = .static, .root_module = b.createModule(
            .{
                .root_source_file = b.path("blip_buf.zig"), // create this file
                .target = target,
                .optimize = optimize,
                .link_libc = true,
            },
        ) },
    );
    blip_buf_lib.addCSourceFile(.{ .file = b.path("third-party/blip_buf-1.1.0/blip_buf.c") });
    blip_buf_lib.root_module.addIncludePath(b.path("third-party/blip_buf-1.1.0"));
    blip_buf_lib.addIncludePath(b.path("third-party/blip_buf-1.1.0"));
    b.installArtifact(blip_buf_lib);
    mod.linkLibrary(blip_buf_lib);

Then

    const exe = b.addExecutable(.{
        .name = "8bit_emulator",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/main.zig"),
            .target = target,
            .optimize = optimize,
            .imports = &.{
                .{ .name = "blip_buf", .module = blip_buf_lib.root_module },
                .{ .name = "8bit_emulator", .module = mod },
            },
        }),
    });
1 Like

That worked, thanks!

I just have one more question, though. In my root.zig I have another @cImport, in the documentation it says that you should only have one @cImport in the entire application. So is this fine, having two @cImport, or is there a better way of handling this?

1 Like

If its a cimport that includes different headers you should be fine.

1 Like

Also for the sake of completeness instead of creating a wrapper you could call addIncludePath on the exe’s module instead and you should be able to import in the root module, works fine since you’re creating and linking to a library. But if you don’t need the compiler to output the library then creating a wrapper is needed but the library is not needed you could simplify things to

    const blip_buf_mod = b.createModule(.{
        .root_source_file = b.path("blip_buf.zig"),
        .target = target,
        .optimize = optimize,
        .link_libc = true,
    });
    blip_buf_mod.addCSourceFile(.{ .file = b.path("third-party/blip_buf-1.1.0/blip_buf.c") });
    blip_buf_mod.addIncludePath(b.path("third-party/blip_buf-1.1.0"));

2 Likes

Hello, it’s me again. I’m getting the same error using this approach and the other approach as well. I guess it had worked before because the files that depended on blip_buf weren’t being used by others files of the project, so the compiler probably skipped compiling them.

Could you take a look at the code and check what I’m doing wrong?

Hello!

The addIncludePath should be called on the module that contains the file calling @cImport that would be the _8bit_emulator module

    const mod = b.addModule("_8bit_emulator", .{
        .root_source_file = b.path("src/root.zig"),
        .target = target,
    });
    mod.addIncludePath(b.path("third-party/blip_buf-1.1.0"));

I had assumed the import was done by the src/main.zig and the exe sorry about that

1 Like

Hello, again!

I’m trying to run a test with --test-filter but it fails with this error

src/root.zig:19:15: error: C import failed
pub const c = @cImport({
              ^~~~~~~~
src/root.zig:19:15: note: libc headers not available; compilation does not link against libc
referenced by:
    c: src/apu/buffer.zig:2:33
    apu.buffer.SampleBuffer: src/apu/buffer.zig:11:13
    14 reference(s) hidden; use '-freference-trace=16' to see all references
.zig-cache/o/ecb56e828e2cd32e919574aae0b45d08/cimport.h:1:10: error: 'SDL3/SDL.h' file not found
#include <SDL3/SDL.h>

I’ve tried using this code, but it didn’t work:

    const mod_tests = b.addTest(.{
        .root_module = mod,
    });
    const sdl_lib_tests = sdl_dep.artifact("SDL3_test");
    mod_tests.linkLibrary(sdl_lib_tests);

    // A run step that will run the test executable.
    const run_mod_tests = b.addRunArtifact(mod_tests);

    // Creates an executable that will run `test` blocks from the executable's
    // root module. Note that test executables only test one module at a time,
    // hence why we have to create two separate ones.
    const exe_tests = b.addTest(.{
        .root_module = exe.root_module,
    });
    exe_tests.linkLibrary(sdl_lib_tests);

    // A run step that will run the second test executable.
    const run_exe_tests = b.addRunArtifact(exe_tests);

I’m using this for the SDL dependency.

Hello again!

You’re not linking libc on a module that needs it. You can link it with .link_libc = true on module creation or later with module.link_libc = true;

Also this is a different issue than the original topic better creating a new topic for it.

I’ve already used .link_libc = true on the mod creation. Anyway, I’ve created a new topic here.