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)]
140 pub enable_product_policy: bool,
141 #[clap(long)]
150 pub confidential_debug: bool,
151
152 #[clap(long)]
154 pub custom_openvmm_hcl: Option<PathBuf>,
155
156 #[clap(long)]
158 pub custom_openhcl_boot: Option<PathBuf>,
159
160 #[clap(long)]
162 pub custom_uefi: Option<PathBuf>,
163
164 #[clap(long)]
167 pub custom_kernel: Option<PathBuf>,
168
169 #[clap(long, requires = "custom_kernel")]
172 pub custom_kernel_modules: Option<PathBuf>,
173
174 #[clap(long)]
179 pub custom_vtl0_kernel: Option<PathBuf>,
180
181 #[clap(long)]
183 pub custom_layer: Vec<PathBuf>,
184
185 #[clap(long)]
187 pub custom_directory: Vec<PathBuf>,
188
189 #[clap(long)]
191 pub custom_extra_rootfs: Vec<PathBuf>,
192
193 #[clap(long)]
195 pub with_sidecar: bool,
196
197 #[clap(long, requires = "with_sidecar")]
200 pub custom_sidecar: Option<PathBuf>,
201
202 #[clap(long)]
205 pub max_trace_level: Option<MaxTraceLevelCli>,
206
207 #[clap(long, requires_all = ["custom_openvmm_deps", "custom_protoc", "custom_kernel", "custom_kernel_modules", "custom_uefi"])]
210 pub use_local_deps: bool,
211
212 #[clap(long)]
214 pub custom_openvmm_deps: Option<PathBuf>,
215
216 #[clap(long)]
218 pub custom_protoc: Option<PathBuf>,
219}
220
221#[derive(clap::ValueEnum, Copy, Clone, PartialEq, Eq, Debug)]
222pub enum KernelPackageKindCli {
223 Main,
225 Cvm,
227 Dev,
229 CvmDev,
231}
232
233#[derive(clap::ValueEnum, Copy, Clone, PartialEq, Eq, Debug)]
234pub enum MaxTraceLevelCli {
235 Trace,
237 Debug,
239 Info,
241 Warn,
243 Error,
245 Off,
247}
248
249impl From<MaxTraceLevelCli> for MaxTraceLevel {
250 fn from(cli: MaxTraceLevelCli) -> Self {
251 match cli {
252 MaxTraceLevelCli::Trace => MaxTraceLevel::Trace,
253 MaxTraceLevelCli::Debug => MaxTraceLevel::Debug,
254 MaxTraceLevelCli::Info => MaxTraceLevel::Info,
255 MaxTraceLevelCli::Warn => MaxTraceLevel::Warn,
256 MaxTraceLevelCli::Error => MaxTraceLevel::Error,
257 MaxTraceLevelCli::Off => MaxTraceLevel::Off,
258 }
259 }
260}
261
262pub fn bail_if_running_in_ci() -> anyhow::Result<()> {
263 const OVERRIDE_ENV: &str = "I_HAVE_A_GOOD_REASON_TO_RUN_BUILD_IGVM_IN_CI";
264
265 if std::env::var(OVERRIDE_ENV).is_ok() {
266 return Ok(());
267 }
268
269 for ci_env in ["TF_BUILD", "GITHUB_ACTIONS"] {
270 if std::env::var(ci_env).is_ok() {
271 log::warn!("Detected that {ci_env} is set");
272 log::warn!("");
273 log::warn!("Do not use `build-igvm` in CI scripts!");
274 log::warn!(
275 "This is a local-only, inner-dev-loop tool to build IGVM files, with an UNSTABLE CLI."
276 );
277 log::warn!("");
278 log::warn!(
279 "Automated pipelines should use the underlying `flowey` nodes that power build-igvm directly, _without_ relying on its CLI!"
280 );
281 log::warn!("");
282 log::warn!(
283 "If you _really_ know what you're doing, you can set {OVERRIDE_ENV} to disable this error."
284 );
285 anyhow::bail!("attempted to run `build-igvm` in CI")
286 }
287 }
288
289 Ok(())
290}
291
292impl IntoPipeline for BuildIgvmCli {
293 fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
294 if !matches!(backend_hint, PipelineBackendHint::Local) {
295 anyhow::bail!("build-igvm is for local use only")
296 }
297
298 bail_if_running_in_ci()?;
299
300 let openvmm_repo = flowey_lib_common::git_checkout::RepoSource::ExistingClone(
301 ReadVar::from_static(crate::repo_root()),
302 );
303
304 let Self {
305 recipe,
306 release,
307 release_cfg,
308 verbose,
309 locked,
310 install_missing_deps,
311 customizations:
312 BuildIgvmCliCustomizations {
313 build_label,
314 override_kernel_pkg,
315 override_openvmm_hcl_feature,
316 override_arch,
317 override_manifest,
318 with_perf_tools,
319 with_debuginfo,
320 with_mi_secure,
321 disable_secure_avic,
322 enable_product_policy,
323 confidential_debug,
324 custom_openvmm_hcl,
325 custom_openhcl_boot,
326 custom_uefi,
327 custom_kernel,
328 custom_kernel_modules,
329 custom_vtl0_kernel,
330 custom_layer,
331 custom_directory,
332 with_sidecar,
333 custom_sidecar,
334 mut custom_extra_rootfs,
335 max_trace_level,
336 custom_openvmm_deps,
337 custom_protoc,
338 use_local_deps: _, },
340 } = self;
341
342 if with_perf_tools {
343 custom_extra_rootfs.push(crate::repo_root().join("openhcl/perftoolsfs.config"));
344 }
345
346 let mut pipeline = Pipeline::new();
347
348 let (pub_out_dir, _) = pipeline.new_artifact("build-igvm");
349
350 let recipe_arch = match recipe {
352 OpenhclRecipeCli::X64
353 | OpenhclRecipeCli::X64Devkern
354 | OpenhclRecipeCli::X64Cvm
355 | OpenhclRecipeCli::X64CvmDevkern
356 | OpenhclRecipeCli::X64TestLinuxDirect
357 | OpenhclRecipeCli::X64TestLinuxDirectDevkern => CommonArch::X86_64,
358 OpenhclRecipeCli::Aarch64 | OpenhclRecipeCli::Aarch64Devkern => CommonArch::Aarch64,
359 };
360
361 let effective_arch = override_arch.map(CommonArch::from).unwrap_or(recipe_arch);
363
364 let mut job = pipeline.new_job(
365 FlowPlatform::host(backend_hint),
366 FlowArch::host(backend_hint),
367 "build-igvm",
368 );
369
370 job = job.dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request::Init);
373
374 if let Some(openvmm_deps_path) = custom_openvmm_deps {
376 job = job.dep_on(move |_| {
377 flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalOpenvmmDeps(
378 effective_arch,
379 ReadVar::from_static(openvmm_deps_path),
380 )
381 });
382 }
383
384 if let Some(protoc_path) = custom_protoc {
386 job = job.dep_on(move |_| {
387 flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalProtoc(ReadVar::from_static(
388 protoc_path,
389 ))
390 });
391 }
392
393 if let (Some(kernel_path), Some(modules_path)) =
395 (custom_kernel.clone(), custom_kernel_modules.clone())
396 {
397 job =
398 job.dep_on(
399 move |_| flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalKernel {
400 arch: effective_arch,
401 kernel: ReadVar::from_static(kernel_path),
402 modules: ReadVar::from_static(modules_path),
403 },
404 );
405 }
406
407 if let Some(uefi_path) = custom_uefi {
409 job = job.dep_on(move |_| {
410 flowey_lib_hvlite::_jobs::cfg_versions::Request::LocalUefi(
411 effective_arch,
412 ReadVar::from_static(uefi_path),
413 )
414 });
415 }
416
417 job.dep_on(
418 |_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
419 hvlite_repo_source: openvmm_repo,
420 },
421 )
422 .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_common::Params {
423 local_only: Some(flowey_lib_hvlite::_jobs::cfg_common::LocalOnlyParams {
424 interactive: true,
425 auto_install: install_missing_deps,
426 ignore_rust_version: true,
427 }),
428 verbose: ReadVar::from_static(verbose),
429 locked,
430 deny_warnings: false,
431 no_incremental: false,
432 })
433 .dep_on(|ctx| flowey_lib_hvlite::_jobs::local_build_igvm::Params {
434 artifact_dir: ctx.publish_artifact(pub_out_dir),
435 done: ctx.new_done_handle(),
436
437 base_recipe: match recipe {
438 OpenhclRecipeCli::X64 => OpenhclIgvmRecipe::X64,
439 OpenhclRecipeCli::X64Devkern => OpenhclIgvmRecipe::X64Devkern,
440 OpenhclRecipeCli::X64TestLinuxDirect => OpenhclIgvmRecipe::X64TestLinuxDirect,
441 OpenhclRecipeCli::X64TestLinuxDirectDevkern => {
442 OpenhclIgvmRecipe::X64TestLinuxDirectDevkern
443 }
444 OpenhclRecipeCli::X64Cvm => OpenhclIgvmRecipe::X64Cvm,
445 OpenhclRecipeCli::X64CvmDevkern => OpenhclIgvmRecipe::X64CvmDevkern,
446 OpenhclRecipeCli::Aarch64 => OpenhclIgvmRecipe::Aarch64,
447 OpenhclRecipeCli::Aarch64Devkern => OpenhclIgvmRecipe::Aarch64Devkern,
448 },
449 release,
450 release_cfg,
451
452 customizations: flowey_lib_hvlite::_jobs::local_build_igvm::Customizations {
453 build_label,
454 override_arch: override_arch.map(CommonArch::from),
455 with_perf_tools,
456 with_debuginfo,
457 with_mi_secure,
458 disable_secure_avic,
459 enable_product_policy,
460 confidential_debug,
461 override_kernel_pkg: override_kernel_pkg.map(|p| match p {
462 KernelPackageKindCli::Main => OpenhclKernelPackage::Main,
463 KernelPackageKindCli::Cvm => OpenhclKernelPackage::Cvm,
464 KernelPackageKindCli::Dev => OpenhclKernelPackage::Dev,
465 KernelPackageKindCli::CvmDev => OpenhclKernelPackage::CvmDev,
466 }),
467 with_sidecar,
468 custom_extra_rootfs,
469 override_openvmm_hcl_feature,
470 custom_sidecar,
471 override_manifest,
472 override_max_trace_level: max_trace_level.map(Into::into),
473 custom_openvmm_hcl,
474 custom_openhcl_boot,
475 custom_kernel,
476 custom_vtl0_kernel,
477 custom_layer,
478 custom_directory,
479 },
480 })
481 .finish();
482
483 Ok(pipeline)
484 }
485}