xtask: build usage examples and general improvements

This commit is contained in:
datdenkikniet 2023-04-14 23:59:23 +02:00
parent cba786529a
commit 63b7024cb9
4 changed files with 178 additions and 19 deletions

View file

@ -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)]

View file

@ -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

View file

@ -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, _)| {
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();

View file

@ -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)
}