제가 하려는 건 이렇습니다. 외부 명령을 실행해 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를 실행하고 싶어요. ISO의 LazyPath를 addFileArg에 넘기기만 하면 된다고 생각했어요(위에서는 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: