`std.build.Builder.addObject` does not copy artifacts by default
Zig Version
0.11.0-dev.947+cf822c6dd
Steps to Reproduce and Observed Behavior
zig init-exe- Copy the following into build.zig:
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const exe = b.addObject("zls-missing-root-obj", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
b.default_step.dependOn(&exe.step);
// exe.output_dir = b.pathFromRoot("bin");
const exe_tests = b.addTest("src/main.zig");
exe_tests.setTarget(target);
exe_tests.setBuildMode(mode);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&exe_tests.step);
}
- Run
zig build - The following directory tree, which does not have an object file in
zig-out:
❯ tree
.
├── build.zig
├── src
│ └── main.zig
└── zig-cache
[ ... ]
16 directories, 24 files
Expected Behavior
zls-missing-root-obj.o should exist in zig-out/lib/zls-missing-root-obj.o or otherwise somewhere in zig-out.
I'm not sure what changed, but the -femit-bin hack we've been relying on no longer works.
The exact shell command is:
zig build-obj <project>/root.zig -lc -femit-bin=<project>/packages/debug-bun-darwin-aarch64/bun-debug.o -fno-strip --cache-dir <project>/zig-cache --global-cache-dir /Users/jarred/.cache/zig --name bun-debug -fno-compiler-rt -fno-omit-frame-pointer -target native-native.11 -mcpu apple_m1 --mod build_options::<project>/zig-cache/c/85f8bbe9bf69b61d1147ff5eb7fce805/options.zig --mod async_io::<project>/src/io/io_darwin.zig --deps async_io,build_options -I <project>/src/deps -I <project>/src/deps --main-pkg-path /Users/jarred/Code/bun --enable-cache
The .o files are in the zig-cache directory, but they're never copied out of the cache.
you might need b.installArtifact(exe);
edit: relevant pr + issue https://github.com/ziglang/zig/pull/15245
❯ zig build obj --verbose
Build darwin-aarch64 v11 - v13.2.1 (apple_m1)
Output: /Users/jarred/Code/bun/packages/debug-bun-darwin-aarch64/bun-debug
thread 40542 panic: Cannot install a .obj build artifact.
/Users/jarred/zig/0.11.0-dev.2371+a31450375/files/lib/std/Build/InstallArtifactStep.zig:34:21: 0x10443d8d3 in create (build)
.obj => @panic("Cannot install a .obj build artifact."),
^
/Users/jarred/zig/0.11.0-dev.2371+a31450375/files/lib/std/Build.zig:1173:38: 0x1043fe60f in addInstallArtifact (build)
return InstallArtifactStep.create(self, artifact);
Unfortunately it refuses
Workaround:
const obj = b.addObject(.{
.name = "obj-name",
// ...
});
obj.override_dest_dir = std.Build.InstallDir{ .custom = "obj" };
b.installArtifact(obj);
Will output to zig-out/obj/obj-name.o.
Workaround:
const obj = b.addObject(.{ .name = "obj-name", // ... }); obj.override_dest_dir = std.Build.InstallDir{ .custom = "obj" }; b.installArtifact(obj);Will output to
zig-out/obj/obj-name.o.
Thanks! it works.
This is working as designed. Not only are objects not installed by default, neither are executables, libraries, or tests. You need to add install steps into your build graph in order to install things.
InstallArtifact is for installing binaries in standard ways, and there is no standard way to install object files. Therefore, you should use getOutputSource like this:
const install_object = b.addInstallFile(obj.getOutputSource(), "my_object_path.o");
b.getInstallStep().dependOn(&install_object.step);
Note that you might want a different extension than .o depending the target. There are some helpers for this you can call into if you need it, but first you should reconsider why you are installing object files. I can't think of any reason to do that.
Related: #16351
@andrewrk
..., but first you should reconsider why you are installing object files. I can't think of any reason to do that.
My reason for installing an object file is to get it into a known location where it can then be linked together with other object files as part of an existing Makefile project. The codebase I'm doing this for depends on now unsupported gcc-9 behavior so I couldn't compile/link it from Zig.
Do you think this is a bad reason? Is there a better path?
Zig's build system allows you to run external commands using a Step.Run. As such, I'd suggest using the Zig build system as the "root", and do one of the following to use gcc-9:
- If it's a single command, directly trigger it through a
std.Build.Step.Run - If it's more complex, write a shell script and trigger that through a
std.Build.Step.Run, passing the paths to the relevant object fields as arguments
In either case, you need to pass the path to the object file to a Step.Run. Use getOutputSource to get the FileSource corresponding to the object file, and use std.Build.Step.Run.addFileSourceArg.
@mlugg beat me to the punch, but I'll finish what I was typing up too:
For that use case, either zig build calls make, or vice versa.
If zig build is driving, then you should use std.Build.Step.Run.addArtifactArg(obj) in order to pass it as an argument to make, for example, make ZIG_OBJ=<path>.
If make is driving, then I think "installing" the objects is indeed the right way to go. In this case you treat the installation prefix not as a standard file system hierarchy but the file layout that your make system expects to see. However, that means that InstallArtifact is still correct in refusing to install object files, because you need to use getOutputSource (soon to be renamed to getOutputBin as part of #16353) and an InstallFile step to manually put that file exactly where make expects it to be relative to the installation prefix.