Building Zig Libraries With C Dependencies

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 = .{
        "",
    },
}

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,
});

Then we need to add our dependency btree_c

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

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(.{
	.dependency = dep_btree_c,
	.files = &.{"btree.c"},
});

btree_zig.installHeadersDirectoryOptions(.{
	.source_dir = dep_btree_c.path(""),
	.install_dir = .header,
	.install_subdir = "",
	.include_extensions = &.{"btree.h"},
});

And finally we can build the btree-zig

btree_zig.linkLibC();
b.installArtifact(btree_zig);

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" },
});

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(""));

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",
	},
},

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"));

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

Originally published at https://zig.news/almmiko