1use anyhow::Context as _;
13use flowey::node::prelude::ReadVar;
14use flowey::pipeline::prelude::*;
15use flowey_lib_hvlite::_jobs::local_build_and_run_nextest_vmm_tests::BuildSelections;
16use flowey_lib_hvlite::_jobs::local_build_and_run_nextest_vmm_tests::VmmTestSelections;
17use flowey_lib_hvlite::build_incubator::IncubatorProfileNameOrPath;
18use flowey_lib_hvlite::common::CommonPlatform;
19use flowey_lib_hvlite::common::CommonTriple;
20use flowey_lib_hvlite::init_vmm_tests_env::PetriParams;
21use flowey_lib_hvlite::install_vmm_tests_external_deps::VmmTestsExternalDeps;
22use flowey_lib_hvlite::install_vmm_tests_external_deps::VmmTestsExternalDepsLinux;
23use flowey_lib_hvlite::install_vmm_tests_external_deps::VmmTestsExternalDepsWindows;
24use petri_artifacts_core::ArtifactId;
25use petri_artifacts_core::ArtifactListOutput;
26use std::collections::BTreeMap;
27use std::collections::BTreeSet;
28use std::io::Write as _;
29use std::num::NonZeroU64;
30use std::path::Path;
31use std::path::PathBuf;
32use std::process::Command;
33use std::process::Stdio;
34use vmm_test_images::KnownTestArtifacts;
35
36#[derive(clap::Args)]
38pub struct VmmTestsRunCli {
39 #[clap(long)]
43 target: Option<VmmTestTargetCli>,
44
45 #[clap(long)]
53 dir: Option<PathBuf>,
54
55 #[clap(long, default_value = "all()")]
62 filter: String,
63
64 #[clap(long)]
66 verbose: bool,
67 #[clap(long)]
69 install_missing_deps: bool,
70
71 #[clap(long)]
73 release: bool,
74
75 #[clap(long)]
77 build_only: bool,
78 #[clap(long)]
80 copy_extras: bool,
81
82 #[clap(long)]
84 skip_vhd_prompt: bool,
85
86 #[clap(long)]
92 no_lazy_fetch: bool,
93
94 #[clap(long)]
96 custom_kernel_modules: Option<PathBuf>,
97 #[clap(long)]
99 custom_kernel: Option<PathBuf>,
100 #[clap(long)]
103 custom_uefi_firmware: Option<PathBuf>,
104
105 #[clap(long)]
107 ci_profile: bool,
108
109 #[clap(long)]
112 no_reuse_prepped_vhds: bool,
113
114 #[clap(long)]
118 pub disable_secure_avic: bool,
119
120 #[clap(long)]
122 repetitions: Option<u64>,
123
124 #[clap(long, num_args = 0..=1)]
137 #[expect(clippy::option_option)]
138 incubator: Option<Option<PathBuf>>,
139}
140
141struct CargoNextestListRequest<'a> {
142 repo_root: &'a Path,
143 target: &'a str,
144 filter: &'a str,
145 release: bool,
146 include_ignored: bool,
147}
148
149struct RustSuite {
150 binary_path: PathBuf,
151 testcases: Vec<String>,
152}
153
154#[derive(Default, Debug)]
156struct ResolvedArtifactSelections {
157 build: BuildSelections,
159 downloads: BTreeSet<KnownTestArtifacts>,
161 force_downloads: BTreeSet<KnownTestArtifacts>,
164 needs_release_igvm: bool,
166 needs_hyperv: bool,
168 needs_hardware_isolation: bool,
170}
171
172impl IntoPipeline for VmmTestsRunCli {
173 fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
174 if !matches!(backend_hint, PipelineBackendHint::Local) {
175 anyhow::bail!("vmm-tests-run is for local use only")
176 }
177
178 let Self {
179 target,
180 dir,
181 filter,
182 verbose,
183 install_missing_deps,
184 release,
185 build_only,
186 copy_extras,
187 skip_vhd_prompt,
188 no_lazy_fetch,
189 custom_kernel_modules,
190 custom_kernel,
191 custom_uefi_firmware,
192 ci_profile,
193 no_reuse_prepped_vhds,
194 disable_secure_avic,
195 repetitions,
196 incubator,
197 } = self;
198
199 if incubator.is_some() && target.is_none() {
202 anyhow::bail!("--incubator requires --target (e.g., --target linux-aarch64-musl)");
203 }
204
205 let repetitions =
206 NonZeroU64::new(repetitions.unwrap_or(1)).context("repetitions must not be zero")?;
207
208 let target = resolve_target(target, backend_hint)?;
209 let target_os = target.as_triple().operating_system;
210 let target_architecture = target.common_arch()?;
211 let target_str = target.as_triple().to_string();
212
213 let windows_guest_platform =
218 if matches!(FlowPlatform::host(backend_hint), FlowPlatform::Linux(_))
219 && !flowey_cli::running_in_wsl()
220 {
221 CommonPlatform::WindowsGnu
222 } else {
223 CommonPlatform::WindowsMsvc
224 };
225
226 let repo_root = crate::repo_root();
227
228 let incubator_profile = incubator
229 .map(|i| resolve_incubator(i, &target))
230 .transpose()?;
231
232 let include_ignored = build_only || incubator_profile.is_some();
244
245 log::info!(
248 "Discovering artifacts for filter: {} (target: {})",
249 filter,
250 target
251 );
252
253 let suites = run_cargo_nextest_list(CargoNextestListRequest {
255 repo_root: &repo_root,
256 target: &target_str,
257 filter: &filter,
258 release,
259 include_ignored,
264 })?;
265
266 if suites.is_empty() {
267 anyhow::bail!("No tests found for the given filter");
268 }
269
270 let mut artifacts = Vec::new();
272 for suite in suites.values() {
273 artifacts.append(&mut query_test_binary_artifacts(suite)?);
274 }
275
276 let mut resolved = ResolvedArtifactSelections::default();
278 for artifact in artifacts {
279 resolved.resolve_artifact(&artifact)?;
280 }
281
282 resolved.needs_hyperv = suites
284 .values()
285 .any(|s| s.testcases.iter().any(|name| name.contains("hyperv")));
286 resolved.needs_hardware_isolation = suites.values().any(|s| {
287 s.testcases
288 .iter()
289 .any(|name| name.contains("snp") || name.contains("tdx"))
290 });
291
292 if no_lazy_fetch {
306 log::info!("Lazy fetch disabled");
307 } else {
308 let mut hyperv_tests: usize = 0;
309 let mut hyperv_artifacts = Vec::new();
310 for suite in suites.values() {
311 let hyperv_testcases: Vec<_> = suite
312 .testcases
313 .iter()
314 .filter(|name| name.contains("hyperv"))
315 .cloned()
316 .collect();
317
318 if !hyperv_testcases.is_empty() {
319 hyperv_tests += hyperv_testcases.len();
320 hyperv_artifacts.append(&mut query_test_binary_artifacts(&RustSuite {
321 binary_path: suite.binary_path.clone(),
322 testcases: hyperv_testcases,
323 })?);
324 }
325 }
326
327 resolved.downloads.retain(|a| !a.supports_blob_disk());
328
329 resolved
331 .downloads
332 .extend(resolved.force_downloads.iter().cloned());
333
334 if hyperv_tests == 0 {
335 log::info!("Lazy fetch enabled: disk images will be streamed on demand via HTTP");
336 } else {
337 log::info!(
338 "Downloading disk images required by {} Hyper-V tests",
339 hyperv_tests
340 );
341 }
342
343 for artifact in hyperv_artifacts {
346 resolved.resolve_artifact(&artifact)?;
347 }
348 }
349
350 log::info!("Resolved selections: {:?}", resolved);
351
352 let needs_windows_disk = !build_only
368 && (resolved.needs_hyperv
369 || resolved
370 .downloads
371 .iter()
372 .any(|a| a.filename().ends_with(".vhdx")));
373 validate_output_dir(dir.as_deref(), target_os, needs_windows_disk)?;
374 let test_content_dir = dir.unwrap_or_else(|| repo_root.join("target").join("vmm_tests"));
375 std::fs::create_dir_all(&test_content_dir).context("failed to create output directory")?;
376
377 let openvmm_repo = flowey_lib_common::git_checkout::RepoSource::ExistingClone(
378 ReadVar::from_static(repo_root),
379 );
380
381 let mut pipeline = Pipeline::new();
382
383 let mut job = pipeline.new_job(
384 FlowPlatform::host(backend_hint),
385 FlowArch::host(backend_hint),
386 "build all dependencies and run vmm tests",
387 );
388
389 job = job.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init);
390
391 if let (Some(kernel_path), Some(modules_path)) =
393 (custom_kernel.clone(), custom_kernel_modules.clone())
394 {
395 job =
396 job.dep_on(
397 move |_| flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalKernel {
398 arch: target_architecture,
399 kernel: ReadVar::from_static(kernel_path),
400 modules: ReadVar::from_static(modules_path),
401 },
402 );
403 }
404
405 if let Some(fw_path) = custom_uefi_firmware {
407 job = job.dep_on(move |_| {
408 flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalUefi(
409 target_architecture,
410 ReadVar::from_static(fw_path),
411 )
412 });
413 }
414
415 job = job
416 .dep_on(
417 |_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
418 hvlite_repo_source: openvmm_repo.clone(),
419 },
420 )
421 .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_common::Params {
422 local_only: Some(flowey_lib_hvlite::_jobs::cfg_common::LocalOnlyParams {
423 interactive: true,
424 auto_install: install_missing_deps,
425 ignore_rust_version: true,
426 }),
427 verbose: ReadVar::from_static(verbose),
428 locked: false,
429 deny_warnings: false,
430 no_incremental: false,
431 })
432 .dep_on(|ctx| {
433 flowey_lib_hvlite::_jobs::local_build_and_run_nextest_vmm_tests::Params {
434 target,
435 windows_guest_platform,
436 test_content_dir,
437 selections: selections_from_resolved(filter, resolved, target_os),
438 release,
439 build_only,
440 copy_extras,
441 custom_kernel_modules,
442 custom_kernel,
443 skip_vhd_prompt,
444 nextest_profile: if ci_profile {
445 flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Ci
446 } else {
447 flowey_lib_hvlite::run_cargo_nextest_run::NextestProfile::Default
448 },
449 petri_params: PetriParams {
450 disable_remote_artifacts: false,
451 reuse_prepped_vhds: !no_reuse_prepped_vhds,
452 require_2mb_hugetlb: false, },
454 disable_secure_avic,
455 repetitions,
456 incubator_profile,
457 done: ctx.new_done_handle(),
458 }
459 });
460
461 job.finish();
462
463 Ok(pipeline)
464 }
465}
466
467fn run_cargo_nextest_list<'a>(
471 req: CargoNextestListRequest<'a>,
472) -> anyhow::Result<BTreeMap<String, RustSuite>> {
473 let CargoNextestListRequest {
474 repo_root,
475 target,
476 filter,
477 release,
478 include_ignored,
479 } = req;
480
481 let nextest_check = Command::new("cargo")
483 .args(["nextest", "--version"])
484 .stdout(Stdio::null())
485 .stderr(Stdio::null())
486 .status();
487 match nextest_check {
488 Ok(status) if status.success() => {}
489 _ => anyhow::bail!(
490 "cargo-nextest not found. Run 'cargo install --locked cargo-nextest' first."
491 ),
492 }
493
494 let mut cmd = Command::new("cargo");
497 cmd.stderr(Stdio::inherit());
498 cmd.current_dir(repo_root).args([
499 "nextest",
500 "list",
501 "-p",
502 "vmm_tests",
503 "--target",
504 target,
505 "--filter-expr",
506 filter,
507 "--message-format",
508 "json",
509 ]);
510 if release {
511 cmd.arg("--release");
512 }
513 if include_ignored {
514 cmd.args(["--run-ignored", "all"]);
515 }
516 let nextest_output = cmd.output().context("failed to run cargo nextest list")?;
517 anyhow::ensure!(nextest_output.status.success(), "cargo nextest list failed",);
518 let nextest_stdout = String::from_utf8(nextest_output.stdout)
519 .map_err(|e| anyhow::anyhow!("nextest output is not valid UTF-8: {}", e))?;
520
521 parse_nextest_output(&nextest_stdout)
522}
523
524fn parse_nextest_output(stdout: &str) -> anyhow::Result<BTreeMap<String, RustSuite>> {
527 let json: serde_json::Value = serde_json::from_str(stdout)
528 .map_err(|e| anyhow::anyhow!("failed to parse nextest JSON output: {}", e))?;
529
530 let mut suites = BTreeMap::new();
531
532 for (name, suite) in json
533 .get("rust-suites")
534 .and_then(|s| s.as_object())
535 .context("no rust-suites object")?
536 {
537 let binary_path = PathBuf::from(
538 suite
539 .get("binary-path")
540 .and_then(|v| v.as_str())
541 .context("no binary-path str")?,
542 );
543
544 let testcases: Vec<_> = suite
545 .get("testcases")
546 .and_then(|t| t.as_object())
547 .context("no testcases object")?
548 .iter()
549 .filter(|(_, test_info)| {
550 test_info
551 .get("filter-match")
552 .and_then(|fm| fm.get("status"))
553 .and_then(|s| s.as_str())
554 .is_some_and(|s| s == "matches")
555 })
556 .map(|(test_name, _)| test_name.to_owned())
557 .collect();
558
559 if !testcases.is_empty() {
560 suites.insert(
561 name.to_owned(),
562 RustSuite {
563 binary_path,
564 testcases,
565 },
566 );
567 }
568 }
569
570 Ok(suites)
571}
572
573fn query_test_binary_artifacts(suite: &RustSuite) -> anyhow::Result<Vec<String>> {
577 log::info!("Using test binary: {}", suite.binary_path.display());
578 log::info!("Querying artifacts for {} tests", suite.testcases.len());
579
580 let mut command = Command::new(&suite.binary_path);
581 command.arg("--list-required-artifacts");
582 command.arg("--tests-from-stdin").stdin(Stdio::piped());
583
584 let mut child = command
585 .stdout(Stdio::piped())
586 .stderr(Stdio::piped())
587 .spawn()
588 .context("failed to spawn test binary")?;
589
590 let stdin_data = suite
591 .testcases
592 .iter()
593 .map(|n| format!("{n}\n"))
594 .collect::<String>();
595 child
596 .stdin
597 .take()
598 .expect("stdin was piped")
599 .write_all(stdin_data.as_bytes())
600 .context("failed to write test names to stdin")?;
601
602 let artifact_output = child
603 .wait_with_output()
604 .context("failed to wait for test binary")?;
605 anyhow::ensure!(
606 artifact_output.status.success(),
607 "test binary failed: {}",
608 String::from_utf8_lossy(&artifact_output.stderr)
609 );
610 let artifact_stdout = String::from_utf8(artifact_output.stdout)
611 .map_err(|e| anyhow::anyhow!("test output is not valid UTF-8: {}", e))?;
612
613 let ArtifactListOutput {
614 mut required,
615 mut optional,
616 } = serde_json::from_str(&artifact_stdout)
617 .map_err(|e| anyhow::anyhow!("failed to parse test output JSON: {}", e))?;
618
619 let mut artifacts = Vec::new();
620 artifacts.append(&mut required);
621 artifacts.append(&mut optional);
622 Ok(artifacts)
623}
624
625#[derive(clap::ValueEnum, Copy, Clone)]
626pub(crate) enum VmmTestTargetCli {
627 WindowsAarch64,
629 WindowsX64,
631 LinuxX64,
633 LinuxAarch64Musl,
635}
636
637pub(crate) fn resolve_target(
639 target: Option<VmmTestTargetCli>,
640 backend_hint: PipelineBackendHint,
641) -> anyhow::Result<CommonTriple> {
642 let target = if let Some(t) = target {
643 t
644 } else {
645 match (
646 FlowArch::host(backend_hint),
647 FlowPlatform::host(backend_hint),
648 ) {
649 (FlowArch::Aarch64, FlowPlatform::Windows) => VmmTestTargetCli::WindowsAarch64,
650 (FlowArch::X86_64, FlowPlatform::Windows) => VmmTestTargetCli::WindowsX64,
651 (FlowArch::X86_64, FlowPlatform::Linux(_)) => VmmTestTargetCli::LinuxX64,
652 _ => anyhow::bail!("unsupported host"),
653 }
654 };
655
656 Ok(match target {
657 VmmTestTargetCli::WindowsAarch64 => CommonTriple::AARCH64_WINDOWS_MSVC,
658 VmmTestTargetCli::WindowsX64 => CommonTriple::X86_64_WINDOWS_MSVC,
659 VmmTestTargetCli::LinuxX64 => CommonTriple::X86_64_LINUX_GNU,
660 VmmTestTargetCli::LinuxAarch64Musl => CommonTriple::AARCH64_LINUX_MUSL,
661 })
662}
663
664fn validate_output_dir(
674 dir: Option<&Path>,
675 target_os: target_lexicon::OperatingSystem,
676 needs_windows_disk: bool,
677) -> anyhow::Result<()> {
678 if needs_windows_disk
679 && flowey_cli::running_in_wsl()
680 && matches!(target_os, target_lexicon::OperatingSystem::Windows)
681 {
682 if let Some(dir) = dir {
683 if !flowey_cli::is_wsl_windows_path(dir) {
684 anyhow::bail!(
685 "When targeting Windows from WSL, --dir must be a path on Windows \
686 (i.e., on a DrvFs mount like /mnt/c/vmm_tests) because the selected \
687 tests use disk images that require a Windows filesystem (Hyper-V \
688 disks, or VHDX / dynamic VHD1 images). \
689 Got: {}",
690 dir.display()
691 );
692 }
693 } else {
694 anyhow::bail!(
695 "The selected tests use disk images that require a Windows filesystem \
696 (Hyper-V disks, or VHDX / dynamic VHD1 images) when targeting Windows \
697 from WSL. Specify an output directory on a DrvFs mount with --dir \
698 (e.g., --dir /mnt/c/vmm_tests)."
699 )
700 }
701 }
702 Ok(())
703}
704
705fn selections_from_resolved(
707 filter: String,
708 resolved: ResolvedArtifactSelections,
709 target_os: target_lexicon::OperatingSystem,
710) -> VmmTestSelections {
711 VmmTestSelections {
712 filter,
713 downloaded_artifacts: resolved.downloads.into_iter().collect(),
714 build: resolved.build.clone(),
715 external_deps: match target_os {
716 target_lexicon::OperatingSystem::Windows => {
717 VmmTestsExternalDeps::Windows(VmmTestsExternalDepsWindows {
718 hyperv: resolved.needs_hyperv,
719 whp: resolved.build.openvmm,
720 hardware_isolation: resolved.needs_hardware_isolation,
721 })
722 }
723 target_lexicon::OperatingSystem::Linux => {
724 VmmTestsExternalDeps::Linux(VmmTestsExternalDepsLinux {
725 hugetlb_2mb_overcommit_pages: None, prepare_vhost_vsock: false, })
728 }
729 _ => unreachable!(),
730 },
731 needs_release_igvm: resolved.needs_release_igvm,
732 }
733}
734
735impl ResolvedArtifactSelections {
736 fn resolve_artifact(&mut self, id: &str) -> anyhow::Result<()> {
738 match id {
739 petri_artifacts_vmm_test::artifacts::OPENVMM_WIN_X64::GLOBAL_UNIQUE_ID
741 | petri_artifacts_vmm_test::artifacts::OPENVMM_LINUX_X64::GLOBAL_UNIQUE_ID
742 | petri_artifacts_vmm_test::artifacts::OPENVMM_WIN_AARCH64::GLOBAL_UNIQUE_ID
743 | petri_artifacts_vmm_test::artifacts::OPENVMM_LINUX_AARCH64::GLOBAL_UNIQUE_ID
744 | petri_artifacts_vmm_test::artifacts::OPENVMM_MACOS_AARCH64::GLOBAL_UNIQUE_ID => {
745 self.build.openvmm = true;
746 }
747
748 petri_artifacts_vmm_test::artifacts::OPENVMM_VHOST_LINUX_X64::GLOBAL_UNIQUE_ID
750 | petri_artifacts_vmm_test::artifacts::OPENVMM_VHOST_LINUX_AARCH64 ::GLOBAL_UNIQUE_ID => {
751 self.build.openvmm_vhost = true;
752 }
753
754 petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_STANDARD_X64::GLOBAL_UNIQUE_ID
756 | petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_STANDARD_AARCH64::GLOBAL_UNIQUE_ID =>
757 {
758 self.build.openhcl_standard = true;
759 }
760 petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_X64::GLOBAL_UNIQUE_ID
761 | petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_AARCH64::GLOBAL_UNIQUE_ID => {
762 self.build.openhcl_standard_dev = true;
763 }
764 petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_CVM_X64::GLOBAL_UNIQUE_ID
765 =>
766 {
767 self.build.openhcl_cvm = true;
768 }
769 petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_LINUX_DIRECT_TEST_X64::GLOBAL_UNIQUE_ID =>
770 {
771 self.build.openhcl_linux_direct = true;
772 }
773
774 petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_RELEASE_STANDARD_X64::GLOBAL_UNIQUE_ID
776 | petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_RELEASE_LINUX_DIRECT_X64::GLOBAL_UNIQUE_ID
777 | petri_artifacts_vmm_test::artifacts::openhcl_igvm::LATEST_RELEASE_STANDARD_AARCH64::GLOBAL_UNIQUE_ID =>
778 {
779 self.needs_release_igvm = true;
781 }
782
783 petri_artifacts_vmm_test::artifacts::test_vhd::GUEST_TEST_UEFI_X64::GLOBAL_UNIQUE_ID
785 | petri_artifacts_vmm_test::artifacts::test_vhd::GUEST_TEST_UEFI_AARCH64 ::GLOBAL_UNIQUE_ID => {
786 self.build.guest_test_uefi = true;
787 }
788
789 petri_artifacts_vmm_test::artifacts::tmks::SIMPLE_TMK_X64::GLOBAL_UNIQUE_ID
791 | petri_artifacts_vmm_test::artifacts::tmks::SIMPLE_TMK_AARCH64 ::GLOBAL_UNIQUE_ID => {
792 self.build.tmks = true;
793 }
794
795 petri_artifacts_vmm_test::artifacts::tmks::TMK_VMM_WIN_X64::GLOBAL_UNIQUE_ID
797 | petri_artifacts_vmm_test::artifacts::tmks::TMK_VMM_WIN_AARCH64::GLOBAL_UNIQUE_ID => {
798 self.build.tmk_vmm_windows = true;
799 }
800 petri_artifacts_vmm_test::artifacts::tmks::TMK_VMM_LINUX_X64::GLOBAL_UNIQUE_ID
801 | petri_artifacts_vmm_test::artifacts::tmks::TMK_VMM_LINUX_AARCH64::GLOBAL_UNIQUE_ID
802 | petri_artifacts_vmm_test::artifacts::tmks::TMK_VMM_LINUX_X64_MUSL::GLOBAL_UNIQUE_ID
803 | petri_artifacts_vmm_test::artifacts::tmks::TMK_VMM_LINUX_AARCH64_MUSL::GLOBAL_UNIQUE_ID
804 | petri_artifacts_vmm_test::artifacts::tmks::TMK_VMM_MACOS_AARCH64::GLOBAL_UNIQUE_ID => {
805 self.build.tmk_vmm_linux = true;
806 }
807
808 petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_WIN_X64::GLOBAL_UNIQUE_ID
810 | petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_WIN_AARCH64::GLOBAL_UNIQUE_ID
811 | petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_LINUX_X64::GLOBAL_UNIQUE_ID
812 | petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_LINUX_AARCH64::GLOBAL_UNIQUE_ID
813 | petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_MACOS_AARCH64::GLOBAL_UNIQUE_ID => {
814 self.build.vmgstool = true;
815 }
816
817 petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_DEV_WIN_X64::GLOBAL_UNIQUE_ID
819 | petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_DEV_WIN_AARCH64::GLOBAL_UNIQUE_ID
820 | petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_DEV_LINUX_X64::GLOBAL_UNIQUE_ID
821 | petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_DEV_LINUX_AARCH64::GLOBAL_UNIQUE_ID
822 | petri_artifacts_vmm_test::artifacts::vmgstool::VMGSTOOL_DEV_MACOS_AARCH64::GLOBAL_UNIQUE_ID => {
823 self.build.vmgstool_dev = true;
824 }
825
826 petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_WINDOWS_X64::GLOBAL_UNIQUE_ID => {
828 self.build.tpm_guest_tests_windows = true;
829 }
830 petri_artifacts_vmm_test::artifacts::guest_tools::TPM_GUEST_TESTS_LINUX_X64::GLOBAL_UNIQUE_ID => {
831 self.build.tpm_guest_tests_linux = true;
832 }
833
834 petri_artifacts_vmm_test::artifacts::host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64::GLOBAL_UNIQUE_ID =>
836 {
837 self.build.test_igvm_agent_rpc_server = true;
838 }
839
840 petri_artifacts_vmm_test::artifacts::loadable::LINUX_DIRECT_TEST_KERNEL_X64::GLOBAL_UNIQUE_ID
842 | petri_artifacts_vmm_test::artifacts::loadable::LINUX_DIRECT_TEST_INITRD_X64::GLOBAL_UNIQUE_ID
843 | petri_artifacts_vmm_test::artifacts::loadable::LINUX_DIRECT_TEST_BZIMAGE_X64::GLOBAL_UNIQUE_ID
844 | petri_artifacts_vmm_test::artifacts::loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64::GLOBAL_UNIQUE_ID
845 | petri_artifacts_vmm_test::artifacts::loadable::LINUX_DIRECT_TEST_INITRD_AARCH64::GLOBAL_UNIQUE_ID
846 | petri_artifacts_vmm_test::artifacts::loadable::PCAT_FIRMWARE_X64::GLOBAL_UNIQUE_ID
847 | petri_artifacts_vmm_test::artifacts::loadable::SVGA_FIRMWARE_X64::GLOBAL_UNIQUE_ID
848 | petri_artifacts_vmm_test::artifacts::loadable::UEFI_FIRMWARE_X64::GLOBAL_UNIQUE_ID
849 | petri_artifacts_vmm_test::artifacts::loadable::UEFI_FIRMWARE_AARCH64::GLOBAL_UNIQUE_ID => {
850 }
852
853 petri_artifacts_vmm_test::artifacts::test_vhd::GEN1_WINDOWS_DATA_CENTER_CORE2022_X64::GLOBAL_UNIQUE_ID =>
855 {
856 self.downloads
857 .insert(KnownTestArtifacts::Gen1WindowsDataCenterCore2022X64Vhd);
858 }
859 petri_artifacts_vmm_test::artifacts::test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2022_X64::GLOBAL_UNIQUE_ID =>
860 {
861 self.downloads
862 .insert(KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd);
863 }
864 petri_artifacts_vmm_test::artifacts::test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2025_X64::GLOBAL_UNIQUE_ID =>
865 {
866 self.downloads
867 .insert(KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd);
868 }
869 petri_artifacts_vmm_test::artifacts::test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2025_X64_PREPPED::GLOBAL_UNIQUE_ID =>
870 {
871 self.build.openvmm = true;
872 self.build.prep_steps_standard = true;
873 self.force_downloads
876 .insert(KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd);
877 self.force_downloads
878 .insert(KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd);
879 }
880 petri_artifacts_vmm_test::artifacts::test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2022_X64_NO_VMBUS_PREPPED::GLOBAL_UNIQUE_ID =>
881 {
882 self.build.openvmm = true;
883 self.build.prep_steps_no_vmbus = true;
884 self.force_downloads
885 .insert(KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd);
886 }
887 petri_artifacts_vmm_test::artifacts::test_vhd::FREE_BSD_13_2_X64::GLOBAL_UNIQUE_ID => {
888 self.downloads.insert(KnownTestArtifacts::FreeBsd13_2X64Vhd);
889 }
890 petri_artifacts_vmm_test::artifacts::test_vhd::ALPINE_3_23_X64::GLOBAL_UNIQUE_ID => {
891 self.downloads.insert(KnownTestArtifacts::Alpine323X64Vhd);
892 }
893 petri_artifacts_vmm_test::artifacts::test_vhd::ALPINE_3_23_AARCH64::GLOBAL_UNIQUE_ID => {
894 self.downloads
895 .insert(KnownTestArtifacts::Alpine323Aarch64Vhd);
896 }
897 petri_artifacts_vmm_test::artifacts::test_vhd::UBUNTU_2404_SERVER_X64::GLOBAL_UNIQUE_ID => {
898 self.downloads
899 .insert(KnownTestArtifacts::Ubuntu2404ServerX64Vhd);
900 }
901 petri_artifacts_vmm_test::artifacts::test_vhd::UBUNTU_2504_SERVER_X64::GLOBAL_UNIQUE_ID => {
902 self.downloads
903 .insert(KnownTestArtifacts::Ubuntu2504ServerX64Vhd);
904 }
905 petri_artifacts_vmm_test::artifacts::test_vhd::UBUNTU_2404_SERVER_AARCH64::GLOBAL_UNIQUE_ID => {
906 self.downloads
907 .insert(KnownTestArtifacts::Ubuntu2404ServerAarch64Vhd);
908 }
909 petri_artifacts_vmm_test::artifacts::test_vhd::WINDOWS_11_ENTERPRISE_AARCH64::GLOBAL_UNIQUE_ID => {
910 self.downloads
911 .insert(KnownTestArtifacts::Windows11EnterpriseAarch64Vhdx);
912 }
913
914 petri_artifacts_vmm_test::artifacts::test_iso::FREE_BSD_13_2_X64::GLOBAL_UNIQUE_ID => {
916 self.downloads.insert(KnownTestArtifacts::FreeBsd13_2X64Iso);
917 }
918
919 petri_artifacts_vmm_test::artifacts::test_vmgs::VMGS_WITH_BOOT_ENTRY::GLOBAL_UNIQUE_ID => {
921 self.downloads.insert(KnownTestArtifacts::VmgsWithBootEntry);
922 }
923 petri_artifacts_vmm_test::artifacts::test_vmgs::VMGS_WITH_16K_TPM::GLOBAL_UNIQUE_ID => {
924 self.downloads.insert(KnownTestArtifacts::VmgsWith16kTpm);
925 }
926
927 petri_artifacts_vmm_test::artifacts::openhcl_igvm::um_bin::LATEST_LINUX_DIRECT_TEST_X64::GLOBAL_UNIQUE_ID
929 | petri_artifacts_vmm_test::artifacts::openhcl_igvm::um_dbg::LATEST_LINUX_DIRECT_TEST_X64::GLOBAL_UNIQUE_ID =>
930 {
931 self.build.openhcl_linux_direct = true;
932 }
933
934 petri_artifacts_common::artifacts::TEST_LOG_DIRECTORY::GLOBAL_UNIQUE_ID => {}
936
937 petri_artifacts_vmm_test::artifacts::virtio_win::VIRTIO_WIN_DRIVERS::GLOBAL_UNIQUE_ID => {}
939
940 petri_artifacts_common::artifacts::PIPETTE_LINUX_X64::GLOBAL_UNIQUE_ID
942 | petri_artifacts_common::artifacts::PIPETTE_LINUX_AARCH64::GLOBAL_UNIQUE_ID => {
943 self.build.pipette_linux = true;
944 }
945 petri_artifacts_common::artifacts::PIPETTE_WINDOWS_X64::GLOBAL_UNIQUE_ID
946 | petri_artifacts_common::artifacts::PIPETTE_WINDOWS_AARCH64::GLOBAL_UNIQUE_ID => {
947 self.build.pipette_windows = true;
948 }
949
950 _ => anyhow::bail!("unknown artifact: {id}"),
951 };
952 Ok(())
953 }
954}
955
956pub(crate) fn resolve_incubator(
959 incubator: Option<PathBuf>,
960 target: &CommonTriple,
961) -> anyhow::Result<IncubatorProfileNameOrPath> {
962 Ok(match incubator {
963 Some(path) if path.components().count() == 1 && path.extension().is_none() => {
965 IncubatorProfileNameOrPath::Name(path.to_string_lossy().to_string())
966 }
967 Some(path) => IncubatorProfileNameOrPath::Path(path),
968 None => IncubatorProfileNameOrPath::Name(
969 flowey_lib_hvlite::build_incubator::default_incubator_profile(target)
970 .ok_or_else(|| {
971 anyhow::anyhow!(
972 "no default incubator profile for target {}; \
973 pass an explicit path with --incubator <PATH>",
974 target.as_triple().to_string()
975 )
976 })?
977 .into(),
978 ),
979 })
980}