flowey_lib_hvlite/
resolve_openvmm_qemu.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 QemuFile {
19 SystemAarch64,
21}
22
23impl QemuFile {
24 pub fn filename(self) -> &'static str {
26 match self {
27 Self::SystemAarch64 => "qemu-system-aarch64",
28 }
29 }
30}
31
32flowey_config! {
33 pub struct Config {
35 pub version: Option<String>,
37 pub local_paths: BTreeMap<CommonArch, ConfigVar<PathBuf>>,
39 }
40}
41
42flowey_request! {
43 pub enum Request {
44 Get(QemuFile, CommonArch, WriteVar<PathBuf>),
46 }
47}
48
49new_flow_node_with_config!(struct Node);
50
51impl FlowNodeWithConfig for Node {
52 type Request = Request;
53 type Config = Config;
54
55 fn imports(ctx: &mut ImportCtx<'_>) {
56 ctx.import::<flowey_lib_common::install_dist_pkg::Node>();
57 ctx.import::<flowey_lib_common::download_gh_release::Node>();
58 }
59
60 fn emit(
61 config: Config,
62 requests: Vec<Self::Request>,
63 ctx: &mut NodeCtx<'_>,
64 ) -> anyhow::Result<()> {
65 let Config {
66 version,
67 local_paths,
68 } = config;
69 let mut deps: BTreeMap<(QemuFile, CommonArch), Vec<WriteVar<PathBuf>>> = BTreeMap::new();
70
71 for req in requests {
72 match req {
73 Request::Get(file, arch, var) => {
74 deps.entry((file, arch)).or_default().push(var);
75 }
76 }
77 }
78
79 if version.is_some() && !local_paths.is_empty() {
80 anyhow::bail!("Cannot specify both Version and LocalPath requests");
81 }
82
83 if version.is_none() && local_paths.is_empty() {
84 anyhow::bail!("Must specify a Version or LocalPath request");
85 }
86
87 if deps.is_empty() {
90 return Ok(());
91 }
92
93 if !local_paths.is_empty() {
94 ctx.emit_rust_step("use local QEMU", |ctx| {
95 let deps = deps.claim(ctx);
96 let local_paths: BTreeMap<_, _> = local_paths
97 .into_iter()
98 .map(|(key, var)| (key, var.claim(ctx)))
99 .collect();
100 move |rt| {
101 let resolved_paths: BTreeMap<CommonArch, PathBuf> = local_paths
102 .into_iter()
103 .map(|(key, var)| (key, rt.read(var)))
104 .collect();
105
106 for ((file, arch), vars) in deps {
107 let base_dir = resolved_paths.get(&arch).ok_or_else(|| {
108 anyhow::anyhow!("No local path specified for {:?}", arch)
109 })?;
110 let path = base_dir.join(file.filename());
111 rt.write_all(vars, &path)
112 }
113
114 Ok(())
115 }
116 });
117
118 return Ok(());
119 }
120
121 let version = version.expect("local requests handled above");
122
123 let needed_archives: BTreeSet<CommonArch> = deps.keys().map(|(_, arch)| *arch).collect();
125
126 let mut archives = BTreeMap::new();
127 for arch in needed_archives {
128 let arch_str = match arch {
129 CommonArch::X86_64 => "x86_64",
130 CommonArch::Aarch64 => "aarch64",
131 };
132 let archive = ctx.reqv(|v| flowey_lib_common::download_gh_release::Request {
133 repo_owner: "microsoft".into(),
134 repo_name: "openvmm-deps".into(),
135 needs_auth: false,
136 tag: version.clone(),
137 file_name: format!("qemu-linux-static.{arch_str}.{version}.tar.gz"),
138 path: v,
139 });
140 archives.insert(arch, archive);
141 }
142
143 let persistent_dir = ctx.persistent_dir();
144
145 ctx.emit_rust_step("unpack QEMU archive", |ctx| {
146 let persistent_dir = persistent_dir.claim(ctx);
147 let archives = archives.claim(ctx);
148 let deps = deps.claim(ctx);
149 let version = version.clone();
150 move |rt| {
151 let persistent_dir = persistent_dir.map(|d| rt.read(d));
152
153 let mut extract_dirs = BTreeMap::new();
154 for (arch, archive) in archives {
155 let file = rt.read(archive);
156 let dir = flowey_lib_common::_util::extract::extract_tar_gz_if_new(
157 rt,
158 persistent_dir.as_deref(),
159 &file,
160 &version,
161 )?;
162 extract_dirs.insert(arch, dir);
163 }
164
165 for ((file, arch), vars) in deps {
166 let extract_dir = extract_dirs
167 .get(&arch)
168 .expect("archive was downloaded for this arch");
169 let path = extract_dir.join(file.filename());
170 if !path.exists() {
171 anyhow::bail!(
172 "expected QEMU binary not found in archive: {}",
173 path.display()
174 );
175 }
176 path.make_executable()?;
178 rt.write_all(vars, &path)
179 }
180
181 Ok(())
182 }
183 });
184
185 Ok(())
186 }
187}