Package manager 101 - what am I doing wrong?

I’m trying to create a couple of “helloworld” projects (library + library user) to test my ability to use Zig package manager… and I invariably fail.

Could someone please take a look and tell me where do I go wrong?
I’m on 0.12.0-dev.3152+90c1a2c41. Here’s what I tried:

  1. Created two folders for two projects: test-lib-01 and test-lib-user-01.
  2. In test-lib-01: ran zig init.
  3. In test-lib-01: in build.zig commented out everything related to the executable part:
    const exe = b.addExecutable(.{
        .name = "test-lib-01",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    b.installArtifact(exe);
    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());
    if (b.args) |args| {
        run_cmd.addArgs(args);
    }
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);
    . . .
    const exe_unit_tests = b.addTest(.{
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });
    const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
    . . .
    test_step.dependOn(&run_exe_unit_tests.step);
  1. Edit: this is what was missing: in test-lib-01: in build.zig added the following line
    _ = b.addModule("test_lib_01",
                    .{
                        .root_source_file = .{ .path = "src/root.zig" },
                        .target = target, // Pass these values from the host project to the
                        .optimize = optimize, // dependency module
                    });

just before b.installArtifact(lib);

  1. In test-lib-01: removed src/main.zig.
  2. In test-lib-01: in build.zig.zon, in .path, uncommented the following lines:
        "build.zig",
        "build.zig.zon",
        "src",
  1. In test-lib-01: ran zig build command, observed the library binary file zig-out/lib/libtest-lib-01.a.
  2. In test-lib-01: removed zig-out and zig-cache folders.
  3. Compressed test-lib-01 folder as test-lib-01.tar.gz and copied this file to test-lib-user-01 folder.
  4. In test-lib-user-01: ran zig init command.
  5. In test-lib-user-01: in build.zig.zon, in .dependencies, added the following lines:
        .test_lib_01 = .{
            .url = "file:///home/archie/projects/zig-playground/test-lib-user-01/test-lib-01.tar.gz",
            .hash = "1220d8cb8a71180f18834eb687ddb04b0500a78295d819cef6c89f6e5ff9eb417188",
        },

