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