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: Option<PathBuf>,
30 },
31 ElfBin {
32 bin: PathBuf,
33 dbg: Option<PathBuf>,
34 },
35 LinuxStaticLib {
36 a: PathBuf,
37 },
38 LinuxDynamicLib {
39 so: PathBuf,
40 },
41 WindowsStaticLib {
42 lib: PathBuf,
43 pdb: PathBuf,
44 },
45 WindowsDynamicLib {
46 dll: PathBuf,
47 dll_lib: PathBuf,
48 pdb: PathBuf,
49 },
50 UefiBin {
51 efi: PathBuf,
52 pdb: PathBuf,
53 },
54}
55
56impl CargoBuildOutput {
57 pub fn from_base_cargo_build_output(
58 base: flowey_lib_common::run_cargo_build::CargoBuildOutput,
59 elf_dbg: Option<PathBuf>,
60 ) -> Self {
61 use flowey_lib_common::run_cargo_build::CargoBuildOutput as Base;
62
63 match base {
64 Base::WindowsBin { exe, pdb } => Self::WindowsBin { exe, pdb },
65 Base::LinuxStaticLib { a } => Self::LinuxStaticLib { a },
66 Base::LinuxDynamicLib { so } => Self::LinuxDynamicLib { so },
67 Base::WindowsStaticLib { lib, pdb } => Self::WindowsStaticLib { lib, pdb },
68 Base::WindowsDynamicLib { dll, dll_lib, pdb } => {
69 Self::WindowsDynamicLib { dll, dll_lib, pdb }
70 }
71 Base::UefiBin { efi, pdb } => Self::UefiBin { efi, pdb },
72
73 Base::ElfBin { bin } => Self::ElfBin { bin, dbg: elf_dbg },
74 }
75 }
76}
77
78#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
79pub enum BuildProfile {
80 Debug,
81 Release,
82 UnderhillShip,
83 BootDev,
84 BootRelease,
85 Light,
86}
87
88flowey_request! {
89 pub struct Request {
90 pub crate_name: String,
91 pub out_name: String,
92 pub profile: BuildProfile, pub features: CargoFeatureSet,
94 pub crate_type: CargoCrateType,
95 pub target: target_lexicon::Triple,
96 pub no_split_dbg_info: bool,
98 pub extra_env: Option<ReadVar<BTreeMap<String, String>>>,
99 pub pre_build_deps: Vec<ReadVar<SideEffect>>,
104 pub output: WriteVar<CargoBuildOutput>,
106 }
107}
108
109new_flow_node!(struct Node);
110
111impl FlowNode for Node {
112 type Request = Request;
113
114 fn imports(ctx: &mut ImportCtx<'_>) {
115 ctx.import::<crate::install_openvmm_rust_build_essential::Node>();
116 ctx.import::<crate::git_checkout_openvmm_repo::Node>();
117 ctx.import::<crate::init_openvmm_magicpath_openhcl_sysroot::Node>();
118 ctx.import::<crate::run_split_debug_info::Node>();
119 ctx.import::<crate::init_cross_build::Node>();
120 ctx.import::<flowey_lib_common::run_cargo_build::Node>();
121 ctx.import::<flowey_lib_common::install_rust::Node>();
122 }
123
124 fn emit(requests: Vec<Self::Request>, ctx: &mut NodeCtx<'_>) -> anyhow::Result<()> {
125 let base_pre_build_deps =
126 [ctx.reqv(crate::install_openvmm_rust_build_essential::Request)].to_vec();
127
128 let openvmm_repo_path = ctx.reqv(crate::git_checkout_openvmm_repo::req::GetRepoDir);
129
130 for Request {
131 crate_name,
132 out_name,
133 profile,
134 features,
135 crate_type,
136 mut target,
137 no_split_dbg_info,
138 extra_env,
139 pre_build_deps: user_pre_build_deps,
140 output,
141 } in requests
142 {
143 let mut pre_build_deps = base_pre_build_deps.clone();
144 pre_build_deps.extend(user_pre_build_deps);
145
146 let sysroot_arch = crate::common::CommonArch::from_architecture(target.architecture)?;
153
154 if matches!(target.environment, target_lexicon::Environment::Musl) {
155 pre_build_deps.push(
156 ctx.reqv(|v| crate::init_openvmm_magicpath_openhcl_sysroot::Request {
157 arch: sysroot_arch,
158 path: v,
159 })
160 .into_side_effect(),
161 );
162 }
163
164 let injected_env = ctx.reqv(|v| crate::init_cross_build::Request {
165 target: target.clone(),
166 injected_env: v,
167 });
168
169 let extra_env = if let Some(extra_env) = extra_env {
170 extra_env
171 .zip(ctx, injected_env)
172 .map(ctx, move |(mut a, b)| {
173 a.extend(b);
174 a
175 })
176 } else {
177 injected_env
178 };
179
180 let mut config = Vec::new();
181
182 let passed_target = if target.vendor.as_str() == "minimal_rt" {
186 config.push(format!(
187 "openhcl/minimal_rt/{arch}-config.toml",
188 arch = target.architecture.into_str()
189 ));
190 if target.architecture == target_lexicon::Architecture::X86_64 {
191 target.vendor = target_lexicon::Vendor::Unknown;
195 Some(target.clone())
196 } else {
197 ctx.req(flowey_lib_common::install_rust::Request::InstallComponent(
201 "rust-src".into(),
202 ));
203 None
204 }
205 } else {
206 Some(target.clone())
207 };
208
209 let base_output = ctx.reqv(|v| flowey_lib_common::run_cargo_build::Request {
210 in_folder: openvmm_repo_path.clone(),
211 crate_name,
212 out_name,
213 profile: match profile {
214 BuildProfile::Debug => CargoBuildProfile::Debug,
215 BuildProfile::Release => CargoBuildProfile::Release,
216 BuildProfile::UnderhillShip => {
217 CargoBuildProfile::Custom("underhill-ship".into())
218 }
219 BuildProfile::BootDev => CargoBuildProfile::Custom("boot-dev".into()),
220 BuildProfile::BootRelease => CargoBuildProfile::Custom("boot-release".into()),
221 BuildProfile::Light => CargoBuildProfile::Custom("light".into()),
222 },
223 features,
224 output_kind: crate_type,
225 target: passed_target,
226 extra_env: Some(extra_env),
227 config,
228 pre_build_deps,
229 output: v,
230 });
231
232 if !no_split_dbg_info
233 && matches!(
234 (crate_type, target.operating_system),
235 (
236 CargoCrateType::Bin,
237 target_lexicon::OperatingSystem::Linux
238 | target_lexicon::OperatingSystem::None_
239 )
240 )
241 {
242 let elf_bin = base_output.clone().map(ctx, |o| match o {
243 flowey_lib_common::run_cargo_build::CargoBuildOutput::ElfBin { bin } => bin,
244 _ => unreachable!(),
245 });
246
247 let (out_bin, write_out_bin) = ctx.new_var();
248 let (out_dbg, write_out_dbg) = ctx.new_var();
249
250 ctx.req(crate::run_split_debug_info::Request {
251 arch: crate::common::CommonArch::from_architecture(target.architecture)
252 .context("cannot split linux dbginfo on specified arch")?,
253 in_bin: elf_bin,
254 out_bin: write_out_bin,
255 out_dbg_info: write_out_dbg,
256 reproducible_without_debuglink: matches!(
257 ctx.platform(),
258 FlowPlatform::Linux(FlowPlatformLinuxDistro::Nix)
259 ),
260 });
261
262 ctx.emit_minor_rust_step("reporting split debug info", |ctx| {
263 let out_bin = out_bin.claim(ctx);
264 let out_dbg = out_dbg.claim(ctx);
265 let base_output = base_output.claim(ctx);
266 let output = output.claim(ctx);
267
268 move |rt| {
269 let mut fixed = CargoBuildOutput::from_base_cargo_build_output(
270 rt.read(base_output),
271 Some(rt.read(out_dbg)),
272 );
273 let CargoBuildOutput::ElfBin { bin, .. } = &mut fixed else {
274 unreachable!()
275 };
276 *bin = rt.read(out_bin);
277 rt.write(output, &fixed);
278 }
279 });
280 } else {
281 base_output.write_into_with(ctx, output, |o| {
282 CargoBuildOutput::from_base_cargo_build_output(o, None)
283 });
284 }
285 }
286
287 Ok(())
288 }
289}