aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2023-04-15 16:08:33 +0000
committerGitHub <noreply@github.com>2023-04-15 16:08:33 +0000
commitef8046b060a375fd5e6b23d62c3a9a303bbd6e11 (patch)
tree05e07ff7c734870af0e8996afe27913248980d64
parentf741475a3f552585f789b3b2b9c622b090e72057 (diff)
parentaeeb623a70b2bf4ae78f1629a5171406ba51ec4d (diff)
Merge #734
734: QoL + fixes for `xtasks` r=korken89 a=datdenkikniet Preferably merge this before #732 Draft till I get around to fixing "no package" -> "all packages" on `xtask build` (and probably others) Co-authored-by: datdenkikniet <jcdra1@gmail.com>
-rw-r--r--.cargo/config.toml3
-rw-r--r--.github/workflows/build.yml6
-rw-r--r--Cargo.toml8
-rw-r--r--xtask/Cargo.toml6
-rw-r--r--xtask/src/argument_parsing.rs167
-rw-r--r--xtask/src/cargo_commands.rs434
-rw-r--r--xtask/src/command.rs378
-rw-r--r--xtask/src/main.rs198
8 files changed, 855 insertions, 345 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml
index d70faef..0a62466 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,5 +1,6 @@
[alias]
xtask = "run --package xtask --"
+pxtask = "run --package xtask --features rayon --"
[target.thumbv6m-none-eabi]
runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
@@ -10,4 +11,4 @@ runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semiho
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
rustflags = [
"-C", "link-arg=-Tlink.x",
-] \ No newline at end of file
+]
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index aadb0a9..f2bec7d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,8 +31,8 @@ jobs:
- name: Cache Dependencies
uses: Swatinem/rust-cache@v2
- - name: cargo xtask format-check
- run: cargo xtask --verbose format-check
+ - name: cargo xtask fmt
+ run: cargo xtask --verbose fmt -c
# Compilation check
check:
@@ -251,7 +251,7 @@ jobs:
tool: lychee
- name: Remove cargo-config
- run: rm -f .cargo/config
+ run: rm -f .cargo/config.toml
- name: Build docs
# TODO: Any difference between backends?
diff --git a/Cargo.toml b/Cargo.toml
index 888a6ee..6fccc1d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,4 +1,12 @@
[workspace]
+default-members = [
+ "rtic",
+ "rtic-sync",
+ "rtic-common",
+ "rtic-macros",
+ "rtic-monotonics",
+ "rtic-time",
+]
members = [
"rtic",
"rtic-sync",
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 3c72bf1..9e565fa 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -6,10 +6,8 @@ publish = false
[dependencies]
anyhow = "1.0.43"
-os_pipe = "1.1.2"
clap = { version = "4", features = ["derive"] }
-env_logger = "0.10.0"
+pretty_env_logger = "0.4.0"
log = "0.4.17"
-rayon = "1.6.1"
+rayon = { version = "1.6.1", optional = true }
diffy = "0.3.0"
-exitcode = "1.1.2"
diff --git a/xtask/src/argument_parsing.rs b/xtask/src/argument_parsing.rs
index c0538e2..3ee9e34 100644
--- a/xtask/src/argument_parsing.rs
+++ b/xtask/src/argument_parsing.rs
@@ -1,4 +1,4 @@
-use crate::{command::CargoCommand, ARMV6M, ARMV7M, ARMV8MBASE, ARMV8MMAIN, DEFAULT_FEATURES};
+use crate::{command::CargoCommand, Target, ARMV6M, ARMV7M, ARMV8MBASE, ARMV8MMAIN};
use clap::{Args, Parser, Subcommand};
use core::fmt;
@@ -29,6 +29,64 @@ impl Package {
Package::RticTime => "rtic-time",
}
}
+
+ pub fn all() -> Vec<Self> {
+ vec![
+ Self::Rtic,
+ Self::RticCommon,
+ Self::RticMacros,
+ Self::RticMonotonics,
+ Self::RticSync,
+ Self::RticTime,
+ ]
+ }
+
+ /// Get the features needed given the selected package
+ ///
+ /// Without package specified the features for RTIC are required
+ /// With only a single package which is not RTIC, no special
+ /// features are needed
+ pub fn features(
+ &self,
+ target: Target,
+ backend: Backends,
+ partial: bool,
+ ) -> Vec<Option<String>> {
+ match self {
+ Package::Rtic => vec![Some(target.and_features(backend.to_rtic_feature()))],
+ Package::RticMacros => {
+ vec![Some(backend.to_rtic_macros_feature().to_string())]
+ }
+ Package::RticMonotonics => {
+ let features = if partial {
+ &["cortex-m-systick", "rp2040", "nrf52840"][..]
+ } else {
+ &[
+ "cortex-m-systick",
+ "cortex-m-systick,systick-100hz",
+ "cortex-m-systick,systick-10khz",
+ "rp2040",
+ "nrf52810",
+ "nrf52811",
+ "nrf52832",
+ "nrf52833",
+ "nrf52840",
+ "nrf5340-app",
+ "nrf5340-net",
+ "nrf9160",
+ ][..]
+ };
+
+ features
+ .into_iter()
+ .map(ToString::to_string)
+ .map(Some)
+ .chain(std::iter::once(None))
+ .collect()
+ }
+ _ => vec![None],
+ }
+ }
}
pub struct TestMetadata {}
@@ -37,12 +95,12 @@ impl TestMetadata {
pub fn match_package(package: Package, backend: Backends) -> CargoCommand<'static> {
match package {
Package::Rtic => {
- let features = Some(format!(
- "{},{},{}",
- DEFAULT_FEATURES,
+ let features = format!(
+ "{},{}",
backend.to_rtic_feature(),
- backend.to_rtic_uitest_feature(),
- ));
+ backend.to_rtic_uitest_feature()
+ );
+ let features = Some(backend.to_target().and_features(&features));
CargoCommand::Test {
package: Some(package),
features,
@@ -89,7 +147,7 @@ pub enum Backends {
impl Backends {
#[allow(clippy::wrong_self_convention)]
- pub fn to_target(&self) -> &str {
+ pub fn to_target(&self) -> Target<'static> {
match self {
Backends::Thumbv6 => ARMV6M,
Backends::Thumbv7 => ARMV7M,
@@ -99,7 +157,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",
@@ -108,14 +166,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",
@@ -130,12 +188,10 @@ 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)]
+ #[arg(value_enum, short, long, global = true)]
pub backend: Option<Backends>,
/// List of comma separated examples to include, all others are excluded
@@ -144,7 +200,7 @@ pub struct Cli {
///
/// Example: `cargo xtask --example complex,spawn,init`
/// would include complex, spawn and init
- #[arg(short, long, group = "example_group")]
+ #[arg(short, long, group = "example_group", global = true)]
pub example: Option<String>,
/// List of comma separated examples to exclude, all others are included
@@ -153,25 +209,44 @@ pub struct Cli {
///
/// Example: `cargo xtask --excludeexample complex,spawn,init`
/// would exclude complex, spawn and init
- #[arg(long, group = "example_group")]
+ #[arg(long, group = "example_group", global = true)]
pub exampleexclude: Option<String>,
/// Enable more verbose output, repeat up to `-vvv` for even more
- #[arg(short, long, action = clap::ArgAction::Count)]
+ #[arg(short, long, action = clap::ArgAction::Count, global = true)]
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, global = true)]
+ pub stderr_inherited: bool,
+
+ /// Don't build/check/test all feature combinations that are available, only
+ /// a necessary subset.
+ #[arg(long, global = true)]
+ pub partial: 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),
-
/// Format code
- Format(PackageOpt),
+ #[clap(alias = "fmt")]
+ Format(FormatOpt),
/// Run clippy
Clippy(PackageOpt),
@@ -227,16 +302,44 @@ pub enum Commands {
Book(Arg),
}
-#[derive(Args, Debug)]
+#[derive(Args, Debug, Clone)]
+pub struct FormatOpt {
+ #[clap(flatten)]
+ pub package: PackageOpt,
+ /// Check-only, do not apply formatting fixes.
+ #[clap(short, long)]
+ pub check: bool,
+}
+
+#[derive(Args, Debug, Clone)]
/// Restrict to package, or run on whole workspace
pub struct PackageOpt {
/// For which package/workspace member to operate
///
/// If omitted, work on all
- pub package: Option<Package>,
+ package: Option<Package>,
+}
+
+impl PackageOpt {
+ #[cfg(not(feature = "rayon"))]
+ pub fn packages(&self) -> impl Iterator<Item = Package> {
+ self.package
+ .map(|p| vec![p])
+ .unwrap_or(Package::all())
+ .into_iter()
+ }
+
+ #[cfg(feature = "rayon")]
+ pub fn packages(&self) -> impl rayon::prelude::ParallelIterator<Item = Package> {
+ use rayon::prelude::*;
+ self.package
+ .map(|p| vec![p])
+ .unwrap_or(Package::all())
+ .into_par_iter()
+ }
}
-#[derive(Args, Debug)]
+#[derive(Args, Debug, Clone)]
pub struct QemuAndRun {
/// If expected output is missing or mismatching, recreate the file
///
@@ -245,7 +348,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)]
@@ -258,3 +361,13 @@ pub enum ExtraArguments {
#[command(external_subcommand)]
Other(Vec<String>),
}
+
+impl core::fmt::Display for ExtraArguments {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ ExtraArguments::Other(args) => {
+ write!(f, "{}", args.join(" "))
+ }
+ }
+ }
+}
diff --git a/xtask/src/cargo_commands.rs b/xtask/src/cargo_commands.rs
index 7ac7aea..9cbdaef 100644
--- a/xtask/src/cargo_commands.rs
+++ b/xtask/src/cargo_commands.rs
@@ -1,55 +1,143 @@
use crate::{
- argument_parsing::{Backends, BuildOrCheck, ExtraArguments, Package, PackageOpt, TestMetadata},
+ argument_parsing::{Backends, BuildOrCheck, ExtraArguments, Globals, PackageOpt, TestMetadata},
command::{BuildMode, CargoCommand},
- command_parser, package_feature_extractor, DEFAULT_FEATURES,
+ command_parser, RunResult,
};
use log::error;
+
+#[cfg(feature = "rayon")]
use rayon::prelude::*;
+use iters::*;
+
+pub 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),
+ }
+}
+
+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) -> Vec<FinalRunResult<'c>>;
+}
+
+#[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> CoalescingRunner<'c> for I
+ where
+ I: Iterator<Item = (&'g Globals, CargoCommand<'c>, bool)>,
+ {
+ fn run_and_coalesce(self) -> Vec<FinalRunResult<'c>> {
+ self.map(run_and_convert).collect()
+ }
+ }
+}
+
+#[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> CoalescingRunner<'c> for I
+ where
+ I: ParallelIterator<Item = (&'g Globals, CargoCommand<'c>, bool)>,
+ {
+ fn run_and_coalesce(self) -> Vec<FinalRunResult<'c>> {
+ 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 features = package_feature_extractor(package, backend);
+) -> Vec<FinalRunResult<'c>> {
+ let runner = package
+ .packages()
+ .flat_map(|package| {
+ let target = backend.to_target();
+ let features = package.features(target, backend, globals.partial);
- let command = match operation {
- BuildOrCheck::Check => CargoCommand::Check {
- cargoarg,
- package: package.package,
- target: backend.to_target(),
- features,
- mode: BuildMode::Release,
- },
- BuildOrCheck::Build => CargoCommand::Build {
- cargoarg,
- package: package.package,
- target: backend.to_target(),
- features,
- mode: BuildMode::Release,
- },
- };
- command_parser(&command, false)?;
- Ok(())
+ #[cfg(feature = "rayon")]
+ {
+ features.into_par_iter().map(move |f| (package, target, f))
+ }
+
+ #[cfg(not(feature = "rayon"))]
+ {
+ features.into_iter().map(move |f| (package, target, f))
+ }
+ })
+ .map(move |(package, target, features)| {
+ let command = match operation {
+ BuildOrCheck::Check => CargoCommand::Check {
+ cargoarg,
+ package: Some(package),
+ target,
+ features,
+ mode: BuildMode::Release,
+ },
+ BuildOrCheck::Build => CargoCommand::Build {
+ cargoarg,
+ package: Some(package),
+ target,
+ features,
+ mode: BuildMode::Release,
+ },
+ };
+
+ (globals, command, false)
+ });
+
+ runner.run_and_coalesce()
}
/// 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.into_par_iter().for_each(|example| {
- let features = Some(format!(
- "{},{}",
- DEFAULT_FEATURES,
- backend.to_rtic_feature()
- ));
+ examples: &'c [String],
+) -> Vec<FinalRunResult<'c>> {
+ let runner = examples_iter(examples).map(|example| {
+ let features = Some(backend.to_target().and_features(backend.to_rtic_feature()));
let command = match operation {
BuildOrCheck::Check => CargoCommand::ExampleCheck {
@@ -67,184 +155,178 @@ pub fn cargo_example(
mode: BuildMode::Release,
},
};
-
- if let Err(err) = command_parser(&command, false) {
- error!("{err}");
- }
+ (globals, command, false)
});
-
- Ok(())
+ runner.run_and_coalesce()
}
/// Run cargo clippy on selected package
-pub fn cargo_clippy(
- cargoarg: &Option<&str>,
- package: &PackageOpt,
+pub fn cargo_clippy<'c>(
+ globals: &Globals,
+ cargoarg: &'c Option<&'c str>,
+ package: &'c PackageOpt,
backend: Backends,
-) -> anyhow::Result<()> {
- let features = package_feature_extractor(package, backend);
- command_parser(
- &CargoCommand::Clippy {
- cargoarg,
- package: package.package,
- target: backend.to_target(),
- features,
- },
- false,
- )?;
- Ok(())
+) -> Vec<FinalRunResult<'c>> {
+ let runner = package
+ .packages()
+ .flat_map(|package| {
+ let target = backend.to_target();
+ let features = package.features(target, backend, globals.partial);
+
+ #[cfg(feature = "rayon")]
+ {
+ features.into_par_iter().map(move |f| (package, target, f))
+ }
+
+ #[cfg(not(feature = "rayon"))]
+ {
+ features.into_iter().map(move |f| (package, target, f))
+ }
+ })
+ .map(move |(package, target, features)| {
+ (
+ globals,
+ CargoCommand::Clippy {
+ cargoarg,
+ package: Some(package),
+ target,
+ features,
+ },
+ false,
+ )
+ });
+
+ runner.run_and_coalesce()
}
/// Run cargo fmt on selected package
-pub fn cargo_format(
- cargoarg: &Option<&str>,
- package: &PackageOpt,
+pub fn cargo_format<'c>(
+ globals: &Globals,
+ cargoarg: &'c Option<&'c str>,
+ package: &'c PackageOpt,
check_only: bool,
-) -> anyhow::Result<()> {
- command_parser(
- &CargoCommand::Format {
- cargoarg,
- package: package.package,
- check_only,
- },
- false,
- )?;
- Ok(())
+) -> Vec<FinalRunResult<'c>> {
+ let runner = package.packages().map(|p| {
+ (
+ globals,
+ CargoCommand::Format {
+ cargoarg,
+ package: Some(p),
+ check_only,
+ },
+ false,
+ )
+ });
+ runner.run_and_coalesce()
}
/// Run cargo doc
-pub fn cargo_doc(
- cargoarg: &Option<&str>,
+pub fn cargo_doc<'c>(
+ globals: &Globals,
+ cargoarg: &'c Option<&'c str>,
backend: Backends,
- arguments: &Option<ExtraArguments>,
-) -> anyhow::Result<()> {
- let features = Some(format!(
- "{},{}",
- DEFAULT_FEATURES,
- backend.to_rtic_feature()
- ));
-
- command_parser(
- &CargoCommand::Doc {
- cargoarg,
- features,
- arguments: arguments.clone(),
- },
- false,
- )?;
- Ok(())
+ arguments: &'c Option<ExtraArguments>,
+) -> Vec<FinalRunResult<'c>> {
+ let features = Some(backend.to_target().and_features(backend.to_rtic_feature()));
+
+ let command = CargoCommand::Doc {
+ cargoarg,
+ features,
+ arguments: arguments.clone(),
+ };
+
+ vec![run_and_convert((globals, command, false))]
}
-/// Run cargo test on the selcted package or all packages
+/// Run cargo test on the selected package or all packages
///
/// If no package is specified, loop through all packages
-pub fn cargo_test(package: &PackageOpt, backend: Backends) -> anyhow::Result<()> {
- if let Some(package) = package.package {
- let cmd = TestMetadata::match_package(package, backend);
- command_parser(&cmd, false)?;
- } else {
- // Iterate over all workspace packages
- for package in [
- Package::Rtic,
- Package::RticCommon,
- Package::RticMacros,
- Package::RticMonotonics,
- Package::RticSync,
- Package::RticTime,
- ] {
- let mut error_messages = vec![];
- let cmd = &TestMetadata::match_package(package, backend);
- if let Err(err) = command_parser(cmd, false) {
- error_messages.push(err);
- }
-
- if !error_messages.is_empty() {
- for err in error_messages {
- error!("{err}");
- }
- }
- }
- }
- Ok(())
+pub fn cargo_test<'c>(
+ globals: &Globals,
+ package: &'c PackageOpt,
+ backend: Backends,
+) -> Vec<FinalRunResult<'c>> {
+ package
+ .packages()
+ .map(|p| (globals, TestMetadata::match_package(p, backend), false))
+ .run_and_coalesce()
}
/// Use mdbook to build the book
-pub fn cargo_book(arguments: &Option<ExtraArguments>) -> anyhow::Result<()> {
- command_parser(
- &CargoCommand::Book {
+pub fn cargo_book<'c>(
+ globals: &Globals,
+ arguments: &'c Option<ExtraArguments>,
+) -> Vec<FinalRunResult<'c>> {
+ vec![run_and_convert((
+ globals,
+ CargoCommand::Book {
arguments: arguments.clone(),
},
false,
- )?;
- Ok(())
+ ))]
}
/// Run examples
///
/// Supports updating the expected output via the overwrite argument
-pub fn run_test(
- cargoarg: &Option<&str>,
+pub fn run_test<'c>(
+ globals: &Globals,
+ cargoarg: &'c Option<&'c str>,
backend: Backends,
- examples: &[String],
+ examples: &'c [String],
overwrite: bool,
-) -> anyhow::Result<()> {
- examples.into_par_iter().for_each(|example| {
- let cmd = CargoCommand::ExampleBuild {
- cargoarg: &Some("--quiet"),
- example,
- target: backend.to_target(),
- features: Some(format!(
- "{},{}",
- DEFAULT_FEATURES,
- backend.to_rtic_feature()
- )),
- mode: BuildMode::Release,
- };
- if let Err(err) = command_parser(&cmd, false) {
- error!("{err}");
- }
+) -> Vec<FinalRunResult<'c>> {
+ let target = backend.to_target();
+ let features = Some(target.and_features(backend.to_rtic_feature()));
- let cmd = CargoCommand::Qemu {
- cargoarg,
- example,
- target: backend.to_target(),
- features: Some(format!(
- "{},{}",
- DEFAULT_FEATURES,
- backend.to_rtic_feature()
- )),
- mode: BuildMode::Release,
- };
+ examples_iter(examples)
+ .map(|example| {
+ let cmd = CargoCommand::ExampleBuild {
+ cargoarg: &Some("--quiet"),
+ example,
+ target,
+ features: features.clone(),
+ mode: BuildMode::Release,
+ };
- if let Err(err) = command_parser(&cmd, overwrite) {
- error!("{err}");
- }
- });
+ if let Err(err) = command_parser(globals, &cmd, false) {
+ 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
-pub fn build_and_check_size(
- cargoarg: &Option<&str>,
+pub fn build_and_check_size<'c>(
+ globals: &Globals,
+ cargoarg: &'c Option<&'c str>,
backend: Backends,
- examples: &[String],
- arguments: &Option<ExtraArguments>,
-) -> anyhow::Result<()> {
- examples.into_par_iter().for_each(|example| {
+ examples: &'c [String],
+ arguments: &'c Option<ExtraArguments>,
+) -> Vec<FinalRunResult<'c>> {
+ let target = backend.to_target();
+ let features = Some(target.and_features(backend.to_rtic_feature()));
+
+ let runner = examples_iter(examples).map(|example| {
// Make sure the requested example(s) are built
let cmd = CargoCommand::ExampleBuild {
cargoarg: &Some("--quiet"),
example,
- target: backend.to_target(),
- features: Some(format!(
- "{},{}",
- DEFAULT_FEATURES,
- backend.to_rtic_feature()
- )),
+ target,
+ features: features.clone(),
mode: BuildMode::Release,
};
- if let Err(err) = command_parser(&cmd, false) {
+ if let Err(err) = command_parser(globals, &cmd, false) {
error!("{err}");
}
@@ -252,18 +334,12 @@ pub fn build_and_check_size(
cargoarg,
example,
target: backend.to_target(),
- features: Some(format!(
- "{},{}",
- DEFAULT_FEATURES,
- backend.to_rtic_feature()
- )),
+ features: features.clone(),
mode: BuildMode::Release,
arguments: arguments.clone(),
};
- if let Err(err) = command_parser(&cmd, false) {
- error!("{err}");
- }
+ (globals, cmd, false)
});
- Ok(())
+ runner.run_and_coalesce()
}
diff --git a/xtask/src/command.rs b/xtask/src/command.rs
index 6e91a52..b62724a 100644
--- a/xtask/src/command.rs
+++ b/xtask/src/command.rs
@@ -1,7 +1,15 @@
-use crate::{debug, ExtraArguments, Package, RunResult, TestRunError};
+use log::{error, info, Level};
+
+use crate::{
+ argument_parsing::Globals, cargo_commands::FinalRunResult, ExtraArguments, Package, RunResult,
+ Target, TestRunError,
+};
use core::fmt;
-use os_pipe::pipe;
-use std::{fs::File, io::Read, process::Command};
+use std::{
+ fs::File,
+ io::Read,
+ process::{Command, Stdio},
+};
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq)]
@@ -10,6 +18,21 @@ pub enum BuildMode {
Debug,
}
+#[derive(Debug, Clone, Copy, PartialEq)]
+pub enum OutputMode {
+ PipedAndCollected,
+ Inherited,
+}
+
+impl From<OutputMode> 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
@@ -17,49 +40,49 @@ pub enum CargoCommand<'a> {
Run {
cargoarg: &'a Option<&'a str>,
example: &'a str,
- target: &'a str,
+ target: Target<'a>,
features: Option<String>,
mode: BuildMode,
},
Qemu {
cargoarg: &'a Option<&'a str>,
example: &'a str,
- target: &'a str,
+ target: Target<'a>,
features: Option<String>,
mode: BuildMode,
},
ExampleBuild {
cargoarg: &'a Option<&'a str>,
example: &'a str,
- target: &'a str,
+ target: Target<'a>,
features: Option<String>,
mode: BuildMode,
},
ExampleCheck {
cargoarg: &'a Option<&'a str>,
example: &'a str,
- target: &'a str,
+ target: Target<'a>,
features: Option<String>,
mode: BuildMode,
},
Build {
cargoarg: &'a Option<&'a str>,
package: Option<Package>,
- target: &'a str,
+ target: Target<'a>,
features: Option<String>,
mode: BuildMode,
},
Check {
cargoarg: &'a Option<&'a str>,
package: Option<Package>,
- target: &'a str,
+ target: Target<'a>,
features: Option<String>,
mode: BuildMode,
},
Clippy {
cargoarg: &'a Option<&'a str>,
package: Option<Package>,
- target: &'a str,
+ target: Target<'a>,
features: Option<String>,
},
Format {
@@ -83,14 +106,216 @@ pub enum CargoCommand<'a> {
ExampleSize {
cargoarg: &'a Option<&'a str>,
example: &'a str,
- target: &'a str,
+ target: Target<'a>,
features: Option<String>,
mode: BuildMode,
arguments: Option<ExtraArguments>,
},
}
+impl core::fmt::Display for CargoCommand<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let p = |p: &Option<Package>| {
+ if let Some(package) = p {
+ format!("package {package}")
+ } else {
+ format!("default package")
+ }
+ };
+
+ let feat = |f: &Option<String>| {
+ if let Some(features) = f {
+ format!("\"{features}\"")
+ } else {
+ format!("no features")
+ }
+ };
+
+ let carg = |f: &&Option<&str>| {
+ if let Some(cargoarg) = f {
+ format!("{cargoarg}")
+ } else {
+ format!("no cargo args")
+ }
+ };
+
+ let details = |target: &Target,
+ mode: &BuildMode,
+ features: &Option<String>,
+ cargoarg: &&Option<&str>| {
+ let feat = feat(features);
+ let carg = carg(cargoarg);
+ if cargoarg.is_some() {
+ format!("({target}, {mode}, {feat}, {carg})")
+ } else {
+ format!("({target}, {mode}, {feat})")
+ }
+ };
+
+ match self {
+ CargoCommand::Run {
+ cargoarg,
+ example,
+ target,
+ features,
+ mode,
+ } => write!(
+ f,
+ "Run example {example} {}",
+ details(target, mode, features, cargoarg)
+ ),
+ CargoCommand::Qemu {
+ cargoarg,
+ example,
+ target,
+ features,
+ mode,
+ } => write!(
+ f,
+ "Run example {example} in QEMU {}",
+ details(target, mode, features, cargoarg)
+ ),
+ CargoCommand::ExampleBuild {
+ cargoarg,
+ example,
+ target,
+ features,
+ mode,
+ } => write!(
+ f,
+ "Build example {example} {}",
+ details(target, mode, features, cargoarg)
+ ),
+ CargoCommand::ExampleCheck {
+ cargoarg,
+ example,
+ target,
+ features,
+ mode,
+ } => write!(
+ f,
+ "Check example {example} {}",
+ details(target, mode, features, cargoarg)
+ ),
+ CargoCommand::Build {
+ cargoarg,
+ package,
+ target,
+ features,
+ mode,
+ } => {
+ let package = p(package);
+ write!(
+ f,
+ "Build {package} {}",
+ details(target, mode, features, cargoarg)
+ )
+ }
+ CargoCommand::Check {
+ cargoarg,
+ package,
+ target,
+ features,
+ mode,
+ } => {
+ let package = p(package);
+ write!(
+ f,
+ "Check {package} {}",
+ details(target, mode, features, cargoarg)
+ )
+ }
+ CargoCommand::Clippy {
+ cargoarg,
+ package,
+ target,
+ features,
+ } => {
+ let package = p(package);
+ let features = feat(features);
+ let carg = carg(cargoarg);
+ if cargoarg.is_some() {
+ write!(f, "Clippy {package} ({target}, {features}, {carg})")
+ } else {
+ write!(f, "Clippy {package} ({target}, {features})")
+ }
+ }
+ CargoCommand::Format {
+ cargoarg,
+ package,
+ check_only,
+ } => {
+ let package = p(package);
+ let carg = carg(cargoarg);
+
+ let carg = if cargoarg.is_some() {
+ format!("(cargo args: {carg})")
+ } else {
+ format!("")
+ };
+
+ if *check_only {
+ write!(f, "Check format for {package} {carg}")
+ } else {
+ write!(f, "Format {package} {carg}")
+ }
+ }
+ CargoCommand::Doc {
+ cargoarg,
+ features,
+ arguments,
+ } => {
+ let feat = feat(features);
+ let carg = carg(cargoarg);
+ let arguments = arguments
+ .clone()
+ .map(|a| format!("{a}"))
+ .unwrap_or_else(|| "no extra arguments".into());
+ if cargoarg.is_some() {
+ write!(f, "Document ({feat}, {carg}, {arguments})")
+ } else {
+ write!(f, "Document ({feat}, {arguments})")
+ }
+ }
+ CargoCommand::Test {
+ package,
+ features,
+ test,
+ } => {
+ let p = p(package);
+ let test = test
+ .clone()
+ .map(|t| format!("test {t}"))
+ .unwrap_or("all tests".into());
+ let feat = feat(features);
+ write!(f, "Run {test} in {p} (features: {feat})")
+ }
+ CargoCommand::Book { arguments: _ } => write!(f, "Build the book"),
+ CargoCommand::ExampleSize {
+ cargoarg,
+ example,
+ target,
+ features,
+ mode,
+ arguments: _,
+ } => {
+ write!(
+ f,
+ "Compute size of example {example} {}",
+ details(target, mode, features, cargoarg)
+ )
+ }
+ }
+ }
+}
+
impl<'a> CargoCommand<'a> {
+ pub fn as_cmd_string(&self) -> String {
+ let executable = self.executable();
+ let args = self.args().join(" ");
+ format!("{executable} {args}")
+ }
+
fn command(&self) -> &str {
match self {
CargoCommand::Run { .. } | CargoCommand::Qemu { .. } => "run",
@@ -135,7 +360,13 @@ impl<'a> CargoCommand<'a> {
if let Some(cargoarg) = cargoarg {
args.extend_from_slice(&[cargoarg]);
}
- args.extend_from_slice(&[self.command(), "--example", example, "--target", target]);
+ args.extend_from_slice(&[
+ self.command(),
+ "--example",
+ example,
+ "--target",
+ target.triple(),
+ ]);
if let Some(feature) = features {
args.extend_from_slice(&["--features", feature]);
@@ -156,7 +387,13 @@ impl<'a> CargoCommand<'a> {
if let Some(cargoarg) = cargoarg {
args.extend_from_slice(&[cargoarg]);
}
- args.extend_from_slice(&[self.command(), "--example", example, "--target", target]);
+ args.extend_from_slice(&[
+ self.command(),
+ "--example",
+ example,
+ "--target",
+ target.triple(),
+ ]);
if let Some(feature) = features {
args.extend_from_slice(&["--features", feature]);
@@ -178,7 +415,7 @@ impl<'a> CargoCommand<'a> {
args.extend_from_slice(&[cargoarg]);
}
- args.extend_from_slice(&[self.command(), "--target", target]);
+ args.extend_from_slice(&[self.command(), "--target", target.triple()]);
if let Some(package) = package {
args.extend_from_slice(&["--package", package.name()]);
@@ -326,7 +563,13 @@ impl<'a> CargoCommand<'a> {
if let Some(cargoarg) = cargoarg {
args.extend_from_slice(&[cargoarg]);
}
- args.extend_from_slice(&[self.command(), "--example", example, "--target", target]);
+ args.extend_from_slice(&[
+ self.command(),
+ "--example",
+ example,
+ "--target",
+ target.triple(),
+ ]);
if let Some(feature) = features {
args.extend_from_slice(&["--features", feature]);
@@ -347,7 +590,13 @@ impl<'a> CargoCommand<'a> {
if let Some(cargoarg) = cargoarg {
args.extend_from_slice(&[cargoarg]);
}
- args.extend_from_slice(&[self.command(), "--example", example, "--target", target]);
+ args.extend_from_slice(&[
+ self.command(),
+ "--example",
+ example,
+ "--target",
+ target.triple(),
+ ]);
if let Some(feature) = features {
args.extend_from_slice(&["--features", feature]);
@@ -369,7 +618,13 @@ impl<'a> CargoCommand<'a> {
if let Some(cargoarg) = cargoarg {
args.extend_from_slice(&[cargoarg]);
}
- args.extend_from_slice(&[self.command(), "--example", example, "--target", target]);
+ args.extend_from_slice(&[
+ self.command(),
+ "--example",
+ example,
+ "--target",
+ target.triple(),
+ ]);
if let Some(feature_name) = features {
args.extend_from_slice(&["--features", feature_name]);
@@ -411,24 +666,18 @@ impl fmt::Display for BuildMode {
}
}
-pub fn run_command(command: &CargoCommand) -> anyhow::Result<RunResult> {
- let (mut reader, writer) = pipe()?;
- let (mut error_reader, error_writer) = pipe()?;
- debug!("👟 {} {}", command.executable(), command.args().join(" "));
+pub fn run_command(command: &CargoCommand, stderr_mode: OutputMode) -> anyhow::Result<RunResult> {
+ log::info!("👟 {command}");
- let mut handle = Command::new(command.executable())
+ let result = Command::new(command.executable())
.args(command.args())
- .stdout(writer)
- .stderr(error_writer)
- .spawn()?;
-
- // retrieve output and clean up
- let mut stdout = String::new();
- reader.read_to_string(&mut stdout)?;
- let exit_status = handle.wait()?;
+ .stdout(Stdio::piped())
+ .stderr(stderr_mode)
+ .output()?;
- let mut stderr = String::new();
- error_reader.read_to_string(&mut stderr)?;
+ let exit_status = result.status;
+ let stderr = String::from_utf8(result.stderr).unwrap_or("Not displayable".into());
+ let stdout = String::from_utf8(result.stdout).unwrap_or("Not displayable".into());
Ok(RunResult {
exit_status,
@@ -463,3 +712,68 @@ pub fn run_successful(run: &RunResult, expected_output_file: &str) -> Result<(),
Ok(())
}
}
+
+pub fn handle_results(globals: &Globals, 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,
+ "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, _)| {
+ if globals.verbose > 0 {
+ info!("✅ Success: {cmd}\n {}", cmd.as_cmd_string());
+ } else {
+ info!("✅ Success: {cmd}");
+ }
+ });
+
+ errors.clone().for_each(|(cmd, _)| {
+ error!("❌ Failed: {cmd}\n {}", cmd.as_cmd_string());
+ });
+
+ 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 a7fd1d3..2bfe851 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -3,9 +3,9 @@ mod build;
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::{
@@ -14,32 +14,60 @@ use std::{
fs::File,
io::prelude::*,
path::{Path, PathBuf},
- process,
process::ExitStatus,
str,
};
-use env_logger::Env;
-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, PackageOpt},
+ argument_parsing::{Backends, BuildOrCheck, Cli, Commands},
build::init_build_dir,
cargo_commands::{
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},
};
-// x86_64-unknown-linux-gnu
-const _X86_64: &str = "x86_64-unknown-linux-gnu";
-const ARMV6M: &str = "thumbv6m-none-eabi";
-const ARMV7M: &str = "thumbv7m-none-eabi";
-const ARMV8MBASE: &str = "thumbv8m.base-none-eabi";
-const ARMV8MMAIN: &str = "thumbv8m.main-none-eabi";
+#[derive(Debug, Clone, Copy)]
+pub struct Target<'a> {
+ triple: &'a str,
+ has_std: bool,
+}
+
+impl<'a> Target<'a> {
+ const DEFAULT_FEATURES: &str = "test-critical-section";
+
+ pub const fn new(triple: &'a str, has_std: bool) -> Self {
+ Self { triple, has_std }
+ }
+
+ pub fn triple(&self) -> &str {
+ self.triple
+ }
+
+ pub fn has_std(&self) -> bool {
+ self.has_std
+ }
-const DEFAULT_FEATURES: &str = "test-critical-section";
+ pub fn and_features(&self, features: &str) -> String {
+ format!("{},{}", Self::DEFAULT_FEATURES, features)
+ }
+}
+
+impl core::fmt::Display for Target<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.triple)
+ }
+}
+
+// x86_64-unknown-linux-gnu
+const _X86_64: Target = Target::new("x86_64-unknown-linux-gnu", true);
+const ARMV6M: Target = Target::new("thumbv6m-none-eabi", false);
+const ARMV7M: Target = Target::new("thumbv7m-none-eabi", false);
+const ARMV8MBASE: Target = Target::new("thumbv8m.base-none-eabi", false);
+const ARMV8MMAIN: Target = Target::new("thumbv8m.main-none-eabi", false);
#[derive(Debug, Clone)]
pub struct RunResult {
@@ -96,7 +124,9 @@ fn main() -> anyhow::Result<()> {
// check the name of `env::current_dir()` because people might clone it into a different name)
let probably_running_from_repo_root = Path::new("./xtask").exists();
if !probably_running_from_repo_root {
- bail!("xtasks can only be executed from the root of the `rtic` repository");
+ return Err(anyhow::anyhow!(
+ "xtasks can only be executed from the root of the `rtic` repository"
+ ));
}
let examples: Vec<_> = std::fs::read_dir("./rtic/examples")?
@@ -108,26 +138,28 @@ fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
- let env_logger_default_level = match cli.verbose {
- 0 => Env::default().default_filter_or("info"),
- 1 => Env::default().default_filter_or("debug"),
- _ => Env::default().default_filter_or("trace"),
+ let globals = &cli.globals;
+
+ let env_logger_default_level = match globals.verbose {
+ 0 => "info",
+ 1 => "debug",
+ _ => "trace",
};
- env_logger::Builder::from_env(env_logger_default_level)
- .format_module_path(false)
- .format_timestamp(None)
+
+ pretty_env_logger::formatted_builder()
+ .parse_filters(&std::env::var("RUST_LOG").unwrap_or(env_logger_default_level.into()))
.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();
@@ -164,10 +196,10 @@ fn main() -> anyhow::Result<()> {
\n{examples:#?}\n\
By default if example flag is emitted, all examples are tested.",
);
- process::exit(exitcode::USAGE);
+ return Err(anyhow::anyhow!("Incorrect usage"));
} else {
+ examples_to_run
}
- examples_to_run
};
init_build_dir()?;
@@ -185,104 +217,91 @@ fn main() -> anyhow::Result<()> {
Some("--quiet")
};
- match cli.command {
- Commands::FormatCheck(args) => {
- info!("Running cargo fmt --check: {args:?}");
- let check_only = true;
- cargo_format(&cargologlevel, &args, check_only)?;
- }
- Commands::Format(args) => {
- info!("Running cargo fmt: {args:?}");
- let check_only = false;
- cargo_format(&cargologlevel, &args, check_only)?;
- }
+ let final_run_results = match &cli.command {
+ Commands::Format(args) => cargo_format(globals, &cargologlevel, &args.package, args.check),
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,
&examples_to_run,
- )?;
+ )
}
Commands::ExampleBuild => {
info!("Building for backend: {backend:?}");
cargo_example(
+ globals,
BuildOrCheck::Build,
&cargologlevel,
backend,
&examples_to_run,
- )?;
+ )
}
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,
args.overwrite_expected,
- )?;
+ )
}
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)
}
- }
+ };
- Ok(())
+ handle_results(globals, final_run_results)
}
-/// Get the features needed given the selected package
-///
-/// Without package specified the features for RTIC are required
-/// With only a single package which is not RTIC, no special
-/// features are needed
-fn package_feature_extractor(package: &PackageOpt, backend: Backends) -> Option<String> {
- let default_features = Some(format!(
- "{},{}",
- DEFAULT_FEATURES,
- backend.to_rtic_feature()
- ));
- if let Some(package) = package.package {
- debug!("\nTesting package: {package}");
- match package {
- Package::Rtic => default_features,
- Package::RticMacros => Some(backend.to_rtic_macros_feature().to_owned()),
- _ => None,
- }
+// run example binary `example`
+fn command_parser(
+ glob: &Globals,
+ command: &CargoCommand,
+ overwrite: bool,
+) -> anyhow::Result<RunResult> {
+ let output_mode = if glob.stderr_inherited {
+ OutputMode::Inherited
} else {
- default_features
- }
-}
+ OutputMode::PipedAndCollected
+ };
-// run example binary `example`
-fn command_parser(command: &CargoCommand, overwrite: bool) -> anyhow::Result<()> {
match *command {
CargoCommand::Qemu { example, .. } | CargoCommand::Run { example, .. } => {
let run_file = format!("{example}.run");
@@ -295,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
@@ -315,8 +334,9 @@ fn command_parser(command: &CargoCommand, overwrite: bool) -> anyhow::Result<()>
};
} else {
run_successful(&cargo_run_result, &expected_output_file)?;
- }
- Ok(())
+ };
+
+ Ok(cargo_run_result)
}
CargoCommand::Format { .. }
| CargoCommand::ExampleCheck { .. }
@@ -328,28 +348,8 @@ fn command_parser(command: &CargoCommand, overwrite: bool) -> anyhow::Result<()>
| CargoCommand::Test { .. }
| CargoCommand::Book { .. }
| CargoCommand::ExampleSize { .. } => {
- let cargo_result = run_command(command)?;
- if let Some(exit_code) = cargo_result.exit_status.code() {
- if exit_code != exitcode::OK {
- error!("Exit code from command: {exit_code}");
- if !cargo_result.stdout.is_empty() {
- info!("{}", cargo_result.stdout);
- }
- if !cargo_result.stderr.is_empty() {
- error!("{}", cargo_result.stderr);
- }
- process::exit(exit_code);
- } else {
- if !cargo_result.stdout.is_empty() {
- info!("{}", cargo_result.stdout);
- }
- if !cargo_result.stderr.is_empty() {
- info!("{}", cargo_result.stderr);
- }
- }
- }
-
- Ok(())
+ let cargo_result = run_command(command, output_mode)?;
+ Ok(cargo_result)
}
}
}