flowey_hvlite/pipelines/
build_igvm.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! See [`BuildIgvmCli`]
5
6use flowey::node::prelude::ReadVar;
7use flowey::pipeline::prelude::*;
8use flowey_lib_hvlite::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
9use flowey_lib_hvlite::build_openhcl_igvm_from_recipe::OpenhclKernelPackage;
10use flowey_lib_hvlite::run_cargo_build::common::CommonArch;
11use std::path::PathBuf;
12
13#[derive(clap::ValueEnum, Copy, Clone)]
14pub enum OpenhclRecipeCli {
15    /// Aarch64 OpenHCL
16    Aarch64,
17    /// Aarch64 OpenHCL, using the dev kernel in VTL2
18    Aarch64Devkern,
19    /// X64 OpenHCL, with CVM support.
20    X64Cvm,
21    /// X64 OpenHCL, with CVM support using the dev kernel in VTL2
22    X64CvmDevkern,
23    /// X64 OpenHCL booting VTL0 using a test linux-direct kernel + initrd (no
24    /// UEFI).
25    X64TestLinuxDirect,
26    /// X64 OpenHCL booting VTL0 using a test linux-direct kernel + initrd (no
27    /// UEFI), using the dev kernel in VTL2.
28    X64TestLinuxDirectDevkern,
29    /// X64 OpenHCL
30    X64,
31    /// X64 OpenHCL, using the dev kernel in VTL2
32    X64Devkern,
33}
34
35/// Build OpenHCL IGVM files for local development. DO NOT USE IN CI.
36#[derive(clap::Args)]
37pub struct BuildIgvmCli<Recipe = OpenhclRecipeCli>
38where
39    // Make the recipe generic so that out-of-tree flowey implementations can
40    // slot in a custom set of recipes to build with.
41    Recipe: clap::ValueEnum + Clone + Send + Sync + 'static,
42{
43    /// Specify which OpenHCL recipe to build / customize off-of.
44    ///
45    /// A "recipe" corresponds to the various standard IGVM SKUs that are
46    /// actively supported and tested in our build infrastructure.
47    ///
48    /// It encodes all the details of what goes into an individual IGVM file,
49    /// such as what build flags `openvmm_hcl` should be built with, what goes
50    /// into a VTL2 initrd, what `igvmfilegen` manifest is being used, etc...
51    pub recipe: Recipe,
52
53    /// Build using release variants of all constituent components.
54    ///
55    /// Uses --profile=boot-release for openhcl_boot, --profile=openhcl-ship
56    /// when building openvmm_hcl, `--min-interactive` vtl2 initrd
57    /// configuration, `-release.json` manifest variant, etc...
58    #[clap(long)]
59    pub release: bool,
60
61    /// pass `--verbose` to cargo
62    #[clap(long)]
63    pub verbose: bool,
64
65    /// pass `--locked` to cargo
66    #[clap(long)]
67    pub locked: bool,
68
69    /// Automatically install any missing required dependencies.
70    #[clap(long)]
71    pub install_missing_deps: bool,
72
73    #[clap(flatten)]
74    pub customizations: BuildIgvmCliCustomizations,
75}
76
77#[derive(clap::Args)]
78#[clap(next_help_heading = "Customizations")]
79pub struct BuildIgvmCliCustomizations {
80    /// Set a custom label for this `build-igvm` invocation. If no label is
81    /// provided, customized IGVM files will be output with the label
82    /// `{base_recipe_name}-custom`
83    #[clap(long, short = 'o')]
84    pub build_label: Option<String>,
85
86    /// Override which kernel package to use.
87    #[clap(long)]
88    pub override_kernel_pkg: Option<KernelPackageKindCli>,
89
90    /// Pass additional features when building openmm_hcl
91    #[clap(long)]
92    pub override_openvmm_hcl_feature: Vec<String>,
93
94    /// Override architecture used when building. You probably don't want this -
95    /// prefer changing the base recipe to something more appropriate.
96    #[clap(long)]
97    pub override_arch: Option<BuildIgvmArch>,
98
99    /// Override the json manifest passed to igvmfilegen, none means the
100    /// debug/release manifest from the base recipe will be used.
101    #[clap(long)]
102    pub override_manifest: Option<PathBuf>,
103
104    /// Ensure perf tools are included in the release initrd.
105    ///
106    /// Ensures that openvmm_hcl is not stripped, so that perf tools work
107    /// correctly, and requires that the file be built in `--release` mode, so
108    /// that perf numbers are more representative of production binaries.
109    #[clap(long, requires = "release")]
110    pub with_perf_tools: bool,
111
112    /// Preserve debuginfo in the openvmm_hcl binary in the IGVM file.
113    ///
114    /// This increases the VTL2 memory requirements significantly, and will
115    /// likely require passing a `--override-manifest` to compensate.
116    #[clap(long)]
117    pub with_debuginfo: bool,
118
119    /// Path to custom openvmm_hcl binary, none means openhcl will be built.
120    #[clap(long)]
121    pub custom_openvmm_hcl: Option<PathBuf>,
122
123    /// Path to custom openhcl_boot, none means the boot loader will be built.
124    #[clap(long)]
125    pub custom_openhcl_boot: Option<PathBuf>,
126
127    /// Path to custom uefi MSVM.fd, none means the packaged uefi will be used.
128    #[clap(long)]
129    pub custom_uefi: Option<PathBuf>,
130
131    /// Path to custom kernel vmlinux / Image, none means the packaged kernel
132    /// will be used.
133    #[clap(long)]
134    pub custom_kernel: Option<PathBuf>,
135
136    /// Path to kernel modules, none means the packaged kernel modules will be
137    /// used.
138    #[clap(long)]
139    pub custom_kernel_modules: Option<PathBuf>,
140
141    /// Path to custom vtl0 linux kernel to use if the manifest includes a
142    /// direct-boot linux VM.
143    ///
144    /// If not specified, the packaged openvmm test linux direct kernel is used.
145    #[clap(long)]
146    pub custom_vtl0_kernel: Option<PathBuf>,
147
148    /// Additional layers to be included in the initrd
149    #[clap(long)]
150    pub custom_layer: Vec<PathBuf>,
151
152    /// Additional directories to be included in the initrd
153    #[clap(long)]
154    pub custom_directory: Vec<PathBuf>,
155
156    /// Additional rootfs.config files to use to generate the initrd
157    #[clap(long)]
158    pub custom_extra_rootfs: Vec<PathBuf>,
159
160    /// (experimental) Include the AP kernel in the IGVM file
161    #[clap(long)]
162    pub with_sidecar: bool,
163
164    /// (experimental) Path to custom sidecar kernel binary, none means sidecar
165    /// will be built.
166    #[clap(long, requires = "with_sidecar")]
167    pub custom_sidecar: Option<PathBuf>,
168}
169
170#[derive(clap::ValueEnum, Copy, Clone, PartialEq, Eq, Debug)]
171pub enum KernelPackageKindCli {
172    /// Kernel from the hcl-main branch
173    Main,
174    /// CVM kernel from the hcl-main branch
175    Cvm,
176    /// Kernel from the hcl-dev branch
177    Dev,
178    /// CVM kernel from the hcl-dev brnach
179    CvmDev,
180}
181
182#[derive(clap::ValueEnum, Copy, Clone, PartialEq, Eq, Debug)]
183pub enum BuildIgvmArch {
184    X86_64,
185    Aarch64,
186}
187
188pub fn bail_if_running_in_ci() -> anyhow::Result<()> {
189    const OVERRIDE_ENV: &str = "I_HAVE_A_GOOD_REASON_TO_RUN_BUILD_IGVM_IN_CI";
190
191    if std::env::var(OVERRIDE_ENV).is_ok() {
192        return Ok(());
193    }
194
195    for ci_env in ["TF_BUILD", "GITHUB_ACTIONS"] {
196        if std::env::var(ci_env).is_ok() {
197            log::warn!("Detected that {ci_env} is set");
198            log::warn!("");
199            log::warn!("Do not use `build-igvm` in CI scripts!");
200            log::warn!(
201                "This is a local-only, inner-dev-loop tool to build IGVM files, with an UNSTABLE CLI."
202            );
203            log::warn!("");
204            log::warn!(
205                "Automated pipelines should use the underlying `flowey` nodes that power build-igvm directly, _without_ relying on its CLI!"
206            );
207            log::warn!("");
208            log::warn!(
209                "If you _really_ know what you're doing, you can set {OVERRIDE_ENV} to disable this error."
210            );
211            anyhow::bail!("attempted to run `build-igvm` in CI")
212        }
213    }
214
215    Ok(())
216}
217
218impl IntoPipeline for BuildIgvmCli {
219    fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
220        if !matches!(backend_hint, PipelineBackendHint::Local) {
221            anyhow::bail!("build-igvm is for local use only")
222        }
223
224        bail_if_running_in_ci()?;
225
226        let openvmm_repo = flowey_lib_common::git_checkout::RepoSource::ExistingClone(
227            ReadVar::from_static(crate::repo_root()),
228        );
229
230        let Self {
231            recipe,
232            release,
233            verbose,
234            locked,
235            install_missing_deps,
236            customizations:
237                BuildIgvmCliCustomizations {
238                    build_label,
239                    override_kernel_pkg,
240                    override_openvmm_hcl_feature,
241                    override_arch,
242                    override_manifest,
243                    with_perf_tools,
244                    with_debuginfo,
245                    custom_openvmm_hcl,
246                    custom_openhcl_boot,
247                    custom_uefi,
248                    custom_kernel,
249                    custom_kernel_modules,
250                    custom_vtl0_kernel,
251                    custom_layer,
252                    custom_directory,
253                    with_sidecar,
254                    custom_sidecar,
255                    mut custom_extra_rootfs,
256                },
257        } = self;
258
259        if with_perf_tools {
260            custom_extra_rootfs.push(
261                crate::repo_root()
262                    .join("openhcl/perftoolsfs.config")
263                    .clone(),
264            );
265        }
266
267        let mut pipeline = Pipeline::new();
268
269        let (pub_out_dir, _) = pipeline.new_artifact("build-igvm");
270
271        pipeline
272            .new_job(
273                FlowPlatform::host(backend_hint),
274                FlowArch::host(backend_hint),
275                "build-igvm",
276            )
277            .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_versions::Request {})
278            .dep_on(
279                |_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
280                    hvlite_repo_source: openvmm_repo,
281                },
282            )
283            .dep_on(|_| flowey_lib_hvlite::_jobs::cfg_common::Params {
284                local_only: Some(flowey_lib_hvlite::_jobs::cfg_common::LocalOnlyParams {
285                    interactive: true,
286                    auto_install: install_missing_deps,
287                    force_nuget_mono: false, // no oss nuget packages
288                    external_nuget_auth: false,
289                    ignore_rust_version: true,
290                }),
291                verbose: ReadVar::from_static(verbose),
292                locked,
293                deny_warnings: false,
294            })
295            .dep_on(|ctx| flowey_lib_hvlite::_jobs::local_build_igvm::Params {
296                artifact_dir: ctx.publish_artifact(pub_out_dir),
297                done: ctx.new_done_handle(),
298
299                base_recipe: match recipe {
300                    OpenhclRecipeCli::X64 => OpenhclIgvmRecipe::X64,
301                    OpenhclRecipeCli::X64Devkern => OpenhclIgvmRecipe::X64Devkern,
302                    OpenhclRecipeCli::X64TestLinuxDirect => OpenhclIgvmRecipe::X64TestLinuxDirect,
303                    OpenhclRecipeCli::X64TestLinuxDirectDevkern => {
304                        OpenhclIgvmRecipe::X64TestLinuxDirectDevkern
305                    }
306                    OpenhclRecipeCli::X64Cvm => OpenhclIgvmRecipe::X64Cvm,
307                    OpenhclRecipeCli::X64CvmDevkern => OpenhclIgvmRecipe::X64CvmDevkern,
308                    OpenhclRecipeCli::Aarch64 => OpenhclIgvmRecipe::Aarch64,
309                    OpenhclRecipeCli::Aarch64Devkern => OpenhclIgvmRecipe::Aarch64Devkern,
310                },
311                release,
312
313                customizations: flowey_lib_hvlite::_jobs::local_build_igvm::Customizations {
314                    build_label,
315                    override_arch: override_arch.map(|a| match a {
316                        BuildIgvmArch::X86_64 => CommonArch::X86_64,
317                        BuildIgvmArch::Aarch64 => CommonArch::Aarch64,
318                    }),
319                    with_perf_tools,
320                    with_debuginfo,
321                    override_kernel_pkg: override_kernel_pkg.map(|p| match p {
322                        KernelPackageKindCli::Main => OpenhclKernelPackage::Main,
323                        KernelPackageKindCli::Cvm => OpenhclKernelPackage::Cvm,
324                        KernelPackageKindCli::Dev => OpenhclKernelPackage::Dev,
325                        KernelPackageKindCli::CvmDev => OpenhclKernelPackage::CvmDev,
326                    }),
327                    with_sidecar,
328                    custom_extra_rootfs,
329                    override_openvmm_hcl_feature,
330                    custom_sidecar,
331                    override_manifest,
332                    custom_openvmm_hcl,
333                    custom_openhcl_boot,
334                    custom_uefi,
335                    custom_kernel,
336                    custom_kernel_modules,
337                    custom_vtl0_kernel,
338                    custom_layer,
339                    custom_directory,
340                },
341            })
342            .finish();
343
344        Ok(pipeline)
345    }
346}