1use log::error;
2use rayon::prelude::*;
3
4use std::collections::HashMap;
5use std::path::PathBuf;
6use std::sync::Arc;
7
8use crate::args::{BuildSystem, Platform, TargetLanguage};
9use crate::package::{
10 management::DependencyManager, target_properties::MergeTargetProperties, App, Config,
11 OUTPUT_DIRECTORY,
12};
13use crate::util::errors::{AnyError, BuildResult, LingoError};
14use crate::{GitCloneAndCheckoutCap, WhichCapability};
15
16pub mod cmake_c;
17pub mod cmake_cpp;
18pub mod lfc;
19pub mod npm;
20pub mod pnpm;
21
22#[allow(clippy::single_match)] pub fn execute_command<'a>(
24 command: &CommandSpec,
25 config: &'a mut Config,
26 which: WhichCapability,
27 clone: GitCloneAndCheckoutCap,
28) -> BatchBuildResults<'a> {
29 let mut result = BatchBuildResults::new();
30 let dependencies = Vec::from_iter(config.dependencies.clone());
31
32 match command {
33 CommandSpec::Build(_build) => {
34 let manager = match DependencyManager::from_dependencies(
35 dependencies.clone(),
36 &PathBuf::from(OUTPUT_DIRECTORY),
37 &clone,
38 ) {
39 Ok(value) => value,
40 Err(e) => {
41 error!("failed to create dependency manager because of {e}");
42 return result;
43 }
44 };
45
46 let library_properties = manager.get_target_properties().expect("lib properties");
48
49 for app in &mut config.apps {
51 if let Err(e) = app.properties.merge(&library_properties) {
52 error!("cannot merge properties from the libraries with the app. error: {e}");
53 return result;
54 }
55 }
56 }
57 _ => {}
58 }
59
60 let mut by_build_system = HashMap::<(BuildSystem, TargetLanguage), Vec<&App>>::new();
62 for app in &config.apps {
63 by_build_system
64 .entry((app.build_system(&which), app.target))
65 .or_default()
66 .push(app);
67 }
68
69 for (build_system, apps) in by_build_system {
70 let mut sub_res = BatchBuildResults::for_apps(&apps);
71
72 sub_res.map(|app| {
73 if app.platform == Platform::Zephyr {
75 Err(Box::new(LingoError::UseWestBuildToBuildApp))
76 } else {
77 Ok(())
78 }
79 });
80
81 match build_system {
82 (BuildSystem::CMake, TargetLanguage::Cpp) => {
83 cmake_cpp::CmakeCpp.execute_command(command, &mut sub_res)
84 }
85 (BuildSystem::CMake, TargetLanguage::C) => {
86 cmake_c::CmakeC.execute_command(command, &mut sub_res)
87 }
88 (BuildSystem::Npm, TargetLanguage::TypeScript) => {
89 npm::Npm.execute_command(command, &mut sub_res)
90 }
91 (BuildSystem::Pnpm, TargetLanguage::TypeScript) => {
92 pnpm::Pnpm.execute_command(command, &mut sub_res)
93 }
94 (BuildSystem::LFC, _) => lfc::LFC.execute_command(command, &mut sub_res),
95 (BuildSystem::Cargo, _) => todo!(),
96 _ => {
97 error!("invalid combination of target and platform!");
98 todo!()
99 }
100 };
101 result.append(sub_res);
102 }
103 result
104}
105
106#[derive(Copy, Clone, Eq, PartialEq)]
107pub enum BuildProfile {
108 Release,
110 Debug,
112}
113
114pub struct BuildCommandOptions {
115 pub profile: BuildProfile,
117 pub compile_target_code: bool,
119 pub lfc_exec_path: PathBuf,
121 pub max_threads: usize,
125 pub keep_going: bool,
127}
128
129pub enum CommandSpec {
131 Build(BuildCommandOptions),
133 Update,
135 Clean,
137}
138
139pub trait BatchBackend {
141 fn execute_command(&mut self, command: &CommandSpec, results: &mut BatchBuildResults);
143}
144
145pub struct BatchBuildResults<'a> {
147 results: Vec<(&'a App, BuildResult)>,
148 keep_going: bool,
149}
150
151impl<'a> BatchBuildResults<'a> {
152 fn new() -> Self {
154 Self {
155 results: Vec::new(),
156 keep_going: false,
157 }
158 }
159
160 fn for_apps(apps: &[&'a App]) -> Self {
163 Self {
164 results: apps.iter().map(|&a| (a, Ok(()))).collect(),
165 keep_going: false,
166 }
167 }
168 fn keep_going(&mut self, value: bool) {
170 self.keep_going = value
171 }
172
173 pub fn print_results(&self) {
175 for (app, b) in &self.results {
176 match b {
177 Ok(()) => {
178 log::info!("- {}: Success", &app.name);
179 }
180 Err(e) => {
181 log::error!("- {}: Error: {}", &app.name, e);
182 }
183 }
184 }
185 }
186
187 fn append(&mut self, mut other: BatchBuildResults<'a>) {
190 self.results.append(&mut other.results);
191 self.results.sort_by_key(|(app, _)| &app.name);
192 }
193
194 pub fn map<F>(&mut self, f: F) -> &mut Self
201 where
202 F: Fn(&'a App) -> BuildResult,
203 {
204 self.results.iter_mut().for_each(|(app, res)| {
205 if let Ok(()) = res {
206 *res = f(app);
207
208 if (*res).is_err() && !self.keep_going {
209 panic!(
210 "build step failed because of {} with main reactor {}!",
211 &app.name,
212 &app.main_reactor.display()
213 );
214 }
215 }
216 });
217 self
218 }
219
220 pub fn par_map<F>(&mut self, f: F) -> &mut Self
223 where
224 F: Fn(&'a App) -> BuildResult + Send + Sync,
225 {
226 self.results.par_iter_mut().for_each(|(app, res)| {
227 if let Ok(()) = res {
228 *res = f(app);
229
230 if (*res).is_err() && !self.keep_going {
231 panic!(
232 "build step failed with error {:?} because of app {} with main reactor {}!",
233 &res,
234 &app.name,
235 &app.main_reactor.display()
236 );
237 }
238 }
239 });
240 self
241 }
242
243 pub fn gather<F>(&mut self, f: F) -> &mut Self
247 where
248 F: FnOnce(&Vec<&'a App>) -> BuildResult,
249 {
250 let vec: Vec<&'a App> = self
252 .results
253 .iter()
254 .filter_map(|&(app, ref res)| res.as_ref().ok().map(|()| app))
255 .collect();
256
257 if vec.is_empty() {
258 return self;
259 }
260
261 match f(&vec) {
262 Ok(()) => { }
263 Err(e) => {
264 if !self.keep_going {
265 panic!("build step failed!");
266 }
267
268 let shared: Arc<AnyError> = e.into();
270 for (_app, res) in &mut self.results {
271 if let Ok(()) = res {
272 *res = Err(Box::new(LingoError::Shared(shared.clone())));
273 }
274 }
275 }
276 }
277 self
278 }
279}