tsdtas
February 20, 2025, 10:03am
1
Question in the title.
If you have some Zig module that depends on a C library, the module needs to include the header files from the C library, but it doesn’t strictly speaking need to link the library. The importing application can do that.
What’s considered best practice here?
1 Like
floooh
February 20, 2025, 11:26am
2
Not sure if it’s “best practice”, but for the sokol-zig bindings (which consists of a Zig module and a C library) I link the library to the module.
.optimize = optimize,
.backend = sokol_backend,
.use_wayland = opt_use_wayland,
.use_x11 = opt_use_x11,
.use_egl = opt_use_egl,
.with_sokol_imgui = opt_with_sokol_imgui,
.sokol_imgui_cprefix = opt_sokol_imgui_cprefix,
.cimgui_header_path = opt_cimgui_header_path,
.emsdk = emsdk,
});
mod_sokol.linkLibrary(lib_sokol);
// the integrated examples
const examples = .{
"clear",
"triangle",
"quad",
"bufferoffsets",
"cube",
"noninterleaved",
"texcube",
That way a sokol-zig user only needs to depend on the module in order to get the C library linked, and the top-level project build.zig can be free of “C shenanigans”.
.imports = &.{
.{ .name = "common", .module = mod_common },
.{ .name = "chips", .module = mod_chips },
},
});
const mod_host = b.addModule("host", .{
.root_source_file = b.path("src/host/host.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "sokol", .module = dep_sokol.module("sokol") },
.{ .name = "common", .module = mod_common },
},
});
// top-level modules
const mod_chipz = b.addModule("chipz", .{
.root_source_file = b.path("src/chipz.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
I still expose the C library as an artifact though:
…this is mainly useful for projects which have some C code themselves which wants to directly use the C API of the C library, e.g. see this issue:
opened 07:06PM - 03 Jan 25 UTC
closed 11:27PM - 04 Jan 25 UTC
I have a project that's written in Zig that uses Sokol.
In my Zig project, I'… d like to write also some C code that uses Sokol gfx (as opposed to porting that to Zig). But I don't know how to setup my include paths in build.zig so that the C compiler can find sokol_gfx.h.
I tried something like this but I have no idea if this is the right thing to do:
```
const libsokol = dep_sokol.artifact("sokol_clib");
for (libsokol.root_module.include_dirs.items) |include_dir| {
const p = include_dir.path.join(b.allocator, "src/sokol/c") catch unreachable;
step.addIncludePath(p);
}
```
I wonder if the src/sokol/c .h files would need to be somehow listed for the artifact for the above to work? For example, I see that some packages install header files separately:
https://github.com/allyourcodebase/zstd/blob/master/build.zig
```
const zstd = b.addStaticLibrary(.{
.name = "zstd",
.target = target,
.optimize = optimize,
.strip = strip,
.pic = pic,
.link_libc = true,
});
b.installArtifact(zstd);
zstd.addCSourceFiles(.{ .root = upstream.path("lib"), .files = common_sources });
// zstd does not install into its own subdirectory. :(
zstd.installHeader(upstream.path("lib/zstd.h"), "zstd.h");
zstd.installHeader(upstream.path("lib/zdict.h"), "zdict.h");
zstd.installHeader(upstream.path("lib/zstd_errors.h"), "zstd_errors.h");
```
1 Like
If you write CABI binding, you don’t need header files.
pub extern "c" fn some_func_1(fd: c.fd_t, buf: *c.Stat) c_int;
pub extern "c" fn some_func_2(fd: c.fd_t, buf: *c.Stat) c_int;
pub extern "c" fn some_func_3(fd: c.fd_t, buf: *c.Stat) c_int;
....
A Zig language is supported generating bindings from header files.
In this case, you will need bundle header files.
const c = @cImport({
@cInclude("path/to/foo.h");
});
Or you can use translate-c to create bindings.
1 Like
tsdtas
February 20, 2025, 1:18pm
4
I’d consider how it obtains the bindings an implementation detail of the module, but good point.