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 = .{
"",
},
}
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(.{
.root = dep_btree_c.path(""),
.files = &.{"btree.c"},
});
btree_zig.installHeadersDirectory(dep_btree_c.path(""), "", .{
.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
If you have any questions, let me know in the comments.
Top comments (8)
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
.I don't quite understand, if
btree-zig
is a wrapper on a .c/.h project, why it needs to expose header files frombtree_c
? I think the .h file should be hidden instead.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.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.
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.updated to support
zig 0.12.0
From where did you get the value of .hash = "122032....."?
What does .target = target means?
.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.