diff options
Diffstat (limited to 'xtask/src/cargo_commands.rs')
| -rw-r--r-- | xtask/src/cargo_commands.rs | 261 |
1 files changed, 187 insertions, 74 deletions
diff --git a/xtask/src/cargo_commands.rs b/xtask/src/cargo_commands.rs index 2a15b3c..e88f31e 100644 --- a/xtask/src/cargo_commands.rs +++ b/xtask/src/cargo_commands.rs @@ -1,11 +1,145 @@ use crate::{ argument_parsing::{Backends, BuildOrCheck, ExtraArguments, Globals, PackageOpt, TestMetadata}, command::{BuildMode, CargoCommand}, - command_parser, + command_parser, RunResult, }; -use log::error; +use log::{error, info, Level}; + +#[cfg(feature = "rayon")] use rayon::prelude::*; +use iters::*; + +enum FinalRunResult<'c> { + Success(CargoCommand<'c>, RunResult), + Failed(CargoCommand<'c>, RunResult), + CommandError(anyhow::Error), +} + +fn run_and_convert<'a>( + (global, command, overwrite): (&Globals, CargoCommand<'a>, bool), +) -> FinalRunResult<'a> { + // Run the command + let result = command_parser(global, &command, overwrite); + match result { + // If running the command succeeded without looking at any of the results, + // log the data and see if the actual execution was succesfull too. + Ok(result) => { + if result.exit_status.success() { + FinalRunResult::Success(command, result) + } else { + FinalRunResult::Failed(command, result) + } + } + // If it didn't and some IO error occured, just panic + Err(e) => FinalRunResult::CommandError(e), + } +} + +fn handle_results(results: Vec<FinalRunResult>) -> anyhow::Result<()> { + let errors = results.iter().filter_map(|r| { + if let FinalRunResult::Failed(c, r) = r { + Some((c, r)) + } else { + None + } + }); + + let successes = results.iter().filter_map(|r| { + if let FinalRunResult::Success(c, r) = r { + Some((c, r)) + } else { + None + } + }); + + let log_stdout_stderr = |level: Level| { + move |(command, result): (&CargoCommand, &RunResult)| { + let stdout = &result.stdout; + let stderr = &result.stderr; + if !stdout.is_empty() && !stderr.is_empty() { + log::log!( + level, + "Command output for {command}\nStdout:\n{stdout}\nStderr:\n{stderr}" + ); + } else if !stdout.is_empty() { + log::log!( + level, + "Command output for {command}\nStdout:\n{}", + stdout.trim_end() + ); + } else if !stderr.is_empty() { + log::log!( + level, + "Command output for {command}\nStderr:\n{}", + stderr.trim_end() + ); + } + } + }; + + successes.clone().for_each(log_stdout_stderr(Level::Debug)); + errors.clone().for_each(log_stdout_stderr(Level::Error)); + + successes.for_each(|(cmd, _)| { + info!("Succesfully executed {cmd}"); + }); + + errors.clone().for_each(|(cmd, _)| { + error!("Command {cmd} failed"); + }); + + if errors.count() != 0 { + Err(anyhow::anyhow!("Some commands failed.")) + } else { + Ok(()) + } +} + +pub trait CoalescingRunning { + /// Run all the commands in this iterator, and coalesce the results into + /// one error (if any individual commands failed) + fn run_and_coalesce(self) -> anyhow::Result<()>; +} + +#[cfg(not(feature = "rayon"))] +mod iters { + use super::*; + + pub fn examples_iter(examples: &[String]) -> impl Iterator<Item = &String> { + examples.into_iter() + } + + impl<'g, 'c, I> CoalescingRunning for I + where + I: Iterator<Item = (&'g Globals, CargoCommand<'c>, bool)>, + { + fn run_and_coalesce(self) -> anyhow::Result<()> { + let results: Vec<_> = self.map(run_and_convert).collect(); + handle_results(results) + } + } +} + +#[cfg(feature = "rayon")] +mod iters { + use super::*; + + pub fn examples_iter(examples: &[String]) -> impl ParallelIterator<Item = &String> { + examples.into_par_iter() + } + + impl<'g, 'c, I> CoalescingRunning for I + where + I: ParallelIterator<Item = (&'g Globals, CargoCommand<'c>, bool)>, + { + fn run_and_coalesce(self) -> anyhow::Result<()> { + let results: Vec<_> = self.map(run_and_convert).collect(); + handle_results(results) + } + } +} + /// Cargo command to either build or check pub fn cargo( globals: &Globals, @@ -14,7 +148,7 @@ pub fn cargo( package: &PackageOpt, backend: Backends, ) -> anyhow::Result<()> { - package.packages().for_each(|package| { + let runner = package.packages().map(|package| { let target = backend.to_target(); let features = package.extract_features(target, backend); @@ -44,13 +178,11 @@ pub fn cargo( mode: BuildMode::Release, }, }; - let res = command_parser(globals, &command, false); - if let Err(e) = res { - error!("{e}"); - } + + (globals, command, false) }); - Ok(()) + runner.run_and_coalesce() } /// Cargo command to either build or check all examples @@ -63,7 +195,7 @@ pub fn cargo_example( backend: Backends, examples: &[String], ) -> anyhow::Result<()> { - examples.into_par_iter().for_each(|example| { + let runner = examples_iter(examples).map(|example| { let features = Some(backend.to_target().and_features(backend.to_rtic_feature())); let command = match operation { @@ -82,13 +214,9 @@ pub fn cargo_example( mode: BuildMode::Release, }, }; - - if let Err(err) = command_parser(globals, &command, false) { - error!("{err}"); - } + (globals, command, false) }); - - Ok(()) + runner.run_and_coalesce() } /// Run cargo clippy on selected package @@ -98,27 +226,23 @@ pub fn cargo_clippy( package: &PackageOpt, backend: Backends, ) -> anyhow::Result<()> { - package.packages().for_each(|p| { + let runner = package.packages().map(|p| { let target = backend.to_target(); let features = p.extract_features(target, backend); - let res = command_parser( + ( globals, - &CargoCommand::Clippy { + CargoCommand::Clippy { cargoarg, package: Some(p), target, features, }, false, - ); - - if let Err(e) = res { - error!("{e}") - } + ) }); - Ok(()) + runner.run_and_coalesce() } /// Run cargo fmt on selected package @@ -128,23 +252,18 @@ pub fn cargo_format( package: &PackageOpt, check_only: bool, ) -> anyhow::Result<()> { - package.packages().for_each(|p| { - let res = command_parser( + let runner = package.packages().map(|p| { + ( globals, - &CargoCommand::Format { + CargoCommand::Format { cargoarg, package: Some(p), check_only, }, false, - ); - - if let Err(e) = res { - error!("{e}") - } + ) }); - - Ok(()) + runner.run_and_coalesce() } /// Run cargo doc @@ -176,26 +295,24 @@ pub fn cargo_test( package: &PackageOpt, backend: Backends, ) -> anyhow::Result<()> { - package.packages().for_each(|p| { - let cmd = &TestMetadata::match_package(p, backend); - if let Err(err) = command_parser(globals, cmd, false) { - error!("{err}") - } - }); - - Ok(()) + package + .packages() + .map(|p| (globals, TestMetadata::match_package(p, backend), false)) + .run_and_coalesce() } /// Use mdbook to build the book -pub fn cargo_book(globals: &Globals, arguments: &Option<ExtraArguments>) -> anyhow::Result<()> { +pub fn cargo_book( + globals: &Globals, + arguments: &Option<ExtraArguments>, +) -> anyhow::Result<RunResult> { command_parser( globals, &CargoCommand::Book { arguments: arguments.clone(), }, false, - )?; - Ok(()) + ) } /// Run examples @@ -211,33 +328,31 @@ pub fn run_test( let target = backend.to_target(); let features = Some(target.and_features(backend.to_rtic_feature())); - examples.into_par_iter().for_each(|example| { - let cmd = CargoCommand::ExampleBuild { - cargoarg: &Some("--quiet"), - example, - target, - features: features.clone(), - mode: BuildMode::Release, - }; - - if let Err(err) = command_parser(globals, &cmd, false) { - error!("{err}"); - } + examples_iter(examples) + .map(|example| { + let cmd = CargoCommand::ExampleBuild { + cargoarg: &Some("--quiet"), + example, + target, + features: features.clone(), + mode: BuildMode::Release, + }; - let cmd = CargoCommand::Qemu { - cargoarg, - example, - target, - features: features.clone(), - mode: BuildMode::Release, - }; + if let Err(err) = command_parser(globals, &cmd, false) { + error!("{err}"); + } - if let Err(err) = command_parser(globals, &cmd, overwrite) { - error!("{err}"); - } - }); + let cmd = CargoCommand::Qemu { + cargoarg, + example, + target, + features: features.clone(), + mode: BuildMode::Release, + }; - Ok(()) + (globals, cmd, overwrite) + }) + .run_and_coalesce() } /// Check the binary sizes of examples @@ -251,7 +366,7 @@ pub fn build_and_check_size( let target = backend.to_target(); let features = Some(target.and_features(backend.to_rtic_feature())); - examples.into_par_iter().for_each(|example| { + let runner = examples_iter(examples).map(|example| { // Make sure the requested example(s) are built let cmd = CargoCommand::ExampleBuild { cargoarg: &Some("--quiet"), @@ -272,10 +387,8 @@ pub fn build_and_check_size( mode: BuildMode::Release, arguments: arguments.clone(), }; - if let Err(err) = command_parser(globals, &cmd, false) { - error!("{err}"); - } + (globals, cmd, false) }); - Ok(()) + runner.run_and_coalesce() } |
