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