mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-28 04:40:52 +00:00
Refactor xtask
subcommands to be group by common functionality (#3457)
* Create `command` submodule, extract build-related args/actions * Extract run-related args/actions * Fix clippy warnings * Update `README.md` for xtask package * Fix order of positional arguments for examples * Update workflows and cargo aliases * Inline function which is only called in one place * Update HIL workflow
This commit is contained in:
parent
78bd99e653
commit
e5ea7e35cd
@ -1,5 +1,5 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
||||
xdoc = "run --package xtask --features=deploy-docs,preview-docs --"
|
||||
xfmt = "xtask fmt-packages"
|
||||
qa = "xtask run-example qa-test"
|
||||
xdoc = "run --package xtask --features=deploy-docs,preview-docs --"
|
||||
xfmt = "xtask fmt-packages"
|
||||
qa = "xtask run example qa-test"
|
||||
|
9
.github/workflows/documentation.yml
vendored
9
.github/workflows/documentation.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
setup:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
packages: '${{ github.event.inputs.packages }}'
|
||||
packages: "${{ github.event.inputs.packages }}"
|
||||
steps:
|
||||
- run: echo "Setup complete!"
|
||||
build:
|
||||
@ -71,7 +71,7 @@ jobs:
|
||||
ref: ${{ matrix.packages.tag }}
|
||||
|
||||
- name: Build documentation
|
||||
run: hal-xtask build-documentation --packages=${{ matrix.packages.name }} --base-url /projects/rust/
|
||||
run: hal-xtask build documentation --packages=${{ matrix.packages.name }} --base-url /projects/rust/
|
||||
|
||||
# https://github.com/actions/deploy-pages/issues/303#issuecomment-1951207879
|
||||
- name: Remove problematic '.lock' files
|
||||
@ -98,11 +98,6 @@ jobs:
|
||||
with:
|
||||
path: "docs/"
|
||||
|
||||
# Create an index for _all_ packages.
|
||||
- name: Create index.html
|
||||
run: cargo xtask build-documentation-index
|
||||
|
||||
|
||||
- if: ${{ github.event.inputs.server == 'preview' }}
|
||||
name: Deploy to preview server
|
||||
uses: appleboy/scp-action@v0.1.7
|
||||
|
6
.github/workflows/hil.yml
vendored
6
.github/workflows/hil.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
repository:
|
||||
description: "Owner and repository to test"
|
||||
required: true
|
||||
default: 'esp-rs/esp-hal'
|
||||
default: "esp-rs/esp-hal"
|
||||
branch:
|
||||
description: "Branch, tag or SHA to checkout."
|
||||
required: true
|
||||
@ -118,7 +118,7 @@ jobs:
|
||||
version: 1.85.0.0
|
||||
|
||||
- name: Build tests
|
||||
run: cargo xtask build-tests ${{ matrix.target.soc }}
|
||||
run: cargo xtask build tests ${{ matrix.target.soc }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@ -176,7 +176,7 @@ jobs:
|
||||
|
||||
export PATH=$PATH:/home/espressif/.cargo/bin
|
||||
chmod +x xtask
|
||||
./xtask run-elfs ${{ matrix.target.soc }} tests-${{ matrix.target.soc }}
|
||||
./xtask run elfs ${{ matrix.target.soc }} tests-${{ matrix.target.soc }}
|
||||
|
||||
- name: Clean up
|
||||
if: always()
|
||||
|
@ -8,22 +8,15 @@ Automation using [cargo-xtask](https://github.com/matklad/cargo-xtask).
|
||||
Usage: xtask <COMMAND>
|
||||
|
||||
Commands:
|
||||
build-documentation Build documentation for the specified chip
|
||||
build-documentation-index Build documentation index including the specified packages
|
||||
build-examples Build all examples for the specified chip
|
||||
build-package Build the specified package with the given options
|
||||
build-tests Build all applicable tests or the specified test for a specified chip
|
||||
bump-version Bump the version of the specified package(s)
|
||||
fmt-packages Format all packages in the workspace with rustfmt
|
||||
lint-packages Lint all packages in the workspace with clippy
|
||||
publish Attempt to publish the specified package
|
||||
run-doc-tests Run doctests for specified chip and package
|
||||
run-example Run the given example for the specified chip
|
||||
run-tests Run all applicable tests or the specified test for a specified chip
|
||||
run-elfs Run all ELFs in a folder
|
||||
ci Perform (parts of) the checks done in CI
|
||||
tag-releases Generate git tags for all new package releases
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
build Build-related subcommands
|
||||
run Run-related subcommands
|
||||
bump-version Bump the version of the specified package(s)
|
||||
ci Perform (parts of) the checks done in CI
|
||||
fmt-packages Format all packages in the workspace with rustfmt
|
||||
lint-packages Lint all packages in the workspace with clippy
|
||||
publish Attempt to publish the specified package
|
||||
tag-releases Generate git tags for all new package releases
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
@ -32,14 +25,15 @@ Options:
|
||||
You can get help for subcommands, too!
|
||||
|
||||
```text
|
||||
cargo xtask build-examples --help
|
||||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
|
||||
Running `target\debug\xtask.exe build-examples --help`
|
||||
cargo xtask build examples --help
|
||||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
|
||||
Running `[...]/target/debug/xtask build examples --help`
|
||||
|
||||
Build all examples for the specified chip
|
||||
|
||||
Usage: xtask.exe build-examples [OPTIONS] <PACKAGE> <CHIP> [EXAMPLE]
|
||||
Usage: xtask build examples [OPTIONS] <CHIP> <PACKAGE>
|
||||
|
||||
...
|
||||
[...]
|
||||
```
|
||||
|
||||
## Test/example metadata use
|
||||
|
199
xtask/src/commands/build.rs
Normal file
199
xtask/src/commands/build.rs
Normal file
@ -0,0 +1,199 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use clap::{Args, Subcommand};
|
||||
use esp_metadata::Chip;
|
||||
use strum::IntoEnumIterator as _;
|
||||
|
||||
use super::{ExamplesArgs, TestsArgs};
|
||||
use crate::{
|
||||
Package,
|
||||
cargo::{self, CargoAction, CargoArgsBuilder},
|
||||
firmware::Metadata,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommands
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum Build {
|
||||
/// Build documentation for the specified chip.
|
||||
Documentation(BuildDocumentationArgs),
|
||||
/// Build all examples for the specified chip.
|
||||
Examples(ExamplesArgs),
|
||||
/// Build the specified package with the given options.
|
||||
Package(BuildPackageArgs),
|
||||
/// Build all applicable tests or the specified test for a specified chip.
|
||||
Tests(TestsArgs),
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommand Arguments
|
||||
|
||||
#[derive(Debug, Default, Args)]
|
||||
pub struct BuildDocumentationArgs {
|
||||
/// Package(s) to document.
|
||||
#[arg(long, value_enum, value_delimiter = ',', default_values_t = Package::iter())]
|
||||
pub packages: Vec<Package>,
|
||||
/// Chip(s) to build documentation for.
|
||||
#[arg(long, value_enum, value_delimiter = ',', default_values_t = Chip::iter())]
|
||||
pub chips: Vec<Chip>,
|
||||
/// Base URL of the deployed documentation.
|
||||
#[arg(long)]
|
||||
pub base_url: Option<String>,
|
||||
#[cfg(feature = "preview-docs")]
|
||||
#[arg(long)]
|
||||
pub serve: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct BuildPackageArgs {
|
||||
/// Package to build.
|
||||
#[arg(value_enum)]
|
||||
pub package: Package,
|
||||
/// Target to build for.
|
||||
#[arg(long)]
|
||||
pub target: Option<String>,
|
||||
/// Features to build with.
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
pub features: Vec<String>,
|
||||
/// Toolchain to build with.
|
||||
#[arg(long)]
|
||||
pub toolchain: Option<String>,
|
||||
/// Don't enabled the default features.
|
||||
#[arg(long)]
|
||||
pub no_default_features: bool,
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommand Actions
|
||||
|
||||
pub fn build_documentation(workspace: &Path, mut args: BuildDocumentationArgs) -> Result<()> {
|
||||
crate::documentation::build_documentation(
|
||||
workspace,
|
||||
&mut args.packages,
|
||||
&mut args.chips,
|
||||
args.base_url,
|
||||
)?;
|
||||
|
||||
crate::documentation::build_documentation_index(workspace, &mut args.packages)?;
|
||||
|
||||
#[cfg(feature = "preview-docs")]
|
||||
if args.serve {
|
||||
use std::{
|
||||
thread::{sleep, spawn},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use rocket::fs::{FileServer, Options};
|
||||
|
||||
spawn(|| {
|
||||
sleep(Duration::from_millis(1000));
|
||||
opener::open_browser("http://127.0.0.1:8000/").ok();
|
||||
});
|
||||
|
||||
rocket::async_main(
|
||||
{
|
||||
rocket::build().mount(
|
||||
"/",
|
||||
FileServer::new(
|
||||
"docs",
|
||||
Options::Index | Options::IndexFile | Options::DotFiles,
|
||||
),
|
||||
)
|
||||
}
|
||||
.launch(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_examples(
|
||||
args: ExamplesArgs,
|
||||
examples: Vec<Metadata>,
|
||||
package_path: &Path,
|
||||
out_path: &Path,
|
||||
) -> Result<()> {
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = args.package.target_triple(&args.chip)?;
|
||||
|
||||
if examples.iter().any(|ex| ex.matches(&args.example)) {
|
||||
// Attempt to build only the specified example:
|
||||
for example in examples.iter().filter(|ex| ex.matches(&args.example)) {
|
||||
crate::execute_app(
|
||||
package_path,
|
||||
args.chip,
|
||||
target,
|
||||
example,
|
||||
CargoAction::Build(out_path.to_path_buf()),
|
||||
1,
|
||||
args.debug,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
} 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| {
|
||||
crate::execute_app(
|
||||
package_path,
|
||||
args.chip,
|
||||
target,
|
||||
example,
|
||||
CargoAction::Build(out_path.to_path_buf()),
|
||||
1,
|
||||
args.debug,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_package(workspace: &Path, args: BuildPackageArgs) -> Result<()> {
|
||||
// Absolute path of the package's root:
|
||||
let package_path = crate::windows_safe_path(&workspace.join(args.package.to_string()));
|
||||
|
||||
// Build the package using the provided features and/or target, if any:
|
||||
|
||||
log::info!("Building package '{}'", package_path.display());
|
||||
if !args.features.is_empty() {
|
||||
log::info!(" Features: {}", args.features.join(","));
|
||||
}
|
||||
if let Some(ref target) = args.target {
|
||||
log::info!(" Target: {}", target);
|
||||
}
|
||||
|
||||
let mut builder = CargoArgsBuilder::default()
|
||||
.subcommand("build")
|
||||
.arg("--release");
|
||||
|
||||
if let Some(toolchain) = args.toolchain {
|
||||
builder = builder.toolchain(toolchain);
|
||||
}
|
||||
|
||||
if let Some(target) = args.target {
|
||||
// If targeting an Xtensa device, we must use the '+esp' toolchain modifier:
|
||||
if target.starts_with("xtensa") {
|
||||
builder = builder.toolchain("esp");
|
||||
builder = builder.arg("-Zbuild-std=core,alloc")
|
||||
}
|
||||
builder = builder.target(target);
|
||||
}
|
||||
|
||||
if !args.features.is_empty() {
|
||||
builder = builder.features(&args.features);
|
||||
}
|
||||
|
||||
if args.no_default_features {
|
||||
builder = builder.arg("--no-default-features");
|
||||
}
|
||||
|
||||
let args = builder.build();
|
||||
log::debug!("{args:#?}");
|
||||
|
||||
cargo::run(&args, &package_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
147
xtask/src/commands/mod.rs
Normal file
147
xtask/src/commands/mod.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::{Result, bail};
|
||||
use clap::Args;
|
||||
use esp_metadata::Chip;
|
||||
|
||||
pub use self::{build::*, run::*};
|
||||
use crate::{Package, cargo::CargoAction};
|
||||
|
||||
mod build;
|
||||
mod run;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommand Arguments
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct ExamplesArgs {
|
||||
/// Package whose examples we which to act on.
|
||||
#[arg(value_enum)]
|
||||
pub package: Package,
|
||||
/// Chip to target.
|
||||
#[arg(value_enum)]
|
||||
pub chip: Chip,
|
||||
|
||||
/// Build examples in debug mode only
|
||||
#[arg(long)]
|
||||
pub debug: bool,
|
||||
/// Optional example to act on (all examples used if omitted)
|
||||
#[arg(long)]
|
||||
pub example: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct TestsArgs {
|
||||
/// Chip to target.
|
||||
#[arg(value_enum)]
|
||||
pub chip: Chip,
|
||||
|
||||
/// Repeat the tests for a specific number of times.
|
||||
#[arg(long, default_value_t = 1)]
|
||||
pub repeat: usize,
|
||||
/// Optional test to act on (all tests used if omitted)
|
||||
#[arg(long, short = 't')]
|
||||
pub test: Option<String>,
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommand Actions
|
||||
|
||||
pub fn examples(workspace: &Path, mut args: ExamplesArgs, action: CargoAction) -> Result<()> {
|
||||
// Ensure that the package/chip combination provided are valid:
|
||||
args.package.validate_package_chip(&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 = crate::windows_safe_path(&workspace.join(args.package.to_string()));
|
||||
|
||||
let example_path = match args.package {
|
||||
Package::Examples | Package::QaTest => 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 = crate::firmware::load(&example_path)?
|
||||
.into_iter()
|
||||
.filter(|example| example.supports_chip(args.chip))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sort all examples by name:
|
||||
examples.sort_by_key(|a| a.binary_name());
|
||||
|
||||
// Execute the specified action:
|
||||
match action {
|
||||
CargoAction::Build(out_path) => build_examples(args, examples, &package_path, &out_path),
|
||||
CargoAction::Run if args.example.is_some() => run_example(args, examples, &package_path),
|
||||
CargoAction::Run => run_examples(args, examples, &package_path),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tests(workspace: &Path, args: TestsArgs, action: CargoAction) -> Result<()> {
|
||||
// Absolute path of the 'hil-test' package's root:
|
||||
let package_path = crate::windows_safe_path(&workspace.join("hil-test"));
|
||||
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = Package::HilTest.target_triple(&args.chip)?;
|
||||
|
||||
// Load all tests which support the specified chip and parse their metadata:
|
||||
let mut tests = crate::firmware::load(&package_path.join("tests"))?
|
||||
.into_iter()
|
||||
.filter(|example| example.supports_chip(args.chip))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sort all tests by name:
|
||||
tests.sort_by_key(|a| a.binary_name());
|
||||
|
||||
// Execute the specified action:
|
||||
if tests.iter().any(|test| test.matches(&args.test)) {
|
||||
for test in tests.iter().filter(|test| test.matches(&args.test)) {
|
||||
crate::execute_app(
|
||||
&package_path,
|
||||
args.chip,
|
||||
target,
|
||||
test,
|
||||
action.clone(),
|
||||
args.repeat,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
} 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 crate::execute_app(
|
||||
&package_path,
|
||||
args.chip,
|
||||
target,
|
||||
&test,
|
||||
action.clone(),
|
||||
args.repeat,
|
||||
false,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
failed.push(test.name_with_configuration());
|
||||
}
|
||||
}
|
||||
|
||||
if !failed.is_empty() {
|
||||
bail!("Failed tests: {:#?}", failed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
228
xtask/src/commands/run.rs
Normal file
228
xtask/src/commands/run.rs
Normal file
@ -0,0 +1,228 @@
|
||||
use std::{
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::{Context as _, Result, bail, ensure};
|
||||
use clap::{Args, Subcommand};
|
||||
use esp_metadata::Chip;
|
||||
|
||||
use super::{ExamplesArgs, TestsArgs};
|
||||
use crate::{
|
||||
cargo::{CargoAction, CargoArgsBuilder},
|
||||
firmware::Metadata,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommands
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
pub enum Run {
|
||||
/// Run doctests for specified chip and package.
|
||||
DocTests(ExamplesArgs),
|
||||
/// Run all ELFs in a folder.
|
||||
Elfs(RunElfsArgs),
|
||||
/// Run the given example for the specified chip.
|
||||
Example(ExamplesArgs),
|
||||
/// Run all applicable tests or the specified test for a specified chip.
|
||||
Tests(TestsArgs),
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommand Arguments
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct RunElfsArgs {
|
||||
/// Which chip to run the tests for.
|
||||
#[arg(value_enum)]
|
||||
pub chip: Chip,
|
||||
/// Path to the ELFs.
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommand Actions
|
||||
|
||||
pub fn run_doc_tests(workspace: &Path, args: ExamplesArgs) -> Result<()> {
|
||||
let chip = args.chip;
|
||||
|
||||
let package_name = args.package.to_string();
|
||||
let package_path = crate::windows_safe_path(&workspace.join(&package_name));
|
||||
|
||||
// Determine the appropriate build target, and cargo features for the given
|
||||
// package and chip:
|
||||
let target = args.package.target_triple(&chip)?;
|
||||
let features = vec![chip.to_string(), "unstable".to_string()];
|
||||
|
||||
// We need `nightly` for building the doc tests, unfortunately:
|
||||
let toolchain = if chip.is_xtensa() { "esp" } else { "nightly" };
|
||||
|
||||
// Build up an array of command-line arguments to pass to `cargo`:
|
||||
let builder = CargoArgsBuilder::default()
|
||||
.toolchain(toolchain)
|
||||
.subcommand("test")
|
||||
.arg("--doc")
|
||||
.arg("-Zdoctest-xcompile")
|
||||
.arg("-Zbuild-std=core,panic_abort")
|
||||
.target(target)
|
||||
.features(&features)
|
||||
.arg("--release");
|
||||
|
||||
let args = builder.build();
|
||||
log::debug!("{args:#?}");
|
||||
|
||||
// Execute `cargo doc` from the package root:
|
||||
crate::cargo::run(&args, &package_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_elfs(args: RunElfsArgs) -> Result<()> {
|
||||
let mut failed: Vec<String> = Vec::new();
|
||||
for elf in fs::read_dir(&args.path)? {
|
||||
let entry = elf?;
|
||||
|
||||
let elf_path = entry.path();
|
||||
let elf_name = elf_path
|
||||
.with_extension("")
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
log::info!("Running test '{}' for '{}'", elf_name, args.chip);
|
||||
|
||||
let mut command = Command::new("probe-rs");
|
||||
command.arg("run").arg(elf_path);
|
||||
|
||||
if args.chip == Chip::Esp32c2 {
|
||||
command.arg("--speed").arg("15000");
|
||||
};
|
||||
|
||||
command.arg("--verify");
|
||||
|
||||
let mut command = command.spawn().context("Failed to execute probe-rs")?;
|
||||
let status = command
|
||||
.wait()
|
||||
.context("Error while waiting for probe-rs to exit")?;
|
||||
|
||||
log::info!("'{elf_name}' done");
|
||||
|
||||
if !status.success() {
|
||||
failed.push(elf_name);
|
||||
}
|
||||
}
|
||||
|
||||
if !failed.is_empty() {
|
||||
bail!("Failed tests: {:?}", failed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_example(args: ExamplesArgs, examples: Vec<Metadata>, package_path: &Path) -> Result<()> {
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = args.package.target_triple(&args.chip)?;
|
||||
|
||||
// Filter the examples down to only the binary we're interested in, assuming it
|
||||
// actually supports the specified chip:
|
||||
let mut found_one = false;
|
||||
for example in examples.iter().filter(|ex| ex.matches(&args.example)) {
|
||||
found_one = true;
|
||||
crate::execute_app(
|
||||
package_path,
|
||||
args.chip,
|
||||
target,
|
||||
example,
|
||||
CargoAction::Run,
|
||||
1,
|
||||
args.debug,
|
||||
)?;
|
||||
}
|
||||
|
||||
ensure!(
|
||||
found_one,
|
||||
"Example not found or unsupported for {}",
|
||||
args.chip
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run_examples(
|
||||
args: ExamplesArgs,
|
||||
examples: Vec<Metadata>,
|
||||
package_path: &Path,
|
||||
) -> Result<()> {
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = args.package.target_triple(&args.chip)?;
|
||||
|
||||
// Filter the examples down to only the binaries we're interested in
|
||||
let mut examples: Vec<Metadata> = examples
|
||||
.iter()
|
||||
.filter(|ex| ex.supports_chip(args.chip))
|
||||
.cloned()
|
||||
.collect();
|
||||
examples.sort_by_key(|ex| ex.tag());
|
||||
|
||||
let console = console::Term::stdout();
|
||||
|
||||
for example in examples {
|
||||
let mut skip = false;
|
||||
|
||||
log::info!("Running example '{}'", example.output_file_name());
|
||||
if let Some(description) = example.description() {
|
||||
log::info!(
|
||||
"\n\n{}\n\nPress ENTER to run example, `s` to skip",
|
||||
description.trim()
|
||||
);
|
||||
} else {
|
||||
log::info!("\n\nPress ENTER to run example, `s` to skip");
|
||||
}
|
||||
|
||||
loop {
|
||||
let key = console.read_key();
|
||||
|
||||
match key {
|
||||
Ok(console::Key::Enter) => break,
|
||||
Ok(console::Key::Char('s')) => {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if !skip {
|
||||
while !skip
|
||||
&& crate::execute_app(
|
||||
package_path,
|
||||
args.chip,
|
||||
target,
|
||||
&example,
|
||||
CargoAction::Run,
|
||||
1,
|
||||
args.debug,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
log::info!("Failed to run example. Retry or skip? (r/s)");
|
||||
loop {
|
||||
let key = console.read_key();
|
||||
|
||||
match key {
|
||||
Ok(console::Key::Char('r')) => break,
|
||||
Ok(console::Key::Char('s')) => {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -5,7 +5,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use anyhow::{ensure, Context as _, Result};
|
||||
use anyhow::{Context as _, Result, ensure};
|
||||
use clap::ValueEnum;
|
||||
use esp_metadata::Config;
|
||||
use kuchikiki::traits::*;
|
||||
@ -13,7 +13,7 @@ use minijinja::Value;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::{cargo::CargoArgsBuilder, Chip, Package};
|
||||
use crate::{Chip, Package, cargo::CargoArgsBuilder};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Build Documentation
|
||||
@ -159,11 +159,7 @@ fn build_documentation_for_package(
|
||||
if package.chip_features_matter() {
|
||||
version.to_string()
|
||||
} else {
|
||||
format!(
|
||||
"{}/{}",
|
||||
version.to_string(),
|
||||
package.to_string().replace('-', "_")
|
||||
)
|
||||
format!("{}/{}", version, package.to_string().replace('-', "_"))
|
||||
}
|
||||
)
|
||||
.as_bytes(),
|
||||
@ -200,7 +196,7 @@ fn cargo_doc(workspace: &Path, package: Package, chip: Option<Chip>) -> Result<P
|
||||
let mut features = vec![];
|
||||
if let Some(chip) = &chip {
|
||||
features.push(chip.to_string());
|
||||
features.extend(package.feature_rules(Config::for_chip(&chip)));
|
||||
features.extend(package.feature_rules(Config::for_chip(chip)));
|
||||
} else {
|
||||
features.extend(package.feature_rules(&Config::empty()));
|
||||
}
|
||||
@ -319,7 +315,9 @@ pub fn build_documentation_index(workspace: &Path, packages: &mut [Package]) ->
|
||||
// If the chip features are not relevant, then there is no need to generate an
|
||||
// index for the given package's documentation:
|
||||
if !package.chip_features_matter() {
|
||||
log::warn!("Package '{package}' does not have device-specific documentation, no need to generate an index");
|
||||
log::warn!(
|
||||
"Package '{package}' does not have device-specific documentation, no need to generate an index"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -354,14 +352,12 @@ pub fn build_documentation_index(workspace: &Path, packages: &mut [Package]) ->
|
||||
.map(|path| {
|
||||
let chip = path
|
||||
.components()
|
||||
.last()
|
||||
.next_back()
|
||||
.unwrap()
|
||||
.as_os_str()
|
||||
.to_string_lossy();
|
||||
|
||||
let chip = Chip::from_str(&chip, true).unwrap();
|
||||
|
||||
chip
|
||||
Chip::from_str(&chip, true).unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@ -376,7 +372,7 @@ pub fn build_documentation_index(workspace: &Path, packages: &mut [Package]) ->
|
||||
minijinja::context! { metadata => meta },
|
||||
)?;
|
||||
let path = version_path.join("index.html");
|
||||
fs::write(&path, html).context(format!("Failed to write index.html"))?;
|
||||
fs::write(&path, html).context("Failed to write index.html")?;
|
||||
log::info!("Created {}", path.display());
|
||||
}
|
||||
}
|
||||
@ -388,7 +384,7 @@ pub fn build_documentation_index(workspace: &Path, packages: &mut [Package]) ->
|
||||
)
|
||||
.context("Failed to copy esp-rs.svg")?;
|
||||
|
||||
let meta = generate_documentation_meta_for_index(&workspace)?;
|
||||
let meta = generate_documentation_meta_for_index(workspace)?;
|
||||
|
||||
// Render the template to HTML and write it out to the desired path:
|
||||
let html = render_template(
|
||||
@ -397,7 +393,7 @@ pub fn build_documentation_index(workspace: &Path, packages: &mut [Package]) ->
|
||||
minijinja::context! { metadata => meta },
|
||||
)?;
|
||||
let path = docs_path.join("index.html");
|
||||
fs::write(&path, html).context(format!("Failed to write index.html"))?;
|
||||
fs::write(&path, html).context("Failed to write index.html")?;
|
||||
log::info!("Created {}", path.display());
|
||||
|
||||
Ok(())
|
||||
|
@ -12,6 +12,7 @@ use strum::{Display, EnumIter, IntoEnumIterator as _};
|
||||
use crate::{cargo::CargoArgsBuilder, firmware::Metadata};
|
||||
|
||||
pub mod cargo;
|
||||
pub mod commands;
|
||||
pub mod documentation;
|
||||
pub mod firmware;
|
||||
|
||||
@ -172,15 +173,12 @@ impl Package {
|
||||
pub fn lint_feature_rules(&self, _config: &Config) -> Vec<Vec<String>> {
|
||||
let mut cases = Vec::new();
|
||||
|
||||
match self {
|
||||
Package::EspWifi => {
|
||||
// minimal set of features that when enabled _should_ still compile
|
||||
cases.push(vec![
|
||||
"esp-hal/unstable".to_owned(),
|
||||
"builtin-scheduler".to_owned(),
|
||||
]);
|
||||
}
|
||||
_ => {}
|
||||
if self == &Package::EspWifi {
|
||||
// Minimal set of features that when enabled _should_ still compile:
|
||||
cases.push(vec![
|
||||
"esp-hal/unstable".to_owned(),
|
||||
"builtin-scheduler".to_owned(),
|
||||
]);
|
||||
}
|
||||
|
||||
cases
|
||||
@ -307,7 +305,7 @@ pub fn execute_app(
|
||||
let output = cargo::run_with_env(&args, package_path, env_vars, true)?;
|
||||
for line in output.lines() {
|
||||
if let Ok(artifact) = serde_json::from_str::<cargo::Artifact>(line) {
|
||||
let out_dir = out_dir.join(&chip.to_string());
|
||||
let out_dir = out_dir.join(chip.to_string());
|
||||
std::fs::create_dir_all(&out_dir)?;
|
||||
|
||||
let output_file = out_dir.join(app.output_file_name());
|
||||
@ -327,56 +325,6 @@ pub fn execute_app(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Build the specified package, using the given toolchain/target/features if
|
||||
/// provided.
|
||||
pub fn build_package(
|
||||
package_path: &Path,
|
||||
features: Vec<String>,
|
||||
no_default_features: bool,
|
||||
toolchain: Option<String>,
|
||||
target: Option<String>,
|
||||
) -> Result<()> {
|
||||
log::info!("Building package '{}'", package_path.display());
|
||||
if !features.is_empty() {
|
||||
log::info!(" Features: {}", features.join(","));
|
||||
}
|
||||
if let Some(ref target) = target {
|
||||
log::info!(" Target: {}", target);
|
||||
}
|
||||
|
||||
let mut builder = CargoArgsBuilder::default()
|
||||
.subcommand("build")
|
||||
.arg("--release");
|
||||
|
||||
if let Some(toolchain) = toolchain {
|
||||
builder = builder.toolchain(toolchain);
|
||||
}
|
||||
|
||||
if let Some(target) = target {
|
||||
// If targeting an Xtensa device, we must use the '+esp' toolchain modifier:
|
||||
if target.starts_with("xtensa") {
|
||||
builder = builder.toolchain("esp");
|
||||
builder = builder.arg("-Zbuild-std=core,alloc")
|
||||
}
|
||||
builder = builder.target(target);
|
||||
}
|
||||
|
||||
if !features.is_empty() {
|
||||
builder = builder.features(&features);
|
||||
}
|
||||
|
||||
if no_default_features {
|
||||
builder = builder.arg("--no-default-features");
|
||||
}
|
||||
|
||||
let args = builder.build();
|
||||
log::debug!("{args:#?}");
|
||||
|
||||
cargo::run(&args, package_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Bump the version of the specified package by the specified amount.
|
||||
pub fn bump_version(workspace: &Path, package: Package, amount: Version) -> Result<()> {
|
||||
let manifest_path = workspace.join(package.to_string()).join("Cargo.toml");
|
||||
@ -431,9 +379,9 @@ pub fn bump_version(workspace: &Path, package: Package, amount: Version) -> Resu
|
||||
" Bumping {package} version for package {pkg}: ({prev_version} -> {version})"
|
||||
);
|
||||
|
||||
manifest["dependencies"].as_table_mut().map(|table| {
|
||||
table[&package.to_string()]["version"] = toml_edit::value(version.to_string())
|
||||
});
|
||||
if let Some(table) = manifest["dependencies"].as_table_mut() {
|
||||
table[&package.to_string()]["version"] = toml_edit::value(version.to_string());
|
||||
}
|
||||
|
||||
fs::write(&manifest_path, manifest.to_string())
|
||||
.with_context(|| format!("Could not write {}", manifest_path.display()))?;
|
||||
@ -471,10 +419,8 @@ pub fn package_paths(workspace: &Path) -> Result<Vec<PathBuf>> {
|
||||
let mut paths = Vec::new();
|
||||
for entry in fs::read_dir(workspace)? {
|
||||
let entry = entry?;
|
||||
if entry.file_type()?.is_dir() {
|
||||
if entry.path().join("Cargo.toml").exists() {
|
||||
paths.push(entry.path());
|
||||
}
|
||||
if entry.file_type()?.is_dir() && entry.path().join("Cargo.toml").exists() {
|
||||
paths.push(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use std::{
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use anyhow::{Context as _, Result, bail, ensure};
|
||||
use anyhow::{Result, bail};
|
||||
use clap::{Args, Parser};
|
||||
use esp_metadata::{Chip, Config};
|
||||
use strum::IntoEnumIterator;
|
||||
@ -13,7 +13,7 @@ use xtask::{
|
||||
Package,
|
||||
Version,
|
||||
cargo::{CargoAction, CargoArgsBuilder},
|
||||
firmware::Metadata,
|
||||
commands::*,
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -21,18 +21,17 @@ use xtask::{
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
enum Cli {
|
||||
/// Build documentation for the specified chip.
|
||||
BuildDocumentation(BuildDocumentationArgs),
|
||||
/// Build documentation index including the specified packages.
|
||||
BuildDocumentationIndex(BuildDocumentationIndexArgs),
|
||||
/// Build all examples for the specified chip.
|
||||
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(TestArgs),
|
||||
/// Build-related subcommands
|
||||
#[clap(subcommand)]
|
||||
Build(Build),
|
||||
/// Run-related subcommands
|
||||
#[clap(subcommand)]
|
||||
Run(Run),
|
||||
|
||||
/// Bump the version of the specified package(s).
|
||||
BumpVersion(BumpVersionArgs),
|
||||
/// Perform (parts of) the checks done in CI
|
||||
Ci(CiArgs),
|
||||
/// Format all packages in the workspace with rustfmt
|
||||
#[clap(alias = "format-packages")]
|
||||
FmtPackages(FmtPackagesArgs),
|
||||
@ -40,91 +39,10 @@ enum Cli {
|
||||
LintPackages(LintPackagesArgs),
|
||||
/// Attempt to publish the specified package.
|
||||
Publish(PublishArgs),
|
||||
/// Run doctests for specified chip and package.
|
||||
#[clap(alias = "run-doc-test")]
|
||||
RunDocTests(ExampleArgs),
|
||||
/// Run the given example for the specified chip.
|
||||
RunExample(ExampleArgs),
|
||||
/// Run all applicable tests or the specified test for a specified chip.
|
||||
RunTests(TestArgs),
|
||||
/// Run all ELFs in a folder.
|
||||
RunElfs(RunElfArgs),
|
||||
/// Perform (parts of) the checks done in CI
|
||||
Ci(CiArgs),
|
||||
/// Generate git tags for all new package releases.
|
||||
TagReleases(TagReleasesArgs),
|
||||
}
|
||||
|
||||
#[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>,
|
||||
/// Build examples in debug mode only
|
||||
#[arg(long)]
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
#[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>,
|
||||
/// Repeat the tests for a specific number of times.
|
||||
#[arg(long)]
|
||||
repeat: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct BuildDocumentationArgs {
|
||||
/// Package(s) to document.
|
||||
#[arg(long, value_enum, value_delimiter = ',', default_values_t = Package::iter())]
|
||||
packages: Vec<Package>,
|
||||
/// Chip(s) to build documentation for.
|
||||
#[arg(long, value_enum, value_delimiter = ',', default_values_t = Chip::iter())]
|
||||
chips: Vec<Chip>,
|
||||
/// Base URL of the deployed documentation.
|
||||
#[arg(long)]
|
||||
base_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct BuildDocumentationIndexArgs {
|
||||
/// Package(s) to build documentation index for.
|
||||
#[arg(long, value_enum, value_delimiter = ',', default_values_t = Package::iter())]
|
||||
packages: Vec<Package>,
|
||||
#[cfg(feature = "preview-docs")]
|
||||
#[arg(long)]
|
||||
serve: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct BuildPackageArgs {
|
||||
/// Package to build.
|
||||
#[arg(value_enum)]
|
||||
package: Package,
|
||||
/// Target to build for.
|
||||
#[arg(long)]
|
||||
target: Option<String>,
|
||||
/// Features to build with.
|
||||
#[arg(long, value_delimiter = ',')]
|
||||
features: Vec<String>,
|
||||
/// Toolchain to build with.
|
||||
#[arg(long)]
|
||||
toolchain: Option<String>,
|
||||
/// Don't enabled the default features.
|
||||
#[arg(long)]
|
||||
no_default_features: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct BumpVersionArgs {
|
||||
/// How much to bump the version by.
|
||||
@ -135,6 +53,13 @@ struct BumpVersionArgs {
|
||||
packages: Vec<Package>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct CiArgs {
|
||||
/// Chip to target.
|
||||
#[arg(value_enum)]
|
||||
chip: Chip,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct FmtPackagesArgs {
|
||||
/// Run in 'check' mode; exists with 0 if formatted correctly, 1 otherwise
|
||||
@ -146,15 +71,6 @@ struct FmtPackagesArgs {
|
||||
packages: Vec<Package>,
|
||||
}
|
||||
|
||||
#[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 LintPackagesArgs {
|
||||
/// Package(s) to target.
|
||||
@ -181,22 +97,6 @@ struct PublishArgs {
|
||||
no_dry_run: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct RunElfArgs {
|
||||
/// Which chip to run the tests for.
|
||||
#[arg(value_enum)]
|
||||
chip: Chip,
|
||||
/// Path to the ELFs.
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct CiArgs {
|
||||
/// Chip to target.
|
||||
#[arg(value_enum)]
|
||||
chip: Chip,
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct TagReleasesArgs {
|
||||
/// Package(s) to tag.
|
||||
@ -218,28 +118,35 @@ fn main() -> Result<()> {
|
||||
let target_path = Path::new("target");
|
||||
|
||||
match Cli::parse() {
|
||||
Cli::BuildDocumentation(args) => build_documentation(&workspace, args),
|
||||
Cli::BuildDocumentationIndex(args) => build_documentation_index(&workspace, args),
|
||||
Cli::BuildExamples(args) => examples(
|
||||
&workspace,
|
||||
args,
|
||||
CargoAction::Build(target_path.join("examples")),
|
||||
),
|
||||
Cli::BuildPackage(args) => build_package(&workspace, args),
|
||||
Cli::BuildTests(args) => tests(
|
||||
&workspace,
|
||||
args,
|
||||
CargoAction::Build(target_path.join("tests")),
|
||||
),
|
||||
// Build-related subcommands:
|
||||
Cli::Build(build) => match build {
|
||||
Build::Documentation(args) => build_documentation(&workspace, args),
|
||||
Build::Examples(args) => examples(
|
||||
&workspace,
|
||||
args,
|
||||
CargoAction::Build(target_path.join("examples")),
|
||||
),
|
||||
Build::Package(args) => build_package(&workspace, args),
|
||||
Build::Tests(args) => tests(
|
||||
&workspace,
|
||||
args,
|
||||
CargoAction::Build(target_path.join("tests")),
|
||||
),
|
||||
},
|
||||
|
||||
// Run-related subcommands:
|
||||
Cli::Run(run) => match run {
|
||||
Run::DocTests(args) => run_doc_tests(&workspace, args),
|
||||
Run::Elfs(args) => run_elfs(args),
|
||||
Run::Example(args) => examples(&workspace, args, CargoAction::Run),
|
||||
Run::Tests(args) => tests(&workspace, args, CargoAction::Run),
|
||||
},
|
||||
|
||||
Cli::BumpVersion(args) => bump_version(&workspace, args),
|
||||
Cli::Ci(args) => run_ci_checks(&workspace, args),
|
||||
Cli::FmtPackages(args) => fmt_packages(&workspace, args),
|
||||
Cli::LintPackages(args) => lint_packages(&workspace, args),
|
||||
Cli::Publish(args) => publish(&workspace, args),
|
||||
Cli::RunDocTests(args) => run_doc_tests(&workspace, args),
|
||||
Cli::RunElfs(args) => run_elfs(args),
|
||||
Cli::RunExample(args) => examples(&workspace, args, CargoAction::Run),
|
||||
Cli::RunTests(args) => tests(&workspace, args, CargoAction::Run),
|
||||
Cli::Ci(args) => run_ci_checks(&workspace, args),
|
||||
Cli::TagReleases(args) => tag_releases(&workspace, args),
|
||||
}
|
||||
}
|
||||
@ -247,314 +154,6 @@ fn main() -> Result<()> {
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommands
|
||||
|
||||
fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Result<()> {
|
||||
// Ensure that the package/chip combination provided are valid:
|
||||
args.package.validate_package_chip(&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::QaTest => 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::firmware::load(&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_key(|a| a.binary_name());
|
||||
|
||||
// Execute the specified action:
|
||||
match action {
|
||||
CargoAction::Build(out_path) => build_examples(args, examples, &package_path, out_path),
|
||||
CargoAction::Run if args.example.is_some() => run_example(args, examples, &package_path),
|
||||
CargoAction::Run => run_examples(args, examples, &package_path),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_examples(
|
||||
args: ExampleArgs,
|
||||
examples: Vec<Metadata>,
|
||||
package_path: &Path,
|
||||
out_path: PathBuf,
|
||||
) -> Result<()> {
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = args.package.target_triple(&args.chip)?;
|
||||
|
||||
if examples
|
||||
.iter()
|
||||
.find(|ex| ex.matches(&args.example))
|
||||
.is_some()
|
||||
{
|
||||
// Attempt to build only the specified example:
|
||||
for example in examples.iter().filter(|ex| ex.matches(&args.example)) {
|
||||
xtask::execute_app(
|
||||
package_path,
|
||||
args.chip,
|
||||
target,
|
||||
example,
|
||||
CargoAction::Build(out_path.clone()),
|
||||
1,
|
||||
args.debug,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
} 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(out_path.clone()),
|
||||
1,
|
||||
args.debug,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
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 = args.package.target_triple(&args.chip)?;
|
||||
|
||||
// Filter the examples down to only the binary we're interested in, assuming it
|
||||
// actually supports the specified chip:
|
||||
let mut found_one = false;
|
||||
for example in examples.iter().filter(|ex| ex.matches(&args.example)) {
|
||||
found_one = true;
|
||||
xtask::execute_app(
|
||||
package_path,
|
||||
args.chip,
|
||||
target,
|
||||
example,
|
||||
CargoAction::Run,
|
||||
1,
|
||||
args.debug,
|
||||
)?;
|
||||
}
|
||||
|
||||
ensure!(
|
||||
found_one,
|
||||
"Example not found or unsupported for {}",
|
||||
args.chip
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_examples(args: ExampleArgs, examples: Vec<Metadata>, package_path: &Path) -> Result<()> {
|
||||
// Determine the appropriate build target for the given package and chip:
|
||||
let target = args.package.target_triple(&args.chip)?;
|
||||
|
||||
// Filter the examples down to only the binaries we're interested in
|
||||
let mut examples: Vec<Metadata> = examples
|
||||
.iter()
|
||||
.filter(|ex| ex.supports_chip(args.chip))
|
||||
.cloned()
|
||||
.collect();
|
||||
examples.sort_by_key(|ex| ex.tag());
|
||||
|
||||
let console = console::Term::stdout();
|
||||
|
||||
for example in examples {
|
||||
let mut skip = false;
|
||||
|
||||
log::info!("Running example '{}'", example.output_file_name());
|
||||
if let Some(description) = example.description() {
|
||||
log::info!(
|
||||
"\n\n{}\n\nPress ENTER to run example, `s` to skip",
|
||||
description.trim()
|
||||
);
|
||||
} else {
|
||||
log::info!("\n\nPress ENTER to run example, `s` to skip");
|
||||
}
|
||||
|
||||
loop {
|
||||
let key = console.read_key();
|
||||
|
||||
match key {
|
||||
Ok(console::Key::Enter) => break,
|
||||
Ok(console::Key::Char('s')) => {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if !skip {
|
||||
while !skip
|
||||
&& xtask::execute_app(
|
||||
package_path,
|
||||
args.chip,
|
||||
target,
|
||||
&example,
|
||||
CargoAction::Run,
|
||||
1,
|
||||
args.debug,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
log::info!("Failed to run example. Retry or skip? (r/s)");
|
||||
loop {
|
||||
let key = console.read_key();
|
||||
|
||||
match key {
|
||||
Ok(console::Key::Char('r')) => break,
|
||||
Ok(console::Key::Char('s')) => {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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 = Package::HilTest.target_triple(&args.chip)?;
|
||||
|
||||
// Load all tests which support the specified chip and parse their metadata:
|
||||
let mut tests = xtask::firmware::load(&package_path.join("tests"))?
|
||||
.into_iter()
|
||||
.filter(|example| example.supports_chip(args.chip))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Sort all tests by name:
|
||||
tests.sort_by_key(|a| a.binary_name());
|
||||
|
||||
// Execute the specified action:
|
||||
if tests.iter().find(|test| test.matches(&args.test)).is_some() {
|
||||
for test in tests.iter().filter(|test| test.matches(&args.test)) {
|
||||
xtask::execute_app(
|
||||
&package_path,
|
||||
args.chip,
|
||||
target,
|
||||
test,
|
||||
action.clone(),
|
||||
args.repeat.unwrap_or(1),
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
} 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.clone(),
|
||||
args.repeat.unwrap_or(1),
|
||||
false,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
failed.push(test.name_with_configuration());
|
||||
}
|
||||
}
|
||||
|
||||
if !failed.is_empty() {
|
||||
bail!("Failed tests: {:#?}", failed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn build_documentation(workspace: &Path, mut args: BuildDocumentationArgs) -> Result<()> {
|
||||
xtask::documentation::build_documentation(
|
||||
workspace,
|
||||
&mut args.packages,
|
||||
&mut args.chips,
|
||||
args.base_url,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_documentation_index(
|
||||
workspace: &Path,
|
||||
mut args: BuildDocumentationIndexArgs,
|
||||
) -> Result<()> {
|
||||
xtask::documentation::build_documentation_index(workspace, &mut args.packages)?;
|
||||
|
||||
#[cfg(feature = "preview-docs")]
|
||||
if args.serve {
|
||||
std::thread::spawn(|| {
|
||||
std::thread::sleep(std::time::Duration::from_millis(1000));
|
||||
opener::open_browser("http://127.0.0.1:8000/").ok();
|
||||
});
|
||||
|
||||
rocket::async_main(
|
||||
{
|
||||
rocket::build().mount(
|
||||
"/",
|
||||
rocket::fs::FileServer::new(
|
||||
"docs",
|
||||
rocket::fs::Options::Index
|
||||
| rocket::fs::Options::IndexFile
|
||||
| rocket::fs::Options::DotFiles,
|
||||
),
|
||||
)
|
||||
}
|
||||
.launch(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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()));
|
||||
|
||||
// Build the package using the provided features and/or target, if any:
|
||||
xtask::build_package(
|
||||
&package_path,
|
||||
args.features,
|
||||
args.no_default_features,
|
||||
args.toolchain,
|
||||
args.target,
|
||||
)
|
||||
}
|
||||
|
||||
fn bump_version(workspace: &Path, args: BumpVersionArgs) -> Result<()> {
|
||||
// Bump the version by the specified amount for each given package:
|
||||
for package in args.packages {
|
||||
@ -617,7 +216,7 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> {
|
||||
for chip in &args.chips {
|
||||
let device = Config::for_chip(chip);
|
||||
|
||||
if let Err(_) = package.validate_package_chip(chip) {
|
||||
if package.validate_package_chip(chip).is_err() {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -734,83 +333,6 @@ fn publish(workspace: &Path, args: PublishArgs) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_elfs(args: RunElfArgs) -> Result<()> {
|
||||
let mut failed: Vec<String> = Vec::new();
|
||||
for elf in fs::read_dir(&args.path)? {
|
||||
let entry = elf?;
|
||||
|
||||
let elf_path = entry.path();
|
||||
let elf_name = elf_path
|
||||
.with_extension("")
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
log::info!("Running test '{}' for '{}'", elf_name, args.chip);
|
||||
|
||||
let mut command = Command::new("probe-rs");
|
||||
command.arg("run").arg(elf_path);
|
||||
|
||||
if args.chip == Chip::Esp32c2 {
|
||||
command.arg("--speed").arg("15000");
|
||||
};
|
||||
|
||||
command.arg("--verify");
|
||||
|
||||
let mut command = command.spawn().context("Failed to execute probe-rs")?;
|
||||
let status = command
|
||||
.wait()
|
||||
.context("Error while waiting for probe-rs to exit")?;
|
||||
|
||||
log::info!("'{elf_name}' done");
|
||||
|
||||
if !status.success() {
|
||||
failed.push(elf_name);
|
||||
}
|
||||
}
|
||||
|
||||
if !failed.is_empty() {
|
||||
bail!("Failed tests: {:?}", failed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_doc_tests(workspace: &Path, args: ExampleArgs) -> Result<()> {
|
||||
let chip = args.chip;
|
||||
|
||||
let package_name = args.package.to_string();
|
||||
let package_path = xtask::windows_safe_path(&workspace.join(&package_name));
|
||||
|
||||
// Determine the appropriate build target, and cargo features for the given
|
||||
// package and chip:
|
||||
let target = args.package.target_triple(&chip)?;
|
||||
let features = vec![chip.to_string(), "unstable".to_string()];
|
||||
|
||||
// We need `nightly` for building the doc tests, unfortunately:
|
||||
let toolchain = if chip.is_xtensa() { "esp" } else { "nightly" };
|
||||
|
||||
// Build up an array of command-line arguments to pass to `cargo`:
|
||||
let builder = CargoArgsBuilder::default()
|
||||
.toolchain(toolchain)
|
||||
.subcommand("test")
|
||||
.arg("--doc")
|
||||
.arg("-Zdoctest-xcompile")
|
||||
.arg("-Zbuild-std=core,panic_abort")
|
||||
.target(target)
|
||||
.features(&features)
|
||||
.arg("--release");
|
||||
|
||||
let args = builder.build();
|
||||
log::debug!("{args:#?}");
|
||||
|
||||
// Execute `cargo doc` from the package root:
|
||||
xtask::cargo::run(&args, &package_path)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
|
||||
let mut failure = false;
|
||||
let started_at = Instant::now();
|
||||
@ -832,7 +354,7 @@ fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
|
||||
// Check doc-tests
|
||||
run_doc_tests(
|
||||
workspace,
|
||||
ExampleArgs {
|
||||
ExamplesArgs {
|
||||
package: Package::EspHal,
|
||||
chip: args.chip,
|
||||
example: None,
|
||||
@ -848,7 +370,7 @@ fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
|
||||
BuildDocumentationArgs {
|
||||
packages: vec![Package::EspHal, Package::EspWifi, Package::EspHalEmbassy],
|
||||
chips: vec![args.chip],
|
||||
base_url: None,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.inspect_err(|_| failure = true)
|
||||
@ -863,7 +385,7 @@ fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
|
||||
// expects it
|
||||
examples(
|
||||
workspace,
|
||||
ExampleArgs {
|
||||
ExamplesArgs {
|
||||
package: Package::EspLpHal,
|
||||
chip: args.chip,
|
||||
example: None,
|
||||
@ -879,7 +401,7 @@ fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
|
||||
let from_dir = PathBuf::from(format!(
|
||||
"./esp-lp-hal/target/{}/release/examples/{}",
|
||||
args.chip.target(),
|
||||
args.chip.to_string()
|
||||
args.chip
|
||||
));
|
||||
let to_dir = PathBuf::from(format!(
|
||||
"./esp-lp-hal/target/{}/release/examples",
|
||||
@ -901,7 +423,7 @@ fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
|
||||
BuildDocumentationArgs {
|
||||
packages: vec![Package::EspLpHal],
|
||||
chips: vec![args.chip],
|
||||
base_url: None,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.inspect_err(|_| failure = true)
|
||||
@ -925,13 +447,13 @@ fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
|
||||
// Build (examples)
|
||||
examples(
|
||||
workspace,
|
||||
ExampleArgs {
|
||||
ExamplesArgs {
|
||||
package: Package::Examples,
|
||||
chip: args.chip,
|
||||
example: None,
|
||||
debug: true,
|
||||
},
|
||||
CargoAction::Build(PathBuf::from(format!("./examples/target/"))),
|
||||
CargoAction::Build(PathBuf::from("./examples/target/")),
|
||||
)
|
||||
.inspect_err(|_| failure = true)
|
||||
.ok();
|
||||
@ -939,13 +461,13 @@ fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
|
||||
// Build (qa-test)
|
||||
examples(
|
||||
workspace,
|
||||
ExampleArgs {
|
||||
ExamplesArgs {
|
||||
package: Package::QaTest,
|
||||
chip: args.chip,
|
||||
example: None,
|
||||
debug: true,
|
||||
},
|
||||
CargoAction::Build(PathBuf::from(format!("./qa-test/target/"))),
|
||||
CargoAction::Build(PathBuf::from("./qa-test/target/")),
|
||||
)
|
||||
.inspect_err(|_| failure = true)
|
||||
.ok();
|
||||
|
Loading…
x
Reference in New Issue
Block a user