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
chain mode 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 AnalogOutput
CI 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
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/.dmabuffersthat--gc-sectionscan't prune (init_array references pin them).This is the root cause of FastLED/FastLED#2406.
Evidence
For
fbuild 2.2.3building the Blink sketch (FastLEDexamples/Blink/Blink.ino) onteensylc:chainmode on the same sketch/board)cores/teensy3libraries/Audioaudio_input_teensy.h)libraries/SdFatfs_sdcard_arduino.hpp)libraries/FNETlibraries/Snoozelibraries/RadioHeadlibraries/mbedtls300 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):
For comparison, the same Blink sketch built via PlatformIO
pio runon the same host:→ 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
.bssstatics from the four unreferenced libraries.Suspected cause
#163 / #164 fixed the opposite problem — not discovering framework libraries at all — by switching to
framework-arduinoteensyand "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:
libraries/*set because one of the reachable headers (e.g.,Audio.h) transitively#includes something that fans out into FNET/RadioHead/Snooze/mbedtls, orWalking
.fbuild/build/teensylc/release/compile_commands.jsonconfirms everylibraries/FNET/**/*.c(pp),libraries/Snooze/**/*.c(pp),libraries/RadioHead/**/*.c(pp),libraries/mbedtls/**/*.c(pp)is compiled.Reproduction
In FastLED master:
CI 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
chainmode for theframework = arduino/platform = teensypair.Suggested direction
Two tiers:
Short-term stopgap (unblock CI for teensy30/LC):
FNET,Snooze,RadioHead,mbedtls,FlightSim,HID,OSC,TimerOne,TimerThree) when a per-target RAM budget <= 16 KB..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 runinstead of fbuild (see FastLED/FastLED#2406 for the consumer-side tracking issue and the--backend=platformioescape hatch added there).Related