diff --git a/.github/workflows/hil.yml b/.github/workflows/hil.yml index 0c9d327e69b..7a67f5cf312 100644 --- a/.github/workflows/hil.yml +++ b/.github/workflows/hil.yml @@ -121,26 +121,10 @@ jobs: - name: Build tests run: cargo xtask build-tests ${{ matrix.target.soc }} - - name: Prepare artifact - run: | - # Create the 'tests' directory if it doesn't exist - mkdir -p tests - - # Find ELF files in the specified path and move them to 'tests' - find "hil-test/target/${{ matrix.target.rust-target }}/release/deps/" -type f -exec file {} + | \ - grep ELF | \ - awk -F: '{print $1}' | \ - xargs -I {} mv {} tests - - # Rename files in 'tests' by removing everything after the first dash - for file in tests/*-*; do - base_name="$(basename "$file" | cut -d'-' -f1)" - mv "$file" "tests/$base_name" - done - uses: actions/upload-artifact@v4 with: name: tests-${{ matrix.target.soc }} - path: /home/runner/work/esp-hal/esp-hal/tests + path: /home/runner/work/esp-hal/esp-hal/target/tests/${{ matrix.target.soc }} if-no-files-found: error overwrite: true diff --git a/esp-hal/src/gpio/interconnect.rs b/esp-hal/src/gpio/interconnect.rs index 2cab3518c89..f1798cc0796 100644 --- a/esp-hal/src/gpio/interconnect.rs +++ b/esp-hal/src/gpio/interconnect.rs @@ -212,6 +212,7 @@ fn disconnect_peripheral_output_from_pin(pin: &mut AnyPin, signal: gpio::OutputS /// A configurable input signal between a peripheral and a GPIO pin. /// /// Multiple input signals can be connected to one pin. +#[instability::unstable] pub struct InputSignal { pin: AnyPin, is_inverted: bool, @@ -347,6 +348,7 @@ impl DirectInputSignal { /// A configurable output signal between a peripheral and a GPIO pin. /// /// Multiple pins can be connected to one output signal. +#[instability::unstable] pub struct OutputSignal { pin: AnyPin, is_inverted: bool, @@ -506,6 +508,7 @@ enum InputConnectionInner { /// This is mainly intended for internal use, but it can be used to connect /// peripherals within the MCU without external hardware. #[derive(Clone)] +#[doc(hidden)] // FIXME: replace with `#[unstable]` when we can mark delegated methods https://github.com/Kobzol/rust-delegate/issues/77 pub struct InputConnection(InputConnectionInner); impl Peripheral for InputConnection { @@ -614,6 +617,7 @@ enum OutputConnectionInner { /// /// This is mainly intended for internal use, but it can be used to connect /// peripherals within the MCU without external hardware. +#[doc(hidden)] // FIXME: replace with `#[unstable]` when we can mark delegated methods https://github.com/Kobzol/rust-delegate/issues/77 pub struct OutputConnection(OutputConnectionInner); impl Sealed for OutputConnection {} diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index 277e9803977..0cd1a26a927 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -79,23 +79,20 @@ use crate::{ private::{self, Sealed}, }; -pub mod interconnect; mod placeholder; pub use placeholder::NoPin; -#[cfg(soc_etm)] crate::unstable_module! { + pub mod interconnect; + + #[cfg(soc_etm)] pub mod etm; -} -#[cfg(lp_io)] -crate::unstable_module! { + #[cfg(lp_io)] pub mod lp_io; -} -#[cfg(all(rtc_io, not(esp32)))] -crate::unstable_module! { + #[cfg(all(rtc_io, not(esp32)))] pub mod rtc_io; } @@ -820,6 +817,7 @@ where /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. + #[instability::unstable] pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { ( interconnect::InputSignal::new(self.degrade_pin(private::Internal)), @@ -1296,6 +1294,8 @@ impl<'d> Output<'d> { /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. + #[inline] + #[instability::unstable] pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { self.pin.split() } @@ -1305,6 +1305,7 @@ impl<'d> Output<'d> { /// /// The input signal can be passed to peripherals in place of an input pin. #[inline] + #[instability::unstable] pub fn peripheral_input(&self) -> interconnect::InputSignal { self.pin.peripheral_input() } @@ -1315,6 +1316,7 @@ impl<'d> Output<'d> { /// The output signal can be passed to peripherals in place of an output /// pin. #[inline] + #[instability::unstable] pub fn into_peripheral_output(self) -> interconnect::OutputSignal { self.pin.into_peripheral_output() } @@ -1439,6 +1441,7 @@ impl<'d> Input<'d> { /// /// The input signal can be passed to peripherals in place of an input pin. #[inline] + #[instability::unstable] pub fn peripheral_input(&self) -> interconnect::InputSignal { self.pin.peripheral_input() } @@ -1565,6 +1568,8 @@ impl<'d> Input<'d> { /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. + #[inline] + #[instability::unstable] pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { self.pin.split() } @@ -1575,6 +1580,7 @@ impl<'d> Input<'d> { /// The output signal can be passed to peripherals in place of an output /// pin. #[inline] + #[instability::unstable] pub fn into_peripheral_output(self) -> interconnect::OutputSignal { self.pin.into_peripheral_output() } @@ -1654,6 +1660,8 @@ impl<'d> OutputOpenDrain<'d> { /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. + #[inline] + #[instability::unstable] pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { self.pin.split() } @@ -1663,6 +1671,7 @@ impl<'d> OutputOpenDrain<'d> { /// /// The input signal can be passed to peripherals in place of an input pin. #[inline] + #[instability::unstable] pub fn peripheral_input(&self) -> interconnect::InputSignal { self.pin.peripheral_input() } @@ -1673,6 +1682,7 @@ impl<'d> OutputOpenDrain<'d> { /// The output signal can be passed to peripherals in place of an output /// pin. #[inline] + #[instability::unstable] pub fn into_peripheral_output(self) -> interconnect::OutputSignal { self.pin.into_peripheral_output() } @@ -1797,6 +1807,7 @@ impl<'d> Flex<'d> { /// /// The input signal can be passed to peripherals in place of an input pin. #[inline] + #[instability::unstable] pub fn peripheral_input(&self) -> interconnect::InputSignal { self.pin.degrade_pin(private::Internal).split().0 } @@ -1963,6 +1974,8 @@ impl<'d> Flex<'d> { /// /// Peripheral signals allow connecting peripherals together without using /// external hardware. + #[inline] + #[instability::unstable] pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { assert!(self.pin.is_output()); self.pin.degrade_pin(private::Internal).split() @@ -1974,6 +1987,7 @@ impl<'d> Flex<'d> { /// The output signal can be passed to peripherals in place of an output /// pin. #[inline] + #[instability::unstable] pub fn into_peripheral_output(self) -> interconnect::OutputSignal { self.split().1 } @@ -2028,6 +2042,7 @@ pub(crate) mod internal { /// using external hardware. #[inline] #[allow(unused_braces, reason = "False positive")] + #[instability::unstable] pub fn split(self) -> (interconnect::InputSignal, interconnect::OutputSignal) { handle_gpio_input!(self, target, { target.split() }) } diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 97204555d4e..cddfacb3d89 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -197,6 +197,7 @@ cfg-if = "1.0.0" critical-section = "1.1.3" defmt = "0.3.8" defmt-rtt = { version = "0.4.1", optional = true } +embassy-executor = "0.6.0" embassy-futures = "0.1.1" embassy-sync = "0.6.0" embassy-time = "0.3.2" @@ -206,7 +207,7 @@ embedded-hal-async = "1.0.0" embedded-hal-nb = "1.0.0" esp-alloc = { path = "../esp-alloc", optional = true } esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "defmt", "semihosting"] } -esp-hal = { path = "../esp-hal", features = ["digest", "unstable"], optional = true } +esp-hal = { path = "../esp-hal", features = ["digest"], optional = true } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } esp-wifi = { path = "../esp-wifi", optional = true, features = ["wifi"] } portable-atomic = "1.9.0" @@ -220,7 +221,7 @@ digest = { version = "0.10.7", default-features = false } elliptic-curve = { version = "0.13.8", default-features = false, features = ["sec1"] } embassy-executor = { version = "0.6.0", default-features = false } # Add the `embedded-test/defmt` feature for more verbose testing -embedded-test = { version = "0.5.0", git = "https://github.com/probe-rs/embedded-test.git", rev = "7109473", default-features = false } +embedded-test = { version = "0.5.0", git = "https://github.com/probe-rs/embedded-test.git", rev = "7109473", default-features = false, features = ["embassy", "external-executor"] } fugit = "0.3.7" hex-literal = "0.4.1" nb = "1.1.0" @@ -234,7 +235,8 @@ esp-build = { path = "../esp-build" } esp-metadata = { path = "../esp-metadata" } [features] -default = ["embassy"] +default = [] +unstable = ["esp-hal/unstable"] defmt = ["dep:defmt-rtt", "esp-hal/defmt", "embedded-test/defmt"] @@ -286,14 +288,14 @@ esp32s3 = [ ] # Async & Embassy: embassy = [ - "embedded-test/embassy", - "embedded-test/external-executor", "dep:esp-hal-embassy", ] generic-queue = [ + "embassy", "embassy-time/generic-queue-64" ] integrated-timers = [ + "embassy", "esp-hal-embassy/integrated-timers", ] octal-psram = ["esp-hal/octal-psram", "esp-alloc"] diff --git a/hil-test/src/lib.rs b/hil-test/src/lib.rs index 660e8697cbe..1ce1a1b444b 100644 --- a/hil-test/src/lib.rs +++ b/hil-test/src/lib.rs @@ -82,3 +82,42 @@ macro_rules! unconnected_pin { } }}; } + +// A simple looping executor to test async code without esp-hal-embassy (which +// needs `esp-hal/unstable`). +#[cfg(not(feature = "embassy"))] +mod executor { + use core::marker::PhantomData; + + use embassy_executor::{raw, Spawner}; + + #[export_name = "__pender"] + fn __pender(_: *mut ()) {} + + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + pub fn new() -> Self { + Self { + inner: raw::Executor::new(core::ptr::null_mut()), + not_send: PhantomData, + } + } + + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { self.inner.poll() }; + } + } + } +} + +#[cfg(feature = "embassy")] +pub use esp_hal_embassy::Executor; +#[cfg(not(feature = "embassy"))] +pub use executor::Executor; diff --git a/hil-test/tests/aes.rs b/hil-test/tests/aes.rs index 950f01e9d42..a1bc15f9cc8 100644 --- a/hil-test/tests/aes.rs +++ b/hil-test/tests/aes.rs @@ -1,6 +1,7 @@ //! AES Test //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/aes_dma.rs b/hil-test/tests/aes_dma.rs index 1c27f0151e2..4727f097448 100644 --- a/hil-test/tests/aes_dma.rs +++ b/hil-test/tests/aes_dma.rs @@ -1,6 +1,7 @@ //! AES DMA Test //% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/clock_monitor.rs b/hil-test/tests/clock_monitor.rs index e8ed1f3f15a..b732e58183e 100644 --- a/hil-test/tests/clock_monitor.rs +++ b/hil-test/tests/clock_monitor.rs @@ -1,6 +1,7 @@ //! Clock Monitor Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/crc.rs b/hil-test/tests/crc.rs index b7e8981d53c..c45261e8258 100644 --- a/hil-test/tests/crc.rs +++ b/hil-test/tests/crc.rs @@ -1,6 +1,7 @@ //! CRC and MD5 Tests //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/critical_section.rs b/hil-test/tests/critical_section.rs index e0d8f69d6c3..bd7d8f1e1ad 100644 --- a/hil-test/tests/critical_section.rs +++ b/hil-test/tests/critical_section.rs @@ -1,6 +1,7 @@ //! Ensure invariants of locks are upheld. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable // TODO: add multi-core tests diff --git a/hil-test/tests/delay.rs b/hil-test/tests/delay.rs index 62d66b848dd..b78104947ba 100644 --- a/hil-test/tests/delay.rs +++ b/hil-test/tests/delay.rs @@ -1,6 +1,7 @@ //! Delay Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/delay_async.rs b/hil-test/tests/delay_async.rs index c4ef014f240..6bb4706e043 100644 --- a/hil-test/tests/delay_async.rs +++ b/hil-test/tests/delay_async.rs @@ -4,6 +4,7 @@ //! `embedded_hal_async::delay::DelayNs` trait. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] @@ -69,7 +70,7 @@ async fn test_async_delay_ms(mut timer: impl DelayNs, duration: u32) { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 2, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 2, executor = hil_test::Executor::new())] mod tests { use super::*; diff --git a/hil-test/tests/dma_macros.rs b/hil-test/tests/dma_macros.rs index a017471e923..7a1801bc927 100644 --- a/hil-test/tests/dma_macros.rs +++ b/hil-test/tests/dma_macros.rs @@ -1,6 +1,7 @@ //! DMA macro tests //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/dma_mem2mem.rs b/hil-test/tests/dma_mem2mem.rs index 57430e9baf6..6718d56dced 100644 --- a/hil-test/tests/dma_mem2mem.rs +++ b/hil-test/tests/dma_mem2mem.rs @@ -1,6 +1,7 @@ //! DMA Mem2Mem Tests //% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/ecc.rs b/hil-test/tests/ecc.rs index 3e3fa9f3d6b..d290fbd7a33 100644 --- a/hil-test/tests/ecc.rs +++ b/hil-test/tests/ecc.rs @@ -1,6 +1,7 @@ //! ECC Test //% CHIPS: esp32c2 esp32c6 esp32h2 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/embassy_interrupt_executor.rs b/hil-test/tests/embassy_interrupt_executor.rs index 7731aa9fa0e..51c5b9107e1 100644 --- a/hil-test/tests/embassy_interrupt_executor.rs +++ b/hil-test/tests/embassy_interrupt_executor.rs @@ -2,8 +2,8 @@ //! code. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: integrated-timers -//% FEATURES: generic-queue +//% FEATURES(integrated): unstable embassy integrated-timers +//% FEATURES(generic): unstable embassy generic-queue #![no_std] #![no_main] @@ -48,7 +48,7 @@ struct Context { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod test { use super::*; diff --git a/hil-test/tests/embassy_interrupt_spi_dma.rs b/hil-test/tests/embassy_interrupt_spi_dma.rs index 8404bcd8f75..734869b9399 100644 --- a/hil-test/tests/embassy_interrupt_spi_dma.rs +++ b/hil-test/tests/embassy_interrupt_spi_dma.rs @@ -1,8 +1,8 @@ //! Reproduction and regression test for a sneaky issue. //% CHIPS: esp32 esp32s2 esp32s3 esp32c3 esp32c6 esp32h2 -//% FEATURES: integrated-timers -//% FEATURES: generic-queue +//% FEATURES(integrated): unstable embassy integrated-timers +//% FEATURES(generic): unstable embassy generic-queue #![no_std] #![no_main] @@ -75,7 +75,7 @@ async fn interrupt_driven_task(mut i2s_tx: esp_hal::i2s::master::I2sTx<'static, } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod test { use super::*; diff --git a/hil-test/tests/embassy_timers_executors.rs b/hil-test/tests/embassy_timers_executors.rs index 95c98e404d1..51292bc7ad9 100644 --- a/hil-test/tests/embassy_timers_executors.rs +++ b/hil-test/tests/embassy_timers_executors.rs @@ -1,8 +1,8 @@ //! Embassy timer and executor Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: integrated-timers -//% FEATURES: generic-queue +//% FEATURES(integrated): unstable embassy integrated-timers +//% FEATURES(generic): unstable embassy generic-queue #![no_std] #![no_main] @@ -122,7 +122,7 @@ fn set_up_embassy_with_systimer(peripherals: Peripherals) { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod test { use super::*; use crate::test_cases::*; diff --git a/hil-test/tests/esp_wifi_floats.rs b/hil-test/tests/esp_wifi_floats.rs index 5e9893fe567..d5b2faab9c4 100644 --- a/hil-test/tests/esp_wifi_floats.rs +++ b/hil-test/tests/esp_wifi_floats.rs @@ -1,7 +1,7 @@ //! Cp0Disable exception regression test //% CHIPS: esp32 esp32s2 esp32s3 -//% FEATURES: esp-wifi esp-alloc +//% FEATURES: unstable esp-wifi esp-alloc #![no_std] #![no_main] diff --git a/hil-test/tests/get_time.rs b/hil-test/tests/get_time.rs index b68f8264802..d6a94ae141d 100644 --- a/hil-test/tests/get_time.rs +++ b/hil-test/tests/get_time.rs @@ -1,6 +1,7 @@ //! time::now Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/gpio.rs b/hil-test/tests/gpio.rs index af03c232939..a2ee0b31228 100644 --- a/hil-test/tests/gpio.rs +++ b/hil-test/tests/gpio.rs @@ -1,7 +1,8 @@ //! GPIO Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: generic-queue +//% FEATURES: unstable embassy generic-queue +//% FEATURES(stable): #![no_std] #![no_main] @@ -9,14 +10,22 @@ use core::cell::RefCell; use critical_section::Mutex; +#[cfg(feature = "unstable")] +use embassy_time::{Duration, Timer}; use esp_hal::{ delay::Delay, - gpio::{AnyPin, Input, Io, Level, Output, Pin, Pull}, - interrupt::InterruptConfigurable, + gpio::{AnyPin, Input, Level, Output, OutputOpenDrain, Pin, Pull}, macros::handler, +}; +#[cfg(feature = "unstable")] +use esp_hal::{ + gpio::{Event, Flex, Io}, + interrupt::InterruptConfigurable, timer::timg::TimerGroup, }; use hil_test as _; +#[cfg(feature = "unstable")] +use portable_atomic::{AtomicUsize, Ordering}; static COUNTER: Mutex> = Mutex::new(RefCell::new(0)); static INPUT_PIN: Mutex>> = Mutex::new(RefCell::new(None)); @@ -39,27 +48,28 @@ pub fn interrupt_handler() { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod tests { - use embassy_time::{Duration, Timer}; - use esp_hal::gpio::{Event, Flex, OutputOpenDrain}; - use portable_atomic::{AtomicUsize, Ordering}; - use super::*; #[init] fn init() -> Context { let peripherals = esp_hal::init(esp_hal::Config::default()); - let mut io = Io::new(peripherals.IO_MUX); - io.set_interrupt_handler(interrupt_handler); - let delay = Delay::new(); let (gpio1, gpio2) = hil_test::common_test_pins!(peripherals); - let timg0 = TimerGroup::new(peripherals.TIMG0); - esp_hal_embassy::init(timg0.timer0); + #[cfg(feature = "unstable")] + { + // Interrupts are unstable + let mut io = Io::new(peripherals.IO_MUX); + io.set_interrupt_handler(interrupt_handler); + + // Timers are unstable + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_hal_embassy::init(timg0.timer0); + } Context { test_gpio1: gpio1.degrade(), @@ -69,6 +79,7 @@ mod tests { } #[test] + #[cfg(feature = "unstable")] // Timers are unstable async fn async_edge(ctx: Context) { let counter = AtomicUsize::new(0); let Context { @@ -147,53 +158,6 @@ mod tests { assert_eq!(test_gpio2.is_set_high(), true); } - #[test] - fn gpio_output_embedded_hal_0_2(ctx: Context) { - let test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down); - let mut test_gpio2 = Output::new(ctx.test_gpio2, Level::Low); - - fn set(pin: &mut T, state: bool) - where - T: embedded_hal::digital::OutputPin, - { - if state { - pin.set_high().ok(); - } else { - pin.set_low().ok(); - } - } - - fn toggle(pin: &mut T) - where - T: embedded_hal::digital::StatefulOutputPin, - { - pin.toggle().ok(); - } - - // `StatefulOutputPin`: - assert_eq!(test_gpio2.is_set_low(), true); - assert_eq!(test_gpio2.is_set_high(), false); - assert_eq!(test_gpio1.is_low(), true); - assert_eq!(test_gpio1.is_high(), false); - set(&mut test_gpio2, true); - assert_eq!(test_gpio2.is_set_low(), false); - assert_eq!(test_gpio2.is_set_high(), true); - assert_eq!(test_gpio1.is_low(), false); - assert_eq!(test_gpio1.is_high(), true); - - // `ToggleableOutputPin`: - toggle(&mut test_gpio2); - assert_eq!(test_gpio2.is_set_low(), true); - assert_eq!(test_gpio2.is_set_high(), false); - assert_eq!(test_gpio1.is_low(), true); - assert_eq!(test_gpio1.is_high(), false); - toggle(&mut test_gpio2); - assert_eq!(test_gpio2.is_set_low(), false); - assert_eq!(test_gpio2.is_set_high(), true); - assert_eq!(test_gpio1.is_low(), false); - assert_eq!(test_gpio1.is_high(), true); - } - #[test] fn gpio_output_embedded_hal_1_0(ctx: Context) { let test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down); @@ -242,6 +206,7 @@ mod tests { } #[test] + #[cfg(feature = "unstable")] // Interrupts are unstable fn gpio_interrupt(ctx: Context) { let mut test_gpio1 = Input::new(ctx.test_gpio1, Pull::Down); let mut test_gpio2 = Output::new(ctx.test_gpio2, Level::Low); @@ -325,6 +290,7 @@ mod tests { } #[test] + #[cfg(feature = "unstable")] fn gpio_flex(ctx: Context) { let mut test_gpio1 = Flex::new(ctx.test_gpio1); let mut test_gpio2 = Flex::new(ctx.test_gpio2); @@ -401,6 +367,7 @@ mod tests { } #[test] + #[cfg(feature = "unstable")] fn interrupt_executor_is_not_frozen(ctx: Context) { use esp_hal::interrupt::{software::SoftwareInterrupt, Priority}; use esp_hal_embassy::InterruptExecutor; diff --git a/hil-test/tests/gpio_custom_handler.rs b/hil-test/tests/gpio_custom_handler.rs index c7033206740..718b81df2fa 100644 --- a/hil-test/tests/gpio_custom_handler.rs +++ b/hil-test/tests/gpio_custom_handler.rs @@ -6,7 +6,7 @@ //! async API works for user handlers automatically. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: integrated-timers +//% FEATURES: unstable integrated-timers #![no_std] #![no_main] @@ -66,7 +66,7 @@ async fn drive_pins(gpio1: impl Into, gpio2: impl Into) -> usize } #[cfg(test)] -#[embedded_test::tests(executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(executor = hil_test::Executor::new())] mod tests { use super::*; diff --git a/hil-test/tests/i2c.rs b/hil-test/tests/i2c.rs index 14c1620478c..6aa5af36fb6 100644 --- a/hil-test/tests/i2c.rs +++ b/hil-test/tests/i2c.rs @@ -1,6 +1,7 @@ //! I2C test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/i2s.rs b/hil-test/tests/i2s.rs index 49f089ec61a..c36f299e6ff 100644 --- a/hil-test/tests/i2s.rs +++ b/hil-test/tests/i2s.rs @@ -4,7 +4,7 @@ //! with loopback mode enabled). //% CHIPS: esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: generic-queue +//% FEATURES: unstable generic-queue // FIXME: re-enable on ESP32 when it no longer fails spuriously #![no_std] @@ -97,7 +97,7 @@ fn enable_loopback() { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod tests { use super::*; diff --git a/hil-test/tests/init.rs b/hil-test/tests/init.rs index dc298d38aa3..a0988672c62 100644 --- a/hil-test/tests/init.rs +++ b/hil-test/tests/init.rs @@ -1,6 +1,7 @@ //! Initialization tests //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/interrupt.rs b/hil-test/tests/interrupt.rs index b5339d281c2..4d1c3bff15c 100644 --- a/hil-test/tests/interrupt.rs +++ b/hil-test/tests/interrupt.rs @@ -3,6 +3,7 @@ //! "Disabled" for now - see https://github.com/esp-rs/esp-hal/pull/1635#issuecomment-2137405251 //% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/lcd_cam.rs b/hil-test/tests/lcd_cam.rs index 1ff8942d7a0..1081b43288f 100644 --- a/hil-test/tests/lcd_cam.rs +++ b/hil-test/tests/lcd_cam.rs @@ -1,6 +1,7 @@ //! LCD_CAM Camera and DPI tests //% CHIPS: esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/lcd_cam_i8080.rs b/hil-test/tests/lcd_cam_i8080.rs index 06408255981..e411c9150aa 100644 --- a/hil-test/tests/lcd_cam_i8080.rs +++ b/hil-test/tests/lcd_cam_i8080.rs @@ -1,6 +1,7 @@ //! lcd_cam i8080 tests //% CHIPS: esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/lcd_cam_i8080_async.rs b/hil-test/tests/lcd_cam_i8080_async.rs index f3136e23618..0616ae708bc 100644 --- a/hil-test/tests/lcd_cam_i8080_async.rs +++ b/hil-test/tests/lcd_cam_i8080_async.rs @@ -1,7 +1,7 @@ //! lcd_cam i8080 tests //% CHIPS: esp32s3 -//% FEATURES: generic-queue +//% FEATURES: unstable generic-queue #![no_std] #![no_main] @@ -28,7 +28,7 @@ struct Context<'d> { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod tests { use super::*; diff --git a/hil-test/tests/parl_io_tx.rs b/hil-test/tests/parl_io_tx.rs index b0f83d33536..e361f5dce52 100644 --- a/hil-test/tests/parl_io_tx.rs +++ b/hil-test/tests/parl_io_tx.rs @@ -1,6 +1,8 @@ //! PARL_IO TX test //% CHIPS: esp32c6 esp32h2 +//% FEATURES: unstable + #![no_std] #![no_main] diff --git a/hil-test/tests/parl_io_tx_async.rs b/hil-test/tests/parl_io_tx_async.rs index eb7c8c19c53..76a8f9da281 100644 --- a/hil-test/tests/parl_io_tx_async.rs +++ b/hil-test/tests/parl_io_tx_async.rs @@ -1,7 +1,7 @@ //! PARL_IO TX async test //% CHIPS: esp32c6 esp32h2 -//% FEATURES: generic-queue +//% FEATURES: unstable generic-queue #![no_std] #![no_main] @@ -43,7 +43,7 @@ struct Context { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod tests { use defmt::info; diff --git a/hil-test/tests/pcnt.rs b/hil-test/tests/pcnt.rs index 758e18f5b86..149f89f39d9 100644 --- a/hil-test/tests/pcnt.rs +++ b/hil-test/tests/pcnt.rs @@ -1,6 +1,7 @@ //! PCNT tests //% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/qspi.rs b/hil-test/tests/qspi.rs index 1d285077114..79e10dfc465 100644 --- a/hil-test/tests/qspi.rs +++ b/hil-test/tests/qspi.rs @@ -1,6 +1,7 @@ //! QSPI Test Suite //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/rmt.rs b/hil-test/tests/rmt.rs index 13b05450d99..929a94c9ff1 100644 --- a/hil-test/tests/rmt.rs +++ b/hil-test/tests/rmt.rs @@ -1,6 +1,7 @@ //! RMT Loopback Test //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/rsa.rs b/hil-test/tests/rsa.rs index 0b39046f6ce..48bb86e6fb8 100644 --- a/hil-test/tests/rsa.rs +++ b/hil-test/tests/rsa.rs @@ -1,6 +1,7 @@ //! RSA Test //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/rsa_async.rs b/hil-test/tests/rsa_async.rs index 21ba4d4e229..c9b1a132a60 100644 --- a/hil-test/tests/rsa_async.rs +++ b/hil-test/tests/rsa_async.rs @@ -1,6 +1,7 @@ //! Async RSA Test //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] @@ -48,7 +49,7 @@ const fn compute_mprime(modulus: &U512) -> u32 { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 5, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 5, executor = hil_test::Executor::new())] mod tests { use super::*; diff --git a/hil-test/tests/sha.rs b/hil-test/tests/sha.rs index 4f67c0573d6..3ecde662972 100644 --- a/hil-test/tests/sha.rs +++ b/hil-test/tests/sha.rs @@ -1,19 +1,21 @@ //! SHA Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] use digest::{Digest, Update}; #[cfg(not(feature = "esp32"))] +use esp_hal::clock::CpuClock; +#[cfg(not(feature = "esp32"))] use esp_hal::sha::Sha224; #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] use esp_hal::sha::{Sha384, Sha512}; #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] use esp_hal::sha::{Sha512_224, Sha512_256}; use esp_hal::{ - clock::CpuClock, rng::Rng, sha::{Sha, Sha1, Sha256, ShaAlgorithm, ShaDigest}, }; diff --git a/hil-test/tests/spi_full_duplex.rs b/hil-test/tests/spi_full_duplex.rs index 751bd942a3d..b5dcf2b11e0 100644 --- a/hil-test/tests/spi_full_duplex.rs +++ b/hil-test/tests/spi_full_duplex.rs @@ -1,7 +1,7 @@ //! SPI Full Duplex test suite. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: generic-queue +//% FEATURES: unstable generic-queue // FIXME: add async test cases that don't rely on PCNT @@ -48,7 +48,7 @@ struct Context { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod tests { use super::*; diff --git a/hil-test/tests/spi_half_duplex_read.rs b/hil-test/tests/spi_half_duplex_read.rs index 07254a3b7a1..5d94153670e 100644 --- a/hil-test/tests/spi_half_duplex_read.rs +++ b/hil-test/tests/spi_half_duplex_read.rs @@ -1,6 +1,7 @@ //! SPI Half Duplex Read Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/spi_half_duplex_write.rs b/hil-test/tests/spi_half_duplex_write.rs index 4985f23bf34..6cf5f44f72b 100644 --- a/hil-test/tests/spi_half_duplex_write.rs +++ b/hil-test/tests/spi_half_duplex_write.rs @@ -1,6 +1,7 @@ //! SPI Half Duplex Write Test //% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/spi_half_duplex_write_psram.rs b/hil-test/tests/spi_half_duplex_write_psram.rs index 9a64edd72be..2071fa5d548 100644 --- a/hil-test/tests/spi_half_duplex_write_psram.rs +++ b/hil-test/tests/spi_half_duplex_write_psram.rs @@ -1,6 +1,7 @@ //! SPI Half Duplex Write Test -//% FEATURES: octal-psram + //% CHIPS: esp32s3 +//% FEATURES: unstable octal-psram #![no_std] #![no_main] diff --git a/hil-test/tests/spi_slave.rs b/hil-test/tests/spi_slave.rs index 4e123155ae2..aa0a3c85764 100644 --- a/hil-test/tests/spi_slave.rs +++ b/hil-test/tests/spi_slave.rs @@ -4,6 +4,7 @@ //! testing Mode 1. //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] @@ -94,7 +95,7 @@ impl BitbangSpi { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 10, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 10, executor = hil_test::Executor::new())] mod tests { use super::*; diff --git a/hil-test/tests/systimer.rs b/hil-test/tests/systimer.rs index 7c7c0fe0400..b72156ef327 100644 --- a/hil-test/tests/systimer.rs +++ b/hil-test/tests/systimer.rs @@ -2,6 +2,7 @@ // esp32 disabled as it does not have a systimer //% CHIPS: esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/twai.rs b/hil-test/tests/twai.rs index b244dd8d289..61d2764366e 100644 --- a/hil-test/tests/twai.rs +++ b/hil-test/tests/twai.rs @@ -1,6 +1,7 @@ //! TWAI test //% CHIPS: esp32 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/uart.rs b/hil-test/tests/uart.rs index b768bb54b49..a8f98322fca 100644 --- a/hil-test/tests/uart.rs +++ b/hil-test/tests/uart.rs @@ -1,6 +1,7 @@ //! UART Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/uart_async.rs b/hil-test/tests/uart_async.rs index e2225a56f1c..24d76986685 100644 --- a/hil-test/tests/uart_async.rs +++ b/hil-test/tests/uart_async.rs @@ -1,7 +1,7 @@ //! UART Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: generic-queue +//% FEATURES: unstable embassy generic-queue #![no_std] #![no_main] @@ -17,7 +17,7 @@ struct Context { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod tests { use super::*; diff --git a/hil-test/tests/uart_regression.rs b/hil-test/tests/uart_regression.rs index e1537dfce58..3a0f1ddc0c8 100644 --- a/hil-test/tests/uart_regression.rs +++ b/hil-test/tests/uart_regression.rs @@ -1,6 +1,7 @@ //! Misc UART TX/RX regression tests //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/uart_tx_rx.rs b/hil-test/tests/uart_tx_rx.rs index bc05f042ea8..8494498fae9 100644 --- a/hil-test/tests/uart_tx_rx.rs +++ b/hil-test/tests/uart_tx_rx.rs @@ -1,6 +1,7 @@ //! UART TX/RX Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] diff --git a/hil-test/tests/uart_tx_rx_async.rs b/hil-test/tests/uart_tx_rx_async.rs index 5e8822a01aa..c2f0870f82e 100644 --- a/hil-test/tests/uart_tx_rx_async.rs +++ b/hil-test/tests/uart_tx_rx_async.rs @@ -1,7 +1,7 @@ //! UART TX/RX Async Test //% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3 -//% FEATURES: generic-queue +//% FEATURES: unstable generic-queue #![no_std] #![no_main] @@ -18,7 +18,7 @@ struct Context { } #[cfg(test)] -#[embedded_test::tests(default_timeout = 3, executor = esp_hal_embassy::Executor::new())] +#[embedded_test::tests(default_timeout = 3, executor = hil_test::Executor::new())] mod tests { use super::*; diff --git a/hil-test/tests/usb_serial_jtag.rs b/hil-test/tests/usb_serial_jtag.rs index f441a91b54f..ef04d1bb895 100644 --- a/hil-test/tests/usb_serial_jtag.rs +++ b/hil-test/tests/usb_serial_jtag.rs @@ -1,6 +1,7 @@ //! USB Serial JTAG tests //% CHIPS: esp32c3 esp32c6 esp32h2 esp32s3 +//% FEATURES: unstable #![no_std] #![no_main] @@ -8,16 +9,13 @@ #[cfg(test)] #[embedded_test::tests(default_timeout = 3)] mod tests { - use esp_hal::{timer::timg::TimerGroup, usb_serial_jtag::UsbSerialJtag}; + use esp_hal::usb_serial_jtag::UsbSerialJtag; use hil_test as _; #[test] fn creating_peripheral_does_not_break_debug_connection() { let peripherals = esp_hal::init(esp_hal::Config::default()); - let timg0 = TimerGroup::new(peripherals.TIMG0); - esp_hal_embassy::init(timg0.timer0); - _ = UsbSerialJtag::new(peripherals.USB_DEVICE) .into_async() .split(); diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 078b48ac4f9..29a31449bea 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -17,5 +17,6 @@ log = "0.4.22" minijinja = "2.5.0" semver = { version = "1.0.23", features = ["serde"] } serde = { version = "1.0.215", features = ["derive"] } +serde_json = "1.0.70" strum = { version = "0.26.3", features = ["derive"] } toml_edit = "0.22.22" diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index 68ddc09cf11..d5ceb43ec31 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -2,27 +2,39 @@ use std::{ ffi::OsStr, - path::Path, + path::{Path, PathBuf}, process::{Command, Stdio}, }; use anyhow::{bail, Result}; +use serde::{Deserialize, Serialize}; use crate::windows_safe_path; -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub enum CargoAction { - Build, + Build(PathBuf), Run, } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Artifact { + pub executable: PathBuf, +} + /// Execute cargo with the given arguments and from the specified directory. pub fn run(args: &[String], cwd: &Path) -> Result<()> { - run_with_env::<[(&str, &str); 0], _, _>(args, cwd, []) + run_with_env::<[(&str, &str); 0], _, _>(args, cwd, [], false)?; + Ok(()) +} + +/// Execute cargo with the given arguments and from the specified directory. +pub fn run_and_capture(args: &[String], cwd: &Path) -> Result { + run_with_env::<[(&str, &str); 0], _, _>(args, cwd, [], true) } /// Execute cargo with the given arguments and from the specified directory. -pub fn run_with_env(args: &[String], cwd: &Path, envs: I) -> Result<()> +pub fn run_with_env(args: &[String], cwd: &Path, envs: I, capture: bool) -> Result where I: IntoIterator, K: AsRef, @@ -38,19 +50,26 @@ where // using now or in future! let cwd = windows_safe_path(cwd); - let status = Command::new(get_cargo()) + let output = Command::new(get_cargo()) .args(args) .current_dir(cwd) .envs(envs) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .stdin(Stdio::inherit()) - .status()?; + .stdout(if capture { + Stdio::piped() + } else { + Stdio::inherit() + }) + .stderr(if capture { + Stdio::piped() + } else { + Stdio::inherit() + }) + .output()?; // Make sure that we return an appropriate exit code here, as Github Actions // requires this in order to function correctly: - if status.success() { - Ok(()) + if output.status.success() { + Ok(String::from_utf8_lossy(&output.stdout).to_string()) } else { bail!("Failed to execute cargo subcommand") } @@ -145,16 +164,16 @@ impl CargoArgsBuilder { } #[must_use] - pub fn build(self) -> Vec { + pub fn build(&self) -> Vec { let mut args = vec![]; - if let Some(toolchain) = self.toolchain { + if let Some(ref toolchain) = self.toolchain { args.push(format!("+{toolchain}")); } - args.push(self.subcommand); + args.push(self.subcommand.clone()); - if let Some(target) = self.target { + if let Some(ref target) = self.target { args.push(format!("--target={target}")); } @@ -162,8 +181,8 @@ impl CargoArgsBuilder { args.push(format!("--features={}", self.features.join(","))); } - for arg in self.args { - args.push(arg); + for arg in self.args.iter() { + args.push(arg.clone()); } args diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 6eefb5b04a9..2318ca55d2a 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -58,6 +58,7 @@ pub enum Package { pub struct Metadata { example_path: PathBuf, chip: Chip, + feature_set_name: String, feature_set: Vec, tag: Option, description: Option, @@ -67,6 +68,7 @@ impl Metadata { pub fn new( example_path: &Path, chip: Chip, + feature_set_name: String, feature_set: Vec, tag: Option, description: Option, @@ -74,6 +76,7 @@ impl Metadata { Self { example_path: example_path.to_path_buf(), chip, + feature_set_name, feature_set, tag, description, @@ -161,6 +164,7 @@ pub fn build_documentation(workspace: &Path, package: Package, chip: Chip) -> Re &args, &package_path, [("RUSTDOCFLAGS", "--cfg docsrs --cfg not_really_docsrs")], + false, )?; let docs_path = windows_safe_path( @@ -212,7 +216,7 @@ fn apply_feature_rules(package: &Package, config: &Config) -> Vec { } /// Load all examples at the given path, and parse their metadata. -pub fn load_examples(path: &Path, action: CargoAction) -> Result> { +pub fn load_examples(path: &Path) -> Result> { let mut examples = Vec::new(); for entry in fs::read_dir(path)? { @@ -248,7 +252,7 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result> .split_ascii_whitespace() .map(|s| Chip::from_str(s, false).unwrap()) .collect::>(); - } else if key == "FEATURES" { + } else if let Some(feature_set_name) = key.strip_prefix("FEATURES") { // Base feature set required to run the example. // If multiple are specified, we compile the same example multiple times. let mut values = value @@ -259,7 +263,20 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result> // Sort the features so they are in a deterministic order: values.sort(); - feature_sets.push(values); + let feature_set_name = feature_set_name.trim_matches(&['(', ')']).to_string(); + + if feature_sets + .iter() + .any(|(name, _)| name == &feature_set_name) + { + bail!( + "Duplicate feature set name '{}' in {}", + feature_set_name, + path.display() + ); + } + + feature_sets.push((feature_set_name, values)); } else if key.starts_with("CHIP-FEATURES(") { // Additional features required for specific chips. // These are appended to the base feature set(s). @@ -289,15 +306,10 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result> } if feature_sets.is_empty() { - feature_sets.push(Vec::new()); + feature_sets.push((String::new(), Vec::new())); } - if action == CargoAction::Build { - // Only build the first feature set for each example. - // Rebuilding with a different feature set just wastes time because the latter - // one will overwrite the former one(s). - feature_sets.truncate(1); - } - for feature_set in feature_sets { + + for (feature_set_name, feature_set) in feature_sets { for chip in &chips { let mut feature_set = feature_set.clone(); if let Some(chip_features) = chip_features.get(chip) { @@ -310,6 +322,7 @@ pub fn load_examples(path: &Path, action: CargoAction) -> Result> examples.push(Metadata::new( &path, *chip, + feature_set_name.clone(), feature_set.clone(), tag.clone(), description.clone(), @@ -331,7 +344,7 @@ pub fn execute_app( target: &str, app: &Metadata, action: CargoAction, - mut repeat: usize, + repeat: usize, debug: bool, ) -> Result<()> { log::info!( @@ -348,52 +361,79 @@ pub fn execute_app( let package = app.example_path().strip_prefix(package_path)?; log::info!("Package: {}", package.display()); - let (bin, subcommand) = if action == CargoAction::Build { - repeat = 1; // Do not repeat builds in a loop - let bin = if package.starts_with("src/bin") { - format!("--bin={}", app.name()) - } else if package.starts_with("tests") { - format!("--test={}", app.name()) - } else { - format!("--example={}", app.name()) - }; - (bin, "build") - } else if package.starts_with("src/bin") { - (format!("--bin={}", app.name()), "run") + + let mut builder = CargoArgsBuilder::default() + .target(target) + .features(&features); + + let bin_arg = if package.starts_with("src/bin") { + format!("--bin={}", app.name()) } else if package.starts_with("tests") { - (format!("--test={}", app.name()), "test") + format!("--test={}", app.name()) } else { - (format!("--example={}", app.name()), "run") + format!("--example={}", app.name()) }; + builder.add_arg(bin_arg); - let mut builder = CargoArgsBuilder::default() - .subcommand(subcommand) - .target(target) - .features(&features) - .arg(bin); + let subcommand = if matches!(action, CargoAction::Build(_)) { + "build" + } else if package.starts_with("tests") { + "test" + } else { + "run" + }; + builder = builder.subcommand(subcommand); if !debug { builder.add_arg("--release"); } - if subcommand == "test" && chip == Chip::Esp32c2 { - builder.add_arg("--").add_arg("--speed").add_arg("15000"); - } - // If targeting an Xtensa device, we must use the '+esp' toolchain modifier: if target.starts_with("xtensa") { builder = builder.toolchain("esp"); builder.add_arg("-Zbuild-std=core,alloc"); } + if subcommand == "test" && chip == Chip::Esp32c2 { + builder.add_arg("--").add_arg("--speed").add_arg("15000"); + } + let args = builder.build(); log::debug!("{args:#?}"); - for i in 0..repeat { - if repeat != 1 { - log::info!("Run {}/{}", i + 1, repeat); - } + if let CargoAction::Build(out_dir) = action { cargo::run(&args, package_path)?; + + // Now that the build has succeeded and we printed the output, we can + // rerun the build again quickly enough to capture JSON. We'll use this to + // copy the binary to the output directory. + builder.add_arg("--message-format=json"); + let args = builder.build(); + let output = cargo::run_and_capture(&args, package_path)?; + for line in output.lines() { + if let Ok(artifact) = serde_json::from_str::(line) { + let out_dir = out_dir.join(&chip.to_string()); + std::fs::create_dir_all(&out_dir)?; + + let basename = app.name(); + let name = if app.feature_set_name.is_empty() { + basename + } else { + format!("{}_{}", basename, app.feature_set_name) + }; + + let output_file = out_dir.join(name); + std::fs::copy(artifact.executable, &output_file)?; + log::info!("Output ready: {}", output_file.display()); + } + } + } else { + for i in 0..repeat { + if repeat != 1 { + log::info!("Run {}/{}", i + 1, repeat); + } + cargo::run(&args, package_path)?; + } } Ok(()) diff --git a/xtask/src/main.rs b/xtask/src/main.rs index f1c62f83d71..8eebc52e927 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -181,12 +181,20 @@ fn main() -> Result<()> { let workspace = std::env::current_dir()?; + let out_path = Path::new("target"); + match Cli::parse() { Cli::BuildDocumentation(args) => build_documentation(&workspace, args), Cli::BuildDocumentationIndex(args) => build_documentation_index(&workspace, args), - Cli::BuildExamples(args) => examples(&workspace, args, CargoAction::Build), + Cli::BuildExamples(args) => examples( + &workspace, + args, + CargoAction::Build(out_path.join("examples")), + ), Cli::BuildPackage(args) => build_package(&workspace, args), - Cli::BuildTests(args) => tests(&workspace, args, CargoAction::Build), + Cli::BuildTests(args) => { + tests(&workspace, args, CargoAction::Build(out_path.join("tests"))) + } Cli::BumpVersion(args) => bump_version(&workspace, args), Cli::FmtPackages(args) => fmt_packages(&workspace, args), Cli::GenerateEfuseFields(args) => generate_efuse_src(&workspace, args), @@ -227,7 +235,7 @@ fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Res }; // Load all examples which support the specified chip and parse their metadata: - let mut examples = xtask::load_examples(&example_path, action)? + let mut examples = xtask::load_examples(&example_path)? .iter() .filter_map(|example| { if example.supports_chip(args.chip) { @@ -243,13 +251,18 @@ fn examples(workspace: &Path, mut args: ExampleArgs, action: CargoAction) -> Res // Execute the specified action: match action { - CargoAction::Build => build_examples(args, examples, &package_path), + CargoAction::Build(out_path) => build_examples(args, examples, &package_path, out_path), CargoAction::Run if args.example.is_some() => run_example(args, examples, &package_path), CargoAction::Run => run_examples(args, examples, &package_path), } } -fn build_examples(args: ExampleArgs, examples: Vec, package_path: &Path) -> Result<()> { +fn build_examples( + args: ExampleArgs, + examples: Vec, + package_path: &Path, + out_path: PathBuf, +) -> Result<()> { // Determine the appropriate build target for the given package and chip: let target = target_triple(args.package, &args.chip)?; @@ -265,7 +278,7 @@ fn build_examples(args: ExampleArgs, examples: Vec, package_path: &Pat args.chip, target, example, - CargoAction::Build, + CargoAction::Build(out_path.clone()), 1, args.debug, )?; @@ -282,7 +295,7 @@ fn build_examples(args: ExampleArgs, examples: Vec, package_path: &Pat args.chip, target, example, - CargoAction::Build, + CargoAction::Build(out_path.clone()), 1, args.debug, ) @@ -400,7 +413,7 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> { let target = target_triple(Package::HilTest, &args.chip)?; // Load all tests which support the specified chip and parse their metadata: - let mut tests = xtask::load_examples(&package_path.join("tests"), action)? + let mut tests = xtask::load_examples(&package_path.join("tests"))? .into_iter() .filter(|example| example.supports_chip(args.chip)) .collect::>(); @@ -420,7 +433,7 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> { args.chip, target, test, - action, + action.clone(), args.repeat.unwrap_or(1), false, )?; @@ -436,7 +449,7 @@ fn tests(workspace: &Path, args: TestArgs, action: CargoAction) -> Result<()> { args.chip, target, &test, - action, + action.clone(), args.repeat.unwrap_or(1), false, )