mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-27 04:10:28 +00:00
Add the xtask package, implement automation for building examples (#1157)
* Create the `xtask` package, add command for building examples * Do not perform changelog check for `xtask` package changes * Fix unrelated `rustfmt` error
This commit is contained in:
parent
c92d69cb09
commit
ebdd59b0ae
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@ -0,0 +1,2 @@
|
||||
[alias]
|
||||
xtask = "run --package xtask --"
|
5
.github/workflows/changelog.yml
vendored
5
.github/workflows/changelog.yml
vendored
@ -1,7 +1,10 @@
|
||||
name: Change log check
|
||||
name: Changelog check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# We will not track changes for the `xtask` package.
|
||||
paths-ignore:
|
||||
- "/xtask/"
|
||||
# Run on labeled/unlabeled in addition to defaults to detect
|
||||
# adding/removing skip-changelog labels.
|
||||
types: [opened, reopened, labeled, unlabeled, synchronize]
|
||||
|
17
Cargo.toml
Normal file
17
Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[workspace]
|
||||
members = ["xtask"]
|
||||
exclude = [
|
||||
"esp-hal",
|
||||
"esp-hal-procmacros",
|
||||
"esp-hal-smartled",
|
||||
"esp-lp-hal",
|
||||
"esp-riscv-rt",
|
||||
"esp32-hal",
|
||||
"esp32c2-hal",
|
||||
"esp32c3-hal",
|
||||
"esp32c6-hal",
|
||||
"esp32h2-hal",
|
||||
"esp32p4-hal",
|
||||
"esp32s2-hal",
|
||||
"esp32s3-hal",
|
||||
]
|
@ -26,7 +26,7 @@
|
||||
//!
|
||||
//! ```
|
||||
//! // CRC-32/MPEG-2
|
||||
//! const CRC_INITIAL = 0xffffffff; // "init" or "xorin" of all ones
|
||||
//! const CRC_INITIAL: _ = 0xffffffff; // "init" or "xorin" of all ones
|
||||
//! let mut crc = crc32_be(!CRC_INITIAL, &data0); // start
|
||||
//! crc = crc32_be(crc, &data1);
|
||||
//! crc = !crc32_be(crc, &data2); // finish
|
||||
|
12
xtask/Cargo.toml
Normal file
12
xtask/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "xtask"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.79"
|
||||
clap = { version = "4.5.0", features = ["derive"] }
|
||||
env_logger = "0.11.1"
|
||||
log = "0.4.20"
|
||||
strum = { version = "0.26.1", features = ["derive"] }
|
16
xtask/README.md
Normal file
16
xtask/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
# xtask
|
||||
|
||||
Automation using [cargo-xtask](https://github.com/matklad/cargo-xtask).
|
||||
|
||||
## Usage
|
||||
|
||||
```text
|
||||
Usage: xtask <COMMAND>
|
||||
|
||||
Commands:
|
||||
build-examples Build all examples for the specified chip
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
|
||||
Options:
|
||||
-h, --help Print help
|
||||
```
|
181
xtask/src/lib.rs
Normal file
181
xtask/src/lib.rs
Normal file
@ -0,0 +1,181 @@
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
use anyhow::{bail, Result};
|
||||
use clap::ValueEnum;
|
||||
use strum::{Display, EnumIter, IntoEnumIterator};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Display, EnumIter, ValueEnum)]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum Chip {
|
||||
Esp32,
|
||||
Esp32c2,
|
||||
Esp32c3,
|
||||
Esp32c6,
|
||||
Esp32h2,
|
||||
Esp32p4,
|
||||
Esp32s2,
|
||||
Esp32s3,
|
||||
}
|
||||
|
||||
impl Chip {
|
||||
pub fn target(&self) -> &str {
|
||||
use Chip::*;
|
||||
|
||||
match self {
|
||||
Esp32 => "xtensa-esp32-none-elf",
|
||||
Esp32c2 | Esp32c3 => "riscv32imc-unknown-none-elf",
|
||||
Esp32c6 | Esp32h2 => "riscv32imac-unknown-none-elf",
|
||||
Esp32p4 => "riscv32imafc-unknown-none-elf",
|
||||
Esp32s2 => "xtensa-esp32s2-none-elf",
|
||||
Esp32s3 => "xtensa-esp32s3-none-elf",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toolchain(&self) -> &str {
|
||||
use Chip::*;
|
||||
|
||||
match self {
|
||||
Esp32 | Esp32s2 | Esp32s3 => "xtensa",
|
||||
_ => "nightly",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Metadata {
|
||||
path: PathBuf,
|
||||
chips: Vec<Chip>,
|
||||
features: Vec<String>,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
pub fn new(path: PathBuf, chips: Vec<Chip>, features: Vec<String>) -> Self {
|
||||
let chips = if chips.is_empty() {
|
||||
Chip::iter().collect()
|
||||
} else {
|
||||
chips
|
||||
};
|
||||
|
||||
Self {
|
||||
path,
|
||||
chips,
|
||||
features,
|
||||
}
|
||||
}
|
||||
|
||||
/// Absolute path to the example.
|
||||
pub fn path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
|
||||
/// A list of all features required for building a given examples.
|
||||
pub fn features(&self) -> &[String] {
|
||||
&self.features
|
||||
}
|
||||
|
||||
/// If the specified chip is in the list of chips, then it is supported.
|
||||
pub fn supports_chip(&self, chip: Chip) -> bool {
|
||||
self.chips.contains(&chip)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_examples(workspace: &Path) -> Result<Vec<Metadata>> {
|
||||
let bin_path = workspace
|
||||
.join("examples")
|
||||
.join("src")
|
||||
.join("bin")
|
||||
.canonicalize()?;
|
||||
|
||||
let mut examples = Vec::new();
|
||||
|
||||
for entry in fs::read_dir(bin_path)? {
|
||||
let path = entry?.path();
|
||||
let text = fs::read_to_string(&path)?;
|
||||
|
||||
let mut chips = Vec::new();
|
||||
let mut features = Vec::new();
|
||||
|
||||
// We will indicate metadata lines using the `//%` prefix:
|
||||
let lines = text
|
||||
.lines()
|
||||
.filter(|line| line.starts_with("//%"))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for line in lines {
|
||||
let mut split = line
|
||||
.trim_start_matches("//%")
|
||||
.trim()
|
||||
.split_ascii_whitespace()
|
||||
.map(|s| s.to_string())
|
||||
.collect::<VecDeque<_>>();
|
||||
|
||||
if split.len() < 2 {
|
||||
bail!(
|
||||
"Expected at least two elements (key, value), found {}",
|
||||
split.len()
|
||||
);
|
||||
}
|
||||
|
||||
// The trailing ':' on metadata keys is optional :)
|
||||
let key = split.pop_front().unwrap();
|
||||
let key = key.trim_end_matches(':');
|
||||
|
||||
if key == "CHIPS" {
|
||||
chips = split
|
||||
.iter()
|
||||
.map(|s| Chip::from_str(s, false).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
} else if key == "FEATURES" {
|
||||
features = split.into();
|
||||
} else {
|
||||
log::warn!("Unregognized metadata key '{key}', ignoring");
|
||||
}
|
||||
}
|
||||
|
||||
let meta = Metadata::new(path, chips, features);
|
||||
examples.push(meta);
|
||||
}
|
||||
|
||||
Ok(examples)
|
||||
}
|
||||
|
||||
pub fn build_example(workspace: &Path, chip: Chip, example: &Metadata) -> Result<()> {
|
||||
let path = example.path();
|
||||
let features = example.features();
|
||||
|
||||
log::info!("Building example '{}' for '{}'", path.display(), chip);
|
||||
if !features.is_empty() {
|
||||
log::info!(" Features: {}", features.join(","));
|
||||
}
|
||||
|
||||
let bin_name = example
|
||||
.path()
|
||||
.file_name()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.replace(".rs", "");
|
||||
|
||||
let args = &[
|
||||
&format!("+{}", chip.toolchain()),
|
||||
"build",
|
||||
"--release",
|
||||
&format!("--target={}", chip.target()),
|
||||
&format!("--features={},{}", chip, features.join(",")),
|
||||
&format!("--bin={bin_name}"),
|
||||
];
|
||||
log::debug!("{args:#?}");
|
||||
|
||||
Command::new("cargo")
|
||||
.args(args)
|
||||
.current_dir(workspace.join("examples"))
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::inherit())
|
||||
.output()?;
|
||||
|
||||
Ok(())
|
||||
}
|
50
xtask/src/main.rs
Normal file
50
xtask/src/main.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Args, Parser};
|
||||
use xtask::Chip;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Command-line Interface
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
enum Cli {
|
||||
/// Build all examples for the specified chip.
|
||||
BuildExamples(BuildExamplesArgs),
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct BuildExamplesArgs {
|
||||
/// Which chip to build the examples for.
|
||||
#[clap(value_enum)]
|
||||
chip: Chip,
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Application
|
||||
|
||||
fn main() -> Result<()> {
|
||||
env_logger::Builder::new()
|
||||
.filter_module("xtask", log::LevelFilter::Info)
|
||||
.init();
|
||||
|
||||
let workspace = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
let workspace = workspace.parent().unwrap().canonicalize()?;
|
||||
|
||||
match Cli::parse() {
|
||||
Cli::BuildExamples(args) => build_examples(&workspace, args),
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Subcommands
|
||||
|
||||
fn build_examples(workspace: &Path, args: BuildExamplesArgs) -> Result<()> {
|
||||
// Load all examples and parse their metadata. Filter down the examples to only
|
||||
// those for which our chip is supported, and then attempt to build each
|
||||
// remaining example, with the required features enabled:
|
||||
xtask::load_examples(workspace)?
|
||||
.iter()
|
||||
.filter(|example| example.supports_chip(args.chip))
|
||||
.try_for_each(|example| xtask::build_example(workspace, args.chip, example))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user