1use flowey::node::prelude::*;
11use flowey_lib_common::run_cargo_build::CargoBuildProfile;
12use flowey_lib_common::run_cargo_build::CargoCrateType;
13use flowey_lib_common::run_cargo_build::CargoFeatureSet;
14use std::collections::BTreeMap;
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
74#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
75pub enum BuildProfile {
76 Debug,
77 Release,
78 UnderhillShip,
79 BootDev,
80 BootRelease,
81 Light,
82}
83
84flowey_request! {
85 pub struct Request {
86 pub crate_name: String,
87 pub out_name: String,
88 pub profile: BuildProfile, pub features: CargoFeatureSet,
90 pub crate_type: CargoCrateType,
91 pub target: target_lexicon::Triple,
92 pub no_split_dbg_info: bool,
94 pub extra_env: Option<ReadVar<BTreeMap<String, String>>>,
95 pub pre_build_deps: Vec<ReadVar<SideEffect>>,
100 pub output: WriteVar<CargoBuildOutput>,
102 }
103}
104
105new_flow_node!(struct Node);
106
107impl FlowNode for Node {
108 type Request = Request;
109
110 fn imports(ctx: &mut ImportCtx<'_>) {
111 ctx.import::<crate::install_openvmm_rust_build_essential::Node>();
112 ctx.import::<crate::git_checkout_openvmm_repo::Node>();
113 ctx.import::<crate::init_openvmm_magicpath_openhcl_sysroot::Node>();
114 ctx.import::<crate::run_split_debug_info::Node>();
115 ctx.import::<crate::init_cross_build::Node>();
116 ctx.import::<flowey_lib_common::run_cargo_build::Node>();
117 ctx.import::<flowey_lib_common::install_rust::Node>();
118 }
119
120 fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
121 let base_pre_build_deps =
122 [ctx.reqv(crate::install_openvmm_rust_build_essential::Request)].to_vec();
123
124 let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
125
126 for Request {
127 crate_name,
128 out_name,
129 profile,
130 features,
131 crate_type,
132 mut target,
133 no_split_dbg_info,
134 extra_env,
135 pre_build_deps: user_pre_build_deps,
136 output,
137 } in requests
138 {
139 let mut pre_build_deps = base_pre_build_deps.clone();
140 pre_build_deps.extend(user_pre_build_deps);
141
142 let sysroot_arch = crate::common::CommonArch::from_architecture(target.architecture)?;
149
150 if matches!(target.environment, target_lexicon::Environment::Musl) {
151 pre_build_deps.push(
152 ctx.reqv(|v| crate::init_openvmm_magicpath_openhcl_sysroot::Request {
153 arch: sysroot_arch,
154 path: v,
155 })
156 .into_side_effect(),
157 );
158 }
159
160 let injected_env = ctx.reqv(|v| crate::init_cross_build::Request {
161 target: target.clone(),
162 injected_env: v,
163 });
164
165 let extra_env = if let Some(extra_env) = extra_env {
166 extra_env
167 .zip(ctx, injected_env)
168 .map(ctx, move |(mut a, b)| {
169 a.extend(b);
170 a
171 })
172 } else {
173 injected_env
174 };
175
176 let mut config = Vec::new();
177
178 let passed_target = if target.vendor.as_str() == "minimal_rt" {
182 config.push(format!(
183 "openhcl/minimal_rt/{arch}-config.toml",
184 arch = target.architecture.into_str()
185 ));
186 if target.architecture == target_lexicon::Architecture::X86_64 {
187 target.vendor = target_lexicon::Vendor::Unknown;
191 Some(target.clone())
192 } else {
193 ctx.req(flowey_lib_common::install_rust::Request::InstallComponent(
197 "rust-src".into(),
198 ));
199 None
200 }
201 } else {
202 Some(target.clone())
203 };
204
205 let base_output = ctx.reqv(|v| flowey_lib_common::run_cargo_build::Request {
206 in_folder: openvmm_repo_path.clone(),
207 crate_name,
208 out_name,
209 profile: match profile {
210 BuildProfile::Debug => CargoBuildProfile::Debug,
211 BuildProfile::Release => CargoBuildProfile::Release,
212 BuildProfile::UnderhillShip => {
213 CargoBuildProfile::Custom("underhill-ship".into())
214 }
215 BuildProfile::BootDev => CargoBuildProfile::Custom("boot-dev".into()),
216 BuildProfile::BootRelease => CargoBuildProfile::Custom("boot-release".into()),
217 BuildProfile::Light => CargoBuildProfile::Custom("light".into()),
218 },
219 features,
220 output_kind: crate_type,
221 target: passed_target,
222 extra_env: Some(extra_env),
223 config,
224 pre_build_deps,
225 output: v,
226 });
227
228 if !no_split_dbg_info
229 && matches!(
230 (crate_type, target.operating_system),
231 (
232 CargoCrateType::Bin,
233 target_lexicon::OperatingSystem::Linux
234 | target_lexicon::OperatingSystem::None_
235 )
236 )
237 {
238 let elf_bin = base_output.clone().map(ctx, |o| match o {
239 flowey_lib_common::run_cargo_build::CargoBuildOutput::ElfBin { bin } => bin,
240 _ => unreachable!(),
241 });
242
243 let (out_bin, write_out_bin) = ctx.new_var();
244 let (out_dbg, write_out_dbg) = ctx.new_var();
245
246 ctx.req(crate::run_split_debug_info::Request {
247 arch: crate::common::CommonArch::from_architecture(target.architecture)
248 .context("cannot split linux dbginfo on specified arch")?,
249 in_bin: elf_bin,
250 out_bin: write_out_bin,
251 out_dbg_info: write_out_dbg,
252 reproducible_without_debuglink: matches!(
253 ctx.platform(),
254 FlowPlatform::Linux(FlowPlatformLinuxDistro::Nix)
255 ),
256 });
257
258 ctx.emit_minor_rust_step("reporting split debug info", |ctx| {
259 let out_bin = out_bin.claim(ctx);
260 let out_dbg = out_dbg.claim(ctx);
261 let base_output = base_output.claim(ctx);
262 let output = output.claim(ctx);
263
264 move |rt| {
265 let mut fixed = CargoBuildOutput::from_base_cargo_build_output(
266 rt.read(base_output),
267 Some(rt.read(out_dbg)),
268 );
269 let CargoBuildOutput::ElfBin { bin, .. } = &mut fixed else {
270 unreachable!()
271 };
272 *bin = rt.read(out_bin);
273 rt.write(output, &fixed);
274 }
275 });
276 } else {
277 base_output.write_into(ctx, output, |o| {
278 CargoBuildOutput::from_base_cargo_build_output(o, None)
279 });
280 }
281 }
282
283 Ok(())
284 }
285}