Skip to content

Add support for multiple configuration to deduplicate examples - #1118

Draft
salkinium wants to merge 77 commits into
modm-io:developfrom
salkinium:feature/dedup_examples
Draft

salkinium wants to merge 77 commits into
modm-io:developfrom
salkinium:feature/dedup_examples

Conversation

@salkinium

@salkinium salkinium commented Jan 6, 2024 •

Copy link
Copy Markdown
Member

Depends on #1387 and #1389 merged.

This flips the example matrix from "board -> use case" to "use case -> board": an example now lists all the boards it runs on in its project.xml and the CI compiles it for every one of them. Turns out we had a lot of copy-pasted examples, whoops.

The syntax is based on XML comments, so that the example remains usable manually and is hopefully self-explanatory:

<library>
  <extends>modm:nucleo-l476rg</extends>
  <!-- <extends>modm:blue-pill-f103</extends> -->
  <!-- <extends>modm:hosted</extends> -->
  <modules>
    <!-- Required for modm:nucleo-l476rg -->
    <module>modm:platform:adc:1</module>
    <!-- Required for modm:blue-pill-f103 -->
    <!-- <module>modm:platform:adc:2</module> -->
    <module>modm:build:scons</module>
  </modules>
</library>
  • Every <extends>, commented or not, is one configuration.
  • A comment that names configurations scopes the <option>, <collect> and <module> lines directly below it to these configurations. Everything else applies to all of them.
  • examples_compile.py copies the example into the build folder once per configuration, flips the comments and compiles it. Relative includes still resolve to the original folder.
  • There is now a modm:hosted configuration (from Add board connections, hosted configuration and fixes for deduplicated examples #1387) with the versions linux-x86_64, linux-arm64, darwin-x86_64, darwin-arm64 and windows, defaulting to your computer. So no special casing of hosted-* in the scripts.

Since the board is now just a configuration, sorting the examples by board makes no sense anymore, so they are sorted by what they show:

Folder Examples Content
core 21 Fibers, assertions, fault handling, allocators, internal flash, DMA, watchdogs, debug channels.
gpio 6 Blinking LEDs, buttons, ports, external interrupts.
analog 28 Internal ADCs, DACs and comparators.
timer 12 Timers and PWM.
logging 11 Logging and printing via UART.
can 4 CAN and FDCAN peripherals.
driver 99 External devices, sorted like modm:driver: sensors, displays, radios, LEDs and more.
ui 7 Graphics, GUI and animations on the displays of the boards.
communication 7 Protocols: AMNB, SAB, XPCC, Ethernet.
ext 23 External libraries: FreeRTOS, TinyUSB, LVGL, ETL, nanopb and more.

This is the layout from #1118 (comment). Almost half of all examples show an external device, so the driver folder has the same categories as src/modm/driver: driver/inertial/bmi270, driver/display/ssd1306, driver/radio/nrf24 and so on. If you know where the driver lives, you know where its example is, and the bus it uses does not matter. A suffix like analog/dac_dma_g4 names the family that an example is specific to.

A bunch of "examples" were really tests or benchmarks that nobody would use as a starting point. These moved to test/integration and all end with a INTEGRATION TEST PASSED or FAILED line on the logger (and the exit code on hosted), so that we can eventually run them automatically on real hardware.

Some numbers:

develop this PR
Example projects 431 218 + 39 integration tests
Compiled configurations 431 444 + 98
Lines +4766 −15421

So half the projects, but more builds, since a lot of examples now also compile for boards that never had them. That found a few boards that were missing clocks, connections or aliases, which is fixed here too.

The commits are sorted so that moves and changes are separate and you can review them one by one:

  1. Preparation: the scripts and removing all build paths. The fixes, board connections and modm:hosted moved to Add board connections, hosted configuration and fixes for deduplicated examples #1387.
  2. Moving the integration tests out of the examples. This commit only moves files.
  3. One commit per integration test that adds the pass result or merges several tests.
  4. Sorting the remaining examples into the new folders. This commit only moves files.
  5. One commit per merged example, which lists the folders it replaces, then one commit per category for the remaining small changes.

Only the last commit is guaranteed to compile, since the scripts and CI expect the final layout from the start.

The CI now uses all 20 concurrent jobs and splits the configurations in a stable pseudo-random order (sorted by hash) instead of contiguous chunks, so that every part gets a similar mix of examples and targets. The Cortex-M examples and the HAL compile are matrix jobs now and the STM32 unit tests have their own job, so the check names changed.

  • Rebased on develop.
  • Multi-configuration syntax in examples_compile.py with --target REGEX filter and --split/--part over configurations.
  • New folder layout, near-duplicates merged into the most complete example of the group.
  • Integration tests moved to test/integration with a pass result.
  • examples_check.py checks for unknown configurations, boards without any example, and targets that no CI job compiles. No more silent failures.
  • CI workflows select the examples by target instead of by folder.
  • Syntax and noteworthy examples documented in examples/README.md.
  • CI partitioned into 20 jobs: 7x Cortex-M examples, 7x HAL compile, STM32 unit tests, AVR, hosted, docs, macOS and Windows.
  • Homepage lists the number of examples per folder instead of every single example. The descriptions come from the table in examples/README.md.
  • The Windows fixes that only showed up because Windows compiled a pseudo-random part of all examples moved to Fix build system on Windows and test it deliberately #1389, which is merged. The Windows job now compiles the explicit list of examples from there with the new paths.
  • test/integration/flash and spi_flash generate the CompilationDB again, so that it keeps getting compiled on Windows.
  • Board::i2c and Board::spi for the remaining Nucleo boards: all Nucleo-144 and C031C6 get both, H503RB/H533RE/H563ZI/U575ZI-Q only I2C and U083RC only SPI, since the other driver does not exist yet. U385RG-Q has neither.
  • All Arduino pin aliases of the Nucleo boards compared with the connector tables and solder bridges of the user manuals via modm-data, which found two bugs, whoops: D3 was PF13 instead of PE13 on all UM1974 Nucleo-144, and H723ZG, H743ZI2 and H753ZI are MB1364 boards with their own pinout (UM2407) that differs in 17 pins from our shared header. They now have their own nucleo144_arduino_h7.hpp. Plus A2 on U083RC and A2, D0, D1, D2 on U385RG-Q, which still had the pins of the H5 boards.
  • driver/inertial/lis3mdl now compiles for all 39 boards with Board::i2c, driver/pwm/sk6812 only for the 6 boards whose SPI can do exactly 3 MHz.
  • CI passed after the rebase.
  • Compile-all job passed.
  • Do we need negative filters? no
  • Do we want config globbing support? no, --target takes a regex instead.

Missing: The integration tests compile, but their pass criteria have not been run on hardware yet.

Also missing: examples/analog is still a mess with one example per family and macros in the merged ones. The ADC drivers have ten different APIs with nine initialize() signatures between them, so this needs fixing in the drivers first and not in the examples. I'm leaving the folder as is, its shape alone is a good reminder to fix this later, lol.

cc @rleh @chris-durand

@salkinium

Copy link
Copy Markdown
Member Author

Ok, so in order to deduplicate more examples we need Board interfaces for setting up a default I2C and SPI connection similar to Board::initializeUsbFs() and the LEDs and Button. Then it should also be much easier to anyone to test any sensor example on the hardware they have at hand instead of having to port examples to their board first.

@salkinium

Copy link
Copy Markdown
Member Author

I've prototyped this for some NUCLEO-32 boards, I think it's a good idea to deduplicate all the I2C/SPI examples too.

@salkinium
salkinium force-pushed the feature/dedup_examples branch 4 times, most recently from 2afca50 to 87a6802 Compare January 13, 2024 00:04
@salkinium
salkinium force-pushed the feature/dedup_examples branch from 87a6802 to 6221a7b Compare February 11, 2024 19:25
@salkinium
salkinium force-pushed the feature/dedup_examples branch 2 times, most recently from 3125e63 to 7e9a13f Compare April 6, 2024 19:51
@salkinium

salkinium commented Apr 7, 2024 •

Copy link
Copy Markdown
Member Author

ok, so I think the deduplication is a good thing, but I don't like the idea of putting everything into the example/generic folder.
Here is the new structure, which basically flips the example on the head: first use-case, then board.
What do you think about this, @rleh @chris-durand ?

  • core
    • custom_allocator
    • delay
    • exceptions_rtti
    • fiber
    • flash
    • hard_fault
    • itm
    • multi-core
    • resumable
    • rtt
    • threadsafe_statics
    • tlsf_allocator
    • unaligned_access
    • vector_table_ram
  • gpio
    • blinky
    • exti
    • gpio
    • ports
  • analog
    • adc_dma
    • comp
    • dac
  • timer
    • encoder_input
    • pwm
    • timer
  • logging
    • logger
    • printf
    • uart
  • ui
    • game_of_life
    • graphics
    • lvgl
  • driver
    • can
    • i2c
    • single wire
    • spi
  • communication
    • amnb
    • ethernet_freertos
    • nrf24
    • ros
    • sab
    • xpcc
  • ext
    • cmsis_dsp
    • etl
    • freertos
    • nanopb
    • usb
    • usb_dfu
    • usbfatfs
  • board
    • super custom board-specific examples
    • ideally we get rid of this section

@rleh

rleh commented Apr 7, 2024

Copy link
Copy Markdown
Member

What about a more flat structure?
E.g. with the LIS3 driver/examples it would be nice to have lis3_spi/ and lis3_i2c/ examples located next to each other.

I would prefer no subdirectories over non-obvious subdirectories.

@salkinium

Copy link
Copy Markdown
Member Author

it's flat like that, the second indentation is already the example folder. I just didn't list all the i2c drivers.

@salkinium

salkinium commented Apr 7, 2024 •

Copy link
Copy Markdown
Member Author

ah, you mean completely flat? I think it's just too much, particularly because not everything can actually be deduplicated. I currently have 285 examples in this PR (down from 374 on develop), The analog examples can be fairly device specific, so they would be multiple ones like examples/analog/adc_f1, examples/analog/adc_f3, examples/analog/adc_h7 etc. I've now roughly used the same folder names as in src/modm to reflect the top-level use cases.

@rleh

rleh commented Apr 7, 2024

Copy link
Copy Markdown
Member

I think tags instead of categories are more useful for users (including me) to find an example. But that is not possible to implement using filesystem structures anyway, but we could generate a table of all examples inside examples/README.md with tag/keywords and maybe even a short description.

@rleh

rleh commented Apr 7, 2024 •

Copy link
Copy Markdown
Member

I'm also a bit skeptical about the impact of deduplication, use of scripting and undocumented magic XML comment syntax for new modm users. The examples are most users' first exposure to modm, and I find it unfavorable to add unnecessary complexity.
The project.xml files of the examples are already special: The <repositories>…</repositories> block is missing and <extends>modm:some-configuration</extends> is used. From my experience, that makes the step from a functional modm-example to an own modm-project complicated for new users.

What do we think about generating the examples completely in a separate repo (e.g. https://github.com/modm-io/modm-examples) for all boards, including the modm repo as submodules and a project.xml file which would be at most similar to the one in a custom project?

@salkinium

Copy link
Copy Markdown
Member Author

To get users started with their own projects, I created the modm-template repo once. Back then GitHub template repos didn't support submodules, but that has been fixed now. We should get that working, including how to setup multiple lbuild repos, CI for building, using our unit test system, documentation etc. We can document this in Readmes, which we can then also put onto the website like our other readmes.

I hope users are not even gonna notice that there's magic XML syntax in the project.xml (I'll document it, don't worry). It will look just like more documentation. This is mostly just an internal refactoring to make it more maintainable and not copy around blinky so much and to actually force us to design more APIs to be compatible across devices by actually testing it.

@rleh

rleh commented Apr 7, 2024 •

Copy link
Copy Markdown
Member

(deleted)

@salkinium
salkinium force-pushed the feature/dedup_examples branch 2 times, most recently from 2935385 to a17f93b Compare April 12, 2024 21:27
@salkinium
salkinium force-pushed the feature/dedup_examples branch 2 times, most recently from b6e6323 to 45bf5de Compare October 4, 2026 22:00
Replaces:
- examples/nucleo_g474re/can
- examples/nucleo_h723zg/can
Replaces:
- examples/nucleo_f429zi/ethernet
Replaces:
- examples/avr/assert
Replaces:
- examples/linux/build_info
Replaces:
- examples/avr/fiber
Replaces:
- examples/nucleo_f446re/flash
- examples/nucleo_g071rb/flash
- examples/nucleo_g431kb/flash
- examples/nucleo_g431rb/flash
- examples/nucleo_h723zg/flash
- examples/nucleo_h743zi/flash
Replaces:
- examples/stm32f072_discovery/hard_fault
- examples/stm32f3_discovery/gdb
- examples/stm32f469_discovery/hard_fault
Replaces:
- examples/blue_pill_f103/itm
- examples/nucleo_f103rb/itm
- examples/nucleo_l476rg/itm
Replaces:
- examples/nucleo_f042k6/vector_table_ram
Replaces:
- examples/nucleo_f429zi/rtc_mcp7941x
Replaces:
- examples/stm32f072_discovery/tmp102
- examples/stm32f746g_discovery/tmp102
Replaces:
- examples/nucleo_f103rb/rtos
- examples/nucleo_f303k8/rtos
- examples/nucleo_f303re/rtos
- examples/nucleo_f411re/rtos
- examples/nucleo_f767zi/freertos_blink
- examples/nucleo_g071rb/rtos
- examples/nucleo_l552ze-q/freertos_blink
Replaces:
- examples/weact_u585ci/usb
Replaces:
- examples/nucleo_f429zi/usbfatfs
Replaces:
- examples/feather_m4/usbserial
- examples/nucleo_f446ze/usbserial
- examples/samd21_xplained_pro/usbserial
- examples/same54_xplained_pro/usbserial
- examples/samg55_xplained_pro/usbserial
Replaces:
- examples/arduino_uno/basic/blink
- examples/avr/mega_pro
- examples/black_pill_f103/blink
- examples/black_pill_f401/blink
- examples/black_pill_f411/blink
- examples/blue_pill_f103/blink
- examples/feather_m4/blink
- examples/feather_rp2040/blink
- examples/nrf51422-dk/blink
- examples/nrf52840-dk/blink
- examples/nrf5340-dk/blink
- examples/nucleo_c031c6/blink
- examples/nucleo_f031k6/blink
- examples/nucleo_f042k6/blink
- examples/nucleo_f072rb/blink
- examples/nucleo_f091rc/blink
- examples/nucleo_f103rb/blink
- examples/nucleo_f207zg/blink
- examples/nucleo_f303k8/blink
- examples/nucleo_f303re/blink
- examples/nucleo_f334r8/blink
- examples/nucleo_f401re/blink
- examples/nucleo_f411re/blink
- examples/nucleo_f429zi/blink
- examples/nucleo_f439zi/blink
- examples/nucleo_f446re/blink
- examples/nucleo_f446ze/blink
- examples/nucleo_f722ze/blink
- examples/nucleo_f746zg/blink
- examples/nucleo_f767zi/blink
- examples/nucleo_g070rb/blink
- examples/nucleo_g071rb/blink
- examples/nucleo_g431kb/blink
- examples/nucleo_g431rb/blink
- examples/nucleo_g474re/blink
- examples/nucleo_h503rb/blink
- examples/nucleo_h533re/blink
- examples/nucleo_h563zi/blink
- examples/nucleo_h723zg/blink
- examples/nucleo_h743zi/blink
- examples/nucleo_l031k6/blink
- examples/nucleo_l053r8/blink
- examples/nucleo_l152re/blink
- examples/nucleo_l432kc/blink
- examples/nucleo_l452re/blink
- examples/nucleo_l476rg/blink
- examples/nucleo_l496zg-p/blink
- examples/nucleo_l552ze-q/blink
- examples/nucleo_u083rc/blink
- examples/nucleo_u385rg-q/blink
- examples/nucleo_u575zi-q/blink
- examples/olimexino_stm32/blink
- examples/rp_pico/blink
- examples/samd/blink
- examples/samd21_xplained_pro/blink
- examples/same54_xplained_pro/blink
- examples/same70_xplained/blink
- examples/samg55_xplained_pro/blink
- examples/samv71_xplained_ultra/blink
- examples/srxe/blink
- examples/stm32f030f4p6_demo_board/blink
- examples/stm32f072_discovery/blink
- examples/stm32f0_discovery/blink
- examples/stm32f1_discovery/blink
- examples/stm32f3_discovery/blink
- examples/stm32f401_discovery/blink
- examples/stm32f407vet6_devebox/blink
- examples/stm32f429_discovery/blink
- examples/stm32f469_discovery/blink
- examples/stm32f746g_discovery/blink
- examples/stm32f769i_discovery/blink
- examples/stm32h750vbt6_devebox/blink
- examples/stm32l1_discovery/blink
- examples/stm32l476_discovery/blink
- examples/thingplus_rp2040/blink
- examples/weact_g0b1cb/blink
- examples/weact_h503cb/blink
- examples/weact_h523ce/blink
- examples/weact_h562rg/blink
- examples/weact_u585ci/blink
Replaces:
- examples/avr/logger
- examples/linux/logger
Replaces:
- examples/arduino_nano/printf
- examples/linux/printf
Replaces:
- examples/stm32f072_discovery/uart
- examples/stm32f401_discovery/uart
Replaces:
- examples/samv71_xplained_ultra/pwm
Replaces:
- examples/same70_xplained/timer
Replaces the table of every single example, which is not useful anymore with
examples that run on many boards.
@salkinium
salkinium force-pushed the feature/dedup_examples branch from 8eee4f7 to 9195d0a Compare October 13, 2026 20:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

3 participants