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