I have a Zig build script that fetches a tarball from somewhere, then builds an executable by invoking system commands. A shortened version could look something like this:
const upstream = b.dependency("foo", .{});
// more steps omitted
const run_build_command = b.addSystemCommand(&.{
"./build_foo.sh",
upstream_root_path, // defined in a previous step
});
const exe_file = b.addInstallBinFile(upstream.path("build/output/foo"), "foo");
exe_file.step.dependOn(&run_build_command.step);
b.getInstallStep().dependOn(&exe_file.step);
When I run zig build, foo ends up in zig-out/bin/foo, as expected.
If a downstream dependency then tries to depend on foo through my Zig “wrapper”:
const foo = b.dependency("build_foo", .{});
How can I make sure foo gets installed alongside the downstream project?
The binary in question isn’t needed to build or link the downstream project, but it is needed to run the project on the target platform.
It’s not clear exactly what you’re asking. Is your question about how to expose the foo binary to a package consumer? Or do you want the foo binary to be automatically installed alongside some other artifact?
If you want to expose the binary, you could do so using b.addNamedLazyPath(). Here’s an example (that also includes some fixes to how you’re currently invoking the script):
// Don't pass paths as strings! Use addFile/DirectoryArg instead.
// Always specify input/output files/directories as arguments,
// never hard-code them in your script. By specifying inputs and outputs
// as lazy paths, Zig will correctly cache the results of the scripts
// and only re-run the command when needed.
const run_build_command = b.addSystemCommand(&.{"bash"});
run_build_command.addFileArg(b.path("build_foo.sh"));
// input directory passed to script
run_build_command.addDirectoryArg(upstream.path("root"));
// output directory passed to script
const out_path = run_build_command.addOutputDirectoryArg("out");
// Export the built binary as a named lazy path.
b.addNamedLazyPath("bin", out_path .path(b, "foo"));
// ---
// In the consuming build.zig:
const build_foo_dep = b.dependency("build_foo", .{});
const foo_path = build_foo_dep.namedLazyPath("foo");
b.getInstallStep().dependOn(&b.addInstallBinFile(foo_path , "foo").step);
If your question is about the latter (automatically installing the binary), AFAIK you can’t. Aside from headers (artifact.installHeadersDirectory) there’s currently no way to “bundle” files with an artifact and have them get installed all at once. The package consumer will need to orchestrate the install process themselves so that everything is installed to the right place.
Is your question about how to expose the foo binary to a package consumer? Or do you want the foo binary to be automatically installed alongside some other artifact?
Either will do. Just exposing the binary is also fine.
The downstream/package consumer is building a root filesystem for an embedded system - all the binaries it needs are added as dependencies.