1use flowey::node::prelude::*;
11use flowey_lib_common::run_cargo_build::CargoBuildProfile;
12use flowey_lib_common::run_cargo_build::CargoCrateType;
13use std::collections::BTreeMap;
14use std::collections::BTreeSet;
15
16#[derive(Serialize, Deserialize)]
22pub enum CargoBuildOutput {
23 WindowsBin {
24 exe: PathBuf,
25 pdb: PathBuf,
26 },
27 ElfBin {
28 bin: PathBuf,
29 dbg: Option<PathBuf>,
30 },
31 LinuxStaticLib {
32 a: PathBuf,
33 },
34 LinuxDynamicLib {
35 so: PathBuf,
36 },
37 WindowsStaticLib {
38 lib: PathBuf,
39 pdb: PathBuf,
40 },
41 WindowsDynamicLib {
42 dll: PathBuf,
43 dll_lib: PathBuf,
44 pdb: PathBuf,
45 },
46 UefiBin {
47 efi: PathBuf,
48 pdb: PathBuf,
49 },
50}
51
52impl CargoBuildOutput {
53 pub fn from_base_cargo_build_output(
54 base: flowey_lib_common::run_cargo_build::CargoBuildOutput,
55 elf_dbg: Option<PathBuf>,
56 ) -> Self {
57 use flowey_lib_common::run_cargo_build::CargoBuildOutput as Base;
58
59 match base {
60 Base::WindowsBin { exe, pdb } => Self::WindowsBin { exe, pdb },
61 Base::LinuxStaticLib { a } => Self::LinuxStaticLib { a },
62 Base::LinuxDynamicLib { so } => Self::LinuxDynamicLib { so },
63 Base::WindowsStaticLib { lib, pdb } => Self::WindowsStaticLib { lib, pdb },
64 Base::WindowsDynamicLib { dll, dll_lib, pdb } => {
65 Self::WindowsDynamicLib { dll, dll_lib, pdb }
66 }
67 Base::UefiBin { efi, pdb } => Self::UefiBin { efi, pdb },
68
69 Base::ElfBin { bin } => Self::ElfBin { bin, dbg: elf_dbg },
70 }
71 }
72}
73
74pub mod common {
78 use serde::Deserialize;
79 use serde::Serialize;
80
81 #[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
88 pub enum CommonProfile {
89 Release,
90 Debug,
91 }
92
93 impl CommonProfile {
94 pub fn from_release(release: bool) -> Self {
95 match release {
96 true => Self::Release,
97 false => Self::Debug,
98 }
99 }
100
101 pub fn to_release(self) -> bool {
102 match self {
103 Self::Release => true,
104 Self::Debug => false,
105 }
106 }
107 }
108
109 impl From<CommonProfile> for super::BuildProfile {
110 fn from(value: CommonProfile) -> Self {
111 match value {
112 CommonProfile::Release => super::BuildProfile::Release,
113 CommonProfile::Debug => super::BuildProfile::Debug,
114 }
115 }
116 }
117
118 #[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
121 pub enum CommonArch {
122 X86_64,
123 Aarch64,
124 }
125
126 impl CommonArch {
127 pub fn as_arch(&self) -> target_lexicon::Architecture {
128 match self {
129 CommonArch::X86_64 => target_lexicon::Architecture::X86_64,
130 CommonArch::Aarch64 => target_lexicon::Architecture::Aarch64(
131 target_lexicon::Aarch64Architecture::Aarch64,
132 ),
133 }
134 }
135
136 pub fn from_triple(triple: &target_lexicon::Triple) -> Option<Self> {
137 let arch = match triple.architecture {
138 target_lexicon::Architecture::Aarch64(
139 target_lexicon::Aarch64Architecture::Aarch64,
140 ) => Self::Aarch64,
141 target_lexicon::Architecture::X86_64 => Self::X86_64,
142 _ => return None,
143 };
144 Some(arch)
145 }
146 }
147
148 #[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
151 pub enum CommonPlatform {
152 WindowsMsvc,
153 LinuxGnu,
154 LinuxMusl,
155 MacOs,
156 }
157
158 #[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
159 pub enum CommonTriple {
160 Common {
161 arch: CommonArch,
162 platform: CommonPlatform,
163 },
164 Custom(target_lexicon::Triple),
165 }
166
167 impl CommonTriple {
168 pub const X86_64_WINDOWS_MSVC: Self = Self::Common {
169 arch: CommonArch::X86_64,
170 platform: CommonPlatform::WindowsMsvc,
171 };
172 pub const X86_64_LINUX_GNU: Self = Self::Common {
173 arch: CommonArch::X86_64,
174 platform: CommonPlatform::LinuxGnu,
175 };
176 pub const X86_64_LINUX_MUSL: Self = Self::Common {
177 arch: CommonArch::X86_64,
178 platform: CommonPlatform::LinuxMusl,
179 };
180 pub const AARCH64_WINDOWS_MSVC: Self = Self::Common {
181 arch: CommonArch::Aarch64,
182 platform: CommonPlatform::WindowsMsvc,
183 };
184 pub const AARCH64_LINUX_GNU: Self = Self::Common {
185 arch: CommonArch::Aarch64,
186 platform: CommonPlatform::LinuxGnu,
187 };
188 pub const AARCH64_LINUX_MUSL: Self = Self::Common {
189 arch: CommonArch::Aarch64,
190 platform: CommonPlatform::LinuxMusl,
191 };
192 }
193
194 impl std::fmt::Debug for CommonTriple {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 std::fmt::Debug::fmt(&self.as_triple(), f)
197 }
198 }
199
200 impl std::fmt::Display for CommonTriple {
201 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
202 std::fmt::Display::fmt(&self.as_triple(), f)
203 }
204 }
205
206 impl PartialOrd for CommonTriple {
207 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
208 Some(self.cmp(other))
209 }
210 }
211
212 impl Ord for CommonTriple {
213 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
214 self.as_triple()
215 .to_string()
216 .cmp(&other.as_triple().to_string())
217 }
218 }
219
220 impl CommonTriple {
221 pub fn as_triple(&self) -> target_lexicon::Triple {
222 match self {
223 CommonTriple::Common { arch, platform } => match platform {
224 CommonPlatform::WindowsMsvc => target_lexicon::Triple {
225 architecture: arch.as_arch(),
226 vendor: target_lexicon::Vendor::Pc,
227 operating_system: target_lexicon::OperatingSystem::Windows,
228 environment: target_lexicon::Environment::Msvc,
229 binary_format: target_lexicon::BinaryFormat::Coff,
230 },
231 CommonPlatform::LinuxGnu => target_lexicon::Triple {
232 architecture: arch.as_arch(),
233 vendor: target_lexicon::Vendor::Unknown,
234 operating_system: target_lexicon::OperatingSystem::Linux,
235 environment: target_lexicon::Environment::Gnu,
236 binary_format: target_lexicon::BinaryFormat::Elf,
237 },
238 CommonPlatform::LinuxMusl => target_lexicon::Triple {
239 architecture: arch.as_arch(),
240 vendor: target_lexicon::Vendor::Unknown,
241 operating_system: target_lexicon::OperatingSystem::Linux,
242 environment: target_lexicon::Environment::Musl,
243 binary_format: target_lexicon::BinaryFormat::Elf,
244 },
245 CommonPlatform::MacOs => target_lexicon::Triple {
246 architecture: arch.as_arch(),
247 vendor: target_lexicon::Vendor::Apple,
248 operating_system: target_lexicon::OperatingSystem::Darwin(None),
249 environment: target_lexicon::Environment::Unknown,
250 binary_format: target_lexicon::BinaryFormat::Macho,
251 },
252 },
253 CommonTriple::Custom(t) => t.clone(),
254 }
255 }
256
257 pub fn common_arch(&self) -> Option<CommonArch> {
258 match self {
259 CommonTriple::Common { arch, .. } => Some(*arch),
260 CommonTriple::Custom(target) => CommonArch::from_triple(target),
261 }
262 }
263 }
264}
265
266#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
267pub enum BuildProfile {
268 Debug,
269 Release,
270 UnderhillShip,
271 BootDev,
272 BootRelease,
273 Light,
274}
275
276flowey_request! {
277 pub struct Request {
278 pub crate_name: String,
279 pub out_name: String,
280 pub profile: BuildProfile, pub features: BTreeSet<String>,
282 pub crate_type: CargoCrateType,
283 pub target: target_lexicon::Triple,
284 pub no_split_dbg_info: bool,
286 pub extra_env: Option<ReadVar<BTreeMap<String, String>>>,
287 pub pre_build_deps: Vec<ReadVar<SideEffect>>,
292 pub output: WriteVar<CargoBuildOutput>,
294 }
295}
296
297new_flow_node!(struct Node);
298
299impl FlowNode for Node {
300 type Request = Request;
301
302 fn imports(ctx: &mut ImportCtx<'_>) {
303 ctx.import::<crate::install_openvmm_rust_build_essential::Node>();
304 ctx.import::<crate::git_checkout_openvmm_repo::Node>();
305 ctx.import::<crate::init_openvmm_magicpath_openhcl_sysroot::Node>();
306 ctx.import::<crate::run_split_debug_info::Node>();
307 ctx.import::<crate::init_cross_build::Node>();
308 ctx.import::<flowey_lib_common::run_cargo_build::Node>();
309 ctx.import::<flowey_lib_common::install_rust::Node>();
310 }
311
312 fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
313 let base_pre_build_deps =
314 [ctx.reqv(crate::install_openvmm_rust_build_essential::Request)].to_vec();
315
316 let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
317
318 for Request {
319 crate_name,
320 out_name,
321 profile,
322 features,
323 crate_type,
324 mut target,
325 no_split_dbg_info,
326 extra_env,
327 pre_build_deps: user_pre_build_deps,
328 output,
329 } in requests
330 {
331 let mut pre_build_deps = base_pre_build_deps.clone();
332 pre_build_deps.extend(user_pre_build_deps);
333
334 let sysroot_arch = match target.architecture {
341 target_lexicon::Architecture::Aarch64(_) => {
342 crate::init_openvmm_magicpath_openhcl_sysroot::OpenvmmSysrootArch::Aarch64
343 }
344 target_lexicon::Architecture::X86_64 => {
345 crate::init_openvmm_magicpath_openhcl_sysroot::OpenvmmSysrootArch::X64
346 }
347 arch => anyhow::bail!("unsupported arch {arch}"),
348 };
349
350 if matches!(target.environment, target_lexicon::Environment::Musl) {
351 pre_build_deps.push(
352 ctx.reqv(|v| crate::init_openvmm_magicpath_openhcl_sysroot::Request {
353 arch: sysroot_arch,
354 path: v,
355 })
356 .into_side_effect(),
357 );
358 }
359
360 let injected_env = ctx.reqv(|v| crate::init_cross_build::Request {
361 target: target.clone(),
362 injected_env: v,
363 });
364
365 let extra_env = if let Some(extra_env) = extra_env {
366 extra_env
367 .zip(ctx, injected_env)
368 .map(ctx, move |(mut a, b)| {
369 a.extend(b);
370 a
371 })
372 } else {
373 injected_env
374 };
375
376 let mut config = Vec::new();
377
378 let passed_target = if target.vendor.as_str() == "minimal_rt" {
382 config.push(format!(
383 "openhcl/minimal_rt/{arch}-config.toml",
384 arch = target.architecture.into_str()
385 ));
386 if target.architecture == target_lexicon::Architecture::X86_64 {
387 target.vendor = target_lexicon::Vendor::Unknown;
391 Some(target.clone())
392 } else {
393 ctx.req(flowey_lib_common::install_rust::Request::InstallComponent(
397 "rust-src".into(),
398 ));
399 None
400 }
401 } else {
402 Some(target.clone())
403 };
404
405 let base_output = ctx.reqv(|v| flowey_lib_common::run_cargo_build::Request {
406 in_folder: openvmm_repo_path.clone(),
407 crate_name,
408 out_name,
409 profile: match profile {
410 BuildProfile::Debug => CargoBuildProfile::Debug,
411 BuildProfile::Release => CargoBuildProfile::Release,
412 BuildProfile::UnderhillShip => {
413 CargoBuildProfile::Custom("underhill-ship".into())
414 }
415 BuildProfile::BootDev => CargoBuildProfile::Custom("boot-dev".into()),
416 BuildProfile::BootRelease => CargoBuildProfile::Custom("boot-release".into()),
417 BuildProfile::Light => CargoBuildProfile::Custom("light".into()),
418 },
419 features,
420 output_kind: crate_type,
421 target: passed_target,
422 extra_env: Some(extra_env),
423 config,
424 pre_build_deps,
425 output: v,
426 });
427
428 if !no_split_dbg_info
429 && matches!(
430 (crate_type, target.operating_system),
431 (
432 CargoCrateType::Bin,
433 target_lexicon::OperatingSystem::Linux
434 | target_lexicon::OperatingSystem::None_
435 )
436 )
437 {
438 let elf_bin = base_output.clone().map(ctx, |o| match o {
439 flowey_lib_common::run_cargo_build::CargoBuildOutput::ElfBin { bin } => bin,
440 _ => unreachable!(),
441 });
442
443 let (out_bin, write_out_bin) = ctx.new_var();
444 let (out_dbg, write_out_dbg) = ctx.new_var();
445
446 ctx.req(crate::run_split_debug_info::Request {
447 arch: match target.architecture {
448 target_lexicon::Architecture::Aarch64(_) => common::CommonArch::Aarch64,
449 target_lexicon::Architecture::X86_64 => common::CommonArch::X86_64,
450 _ => anyhow::bail!("cannot split linux dbginfo on specified arch"),
451 },
452 in_bin: elf_bin,
453 out_bin: write_out_bin,
454 out_dbg_info: write_out_dbg,
455 });
456
457 ctx.emit_minor_rust_step("reporting split debug info", |ctx| {
458 let out_bin = out_bin.claim(ctx);
459 let out_dbg = out_dbg.claim(ctx);
460 let base_output = base_output.claim(ctx);
461 let output = output.claim(ctx);
462
463 move |rt| {
464 let mut fixed = CargoBuildOutput::from_base_cargo_build_output(
465 rt.read(base_output),
466 Some(rt.read(out_dbg)),
467 );
468 let CargoBuildOutput::ElfBin { bin, .. } = &mut fixed else {
469 unreachable!()
470 };
471 *bin = rt.read(out_bin);
472 rt.write(output, &fixed);
473 }
474 });
475 } else {
476 base_output.write_into(ctx, output, |o| {
477 CargoBuildOutput::from_base_cargo_build_output(o, None)
478 });
479 }
480 }
481
482 Ok(())
483 }
484}