Note the underscores in test_lib_01.

  1. In test-lib-user-01: in build.zig, after the call to const exe = b.addExecutable( and before the call to b.installArtifact(exe);, added the following lines:
    const zsp = b.dependency("test_lib_01",
    .{
        .target = target,
        .optimize = optimize,
        .openssl = false, // set to true to enable TLS support
    });

    exe.root_module.addImport("test_lib_01", zsp.module("test_lib_01"));
  1. In test-lib-user-01: ran zig build command. Received the following error:
$ zig build
error: invalid option: -Dopenssl
/home/archie/.night.zig/zig-linux-x86_64-0.12.0-dev.3152+90c1a2c41/lib/std/Build.zig:1873:35: 0x1146229 in dependency__anon_16348 (build)
            return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
                                  ^
/home/archie/projects/zig-playground/test-lib-user-01/build.zig:40:29: 0x10fc3bc in build (build)
    const zsp = b.dependency("test_lib_01",
                            ^
/home/archie/.night.zig/zig-linux-x86_64-0.12.0-dev.3152+90c1a2c41/lib/std/Build.zig:1992:33: 0x10d69f3 in runBuild__anon_8909 (build)
        .Void => build_zig.build(b),
                                ^
 . . .

error: the following build command crashed:
/home/archie/projects/zig-playground/test-lib-user-01/zig-cache/o/29cfa6b5f14d921c0a2e559bb6106d09/build /home/archie/.night.zig/zig-linux-x86_64-0.12.0-dev.3152+90c1a2c41/zig /home/archie/projects/zig-playground/test-lib-user-01 /home/archie/projects/zig-playground/test-lib-user-01/zig-cache /home/archie/.cache/zig --seed 0x1b272975 -Zc8310a48dd5b0fb0

Going with .openssl = false produces the same error:

$ zig build
error: invalid option: -Dopenssl
/home/archie/.night.zig/zig-linux-x86_64-0.12.0-dev.3152+90c1a2c41/lib/std/Build.zig:1873:35: 0x1146229 in dependency__anon_16348 (build)
            return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
                                  ^
/home/archie/projects/zig-playground/test-lib-user-01/build.zig:40:29: 0x10fc3bc in build (build)
    const zsp = b.dependency("test_lib_01",
                            ^

the options that you pass to a dependency are mapped to options you have defined in the dependency’s build script.

you can pass target and optimize presumably because you have these two lines in the dependency’s build.zig:

const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

For openssl to be a possible option, you must have something like that in the dependency’s build.zig:

const openssl = b.option(bool, "openssl", "Enable OpenSSL") orelse false;

Okay, truth be told, I took that part from an example I found online, without actually understanding what .openssl means. I thought it was a standard field.
My intention was to have the simplest possible pair of projects that uses the package manager.

After commenting out .openssl in test-lib-user-01’s build.zig:

    const zsp = b.dependency("test_lib_01",
    .{
        .target = target,
        .optimize = optimize,
        //.openssl = false, // set to true to enable TLS support
    });

I now get this error when I run zig build in test-lib-user-01:

$ zig build
thread 2045 panic: unable to find module 'test_lib_01'
/home/archie/.night.zig/zig-linux-x86_64-0.12.0-dev.3152+90c1a2c41/lib/std/debug.zig:434:22: 0x11a1349 in panicExtra__anon_18757 (build)
    std.builtin.panic(msg, trace, ret_addr);
                     ^
/home/archie/.night.zig/zig-linux-x86_64-0.12.0-dev.3152+90c1a2c41/lib/std/debug.zig:409:15: 0x1176199 in panic__anon_17480 (build)
    panicExtra(null, null, format, args);
              ^
/home/archie/.night.zig/zig-linux-x86_64-0.12.0-dev.3152+90c1a2c41/lib/std/Build.zig:1796:18: 0x1146277 in module (build)
            panic("unable to find module '{s}'", .{name});
                 ^
/home/archie/projects/zig-playground/test-lib-user-01/build.zig:47:56: 0x10fc3bf in build (build)
    exe.root_module.addImport("test_lib_01", zsp.module("test_lib_01"));
                                                       ^
/home/archie/.night.zig/zig-linux-x86_64-0.12.0-dev.3152+90c1a2c41/lib/std/Build.zig:1992:33: 0x10d69b3 in runBuild__anon_8909 (build)
        .Void => build_zig.build(b),
                                ^

I don’t really understand what is a “module”, and how it relates to the library project, test-lib-01. Do I have to declare it in test-lib-01’s build.zig or build.zig.zon?

Please note that since test-lib-01 (with dashes) is not a valid struct field name in Zig (at least without @""), I have used test_lib_01 (with underscores) in test-lib-user-01’s build.zig.zon (.test_lib_01 = .{) and build.zig (const zsp = b.dependency("test_lib_01",, exe.root_module.addImport("test_lib_01", zsp.module("test_lib_01"));).

I don’t know whether this approach is good, this was a shot in the dark.

I just wrote How to package a zig source module and how to use it
I hope it helps.

5 Likes

So, it looks like the missing part was this line:

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

in test-lib-01’s build.zig.

I have added it, just before b.installArtifact(lib);:

    const lib = b.addStaticLibrary(.{
        .name = "test-lib-01",
        // In this case the main source file is merely a path, however, in more
        // complicated build scripts, this could be a generated file.
        .root_source_file = .{ .path = "src/root.zig" },
        .target = target,
        .optimize = optimize,
    });

    _ = b.addModule("test_lib_01",
                    .{
                        .root_source_file = .{ .path = "src/root.zig" },
                        .target = target, // Pass these values from the host project to the
                        .optimize = optimize, // dependency module
                    });

    // This declares intent for the library to be installed into the standard
    // location when the user invokes the "install" step (the default step when
    // running `zig build`).
    b.installArtifact(lib);

After testing that test-lib-01 builds, compressing it into .tar.gz and copying this file to test-lib-user-01, I ran zig build in that folder… and got this error:

$ zig build
thread 2537 panic: unable to find module 'test_lib_01'
/home/archie/.night.zig/zig-linux-x86_64-0.12.0-dev.3152+90c1a2c41/lib/std/debug.zig:434:22: 0x11a1349 in panicExtra__anon_18757 (build)
    std.builtin.panic(msg, trace, ret_addr);
                     ^

It’s only when I deleted Zig cache not just in the project folder, but in my home folder (~/.cache/zig), I got the sweet hash error:

$ zig build
/home/archie/projects/zig-playground/test-lib-user-01/build.zig.zon:43:21: error: hash mismatch: manifest declares 1220d8cb8a71180f18834eb687ddb04b0500a78295d819cef6c89f6e5ff9eb417188 but the fetched package has 1220e46e15ceba47952c6690c046145bceeb248f774c94c747da43329ad4197127c2
            .hash = "1220d8cb8a71180f18834eb687ddb04b0500a78295d819cef6c89f6e5ff9eb417188",
                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

And then, when I fixed the hash in test-lib-user-01’s build.zig.zon, I got test-lib-user-01 to build.

Yay!!!

Update: modified b.addModule to pass target and optimize from the host project to the dependency module.

2 Likes