mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-25 21:19:35 +01:00
xtask: build usage examples and general improvements
This commit is contained in:
parent
cba786529a
commit
63b7024cb9
4 changed files with 178 additions and 19 deletions
|
@ -190,8 +190,8 @@ pub enum BuildOrCheck {
|
|||
|
||||
#[derive(Parser, Clone)]
|
||||
pub struct Globals {
|
||||
/// For which backend to build (defaults to thumbv7)
|
||||
#[arg(value_enum, short, long, global = true)]
|
||||
/// For which backend to build.
|
||||
#[arg(value_enum, short, default_value = "thumbv7", long, global = true)]
|
||||
pub backend: Option<Backends>,
|
||||
|
||||
/// List of comma separated examples to include, all others are excluded
|
||||
|
@ -300,6 +300,55 @@ pub enum Commands {
|
|||
|
||||
/// Build books with mdbook
|
||||
Book(Arg),
|
||||
|
||||
/// Check one or more usage examples.
|
||||
///
|
||||
/// Usage examples are located in ./examples
|
||||
UsageExamplesCheck(UsageExamples),
|
||||
|
||||
/// Build one or more usage examples.
|
||||
///
|
||||
/// Usage examples are located in ./examples
|
||||
#[clap(alias = "./examples")]
|
||||
UsageExampleBuild(UsageExamples),
|
||||
}
|
||||
|
||||
#[derive(Args, Clone, Debug)]
|
||||
pub struct UsageExamples {
|
||||
/// The usage examples to build. All usage examples are selected if this argument is not provided.
|
||||
///
|
||||
/// Example: `rp2040_local_i2c_init,stm32f3_blinky`.
|
||||
examples: Option<String>,
|
||||
}
|
||||
|
||||
impl UsageExamples {
|
||||
pub fn examples(&self) -> anyhow::Result<Vec<String>> {
|
||||
let usage_examples: Vec<_> = std::fs::read_dir("./examples")?
|
||||
.filter_map(Result::ok)
|
||||
.filter(|p| p.metadata().ok().map(|p| p.is_dir()).unwrap_or(false))
|
||||
.filter_map(|p| p.file_name().as_os_str().to_str().map(ToString::to_string))
|
||||
.collect();
|
||||
|
||||
let selected_examples: Option<Vec<String>> = self
|
||||
.examples
|
||||
.clone()
|
||||
.map(|s| s.split(",").map(ToString::to_string).collect());
|
||||
|
||||
if let Some(selected_examples) = selected_examples {
|
||||
if let Some(unfound_example) = selected_examples
|
||||
.iter()
|
||||
.find(|e| !usage_examples.contains(e))
|
||||
{
|
||||
Err(anyhow::anyhow!(
|
||||
"Usage example {unfound_example} does not exist"
|
||||
))
|
||||
} else {
|
||||
Ok(selected_examples)
|
||||
}
|
||||
} else {
|
||||
Ok(usage_examples)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
|
|
|
@ -126,6 +126,33 @@ pub fn cargo<'c>(
|
|||
runner.run_and_coalesce()
|
||||
}
|
||||
|
||||
/// Cargo command to build a usage example.
|
||||
///
|
||||
/// The usage examples are in examples/
|
||||
pub fn cargo_usage_example(
|
||||
globals: &Globals,
|
||||
operation: BuildOrCheck,
|
||||
usage_examples: Vec<String>,
|
||||
) -> Vec<FinalRunResult<'_>> {
|
||||
examples_iter(&usage_examples)
|
||||
.map(|example| {
|
||||
let path = format!("examples/{example}");
|
||||
|
||||
let command = match operation {
|
||||
BuildOrCheck::Check => CargoCommand::CheckInDir {
|
||||
mode: BuildMode::Release,
|
||||
dir: path.into(),
|
||||
},
|
||||
BuildOrCheck::Build => CargoCommand::BuildInDir {
|
||||
mode: BuildMode::Release,
|
||||
dir: path.into(),
|
||||
},
|
||||
};
|
||||
(globals, command, false)
|
||||
})
|
||||
.run_and_coalesce()
|
||||
}
|
||||
|
||||
/// Cargo command to either build or check all examples
|
||||
///
|
||||
/// The examples are in rtic/examples
|
||||
|
|
|
@ -8,6 +8,7 @@ use core::fmt;
|
|||
use std::{
|
||||
fs::File,
|
||||
io::Read,
|
||||
path::PathBuf,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
|
@ -111,6 +112,14 @@ pub enum CargoCommand<'a> {
|
|||
mode: BuildMode,
|
||||
arguments: Option<ExtraArguments>,
|
||||
},
|
||||
CheckInDir {
|
||||
mode: BuildMode,
|
||||
dir: PathBuf,
|
||||
},
|
||||
BuildInDir {
|
||||
mode: BuildMode,
|
||||
dir: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl core::fmt::Display for CargoCommand<'_> {
|
||||
|
@ -211,6 +220,10 @@ impl core::fmt::Display for CargoCommand<'_> {
|
|||
details(target, mode, features, cargoarg)
|
||||
)
|
||||
}
|
||||
CargoCommand::BuildInDir { mode, dir } => {
|
||||
let dir = dir.as_os_str().to_str().unwrap_or("Not displayable");
|
||||
write!(f, "Build {dir} ({mode})")
|
||||
}
|
||||
CargoCommand::Check {
|
||||
cargoarg,
|
||||
package,
|
||||
|
@ -225,6 +238,10 @@ impl core::fmt::Display for CargoCommand<'_> {
|
|||
details(target, mode, features, cargoarg)
|
||||
)
|
||||
}
|
||||
CargoCommand::CheckInDir { mode, dir } => {
|
||||
let dir = dir.as_os_str().to_str().unwrap_or("Not displayable");
|
||||
write!(f, "Check {dir} ({mode})")
|
||||
}
|
||||
CargoCommand::Clippy {
|
||||
cargoarg,
|
||||
package,
|
||||
|
@ -316,11 +333,15 @@ impl<'a> CargoCommand<'a> {
|
|||
format!("{executable} {args}")
|
||||
}
|
||||
|
||||
fn command(&self) -> &str {
|
||||
fn command(&self) -> &'static str {
|
||||
match self {
|
||||
CargoCommand::Run { .. } | CargoCommand::Qemu { .. } => "run",
|
||||
CargoCommand::ExampleCheck { .. } | CargoCommand::Check { .. } => "check",
|
||||
CargoCommand::ExampleBuild { .. } | CargoCommand::Build { .. } => "build",
|
||||
CargoCommand::ExampleCheck { .. }
|
||||
| CargoCommand::Check { .. }
|
||||
| CargoCommand::CheckInDir { .. } => "check",
|
||||
CargoCommand::ExampleBuild { .. }
|
||||
| CargoCommand::Build { .. }
|
||||
| CargoCommand::BuildInDir { .. } => "build",
|
||||
CargoCommand::ExampleSize { .. } => "size",
|
||||
CargoCommand::Clippy { .. } => "clippy",
|
||||
CargoCommand::Format { .. } => "fmt",
|
||||
|
@ -329,7 +350,7 @@ impl<'a> CargoCommand<'a> {
|
|||
CargoCommand::Test { .. } => "test",
|
||||
}
|
||||
}
|
||||
pub fn executable(&self) -> &str {
|
||||
pub fn executable(&self) -> &'static str {
|
||||
match self {
|
||||
CargoCommand::Run { .. }
|
||||
| CargoCommand::Qemu { .. }
|
||||
|
@ -341,7 +362,9 @@ impl<'a> CargoCommand<'a> {
|
|||
| CargoCommand::Clippy { .. }
|
||||
| CargoCommand::Format { .. }
|
||||
| CargoCommand::Test { .. }
|
||||
| CargoCommand::Doc { .. } => "cargo",
|
||||
| CargoCommand::Doc { .. }
|
||||
| CargoCommand::CheckInDir { .. }
|
||||
| CargoCommand::BuildInDir { .. } => "cargo",
|
||||
CargoCommand::Book { .. } => "mdbook",
|
||||
}
|
||||
}
|
||||
|
@ -641,6 +664,34 @@ impl<'a> CargoCommand<'a> {
|
|||
}
|
||||
args
|
||||
}
|
||||
CargoCommand::CheckInDir { mode, dir: _ } => {
|
||||
let mut args = vec!["+nightly"];
|
||||
args.push(self.command());
|
||||
|
||||
if let Some(mode) = mode.to_flag() {
|
||||
args.push(mode);
|
||||
}
|
||||
|
||||
args
|
||||
}
|
||||
CargoCommand::BuildInDir { mode, dir: _ } => {
|
||||
let mut args = vec!["+nightly", self.command()];
|
||||
|
||||
if let Some(mode) = mode.to_flag() {
|
||||
args.push(mode);
|
||||
}
|
||||
|
||||
args
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn chdir(&self) -> Option<&PathBuf> {
|
||||
match self {
|
||||
CargoCommand::CheckInDir { dir, .. } | CargoCommand::BuildInDir { dir, .. } => {
|
||||
Some(dir)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -669,11 +720,18 @@ impl fmt::Display for BuildMode {
|
|||
pub fn run_command(command: &CargoCommand, stderr_mode: OutputMode) -> anyhow::Result<RunResult> {
|
||||
log::info!("👟 {command}");
|
||||
|
||||
let result = Command::new(command.executable())
|
||||
let mut process = Command::new(command.executable());
|
||||
|
||||
process
|
||||
.args(command.args())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(stderr_mode)
|
||||
.output()?;
|
||||
.stderr(stderr_mode);
|
||||
|
||||
if let Some(dir) = command.chdir() {
|
||||
process.current_dir(dir);
|
||||
}
|
||||
|
||||
let result = process.output()?;
|
||||
|
||||
let exit_status = result.status;
|
||||
let stderr = String::from_utf8(result.stderr).unwrap_or("Not displayable".into());
|
||||
|
@ -759,15 +817,27 @@ pub fn handle_results(globals: &Globals, results: Vec<FinalRunResult>) -> anyhow
|
|||
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());
|
||||
let path = if let Some(dir) = cmd.chdir() {
|
||||
let path = dir.as_os_str().to_str().unwrap_or("Not displayable");
|
||||
format!(" (in {path}")
|
||||
} else {
|
||||
info!("✅ Success: {cmd}");
|
||||
format!("")
|
||||
};
|
||||
|
||||
if globals.verbose > 0 {
|
||||
info!("✅ Success:{path} {cmd}\n {}", cmd.as_cmd_string());
|
||||
} else {
|
||||
info!("✅ Success:{path} {cmd}");
|
||||
}
|
||||
});
|
||||
|
||||
errors.clone().for_each(|(cmd, _)| {
|
||||
error!("❌ Failed: {cmd}\n {}", cmd.as_cmd_string());
|
||||
if let Some(dir) = cmd.chdir() {
|
||||
let path = dir.as_os_str().to_str().unwrap_or("Not displayable");
|
||||
error!("❌ Failed: (in {path}) {cmd}\n {}", cmd.as_cmd_string());
|
||||
} else {
|
||||
error!("❌ Failed: {cmd}\n {}", cmd.as_cmd_string());
|
||||
}
|
||||
});
|
||||
|
||||
let ecount = errors.count();
|
||||
|
|
|
@ -23,10 +23,7 @@ use log::{error, info, log_enabled, trace, Level};
|
|||
use crate::{
|
||||
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,
|
||||
},
|
||||
cargo_commands::*,
|
||||
command::{handle_results, run_command, run_successful, CargoCommand},
|
||||
};
|
||||
|
||||
|
@ -152,6 +149,12 @@ fn main() -> anyhow::Result<()> {
|
|||
|
||||
trace!("default logging level: {0}", globals.verbose);
|
||||
|
||||
log::debug!(
|
||||
"Stderr of child processes is inherited: {}",
|
||||
globals.stderr_inherited
|
||||
);
|
||||
log::debug!("Partial features: {}", globals.partial);
|
||||
|
||||
let backend = if let Some(backend) = globals.backend {
|
||||
backend
|
||||
} else {
|
||||
|
@ -285,6 +288,14 @@ fn main() -> anyhow::Result<()> {
|
|||
info!("Running mdbook");
|
||||
cargo_book(globals, &args.arguments)
|
||||
}
|
||||
Commands::UsageExamplesCheck(examples) => {
|
||||
info!("Checking usage examples");
|
||||
cargo_usage_example(globals, BuildOrCheck::Check, examples.examples()?)
|
||||
}
|
||||
Commands::UsageExampleBuild(examples) => {
|
||||
info!("Building usage examples");
|
||||
cargo_usage_example(globals, BuildOrCheck::Build, examples.examples()?)
|
||||
}
|
||||
};
|
||||
|
||||
handle_results(globals, final_run_results)
|
||||
|
@ -347,7 +358,9 @@ fn command_parser(
|
|||
| CargoCommand::Doc { .. }
|
||||
| CargoCommand::Test { .. }
|
||||
| CargoCommand::Book { .. }
|
||||
| CargoCommand::ExampleSize { .. } => {
|
||||
| CargoCommand::ExampleSize { .. }
|
||||
| CargoCommand::BuildInDir { .. }
|
||||
| CargoCommand::CheckInDir { .. } => {
|
||||
let cargo_result = run_command(command, output_mode)?;
|
||||
Ok(cargo_result)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue