在zig项目中通过zig构建系统添加另外一个zig工程当然比较容易,添加其依赖即可。
但是zig项目中如何使用zig构建系统来接入一个纯c的库该如何处理呢?毕竟很多流行的c库还是并没有支持zig作为构建系统的。
同样的,还是以ghostty为例来看其是如何实践的,在项目根目录的build.zig.zon中可以找到依赖的c库,这里以比较简单的zlib库来看啊
.zlib = .{ .path = "./pkg/zlib", .lazy = true },由于官方的zlib库并没有支持zig构建系统,所以在本地创建了一层zig化的工程,这个依赖指向的也是本地路径下的zlib工程,进入pkg/zlib目录,内容也非常简单,就是一个zig项目结构的工程。自己实现了一下zlib库的zig工程化。如果后续官方支持了,也可以考虑切换到远端仓库依赖,而不是使用自己的zig工程包装了。
zlib
├── build.zig
└── build.zig.zonc项目zig工程化
zlib zig工程化的思路为,添加zlib的官方c库作为依赖,然后将其编译为一个静态库,作为zlib zig工程的一个制品。一步一步了解整个过程。
- 添加zlib c库依赖
.{
.name = .zlib,
.version = "1.3.1",
.fingerprint = 0x73887d3aef823f9e,
.paths = .{""},
.dependencies = .{
// madler/zlib
.zlib = .{
.url = "https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz",
.hash = "N-V-__8AAB0eQwD-0MdOEBmz7intriBReIsIDNlukNVoNu6o",
.lazy = true,
},
},
}- 将zlib c库编译为静态库,这是核心步骤。这个过程要求对c库的编译流程比较清楚,完成编译过程的处理
// 1. 创建一个静态库对象
const lib = b.addStaticLibrary(.{
.name = "z",
.target = target,
.optimize = optimize,
});
// 2. 这个库需要连接libc库
lib.linkLibC();
// 3. 这个静态库是基于依赖的zlib c库制作的。按照c工程进行编译,添加源代码,设置头文件搜索路径,设置编译选项
if (b.lazyDependency("zlib", .{})) |upstream| {
// 3.1 添加头文件搜索路径
lib.addIncludePath(upstream.path(""));
// 3.2 这个库安装的时候头文件的目录,这里是将zlib c目录下的h后缀的头文件安装到安装目录(zig-out)下的根目录中
lib.installHeadersDirectory(
upstream.path(""),
"",
.{ .include_extensions = &.{".h"} },
);
// 3.3 设置编译选项
var flags = std.ArrayList([]const u8).init(b.allocator);
defer flags.deinit();
try flags.appendSlice(&.{
"-DHAVE_SYS_TYPES_H",
"-DHAVE_STDINT_H",
"-DHAVE_STDDEF_H",
"-DZ_HAVE_UNISTD_H",
});
// 3.4 添加源码文件
lib.addCSourceFiles(.{
// 源码文件相对路径对于的根目录
.root = upstream.path(""),
.files = srcs, // 下面的源码文件列表
.flags = flags.items, // 编译选项
});
}
// 源码文件,这里是个相对路径
const srcs: []const []const u8 = &.{
"adler32.c",
"compress.c",
"crc32.c",
"deflate.c",
"gzclose.c",
"gzlib.c",
"gzread.c",
"gzwrite.c",
"inflate.c",
"infback.c",
"inftrees.c",
"inffast.c",
"trees.c",
"uncompr.c",
"zutil.c",
};
- 将静态库作为安装步骤的一个依赖,执行安装步骤的时候就会安装这个静态库。方便以标准布局查看制品,实际上这个对其他zig模块依赖该静态库没有影响
b.installArtifact(lib);依赖zig工程化的c项目
上面的zlib库已经zig工程化了,在这个工程中添加了一个制品,那就是名为z的这个静态库。
那么现在别的工程要使用zlib库的话,就可以添加这个zlib库依赖,然后链接这个制品
// 1. 添加zlib的zig工程以来
const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize });
// 2. zlib的zig工程提供了名为z的静态库制品,进行连接。
lib.linkLibrary(zlib_dep.artifact("z"));带zig绑定代码的zig工程化c项目库
由于这里举例的zlib库只是简单的提供的zig作为构建系统的编译方式,没有提供zig包装的代码,所以比较简单。
不过很多时候引入c库除了编译之外,还需要添加zig包装代码。c项目库的编译过程还是如上,zig代码部分就可以再额外添加一个module制品,然后外部添加以来的时候同时导致module,链接库即可。比如cimgui这个库
const module = b.addModule("cimgui", .{
.root_source_file = b.path("main.zig"),
.target = target,
.optimize = optimize,
});
// zig代码部分只会调用c项目库的头文件,添加进来就好
module.addIncludePath(b.path("vendor"));除此之外还添加了一个名为cimgui的静态库制品,思路和上面的zlib库是一样的。然后依赖这个库的时候就zig代码部分添加module依赖,然后链接上静态库即可
// cimgui
if (b.lazyDependency("cimgui", .{
.target = target,
.optimize = optimize,
})) |cimgui_dep| {
step.root_module.addImport("cimgui", cimgui_dep.module("cimgui"));
step.linkLibrary(cimgui_dep.artifact("cimgui"));
try static_libs.append(cimgui_dep.artifact("cimgui").getEmittedBin());
}