flowey_lib_hvlite/
resolve_openvmm_deps.rs1use crate::common::CommonArch;
12use flowey::node::prelude::*;
13use std::collections::BTreeMap;
14use std::collections::BTreeSet;
15
16#[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 pub struct Config {
39 pub version: Option<String>,
41 pub local_paths: BTreeMap<CommonArch, ConfigVar<PathBuf>>,
43 }
44}
45
46flowey_request! {
47 pub enum Request {
48 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 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 let needed_archs: BTreeSet<CommonArch> = deps.keys().map(|(_, arch)| *arch).collect();
128
129 let persistent_dir = ctx.persistent_dir();
130
131 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 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}