Zig NEWS

Alexander M
Alexander M

Posted on • Updated on

Building Zig libraries with C dependencies

Edited: Supports zig 0.12.0

Building Zig projects that depend on C code may seem complex. However, Zig tries to make that process as painless as possible. In this article, we will build a Zig library wrapper to help you better understand the Zig build system.

To showcase the process, we will build a wrapper around https://github.com/tidwall/btree.c.

btree.c is a B-tree implementation in C. The lib has no dependencies, so the build for it will be straightforward.

First, we need to fetch the btree.c as a dependency. We could use git submodules, however, zig's package manager Zon can conveniently manage dependency for us.

To add btree_c as a dependency run zig fetch --save https://github.com/tidwall/btree.c/archive/v0.6.1.tar.gz

.{
    .name = "btree-zig",
    .version = "0.0.1",
    .dependencies = .{
        .btree_c = .{
        .url = "https://github.com/tidwall/btree.c/archive/v0.6.1.tar.gz",
        .hash = "122032a19f309225db04415bcecfa87c95d3110b1d90d1223a613f3302a2a46e7f6f"
    }
    },
    .paths = .{
        "",
    },
}
Enter fullscreen mode Exit fullscreen mode

After we fetch our dependency, we can utilize it in our build.

Building btree-zig

Now, let's focus on building our Zig wrapper. We call it btree-zig.

Since it's a library, we will not have any executables. Instead, we will define btree-zig as a static library.

const btree_zig = b.addStaticLibrary(.{
    .name = "btree-zig",
    .root_source_file = .{ .path = "src/btree.zig" },
    .target = target,
    .optimize = optimize,
});
Enter fullscreen mode Exit fullscreen mode

Then we need to add our dependency btree_c

const dep_btree_c = b.dependency("btree_c", .{
    .target = target,
    .optimize = optimize,
});
Enter fullscreen mode Exit fullscreen mode

The code above will create a new dependency in the build graph, which we will use to link with our static lib (btree-zig).

Our build process involves building btree_c, so we need to add source files and headers.

btree_zig.addCSourceFiles(.{
    .root = dep_btree_c.path(""),
    .files = &.{"btree.c"},
});

btree_zig.installHeadersDirectory(dep_btree_c.path(""), "", .{
    .include_extensions = &.{"btree.h"},
});
Enter fullscreen mode Exit fullscreen mode

And finally we can build the btree-zig

btree_zig.linkLibC();
b.installArtifact(btree_zig);
Enter fullscreen mode Exit fullscreen mode

In the zig-out folder, you should have the include folder with the headers for btree_c and the lib folder with the libbtree-zig.a.

Exporting btree-zig as a module

To export btree-zig, we need to take some additional steps.

First, create a new module and add it to the build.

const module = b.addModule("btree_c_zig", .{
    .root_source_file = .{ .path = "src/btree.zig" },
});
Enter fullscreen mode Exit fullscreen mode

We also need to expose header files from btree_c, as a part of the module to avoid consumers dealing with missing headers.

// Include header files from btree.c lib
module.addIncludePath(dep_btree_c.path(""));
Enter fullscreen mode Exit fullscreen mode

Using btree-zig

The consumers can now import btree-zig into their projects.

.dependencies = .{
    .@"btree-zig" = .{
        .url = "https://github.com/almmiko/btree.c-zig/archive/<git-ref>.tar.gz",
        .hash = "1220450bb9feb21c29018e21a8af457859eb2a4607a6017748bb618907b4cf18c67b",
    },
},
Enter fullscreen mode Exit fullscreen mode

And adding it as a dependency at build.zig.

const btree_zig = b.dependency("btree-zig", .{
    .target = target,
    .optimize = optimize,
});

const btree_zig_module = btree_zig.module("btree_c_zig");

exe.root_module.addImport("btree-zig", btree_zig_module);
exe.linkLibrary(btree_zig.artifact("btree-zig"));
Enter fullscreen mode Exit fullscreen mode

Wrapping up

Zig's build system is powerful and can be effectively used to build complex projects.

In this article, we briefly see it in action to learn how to use Zig to build a wrapper for C libraries.

The source code for btree-zig you can find on the github https://github.com/almmiko/btree.c-zig

If you have any questions, let me know in the comments.

Oldest comments (8)

Collapse
 
ram535 profile image
Ramses

From where did you get the value of .hash = "122032....."?
What does .target = target means?

Collapse
 
almmiko profile image
Alexander M

.hash = "..." is a hash of the module. You can add any value to it and run zig build, it will fail, but also give you the correct hash value.

.target=target is used to specify a platform for which you build the project.

Check ziglang.org/learn/build-system/ for more info about zig's build system.

Collapse
 
uggwar profile image
uggwar

Hey! Just looked at the code and tried to compile it, but it doesn't work. AddCSourceFilesOptions doesn't have a .dependency field and there is no function named installHeadersDirectoryOptions.

Collapse
 
almmiko profile image
Alexander M

I've used zig v0.12.0-dev.2546+f2e249e92, zig evolves so fast and most likely this method was renamed or replaced. Thanks for letting me know.

Collapse
 
almmiko profile image
Alexander M

updated to support zig 0.12.0

Collapse
 
felixf4xu profile image
Felix F Xu

I don't quite understand, if btree-zig is a wrapper on a .c/.h project, why it needs to expose header files from btree_c? I think the .h file should be hidden instead.

Collapse
 
almmiko profile image
Alexander M

btree-zig itself creates libbtree-zig.a + headers, in case you only need to build the original btree c project and use it in your c based project. If you have a zig project, you don't need to deal with any headers.

Collapse
 
rwzd profile image
rwzd

The build code is once again broken on master. (0.14.0) Following the tutorial, I also could not find an artifact attribute that was public on *Compile.