Repository navigation
Description
Summary
fbuild's Teensy orchestrator compiles and links every bundled framework library found under the Teensyduino package's libraries/ directory, regardless of whether project sources reference them. For RAM-constrained Teensy targets (teensyLC: 8 KB, teensy30: 16 KB) this blows the RAM budget at link time because unreachable libraries leave static-initialized allocations in .bss / .dmabuffers that --gc-sections can't prune (init_array references pin them).
This is the root cause of FastLED/FastLED#2406.
Evidence
For fbuild 2.2.3 building the Blink sketch (FastLED examples/Blink/Blink.ino) on teensylc:
- Total translation units compiled: 451 (vs. 242 for PlatformIO's LDF
chainmode on the same sketch/board) - Breakdown of fbuild's TUs by origin:
| Library | fbuild TUs | Referenced by FastLED? | Notes |
|---|---|---|---|
cores/teensy3 |
67 | yes (core) | expected |
libraries/Audio |
91 | yes (audio_input_teensy.h) |
expected |
libraries/SdFat |
47 | yes (fs_sdcard_arduino.hpp) |
expected |
libraries/FNET |
153 | NO | ❌ TCP/IP stack, never referenced |
libraries/Snooze |
44 | NO | ❌ low-power lib, never referenced |
libraries/RadioHead |
35 | NO | ❌ radio stack, never referenced |
libraries/mbedtls |
68 | NO | ❌ crypto, pulled in transitively by FNET |
| misc (Wire, SPI, Adafruit_NeoPixel, OctoWS2811, DmxSimple, SoftwareSerial, SerialFlash) | 13 | partial | mixed |
| FastLED + sketch | 23 | yes | expected |
300 of the 451 TUs (~66%) are unreferenced.
RAM impact (teensyLC, 8192 B RAM)
Observed in CI (https://github.com/FastLED/FastLED/actions/runs/24901454197 — link failure logs):
ld: firmware.elf section `.bss' will not fit in region `RAM'
ld: region `RAM' overflowed by 2172 bytes (Blink)
ld: region `RAM' overflowed by 2424 bytes (Apa102HD)
ld: region `RAM' overflowed by 1708 bytes (AudioInput)
...
For comparison, the same Blink sketch built via PlatformIO pio run on the same host:
.text 37852
.data 312
.bss 2636
.dmabuffers 192
.usbbuffers 864
.usbdescriptortable 160
-------------------------------
RAM total 4164 (50.8 % of 8192)
→ fbuild's RAM footprint for Blink is ~10364 B (150 % over budget), PlatformIO's is ~4164 B. Delta ≈ 6.2 KB, almost entirely explained by .bss statics from the four unreferenced libraries.
Suspected cause
#163 / #164 fixed the opposite problem — not discovering framework libraries at all — by switching to framework-arduinoteensy and "selecting and compiling only bundled framework libraries referenced by project sources, following transitive framework includes while skipping examples/extras/tests."
The observed behavior on teensyLC suggests the selection step either:
- Does not actually run for teensyLC/teensy30 (maybe gated on teensy4 only), or
- Runs but the transitive-include walk grows to the entire
libraries/*set because one of the reachable headers (e.g.,Audio.h) transitively#includes something that fans out into FNET/RadioHead/Snooze/mbedtls, or - Runs but conservatively falls back to "include everything" when selection fails.
Walking .fbuild/build/teensylc/release/compile_commands.json confirms every libraries/FNET/**/*.c(pp), libraries/Snooze/**/*.c(pp), libraries/RadioHead/**/*.c(pp), libraries/mbedtls/**/*.c(pp) is compiled.
Reproduction
In FastLED master:
./compile --no-interactive --no-parallel --max-failures 1 teensylc Blink
./compile --no-interactive --no-parallel --max-failures 1 teensy30 AnalogOutputCI reproduces this on every teensy30/teensyLC job.
Expected behavior
For Blink on teensylc, fbuild should compile only the libraries transitively referenced by the sketch + FastLED + the teensy3 core. Minimum set observed in PlatformIO: teensy3 core, Audio, SdFat (+ its deps), SPI, Wire, Adafruit_NeoPixel. Not: FNET, Snooze, RadioHead, mbedtls.
Match PlatformIO's LDF chain mode for the framework = arduino / platform = teensy pair.
Suggested direction
Two tiers:
Short-term stopgap (unblock CI for teensy30/LC):
- Allowlist the libraries that FastLED actually needs, or equivalently deny-list the unreferenced set (
FNET,Snooze,RadioHead,mbedtls,FlightSim,HID,OSC,TimerOne,TimerThree) when a per-target RAM budget <= 16 KB. - Per-target RAM budget guard: refuse to link
.bss-heavy libs when the target's RAM budget is below a threshold.
Long-term fix: implement true LDF-style transitive reachability. Filed as a separate feature issue (see cross-ref when created).
Impact
Blocks teensy30 and teensyLC CI coverage in FastLED/FastLED. Workaround is to route these boards through pio run instead of fbuild (see FastLED/FastLED#2406 for the consumer-side tracking issue and the --backend=platformio escape hatch added there).
Related
- Teensy 3.0 / LC link-time RAM overflow (.dmabuffers / .bss) across all examples FastLED#2406 — consumer-side tracking
- Teensy 4.x builds miss framework libraries like SPI and OctoWS2811 #163 / Fix Teensy framework library resolution #164 — prior Teensy framework-library resolution fix
- stm32: Arduino_Core_STM32 framework libraries (SPI, Wire, ...) not discovered — fatal error: SPI.h: No such file or directory #202 — parallel issue for STM32 (opposite direction: libs not discovered)
Activity
feat(ci-compile): add --backend={fbuild,platformio} switch
feat(library-selection): foundation + Phase 0 scaffolding for #205
feat(library-selection): foundation + Phase 0 scaffolding for #205 (#207
feat(library-selection): #205 Phase 6 acceptance gates + Phase 8 CLI/…
feat(library-selection): #205 Phase 7 perf benchmarks (P-02, P-03) (#210
zackees commentedon Apr 25, 2026
Closed by the foundation work landed for #205.
The teensyLC/teensy30 RAM overflow was caused by the old basename-matching framework-library resolver pulling in
FNET,Snooze,RadioHead, andmbedtlsfor any sketch that mentioned a header whose basename collided with one of those libs.PR #207 replaced the basename matcher with a PlatformIO-LDF-style two-pass resolver (
fbuild-library-select) that uses path-prefix attribution: a library is selected only when an#includeactually resolves to a path inside its owninclude_dirs. The four offending libraries are no longer compiled or linked for a Blink sketch on teensyLC.PR #208 added the acceptance gate at
crates/fbuild-build/tests/teensylc_acceptance.rs. It builds teensyLC/Blink end-to-end and asserts:.bss<= 3 KBfnet_/snooze_/RadioHead/mbedtlssymbol substrings in the ELFcompile_commands.json<= 250compile_commands.jsonThe gate is
#[ignore]'d by default (it downloads the toolchain) and runs in CI via the workflow added in this PR's follow-up.References: PR #207, PR #208, issue #205.
ci(library-selection): #205 acceptance + bench workflows + bench/ stub (