diff --git a/xtask/src/argument_parsing.rs b/xtask/src/argument_parsing.rs index c0538e204e..eda0a89c24 100644 --- a/xtask/src/argument_parsing.rs +++ b/xtask/src/argument_parsing.rs @@ -130,10 +130,8 @@ pub enum BuildOrCheck { Build, } -#[derive(Parser)] -#[command(author, version, about, long_about = None)] -/// RTIC xtask powered testing toolbox -pub struct Cli { +#[derive(Parser, Clone)] +pub struct Globals { /// For which backend to build (defaults to thumbv7) #[arg(value_enum, short, long)] pub backend: Option, @@ -160,12 +158,28 @@ pub struct Cli { #[arg(short, long, action = clap::ArgAction::Count)] pub verbose: u8, + /// Enable `stderr` inheritance on child processes. + /// + /// If this flag is enabled, the output of `stderr` produced by child + /// processes is printed directly to `stderr`. This will cause a lot of + /// clutter, but can make debugging long-running processes a lot easier. + #[arg(short, long)] + pub stderr_inherited: bool, +} + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +/// RTIC xtask powered testing toolbox +pub struct Cli { + #[clap(flatten)] + pub globals: Globals, + /// Subcommand selecting operation #[command(subcommand)] pub command: Commands, } -#[derive(Debug, Subcommand)] +#[derive(Debug, Clone, Subcommand)] pub enum Commands { /// Check formatting FormatCheck(PackageOpt), @@ -227,7 +241,7 @@ pub enum Commands { Book(Arg), } -#[derive(Args, Debug)] +#[derive(Args, Debug, Clone)] /// Restrict to package, or run on whole workspace pub struct PackageOpt { /// For which package/workspace member to operate @@ -236,7 +250,7 @@ pub struct PackageOpt { pub package: Option, } -#[derive(Args, Debug)] +#[derive(Args, Debug, Clone)] pub struct QemuAndRun { /// If expected output is missing or mismatching, recreate the file /// @@ -245,7 +259,7 @@ pub struct QemuAndRun { pub overwrite_expected: bool, } -#[derive(Debug, Parser)] +#[derive(Debug, Parser, Clone)] pub struct Arg { /// Options to pass to `cargo size` #[command(subcommand)] diff --git a/xtask/src/cargo_commands.rs b/xtask/src/cargo_commands.rs index 7ac7aea964..90a803776a 100644 --- a/xtask/src/cargo_commands.rs +++ b/xtask/src/cargo_commands.rs @@ -1,5 +1,7 @@ use crate::{ - argument_parsing::{Backends, BuildOrCheck, ExtraArguments, Package, PackageOpt, TestMetadata}, + argument_parsing::{ + Backends, BuildOrCheck, ExtraArguments, Globals, Package, PackageOpt, TestMetadata, + }, command::{BuildMode, CargoCommand}, command_parser, package_feature_extractor, DEFAULT_FEATURES, }; @@ -8,6 +10,7 @@ use rayon::prelude::*; /// Cargo command to either build or check pub fn cargo( + globals: &Globals, operation: BuildOrCheck, cargoarg: &Option<&str>, package: &PackageOpt, @@ -31,7 +34,7 @@ pub fn cargo( mode: BuildMode::Release, }, }; - command_parser(&command, false)?; + command_parser(globals, &command, false)?; Ok(()) } @@ -39,6 +42,7 @@ pub fn cargo( /// /// The examples are in rtic/examples pub fn cargo_example( + globals: &Globals, operation: BuildOrCheck, cargoarg: &Option<&str>, backend: Backends, @@ -68,7 +72,7 @@ pub fn cargo_example( }, }; - if let Err(err) = command_parser(&command, false) { + if let Err(err) = command_parser(globals, &command, false) { error!("{err}"); } }); @@ -78,12 +82,14 @@ pub fn cargo_example( /// Run cargo clippy on selected package pub fn cargo_clippy( + globals: &Globals, cargoarg: &Option<&str>, package: &PackageOpt, backend: Backends, ) -> anyhow::Result<()> { let features = package_feature_extractor(package, backend); command_parser( + globals, &CargoCommand::Clippy { cargoarg, package: package.package, @@ -97,11 +103,13 @@ pub fn cargo_clippy( /// Run cargo fmt on selected package pub fn cargo_format( + globals: &Globals, cargoarg: &Option<&str>, package: &PackageOpt, check_only: bool, ) -> anyhow::Result<()> { command_parser( + globals, &CargoCommand::Format { cargoarg, package: package.package, @@ -114,6 +122,7 @@ pub fn cargo_format( /// Run cargo doc pub fn cargo_doc( + globals: &Globals, cargoarg: &Option<&str>, backend: Backends, arguments: &Option, @@ -125,6 +134,7 @@ pub fn cargo_doc( )); command_parser( + globals, &CargoCommand::Doc { cargoarg, features, @@ -138,10 +148,14 @@ pub fn cargo_doc( /// Run cargo test on the selcted package or all packages /// /// If no package is specified, loop through all packages -pub fn cargo_test(package: &PackageOpt, backend: Backends) -> anyhow::Result<()> { +pub fn cargo_test( + globals: &Globals, + package: &PackageOpt, + backend: Backends, +) -> anyhow::Result<()> { if let Some(package) = package.package { let cmd = TestMetadata::match_package(package, backend); - command_parser(&cmd, false)?; + command_parser(globals, &cmd, false)?; } else { // Iterate over all workspace packages for package in [ @@ -154,7 +168,7 @@ pub fn cargo_test(package: &PackageOpt, backend: Backends) -> anyhow::Result<()> ] { let mut error_messages = vec![]; let cmd = &TestMetadata::match_package(package, backend); - if let Err(err) = command_parser(cmd, false) { + if let Err(err) = command_parser(globals, cmd, false) { error_messages.push(err); } @@ -169,8 +183,9 @@ pub fn cargo_test(package: &PackageOpt, backend: Backends) -> anyhow::Result<()> } /// Use mdbook to build the book -pub fn cargo_book(arguments: &Option) -> anyhow::Result<()> { +pub fn cargo_book(globals: &Globals, arguments: &Option) -> anyhow::Result<()> { command_parser( + globals, &CargoCommand::Book { arguments: arguments.clone(), }, @@ -183,6 +198,7 @@ pub fn cargo_book(arguments: &Option) -> anyhow::Result<()> { /// /// Supports updating the expected output via the overwrite argument pub fn run_test( + globals: &Globals, cargoarg: &Option<&str>, backend: Backends, examples: &[String], @@ -200,7 +216,7 @@ pub fn run_test( )), mode: BuildMode::Release, }; - if let Err(err) = command_parser(&cmd, false) { + if let Err(err) = command_parser(globals, &cmd, false) { error!("{err}"); } @@ -216,7 +232,7 @@ pub fn run_test( mode: BuildMode::Release, }; - if let Err(err) = command_parser(&cmd, overwrite) { + if let Err(err) = command_parser(globals, &cmd, overwrite) { error!("{err}"); } }); @@ -226,6 +242,7 @@ pub fn run_test( /// Check the binary sizes of examples pub fn build_and_check_size( + globals: &Globals, cargoarg: &Option<&str>, backend: Backends, examples: &[String], @@ -244,7 +261,7 @@ pub fn build_and_check_size( )), mode: BuildMode::Release, }; - if let Err(err) = command_parser(&cmd, false) { + if let Err(err) = command_parser(globals, &cmd, false) { error!("{err}"); } @@ -260,7 +277,7 @@ pub fn build_and_check_size( mode: BuildMode::Release, arguments: arguments.clone(), }; - if let Err(err) = command_parser(&cmd, false) { + if let Err(err) = command_parser(globals, &cmd, false) { error!("{err}"); } }); diff --git a/xtask/src/command.rs b/xtask/src/command.rs index 8a2e99b738..9fa5378dc4 100644 --- a/xtask/src/command.rs +++ b/xtask/src/command.rs @@ -13,6 +13,21 @@ pub enum BuildMode { Debug, } +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum OutputMode { + PipedAndCollected, + Inherited, +} + +impl From for Stdio { + fn from(value: OutputMode) -> Self { + match value { + OutputMode::PipedAndCollected => Stdio::piped(), + OutputMode::Inherited => Stdio::inherit(), + } + } +} + #[derive(Debug)] pub enum CargoCommand<'a> { // For future embedded-ci @@ -414,7 +429,7 @@ impl fmt::Display for BuildMode { } } -pub fn run_command(command: &CargoCommand) -> anyhow::Result { +pub fn run_command(command: &CargoCommand, stderr_mode: OutputMode) -> anyhow::Result { let command_display = command.executable(); let args = command.args(); @@ -425,7 +440,7 @@ pub fn run_command(command: &CargoCommand) -> anyhow::Result { let result = Command::new(command.executable()) .args(command.args()) .stdout(Stdio::piped()) - .stderr(Stdio::piped()) + .stderr(stderr_mode) .output()?; let exit_status = result.status; diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 30608b94b3..4cb38c2ece 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -4,8 +4,9 @@ mod cargo_commands; mod command; use anyhow::bail; -use argument_parsing::{ExtraArguments, Package}; +use argument_parsing::{ExtraArguments, Globals, Package}; use clap::Parser; +use command::OutputMode; use core::fmt; use diffy::{create_patch, PatchFormatter}; use std::{ @@ -109,7 +110,9 @@ fn main() -> anyhow::Result<()> { let cli = Cli::parse(); - let env_logger_default_level = match cli.verbose { + let globals = &cli.globals; + + let env_logger_default_level = match globals.verbose { 0 => Env::default().default_filter_or("info"), 1 => Env::default().default_filter_or("debug"), _ => Env::default().default_filter_or("trace"), @@ -119,16 +122,16 @@ fn main() -> anyhow::Result<()> { .format_timestamp(None) .init(); - trace!("default logging level: {0}", cli.verbose); + trace!("default logging level: {0}", globals.verbose); - let backend = if let Some(backend) = cli.backend { + let backend = if let Some(backend) = globals.backend { backend } else { Backends::default() }; - let example = cli.example; - let exampleexclude = cli.exampleexclude; + let example = globals.example.clone(); + let exampleexclude = globals.exampleexclude.clone(); let examples_to_run = { let mut examples_to_run = examples.clone(); @@ -190,28 +193,29 @@ fn main() -> anyhow::Result<()> { Commands::FormatCheck(args) => { info!("Running cargo fmt --check: {args:?}"); let check_only = true; - cargo_format(&cargologlevel, &args, check_only)?; + cargo_format(globals, &cargologlevel, &args, check_only)?; } Commands::Format(args) => { info!("Running cargo fmt: {args:?}"); let check_only = false; - cargo_format(&cargologlevel, &args, check_only)?; + cargo_format(globals, &cargologlevel, &args, check_only)?; } Commands::Clippy(args) => { info!("Running clippy on backend: {backend:?}"); - cargo_clippy(&cargologlevel, &args, backend)?; + cargo_clippy(globals, &cargologlevel, &args, backend)?; } Commands::Check(args) => { info!("Checking on backend: {backend:?}"); - cargo(BuildOrCheck::Check, &cargologlevel, &args, backend)?; + cargo(globals, BuildOrCheck::Check, &cargologlevel, &args, backend)?; } Commands::Build(args) => { info!("Building for backend: {backend:?}"); - cargo(BuildOrCheck::Build, &cargologlevel, &args, backend)?; + cargo(globals, BuildOrCheck::Build, &cargologlevel, &args, backend)?; } Commands::ExampleCheck => { info!("Checking on backend: {backend:?}"); cargo_example( + globals, BuildOrCheck::Check, &cargologlevel, backend, @@ -221,6 +225,7 @@ fn main() -> anyhow::Result<()> { Commands::ExampleBuild => { info!("Building for backend: {backend:?}"); cargo_example( + globals, BuildOrCheck::Build, &cargologlevel, backend, @@ -230,12 +235,19 @@ fn main() -> anyhow::Result<()> { Commands::Size(args) => { // x86_64 target not valid info!("Measuring for backend: {backend:?}"); - build_and_check_size(&cargologlevel, backend, &examples_to_run, &args.arguments)?; + build_and_check_size( + globals, + &cargologlevel, + backend, + &examples_to_run, + &args.arguments, + )?; } Commands::Qemu(args) | Commands::Run(args) => { // x86_64 target not valid info!("Testing for backend: {backend:?}"); run_test( + globals, &cargologlevel, backend, &examples_to_run, @@ -244,15 +256,15 @@ fn main() -> anyhow::Result<()> { } Commands::Doc(args) => { info!("Running cargo doc on backend: {backend:?}"); - cargo_doc(&cargologlevel, backend, &args.arguments)?; + cargo_doc(globals, &cargologlevel, backend, &args.arguments)?; } Commands::Test(args) => { info!("Running cargo test on backend: {backend:?}"); - cargo_test(&args, backend)?; + cargo_test(globals, &args, backend)?; } Commands::Book(args) => { info!("Running mdbook"); - cargo_book(&args.arguments)?; + cargo_book(globals, &args.arguments)?; } } @@ -283,7 +295,13 @@ fn package_feature_extractor(package: &PackageOpt, backend: Backends) -> Option< } // run example binary `example` -fn command_parser(command: &CargoCommand, overwrite: bool) -> anyhow::Result<()> { +fn command_parser(glob: &Globals, command: &CargoCommand, overwrite: bool) -> anyhow::Result<()> { + let output_mode = if glob.stderr_inherited { + OutputMode::Inherited + } else { + OutputMode::PipedAndCollected + }; + match *command { CargoCommand::Qemu { example, .. } | CargoCommand::Run { example, .. } => { let run_file = format!("{example}.run"); @@ -296,7 +314,7 @@ fn command_parser(command: &CargoCommand, overwrite: bool) -> anyhow::Result<()> // cargo run <..> info!("Running example: {example}"); - let cargo_run_result = run_command(command)?; + let cargo_run_result = run_command(command, output_mode)?; info!("{}", cargo_run_result.stdout); // Create a file for the expected output if it does not exist or mismatches @@ -329,7 +347,7 @@ fn command_parser(command: &CargoCommand, overwrite: bool) -> anyhow::Result<()> | CargoCommand::Test { .. } | CargoCommand::Book { .. } | CargoCommand::ExampleSize { .. } => { - let cargo_result = run_command(command)?; + let cargo_result = run_command(command, output_mode)?; let command = cargo_result.full_command; if let Some(exit_code) = cargo_result.exit_status.code() { if exit_code != exitcode::OK {