1use crate::pipelines_shared::cfg_common_params::CommonArchCli;
7use flowey::node::prelude::ReadVar;
8use flowey::pipeline::prelude::*;
9use flowey_lib_hvlite::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
10use flowey_lib_hvlite::build_openhcl_igvm_from_recipe::OpenhclKernelPackage;
11use flowey_lib_hvlite::build_openvmm_hcl::MaxTraceLevel;
12use flowey_lib_hvlite::common::CommonArch;
13use std::path::PathBuf;
14
15#[derive(clap::ValueEnum, Copy, Clone)]
16pub enum OpenhclRecipeCli {
17 Aarch64,
19 Aarch64Devkern,
21 X64Cvm,
23 X64CvmDevkern,
25 X64TestLinuxDirect,
28 X64TestLinuxDirectDevkern,
31 X64,
33 X64Devkern,
35}
36
37#[derive(clap::Args)]
39pub struct BuildIgvmCli<Recipe = OpenhclRecipeCli>
40where
41 Recipe: clap::ValueEnum + Clone + Send + Sync + 'static,
44{
45 pub recipe: Recipe,
54
55 #[clap(long)]
60 pub release: bool,
61
62 #[clap(long)]
65 pub release_cfg: bool,
66
67 #[clap(long)]
69 pub verbose: bool,
70
71 #[clap(long)]
73 pub locked: bool,
74
75 #[clap(long)]
77 pub install_missing_deps: bool,
78
79 #[clap(flatten)]
80 pub customizations: BuildIgvmCliCustomizations,
81}
82
83#[derive(clap::Args)]
84#[clap(next_help_heading = "Customizations")]
85pub struct BuildIgvmCliCustomizations {
86 #[clap(long, short = 'o')]
90 pub build_label: Option<String>,
91
92 #[clap(long)]
94 pub override_kernel_pkg: Option<KernelPackageKindCli>,
95
96 #[clap(long)]
98 pub override_openvmm_hcl_feature: Vec<String>,
99
100 #[clap(long)]
103 pub override_arch: Option<CommonArchCli>,
104
105 #[clap(long)]
108 pub override_manifest: Option<PathBuf>,
109
110 #[clap(long, requires = "release")]
116 pub with_perf_tools: bool,
117
118 #[clap(long)]
123 pub with_debuginfo: bool,
124
125 #[clap(long)]
129 pub with_mi_secure: bool,
130
131 #[clap(long)]
135 pub disable_secure_avic: bool,
136
137 #[clap(long)]
139 pub custom_openvmm_hcl: Option<PathBuf>,
140
141 #[clap(long)]
143 pub custom_openhcl_boot: Option<PathBuf>,
144
145 #[clap(long)]
147 pub custom_uefi: Option<PathBuf>,
148
149 #[clap(long)]
152 pub custom_kernel: Option<PathBuf>,
153
154 #[clap(long, requires = "custom_kernel")]
157 pub custom_kernel_modules: Option<PathBuf>,
158
159 #[clap(long)]
164 pub custom_vtl0_kernel: Option<PathBuf>,
165
166 #[clap(long)]
168 pub custom_layer: Vec<PathBuf>,
169
170 #[clap(long)]
172 pub custom_directory: Vec<PathBuf>,
173
174 #[clap(long)]
176 pub custom_extra_rootfs: Vec<PathBuf>,
177
178 #[clap(long)]
180 pub with_sidecar: bool,
181
182 #[clap(long, requires = "with_sidecar")]
185 pub custom_sidecar: Option<PathBuf>,
186
187 #[clap(long)]
190 pub max_trace_level: Option<MaxTraceLevelCli>,
191
192 #[clap(long, requires_all = ["custom_openvmm_deps", "custom_protoc", "custom_kernel", "custom_kernel_modules", "custom_uefi"])]
195 pub use_local_deps: bool,
196
197 #[clap(long)]
199 pub custom_openvmm_deps: Option<PathBuf>,
200
201 #[clap(long)]
203 pub custom_protoc: Option<PathBuf>,
204}
205
206#[derive(clap::ValueEnum, Copy, Clone, PartialEq, Eq, Debug)]
207pub enum KernelPackageKindCli {
208 Main,
210 Cvm,
212 Dev,
214 CvmDev,
216}
217
218#[derive(clap::ValueEnum, Copy, Clone, PartialEq, Eq, Debug)]
219pub enum MaxTraceLevelCli {
220 Trace,
222 Debug,
224 Info,
226 Warn,
228 Error,
230 Off,
232}
233
234impl From<MaxTraceLevelCli> for MaxTraceLevel {
235 fn from(cli: MaxTraceLevelCli) -> Self {
236 match cli {
237 MaxTraceLevelCli::Trace => MaxTraceLevel::Trace,
238 MaxTraceLevelCli::Debug => MaxTraceLevel::Debug,
239 MaxTraceLevelCli::Info => MaxTraceLevel::Info,
240 MaxTraceLevelCli::Warn => MaxTraceLevel::Warn,
241 MaxTraceLevelCli::Error => MaxTraceLevel::Error,
242 MaxTraceLevelCli::Off => MaxTraceLevel::Off,
243 }
244 }
245}
246
247pub fn bail_if_running_in_ci() -> anyhow::Result<()> {
248 const OVERRIDE_ENV: &str = "I_HAVE_A_GOOD_REASON_TO_RUN_BUILD_IGVM_IN_CI";
249
250 if std::env::var(OVERRIDE_ENV).is_ok() {
251 return Ok(());
252 }
253
254 for ci_env in ["TF_BUILD", "GITHUB_ACTIONS"] {
255 if std::env::var(ci_env).is_ok() {
256 log::warn!("Detected that {ci_env} is set");
257 log::warn!("");
258 log::warn!("Do not use `build-igvm` in CI scripts!");
259 log::warn!(
260 "This is a local-only, inner-dev-loop tool to build IGVM files, with an UNSTABLE CLI."
261 );
262 log::warn!("");
263 log::warn!(
264 "Automated pipelines should use the underlying `flowey` nodes that power build-igvm directly, _without_ relying on its CLI!"
265 );
266 log::warn!("");
267 log::warn!(
268 "If you _really_ know what you're doing, you can set {OVERRIDE_ENV} to disable this error."
269 );
270 anyhow::bail!("attempted to run `build-igvm` in CI")
271 }
272 }
273
274 Ok(())
275}
276
277impl IntoPipeline for BuildIgvmCli {
278 fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
279 if !matches!(backend_hint, PipelineBackendHint::Local) {
280 anyhow::bail!("build-igvm is for local use only")
281 }
282
283 bail_if_running_in_ci()?;
284
285 let openvmm_repo = flowey_lib_common::git_checkout::RepoSource::ExistingClone(
286 ReadVar::from_static(crate::repo_root()),
287 );
288
289 let Self {
290 recipe,
291 release,
292 release_cfg,
293 verbose,
294 locked,
295 install_missing_deps,
296 customizations:
297 BuildIgvmCliCustomizations {
298 build_label,
299 override_kernel_pkg,
300 override_openvmm_hcl_feature,
301 override_arch,
302 override_manifest,
303 with_perf_tools,
304 with_debuginfo,
305 with_mi_secure,
306 disable_secure_avic,
307 custom_openvmm_hcl,
308 custom_openhcl_boot,
309 custom_uefi,
310 custom_kernel,
311 custom_kernel_modules,
312 custom_vtl0_kernel,
313 custom_layer,
314 custom_directory,
315 with_sidecar,
316 custom_sidecar,
317 mut custom_extra_rootfs,
318 max_trace_level,
319 custom_openvmm_deps,
320 custom_protoc,
321 use_local_deps: _, },
323 } = self;
324
325 if with_perf_tools {
326 custom_extra_rootfs.push(crate::repo_root().join("openhcl/perftoolsfs.config"));
327 }
328
329 let mut pipeline = Pipeline::new();
330
331 let (pub_out_dir, _) = pipeline.new_artifact("build-igvm");
332
333 let recipe_arch = match recipe {
335 OpenhclRecipeCli::X64
336 | OpenhclRecipeCli::X64Devkern
337 | OpenhclRecipeCli::X64Cvm
338 | OpenhclRecipeCli::X64CvmDevkern
339 | OpenhclRecipeCli::X64TestLinuxDirect
340 | OpenhclRecipeCli::X64TestLinuxDirectDevkern => CommonArch::X86_64,
341 OpenhclRecipeCli::Aarch64 | OpenhclRecipeCli::Aarch64Devkern => CommonArch::Aarch64,
342 };
343
344 let effective_arch = override_arch.map(CommonArch::from).unwrap_or(recipe_arch);
346
347 let mut job = pipeline.new_job(
348 FlowPlatform::host(backend_hint),
349 FlowArch::host(backend_hint),
350 "build-igvm",
351 );
352
353 job = job.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init);
356
357 if let Some(openvmm_deps_path) = custom_openvmm_deps {
359 job = job.dep_on(move |_| {
360 flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalOpenvmmDeps(
361 effective_arch,
362 ReadVar::from_static(openvmm_deps_path),
363 )
364 });
365 }
366
367 if let Some(protoc_path) = custom_protoc {
369 job = job.dep_on(move |_| {
370 flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalProtoc(ReadVar::from_static(
371 protoc_path,
372 ))
373 });
374 }
375
376 if let (Some(kernel_path), Some(modules_path)) =
378 (custom_kernel.clone(), custom_kernel_modules.clone())
379 {
380 job =
381 job.dep_on(
382 move |_| flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalKernel {
383 arch: effective_arch,
384 kernel: ReadVar::from_static(kernel_path),
385 modules: ReadVar::from_static(modules_path),
386 },
387 );
388 }
389
390 if let Some(uefi_path) = custom_uefi {
392 job = job.dep_on(move |_| {
393 flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalUefi(
394 effective_arch,
395 ReadVar::from_static(uefi_path),
396 )
397 });
398 }
399
400 job.dep_on(
401 |_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
402 hvlite_repo_source: openvmm_repo,
403 },
404 )
405 .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_common::Params {
406 local_only: Some(flowey_lib_hvlite::_jobs::cfg_common::LocalOnlyParams {
407 interactive: true,
408 auto_install: install_missing_deps,
409 ignore_rust_version: true,
410 }),
411 verbose: ReadVar::from_static(verbose),
412 locked,
413 deny_warnings: false,
414 no_incremental: false,
415 })
416 .dep_on(|ctx| flowey_lib_hvlite::_jobs::local_build_igvm::Params {
417 artifact_dir: ctx.publish_artifact(pub_out_dir),
418 done: ctx.new_done_handle(),
419
420 base_recipe: match recipe {
421 OpenhclRecipeCli::X64 => OpenhclIgvmRecipe::X64,
422 OpenhclRecipeCli::X64Devkern => OpenhclIgvmRecipe::X64Devkern,
423 OpenhclRecipeCli::X64TestLinuxDirect => OpenhclIgvmRecipe::X64TestLinuxDirect,
424 OpenhclRecipeCli::X64TestLinuxDirectDevkern => {
425 OpenhclIgvmRecipe::X64TestLinuxDirectDevkern
426 }
427 OpenhclRecipeCli::X64Cvm => OpenhclIgvmRecipe::X64Cvm,
428 OpenhclRecipeCli::X64CvmDevkern => OpenhclIgvmRecipe::X64CvmDevkern,
429 OpenhclRecipeCli::Aarch64 => OpenhclIgvmRecipe::Aarch64,
430 OpenhclRecipeCli::Aarch64Devkern => OpenhclIgvmRecipe::Aarch64Devkern,
431 },
432 release,
433 release_cfg,
434
435 customizations: flowey_lib_hvlite::_jobs::local_build_igvm::Customizations {
436 build_label,
437 override_arch: override_arch.map(CommonArch::from),
438 with_perf_tools,
439 with_debuginfo,
440 with_mi_secure,
441 disable_secure_avic,
442 override_kernel_pkg: override_kernel_pkg.map(|p| match p {
443 KernelPackageKindCli::Main => OpenhclKernelPackage::Main,
444 KernelPackageKindCli::Cvm => OpenhclKernelPackage::Cvm,
445 KernelPackageKindCli::Dev => OpenhclKernelPackage::Dev,
446 KernelPackageKindCli::CvmDev => OpenhclKernelPackage::CvmDev,
447 }),
448 with_sidecar,
449 custom_extra_rootfs,
450 override_openvmm_hcl_feature,
451 custom_sidecar,
452 override_manifest,
453 override_max_trace_level: max_trace_level.map(Into::into),
454 custom_openvmm_hcl,
455 custom_openhcl_boot,
456 custom_kernel,
457 custom_vtl0_kernel,
458 custom_layer,
459 custom_directory,
460 },
461 })
462 .finish();
463
464 Ok(pipeline)
465 }
466}