Skip to content
zig 版本:0.13.0

包管理

随着 0.11 的发布,zig 终于迎来了一个正式的官方包管理器,此前已知是通过第三方包管理器下载并处理包。

zig 当前并没有一个中心化存储库,包可以来自任何来源,无论是本地还是网络上。

当前的包管理模式为,先在 build.zig.zon 添加包的元信息,然后在 build.zig 中引入包。

新的文件结构

build.zig.zon 这个文件存储了包的信息,它是 zig 新引入的一种简单数据交换格式,使用了 zig 的匿名结构和数组初始化语法。

zig
.{
    // 包名字
    .name = "importer",
    // 包版本
    .version = "0.0.0",
    // 包依赖
    .dependencies = .{
        // 包依赖项的名字
        .@"tarball-exporter" = .{
            // url 为包的下载地址
            .url = "https://github.com/zigcc/zig-msgpack/archive/refs/tags/0.0.5.tar.gz",
            // hash 为包的哈希值,但不是对整个包的哈希值,而是对包中的所有文件的哈希值
            .hash = "12207ec1296852c17c0424b5e650003bd9a3c4f35e9d0ab586d39ea0ab812c2d9f2c",
        },
        .@"path-exporter" = .{
            // path 为本地包的路径
            .path = "../package_management_exporter",
        },
    },
    // 包所包含的源文件,一般用于在对外提供包时才使用,还是建议养成写清楚paths的习惯
    .paths = .{
        "src",
        "build.zig",
        "build.zig.on",
    },
}

以上字段含义为:

  • name:当前你所开发的包的名字
  • version:包的版本,使用 Semantic Version
  • dependencies:依赖项,内部是一连串的匿名结构体,字段 dep_name 是依赖包的名字, url 是源代码地址, hash 是对应的 hash(源文件内容的 hash), path是不使用源码包而是本地目录时目录的路径。 当使用目录方法导入包时就不能使用urlhash,反之同理。
  • 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 这个包的主分支源码打包。

而若是想要离线使用本地包时则是先下载源码包并直接使用绝对或相对路径导入,例如在下载完包之后放在项目的deps目录下,那么使用本地包的格式为:

./deps/tunk.tar.gz

🅿️ 提示

目前 zig 已支持通过 zig fetch 来获取 hash 并写入到 .zon 中!

编写包

🅿️ 提示

zig 支持在一个 build.zig 中对外暴露出多个模块,也就是说一个包本身可以包含多个模块,并且 libexecutable 两种是完全可以共存的!

如何将模块对外暴露呢?

可以使用 build 函数传入的参数 b: *std.Build,它包含一个方法 addModule, 它的原型如下:

zig
pub fn addModule(
  b: *Build,
  name: []const u8,
  options: Module.CreateOptions
) *Module

使用起来也很简单,例如:

zig
_ = b.addModule("exporter", .{
    .root_source_file = b.path("src/root.zig"),
    .target = target,
    .optimize = optimize,
});

这就是一个最基本的包暴露实现,指定了包名和包的入口源文件地址(b.path 是相对当前项目路径取 Path),通过 addModule 函数暴露的模块是完全公开的。

🅿️ 提示

如果需要使用私有的模块,请使用 std.Build.createModule,使用方式和 addModule 同理。

关于二进制构建结果(例如动态链接库和静态链接库),任何被执行 install 操作的构建结果均会被暴露出去(即引入该包的项目均可看到该包的构建结果,但需要手动 link )。

引入包

可以使用 build 函数传入的参数 b: *std.Build,它包含一个方法 dependency, 它的原型如下:

zig
fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency

其中 name 是在在 .zon 中的包名字,它返回一个 *std.Build.Dependency,可以使用 artifactmodule 方法来访问包的链接库和暴露的 module

zig
// 通过 dependency 函数获取到依赖
const pe = b.dependency("path-exporter", .{
    .target = target,
    .optimize = optimize,
});
const te = b.dependency("tarball-exporter", .{
    .target = target,
    .optimize = optimize,
});
// 将 module 添加到 exe 的 root module 中
exe.root_module.addImport("path_exporter", pe.module("exporter"));
exe.root_module.addImport("tarball_exporter", te.module("msgpack"));

🅿️ 提示

dependency 包含一个额外的参数 args,这是传给对应的包构建的参数(类似在命令行构建时使用的 -D 参数,通常是我们使用 b.options 获取,通过 std.Build.option 实现),当前包的参数并不会向包传递,需要手动显式指定转发。