Symptom
Every FastLED ESP32 QEMU workflow on master fails identically:
All compilations completed successfully in 04m:08s
Running esptool merge_bin: ... merged.bin ...
build error: build failed: compilation failed for .build/pio/esp32dev/src/main.cpp:
In file included from src/main.cpp:5:
src/sketch/BlinkParallel.ino:8:10: fatal error: FastLED.h: No such file or directory
8 | #include "FastLED.h"
| ^~~~~~~~~~~
Reproducible: every recent qemu_esp32dev_test.yml / qemu_esp32s3_test.yml / qemu_esp32c3_test.yml run on master.
Sequence
The workflow runs two steps:
# 1. Build via FastLED's ci-compile.py (defaults to fbuild backend)
uv run ci/ci-compile.py esp32dev --examples BlinkParallel --merged-bin \
--defines FASTLED_ESP32_IS_QEMU --verbose
# → "All compilations completed successfully in 04m:08s"
# → merge_bin runs, produces merged.bin OK
# 2. Run the merged binary in QEMU
uv run fbuild test-emu --emulator qemu --environment esp32dev \
.build/pio/esp32dev 2>&1 | tee qemu_output.log
# → fbuild test-emu triggers a project rebuild
# → "fatal error: FastLED.h: No such file or directory"
Step 1's compile succeeded — lib/FastLED/src/FastLED.h is on disk in the build dir, the link succeeded, the merged firmware was produced. Step 2's fbuild test-emu apparently re-validates / rebuilds the project from .build/pio/esp32dev/, and that rebuild can't find FastLED.h.
Root cause hypothesis
fbuild test-emu reads the project's platformio.ini to know which compile args to use, but doesn't replicate ci-compile.py's lib-discovery logic. Specifically:
- PlatformIO auto-discovers any library under
<project>/lib/ and adds its src/ to the include path. This is the implicit lib resolution rule (Library Dependency Finder).
- fbuild's project-config reader appears to only honor explicit
lib_deps entries. The FastLED layout uses lib/FastLED/src/... (no lib_deps entry in the generated platformio.ini — the lib is auto-discovered).
- So fbuild's rebuild during
test-emu produces a compile command without -Ilib/FastLED/src, and #include "FastLED.h" 404s.
That ci-compile.py's first invocation of fbuild succeeded suggests ci-compile.py is augmenting the fbuild call with the FastLED include path explicitly. test-emu doesn't go through ci-compile.py — it goes directly through fbuild's CLI.
Affected workflows
ESP32-DEV QEMU Test
ESP32-S3 QEMU Test
ESP32-C3 QEMU Test
(Any future ESP32 QEMU smoke that uses the same template.)
Proposed fix
In fbuild's project loader (likely crates/fbuild-config/src/ or crates/fbuild-build/src/ — wherever the PlatformIO project ini is read), implement PIO's auto-discovery of <project>/lib/<LibName>/src/:
for d in <project>/lib/*/:
if (d/src exists) -> add d/src to include path AND compile d/src/**/*.{c,cpp,cpp.hpp}
elif (d/include exists) -> add d/include + compile d/src or fall back
else -> add d itself (flat layout)
This matches PlatformIO's LDF "chain" mode default. Without it, every fbuild build of a PIO project that uses lib/FastLED/ (which is FastLED's own CI convention) fails.
Alternative (workaround on FastLED side): have ci-compile.py write an explicit lib_deps = FastLED=symlink://... (or equivalent) into the generated platformio.ini so fbuild test-emu's reader sees it. Heavier than the fbuild-side fix and only papers over the deficiency.
Test plan
- Unit:
BoardConfig or project-loader test: load a synthetic PIO project with lib/Foo/src/foo.h only (no lib_deps), assert the include path contains lib/Foo/src.
- Integration: re-run FastLED's QEMU workflows after fbuild bump.
Filed during a coordinated CI-green sweep on FastLED master. No immediate FastLED-side workaround attempted — the underlying fbuild gap will surface again for any future PIO consumer.
Symptom
Every FastLED ESP32 QEMU workflow on master fails identically:
Reproducible: every recent
qemu_esp32dev_test.yml/qemu_esp32s3_test.yml/qemu_esp32c3_test.ymlrun on master.Sequence
The workflow runs two steps:
Step 1's compile succeeded —
lib/FastLED/src/FastLED.his on disk in the build dir, the link succeeded, the merged firmware was produced. Step 2'sfbuild test-emuapparently re-validates / rebuilds the project from.build/pio/esp32dev/, and that rebuild can't find FastLED.h.Root cause hypothesis
fbuild test-emureads the project'splatformio.inito know which compile args to use, but doesn't replicate ci-compile.py's lib-discovery logic. Specifically:<project>/lib/and adds itssrc/to the include path. This is the implicit lib resolution rule (Library Dependency Finder).lib_depsentries. The FastLED layout useslib/FastLED/src/...(nolib_depsentry in the generated platformio.ini — the lib is auto-discovered).test-emuproduces a compile command without-Ilib/FastLED/src, and#include "FastLED.h"404s.That ci-compile.py's first invocation of fbuild succeeded suggests ci-compile.py is augmenting the fbuild call with the FastLED include path explicitly.
test-emudoesn't go through ci-compile.py — it goes directly through fbuild's CLI.Affected workflows
ESP32-DEV QEMU TestESP32-S3 QEMU TestESP32-C3 QEMU Test(Any future ESP32 QEMU smoke that uses the same template.)
Proposed fix
In fbuild's project loader (likely
crates/fbuild-config/src/orcrates/fbuild-build/src/— wherever the PlatformIO project ini is read), implement PIO's auto-discovery of<project>/lib/<LibName>/src/:This matches PlatformIO's LDF "chain" mode default. Without it, every fbuild build of a PIO project that uses
lib/FastLED/(which is FastLED's own CI convention) fails.Alternative (workaround on FastLED side): have
ci-compile.pywrite an explicitlib_deps = FastLED=symlink://...(or equivalent) into the generated platformio.ini so fbuild test-emu's reader sees it. Heavier than the fbuild-side fix and only papers over the deficiency.Test plan
BoardConfigor project-loader test: load a synthetic PIO project withlib/Foo/src/foo.honly (nolib_deps), assert the include path containslib/Foo/src.Filed during a coordinated CI-green sweep on FastLED master. No immediate FastLED-side workaround attempted — the underlying fbuild gap will surface again for any future PIO consumer.