Skip to main content

flowey_lib_hvlite/
resolve_openvmm_deps.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Download various pre-built `openvmm-deps` dependencies, or use a local path if specified.
5
6use crate::common::CommonArch;
7use flowey::node::prelude::*;
8use std::collections::BTreeMap;
9
10/// Which file to extract from the openvmm-deps archive.
11#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
12pub enum OpenvmmDepFile {
13    LinuxTestKernel,
14    LinuxTestInitrd,
15    OpenhclCpioDbgrd,
16    OpenhclCpioShell,
17    OpenhclSysroot,
18    PetritoolsErofs,
19}
20
21impl OpenvmmDepFile {
22    pub fn filename(self, arch: CommonArch) -> &'static str {
23        match self {
24            Self::LinuxTestKernel => match arch {
25                CommonArch::X86_64 => "vmlinux",
26                CommonArch::Aarch64 => "Image",
27            },
28            Self::LinuxTestInitrd => "initrd",
29            Self::OpenhclCpioDbgrd => "dbgrd.cpio.gz",
30            Self::OpenhclCpioShell => "shell.cpio.gz",
31            Self::OpenhclSysroot => "sysroot.tar.gz",
32            Self::PetritoolsErofs => "petritools.erofs",
33        }
34    }
35}
36
37flowey_config! {
38    /// Config for the resolve_openvmm_deps node.
39    pub struct Config {
40        /// Specify version of the github release to pull from
41        pub version: Option<String>,
42        /// Use locally downloaded openvmm-deps, keyed by architecture
43        pub local_paths: BTreeMap<CommonArch, ConfigVar<PathBuf>>,
44    }
45}
46
47flowey_request! {
48    pub enum Request {
49        /// Get the path to a specific dep file
50        Get(OpenvmmDepFile, CommonArch, WriteVar<PathBuf>),
51    }
52}
53
54new_flow_node_with_config!(struct Node);
55
56impl FlowNodeWithConfig for Node {
57    type Request = Request;
58    type Config = Config;
59
60    fn imports(ctx: &mut ImportCtx<'_>) {
61        ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
62        ctx.import::<flowey_lib_common::download_gh_release::Node>();
63    }
64
65    fn emit(
66        config: Config,
67        requests: Vec<Self::Request>,
68        ctx: &mut NodeCtx<'_>,
69    ) -> anyhow::Result<()> {
70        let version = config.version;
71        let local_paths = config.local_paths;
72        let mut deps: BTreeMap<(OpenvmmDepFile, CommonArch), Vec<WriteVar<PathBuf>>> =
73            BTreeMap::new();
74
75        for req in requests {
76            match req {
77                Request::Get(dep, arch, var) => {
78                    deps.entry((dep, arch)).or_default().push(var);
79                }
80            }
81        }
82
83        if version.is_some() && !local_paths.is_empty() {
84            anyhow::bail!("Cannot specify both Version and LocalPath requests");
85        }
86
87        if version.is_none() && local_paths.is_empty() {
88            anyhow::bail!("Must specify a Version or LocalPath request");
89        }
90
91        // -- end of req processing -- //
92
93        if deps.is_empty() {
94            return Ok(());
95        }
96
97        // Which architectures have at least one dep requested?
98        let needs_arch = |arch: CommonArch| deps.keys().any(|(_, a)| *a == arch);
99
100        if !local_paths.is_empty() {
101            ctx.emit_rust_step("use local openvmm-deps", |ctx| {
102                let deps = deps.claim(ctx);
103                let local_paths: BTreeMap<_, _> = local_paths
104                    .into_iter()
105                    .map(|(arch, var)| (arch, var.claim(ctx)))
106                    .collect();
107                move |rt| {
108                    let resolved_paths: BTreeMap<CommonArch, PathBuf> = local_paths
109                        .into_iter()
110                        .map(|(arch, var)| (arch, rt.read(var)))
111                        .collect();
112
113                    for ((dep, arch), vars) in deps {
114                        let base_dir = resolved_paths.get(&arch).ok_or_else(|| {
115                            anyhow::anyhow!("No local path specified for architecture {:?}", arch)
116                        })?;
117                        let path = base_dir.join(dep.filename(arch));
118                        rt.write_all(vars, &path)
119                    }
120
121                    Ok(())
122                }
123            });
124
125            return Ok(());
126        }
127
128        let extract_tar_bz2_deps =
129            flowey_lib_common::_util::extract::extract_tar_bz2_if_new_deps(ctx);
130
131        let download_archive = |arch: CommonArch, ctx: &mut NodeCtx<'_>| {
132            let version = version.clone().expect("local requests handled above");
133            let arch_str = match arch {
134                CommonArch::X86_64 => "x86_64",
135                CommonArch::Aarch64 => "aarch64",
136            };
137            ctx.reqv(|v| flowey_lib_common::download_gh_release::Request {
138                repo_owner: "microsoft".into(),
139                repo_name: "openvmm-deps".into(),
140                needs_auth: false,
141                tag: version.clone(),
142                file_name: format!("openvmm-deps.{arch_str}.{version}.tar.bz2"),
143                path: v,
144            })
145        };
146
147        let openvmm_deps_tar_bz2_x64 =
148            needs_arch(CommonArch::X86_64).then(|| download_archive(CommonArch::X86_64, ctx));
149        let openvmm_deps_tar_bz2_aarch64 =
150            needs_arch(CommonArch::Aarch64).then(|| download_archive(CommonArch::Aarch64, ctx));
151
152        ctx.emit_rust_step("unpack openvmm-deps archive", |ctx| {
153            let extract_tar_bz2_deps = extract_tar_bz2_deps.claim(ctx);
154            let openvmm_deps_tar_bz2_x64 = openvmm_deps_tar_bz2_x64.claim(ctx);
155            let openvmm_deps_tar_bz2_aarch64 = openvmm_deps_tar_bz2_aarch64.claim(ctx);
156            let deps = deps.claim(ctx);
157            let version = version.clone().expect("local requests handled above");
158            move |rt| {
159                let extract_dir_x64 = openvmm_deps_tar_bz2_x64
160                    .map(|file| {
161                        let file = rt.read(file);
162                        flowey_lib_common::_util::extract::extract_tar_bz2_if_new(
163                            rt,
164                            extract_tar_bz2_deps.clone(),
165                            &file,
166                            &version,
167                        )
168                    })
169                    .transpose()?;
170                let extract_dir_aarch64 = openvmm_deps_tar_bz2_aarch64
171                    .map(|file| {
172                        let file = rt.read(file);
173                        flowey_lib_common::_util::extract::extract_tar_bz2_if_new(
174                            rt,
175                            extract_tar_bz2_deps.clone(),
176                            &file,
177                            &version,
178                        )
179                    })
180                    .transpose()?;
181
182                let base_dir = |arch| match arch {
183                    CommonArch::X86_64 => extract_dir_x64.clone().unwrap(),
184                    CommonArch::Aarch64 => extract_dir_aarch64.clone().unwrap(),
185                };
186
187                for ((dep, arch), vars) in deps {
188                    let path = base_dir(arch).join(dep.filename(arch));
189                    rt.write_all(vars, &path)
190                }
191
192                Ok(())
193            }
194        });
195
196        Ok(())
197    }
198}