Even more cargo-batch, encode configs in Cargo.toml (#4134)

* Abstract out LP-core targeting packages

* Encode targets_lp_core in Cargo.toml

* Encode architecture compatibility in Cargo.toml

* Move semver_checked into Cargo.toml

* Cache parsed tomls

* Parse simple feature sets from Cargo.toml

* Move all basic feature rules to Cargo.toml

* Add check configs

* Limit command length on Windows

* Update cargo.rs

* Add clippy configs

* Use a single syntax, use a single doc-config line

* Fix known problems

* Run cargo check in CI command

* Fix more problems

* Fix esp-storage
This commit is contained in:
Dániel Buga 2025-09-18 18:15:35 +02:00 committed by GitHub
parent 9e892db654
commit 69776eb638
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 589 additions and 325 deletions

View File

@ -10,6 +10,16 @@ categories = ["embedded", "memory-management", "no-std"]
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
doc-config = { features = ["defmt"] }
check-configs = [
{ features = [] },
{ features = ["defmt"] }
]
clippy-configs = [
{ features = ["defmt"] },
]
[package.metadata.docs.rs]
default-target = "riscv32imc-unknown-none-elf"
features = ["nightly"]

View File

@ -10,6 +10,19 @@ categories = ["embedded", "hardware-support", "no-std"]
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
doc-config = { features = ["defmt"] }
check-configs = [
{ features = ["println", "esp-println/auto"] },
{ features = ["defmt"] },
{ features = ["defmt", "panic-handler", "custom-halt"] },
{ features = ["defmt", "panic-handler", "halt-cores"] },
{ features = ["defmt", "panic-handler", "semihosting"] }
]
clippy-configs = [
{ features = ["panic-handler", "halt-cores", "defmt"] },
]
[package.metadata.docs.rs]
default-target = "riscv32imc-unknown-none-elf"
features = ["esp32c3", "panic-handler", "exception-handler", "println", "esp-println/uart"]
@ -60,6 +73,7 @@ print-float-registers = [] # TODO support esp32p4
# additional functionality:
## Print messages in red
colors = []
# TODO: these features assume panic-handler is enabled but they don't enforce it.
## Invoke the extern function `custom_halt()` instead of doing a loop {} in case of a panic. This feature does not imply the `halt-cores` feature.
custom-halt = []
## Invoke the extern function `custom_pre_backtrace()` before handling a panic

View File

@ -120,7 +120,7 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! {
println!("0x{:x}", frame.program_counter());
}
abort();
abort()
}
// Ensure that the address is in DRAM.
@ -186,7 +186,7 @@ fn abort() -> ! {
cfg_if::cfg_if! {
if #[cfg(feature = "semihosting")] {
critical_section::with(|_| {
arch::interrupt_free(|| {
semihosting::process::abort();
});
} else if #[cfg(feature = "halt-cores")] {
@ -201,5 +201,8 @@ fn abort() -> ! {
}
#[allow(unreachable_code)]
arch::interrupt_free(|| loop {})
arch::interrupt_free(|| {
#[allow(clippy::empty_loop)]
loop {}
})
}

View File

@ -10,6 +10,17 @@ categories = ["embedded", "hardware-support", "no-std"]
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
doc-config = { features = ["defmt", "validation"] }
check-configs = [
{ features = [] },
{ features = ["log-04"] },
{ features = ["defmt", "validation"] },
]
clippy-configs = [
{ features = ["defmt", "validation"] },
]
[package.metadata.docs.rs]
default-target = "riscv32imac-unknown-none-elf"

View File

@ -8,6 +8,16 @@ documentation = "https://docs.espressif.com/projects/rust/esp-config/latest/"
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
doc-config = { features = ["build"] }
check-configs = [
{ features = [] },
{ features = ["build"] },
]
clippy-configs = [
{ features = ["tui"] },
]
[lib]
bench = false
test = true

View File

@ -70,7 +70,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.map(|entry| entry.file_name().to_string_lossy().to_string())
.collect();
if files.len() > 0 {
if !files.is_empty() {
let terminal = tui::init_terminal()?;
let mut chooser = tui::ConfigChooser::new(files);
config_file = chooser.run(terminal)?;
@ -134,7 +134,7 @@ fn apply_config(
previous_cfg: Vec<CrateConfig>,
config_toml_path: &PathBuf,
) -> Result<(), Box<dyn Error>> {
let mut config = std::fs::read_to_string(&config_toml_path)?
let mut config = std::fs::read_to_string(config_toml_path)?
.as_str()
.parse::<DocumentMut>()?;
@ -171,7 +171,7 @@ fn apply_config(
}
}
std::fs::write(&config_toml_path, config.to_string().as_bytes())?;
std::fs::write(config_toml_path, config.to_string().as_bytes())?;
Ok(())
}

View File

@ -10,6 +10,19 @@ categories = ["asynchronous", "embedded", "hardware-support", "no-std"]
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
doc-config = { features = ["esp-hal/unstable", "esp-hal/rt", "defmt", "executors"] }
check-configs = [
{ features = ["esp-hal/unstable", "esp-hal/rt"] },
{ features = ["esp-hal/unstable", "esp-hal/rt", "defmt"] },
{ features = ["esp-hal/unstable", "esp-hal/rt", "log-04"] },
{ features = ["esp-hal/unstable", "esp-hal/rt", "defmt", "executors"] },
{ features = ["esp-hal/unstable", "esp-hal/rt", "log-04", "executors"] },
]
clippy-configs = [
{ features = ["esp-hal/unstable", "esp-hal/rt", "log-04", "executors"] },
]
[package.metadata.docs.rs]
default-target = "riscv32imac-unknown-none-elf"
features = ["esp32c6"]

View File

@ -8,6 +8,16 @@ documentation = "https://docs.espressif.com/projects/rust/esp-hal-procmacros/lat
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
doc-config = { features = ["embassy"] }
check-configs = [
{ features = [] },
{ features = ["embassy"] }
]
clippy-configs = [
{ features = ["embassy"] },
]
[package.metadata.docs.rs]
features = ["embassy", "has-ulp-core", "interrupt", "ram", "is-ulp-core"]

View File

@ -11,6 +11,30 @@ repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
exclude = [ "api-baseline", "MIGRATING-*", "CHANGELOG.md" ]
[package.metadata.espressif]
semver_checked = true
doc-config = { features = ["unstable", "rt"], append = [
{ if = 'chip_has("psram")', features = ["psram"] },
{ if = 'chip_has("soc_has_usb0")', features = ["__usb_otg"] },
{ if = 'chip_has("bt")', features = ["__bluetooth"] },
] }
check-configs = [
{ features = [] },
{ features = ["rt"] },
{ features = ["unstable", "rt"] },
{ features = ["unstable", "rt", "psram"], if = 'chip_has("psram")' },
{ features = ["unstable", "rt", "__usb_otg"], if = 'chip_has("soc_has_usb0")' },
{ features = ["unstable", "rt", "__bluetooth"], if = 'chip_has("bt")' },
]
# Prefer fewer, but more complex clippy rules. A clippy run should cover as much code as possible.
clippy-configs = [
{ features = ["unstable", "rt"], append = [
{ if = 'chip_has("psram")', features = ["psram"] },
{ if = 'chip_has("soc_has_usb0")', features = ["__usb_otg"] },
{ if = 'chip_has("bt")', features = ["__bluetooth"] },
] },
]
[package.metadata.docs.rs]
default-target = "riscv32imac-unknown-none-elf"
features = ["esp32c6", "unstable"]

View File

@ -47,12 +47,14 @@
use core::ops::Range;
#[cfg(feature = "psram")]
#[cfg_attr(docsrs, doc(cfg(feature = "psram")))]
#[cfg_attr(esp32, path = "esp32.rs")]
#[cfg_attr(esp32s2, path = "esp32s2.rs")]
#[cfg_attr(esp32s3, path = "esp32s3.rs")]
pub(crate) mod implem;
#[cfg(feature = "psram")]
#[cfg_attr(docsrs, doc(cfg(feature = "psram")))]
pub use implem::*;
/// Size of PSRAM

View File

@ -15,6 +15,21 @@ default-target = "riscv32imac-unknown-none-elf"
features = ["esp32c6"]
rustdoc-args = ["--cfg", "docsrs"]
[package.metadata.espressif]
targets_lp_core = true
doc-config = { features = ["embedded-hal"], append = [
{ features = ["embedded-io"], if = 'chip_has("lp_core")' },
] }
check-configs = [
{ features = [] },
{ features = ["embedded-hal"] },
{ features = ["embedded-io"] },
{ features = ["embedded-hal", "embedded-io"] },
]
clippy-configs = [
{ features = ["embedded-hal", "embedded-io"] },
]
[lib]
bench = false
test = false

View File

@ -8,6 +8,13 @@ documentation = "https://docs.espressif.com/projects/rust/esp-metadata-generated
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
check-configs = [
{ features = [] },
{ features = ["build-script"] },
]
clippy-configs = [] # don't waste time on this
[dependencies]
[build-dependencies]

View File

@ -8,6 +8,13 @@ documentation = "https://docs.espressif.com/projects/rust/esp-metadata/latest/"
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
check-configs = [
{ features = [] },
{ features = ["clap"] },
]
clippy-configs = [] # don't waste time on this
[dependencies]
anyhow = "1.0"
clap = { version = "4.5", features = ["derive"], optional = true }

View File

@ -10,6 +10,16 @@ categories = ["embedded", "hardware-support", "no-std"]
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
doc-config = { features = ["esp-hal/unstable"] }
check-configs = [
{ features = ["esp-hal/unstable"] },
{ features = ["esp-hal/unstable", "esp-alloc"] },
]
clippy-configs = [
{ features = ["esp-hal/unstable", "esp-alloc", "defmt"] },
]
[package.metadata.docs.rs]
default-target = "riscv32imac-unknown-none-elf"
features = ["esp32c6"]

View File

@ -11,6 +11,21 @@ repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
links = "esp-println"
[package.metadata.espressif]
doc-config = { features = ["auto", "defmt-espflash", "critical-section"] }
check-configs = [
{ features = ["auto"] },
{ features = ["jtag-serial"], if = 'chip_has("soc_has_usb_serial_jtag")' },
{ features = ["uart"] },
{ features = ["no-op"] },
{ features = ["auto", "log-04", "timestamp"] },
{ features = ["auto", "defmt-espflash", "timestamp"] },
]
clippy-configs = [
{ features = ["auto", "log-04", "timestamp"] },
{ features = ["auto", "defmt-espflash", "timestamp"] },
]
[package.metadata.docs.rs]
cargo-args = ["-Z", "build-std=core"]
default-target = "riscv32imc-unknown-none-elf"

View File

@ -15,7 +15,7 @@ macro_rules! assert_unique_used_features {
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Ensure that only a single communication method is specified
assert_unique_used_features!("jtag-serial", "uart", "auto");
assert_unique_used_features!("jtag-serial", "uart", "auto", "no-op");
let chip = esp_metadata_generated::Chip::from_cargo_feature()?;
// Ensure that, if the `jtag-serial` communication method feature is enabled,

View File

@ -129,6 +129,9 @@ type PrinterImpl = uart_printer::Printer;
#[cfg(feature = "auto")]
type PrinterImpl = auto_printer::Printer;
#[cfg(feature = "no-op")]
type PrinterImpl = noop::Printer;
#[cfg(all(
feature = "auto",
any(
@ -469,6 +472,17 @@ mod uart_printer {
}
}
#[cfg(feature = "no-op")]
mod noop {
pub struct Printer;
impl Printer {
pub fn write_bytes_in_cs(_bytes: &[u8], _token: super::LockToken<'_>) {}
pub fn flush(_token: super::LockToken<'_>) {}
}
}
use core::marker::PhantomData;
#[derive(Clone, Copy)]

View File

@ -52,7 +52,7 @@ impl log::Log for EspEnvLogger {
#[allow(unused)]
fn log(&self, record: &log::Record) {
if self.enabled(&record.metadata()) {
if self.enabled(record.metadata()) {
print_log_record(record);
}
}
@ -61,28 +61,26 @@ impl log::Log for EspEnvLogger {
}
fn print_log_record(record: &log::Record) {
const RESET: &str = "\u{001B}[0m";
const RED: &str = "\u{001B}[31m";
const GREEN: &str = "\u{001B}[32m";
const YELLOW: &str = "\u{001B}[33m";
const BLUE: &str = "\u{001B}[34m";
const CYAN: &str = "\u{001B}[35m";
let (color, reset) = if cfg!(feature = "colors") {
const RESET: &str = "\u{001B}[0m";
const RED: &str = "\u{001B}[31m";
const GREEN: &str = "\u{001B}[32m";
const YELLOW: &str = "\u{001B}[33m";
const BLUE: &str = "\u{001B}[34m";
const CYAN: &str = "\u{001B}[35m";
#[cfg(feature = "colors")]
let color = match record.level() {
log::Level::Error => RED,
log::Level::Warn => YELLOW,
log::Level::Info => GREEN,
log::Level::Debug => BLUE,
log::Level::Trace => CYAN,
let color = match record.level() {
log::Level::Error => RED,
log::Level::Warn => YELLOW,
log::Level::Info => GREEN,
log::Level::Debug => BLUE,
log::Level::Trace => CYAN,
};
let reset = RESET;
(color, reset)
} else {
("", "")
};
#[cfg(feature = "colors")]
let reset = RESET;
#[cfg(not(feature = "colors"))]
let color = "";
#[cfg(not(feature = "colors"))]
let reset = "";
#[cfg(feature = "timestamp")]
println!(

View File

@ -10,4 +10,8 @@ categories = ["embedded", "hardware-support", "no-std"]
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
check-configs = [{ features = [] }]
clippy-configs = [{ features = [] }]
[dependencies]

View File

@ -10,6 +10,30 @@ categories = ["embedded", "hardware-support", "no-std"]
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
doc-config = { features = ["esp-hal/unstable", "esp-hal/rt", "defmt"], append = [
{ if = 'chip_has("wifi")', features = ["wifi", "wifi-eap", "esp-now", "sniffer", "smoltcp/proto-ipv4", "smoltcp/proto-ipv6"] },
{ if = 'chip_has("bt")', features = ["ble"] },
{ if = 'chip_has("ieee802154")', features = ["ieee802154", "__docs_build"] },
{ if = 'chip_has("wifi") && chip_has("bt")', features = ["coex"] },
{ if = 'chip_has("wifi") || chip_has("bt") || chip_has("ieee802154")', features = ["unstable"] },
] }
check-configs = [
{ features = ["esp-hal/unstable", "esp-hal/rt"] },
{ features = ["esp-hal/unstable", "esp-hal/rt", "defmt"] },
{ features = ["esp-hal/unstable", "esp-hal/rt", "wifi"], if = 'chip_has("wifi")' },
{ features = ["esp-hal/unstable", "esp-hal/rt", "unstable", "defmt", "wifi", "wifi-eap", "esp-now", "sniffer", "smoltcp/proto-ipv4", "smoltcp/proto-ipv6"], if = 'chip_has("wifi")' },
{ features = ["esp-hal/unstable", "esp-hal/rt", "unstable", "ble"], if = 'chip_has("bt")' },
{ features = ["esp-hal/unstable", "esp-hal/rt", "unstable", "ieee802154"], if = 'chip_has("ieee802154")' },
{ features = ["esp-hal/unstable", "esp-hal/rt", "unstable", "defmt", "wifi", "wifi-eap", "esp-now", "sniffer", "smoltcp/proto-ipv4", "smoltcp/proto-ipv6", "ble", "coex"], if = 'chip_has("wifi") && chip_has("bt")' },
{ features = ["esp-hal/unstable", "esp-hal/rt", "wifi-eap", "unstable"], if = 'chip_has("wifi")' },
]
clippy-configs = [
{ features = ["esp-hal/unstable", "esp-hal/rt"] },
{ features = ["esp-hal/unstable", "esp-hal/rt", "wifi"], if = 'chip_has("wifi")' },
{ features = ["esp-hal/unstable", "esp-hal/rt", "wifi-eap", "unstable"], if = 'chip_has("wifi")' },
]
[lib]
bench = false
test = false

View File

@ -11,6 +11,17 @@ repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
links = "esp-riscv-rt"
[package.metadata.espressif]
doc-config = { features = ["rtc-ram"] }
requires_target = ["riscv32imc-unknown-none-elf", "riscv32imac-unknown-none-elf"]
check-configs = [
{ features = [] },
{ features = ["rtc-ram"] },
{ features = ["no-mie-mip"] },
{ features = ["rtc-ram", "no-mie-mip"] },
]
clippy-configs = [{ features = ["rtc-ram"] }]
[lib]
bench = false
test = false

View File

@ -12,6 +12,10 @@ license = "MIT OR Apache-2.0"
links = "esp_rom_sys"
[package.metadata.espressif]
check-configs = [{ features = [] }]
clippy-configs = [{ features = [] }]
[package.metadata.docs.rs]
default-target = "riscv32imac-unknown-none-elf"
features = ["esp32c6"]

View File

@ -10,6 +10,25 @@ categories = ["embedded", "hardware-support", "no-std"]
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
[package.metadata.espressif]
check-configs = [
{ features = [], append = [
{ features = ["portable-atomic/unsafe-assume-single-core"], if = 'chip == "esp32c2" || chip == "esp32c3" || chip == "esp32s2"' }
] },
{ features = ["critical-section"], append = [
{ features = ["portable-atomic/unsafe-assume-single-core"], if = 'chip == "esp32c2" || chip == "esp32c3" || chip == "esp32s2"' }
] },
{ features = ["bytewise-read"], append = [
{ features = ["portable-atomic/unsafe-assume-single-core"], if = 'chip == "esp32c2" || chip == "esp32c3" || chip == "esp32s2"' }
] },
{ features = ["critical-section", "bytewise-read"], append = [
{ features = ["portable-atomic/unsafe-assume-single-core"], if = 'chip == "esp32c2" || chip == "esp32c3" || chip == "esp32s2"' }
] },
]
clippy-configs = [{ features = ["critical-section", "bytewise-read"], append = [
{ features = ["portable-atomic/unsafe-assume-single-core"], if = 'chip == "esp32c2" || chip == "esp32c3" || chip == "esp32s2"' }
]}]
[package.metadata.docs.rs]
default-target = "riscv32imac-unknown-none-elf"
features = ["esp32c6"]

View File

@ -25,7 +25,7 @@ fn maybe_with_critical_section<R>(f: impl FnOnce() -> R) -> R {
{
static LOCK: esp_sync::RawMutex = esp_sync::RawMutex::new();
return LOCK.lock(f);
LOCK.lock(f)
}
#[cfg(not(feature = "critical-section"))]

View File

@ -10,6 +10,14 @@ repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
exclude = [ "MIGRATING-*", "CHANGELOG.md" ]
[package.metadata.espressif]
check-configs = [
{ features = [] },
{ features = ["log-04"] },
{ features = ["defmt"] },
]
clippy-configs = [{ features = ["defmt"] }]
[dependencies]
cfg-if = "1"
document-features = "0.2"

View File

@ -16,12 +16,14 @@ kuchikiki = { version = "0.8.2", optional = true }
log = "0.4.22"
minijinja = { version = "2.5.0", default-features = false }
opener = { version = "0.7.2", optional = true }
parking_lot = "0.12.3"
prettyplease = { version = "0.2.34" }
regex = { version = "1.11.1", optional = true }
rocket = { version = "0.5.1", optional = true }
semver = { version = "1.0.23", features = ["serde"] }
serde = { version = "1.0.215", default-features = false, features = ["derive"] }
serde_json = "1.0.70"
somni-expr = { version = "0.1.0" }
strum = { version = "0.27.1", features = ["derive"] }
syn = { version = "2", default-features = false, features = ["full", "parsing"] }
toml_edit = { version = "0.22.22", features = ["serde"] }

View File

@ -10,7 +10,7 @@ use std::{
use anyhow::{Context as _, Result, bail};
use clap::ValueEnum as _;
use serde::{Deserialize, Serialize};
use toml_edit::{DocumentMut, Formatted, Item, Value};
use toml_edit::{DocumentMut, Formatted, Item, Table, Value};
use crate::{Package, windows_safe_path};
@ -367,29 +367,19 @@ impl CargoCommandBatcher {
}
let mut command = Vec::new();
if let Some(tc) = key.toolchain.as_ref() {
command.push(format!("+{tc}"));
}
command.push("batch".to_string());
if !key.config_file.is_empty()
&& let Some(config_path) = &group[0].config_path
{
// All grouped projects have the same config file content, pick one:
command.push("--config".to_string());
command.push(config_path.display().to_string());
}
let mut batch_len = 0;
let mut commands_in_batch = 0;
command.extend_from_slice(&key.config);
// Windows be Windows, it has a command length limit.
let limit = if cfg!(target_os = "windows") {
Some(8191)
} else {
None
};
for item in group.iter() {
// Only build and doc can be batched
let batchable = [
"build", "doc",
// "check" // soon(TM)
];
// Only some commands can be batched
let batchable = ["build", "doc", "check"];
if !batchable
.iter()
.any(|&subcommand| subcommand == item.subcommand)
@ -398,16 +388,56 @@ impl CargoCommandBatcher {
continue;
}
// Build the new command
let mut c = item.clone();
c.toolchain = None;
c.configs = Vec::new();
c.config_path = None;
let args = c.build();
let command_chars = 4 + args.iter().map(|arg| arg.len() + 1).sum::<usize>();
if !command.is_empty()
&& let Some(limit) = limit
&& batch_len + command_chars > limit
{
// Command would be too long, cut here.
all.push(BuiltCommand {
artifact_name: String::from("batch"),
command: std::mem::take(&mut command),
env_vars: key.env_vars.clone(),
});
}
// Set up head part if empty
if command.is_empty() {
if let Some(tc) = key.toolchain.as_ref() {
command.push(format!("+{tc}"));
}
command.push("batch".to_string());
if !key.config_file.is_empty()
&& let Some(config_path) = &group[0].config_path
{
// All grouped projects have the same config file content, pick one:
command.push("--config".to_string());
command.push(config_path.display().to_string());
}
command.extend_from_slice(&key.config);
commands_in_batch = 0;
batch_len = command.iter().map(|s| s.len() + 1).sum::<usize>() - 1;
}
// Append the new command
command.push("---".to_string());
command.extend_from_slice(&c.build());
command.extend_from_slice(&args);
commands_in_batch += 1;
batch_len += command_chars;
}
if commands_in_batch > 0 {
@ -484,9 +514,9 @@ impl Drop for CargoCommandBatcher {
}
/// A representation of a Cargo.toml file for a specific package.
pub struct CargoToml<'a> {
pub struct CargoToml {
/// The workspace path where the Cargo.toml is located.
pub workspace: &'a Path,
pub workspace: PathBuf,
/// The package this Cargo.toml belongs to.
pub package: Package,
/// The parsed Cargo.toml manifest.
@ -496,9 +526,9 @@ pub struct CargoToml<'a> {
const DEPENDENCY_KINDS: [&'static str; 3] =
["dependencies", "dev-dependencies", "build-dependencies"];
impl<'a> CargoToml<'a> {
impl CargoToml {
/// Load and parse the Cargo.toml for the specified package in the given workspace.
pub fn new(workspace: &'a Path, package: Package) -> Result<Self> {
pub fn new(workspace: &Path, package: Package) -> Result<Self> {
let package_path = workspace.join(package.to_string());
let manifest_path = package_path.join("Cargo.toml");
if !manifest_path.exists() {
@ -514,11 +544,24 @@ impl<'a> CargoToml<'a> {
Self::from_str(workspace, package, &manifest)
}
pub fn espressif_metadata(&self) -> Option<&Table> {
let Some(package) = self.manifest.get("package") else {
return None;
};
let Some(metadata) = package.get("metadata") else {
return None;
};
let Some(espressif) = metadata.get("espressif") else {
return None;
};
Some(espressif.as_table()?)
}
/// Create a `CargoToml` instance from a manifest string.
pub fn from_str(workspace: &'a Path, package: Package, manifest: &str) -> Result<Self> {
pub fn from_str(workspace: &Path, package: Package, manifest: &str) -> Result<Self> {
// Parse the manifest string into a mutable TOML document.
Ok(Self {
workspace,
workspace: workspace.to_path_buf(),
package,
manifest: manifest
.parse::<DocumentMut>()

View File

@ -65,7 +65,7 @@ pub fn bump_version(workspace: &Path, args: BumpVersionArgs) -> Result<()> {
/// Update the specified package by bumping its version, updating its changelog,
pub fn update_package(
package: &mut CargoToml<'_>,
package: &mut CargoToml,
version: &VersionBump,
dry_run: bool,
) -> Result<semver::Version> {
@ -77,7 +77,7 @@ pub fn update_package(
Ok(new_version)
}
fn check_crate_before_bumping(manifest: &mut CargoToml<'_>) -> Result<()> {
fn check_crate_before_bumping(manifest: &mut CargoToml) -> Result<()> {
// Collect errors into a vector to preserve order.
let mut errors = Vec::new();
@ -175,7 +175,7 @@ fn check_dependency_before_bumping(item: &Item) -> Result<()> {
/// Bump the version of the specified package by the specified amount.
fn bump_crate_version(
bumped_package: &mut CargoToml<'_>,
bumped_package: &mut CargoToml,
amount: &VersionBump,
dry_run: bool,
) -> Result<semver::Version> {
@ -197,7 +197,7 @@ fn bump_crate_version(
let package_name = bumped_package.package.to_string();
for pkg in Package::iter() {
let mut dependent = CargoToml::new(bumped_package.workspace, pkg)
let mut dependent = CargoToml::new(&bumped_package.workspace, pkg)
.with_context(|| format!("Could not load Cargo.toml of {pkg}"))?;
if dependent.change_version_of_dependency(&package_name, &version) {
@ -268,7 +268,7 @@ pub fn do_version_bump(version: &semver::Version, amount: &VersionBump) -> Resul
}
fn finalize_changelog(
bumped_package: &CargoToml<'_>,
bumped_package: &CargoToml,
new_version: &semver::Version,
dry_run: bool,
) -> Result<()> {
@ -306,7 +306,7 @@ fn finalize_changelog(
}
fn finalize_placeholders(
bumped_package: &CargoToml<'_>,
bumped_package: &CargoToml,
new_version: &semver::Version,
dry_run: bool,
) -> Result<()> {
@ -405,7 +405,7 @@ mod tests {
let mut doc = CargoToml {
manifest: toml.parse::<DocumentMut>().unwrap(),
package: Package::EspHal,
workspace: Path::new(""),
workspace: PathBuf::new(),
};
let errors = check_crate_before_bumping(&mut doc);
pretty_assertions::assert_eq!(

View File

@ -81,7 +81,7 @@ pub fn plan(workspace: &Path, args: PlanArgs) -> Result<()> {
let mut packages_to_release = args
.packages
.iter()
.filter(|p| p.is_published(workspace))
.filter(|p| p.is_published())
.flat_map(|p| related_crates(workspace, *p))
.collect::<Vec<_>>();

View File

@ -23,7 +23,7 @@ pub fn publish(workspace: &Path, args: PublishArgs) -> Result<()> {
let package_path = windows_safe_path(&workspace.join(&package_name));
ensure!(
args.package.is_published(workspace),
args.package.is_published(),
"Invalid package '{}' specified, this package should not be published!",
args.package
);

View File

@ -33,7 +33,7 @@ pub fn tag_releases(workspace: &Path, mut args: TagReleasesArgs) -> Result<()> {
// If a package does not require documentation, this also means that it is not
// published (maybe this function needs a better name), so we can skip tagging
// it:
if !package.is_published(workspace) {
if !package.is_published() {
continue;
}

View File

@ -62,7 +62,7 @@ pub fn run_doc_tests(workspace: &Path, args: DocTestArgs) -> Result<()> {
let target = args.package.target_triple(&chip)?;
let mut features = args
.package
.feature_rules(&esp_metadata::Config::for_chip(&chip));
.doc_feature_rules(&esp_metadata::Config::for_chip(&chip));
features.push(chip.to_string());
// We need `nightly` for building the doc tests, unfortunately:

View File

@ -39,7 +39,7 @@ pub fn build_documentation(
for package in packages {
// Not all packages need documentation built:
if !package.is_published(workspace) {
if !package.is_published() {
continue;
}
@ -205,9 +205,9 @@ 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.doc_feature_rules(Config::for_chip(chip)));
} else {
features.extend(package.feature_rules(&Config::empty()));
features.extend(package.doc_feature_rules(&Config::empty()));
}
// Build up an array of command-line arguments to pass to `cargo`:
@ -326,7 +326,7 @@ pub fn build_documentation_index(workspace: &Path, packages: &mut [Package]) ->
for package in packages {
log::debug!("Building documentation index for package '{package}'");
// Not all packages have documentation built:
if !package.is_published(workspace) {
if !package.is_published() {
continue;
}
@ -462,7 +462,7 @@ fn generate_documentation_meta_for_index(workspace: &Path) -> Result<Vec<Value>>
for package in Package::iter() {
// Not all packages have documentation built:
if !package.is_published(workspace) {
if !package.is_published() {
continue;
}

View File

@ -1,4 +1,5 @@
use std::{
collections::HashMap,
fs,
path::{Path, PathBuf},
};
@ -6,7 +7,9 @@ use std::{
use anyhow::{Context, Result, anyhow};
use cargo::CargoAction;
use esp_metadata::{Chip, Config, TokenStream};
use parking_lot::{MappedMutexGuard, Mutex, MutexGuard};
use serde::{Deserialize, Serialize};
use toml_edit::{InlineTable, Item, Value};
use crate::{
cargo::{CargoArgsBuilder, CargoCommandBatcher, CargoToml},
@ -72,24 +75,31 @@ pub enum Package {
impl Package {
/// Does the package have chip-specific cargo features?
pub fn has_chip_features(&self) -> bool {
use Package::*;
use strum::IntoEnumIterator;
let chips = Chip::iter()
.map(|chip| chip.to_string())
.collect::<Vec<_>>();
let toml = self.toml();
let Some(Item::Table(features)) = toml.manifest.get("features") else {
return false;
};
matches!(
self,
EspBacktrace
| EspBootloaderEspIdf
| EspAlloc
| EspHal
| EspHalEmbassy
| EspMetadataGenerated
| EspRomSys
| EspLpHal
| EspPrintln
| EspPreempt
| EspStorage
| EspSync
| EspRadio
)
// This is intended to opt-out in case there are features that look like chip names, but
// aren't supposed to be handled like them.
if let Some(metadata) = toml.espressif_metadata() {
if let Some(Item::Value(ov)) = metadata.get("has_chip_features") {
let Value::Boolean(ov) = ov else {
log::warn!("Invalid value for 'has_chip_features' in metadata");
return false;
};
return *ov.value();
}
}
features
.iter()
.any(|(feature, _)| chips.iter().any(|c| c == feature))
}
/// Does the package have inline assembly?
@ -177,18 +187,13 @@ impl Package {
/// Should documentation be built for the package, and should the package be
/// published?
pub fn is_published(&self, workspace: &Path) -> bool {
pub fn is_published(&self) -> bool {
if *self == Package::Examples {
// The `examples/` directory does not contain `Cargo.toml` in its root, and even if it
// did nothing in this directory will be published.
false
} else {
// TODO: we should use some sort of cache instead of parsing the TOML every
// time, but for now this should be good enough.
let toml =
crate::cargo::CargoToml::new(workspace, *self).expect("Failed to parse Cargo.toml");
toml.is_published()
self.toml().is_published()
}
}
@ -201,225 +206,192 @@ impl Package {
}
}
/// Given a device config, return the features which should be enabled for
/// this package.
pub fn feature_rules(&self, config: &Config) -> Vec<String> {
let mut features = vec![];
match self {
Package::EspBacktrace => features.push("defmt".to_owned()),
Package::EspConfig => features.push("build".to_owned()),
Package::EspHal => {
features.push("unstable".to_owned());
features.push("rt".to_owned());
if config.contains("psram") {
// TODO this doesn't test octal psram (since `ESP_HAL_CONFIG_PSRAM_MODE`
// defaults to `quad`) as it would require a separate build
features.push("psram".to_owned())
}
if config.contains("usb0") {
features.push("__usb_otg".to_owned());
}
if config.contains("bt") {
features.push("__bluetooth".to_owned());
}
fn parse_conditional_features(table: &InlineTable, config: &Config) -> Option<Vec<String>> {
let mut eval_context = somni_expr::Context::new();
eval_context.add_function("chip_has", |symbol: &str| {
config.all().iter().any(|sym| sym == symbol)
});
eval_context.add_variable("chip", config.name());
if let Some(condition) = table.get("if") {
let Some(expr) = condition.as_str() else {
panic!("`if` condition must be a string.");
};
if !eval_context
.evaluate::<bool>(expr)
.expect("Failed to evaluate expression")
{
return None;
}
Package::EspRadio => {
features.push("esp-hal/unstable".to_owned());
features.push("esp-hal/rt".to_owned());
features.push("defmt".to_owned());
if config.contains("wifi") {
features.push("wifi".to_owned());
features.push("wifi-eap".to_owned());
features.push("esp-now".to_owned());
features.push("sniffer".to_owned());
features.push("smoltcp/proto-ipv4".to_owned());
features.push("smoltcp/proto-ipv6".to_owned());
}
if config.contains("bt") {
features.push("ble".to_owned());
}
if config.contains("ieee802154") {
features.push("ieee802154".to_owned());
// allow wifi + 802.15.4
features.push("__docs_build".to_owned());
}
if config.contains("wifi") && config.contains("bt") {
features.push("coex".to_owned());
}
if features.iter().any(|f| {
f == "csi"
|| f == "ble"
|| f == "esp-now"
|| f == "sniffer"
|| f == "coex"
|| f == "ieee802154"
}) {
features.push("unstable".to_owned());
}
}
Package::EspHalProcmacros => {
features.push("embassy".to_owned());
}
Package::EspHalEmbassy => {
features.push("esp-hal/unstable".to_owned());
features.push("esp-hal/rt".to_owned());
features.push("defmt".to_owned());
features.push("executors".to_owned());
}
Package::EspLpHal => {
if config.contains("lp_core") {
features.push("embedded-io".to_owned());
}
features.push("embedded-hal".to_owned());
}
Package::EspPrintln => {
features.push("auto".to_owned());
features.push("defmt-espflash".to_owned());
features.push("critical-section".to_owned());
}
Package::EspStorage => {
if config.name() == "esp32c2"
|| config.name() == "esp32c3"
|| config.name() == "esp32s2"
{
features.push("portable-atomic/unsafe-assume-single-core".to_owned());
}
}
Package::EspBootloaderEspIdf => {
features.push("defmt".to_owned());
features.push("validation".to_owned());
}
Package::EspAlloc => {
features.push("defmt".to_owned());
}
Package::EspMetadataGenerated => {}
Package::EspPreempt => features.push("esp-hal/unstable".to_owned()),
Package::EspRiscvRt => features.push("rtc-ram".to_owned()),
_ => {}
}
log::debug!("Features for package '{}': {:?}", self, features);
let Some(config_features) = table.get("features") else {
panic!("Missing features array.");
};
let Value::Array(config_features) = config_features else {
panic!("features must be an array.");
};
let mut features = Vec::new();
for feature in config_features {
let feature = feature.as_str().expect("features must be strings.");
features.push(feature.to_owned());
}
Some(features)
}
fn parse_feature_set(table: &InlineTable, config: &Config) -> Option<Vec<String>> {
// Base features. If their condition is not met, the whole item is ignored.
let mut features = Self::parse_conditional_features(table, config)?;
if let Some(conditionals) = table.get("append") {
// Optional features. If their conditions are met, they are appended to the base
// features.
let Value::Array(conditionals) = conditionals else {
panic!("append must be an array.");
};
for cond in conditionals {
let Value::InlineTable(cond_table) = cond else {
panic!("append items must be inline tables.");
};
if let Some(cond_features) = Self::parse_conditional_features(cond_table, config) {
features.extend(cond_features);
}
}
};
Some(features)
}
/// Given a device config, return the features which should be enabled for
/// this package.
///
/// Features are read from Cargo.toml metadata, from the `doc-config` table. Currently only
/// one feature set is supported.
// TODO: perhaps we should use the docs.rs metadata for doc features for packages that have no
// chip-specific features.
pub fn doc_feature_rules(&self, config: &Config) -> Vec<String> {
let mut features = vec![];
let toml = self.toml();
if let Some(metadata) = toml.espressif_metadata()
&& let Some(config_meta) = metadata.get("doc-config")
{
let Item::Value(Value::InlineTable(table)) = config_meta else {
panic!("doc-config must be inline tables.");
};
if let Some(fs) = Self::parse_feature_set(table, config) {
features = fs;
}
} else {
// Nothing
}
log::debug!("Doc features for package '{}': {:?}", self, features);
features
}
/// Additional feature rules to test subsets of features for a package.
pub fn check_feature_rules(&self, config: &Config) -> Vec<Vec<String>> {
let mut cases = Vec::new();
let mut cases = vec![];
// For now we run a lot of checks, but that will change.
cases.push(self.feature_rules(config));
let toml = self.toml();
if let Some(metadata) = toml.espressif_metadata()
&& let Some(config_meta) = metadata.get("check-configs")
{
let Item::Value(Value::Array(tables)) = config_meta else {
panic!(
"check-configs must be an array of tables. {:?}",
config_meta
);
};
match self {
Package::EspHal => {
// This checks if the `esp-hal` crate compiles with the no features (other than the
// chip selection)
// This tests that disabling the `rt` feature works
cases.push(vec![]);
// This checks if the `esp-hal` crate compiles _without_ the `unstable` feature
// enabled
cases.push(vec!["rt".to_owned()]);
}
Package::EspRadio => {
// Minimal set of features that when enabled _should_ still compile:
cases.push(vec!["esp-hal/rt".to_owned(), "esp-hal/unstable".to_owned()]);
if config.contains("wifi") {
// This tests if `wifi` feature works without `esp-radio/unstable`
cases.push(vec![
"esp-hal/rt".to_owned(),
"esp-hal/unstable".to_owned(),
"wifi".to_owned(),
]);
// This tests `wifi-eap` feature
cases.push(vec![
"esp-hal/rt".to_owned(),
"esp-hal/unstable".to_owned(),
"wifi-eap".to_owned(),
"unstable".to_owned(),
]);
for table in tables {
let Value::InlineTable(table) = table else {
panic!("check-configs items must be inline tables.");
};
if let Some(features) = Self::parse_feature_set(table, config) {
cases.push(features);
}
}
Package::EspMetadataGenerated => {
cases.push(vec!["build-script".to_owned()]);
}
Package::EspPreempt => {
cases.push(vec!["esp-alloc".to_owned(), "esp-hal/unstable".to_owned()])
}
_ => {}
} else {
// Nothing specified, just test no features
cases.push(vec![]);
}
log::debug!("Lint feature cases for package '{}': {:?}", self, cases);
log::debug!("Check features for package '{}': {:?}", self, cases);
cases
}
/// Additional feature rules to test subsets of features for a package.
pub fn lint_feature_rules(&self, config: &Config) -> Vec<Vec<String>> {
let mut cases = Vec::new();
let mut cases = vec![];
// For now we run a lot of clippy checks, but that will change.
cases.push(self.feature_rules(config));
let toml = self.toml();
if let Some(metadata) = toml.espressif_metadata()
&& let Some(config_meta) = metadata.get("clippy-configs")
{
let Item::Value(Value::Array(tables)) = config_meta else {
panic!(
"clippy-configs must be an array of tables. {:?}",
config_meta
);
};
match self {
Package::EspHal => {
// This checks if the `esp-hal` crate compiles with the no features (other than the
// chip selection)
// This tests that disabling the `rt` feature works
cases.push(vec![]);
// This checks if the `esp-hal` crate compiles _without_ the `unstable` feature
// enabled
cases.push(vec!["rt".to_owned()]);
}
Package::EspRadio => {
// Minimal set of features that when enabled _should_ still compile:
cases.push(vec!["esp-hal/rt".to_owned(), "esp-hal/unstable".to_owned()]);
if config.contains("wifi") {
// This tests if `wifi` feature works without `esp-radio/unstable`
cases.push(vec![
"esp-hal/rt".to_owned(),
"esp-hal/unstable".to_owned(),
"wifi".to_owned(),
]);
// This tests `wifi-eap` feature
cases.push(vec![
"esp-hal/rt".to_owned(),
"esp-hal/unstable".to_owned(),
"wifi-eap".to_owned(),
"unstable".to_owned(),
]);
for table in tables {
let Value::InlineTable(table) = table else {
panic!("clippy-configs items must be inline tables.");
};
if let Some(features) = Self::parse_feature_set(table, config) {
cases.push(features);
}
}
Package::EspMetadataGenerated => {
cases.push(vec!["build-script".to_owned()]);
}
Package::EspPreempt => {
cases.push(vec!["esp-alloc".to_owned(), "esp-hal/unstable".to_owned()])
}
Package::EspStorage => {
// TODO: https://github.com/esp-rs/esp-hal/issues/4136
if config.name() == "esp32c2"
|| config.name() == "esp32c3"
|| config.name() == "esp32s2"
{
// println!("kokot usho");
cases.push(vec![
"defmt".to_owned(),
"portable-atomic/unsafe-assume-single-core".to_owned(),
]);
} else {
cases.push(vec!["defmt".to_owned()]);
}
}
_ => {}
}
log::debug!("Lint feature cases for package '{}': {:?}", self, cases);
log::debug!("Check features for package '{}': {:?}", self, cases);
cases
}
fn toml(&self) -> MappedMutexGuard<'_, CargoToml> {
static TOML: Mutex<Option<HashMap<Package, CargoToml>>> = Mutex::new(None);
let tomls = TOML.lock();
MutexGuard::map(tomls, |tomls| {
let tomls = tomls.get_or_insert_default();
tomls.entry(*self).or_insert_with(|| {
CargoToml::new(&std::env::current_dir().unwrap(), *self)
.expect("Failed to parse Cargo.toml")
})
})
}
fn targets_lp_core(&self) -> bool {
if *self == Package::Examples {
return false;
}
let toml = self.toml();
let Some(metadata) = toml.espressif_metadata() else {
return false;
};
let Some(Item::Value(targets_lp_core)) = metadata.get("targets_lp_core") else {
return false;
};
targets_lp_core
.as_bool()
.expect("targets_lp_core must be a boolean")
}
/// Return the target triple for a given package/chip pair.
pub fn target_triple(&self, chip: &Chip) -> Result<String> {
if *self == Package::EspLpHal {
if self.targets_lp_core() {
chip.lp_target().map(ToString::to_string)
} else {
Ok(chip.target())
@ -428,24 +400,29 @@ impl Package {
/// Validate that the specified chip is valid for the specified package.
pub fn validate_package_chip(&self, chip: &Chip) -> Result<()> {
let check = match self {
Package::EspLpHal => chip.has_lp_core(),
Package::XtensaLx | Package::XtensaLxRt | Package::XtensaLxRtProcMacros => {
chip.is_xtensa()
}
Package::EspRiscvRt => chip.is_riscv(),
_ => true,
};
if check {
Ok(())
} else {
Err(anyhow!(
"Invalid chip provided for package '{}': '{}'",
self,
chip
))
if *self == Package::Examples {
return Ok(());
}
if self.targets_lp_core() && !chip.has_lp_core() {
return Err(anyhow!(
"Package '{self}' requires an LP core, but '{chip}' does not have one",
));
}
let toml = self.toml();
if let Some(metadata) = toml.espressif_metadata()
&& let Some(Item::Value(Value::Array(targets))) = metadata.get("requires_target")
&& !targets.iter().any(|t| t.as_str() == Some(&chip.target()))
{
return Err(anyhow!(
"Package '{self}' is not compatible with {chip_target} chips",
chip_target = chip.target()
));
}
Ok(())
}
/// Creates a tag string for this [`Package`] combined with a semantic version.
@ -460,7 +437,18 @@ impl Package {
#[cfg(feature = "release")]
fn is_semver_checked(&self) -> bool {
[Self::EspHal].contains(self)
let toml = self.toml();
let Some(metadata) = toml.espressif_metadata() else {
return false;
};
let Some(Item::Value(semver_checked)) = metadata.get("semver_checked") else {
return false;
};
semver_checked
.as_bool()
.expect("semver_checked must be a boolean")
}
}
@ -670,8 +658,8 @@ pub fn package_paths(workspace: &Path) -> Result<Vec<PathBuf>> {
}
/// Parse the version from the specified package's Cargo manifest.
pub fn package_version(workspace: &Path, package: Package) -> Result<semver::Version> {
CargoToml::new(workspace, package).map(|toml| toml.package_version())
pub fn package_version(_workspace: &Path, package: Package) -> Result<semver::Version> {
Ok(package.toml().package_version())
}
/// Make the path "Windows"-safe

View File

@ -258,7 +258,7 @@ fn check_packages(workspace: &Path, args: CheckPackagesArgs) -> Result<()> {
let mut commands = CargoCommandBatcher::new();
for package in packages.iter().filter(|p| p.is_published(workspace)) {
for package in packages.iter().filter(|p| p.is_published()) {
// Unfortunately each package has its own unique requirements for
// building, so we need to handle each individually (though there
// is *some* overlap)
@ -266,7 +266,8 @@ fn check_packages(workspace: &Path, args: CheckPackagesArgs) -> Result<()> {
log::debug!(" for chip: {}", chip);
let device = Config::for_chip(chip);
if package.validate_package_chip(chip).is_err() {
if let Err(e) = package.validate_package_chip(chip) {
log::warn!("{e}. Skipping");
continue;
}
@ -353,7 +354,7 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> {
let mut packages = args.packages;
packages.sort();
for package in packages.iter().filter(|p| p.is_published(workspace)) {
for package in packages.iter().filter(|p| p.is_published()) {
// Unfortunately each package has its own unique requirements for
// building, so we need to handle each individually (though there
// is *some* overlap)
@ -361,7 +362,8 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> {
log::debug!(" for chip: {}", chip);
let device = Config::for_chip(chip);
if package.validate_package_chip(chip).is_err() {
if let Err(e) = package.validate_package_chip(chip) {
log::warn!("{e}. Skipping");
continue;
}
@ -510,24 +512,11 @@ fn run_ci_checks(workspace: &Path, args: CiArgs) -> Result<()> {
std::env::set_var("CI", "true");
}
// TODO: enable checking all crates once cargo-batch has check support
// runner.run("Check crates", || {
// check_packages(
// workspace,
// LintPackagesArgs {
// packages: Package::iter().collect(),
// chips: vec![args.chip],
// fix: false,
// toolchain: args.toolchain.clone(),
// },
// )
// });
runner.run("Check esp-hal", || {
runner.run("Check crates", || {
check_packages(
workspace,
CheckPackagesArgs {
packages: vec![Package::EspHal],
packages: Package::iter().collect(),
chips: vec![args.chip],
toolchain: args.toolchain.clone(),
},

View File

@ -10,6 +10,9 @@ license = "MIT OR Apache-2.0"
keywords = ["esp32", "xtensa-lx-rt", "runtime", "startup"]
categories = ["embedded", "no-std"]
[package.metadata.espressif]
requires_target = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf", "xtensa-esp32s3-none-elf"]
[lib]
proc-macro = true

View File

@ -11,6 +11,9 @@ keywords = ["lx", "peripheral", "register", "xtensa"]
categories = ["embedded", "hardware-support", "no-std"]
links = "xtensa-lx-rt"
[package.metadata.espressif]
requires_target = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf", "xtensa-esp32s3-none-elf"]
[lib]
bench = false
test = false

View File

@ -10,6 +10,9 @@ license = "MIT OR Apache-2.0"
categories = ["embedded", "hardware-support", "no-std"]
keywords = ["lx", "peripheral", "register", "xtensa"]
[package.metadata.espressif]
requires_target = ["xtensa-esp32-none-elf", "xtensa-esp32s2-none-elf", "xtensa-esp32s3-none-elf"]
[lib]
bench = false
test = false