Troubles with depending on the path of system command output


I am trying to have my project use the Zig build system as an alternative, and am very close to doing so. There is one last thing that I cannot figure out myself unfortunately, it has to do with an object file (created with addObject) that depends on the path of the output of a system command.

The full source of the build.zig can be found here:

I will attempt to show the relevant bits and explain what the problem exactly is.

I have this system command:

    const dts_path = fmtPrint("board/{s}/linux.dts", .{ sel4cp_board });
    const dtc_command = b.addSystemCommand(&[_][]const u8{
        "dtc", "-I", "dts", "-O", "dtb", dts_path, "-o"
    const dtb_image_path = dtc_command.addOutputFileArg("linux.dtb");

and this object file depends on it:

    const guest_images = b.addObject(.{
        .name = "guest_images",
        .target = target,
        .optimize = optimize,

I don’t care where linux.dtb is (ideally in the install directory but I don’t care at this point to be honest). But, what is important is that when guest_images is compiled, that it the path to linux.dtb exists.

    const dtb_image_arg = fmtPrint("-DGUEST_DTB_IMAGE_PATH=\"{s}\"", .{ dtb_image_path.getPath(b) });
    guest_images.addCSourceFiles(&.{ libvmm_tools ++ "package_guest_images.S" }, &.{

Now when I run zig build I end up getting this error message:

getPath() was called on a GeneratedFile that wasn't built yet.
  source package path: /home/ivanv/ts/sel4cp_vmm/examples/simple
  Is there a missing Step dependency on step 'run dtc (linux.dtb)'?
    The step was created by this stack trace:

I thought it was appropriate to use dtb_image_path.getPath(b) since after all guest_images depends on dtb_image_path (via addStepDependencies but maybe I’m wrong) and it needs the full final path in order to compile.

I hope I’ve explained everything, please let me know if I need to add more information. I’m sure I am just misusing the API but after looking at the autodoc and the standard library source code, I am a bit lost.


Full stack trace and error message is here just in case it helps:

getPath() was called on a GeneratedFile that wasn't built yet.
  source package path: /home/ivanv/ts/sel4cp_vmm/examples/simple
  Is there a missing Step dependency on step 'run dtc (linux.dtb)'?
    The step was created by this stack trace:
/home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/lib/std/Build/Step/Run.zig:143:26: 0x342f9e in create (build)
        .step = Step.init(.{
/home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/lib/std/Build.zig:713:37: 0x31b740 in addSystemCommand (build)
    const run_step = Step.Run.create(self, self.fmt("run {s}", .{argv[0]}));
/home/ivanv/ts/sel4cp_vmm/examples/simple/build.zig:140:43: 0x2d548f in build (build)
    const dtc_command = b.addSystemCommand(&[_][]const u8{
/home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/lib/std/Build.zig:1638:33: 0x2c3c83 in runBuild__anon_7173 (build)
        .Void =>,
    Hope that helps. Proceeding to panic.
thread 52943 panic: misconfigured build script
/home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/lib/std/Build.zig:1733:17: 0x34425e in getPath2 (build)
                @panic("misconfigured build script");
/home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/lib/std/Build.zig:1717:24: 0x31c1e4 in getPath (build)
        return getPath2(self, src_builder, null);
/home/ivanv/ts/sel4cp_vmm/examples/simple/build.zig:177:95: 0x2d58a4 in build (build)
    const dtb_image_arg = fmtPrint("-DGUEST_DTB_IMAGE_PATH=\"{s}\"", .{ dtb_image_path.getPath(b) });
/home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/lib/std/Build.zig:1638:33: 0x2c3c83 in runBuild__anon_7173 (build)
        .Void =>,
/home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/lib/build_runner.zig:297:29: 0x2bfa52 in main (build)
        try builder.runBuild(root);
/home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/lib/std/start.zig:574:37: 0x2ab35e in posixCallMainAndExit (build)
            const result = root.main() catch |err| {
/home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/lib/std/start.zig:243:5: 0x2aae41 in _start (build)
    asm volatile (switch (native_arch) {
???:?:?: 0x9 in ??? (???)
Unwind information for `???:0x9` was not available, trace may be incomplete

error: the following build command crashed:
/home/ivanv/ts/sel4cp_vmm/examples/simple/zig-cache/o/27b920b93b9c1ce915fc054057a0b321/build /home/ivanv/zigs/zig-linux-x86_64-0.12.0-dev.21+ac95cfe44/zig /home/ivanv/ts/sel4cp_vmm/examples/simple /home/ivanv/ts/sel4cp_vmm/examples/simple/zig-cache /home/ivanv/.cache/zig -Dsdk=/home/ivanv/ts/sel4cp/release/sel4cp-sdk-1.2.6 -Dboard=qemu_arm_virt qemu --summary all

This may help you (this is how I do it)

at the end, you see prog{] if you don’t put anything, it’s in your source directory

    const Prog = b.addExecutable(.{
    .name = "Gencurs",
    .root_source_file = .{ .path = "./Gencurs.zig" },
    .target = target,
    .optimize = optimize,

    Prog.addIncludePath(.{.path = "./lib/"});
    Prog.addObjectFile(.{.cwd_relative = "/usr/lib/"});
    Prog.addModule("dds"   , dds);
    Prog.addModule("cursed", cursed); 
    Prog.addModule("utils" , utils);
    Prog.addModule("forms" , forms);
    Prog.addModule("grid"  , grid);
    Prog.addModule("match" , match);
    Prog.addModule("mdlPanel" , mdlPanel);
    Prog.addModule("mdlObjet" , mdlObjet);
    Prog.addModule("mdlSjson" , mdlSjson);

    Prog.addModule("logger" , logger);

    const install_exe = b.addInstallArtifact(Prog, .{});

I think this is backwards.
Guest_images depends on dtc_command. When dtc command runs, it will generate dts_path.

I think you need to switch to this:

1 Like

Thanks for the reply! Unfortunately I tried that before (and have tried that again now) but it results in the same error.

If I just give a dummy path instead of dtb_image_path.getPath(b) to avoid the stack trace and print the summary, I get this:

qemu transitive failure
└─ run qemu-system-aarch64 transitive failure
   └─ sel4cp transitive failure
      └─ run /home/ivanv/ts/sel4cp_dev/release/sel4cp-sdk-1.2.6//bin/sel4cp transitive failure
         └─ install transitive failure
            β”œβ”€ install vmm success
            β”‚  └─ zig build-lib vmm Debug aarch64-freestanding-none success 99ms MaxRSS:64M
            └─ install vmm.elf transitive failure
               └─ zig build-exe vmm.elf Debug aarch64-freestanding-none transitive failure
                  β”œβ”€ zig build-lib vmm Debug aarch64-freestanding-none (reused)
                  └─ zig build-obj guest_images Debug aarch64-freestanding-none 1 errors
                     └─ run dtc (linux.dtb) success 99ms MaxRSS:2M
/home/ivanv/ts/sel4cp_vmm/tools/package_guest_images.S:1:1: error: unable to build C object: clang exited with code 1

The error is expected since I haven’t given the actual path to linux.dtb but you can see from the summary that the object depends on the dtc command, just like I want. The problem just seems to be, well how do I get the path of linux.dtb? I can see that it’s in the cache at zig-cache/o/6ecb85b472c17ab1f3377e967da225c0/linux.dtb. So really what I’m looking for now is which API to call to get this path. I thought it would be getPath on the LazyPath but I guess not.

I think I tried in the past to make the dtc command output to the install directory, but the problem with that is that zig-out or whatever the install directory is might not exist depending on the scheduling of the threads that are running each job/command.

Thanks to the new documentation, I was able to solve this! Zig Build System ⚑ Zig Programming Language.

This depend line was what I was missing:

guest_images.step.dependOn(&b.addInstallFileWithDir(dtb, .prefix, "linux.dtb").step);

Thanks to Andrew and Loris for putting out some (much needed) build system documentation.