diff --git a/xtask/src/argument_parsing.rs b/xtask/src/argument_parsing.rs index 3a89dfc531..34e0064d52 100644 --- a/xtask/src/argument_parsing.rs +++ b/xtask/src/argument_parsing.rs @@ -113,7 +113,7 @@ pub enum Backends { impl Backends { #[allow(clippy::wrong_self_convention)] - pub fn to_target(&self) -> Target { + pub fn to_target(&self) -> Target<'static> { match self { Backends::Thumbv6 => ARMV6M, Backends::Thumbv7 => ARMV7M, @@ -123,7 +123,7 @@ impl Backends { } #[allow(clippy::wrong_self_convention)] - pub fn to_rtic_feature(&self) -> &str { + pub fn to_rtic_feature(&self) -> &'static str { match self { Backends::Thumbv6 => "thumbv6-backend", Backends::Thumbv7 => "thumbv7-backend", @@ -132,14 +132,14 @@ impl Backends { } } #[allow(clippy::wrong_self_convention)] - pub fn to_rtic_macros_feature(&self) -> &str { + pub fn to_rtic_macros_feature(&self) -> &'static str { match self { Backends::Thumbv6 | Backends::Thumbv8Base => "cortex-m-source-masking", Backends::Thumbv7 | Backends::Thumbv8Main => "cortex-m-basepri", } } #[allow(clippy::wrong_self_convention)] - pub fn to_rtic_uitest_feature(&self) -> &str { + pub fn to_rtic_uitest_feature(&self) -> &'static str { match self { Backends::Thumbv6 | Backends::Thumbv8Base => "rtic-uitestv6", Backends::Thumbv7 | Backends::Thumbv8Main => "rtic-uitestv7", @@ -205,9 +205,6 @@ pub struct Cli { #[derive(Debug, Clone, Subcommand)] pub enum Commands { - /// Check formatting - FormatCheck(PackageOpt), - /// Format code #[clap(alias = "fmt")] Format(FormatOpt), @@ -270,9 +267,9 @@ pub enum Commands { pub struct FormatOpt { #[clap(flatten)] pub package: PackageOpt, - /// Only check formatting, without applying fixes. - #[clap(short, long, alias = "check-only")] - pub check: bool, + /// Apply formatting fixes immediately. + #[clap(short, long)] + pub apply: bool, } #[derive(Args, Debug, Clone)] diff --git a/xtask/src/cargo_commands.rs b/xtask/src/cargo_commands.rs index 26fb3148be..ec5fcfaa53 100644 --- a/xtask/src/cargo_commands.rs +++ b/xtask/src/cargo_commands.rs @@ -3,14 +3,14 @@ use crate::{ command::{BuildMode, CargoCommand}, command_parser, RunResult, }; -use log::{error, info, Level}; +use log::error; #[cfg(feature = "rayon")] use rayon::prelude::*; use iters::*; -enum FinalRunResult<'c> { +pub enum FinalRunResult<'c> { Success(CargoCommand<'c>, RunResult), Failed(CargoCommand<'c>, RunResult), CommandError(anyhow::Error), @@ -36,71 +36,10 @@ fn run_and_convert<'a>( } } -fn handle_results(results: Vec) -> 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, - "Output for \"{command}\"\nStdout:\n{stdout}\nStderr:\n{stderr}" - ); - } else if !stdout.is_empty() { - log::log!( - level, - "Output for \"{command}\":\nStdout:\n{}", - stdout.trim_end() - ); - } else if !stderr.is_empty() { - log::log!( - level, - "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!("Success: {cmd}"); - }); - - errors.clone().for_each(|(cmd, _)| { - error!("Failed: {cmd}"); - }); - - let ecount = errors.count(); - if ecount != 0 { - Err(anyhow::anyhow!("{ecount} commands failed.")) - } else { - Ok(()) - } -} - -pub trait CoalescingRunning { +pub trait CoalescingRunner<'c> { /// 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<()>; + fn run_and_coalesce(self) -> Vec>; } #[cfg(not(feature = "rayon"))] @@ -111,13 +50,12 @@ mod iters { examples.into_iter() } - impl<'g, 'c, I> CoalescingRunning for I + impl<'g, 'c, I> CoalescingRunner<'c> for I where I: Iterator, bool)>, { - fn run_and_coalesce(self) -> anyhow::Result<()> { - let results: Vec<_> = self.map(run_and_convert).collect(); - handle_results(results) + fn run_and_coalesce(self) -> Vec> { + self.map(run_and_convert).collect() } } } @@ -130,28 +68,26 @@ mod iters { examples.into_par_iter() } - impl<'g, 'c, I> CoalescingRunning for I + impl<'g, 'c, I> CoalescingRunner<'c> for I where I: ParallelIterator, bool)>, { - fn run_and_coalesce(self) -> anyhow::Result<()> { - let results: Vec<_> = self.map(run_and_convert).collect(); - handle_results(results) + fn run_and_coalesce(self) -> Vec> { + self.map(run_and_convert).collect() } } } /// Cargo command to either build or check -pub fn cargo( +pub fn cargo<'c>( globals: &Globals, operation: BuildOrCheck, - cargoarg: &Option<&str>, - package: &PackageOpt, + cargoarg: &'c Option<&'c str>, + package: &'c PackageOpt, backend: Backends, -) -> anyhow::Result<()> { - let runner = package.packages().map(|package| { +) -> Vec> { + let runner = package.packages().map(move |package| { let target = backend.to_target(); - let features = package.extract_features(target, backend); let command = match operation { @@ -180,13 +116,13 @@ pub fn cargo( /// Cargo command to either build or check all examples /// /// The examples are in rtic/examples -pub fn cargo_example( +pub fn cargo_example<'c>( globals: &Globals, operation: BuildOrCheck, - cargoarg: &Option<&str>, + cargoarg: &'c Option<&'c str>, backend: Backends, - examples: &[String], -) -> anyhow::Result<()> { + examples: &'c [String], +) -> Vec> { let runner = examples_iter(examples).map(|example| { let features = Some(backend.to_target().and_features(backend.to_rtic_feature())); @@ -212,12 +148,12 @@ pub fn cargo_example( } /// Run cargo clippy on selected package -pub fn cargo_clippy( +pub fn cargo_clippy<'c>( globals: &Globals, - cargoarg: &Option<&str>, - package: &PackageOpt, + cargoarg: &'c Option<&'c str>, + package: &'c PackageOpt, backend: Backends, -) -> anyhow::Result<()> { +) -> Vec> { let runner = package.packages().map(|p| { let target = backend.to_target(); let features = p.extract_features(target, backend); @@ -238,12 +174,12 @@ pub fn cargo_clippy( } /// Run cargo fmt on selected package -pub fn cargo_format( +pub fn cargo_format<'c>( globals: &Globals, - cargoarg: &Option<&str>, - package: &PackageOpt, + cargoarg: &'c Option<&'c str>, + package: &'c PackageOpt, check_only: bool, -) -> anyhow::Result<()> { +) -> Vec> { let runner = package.packages().map(|p| { ( globals, @@ -259,34 +195,31 @@ pub fn cargo_format( } /// Run cargo doc -pub fn cargo_doc( +pub fn cargo_doc<'c>( globals: &Globals, - cargoarg: &Option<&str>, + cargoarg: &'c Option<&'c str>, backend: Backends, - arguments: &Option, -) -> anyhow::Result<()> { + arguments: &'c Option, +) -> Vec> { let features = Some(backend.to_target().and_features(backend.to_rtic_feature())); - command_parser( - globals, - &CargoCommand::Doc { - cargoarg, - features, - arguments: arguments.clone(), - }, - false, - )?; - Ok(()) + let command = CargoCommand::Doc { + cargoarg, + features, + arguments: arguments.clone(), + }; + + vec![run_and_convert((globals, command, false))] } /// Run cargo test on the selected package or all packages /// /// If no package is specified, loop through all packages -pub fn cargo_test( +pub fn cargo_test<'c>( globals: &Globals, - package: &PackageOpt, + package: &'c PackageOpt, backend: Backends, -) -> anyhow::Result<()> { +) -> Vec> { package .packages() .map(|p| (globals, TestMetadata::match_package(p, backend), false)) @@ -294,29 +227,29 @@ pub fn cargo_test( } /// Use mdbook to build the book -pub fn cargo_book( +pub fn cargo_book<'c>( globals: &Globals, - arguments: &Option, -) -> anyhow::Result { - command_parser( + arguments: &'c Option, +) -> Vec> { + vec![run_and_convert(( globals, - &CargoCommand::Book { + CargoCommand::Book { arguments: arguments.clone(), }, false, - ) + ))] } /// Run examples /// /// Supports updating the expected output via the overwrite argument -pub fn run_test( +pub fn run_test<'c>( globals: &Globals, - cargoarg: &Option<&str>, + cargoarg: &'c Option<&'c str>, backend: Backends, - examples: &[String], + examples: &'c [String], overwrite: bool, -) -> anyhow::Result<()> { +) -> Vec> { let target = backend.to_target(); let features = Some(target.and_features(backend.to_rtic_feature())); @@ -348,13 +281,13 @@ pub fn run_test( } /// Check the binary sizes of examples -pub fn build_and_check_size( +pub fn build_and_check_size<'c>( globals: &Globals, - cargoarg: &Option<&str>, + cargoarg: &'c Option<&'c str>, backend: Backends, - examples: &[String], - arguments: &Option, -) -> anyhow::Result<()> { + examples: &'c [String], + arguments: &'c Option, +) -> Vec> { let target = backend.to_target(); let features = Some(target.and_features(backend.to_rtic_feature())); diff --git a/xtask/src/command.rs b/xtask/src/command.rs index 99b7518557..71dda2b83b 100644 --- a/xtask/src/command.rs +++ b/xtask/src/command.rs @@ -1,4 +1,8 @@ -use crate::{debug, ExtraArguments, Package, RunResult, Target, TestRunError}; +use log::{error, info, Level}; + +use crate::{ + cargo_commands::FinalRunResult, ExtraArguments, Package, RunResult, Target, TestRunError, +}; use core::fmt; use std::{ fs::File, @@ -279,7 +283,7 @@ impl core::fmt::Display for CargoCommand<'_> { .map(|t| format!("test {t}")) .unwrap_or("all tests".into()); let feat = feat(features); - write!(f, "Run {test} in {p} ({feat})") + write!(f, "Run {test} in {p} (features: {feat})") } CargoCommand::Book { arguments: _ } => write!(f, "Build the book"), CargoCommand::ExampleSize { @@ -652,7 +656,7 @@ impl fmt::Display for BuildMode { } pub fn run_command(command: &CargoCommand, stderr_mode: OutputMode) -> anyhow::Result { - debug!("👟 {command}"); + log::info!("👟 {command}"); let result = Command::new(command.executable()) .args(command.args()) @@ -697,3 +701,64 @@ pub fn run_successful(run: &RunResult, expected_output_file: &str) -> Result<(), Ok(()) } } + +pub fn handle_results(results: Vec) -> 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, + "Output for \"{command}\"\nStdout:\n{stdout}\nStderr:\n{stderr}" + ); + } else if !stdout.is_empty() { + log::log!( + level, + "Output for \"{command}\":\nStdout:\n{}", + stdout.trim_end() + ); + } else if !stderr.is_empty() { + log::log!( + level, + "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!("✅ Success: {cmd}"); + }); + + errors.clone().for_each(|(cmd, _)| { + error!("❌ Failed: {cmd}"); + }); + + let ecount = errors.count(); + if ecount != 0 { + Err(anyhow::anyhow!("{ecount} commands failed.")) + } else { + Ok(()) + } +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index ae0f5edacd..b572591fd4 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -18,7 +18,7 @@ use std::{ str, }; -use log::{debug, error, info, log_enabled, trace, Level}; +use log::{error, info, log_enabled, trace, Level}; use crate::{ argument_parsing::{Backends, BuildOrCheck, Cli, Commands}, @@ -27,7 +27,7 @@ use crate::{ build_and_check_size, cargo, cargo_book, cargo_clippy, cargo_doc, cargo_example, cargo_format, cargo_test, run_test, }, - command::{run_command, run_successful, CargoCommand}, + command::{handle_results, run_command, run_successful, CargoCommand}, }; #[derive(Debug, Clone, Copy)] @@ -217,27 +217,19 @@ fn main() -> anyhow::Result<()> { Some("--quiet") }; - match cli.command { - Commands::FormatCheck(args) => { - info!("Running cargo fmt --check: {args:?}"); - let check_only = true; - cargo_format(globals, &cargologlevel, &args, check_only)?; - } - Commands::Format(args) => { - info!("Running cargo fmt: {args:?}"); - cargo_format(globals, &cargologlevel, &args.package, args.check)?; - } + let final_run_results = match &cli.command { + Commands::Format(args) => cargo_format(globals, &cargologlevel, &args.package, !args.apply), Commands::Clippy(args) => { info!("Running clippy on backend: {backend:?}"); - cargo_clippy(globals, &cargologlevel, &args, backend)?; + cargo_clippy(globals, &cargologlevel, &args, backend) } Commands::Check(args) => { info!("Checking on backend: {backend:?}"); - cargo(globals, BuildOrCheck::Check, &cargologlevel, &args, backend)?; + cargo(globals, BuildOrCheck::Check, &cargologlevel, &args, backend) } Commands::Build(args) => { info!("Building for backend: {backend:?}"); - cargo(globals, BuildOrCheck::Build, &cargologlevel, &args, backend)?; + cargo(globals, BuildOrCheck::Build, &cargologlevel, &args, backend) } Commands::ExampleCheck => { info!("Checking on backend: {backend:?}"); @@ -247,7 +239,7 @@ fn main() -> anyhow::Result<()> { &cargologlevel, backend, &examples_to_run, - )?; + ) } Commands::ExampleBuild => { info!("Building for backend: {backend:?}"); @@ -257,7 +249,7 @@ fn main() -> anyhow::Result<()> { &cargologlevel, backend, &examples_to_run, - )?; + ) } Commands::Size(args) => { // x86_64 target not valid @@ -268,7 +260,7 @@ fn main() -> anyhow::Result<()> { backend, &examples_to_run, &args.arguments, - )?; + ) } Commands::Qemu(args) | Commands::Run(args) => { // x86_64 target not valid @@ -279,23 +271,23 @@ fn main() -> anyhow::Result<()> { backend, &examples_to_run, args.overwrite_expected, - )?; + ) } Commands::Doc(args) => { info!("Running cargo doc on backend: {backend:?}"); - cargo_doc(globals, &cargologlevel, backend, &args.arguments)?; + cargo_doc(globals, &cargologlevel, backend, &args.arguments) } Commands::Test(args) => { info!("Running cargo test on backend: {backend:?}"); - cargo_test(globals, &args, backend)?; + cargo_test(globals, &args, backend) } Commands::Book(args) => { info!("Running mdbook"); - cargo_book(globals, &args.arguments)?; + cargo_book(globals, &args.arguments) } - } + }; - Ok(()) + handle_results(final_run_results) } // run example binary `example`