diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 3415d74e88b..e5f569613aa 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -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") { diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 2f32615d7b3..6216bc1aa25 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -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 diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 7aa2f2024c2..163eb01c2fb 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -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, +} + +#[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, +} + #[derive(Debug, Args)] struct BuildDocumentationArgs { /// Open the documentation in the default browser once built. @@ -47,16 +69,6 @@ struct BuildDocumentationArgs { chips: Vec, } -#[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. - #[arg(value_enum)] - chip: Chip, - /// Which example to run. - example: String, -} - -#[derive(Debug, Args)] -struct TestsArgs { - /// Which chip to run the tests 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. - #[arg(short = 't', long)] - test: Option, } #[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::>(); + + // 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, 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, 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::>(); + + // 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 = 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 = 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 = 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