From 00339b57455363e288617b3a5ee484b793e90c5e Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Wed, 28 Feb 2024 13:30:28 +0100 Subject: [PATCH 1/3] Make xtask work on Windows, add `run-example` command --- xtask/src/cargo.rs | 33 +++++++++++++++++++++++- xtask/src/lib.rs | 63 +++++++++++++++++++++++++++++++++++++++++++-- xtask/src/main.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 155 insertions(+), 5 deletions(-) diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index de22919ebfc..7d288cd3e8c 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -1,6 +1,7 @@ //! Tools for working with Cargo. use std::{ + env, path::Path, process::{Command, Stdio}, }; @@ -13,7 +14,7 @@ pub fn run(args: &[String], cwd: &Path) -> Result<()> { bail!("The `cwd` argument MUST be a directory"); } - let status = Command::new("cargo") + let status = Command::new(get_cargo()) .args(args) .current_dir(cwd) .stdout(Stdio::piped()) @@ -29,6 +30,36 @@ 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()?; + + Ok(()) +} + +fn get_cargo() -> String { + #[cfg(target_os = "windows")] + let cargo = if let Ok(cargo) = env::var("CARGO_HOME") { + format!("{cargo}/bin/cargo") + } else { + String::from("cargo") + }; + + #[cfg(not(target_os = "windows"))] + let cargo = "cargo"; + cargo +} + #[derive(Debug, Default)] pub struct CargoArgsBuilder { toolchain: Option, diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index eebaf47e330..5083c1d0fce 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -131,7 +131,7 @@ pub fn build_documentation( open: bool, ) -> Result<()> { let package_name = package.to_string(); - let package_path = workspace.join(&package_name); + let package_path = windows_safe_path(&workspace.join(&package_name)); log::info!("Building '{package_name}' documentation targeting '{chip}'"); @@ -146,6 +146,11 @@ pub fn build_documentation( builder = builder.arg("--open"); } + // If targeting an Xtensa device, we must use the '+esp' toolchain modifier: + if target.starts_with("xtensa") { + builder = builder.toolchain("esp"); + } + let args = builder.build(); log::debug!("{args:#?}"); @@ -160,7 +165,7 @@ pub fn load_examples(path: &Path) -> Result> { let mut examples = Vec::new(); for entry in fs::read_dir(path)? { - let path = entry?.path(); + let path = windows_safe_path(&entry?.path()); let text = fs::read_to_string(&path)?; let mut chips = Vec::new(); @@ -254,6 +259,55 @@ pub fn build_example( Ok(()) } +/// Run the specified example for the specified chip. +pub fn run_example( + package_path: &Path, + chip: Chip, + target: &str, + example: &Metadata, +) -> Result<()> { + log::info!( + "Building example '{}' for '{}'", + example.example_path().display(), + chip + ); + if !example.features().is_empty() { + log::info!(" Features: {}", example.features().join(",")); + } + + let bin = if example + .example_path() + .strip_prefix(package_path)? + .starts_with("src/bin") + { + format!("--bin={}", example.name()) + } else { + format!("--example={}", example.name()) + }; + + let mut features = example.features().to_vec(); + features.push(chip.to_string()); + + let mut builder = CargoArgsBuilder::default() + .subcommand("run") + .arg("-Zbuild-std=alloc,core") + .arg("--release") + .target(target) + .features(&features) + .arg(bin); + + // If targeting an Xtensa device, we must use the '+esp' toolchain modifier: + if target.starts_with("xtensa") { + builder = builder.toolchain("esp"); + } + + let args = builder.build(); + log::debug!("{args:#?}"); + + cargo::run_with_input(&args, package_path)?; + Ok(()) +} + /// Build the specified package, using the given toolchain/target/features if /// provided. pub fn build_package( @@ -332,3 +386,8 @@ pub fn bump_version(workspace: &Path, package: Package, amount: Version) -> Resu Ok(()) } + +/// Make the path "Windows"-safe +pub fn windows_safe_path(path: &Path) -> PathBuf { + PathBuf::from(path.to_str().unwrap().to_string().replace("\\\\?\\", "")) +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs index b3fdd508d8a..0dfec9ae519 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -16,6 +16,8 @@ enum Cli { BuildExamples(BuildExamplesArgs), /// Build the specified package with the given options. BuildPackage(BuildPackageArgs), + /// Run the given example for the specified chip. + RunExample(RunExampleArgs), /// Bump the version of the specified package(s) BumpVersion(BumpVersionArgs), } @@ -59,6 +61,18 @@ struct BuildPackageArgs { toolchain: Option, } +#[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 BumpVersionArgs { /// How much to bump the version by. @@ -84,6 +98,7 @@ fn main() -> Result<()> { Cli::BuildDocumentation(args) => build_documentation(&workspace, args), Cli::BuildExamples(args) => build_examples(&workspace, args), Cli::BuildPackage(args) => build_package(&workspace, args), + Cli::RunExample(args) => run_example(&workspace, args), Cli::BumpVersion(args) => bump_version(&workspace, args), } } @@ -119,7 +134,7 @@ fn build_examples(workspace: &Path, mut args: BuildExamplesArgs) -> Result<()> { } // Absolute path of the package's root: - let package_path = workspace.join(args.package.to_string()); + let package_path = xtask::windows_safe_path(&workspace.join(args.package.to_string())); // Absolute path to the directory containing the examples: let example_path = if args.package == Package::Examples { @@ -142,12 +157,57 @@ fn build_examples(workspace: &Path, mut args: BuildExamplesArgs) -> Result<()> { fn build_package(workspace: &Path, args: BuildPackageArgs) -> Result<()> { // Absolute path of the package's root: - let package_path = workspace.join(args.package.to_string()); + let package_path = xtask::windows_safe_path(&workspace.join(args.package.to_string())); // Build the package using the provided features and/or target, if any: xtask::build_package(&package_path, args.features, args.toolchain, args.target) } +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)?; + + // 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())); + + // Absolute path to the directory containing the examples: + let example_path = if args.package == Package::Examples { + package_path.join("src").join("bin") + } else { + 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(|example| example.name() == args.example) + .map(|example| example.clone()); + + if let Some(example) = example { + xtask::run_example(&package_path, args.chip, target, &example)?; + } else { + log::error!("Example not found or unsupported for the given chip"); + } + + Ok(()) +} + fn bump_version(workspace: &Path, args: BumpVersionArgs) -> Result<()> { // Bump the version by the specified amount for each given package: for package in args.packages { From f7029a868c3cfeb85becb152d0a342dd60ae1afd Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Wed, 28 Feb 2024 13:36:51 +0100 Subject: [PATCH 2/3] Fix xtask for non-Windows users --- xtask/src/cargo.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 7d288cd3e8c..3dff7f6b8ab 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -1,7 +1,6 @@ //! Tools for working with Cargo. use std::{ - env, path::Path, process::{Command, Stdio}, }; @@ -49,14 +48,15 @@ pub fn run_with_input(args: &[String], cwd: &Path) -> Result<()> { fn get_cargo() -> String { #[cfg(target_os = "windows")] - let cargo = if let Ok(cargo) = env::var("CARGO_HOME") { + let cargo = if let Ok(cargo) = std::env::var("CARGO_HOME") { format!("{cargo}/bin/cargo") } else { String::from("cargo") }; #[cfg(not(target_os = "windows"))] - let cargo = "cargo"; + let cargo = String::from("cargo"); + cargo } From 902d8060e8014f063b7a2af2c706ced23a7818a8 Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Wed, 28 Feb 2024 15:08:11 +0100 Subject: [PATCH 3/3] Use `find_map` --- xtask/src/main.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 0dfec9ae519..661bf6c2171 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -196,8 +196,13 @@ fn run_example(workspace: &Path, mut args: RunExampleArgs) -> Result<()> { .iter() // Filter down the examples to only those for which the specified chip is supported: .filter(|example| example.supports_chip(args.chip)) - .find(|example| example.name() == args.example) - .map(|example| example.clone()); + .find_map(|example| { + if example.name() == args.example { + Some(example.clone()) + } else { + None + } + }); if let Some(example) = example { xtask::run_example(&package_path, args.chip, target, &example)?;