Refactor and clean up xtask package (#1540)
* Simplify building/running examples, allow building a single example * Simplify building/running of tests a bit * Clean up the `run_elfs` function a bit * Remove unnecessary duplication in `cargo` module
This commit is contained in:
parent
8567d35cdc
commit
df2b7bd847
@ -22,8 +22,9 @@ pub fn run(args: &[String], cwd: &Path) -> Result<()> {
|
||||
let status = Command::new(get_cargo())
|
||||
.args(args)
|
||||
.current_dir(cwd)
|
||||
.stdout(Stdio::piped())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.stdin(Stdio::inherit())
|
||||
.status()?;
|
||||
|
||||
// Make sure that we return an appropriate exit code here, as Github Actions
|
||||
@ -35,27 +36,6 @@ pub fn run(args: &[String], cwd: &Path) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute cargo with the given arguments and from the specified directory.
|
||||
pub fn run_with_input(args: &[String], cwd: &Path) -> Result<()> {
|
||||
if !cwd.is_dir() {
|
||||
bail!("The `cwd` argument MUST be a directory");
|
||||
}
|
||||
|
||||
let status = Command::new(get_cargo())
|
||||
.args(args)
|
||||
.current_dir(cwd)
|
||||
.stdout(std::process::Stdio::inherit())
|
||||
.stderr(std::process::Stdio::inherit())
|
||||
.stdin(std::process::Stdio::inherit())
|
||||
.status()?;
|
||||
|
||||
if status.success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow::anyhow!("Failed to execute cargo subcommand"))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cargo() -> String {
|
||||
#[cfg(target_os = "windows")]
|
||||
let cargo = if let Ok(cargo) = std::env::var("CARGO_HOME") {
|
||||
|
||||
@ -294,7 +294,7 @@ pub fn execute_app(
|
||||
let args = builder.build();
|
||||
log::debug!("{args:#?}");
|
||||
|
||||
cargo::run_with_input(&args, package_path)
|
||||
cargo::run(&args, package_path)
|
||||
}
|
||||
|
||||
/// Build the specified package, using the given toolchain/target/features if
|
||||
|
||||
@ -7,7 +7,7 @@ use std::{
|
||||
use anyhow::{bail, Result};
|
||||
use clap::{Args, Parser};
|
||||
use strum::IntoEnumIterator;
|
||||
use xtask::{cargo::CargoAction, Chip, Package, Version};
|
||||
use xtask::{cargo::CargoAction, Chip, Metadata, Package, Version};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Command-line Interface
|
||||
@ -17,23 +17,45 @@ enum Cli {
|
||||
/// Build documentation for the specified chip.
|
||||
BuildDocumentation(BuildDocumentationArgs),
|
||||
/// Build all examples for the specified chip.
|
||||
BuildExamples(BuildExamplesArgs),
|
||||
BuildExamples(ExampleArgs),
|
||||
/// Build the specified package with the given options.
|
||||
BuildPackage(BuildPackageArgs),
|
||||
/// Build all applicable tests or the specified test for a specified chip.
|
||||
BuildTests(TestsArgs),
|
||||
BuildTests(TestArgs),
|
||||
/// Bump the version of the specified package(s).
|
||||
BumpVersion(BumpVersionArgs),
|
||||
/// Generate the eFuse fields source file from a CSV.
|
||||
GenerateEfuseFields(GenerateEfuseFieldsArgs),
|
||||
/// Run the given example for the specified chip.
|
||||
RunExample(RunExampleArgs),
|
||||
RunExample(ExampleArgs),
|
||||
/// Run all applicable tests or the specified test for a specified chip.
|
||||
RunTests(TestsArgs),
|
||||
RunTests(TestArgs),
|
||||
/// Run all ELFs in a folder.
|
||||
RunElfs(RunElfArgs),
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct ExampleArgs {
|
||||
/// Package whose examples we which to act on.
|
||||
#[arg(value_enum)]
|
||||
package: Package,
|
||||
/// Chip to target.
|
||||
#[arg(value_enum)]
|
||||
chip: Chip,
|
||||
/// Optional example to act on (all examples used if omitted)
|
||||
example: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct TestArgs {
|
||||
/// Chip to target.
|
||||
#[arg(value_enum)]
|
||||
chip: Chip,
|
||||
/// Optional test to act on (all tests used if omitted)
|
||||
#[arg(short = 't', long)]
|
||||
test: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct BuildDocumentationArgs {
|
||||
/// Open the documentation in the default browser once built.
|
||||
@ -47,16 +69,6 @@ struct BuildDocumentationArgs {
|
||||
chips: Vec<Chip>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct BuildExamplesArgs {
|
||||
/// Package to build examples for.
|
||||
#[arg(value_enum)]
|
||||
package: Package,
|
||||
/// Which chip to build the examples for.
|
||||
#[arg(value_enum)]
|
||||
chip: Chip,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct BuildPackageArgs {
|
||||
/// Package to build.
|
||||
@ -76,15 +88,6 @@ struct BuildPackageArgs {
|
||||
no_default_features: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct GenerateEfuseFieldsArgs {
|
||||
/// Path to the local ESP-IDF repository.
|
||||
idf_path: PathBuf,
|
||||
/// Chip to build eFuse fields table for.
|
||||
#[arg(value_enum)]
|
||||
chip: Chip,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct BumpVersionArgs {
|
||||
/// How much to bump the version by.
|
||||
@ -96,25 +99,12 @@ struct BumpVersionArgs {
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct RunExampleArgs {
|
||||
/// Package to run example from.
|
||||
#[arg(value_enum)]
|
||||
package: Package,
|
||||
/// Which chip to run the examples for.
|
||||
struct GenerateEfuseFieldsArgs {
|
||||
/// Path to the local ESP-IDF repository.
|
||||
idf_path: PathBuf,
|
||||
/// Chip to build eFuse fields table for.
|
||||
#[arg(value_enum)]
|
||||
chip: Chip,
|
||||
/// Which example to run.
|
||||
example: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct TestsArgs {
|
||||
/// Which chip to run the tests for.
|
||||
#[arg(value_enum)]
|
||||
chip: Chip,
|
||||
/// Which example to run.
|
||||
#[arg(short = 't', long)]
|
||||
test: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
@ -139,13 +129,13 @@ fn main() -> Result<()> {
|
||||
|
||||
match Cli::parse() {
|
||||
Cli::BuildDocumentation(args) => build_documentation(&workspace, args),
|
||||
Cli::BuildExamples(args) => build_examples(&workspace, args),
|
||||
Cli::BuildExamples(args) => examples(&workspace, args, CargoAction::Build),
|
||||
Cli::BuildPackage(args) => build_package(&workspace, args),
|
||||
Cli::BuildTests(args) => execute_tests(&workspace, args, CargoAction::Build),
|
||||
Cli::BuildTests(args) => tests(&workspace, args, CargoAction::Build),
|
||||
Cli::BumpVersion(args) => bump_version(&workspace, args),
|
||||
Cli::GenerateEfuseFields(args) => generate_efuse_src(&workspace, args),
|
||||
Cli::RunExample(args) => run_example(&workspace, args),
|
||||
Cli::RunTests(args) => execute_tests(&workspace, args, CargoAction::Run),
|
||||
Cli::RunExample(args) => examples(&workspace, args, CargoAction::Run),
|
||||
Cli::RunTests(args) => tests(&workspace, args, CargoAction::Run),
|
||||
Cli::RunElfs(args) => run_elfs(args),
|
||||
}
|
||||
}
|
||||
@ -153,6 +143,138 @@ fn main() -> Result<()> {
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommands
|
||||
|
||||
fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Result<()> {
|
||||
// Ensure that the package/chip combination provided are valid:
|
||||
validate_package_chip(&args.package, &args.chip)?;
|
||||
|
||||
// If the 'esp-hal' package is specified, what we *really* want is the
|
||||
// 'examples' package instead:
|
||||
if args.package == Package::EspHal {
|
||||
log::warn!(
|
||||
"Package '{}' specified, using '{}' instead",
|
||||
Package::EspHal,
|
||||
Package::Examples
|
||||
);
|
||||
args.package = Package::Examples;
|
||||
}
|
||||
|
||||
// Absolute path of the package's root:
|
||||
let package_path = xtask::windows_safe_path(&workspace.join(args.package.to_string()));
|
||||
|
||||
let example_path = match args.package {
|
||||
Package::Examples => package_path.join("src").join("bin"),
|
||||
Package::HilTest => package_path.join("tests"),
|
||||
_ => package_path.join("examples"),
|
||||
};
|
||||
|
||||
// Load all examples which support the specified chip and parse their metadata:
|
||||
let mut examples = xtask::load_examples(&example_path)?
|
||||
.iter()
|
||||
.filter_map(|example| {
|
||||
if example.supports_chip(args.chip) {
|
||||
Some(example.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sort all examples by name:
|
||||
examples.sort_by(|a, b| a.name().cmp(&b.name()));
|
||||
|
||||
// Execute the specified action:
|
||||
match action {
|
||||
CargoAction::Build => build_examples(args, examples, &package_path),
|
||||
CargoAction::Run => run_example(args, examples, &package_path),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_examples(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Path) -> Result<()> {
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = target_triple(&args.package, &args.chip)?;
|
||||
|
||||
if let Some(example) = examples.iter().find(|ex| Some(ex.name()) == args.example) {
|
||||
// Attempt to build only the specified example:
|
||||
xtask::execute_app(
|
||||
&package_path,
|
||||
args.chip,
|
||||
target,
|
||||
example,
|
||||
&CargoAction::Build,
|
||||
)
|
||||
} else if args.example.is_some() {
|
||||
// An invalid argument was provided:
|
||||
bail!("Example not found or unsupported for the given chip")
|
||||
} else {
|
||||
// Attempt to build each supported example, with all required features enabled:
|
||||
examples.iter().try_for_each(|example| {
|
||||
xtask::execute_app(
|
||||
&package_path,
|
||||
args.chip,
|
||||
target,
|
||||
example,
|
||||
&CargoAction::Build,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn run_example(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Path) -> Result<()> {
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = target_triple(&args.package, &args.chip)?;
|
||||
|
||||
// Filter the examples down to only the binary we're interested in, assuming it
|
||||
// actually supports the specified chip:
|
||||
if let Some(example) = examples.iter().find(|ex| Some(ex.name()) == args.example) {
|
||||
xtask::execute_app(
|
||||
&package_path,
|
||||
args.chip,
|
||||
target,
|
||||
&example,
|
||||
&CargoAction::Run,
|
||||
)
|
||||
} else {
|
||||
bail!("Example not found or unsupported for the given chip")
|
||||
}
|
||||
}
|
||||
|
||||
fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> {
|
||||
// Absolute path of the 'hil-test' package's root:
|
||||
let package_path = xtask::windows_safe_path(&workspace.join("hil-test"));
|
||||
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = target_triple(&Package::HilTest, &args.chip)?;
|
||||
|
||||
// Load all tests which support the specified chip and parse their metadata:
|
||||
let mut tests = xtask::load_examples(&package_path.join("tests"))?
|
||||
.into_iter()
|
||||
.filter(|example| example.supports_chip(args.chip))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sort all tests by name:
|
||||
tests.sort_by(|a, b| a.name().cmp(&b.name()));
|
||||
|
||||
// Execute the specified action:
|
||||
if let Some(test) = tests.iter().find(|test| Some(test.name()) == args.test) {
|
||||
xtask::execute_app(&package_path, args.chip, target, &test, &action)
|
||||
} else if args.test.is_some() {
|
||||
bail!("Test not found or unsupported for the given chip")
|
||||
} else {
|
||||
let mut failed = Vec::new();
|
||||
for test in tests {
|
||||
if xtask::execute_app(&package_path, args.chip, target, &test, &action).is_err() {
|
||||
failed.push(test.name());
|
||||
}
|
||||
}
|
||||
|
||||
if !failed.is_empty() {
|
||||
bail!("Failed tests: {:?}", failed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn build_documentation(workspace: &Path, args: BuildDocumentationArgs) -> Result<()> {
|
||||
let output_path = workspace.join("docs");
|
||||
let resources = workspace.join("resources");
|
||||
@ -220,50 +342,6 @@ fn build_documentation(workspace: &Path, args: BuildDocumentationArgs) -> Result
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_examples(workspace: &Path, mut args: BuildExamplesArgs) -> Result<()> {
|
||||
// Ensure that the package/chip combination provided are valid:
|
||||
validate_package_chip(&args.package, &args.chip)?;
|
||||
|
||||
// If the 'esp-hal' package is specified, what we *really* want is the
|
||||
// 'examples' package instead:
|
||||
if args.package == Package::EspHal {
|
||||
log::warn!(
|
||||
"Package '{}' specified, using '{}' instead",
|
||||
Package::EspHal,
|
||||
Package::Examples
|
||||
);
|
||||
args.package = Package::Examples;
|
||||
}
|
||||
|
||||
// Absolute path of the package's root:
|
||||
let package_path = xtask::windows_safe_path(&workspace.join(args.package.to_string()));
|
||||
|
||||
let example_path = match args.package {
|
||||
Package::Examples => package_path.join("src").join("bin"),
|
||||
Package::HilTest => package_path.join("tests"),
|
||||
_ => package_path.join("examples"),
|
||||
};
|
||||
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = target_triple(&args.package, &args.chip)?;
|
||||
|
||||
// Load all examples and parse their metadata:
|
||||
xtask::load_examples(&example_path)?
|
||||
.iter()
|
||||
// Filter down the examples to only those for which the specified chip is supported:
|
||||
.filter(|example| example.supports_chip(args.chip))
|
||||
// Attempt to build each supported example, with all required features enabled:
|
||||
.try_for_each(|example| {
|
||||
xtask::execute_app(
|
||||
&package_path,
|
||||
args.chip,
|
||||
target,
|
||||
example,
|
||||
&CargoAction::Build,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn build_package(workspace: &Path, args: BuildPackageArgs) -> Result<()> {
|
||||
// Absolute path of the package's root:
|
||||
let package_path = xtask::windows_safe_path(&workspace.join(args.package.to_string()));
|
||||
@ -309,138 +387,46 @@ fn generate_efuse_src(workspace: &Path, args: GenerateEfuseFieldsArgs) -> Result
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_example(workspace: &Path, mut args: RunExampleArgs) -> Result<()> {
|
||||
// Ensure that the package/chip combination provided are valid:
|
||||
validate_package_chip(&args.package, &args.chip)?;
|
||||
fn run_elfs(args: RunElfArgs) -> Result<()> {
|
||||
let mut failed: Vec<String> = Vec::new();
|
||||
for elf in fs::read_dir(&args.path)? {
|
||||
let entry = elf?;
|
||||
|
||||
// If the 'esp-hal' package is specified, what we *really* want is the
|
||||
// 'examples' package instead:
|
||||
if args.package == Package::EspHal {
|
||||
log::warn!(
|
||||
"Package '{}' specified, using '{}' instead",
|
||||
Package::EspHal,
|
||||
Package::Examples
|
||||
);
|
||||
args.package = Package::Examples;
|
||||
}
|
||||
let elf_path = entry.path();
|
||||
let elf_name = elf_path
|
||||
.with_extension("")
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
// Absolute path of the package's root:
|
||||
let package_path = xtask::windows_safe_path(&workspace.join(args.package.to_string()));
|
||||
|
||||
let example_path = match args.package {
|
||||
Package::Examples => package_path.join("src").join("bin"),
|
||||
Package::HilTest => package_path.join("tests"),
|
||||
_ => package_path.join("examples"),
|
||||
};
|
||||
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = target_triple(&args.package, &args.chip)?;
|
||||
|
||||
// Load all examples and parse their metadata:
|
||||
let example = xtask::load_examples(&example_path)?
|
||||
.iter()
|
||||
// Filter down the examples to only those for which the specified chip is supported:
|
||||
.filter(|example| example.supports_chip(args.chip))
|
||||
.find_map(|example| {
|
||||
if example.name() == args.example {
|
||||
Some(example.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(example) = example {
|
||||
xtask::execute_app(
|
||||
&package_path,
|
||||
args.chip,
|
||||
target,
|
||||
&example,
|
||||
&CargoAction::Run,
|
||||
)?;
|
||||
} else {
|
||||
log::error!("Example not found or unsupported for the given chip");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_tests(
|
||||
workspace: &Path,
|
||||
args: TestsArgs,
|
||||
action: CargoAction,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
// Absolute path of the package's root:
|
||||
let package_path = xtask::windows_safe_path(&workspace.join("hil-test"));
|
||||
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = target_triple(&Package::HilTest, &args.chip)?;
|
||||
|
||||
// Load all examples and parse their metadata:
|
||||
let tests = xtask::load_examples(&package_path.join("tests"))?;
|
||||
let mut supported_tests = tests
|
||||
.iter()
|
||||
// Filter down the examples to only those for which the specified chip is supported:
|
||||
.filter(|example| example.supports_chip(args.chip));
|
||||
if let Some(test_name) = &args.test {
|
||||
let test = supported_tests.find_map(|example| {
|
||||
if &example.name() == test_name {
|
||||
Some(example.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(test) = test {
|
||||
xtask::execute_app(&package_path, args.chip, target, &test, &action)?
|
||||
} else {
|
||||
log::error!("Test not found or unsupported for the given chip");
|
||||
}
|
||||
} else {
|
||||
let mut failed_tests: Vec<String> = Vec::new();
|
||||
for test in supported_tests {
|
||||
if xtask::execute_app(&package_path, args.chip, target, test, &action).is_err() {
|
||||
failed_tests.push(test.name());
|
||||
}
|
||||
}
|
||||
if !failed_tests.is_empty() {
|
||||
bail!("Failed tests: {:?}", failed_tests);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_elfs(args: RunElfArgs) -> Result<(), anyhow::Error> {
|
||||
let elfs = fs::read_dir(&args.path)?;
|
||||
let mut failed_elfs: Vec<String> = Vec::new();
|
||||
for elf in elfs {
|
||||
let elf = elf?;
|
||||
let elf_path = elf.path();
|
||||
let elf_name = elf_path.file_name().unwrap().to_str().unwrap();
|
||||
let elf_name = elf_name.split('.').next().unwrap();
|
||||
let elf_name = elf_name.to_string();
|
||||
println!("Running '{}' test", elf_name);
|
||||
log::info!("Running test '{}' for '{}'", elf_name, args.chip);
|
||||
|
||||
let command = Command::new("probe-rs")
|
||||
.arg("run")
|
||||
.arg("--chip")
|
||||
.arg(args.chip.to_string())
|
||||
.arg(elf_path)
|
||||
.output()
|
||||
.expect("Failed to execute probe-rs run command");
|
||||
let stdout = String::from_utf8_lossy(&command.stdout);
|
||||
let stderr = String::from_utf8_lossy(&command.stderr);
|
||||
println!("{}\n{}", stderr, stdout);
|
||||
.output()?;
|
||||
|
||||
println!(
|
||||
"{}\n{}",
|
||||
String::from_utf8_lossy(&command.stderr),
|
||||
String::from_utf8_lossy(&command.stdout)
|
||||
);
|
||||
|
||||
if !command.status.success() {
|
||||
failed_elfs.push(elf_name);
|
||||
failed.push(elf_name);
|
||||
}
|
||||
}
|
||||
|
||||
if !failed_elfs.is_empty() {
|
||||
bail!("Failed tests: {:?}", failed_elfs);
|
||||
if !failed.is_empty() {
|
||||
bail!("Failed tests: {:?}", failed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Helper Functions
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user