1#![forbid(unsafe_code)]
7
8use petri_artifacts_common::tags::MachineArch;
9use petri_artifacts_core::ErasedArtifactHandle;
10use std::env::consts::EXE_EXTENSION;
11use std::path::Path;
12use std::path::PathBuf;
13use vmm_test_images::KnownTestArtifacts;
14
15pub struct OpenvmmKnownPathsTestArtifactResolver<'a>(&'a str);
19
20impl<'a> OpenvmmKnownPathsTestArtifactResolver<'a> {
21 pub fn new(test_name: &'a str) -> Self {
23 Self(test_name)
24 }
25}
26
27impl petri_artifacts_core::ResolveTestArtifact for OpenvmmKnownPathsTestArtifactResolver<'_> {
28 #[rustfmt::skip]
29 fn resolve(&self, id: ErasedArtifactHandle) -> anyhow::Result<PathBuf> {
30 use petri_artifacts_common::artifacts as common;
31 use petri_artifacts_vmm_test::artifacts::*;
32
33 match id {
34 _ if id == common::PIPETTE_WINDOWS_X64 => pipette_path(MachineArch::X86_64, PipetteFlavor::Windows),
35 _ if id == common::PIPETTE_LINUX_X64 => pipette_path(MachineArch::X86_64, PipetteFlavor::Linux),
36 _ if id == common::PIPETTE_WINDOWS_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Windows),
37 _ if id == common::PIPETTE_LINUX_AARCH64 => pipette_path(MachineArch::Aarch64, PipetteFlavor::Linux),
38
39 _ if id == common::TEST_LOG_DIRECTORY => test_log_directory_path(self.0),
40
41 _ if id == OPENVMM_NATIVE => openvmm_native_executable_path(),
42
43 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_X64 => linux_direct_x64_test_kernel_path(),
44 _ if id == loadable::LINUX_DIRECT_TEST_KERNEL_AARCH64 => linux_direct_arm_image_path(),
45 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_X64 => linux_direct_test_initrd_path(MachineArch::X86_64),
46 _ if id == loadable::LINUX_DIRECT_TEST_INITRD_AARCH64 => linux_direct_test_initrd_path(MachineArch::Aarch64),
47
48 _ if id == loadable::PCAT_FIRMWARE_X64 => pcat_firmware_path(),
49 _ if id == loadable::SVGA_FIRMWARE_X64 => svga_firmware_path(),
50 _ if id == loadable::UEFI_FIRMWARE_X64 => uefi_firmware_path(MachineArch::X86_64),
51 _ if id == loadable::UEFI_FIRMWARE_AARCH64 => uefi_firmware_path(MachineArch::Aarch64),
52
53 _ if id == openhcl_igvm::LATEST_STANDARD_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Standard),
54 _ if id == openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel),
55 _ if id == openhcl_igvm::LATEST_CVM_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Cvm),
56 _ if id == openhcl_igvm::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::LinuxDirect),
57 _ if id == openhcl_igvm::LATEST_STANDARD_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::Standard),
58 _ if id == openhcl_igvm::LATEST_STANDARD_DEV_KERNEL_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel),
59
60 _ if id == openhcl_igvm::LATEST_RELEASE_STANDARD_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Release2511, OpenhclFlavor::Standard),
61 _ if id == openhcl_igvm::LATEST_RELEASE_LINUX_DIRECT_X64 => openhcl_bin_path(MachineArch::X86_64, OpenhclVersion::Release2511, OpenhclFlavor::LinuxDirect),
62 _ if id == openhcl_igvm::LATEST_RELEASE_STANDARD_AARCH64 => openhcl_bin_path(MachineArch::Aarch64, OpenhclVersion::Release2511, OpenhclFlavor::Standard),
63
64 _ if id == openhcl_igvm::um_bin::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_extras_path(OpenhclVersion::Latest,OpenhclFlavor::LinuxDirect,OpenhclExtras::UmBin),
65 _ if id == openhcl_igvm::um_dbg::LATEST_LINUX_DIRECT_TEST_X64 => openhcl_extras_path(OpenhclVersion::Latest,OpenhclFlavor::LinuxDirect,OpenhclExtras::UmDbg),
66
67 _ if id == test_vhd::GUEST_TEST_UEFI_X64 => guest_test_uefi_disk_path(MachineArch::X86_64),
68 _ if id == test_vhd::GUEST_TEST_UEFI_AARCH64 => guest_test_uefi_disk_path(MachineArch::Aarch64),
69 _ if id == test_vhd::GEN1_WINDOWS_DATA_CENTER_CORE2022_X64 => get_test_artifact_path(KnownTestArtifacts::Gen1WindowsDataCenterCore2022X64Vhd),
70 _ if id == test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2022_X64 => get_test_artifact_path(KnownTestArtifacts::Gen2WindowsDataCenterCore2022X64Vhd),
71 _ if id == test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2025_X64 => get_test_artifact_path(KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd),
72 _ if id == test_vhd::GEN2_WINDOWS_DATA_CENTER_CORE2025_X64_PREPPED => get_prepped_test_artifact_path(KnownTestArtifacts::Gen2WindowsDataCenterCore2025X64Vhd),
73 _ if id == test_vhd::FREE_BSD_13_2_X64 => get_test_artifact_path(KnownTestArtifacts::FreeBsd13_2X64Vhd),
74 _ if id == test_vhd::ALPINE_3_23_X64 => get_test_artifact_path(KnownTestArtifacts::Alpine323X64Vhd),
75 _ if id == test_vhd::ALPINE_3_23_AARCH64 => get_test_artifact_path(KnownTestArtifacts::Alpine323Aarch64Vhd),
76 _ if id == test_vhd::UBUNTU_2404_SERVER_X64 => get_test_artifact_path(KnownTestArtifacts::Ubuntu2404ServerX64Vhd),
77 _ if id == test_vhd::UBUNTU_2504_SERVER_X64 => get_test_artifact_path(KnownTestArtifacts::Ubuntu2504ServerX64Vhd),
78 _ if id == test_vhd::UBUNTU_2404_SERVER_AARCH64 => get_test_artifact_path(KnownTestArtifacts::Ubuntu2404ServerAarch64Vhd),
79 _ if id == test_vhd::WINDOWS_11_ENTERPRISE_AARCH64 => get_test_artifact_path(KnownTestArtifacts::Windows11EnterpriseAarch64Vhdx),
80
81 _ if id == test_iso::FREE_BSD_13_2_X64 => get_test_artifact_path(KnownTestArtifacts::FreeBsd13_2X64Iso),
82
83 _ if id == test_vmgs::VMGS_WITH_BOOT_ENTRY => get_test_artifact_path(KnownTestArtifacts::VmgsWithBootEntry),
84 _ if id == test_vmgs::VMGS_WITH_16K_TPM => get_test_artifact_path(KnownTestArtifacts::VmgsWith16kTpm),
85
86 _ if id == tmks::TMK_VMM_NATIVE => tmk_vmm_native_executable_path(),
87 _ if id == tmks::TMK_VMM_LINUX_X64_MUSL => tmk_vmm_paravisor_path(MachineArch::X86_64),
88 _ if id == tmks::TMK_VMM_LINUX_AARCH64_MUSL => tmk_vmm_paravisor_path(MachineArch::Aarch64),
89 _ if id == tmks::SIMPLE_TMK_X64 => simple_tmk_path(MachineArch::X86_64),
90 _ if id == tmks::SIMPLE_TMK_AARCH64 => simple_tmk_path(MachineArch::Aarch64),
91
92 _ if id == VMGSTOOL_NATIVE => vmgstool_native_executable_path(),
93
94 _ if id == guest_tools::TPM_GUEST_TESTS_WINDOWS_X64 => {
95 tpm_guest_tests_windows_path(MachineArch::X86_64)
96 }
97 _ if id == guest_tools::TPM_GUEST_TESTS_LINUX_X64 => {
98 tpm_guest_tests_linux_path(MachineArch::X86_64)
99 }
100
101 _ if id == host_tools::TEST_IGVM_AGENT_RPC_SERVER_WINDOWS_X64 => {
102 test_igvm_agent_rpc_server_windows_path(MachineArch::X86_64)
103 }
104
105 _ => anyhow::bail!("no support for given artifact type"),
106 }
107 }
108}
109
110#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
111enum PipetteFlavor {
112 Windows,
113 Linux,
114}
115
116#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
117enum OpenhclVersion {
118 Latest,
119 Release2511,
120}
121
122#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
123enum OpenhclFlavor {
124 Standard,
125 StandardDevKernel,
126 Cvm,
127 LinuxDirect,
128}
129
130#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
131enum OpenhclExtras {
132 UmBin,
133 UmDbg,
134}
135
136fn target_arch_path(arch: MachineArch) -> &'static str {
138 match arch {
139 MachineArch::X86_64 => "x86_64",
140 MachineArch::Aarch64 => "aarch64",
141 }
142}
143
144fn windows_msvc_target(arch: MachineArch) -> &'static str {
145 match arch {
146 MachineArch::X86_64 => "x86_64-pc-windows-msvc",
147 MachineArch::Aarch64 => "aarch64-pc-windows-msvc",
148 }
149}
150
151fn get_test_artifact_path(artifact: KnownTestArtifacts) -> Result<PathBuf, anyhow::Error> {
152 let images_dir = std::env::var("VMM_TEST_IMAGES");
153 let full_path = Path::new(images_dir.as_deref().unwrap_or("images"));
154
155 get_path(
156 full_path,
157 artifact.filename(),
158 MissingCommand::Xtask {
159 xtask_args: &[
160 "guest-test",
161 "download-image",
162 "--artifacts",
163 &artifact.name(),
164 ],
165 description: "test artifact",
166 },
167 )
168}
169
170fn get_prepped_test_artifact_path(artifact: KnownTestArtifacts) -> Result<PathBuf, anyhow::Error> {
171 let images_dir = std::env::var("VMM_TEST_IMAGES");
172 let full_path = Path::new(images_dir.as_deref().unwrap_or("images"));
173 let prepped_filename = artifact.filename().replace(".vhd", "-prepped.vhd");
174
175 get_path(
176 full_path,
177 prepped_filename,
178 MissingCommand::Run {
179 description: "prepped test image",
180 package: "prep_steps",
181 },
182 )
183}
184
185fn guest_test_uefi_disk_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
187 get_path(
189 format!("target/{}-unknown-uefi/debug", target_arch_path(arch)),
190 "guest_test_uefi.img",
191 MissingCommand::Xtask {
192 xtask_args: &[
193 "guest-test",
194 "uefi",
195 &format!(
196 "--boot{}",
197 match arch {
198 MachineArch::X86_64 => "x64",
199 MachineArch::Aarch64 => "aa64",
200 }
201 ),
202 ],
203 description: "guest_test_uefi image",
204 },
205 )
206}
207
208fn pipette_path(arch: MachineArch, os_flavor: PipetteFlavor) -> anyhow::Result<PathBuf> {
210 let (target_suffixes, binary) = match os_flavor {
213 PipetteFlavor::Windows => (vec!["pc-windows-msvc", "pc-windows-gnu"], "pipette.exe"),
214 PipetteFlavor::Linux => (vec!["unknown-linux-musl"], "pipette"),
215 };
216 for (index, target_suffix) in target_suffixes.iter().enumerate() {
217 let target = format!("{}-{}", target_arch_path(arch), target_suffix);
218 match get_path(
219 format!("target/{target}/debug"),
220 binary,
221 MissingCommand::Build {
222 package: "pipette",
223 target: Some(&target),
224 },
225 ) {
226 Ok(path) => return Ok(path),
227 Err(err) => {
228 if index < target_suffixes.len() - 1 {
229 continue;
230 } else {
231 anyhow::bail!(
232 "None of the suffixes {:?} had `pipette` built, {err:?}",
233 target_suffixes
234 );
235 }
236 }
237 }
238 }
239
240 unreachable!()
241}
242
243fn openvmm_native_executable_path() -> anyhow::Result<PathBuf> {
245 get_output_executable_path("openvmm")
246}
247
248fn tmk_vmm_native_executable_path() -> anyhow::Result<PathBuf> {
250 get_output_executable_path("tmk_vmm")
251}
252
253fn vmgstool_native_executable_path() -> anyhow::Result<PathBuf> {
255 get_output_executable_path("vmgstool")
256}
257
258fn tpm_guest_tests_windows_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
260 let target = windows_msvc_target(arch);
261 get_path(
262 format!("target/{target}/debug"),
263 "tpm_guest_tests.exe",
264 MissingCommand::Build {
265 package: "tpm_guest_tests",
266 target: Some(target),
267 },
268 )
269}
270
271fn tpm_guest_tests_linux_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
272 let target = match arch {
273 MachineArch::X86_64 => "x86_64-unknown-linux-gnu",
274 MachineArch::Aarch64 => "aarch64-unknown-linux-gnu",
275 };
276
277 get_path(
278 format!("target/{target}/debug"),
279 "tpm_guest_tests",
280 MissingCommand::Build {
281 package: "tpm_guest_tests",
282 target: Some(target),
283 },
284 )
285}
286
287fn test_igvm_agent_rpc_server_windows_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
289 let target = windows_msvc_target(arch);
290 get_path(
291 format!("target/{target}/debug"),
292 "test_igvm_agent_rpc_server.exe",
293 MissingCommand::Build {
294 package: "test_igvm_agent_rpc_server",
295 target: Some(target),
296 },
297 )
298}
299
300fn tmk_vmm_paravisor_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
301 let target = match arch {
302 MachineArch::X86_64 => "x86_64-unknown-linux-musl",
303 MachineArch::Aarch64 => "aarch64-unknown-linux-musl",
304 };
305 get_path(
306 format!("target/{target}/debug"),
307 "tmk_vmm",
308 MissingCommand::Build {
309 package: "tmk_vmm",
310 target: Some(target),
311 },
312 )
313}
314
315fn simple_tmk_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
317 let arch_str = match arch {
318 MachineArch::X86_64 => "x86_64",
319 MachineArch::Aarch64 => "aarch64",
320 };
321 let target = match arch {
322 MachineArch::X86_64 => "x86_64-unknown-none",
323 MachineArch::Aarch64 => "aarch64-minimal_rt-none",
324 };
325 get_path(
326 format!("target/{target}/debug"),
327 "simple_tmk",
328 MissingCommand::Custom {
329 description: "simple_tmk",
330 cmd: &format!(
331 "RUSTC_BOOTSTRAP=1 cargo build -p simple_tmk --config openhcl/minimal_rt/{arch_str}-config.toml"
332 ),
333 },
334 )
335}
336
337fn linux_direct_x64_test_kernel_path() -> anyhow::Result<PathBuf> {
339 get_path(
340 ".packages/underhill-deps-private",
341 "x64/vmlinux",
342 MissingCommand::Restore {
343 description: "linux direct test kernel",
344 },
345 )
346}
347
348fn linux_direct_test_initrd_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
350 get_path(
351 ".packages/underhill-deps-private",
352 format!(
353 "{}/initrd",
354 match arch {
355 MachineArch::X86_64 => "x64",
356 MachineArch::Aarch64 => "aarch64",
357 }
358 ),
359 MissingCommand::Restore {
360 description: "linux direct test initrd",
361 },
362 )
363}
364
365fn linux_direct_arm_image_path() -> anyhow::Result<PathBuf> {
367 get_path(
368 ".packages/underhill-deps-private",
369 "aarch64/Image",
370 MissingCommand::Restore {
371 description: "linux direct test kernel",
372 },
373 )
374}
375
376fn pcat_firmware_path() -> anyhow::Result<PathBuf> {
378 get_path(
379 ".packages",
380 "Microsoft.Windows.VmFirmware.Pcat.amd64fre/content/vmfirmwarepcat.dll",
381 MissingCommand::Restore {
382 description: "PCAT firmware binary",
383 },
384 )
385}
386
387fn svga_firmware_path() -> anyhow::Result<PathBuf> {
389 get_path(
390 ".packages",
391 "Microsoft.Windows.VmEmulatedDevices.amd64fre/content/VmEmulatedDevices.dll",
392 MissingCommand::Restore {
393 description: "SVGA firmware binary",
394 },
395 )
396}
397
398fn uefi_firmware_path(arch: MachineArch) -> anyhow::Result<PathBuf> {
400 get_path(
401 ".packages",
402 match arch {
403 MachineArch::X86_64 => {
404 "hyperv.uefi.mscoreuefi.x64.RELEASE/MsvmX64/RELEASE_VS2022/FV/MSVM.fd"
405 }
406 MachineArch::Aarch64 => {
407 "hyperv.uefi.mscoreuefi.AARCH64.RELEASE/MsvmAARCH64/RELEASE_VS2022/FV/MSVM.fd"
408 }
409 },
410 MissingCommand::Restore {
411 description: "UEFI firmware binary",
412 },
413 )
414}
415
416fn openhcl_bin_path(
418 arch: MachineArch,
419 version: OpenhclVersion,
420 flavor: OpenhclFlavor,
421) -> anyhow::Result<PathBuf> {
422 let (path, name, cmd) = match (arch, version, flavor) {
423 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Standard) => (
424 "flowey-out/artifacts/build-igvm/debug/x64",
425 "openhcl-x64.bin",
426 MissingCommand::XFlowey {
427 description: "OpenHCL IGVM file",
428 xflowey_args: &["build-igvm", "x64"],
429 },
430 ),
431 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel) => (
432 "flowey-out/artifacts/build-igvm/debug/x64-devkern",
433 "openhcl-x64-devkern.bin",
434 MissingCommand::XFlowey {
435 description: "OpenHCL IGVM file",
436 xflowey_args: &["build-igvm", "x64-devkern"],
437 },
438 ),
439 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::Cvm) => (
440 "flowey-out/artifacts/build-igvm/debug/x64-cvm",
441 "openhcl-x64-cvm.bin",
442 MissingCommand::XFlowey {
443 description: "OpenHCL IGVM file",
444 xflowey_args: &["build-igvm", "x64-cvm"],
445 },
446 ),
447 (MachineArch::X86_64, OpenhclVersion::Latest, OpenhclFlavor::LinuxDirect) => (
448 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
449 "openhcl-x64-test-linux-direct.bin",
450 MissingCommand::XFlowey {
451 description: "OpenHCL IGVM file",
452 xflowey_args: &["build-igvm", "x64-test-linux-direct"],
453 },
454 ),
455 (MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::Standard) => (
456 "flowey-out/artifacts/build-igvm/debug/aarch64",
457 "openhcl-aarch64.bin",
458 MissingCommand::XFlowey {
459 description: "OpenHCL IGVM file",
460 xflowey_args: &["build-igvm", "aarch64"],
461 },
462 ),
463 (MachineArch::Aarch64, OpenhclVersion::Latest, OpenhclFlavor::StandardDevKernel) => (
464 "flowey-out/artifacts/build-igvm/debug/aarch64-devkern",
465 "openhcl-aarch64-devkern.bin",
466 MissingCommand::XFlowey {
467 description: "OpenHCL IGVM file",
468 xflowey_args: &["build-igvm", "aarch64-devkern"],
469 },
470 ),
471 (MachineArch::X86_64, OpenhclVersion::Release2511, OpenhclFlavor::LinuxDirect) => (
472 "flowey-out/artifacts/last-release-igvm-files",
473 "release-2511-x64-direct-openhcl.bin",
474 MissingCommand::XFlowey {
475 description: "Previous OpenHCL release IGVM file",
476 xflowey_args: &["restore-packages"],
477 },
478 ),
479 (MachineArch::X86_64, OpenhclVersion::Release2511, OpenhclFlavor::Standard) => (
480 "flowey-out/artifacts/last-release-igvm-files",
481 "release-2511-x64-openhcl.bin",
482 MissingCommand::XFlowey {
483 description: "Previous OpenHCL release IGVM file",
484 xflowey_args: &["restore-packages"],
485 },
486 ),
487 (MachineArch::Aarch64, OpenhclVersion::Release2511, OpenhclFlavor::Standard) => (
488 "flowey-out/artifacts/last-release-igvm-files",
489 "release-2511-aarch64-openhcl.bin",
490 MissingCommand::XFlowey {
491 description: "Previous OpenHCL release IGVM file",
492 xflowey_args: &["restore-packages"],
493 },
494 ),
495 _ => anyhow::bail!("no openhcl bin with given arch, version, and flavor"),
496 };
497
498 get_path(path, name, cmd)
499}
500
501fn openhcl_extras_path(
503 version: OpenhclVersion,
504 flavor: OpenhclFlavor,
505 item: OpenhclExtras,
506) -> anyhow::Result<PathBuf> {
507 if !matches!(version, OpenhclVersion::Latest) || !matches!(flavor, OpenhclFlavor::LinuxDirect) {
508 anyhow::bail!("Debug symbol path currently only available for LATEST_LINUX_DIRECT_TEST")
509 }
510
511 let (path, name) = match item {
512 OpenhclExtras::UmBin => (
513 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
514 "openvmm_hcl_msft",
515 ),
516 OpenhclExtras::UmDbg => (
517 "flowey-out/artifacts/build-igvm/debug/x64-test-linux-direct",
518 "openvmm_hcl_msft.dbg",
519 ),
520 };
521
522 get_path(
523 path,
524 name,
525 MissingCommand::XFlowey {
526 description: "OpenHCL IGVM file",
527 xflowey_args: &["build-igvm", "x64-test-linux-direct"],
528 },
529 )
530}
531
532fn test_log_directory_path(test_name: &str) -> anyhow::Result<PathBuf> {
534 let root = if let Some(path) = std::env::var_os("TEST_OUTPUT_PATH") {
535 PathBuf::from(path)
536 } else {
537 get_repo_root()?.join("vmm_test_results")
538 };
539 let path = root.join(test_name.replace("::", "__"));
542 fs_err::create_dir_all(&path)?;
543 Ok(path)
544}
545
546const VMM_TESTS_DIR_ENV_VAR: &str = "VMM_TESTS_CONTENT_DIR";
547
548pub fn get_repo_root() -> anyhow::Result<PathBuf> {
550 Ok(Path::new(env!("CARGO_MANIFEST_DIR")).join("../.."))
551}
552
553pub fn get_path(
567 search_path: impl AsRef<Path>,
568 file_name: impl AsRef<Path>,
569 missing_cmd: MissingCommand<'_>,
570) -> anyhow::Result<PathBuf> {
571 let search_path = search_path.as_ref();
572 let file_name = file_name.as_ref();
573 if file_name.is_absolute() {
574 anyhow::bail!("{} should be a relative path", file_name.display());
575 }
576
577 if let Ok(env_dir) = std::env::var(VMM_TESTS_DIR_ENV_VAR) {
578 let full_path = Path::new(&env_dir).join(file_name);
579 if full_path.try_exists()? {
580 return Ok(full_path);
581 }
582 }
583
584 let file_path = if search_path.is_absolute() {
585 search_path.to_owned()
586 } else {
587 get_repo_root()?.join(search_path)
588 };
589
590 let full_path = file_path.join(file_name);
591 if !full_path.exists() {
592 eprintln!("Failed to find {:?}.", full_path);
593 missing_cmd.to_error()?;
594 }
595
596 Ok(full_path)
597}
598
599pub fn get_output_executable_path(name: &str) -> anyhow::Result<PathBuf> {
603 let mut path: PathBuf = std::env::current_exe()?;
604 if path.parent().and_then(|x| x.file_name()).unwrap() == "deps" {
607 path.pop();
608 }
609
610 get_path(
611 path.parent().unwrap(),
612 Path::new(name).with_extension(EXE_EXTENSION),
613 MissingCommand::Build {
614 package: name,
615 target: None,
616 },
617 )
618}
619
620#[derive(Copy, Clone)]
623#[expect(missing_docs)] pub enum MissingCommand<'a> {
625 Build {
627 package: &'a str,
628 target: Option<&'a str>,
629 },
630 Run {
632 description: &'a str,
633 package: &'a str,
634 },
635 Xtask {
637 description: &'a str,
638 xtask_args: &'a [&'a str],
639 },
640 XFlowey {
642 description: &'a str,
643 xflowey_args: &'a [&'a str],
644 },
645 Restore { description: &'a str },
647 Custom { description: &'a str, cmd: &'a str },
649}
650
651impl MissingCommand<'_> {
652 fn to_error(self) -> anyhow::Result<()> {
653 match self {
654 MissingCommand::Build { package, target } => anyhow::bail!(
655 "Failed to find {package} binary. Run `cargo build {target_args}-p {package}` to build it.",
656 target_args =
657 target.map_or(String::new(), |target| format!("--target {} ", target)),
658 ),
659 MissingCommand::Run {
660 description,
661 package,
662 } => anyhow::bail!(
663 "Failed to find {}. Run `cargo run -p {}` to create it.",
664 description,
665 package
666 ),
667 MissingCommand::Xtask {
668 description,
669 xtask_args: args,
670 } => {
671 anyhow::bail!(
672 "Failed to find {}. Run `cargo xtask {}` to create it.",
673 description,
674 args.join(" ")
675 )
676 }
677 MissingCommand::XFlowey {
678 description,
679 xflowey_args: args,
680 } => anyhow::bail!(
681 "Failed to find {}. Run `cargo xflowey {}` to create it.",
682 description,
683 args.join(" ")
684 ),
685 MissingCommand::Restore { description } => {
686 anyhow::bail!(
687 "Failed to find {}. Run `cargo xflowey restore-packages`.",
688 description
689 )
690 }
691 MissingCommand::Custom { description, cmd } => {
692 anyhow::bail!(
693 "Failed to find {}. Run `{}` to create it.",
694 description,
695 cmd
696 )
697 }
698 }
699 }
700}