diff --git a/esp-alloc/Cargo.toml b/esp-alloc/Cargo.toml index c4260be22..08466f6e8 100644 --- a/esp-alloc/Cargo.toml +++ b/esp-alloc/Cargo.toml @@ -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"] diff --git a/esp-backtrace/Cargo.toml b/esp-backtrace/Cargo.toml index 11b793005..67e426664 100644 --- a/esp-backtrace/Cargo.toml +++ b/esp-backtrace/Cargo.toml @@ -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 diff --git a/esp-backtrace/src/lib.rs b/esp-backtrace/src/lib.rs index 35b837d68..55afd028a 100644 --- a/esp-backtrace/src/lib.rs +++ b/esp-backtrace/src/lib.rs @@ -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 {} + }) } diff --git a/esp-bootloader-esp-idf/Cargo.toml b/esp-bootloader-esp-idf/Cargo.toml index 0cdea0ea9..9ac4b1062 100644 --- a/esp-bootloader-esp-idf/Cargo.toml +++ b/esp-bootloader-esp-idf/Cargo.toml @@ -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" diff --git a/esp-config/Cargo.toml b/esp-config/Cargo.toml index 5bffc367c..a0656e51b 100644 --- a/esp-config/Cargo.toml +++ b/esp-config/Cargo.toml @@ -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 diff --git a/esp-config/src/bin/esp-config/main.rs b/esp-config/src/bin/esp-config/main.rs index 605316a69..0aabba125 100644 --- a/esp-config/src/bin/esp-config/main.rs +++ b/esp-config/src/bin/esp-config/main.rs @@ -70,7 +70,7 @@ fn main() -> Result<(), Box> { .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, config_toml_path: &PathBuf, ) -> Result<(), Box> { - 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::()?; @@ -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(()) } diff --git a/esp-hal-embassy/Cargo.toml b/esp-hal-embassy/Cargo.toml index 318a1f1d6..372fec32b 100644 --- a/esp-hal-embassy/Cargo.toml +++ b/esp-hal-embassy/Cargo.toml @@ -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"] diff --git a/esp-hal-procmacros/Cargo.toml b/esp-hal-procmacros/Cargo.toml index a92984155..ce6e14fd4 100644 --- a/esp-hal-procmacros/Cargo.toml +++ b/esp-hal-procmacros/Cargo.toml @@ -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"] diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 609749134..d49394f89 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -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"] diff --git a/esp-hal/src/psram/mod.rs b/esp-hal/src/psram/mod.rs index 2c05426bc..a2dc5f334 100644 --- a/esp-hal/src/psram/mod.rs +++ b/esp-hal/src/psram/mod.rs @@ -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 diff --git a/esp-lp-hal/Cargo.toml b/esp-lp-hal/Cargo.toml index 71c8b4d47..4d8ef0388 100644 --- a/esp-lp-hal/Cargo.toml +++ b/esp-lp-hal/Cargo.toml @@ -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 diff --git a/esp-metadata-generated/Cargo.toml b/esp-metadata-generated/Cargo.toml index eb10d0657..c008bbfc9 100644 --- a/esp-metadata-generated/Cargo.toml +++ b/esp-metadata-generated/Cargo.toml @@ -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] diff --git a/esp-metadata/Cargo.toml b/esp-metadata/Cargo.toml index d78bc6fcb..7ac211e00 100644 --- a/esp-metadata/Cargo.toml +++ b/esp-metadata/Cargo.toml @@ -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 } diff --git a/esp-preempt/Cargo.toml b/esp-preempt/Cargo.toml index f5eb25f22..29e6cae50 100644 --- a/esp-preempt/Cargo.toml +++ b/esp-preempt/Cargo.toml @@ -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"] diff --git a/esp-println/Cargo.toml b/esp-println/Cargo.toml index 586cf17ce..f9e04ab60 100644 --- a/esp-println/Cargo.toml +++ b/esp-println/Cargo.toml @@ -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" diff --git a/esp-println/build.rs b/esp-println/build.rs index d9bad8641..98b551775 100644 --- a/esp-println/build.rs +++ b/esp-println/build.rs @@ -15,7 +15,7 @@ macro_rules! assert_unique_used_features { fn main() -> Result<(), Box> { // 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, diff --git a/esp-println/src/lib.rs b/esp-println/src/lib.rs index 52b0e8c5a..e1e6bc151 100644 --- a/esp-println/src/lib.rs +++ b/esp-println/src/lib.rs @@ -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)] diff --git a/esp-println/src/logger.rs b/esp-println/src/logger.rs index b6f981d27..9c63ffa5a 100644 --- a/esp-println/src/logger.rs +++ b/esp-println/src/logger.rs @@ -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!( diff --git a/esp-radio-preempt-driver/Cargo.toml b/esp-radio-preempt-driver/Cargo.toml index 2bf6976b4..d528cf391 100644 --- a/esp-radio-preempt-driver/Cargo.toml +++ b/esp-radio-preempt-driver/Cargo.toml @@ -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] diff --git a/esp-radio/Cargo.toml b/esp-radio/Cargo.toml index 0691f17e1..d82373a0d 100644 --- a/esp-radio/Cargo.toml +++ b/esp-radio/Cargo.toml @@ -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 diff --git a/esp-riscv-rt/Cargo.toml b/esp-riscv-rt/Cargo.toml index 36ce931c5..d72da9593 100644 --- a/esp-riscv-rt/Cargo.toml +++ b/esp-riscv-rt/Cargo.toml @@ -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 diff --git a/esp-rom-sys/Cargo.toml b/esp-rom-sys/Cargo.toml index bdf27d940..cc1497e32 100644 --- a/esp-rom-sys/Cargo.toml +++ b/esp-rom-sys/Cargo.toml @@ -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"] diff --git a/esp-storage/Cargo.toml b/esp-storage/Cargo.toml index 13b8ab581..2aaf54fff 100644 --- a/esp-storage/Cargo.toml +++ b/esp-storage/Cargo.toml @@ -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"] diff --git a/esp-storage/src/lib.rs b/esp-storage/src/lib.rs index e4208d616..53c5b6628 100644 --- a/esp-storage/src/lib.rs +++ b/esp-storage/src/lib.rs @@ -25,7 +25,7 @@ fn maybe_with_critical_section(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"))] diff --git a/esp-sync/Cargo.toml b/esp-sync/Cargo.toml index 8cb2c4826..5bf76a2a0 100644 --- a/esp-sync/Cargo.toml +++ b/esp-sync/Cargo.toml @@ -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" diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index bcc94a420..ef09d32e5 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -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"] } diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 6505b969e..14cd9b178 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -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::(); + + 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::() - 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 { + pub fn new(workspace: &Path, package: Package) -> Result { 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 { + pub fn from_str(workspace: &Path, package: Package, manifest: &str) -> Result { // Parse the manifest string into a mutable TOML document. Ok(Self { - workspace, + workspace: workspace.to_path_buf(), package, manifest: manifest .parse::() diff --git a/xtask/src/commands/release/bump_version.rs b/xtask/src/commands/release/bump_version.rs index f2caaff6a..ac7139ca4 100644 --- a/xtask/src/commands/release/bump_version.rs +++ b/xtask/src/commands/release/bump_version.rs @@ -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 { @@ -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 { @@ -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::().unwrap(), package: Package::EspHal, - workspace: Path::new(""), + workspace: PathBuf::new(), }; let errors = check_crate_before_bumping(&mut doc); pretty_assertions::assert_eq!( diff --git a/xtask/src/commands/release/plan.rs b/xtask/src/commands/release/plan.rs index ee97e1079..c8078b029 100644 --- a/xtask/src/commands/release/plan.rs +++ b/xtask/src/commands/release/plan.rs @@ -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::>(); diff --git a/xtask/src/commands/release/publish.rs b/xtask/src/commands/release/publish.rs index d11a5b7a2..8fe2bea39 100644 --- a/xtask/src/commands/release/publish.rs +++ b/xtask/src/commands/release/publish.rs @@ -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 ); diff --git a/xtask/src/commands/release/tag_releases.rs b/xtask/src/commands/release/tag_releases.rs index ff7caa4b6..3d020181c 100644 --- a/xtask/src/commands/release/tag_releases.rs +++ b/xtask/src/commands/release/tag_releases.rs @@ -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; } diff --git a/xtask/src/commands/run.rs b/xtask/src/commands/run.rs index 9e7ed7595..a96c49202 100644 --- a/xtask/src/commands/run.rs +++ b/xtask/src/commands/run.rs @@ -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: diff --git a/xtask/src/documentation.rs b/xtask/src/documentation.rs index 9f75261db..ccf8b8579 100644 --- a/xtask/src/documentation.rs +++ b/xtask/src/documentation.rs @@ -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) -> Result

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> for package in Package::iter() { // Not all packages have documentation built: - if !package.is_published(workspace) { + if !package.is_published() { continue; } diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 711eb0cb4..7be8e5ca3 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -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::>(); + 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 { - 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> { + 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::(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> { + // 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 { + 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> { - 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> { - 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>> = 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 { - 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> { } /// Parse the version from the specified package's Cargo manifest. -pub fn package_version(workspace: &Path, package: Package) -> Result { - CargoToml::new(workspace, package).map(|toml| toml.package_version()) +pub fn package_version(_workspace: &Path, package: Package) -> Result { + Ok(package.toml().package_version()) } /// Make the path "Windows"-safe diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 5da72fd15..3ad00f16a 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -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(), }, diff --git a/xtensa-lx-rt-proc-macros/Cargo.toml b/xtensa-lx-rt-proc-macros/Cargo.toml index 8f5e42218..cf6fc6752 100644 --- a/xtensa-lx-rt-proc-macros/Cargo.toml +++ b/xtensa-lx-rt-proc-macros/Cargo.toml @@ -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 diff --git a/xtensa-lx-rt/Cargo.toml b/xtensa-lx-rt/Cargo.toml index f8708810f..c278f2e17 100644 --- a/xtensa-lx-rt/Cargo.toml +++ b/xtensa-lx-rt/Cargo.toml @@ -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 diff --git a/xtensa-lx/Cargo.toml b/xtensa-lx/Cargo.toml index 8b7c87b4c..eb04d1afd 100644 --- a/xtensa-lx/Cargo.toml +++ b/xtensa-lx/Cargo.toml @@ -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