包管理
随着 0.11
的发布,zig 终于迎来了一个正式的官方包管理器,此前已知是通过第三方包管理器下载并处理包。
zig 当前并没有一个中心化存储库,包可以来自任何来源,无论是本地还是网络上。
当前的包管理模式为,先在 build.zig.zon
添加包的元信息,然后在 build.zig
中引入包。
新的文件结构
build.zig.zon
这个文件存储了包的信息,它是 zig 新引入的一种简单数据交换格式,使用了 zig 的匿名结构和数组初始化语法。
.{
// 包名字
.name = "package_management",
// 包的版本
.version = "0.0.0",
// 包的依赖
.dependencies = .{
// 包依赖项的名字
.@"zig-msgpack" = .{
// url 为包的下载地址
.url = "https://github.com/zigcc/zig-msgpack/archive/refs/tags/0.0.4.tar.gz",
// hash 为包的哈希值,但不是对整个包的哈希值,而是对包中的所有文件的哈希值
.hash = "12204ec362b0784a04825021b1bab2fe62d062db5f07052207363a835af7613bb12e",
},
},
// 包包含的源文件,一般用于在对外提供包时才使用
.paths = .{
"",
},
}
以上字段含义为:
name
:当前你所开发的包的名字version
:包的版本,使用 Semantic Version。dependencies
:依赖项,内部是一连串的匿名结构体,字段dep_name
是依赖包的名字,url
是源代码地址,hash
是对应的 hash(源文件内容的 hash)。paths
:显式声明包含的源文件,包含所有则指定为空。
🅿️ 提示
小技巧:如何直接使用指定分支的源码?
如果代码托管平台提供分支源码打包直接返回功能,就支持,例如 github 的源码分支打包返回的 url 格式为:
https://github.com/username/repo-name/archive/branch.tar.gz
其中的 username
就是组织名或者用户名,repo-name
就是对应的仓库名,branch
就是分支名。
例如 https://github.com/limine-bootloader/limine-zig/archive/trunk.tar.gz
就是获取 limine-zig 这个包的主分支源码打包。
🅿️ 提示
目前 zig 已支持通过 zig fetch
来获取 hash 并写入到 .zon
中!
编写包
🅿️ 提示
zig 支持在一个 build.zig
中对外暴露出多个模块,也就是说一个包本身可以包含多个模块,并且 lib
和 executable
两种是完全可以共存的!
如何将模块对外暴露呢?
可以使用 build
函数传入的参数 b: *std.Build
,它包含一个方法 addModule
, 它的原型如下:
pub fn addModule(b: *Build, name: []const u8, options: Module.CreateOptions) *Module
使用起来也很简单,例如:
const lib_module = b.addModule("package", .{
.root_source_file = b.path("src/root.zig"),
});
_ = lib_module;
这就是一个最基本的包暴露实现,指定了包名和包的入口源文件地址(b.path
是相对当前项目路径取 Path
),通过 addModule
函数暴露的模块是完全公开的。
🅿️ 提示
如果需要使用私有的模块,请使用 std.Build.createModule
,使用方式和 addModule
同理。
关于二进制构建结果(例如动态链接库和静态链接库),任何被执行 install
操作的构建结果均会被暴露出去(即引入该包的项目均可看到该包的构建结果,但需要手动 link )。
引入包
可以使用 build
函数传入的参数 b: *std.Build
,它包含一个方法 dependency
, 它的原型如下:
fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency
其中 name
是在在 .zon
中的包名字,它返回一个 *std.Build.Dependency
,可以使用 artifact
和 module
方法来访问包的链接库和暴露的 module
。
// 标准的构建 exe 过程
const exe = b.addExecutable(.{
.name = "package_management",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// 通过 dependency 函数获取到依赖
const msgpack = b.dependency("zig-msgpack", .{
.target = target,
.optimize = optimize,
});
// 将 module 添加到 exe 的 root module 中
exe.root_module.addImport("msgpack", msgpack.module("msgpack"));
🅿️ 提示
dependency
包含一个额外的参数 args
,这是传给对应的包构建的参数(类似在命令行构建时使用的 -D
参数,通常是我们使用 b.options
获取,通过 std.Build.option
实现),当前包的参数并不会向包传递,需要手动显式指定转发。
TODO:更多示例说明