mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-11-23 20:22:51 +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)]
|
#[derive(Parser, Clone)]
|
||||||
pub struct Globals {
|
pub struct Globals {
|
||||||
/// For which backend to build (defaults to thumbv7)
|
/// For which backend to build.
|
||||||
#[arg(value_enum, short, long, global = true)]
|
#[arg(value_enum, short, default_value = "thumbv7", long, global = true)]
|
||||||
pub backend: Option<Backends>,
|
pub backend: Option<Backends>,
|
||||||
|
|
||||||
/// List of comma separated examples to include, all others are excluded
|
/// List of comma separated examples to include, all others are excluded
|
||||||
|
@ -300,6 +300,55 @@ pub enum Commands {
|
||||||
|
|
||||||
/// Build books with mdbook
|
/// Build books with mdbook
|
||||||
Book(Arg),
|
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)]
|
#[derive(Args, Debug, Clone)]
|
||||||
|
|
|
@ -126,6 +126,33 @@ pub fn cargo<'c>(
|
||||||
runner.run_and_coalesce()
|
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
|
/// Cargo command to either build or check all examples
|
||||||
///
|
///
|
||||||
/// The examples are in rtic/examples
|
/// The examples are in rtic/examples
|
||||||
|
|
|
@ -8,6 +8,7 @@ use core::fmt;
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::Read,
|
io::Read,
|
||||||
|
path::PathBuf,
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -111,6 +112,14 @@ pub enum CargoCommand<'a> {
|
||||||
mode: BuildMode,
|
mode: BuildMode,
|
||||||
arguments: Option<ExtraArguments>,
|
arguments: Option<ExtraArguments>,
|
||||||
},
|
},
|
||||||
|
CheckInDir {
|
||||||
|
mode: BuildMode,
|
||||||
|
dir: PathBuf,
|
||||||
|
},
|
||||||
|
BuildInDir {
|
||||||
|
mode: BuildMode,
|
||||||
|
dir: PathBuf,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::fmt::Display for CargoCommand<'_> {
|
impl core::fmt::Display for CargoCommand<'_> {
|
||||||
|
@ -211,6 +220,10 @@ impl core::fmt::Display for CargoCommand<'_> {
|
||||||
details(target, mode, features, cargoarg)
|
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 {
|
CargoCommand::Check {
|
||||||
cargoarg,
|
cargoarg,
|
||||||
package,
|
package,
|
||||||
|
@ -225,6 +238,10 @@ impl core::fmt::Display for CargoCommand<'_> {
|
||||||
details(target, mode, features, cargoarg)
|
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 {
|
CargoCommand::Clippy {
|
||||||
cargoarg,
|
cargoarg,
|
||||||
package,
|
package,
|
||||||
|
@ -316,11 +333,15 @@ impl<'a> CargoCommand<'a> {
|
||||||
format!("{executable} {args}")
|
format!("{executable} {args}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn command(&self) -> &str {
|
fn command(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
CargoCommand::Run { .. } | CargoCommand::Qemu { .. } => "run",
|
CargoCommand::Run { .. } | CargoCommand::Qemu { .. } => "run",
|
||||||
CargoCommand::ExampleCheck { .. } | CargoCommand::Check { .. } => "check",
|
CargoCommand::ExampleCheck { .. }
|
||||||
CargoCommand::ExampleBuild { .. } | CargoCommand::Build { .. } => "build",
|
| CargoCommand::Check { .. }
|
||||||
|
| CargoCommand::CheckInDir { .. } => "check",
|
||||||
|
CargoCommand::ExampleBuild { .. }
|
||||||
|
| CargoCommand::Build { .. }
|
||||||
|
| CargoCommand::BuildInDir { .. } => "build",
|
||||||
CargoCommand::ExampleSize { .. } => "size",
|
CargoCommand::ExampleSize { .. } => "size",
|
||||||
CargoCommand::Clippy { .. } => "clippy",
|
CargoCommand::Clippy { .. } => "clippy",
|
||||||
CargoCommand::Format { .. } => "fmt",
|
CargoCommand::Format { .. } => "fmt",
|
||||||
|
@ -329,7 +350,7 @@ impl<'a> CargoCommand<'a> {
|
||||||
CargoCommand::Test { .. } => "test",
|
CargoCommand::Test { .. } => "test",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn executable(&self) -> &str {
|
pub fn executable(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
CargoCommand::Run { .. }
|
CargoCommand::Run { .. }
|
||||||
| CargoCommand::Qemu { .. }
|
| CargoCommand::Qemu { .. }
|
||||||
|
@ -341,7 +362,9 @@ impl<'a> CargoCommand<'a> {
|
||||||
| CargoCommand::Clippy { .. }
|
| CargoCommand::Clippy { .. }
|
||||||
| CargoCommand::Format { .. }
|
| CargoCommand::Format { .. }
|
||||||
| CargoCommand::Test { .. }
|
| CargoCommand::Test { .. }
|
||||||
| CargoCommand::Doc { .. } => "cargo",
|
| CargoCommand::Doc { .. }
|
||||||
|
| CargoCommand::CheckInDir { .. }
|
||||||
|
| CargoCommand::BuildInDir { .. } => "cargo",
|
||||||
CargoCommand::Book { .. } => "mdbook",
|
CargoCommand::Book { .. } => "mdbook",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,6 +664,34 @@ impl<'a> CargoCommand<'a> {
|
||||||
}
|
}
|
||||||
args
|
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> {
|
pub fn run_command(command: &CargoCommand, stderr_mode: OutputMode) -> anyhow::Result<RunResult> {
|
||||||
log::info!("👟 {command}");
|
log::info!("👟 {command}");
|
||||||
|
|
||||||
let result = Command::new(command.executable())
|
let mut process = Command::new(command.executable());
|
||||||
|
|
||||||
|
process
|
||||||
.args(command.args())
|
.args(command.args())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.stderr(stderr_mode)
|
.stderr(stderr_mode);
|
||||||
.output()?;
|
|
||||||
|
if let Some(dir) = command.chdir() {
|
||||||
|
process.current_dir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = process.output()?;
|
||||||
|
|
||||||
let exit_status = result.status;
|
let exit_status = result.status;
|
||||||
let stderr = String::from_utf8(result.stderr).unwrap_or("Not displayable".into());
|
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));
|
errors.clone().for_each(log_stdout_stderr(Level::Error));
|
||||||
|
|
||||||
successes.for_each(|(cmd, _)| {
|
successes.for_each(|(cmd, _)| {
|
||||||
if globals.verbose > 0 {
|
let path = if let Some(dir) = cmd.chdir() {
|
||||||
info!("✅ Success: {cmd}\n {}", cmd.as_cmd_string());
|
let path = dir.as_os_str().to_str().unwrap_or("Not displayable");
|
||||||
|
format!(" (in {path}")
|
||||||
} else {
|
} 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, _)| {
|
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();
|
let ecount = errors.count();
|
||||||
|
|
|
@ -23,10 +23,7 @@ use log::{error, info, log_enabled, trace, Level};
|
||||||
use crate::{
|
use crate::{
|
||||||
argument_parsing::{Backends, BuildOrCheck, Cli, Commands},
|
argument_parsing::{Backends, BuildOrCheck, Cli, Commands},
|
||||||
build::init_build_dir,
|
build::init_build_dir,
|
||||||
cargo_commands::{
|
cargo_commands::*,
|
||||||
build_and_check_size, cargo, cargo_book, cargo_clippy, cargo_doc, cargo_example,
|
|
||||||
cargo_format, cargo_test, run_test,
|
|
||||||
},
|
|
||||||
command::{handle_results, run_command, run_successful, CargoCommand},
|
command::{handle_results, run_command, run_successful, CargoCommand},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -152,6 +149,12 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
trace!("default logging level: {0}", globals.verbose);
|
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 {
|
let backend = if let Some(backend) = globals.backend {
|
||||||
backend
|
backend
|
||||||
} else {
|
} else {
|
||||||
|
@ -285,6 +288,14 @@ fn main() -> anyhow::Result<()> {
|
||||||
info!("Running mdbook");
|
info!("Running mdbook");
|
||||||
cargo_book(globals, &args.arguments)
|
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)
|
handle_results(globals, final_run_results)
|
||||||
|
@ -347,7 +358,9 @@ fn command_parser(
|
||||||
| CargoCommand::Doc { .. }
|
| CargoCommand::Doc { .. }
|
||||||
| CargoCommand::Test { .. }
|
| CargoCommand::Test { .. }
|
||||||
| CargoCommand::Book { .. }
|
| CargoCommand::Book { .. }
|
||||||
| CargoCommand::ExampleSize { .. } => {
|
| CargoCommand::ExampleSize { .. }
|
||||||
|
| CargoCommand::BuildInDir { .. }
|
||||||
|
| CargoCommand::CheckInDir { .. } => {
|
||||||
let cargo_result = run_command(command, output_mode)?;
|
let cargo_result = run_command(command, output_mode)?;
|
||||||
Ok(cargo_result)
|
Ok(cargo_result)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue