ADR-001: CubeMX-Generated Code Lives Under vendor/cubemx/¶
| Field | Value |
|---|---|
| Date | 2026-09-29 |
| Status | Accepted |
| Deciders | TODO |
| Supersedes | — |
| Superseded by | — |
Context and Problem Statement¶
STM32CubeMX generates and re-generates MCU init code, HAL drivers, and a linker script into fixed
paths relative to its own configured project root, whenever the developer re-runs it from the
CubeMX GUI.
This generator (project templates' repo command) also scaffolds and later updates
(repo update) the same project over time.
If both tools write into overlapping paths, one can silently clobber the other's output, and
renaming any path CubeMX owns breaks CubeMX's own re-generation the next time it runs.
This project type also builds two different things with two different compilers: host-side unit
tests (Catch2, native compiler, driven by the repository-root CMakeLists.txt) and the actual
firmware (ARM cross-compiler). Those need separate CMake project roots regardless of the CubeMX
question — see the "Firmware Subtree" decision driver below.
Decision Drivers¶
- CubeMX must be able to re-generate its output at any time without the generator interfering, and vice versa.
- Vendor-provided or vendor-generated code should live under
vendor/, matching this project type's stated preference for keeping hand-written and generated/foreign code separated. - A directory should have one owner and one reason to change.
firmware/'s own config changes with the project's build setup; CubeMX's output changes only when CubeMX regenerates. Nesting one inside the other mixes those two reasons to change and makesfirmware/'s purpose unclear from its contents alone. - A real-world CubeMX project (aka The Example) already demonstrates a self-contained CubeMX
subtree (
cmake/stm32cubemx/CMakeLists.txt) that isolates its generated sources behindINTERFACE/OBJECTCMake targets — this subtree can be relocated wholesale without internal changes. - The repository root
CMakeLists.txtmust stay the one Conan's host toolchain configures (so the existingmake build/make test/make lintMakefile targets keep working unchanged for every C++ project type, this one included). The firmware/CubeMX side therefore cannot also live at the repository root — it needs its own subtree with its ownCMakeLists.txt.
Considered Options¶
- Let CubeMX generate directly into the project root (
Core/,Drivers/, etc. at the top level), as the example does today. - Configure CubeMX's own project path to
firmware/vendor/cubemx/, nested inside the firmware cross-build's ownfirmware/subtree. - Configure CubeMX's own project path to a repository-root
vendor/cubemx/, a sibling offirmware/rather than nested inside it;firmware/then holds exclusively its own hand-maintained cross-build configuration (CMakeLists.txt, toolchain file,CMakePresets.json) — no foreign code. The generator scaffolds only a README placeholder undervendor/cubemx/once and never regenerates or overwrites anything under it afterward. - Have this generator (
repo) itself invoke CubeMX or merge CubeMX's output into its structure programmatically.
Decision Outcome¶
Chosen option: 3. CubeMX is pointed at vendor/cubemx/ (repository root) as its own project
root via a one-time manual GUI step (documented in the scaffolded vendor/cubemx/README.md). This
generator treats that whole subtree as opaque: it scaffolds a placeholder once and registers no
further action against any path inside it, so repo update never touches it. firmware/
(firmware/CMakeLists.txt) pulls in the shared, host-testable logic from ../src/ and links it
against the CubeMX-generated HAL/init code in ../vendor/cubemx/; the repository-root
CMakeLists.txt remains the host/test project untouched by any of this.
Because ../vendor/cubemx/cmake/stm32cubemx lies outside firmware/'s own source tree,
firmware/CMakeLists.txt's add_subdirectory() call for it must pass an explicit binary_dir
argument — CMake cannot auto-derive a build-tree location for an out-of-tree source directory.
Positive Consequences¶
- Clean, permanent separation — CubeMX never needs a path renamed, no matter how often
repo updateruns. - Matches the project's
vendor/convention for foreign/generated code. firmware/has exactly one job — turn../src/plus../vendor/cubemx/into a target executable — and its own contents are entirely hand-maintained project files; nothing inside it is foreign or CubeMX-owned.- The example's existing
cmake/stm32cubemx/CMakeLists.txtpattern relocates intovendor/cubemx/cmake/stm32cubemx/without any internal changes — just anadd_subdirectory(../vendor/cubemx/cmake/stm32cubemx ...)fromfirmware/CMakeLists.txt. - The repository-root
CMakeLists.txt(host/test build) andfirmware/CMakeLists.txt(cross build) stay two independent CMake project roots, each configured with its own toolchain — no conditional branching onCMAKE_CROSSCOMPILINGinside a single sharedCMakeLists.txt.
Negative Consequences / Risks¶
- Requires a manual, one-time step: pointing CubeMX's GUI at
vendor/cubemx/when the project is first configured. If a developer skips this and lets CubeMX generate at the root instead, the two tools' file sets can collide. add_subdirectory()with an out-of-tree source directory needs an explicit binary directory argument; a future contributor copying this pattern for another out-of-tree subdirectory must remember the same requirement.
Pros and Cons of the Options¶
Option 1 — CubeMX generates at the project root¶
- Pro: Zero manual CubeMX configuration step.
- Con: Board-init code sits directly in the tree this generator also manages, so
repo updateand CubeMX re-generation can conflict; contradicts thevendor/convention.
Option 2 — firmware/vendor/cubemx/ boundary (nested)¶
- Pro: Permanent, conflict-free separation from the generator; matches the
vendor/convention;add_subdirectory()targets stay insidefirmware/'s own tree, no explicitbinary_dirneeded. - Con: Mixes
firmware/'s own hand-maintained build configuration with CubeMX's foreign, regenerated output in one directory — obscures at a glance whatfirmware/is actually for.
Option 3 — vendor/cubemx/ at the repository root (chosen)¶
- Pro: Permanent, conflict-free separation from the generator; matches the
vendor/convention;firmware/reads unambiguously as build configuration only,vendor/as foreign code only. - Con: One manual GUI step required when the project is first set up; the out-of-tree
add_subdirectory()call needs an explicitbinary_dir.
Option 4 — Generator drives CubeMX programmatically¶
- Pro: Fully automated, no manual step.
- Con: CubeMX has no supported non-interactive/scriptable code-generation interface for this
workflow; reverse-engineering
.iocregeneration would be large, fragile scope with no guaranteed stability across CubeMX versions. Rejected as infeasible for this generator.
Notes¶
vendor/cubemx/README.md documents the one-time CubeMX project-path configuration step for
whoever sets up or re-configures the CubeMX side of the project. firmware/CMakePresets.json is
what the STM32 VS Code plug-in consumes — point its CMake Tools extension at the firmware/
subdirectory (cmake.sourceDirectory), not at the repository root.