Skip to main content

flowey_hvlite/pipelines/
build_reproducible.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! See [`BuildReproducibleCli`]
5
6use flowey::node::prelude::FlowPlatformLinuxDistro;
7use flowey::node::prelude::ReadVar;
8use flowey::pipeline::prelude::*;
9use flowey_lib_common::git_checkout::RepoSource;
10use flowey_lib_hvlite::_jobs::build_and_publish_openhcl_igvm_from_recipe::OpenhclIgvmBuildParams;
11use flowey_lib_hvlite::build_openhcl_igvm_from_recipe::OpenhclIgvmRecipe;
12use flowey_lib_hvlite::build_openvmm_hcl::OpenvmmHclBuildProfile;
13use flowey_lib_hvlite::common::CommonArch;
14use flowey_lib_hvlite::common::CommonPlatform;
15use flowey_lib_hvlite::common::CommonTriple;
16use flowey_lib_hvlite::resolve_openhcl_kernel_package::OpenhclKernelPackageKind;
17use std::collections::BTreeSet;
18use target_lexicon::Triple;
19
20/// A list of pre-defined OpenHCL recipes that support being built reproducibly. Each recipe has a matching CI pipeline job that can be reproduced with this local CLI.
21#[derive(clap::ValueEnum, Copy, Clone)]
22pub enum ReproducibleOpenHclRecipe {
23    X64Cvm,
24}
25
26/// Build reproducible artifacts locally. DO NOT USE IN CI.
27#[derive(clap::Args)]
28pub struct BuildReproducibleCli {
29    /// Specify which OpenHCL recipe to build / customize off-of.
30    ///
31    /// A "recipe" corresponds to the various standard IGVM SKUs that are
32    /// actively supported and tested in our build infrastructure.
33    ///
34    /// It encodes all the details of what goes into an individual IGVM file,
35    /// such as what build flags `openvmm_hcl` should be built with, what goes
36    /// into a VTL2 initrd, what `igvmfilegen` manifest is being used, etc...
37    pub recipe: ReproducibleOpenHclRecipe,
38
39    /// Build using release variants of all constituent binary components.
40    ///
41    /// Uses --profile=boot-release for openhcl_boot, --profile=openhcl-ship
42    /// when building openvmm_hcl, etc...
43    #[clap(long)]
44    pub release: bool,
45}
46
47impl IntoPipeline for BuildReproducibleCli {
48    fn into_pipeline(self, backend_hint: PipelineBackendHint) -> anyhow::Result<Pipeline> {
49        if !matches!(backend_hint, PipelineBackendHint::Local) {
50            anyhow::bail!("build-reproducible is for local use only")
51        }
52
53        let Self { recipe, release } = self;
54
55        let mut pipeline = Pipeline::new();
56
57        let (pub_openhcl_igvm, _use_openhcl_igvm) = pipeline.new_artifact("x64-cvm-openhcl-igvm");
58        let (pub_openhcl_igvm_extras, _use_openhcl_igvm_extras) =
59            pipeline.new_artifact("x64-cvm-openhcl-igvm-extras");
60
61        let local_run_args = {
62            let mut args = crate::pipelines_shared::cfg_common_params::LocalRunArgs::default();
63            args.locked = true;
64            args.no_incremental = true;
65            args
66        };
67        let cfg_common_params = crate::pipelines_shared::cfg_common_params::get_cfg_common_params(
68            &mut pipeline,
69            backend_hint,
70            Some(local_run_args),
71        )?;
72
73        let openvmm_repo_source =
74            RepoSource::ExistingClone(ReadVar::from_static(crate::repo_root()));
75
76        let mut job = pipeline.new_job(
77            FlowPlatform::Linux(FlowPlatformLinuxDistro::Nix),
78            FlowArch::host(backend_hint),
79            "build-reproducible",
80        );
81
82        // wrap all shell commands with `nix-shell --pure --run`
83        job = job.set_command_wrapper(flowey::shell::CommandWrapperKind::NixShell {
84            path: Some(crate::repo_root().join("shell.nix")),
85        });
86
87        let openvmm_hcl_profile = if release {
88            OpenvmmHclBuildProfile::OpenvmmHclShip
89        } else {
90            OpenvmmHclBuildProfile::Debug
91        };
92
93        let (recipe_arch, kernel_kind) = match recipe {
94            ReproducibleOpenHclRecipe::X64Cvm => {
95                (CommonArch::X86_64, OpenhclKernelPackageKind::Cvm)
96            }
97        };
98
99        let igvm_file = match recipe {
100            ReproducibleOpenHclRecipe::X64Cvm => OpenhclIgvmRecipe::X64Cvm,
101        };
102
103        let openhcl_musl_target = |arch: CommonArch| -> Triple {
104            CommonTriple::Common {
105                arch,
106                platform: CommonPlatform::LinuxMusl,
107            }
108            .as_triple()
109        };
110
111        job = job.dep_on(&cfg_common_params);
112        job = job.dep_on(
113            |_| flowey_lib_hvlite::_jobs::cfg_hvlite_reposource::Params {
114                hvlite_repo_source: openvmm_repo_source.clone(),
115            },
116        );
117        job = job.dep_on(|ctx| {
118            flowey_lib_hvlite::_jobs::build_openhcl_igvm_from_recipe_nix::Params {
119                arch: recipe_arch,
120                kernel_kind,
121                igvm_files: vec![igvm_file]
122                    .into_iter()
123                    .map(|recipe| OpenhclIgvmBuildParams {
124                        profile: openvmm_hcl_profile,
125                        recipe,
126                        custom_target: Some(CommonTriple::Custom(openhcl_musl_target(recipe_arch))),
127                        extra_features: BTreeSet::new(),
128                        release_cfg: release,
129                    })
130                    .collect(),
131                artifact_dir_openhcl_igvm: ctx.publish_artifact(pub_openhcl_igvm),
132                artifact_dir_openhcl_igvm_extras: ctx.publish_artifact(pub_openhcl_igvm_extras),
133                artifact_openhcl_verify_size_baseline: None,
134                done: ctx.new_done_handle(),
135            }
136        });
137
138        job.finish();
139
140        Ok(pipeline)
141    }
142}