How to run custom build tools in a zig package that implements build tools?

I’m trying to write a zig package that implements complex build steps. Users will import this into their build.zig to get access to these custom steps.

Here is one of my steps that I am providing to users:

pub fn createLayer(b: *std.Build, options: CreateLayerOptions) *Layer {
    const tar_cf_tool = b.addExecutable(.{
        .name = "create_layer",
        .root_module = b.createModule(.{
            .root_source_file = b.path("src/build/create_layer.zig"),
            .target = b.graph.host,
        }),
    });
    // HACK: this is wack and won't work under other peoples projects
    tar_cf_tool.root_module.addImport(
        "oci",
        b.createModule(
            .{ .root_source_file = b.path("src/root.zig") },
        ),
    );
    const run = b.addRunArtifact(tar_cf_tool);
    const blob = run.addOutputFileArg(switch (options.compression) {
        .raw => "blob.tar",
        .gzip => "blob.tar.gz",
    });
    ...

There are a few problems as it is written:

  1. b.path will not refer to my source files. It will refer to the user’s source files. (the user doesn’t have a src/build/create_layer.zig, only I do.
  2. same problem for my b.createModule.

How do I fix this?

a direct solution:

const dep = b.dependency("dep");
const dep_build = dep.builder;

You can simply require they pass along your builder.

But that’s annoying, error-prone, and IDK if it will old up when the build gets sandboxed.

A better solution, that should work given the little I can see of what you’re trying to do:
just install your tar_cf_tool in your normal build fn.
users of your dependency can const create_layer_exe = dep.artifact("create_layer") and run it themselves with their own arguments.
Just document its CLI interface so they know what to give it.

you could do something inbetween aswell.

1 Like

I think you can use const dep = b.dependencyFromBuildZig(@This(), options);
and then within your function you can use dep.path instead of b.path.

I think you also can use dep.builder.createModule.

@vulpesx’s suggestion to let the user use the create_layer artifact also makes a lot of sense, then you still could have a createLayer function if you want to but it would just create the addRunArtifact step and add some input and output arguments. So it would use dep.artifact("create_layer") too.

2 Likes