From a5a0c2c23debcf7c34a93975d3904f3ef7ef8f7b Mon Sep 17 00:00:00 2001 From: zackees Date: Sun, 23 Aug 2026 19:11:02 -0700 Subject: [PATCH 1/2] fix(esp8266): fetch the core from a release asset so submodules come with it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes FastLED/fbuild#1380. The ESP8266 core was fetched from `github.com/esp8266/Arduino/archive/refs/tags/3.1.2.tar.gz` — GitHub's auto-generated source archive, which omits submodules by design. The directories are created; the contents are not. `esp8266/Arduino` keeps five: `libraries/LittleFS/lib/littlefs`, `libraries/SoftwareSerial`, `libraries/ESP8266SdFat`, `tools/sdk/lwip2/builder`, and `tools/sdk/ssl/bearssl`. So any sketch reaching `` failed inside the core's own header: LittleFS.h:38:10: fatal error: ../lib/littlefs/lfs.h: No such file The failure is not guardable from the consumer side, which is what makes it nastier than a missing package: `__has_include()` passes, because the header is present and only the thing it includes is absent. FastLED's esp8266 badge went red on master for this. The release asset published on the tag bundles the submodule contents. Verified against 3.1.2 before switching: 47 entries under `libraries/LittleFS/lib/littlefs`, `lfs.h` among them. The archive root also changes (`Arduino-3.1.2/` -> `esp8266-3.1.2/`), which is fine — `find_framework_root` discovers the root by looking for `cores/` rather than by name — and `.zip` already dispatches to `extract_zip` on extension. The test asserts the URL *shape*, not the string. A future version bump must not drift back to `/archive/refs/`, and that is the easy mistake to make since every other core in this crate uses that form. ## Other cores The issue asks whether others are exposed. Checked each core URL's repo for `.gitmodules` at its pinned tag: esp8266/Arduino@3.1.2 5 submodules <- this bug adafruit/ArduinoCore-samd@1.7.16 2 submodules, both under libraries/ (Adafruit_TinyUSB_Arduino, Adafruit_ZeroDMA) SiliconLabs/arduino@2.2.0 1 submodule (extra/core-api) ArduinoCore-avr, -mbed, -API, ATTinyCore none samd carries the same latent failure — a sketch including TinyUSB or ZeroDMA would break identically. Not fixed here: it needs its own verification that a bundling artifact exists, and bundling this into an esp8266 fix would make both harder to review. Reported on the issue instead. Co-Authored-By: Claude Opus 5 (1M context) --- .../src/library/esp8266_framework.rs | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/crates/fbuild-library/src/library/esp8266_framework.rs b/crates/fbuild-library/src/library/esp8266_framework.rs index 829472cbf..a8e05c366 100644 --- a/crates/fbuild-library/src/library/esp8266_framework.rs +++ b/crates/fbuild-library/src/library/esp8266_framework.rs @@ -19,8 +19,26 @@ use crate::{CacheSubdir, Framework, PackageBase, PackageInfo}; /// Framework version matching espressif8266@4.2.1. const ESP8266_FRAMEWORK_VERSION: &str = "3.1.2"; +/// Release asset, NOT `archive/refs/tags/...`. +/// +/// FastLED/fbuild#1380: GitHub's auto-generated source archives deliberately +/// omit submodules, and `esp8266/Arduino` keeps five of them — +/// `libraries/LittleFS/lib/littlefs`, `libraries/SoftwareSerial`, +/// `libraries/ESP8266SdFat`, `tools/sdk/lwip2/builder`, and +/// `tools/sdk/ssl/bearssl`. Unpacking the source archive creates those +/// directories empty, so `#include ` compiled and then failed +/// inside the core's own header: +/// +/// LittleFS.h:38:10: fatal error: ../lib/littlefs/lfs.h: No such file +/// +/// `__has_include()` passes in that state — the header is there +/// and the thing it needs is not — so consumers cannot guard against it. +/// +/// The release asset published on the tag bundles the submodule contents. +/// Verified against 3.1.2: 47 entries under `libraries/LittleFS/lib/littlefs` +/// including `lfs.h`. const ESP8266_FRAMEWORK_URL: &str = - "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/esp8266/Arduino/archive/refs/tags/3.1.2.tar.gz"; + "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/esp8266/Arduino/releases/download/3.1.2/esp8266-3.1.2.zip"; /// ESP8266 Arduino framework manager. pub struct Esp8266Framework { @@ -264,6 +282,35 @@ mod tests { use super::*; use crate::Package; + /// FastLED/fbuild#1380: this core carries submodules, so it must come + /// from an artifact that bundles them. + /// + /// GitHub's `archive/refs/tags/...` tarballs omit submodule contents by + /// design — the directories are created empty. That produced a build + /// failure inside the core's own `LittleFS.h`, past every capability + /// guard a consumer could write, because `__has_include()` + /// still passes when the header is present and its dependency is not. + /// + /// Asserting the URL *shape* rather than the exact string: the point is + /// that a future version bump must not quietly move back to the source + /// archive, which is the easy mistake since that URL form is what every + /// other core in this crate uses. + #[test] + fn framework_url_is_a_release_asset_not_a_source_archive() { + assert!( + ESP8266_FRAMEWORK_URL.contains("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/releases/download/"), + "esp8266 core must come from a release asset that bundles its submodules: {ESP8266_FRAMEWORK_URL}" + ); + assert!( + !ESP8266_FRAMEWORK_URL.contains("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/archive/refs/"), + "GitHub source archives omit submodules; LittleFS/SoftwareSerial/ SdFat/lwip2/bearssl would unpack empty: {ESP8266_FRAMEWORK_URL}" + ); + assert!( + ESP8266_FRAMEWORK_URL.contains(ESP8266_FRAMEWORK_VERSION), + "asset URL must track the pinned version: {ESP8266_FRAMEWORK_URL}" + ); + } + #[test] fn test_esp8266_framework_not_installed() { let tmp = tempfile::TempDir::new().unwrap(); From 8b45afb6bc6c7a1c730bac813440f83960275e10 Mon Sep 17 00:00:00 2001 From: zackees Date: Sun, 23 Aug 2026 19:51:25 -0700 Subject: [PATCH 2/2] fix(esp8266): fence the compiler-error sample so it stops being a doctest The four-space-indented sample in the `ESP8266_FRAMEWORK_URL` doc comment is a Markdown code block, so rustdoc treated it as Rust and tried to compile `LittleFS.h:38:10: fatal error: ...`: error: expected one of `!` or `::`, found `.` Fenced as ```text. Missed locally because I verified with `cargo test --lib`, which does not run doctests. `--doc` does, and now passes. Co-Authored-By: Claude Opus 5 (1M context) --- crates/fbuild-library/src/library/esp8266_framework.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/fbuild-library/src/library/esp8266_framework.rs b/crates/fbuild-library/src/library/esp8266_framework.rs index a8e05c366..18dcdbeeb 100644 --- a/crates/fbuild-library/src/library/esp8266_framework.rs +++ b/crates/fbuild-library/src/library/esp8266_framework.rs @@ -29,7 +29,9 @@ const ESP8266_FRAMEWORK_VERSION: &str = "3.1.2"; /// directories empty, so `#include ` compiled and then failed /// inside the core's own header: /// -/// LittleFS.h:38:10: fatal error: ../lib/littlefs/lfs.h: No such file +/// ```text +/// LittleFS.h:38:10: fatal error: ../lib/littlefs/lfs.h: No such file +/// ``` /// /// `__has_include()` passes in that state — the header is there /// and the thing it needs is not — so consumers cannot guard against it.