我主要想做的是这样,通过外部命令创建 ISO 文件,再用另一个命令修改同一个 ISO 文件,虽然第二个其实不是外部命令,但在这个问题中可以把它当成外部命令。
我在 build.zig 文件里是这样实现的。
const staging_files = b.addWriteFiles();
// ...prepare the filesystem tree for the ISO...
const create_image = b.addSystemCommand(&.{ "xorriso", "-as", "mkisofs" });
// ...roughly thirty thousand invocations of `create_image.addArgs`...
create_image.addArg("-output");
const image_path = create_image.addOutputFileArg("brindlequasar.iso");
create_image.addDirectoryArg(
staging_files.getDirectory(),
);
const install_boot = b.addRunArtifact(limine_binary); // imagine `addSystemCommand` here
install_boot.addArg("bios-install");
install_boot.addFileArg(image_path);
(limine_binary 是编译包依赖提供的 C 程序得到的,它只支持直接修改原文件。)
接下来我想把这个 ISO 文件作为参数传给 QEMU 并运行。我以为只要调用 addFileArg,传入 ISO 的 LazyPath 就行,上面把它存为 image_path。
const launch_vm = b.addSystemCommand(&.{"qemu-system-x86_64"});
// ...other arguments omitted...
launch_vm.addArg("-cdrom");
launch_vm.addFileArg(image_path);
但结果并不符合预期。干净构建时,也就是删除 zig-cache 后,构建程序执行了 create_image 和 launch_vm,却跳过了 install_boot。要得到预期行为,我得显式添加 launch_vm 对 install_boot 的依赖。
// FIXME: Required here, though I haven't fully understood the reason.
launch_vm.step.dependOn(&install_boot.step);
加上这句后一切似乎正常了,至少我还没找到弄坏它的方法,但我觉得这种做法有点不对。
我本来以为 LazyPath 会记住接收它的所有步骤,让构建程序安排依赖,但看了实现似乎并非如此,我能看出这个类型只存一个 *Step,应该就是最初创建这个 LazyPath 的步骤。继续找又看到了几处文档注释,似乎说明生成步骤之后再修改 LazyPath 指向的文件是不受支持的,或者至少我现在的做法不对。
所以我的问题归结为下面这些。
从英语翻译·查看原文What I'm essentially attempting to do here is this: I'm creating an ISO file by invoking an external command, then I want another command to change that same ISO file, although this second command isn't really an external command, we can treat it as external for this particular question.
Here's the way that I have implemented this in my build.zig file:
const staging_files = b.addWriteFiles();
// ...prepare the filesystem tree for the ISO...
const create_image = b.addSystemCommand(&.{ "xorriso", "-as", "mkisofs" });
// ...roughly thirty thousand invocations of `create_image.addArgs`...
create_image.addArg("-output");
const image_path = create_image.addOutputFileArg("brindlequasar.iso");
create_image.addDirectoryArg(
staging_files.getDirectory(),
);
const install_boot = b.addRunArtifact(limine_binary); // imagine `addSystemCommand` here
install_boot.addArg("bios-install");
install_boot.addFileArg(image_path);
(limine_binary comes from compiling a C program supplied by a package dependency, and it supports only changing files in place.)
The next thing I want to do is launch QEMU with that ISO file passed as an argument. I thought this simply required calling addFileArg with the ISO's LazyPath (stored above as image_path):
const launch_vm = b.addSystemCommand(&.{"qemu-system-x86_64"});
// ...other arguments omitted...
launch_vm.addArg("-cdrom");
launch_vm.addFileArg(image_path);
That doesn't behave as intended, though. On a clean build (after removing zig-cache), the runner executes create_image and launch_vm but omits install_boot. To get the expected behavior, I must add an explicit dependency from launch_vm to install_boot:
// FIXME: Required here, though I haven't fully understood the reason.
launch_vm.step.dependOn(&install_boot.step);
With that addition it all appears to run correctly (at least, I haven't yet discovered a way to break it), but this approach feels off.
My original assumption was that a LazyPath remembered all the steps receiving it, allowing the runner to arrange dependancies, but reading the implementation seems to disprove that; as far as I can see, this type stores only a single *Step, presumably the step which originally constructed that LazyPath. Further searching turned up documentation comments implying that modifying a file behind a LazyPath after its generating step isn't supported, or at least isn't the right approach here.
So my questions boil down to: