From 0762b5485fe06f45408c0b0a4cd938a52b093e5b Mon Sep 17 00:00:00 2001 From: Zach Vorhies Date: Fri, 18 Sep 2026 10:30:16 -0700 Subject: [PATCH] fix(esp32): build a pre-unified-toolchain platform pin against stable #1434 started honoring `platform = ` for ESP32. A pin to a pioarduino release that predates the unified toolchain -- 51.03.04 (arduino-esp32 3.0), which FastLED's IDF 5.1 boards and its esp_extra_libs project use -- names per-MCU registry toolchains (toolchain-xtensa-esp32s3@12.2.0+20230208), and its toolchain-riscv32-esp entry is a registry version, not a URL. Toolchain resolution cannot read either, so it fell through to the legacy hardcoded URLs, which 404: download failed for .../xtensa-esp-elf-14.2.0_20241119.linux-amd64.tar.xz: HTTP 404 Not Found Every release before #1434 built such a pin against the stable platform, because it ignored the pin. Keep that working: when an honored pin has no unified toolchain for the MCU (Esp32Platform::has_unified_toolchain), warn and build and provision against stable, the same fallback #1434 already uses for registry and git pins. Proper per-MCU registry toolchain support is a separate feature. Co-Authored-By: Claude Opus 5 (1M context) --- .../src/esp32/orchestrator/packages.rs | 56 +++++++++++++++++++ .../src/library/esp32_platform.rs | 44 +++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/crates/fbuild-build-esp/src/esp32/orchestrator/packages.rs b/crates/fbuild-build-esp/src/esp32/orchestrator/packages.rs index b28b27cdc..5e4e78d08 100644 --- a/crates/fbuild-build-esp/src/esp32/orchestrator/packages.rs +++ b/crates/fbuild-build-esp/src/esp32/orchestrator/packages.rs @@ -40,6 +40,13 @@ pub(super) async fn resolve_pioarduino_packages( // Ensure pioarduino platform (contains platform.json with metadata URLs). let platform = pioarduino_platform(project_dir, env_config); fbuild_packages::Package::ensure_installed(&platform).await?; + let platform = if legacy_toolchain_pin(&platform, env_config, mcu_config.is_riscv()) { + let stable = fbuild_packages::library::Esp32Platform::new(project_dir); + fbuild_packages::Package::ensure_installed(&stable).await?; + stable + } else { + platform + }; // Resolve toolchain via metadata let toolchain = resolve_and_create_toolchain(&platform, project_dir, mcu_config)?; @@ -132,6 +139,19 @@ pub(crate) async fn provision_esp32( // Every other package is named by the platform's platform.json. return Ok(rows); } + // Provision what the build will actually use (see `legacy_toolchain_pin`). + let platform = if legacy_toolchain_pin(&platform, env_config, mcu_config.is_riscv()) { + let stable = fbuild_packages::library::Esp32Platform::new(project_dir); + let stable_row = provision_package(PackageKind::Platform, &stable, mode).await; + let stable_ready = is_installed(&stable_row); + rows.push(stable_row); + if !stable_ready { + return Ok(rows); + } + stable + } else { + platform + }; rows.push(provision_toolchain(&platform, project_dir, &mcu_config, mode).await); @@ -255,6 +275,42 @@ async fn ensure_sdk_libs( Ok(()) } +/// True when an honored `platform` pin predates the unified ESP32 toolchain +/// and the build must fall back to the pioarduino stable platform. +/// +/// #1432 started honoring `platform = `. Releases before the +/// unified toolchain (pioarduino 51.x, arduino-esp32 3.0) name per-MCU +/// registry packages (`toolchain-xtensa-esp32s3@12.2.0+20230208`) that toolchain +/// resolution cannot read, so it fell through to legacy hardcoded URLs that +/// 404 and failed the build outright. Until per-MCU registry toolchains are +/// supported, such a pin warns and builds against stable -- exactly what +/// every release before #1432 did with it. +fn legacy_toolchain_pin( + platform: &fbuild_packages::library::Esp32Platform, + env_config: Option<&HashMap>, + is_riscv: bool, +) -> bool { + let pinned = env_config + .and_then(|env| { + crate::package_override::resolve_platform_override(env, "platform-espressif32") + }) + .is_some(); + if !pinned || platform.has_unified_toolchain(is_riscv) { + return false; + } + let pin = env_config + .and_then(|env| env.get("platform")) + .map(|p| p.trim()) + .unwrap_or("the pinned platform"); + tracing::warn!( + "platform pin `{pin}` predates the unified ESP32 toolchain (its platform.json has no \ + toolchain metadata URL for this MCU) and fbuild cannot provision its per-MCU registry \ + toolchain yet; building with the pioarduino stable platform instead, as fbuild did \ + before it honored platform pins" + ); + true +} + /// Name a `platform` pin fbuild cannot honor instead of dropping it silently /// (FastLED/fbuild#1407). Registry pins and git URLs fall back to the /// pioarduino stable platform, which carries a different framework release. diff --git a/crates/fbuild-library/src/library/esp32_platform.rs b/crates/fbuild-library/src/library/esp32_platform.rs index 7689bb989..ce1952a41 100644 --- a/crates/fbuild-library/src/library/esp32_platform.rs +++ b/crates/fbuild-library/src/library/esp32_platform.rs @@ -96,6 +96,20 @@ impl Esp32Platform { self.get_package_url(package_name) } + /// Whether this platform names the MCU-primary toolchain by a metadata + /// URL -- the unified `toolchain-xtensa-esp-elf` / `toolchain-riscv32-esp` + /// scheme that toolchain resolution understands. + /// + /// Older pioarduino releases (e.g. 51.03.04) name per-MCU registry + /// packages instead (`toolchain-xtensa-esp32s3@12.2.0+20230208`), and + /// their `toolchain-riscv32-esp` entry is a registry version, not a URL. + pub fn has_unified_toolchain(&self, is_riscv: bool) -> bool { + matches!( + self.get_toolchain_metadata_url(is_riscv), + Ok(url) if url.starts_with("https://") || url.starts_with("http://") + ) + } + /// Read and parse the `packages` section of `platform.json`. /// /// Shared between [`Self::get_package_url`] and @@ -395,4 +409,34 @@ mod tests { "wrong error: {err}" ); } + + /// pioarduino `platform-espressif32@51.03.04` (arduino-esp32 3.0) predates + /// the unified toolchain: per-MCU registry packages, and a + /// `toolchain-riscv32-esp` entry that is a registry version, not a URL. + const PIOARDUINO_51_03_04_PACKAGES_FRAGMENT: &str = r#"{ + "packages": { + "toolchain-xtensa-esp32s3": { "type": "toolchain", "owner": "espressif", "version": "12.2.0+20230208" }, + "toolchain-riscv32-esp": { "type": "toolchain", "owner": "espressif", "version": "12.2.0+20230208" } + } + }"#; + + #[test] + fn test_has_unified_toolchain_for_pioarduino_54_03_20() { + let tmp = tempfile::TempDir::new().unwrap(); + write_platform_json(tmp.path(), PIOARDUINO_54_03_20_PACKAGES_FRAGMENT); + let p = platform_with_install_dir(tmp.path()); + assert!(p.has_unified_toolchain(false)); + assert!(p.has_unified_toolchain(true)); + } + + #[test] + fn test_no_unified_toolchain_for_legacy_pioarduino_51_03_04() { + let tmp = tempfile::TempDir::new().unwrap(); + write_platform_json(tmp.path(), PIOARDUINO_51_03_04_PACKAGES_FRAGMENT); + let p = platform_with_install_dir(tmp.path()); + // Xtensa: no `toolchain-xtensa-esp-elf` entry at all. + assert!(!p.has_unified_toolchain(false)); + // RISC-V: the entry exists but names a registry version, not a URL. + assert!(!p.has_unified_toolchain(true)); + } }