Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ targets = [
]

[workspace.package]
version = "2.5.36"
version = "2.5.37"
edition = "2021"
rust-version = "1.95.0"
license = "AGPL-3.0-only"
Expand Down
26 changes: 22 additions & 4 deletions crates/fbuild-build-esp/src/esp32/fixups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@ type Rule = fn(Esp32McuConfig) -> Esp32McuConfig;

/// Adapt the recipe to the selected toolchain package.
///
/// - GCC < 14: the paired SDKs (pioarduino 51.x/53.x, Arduino 3.0/3.1) do not
/// define `__dso_handle` for `-fuse-cxa-atexit`-generated references, on
/// Xtensa and RISC-V alike.
/// - GCC < 14, and ESP32-C2/H2 with GCC 14: the paired SDKs do not define
/// `__dso_handle` for `-fuse-cxa-atexit`-generated references.
/// - Per-MCU Xtensa packages (GCC 8 and 12) reject the atomics switch.
/// - Per-MCU Xtensa GCC 8 (official `espressif32` 6.x/7.x) needs its own
/// linker recipe, older language standards, and no LTO.
pub fn for_toolchain(config: Esp32McuConfig, toolchain: &PackageInfo) -> Esp32McuConfig {
let legacy_xtensa32 = toolchain.name == "toolchain-xtensa32";
let per_mcu_xtensa = is_per_mcu_xtensa(&toolchain.name) || legacy_xtensa32;
let config = apply_if(is_before_gcc14(&toolchain.version), config, drop_cxa_atexit);
let lacks_dso_handle = matches!(config.mcu.as_str(), "esp32c2" | "esp32h2");
let config = apply_if(
is_before_gcc14(&toolchain.version) || lacks_dso_handle,
config,
drop_cxa_atexit,
);
let config = apply_if(per_mcu_xtensa, config, drop_hardware_atomics);
let config = apply_if(
(per_mcu_xtensa && is_gcc8(&toolchain.version)) || legacy_xtensa32,
Expand Down Expand Up @@ -255,6 +259,20 @@ mod tests {
}
}

#[test]
fn esp32c2_h2_sdk_drops_cxa_atexit_on_gcc14() {
for mcu in ["esp32c2", "esp32h2"] {
let config = for_toolchain(
get_mcu_config(mcu).unwrap(),
&toolchain("toolchain-riscv32-esp", "14.2.0+20241119"),
);
assert!(
!has(&config.compiler_flags.cxx, "-fuse-cxa-atexit"),
"{mcu} kept -fuse-cxa-atexit without __dso_handle"
);
}
}

#[test]
fn unparseable_version_keeps_recipe() {
// pioarduino 55.x URL toolchain: version is not a bare GCC version.
Expand Down
70 changes: 70 additions & 0 deletions crates/fbuild-library/src/library/esp32_framework/libs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,29 @@ fn merge_skeleton_mcu_entries(
}

fn patch_mcu_compatibility(mcu_dir: &Path, mcu: &str) -> fbuild_core::Result<()> {
if matches!(mcu, "esp32c2" | "esp32h2") {
// The packaged esp_bt.h uses ../../../../controller/<mcu>/esp_bt_cfg.h,
// which resolves to include/controller/. The package places the
// actual header under include/bt/controller/ instead.
let source = mcu_dir
.join("include")
.join("bt")
.join("controller")
.join(mcu)
.join("esp_bt_cfg.h");
let expected = mcu_dir
.join("include")
.join("controller")
.join(mcu)
.join("esp_bt_cfg.h");
if source.exists() && !expected.exists() {
if let Some(parent) = expected.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::copy(source, expected)?;
}
}

if mcu != "esp32c2" {
return Ok(());
}
Expand Down Expand Up @@ -165,6 +188,7 @@ impl Esp32Framework {
// (esp32-arduino-libs/) layouts.
for mcu_dir in mcu_sdk_dir_candidates(&tools_dir, mcu) {
if mcu_sdk_complete(&mcu_dir) {
patch_mcu_compatibility(&mcu_dir, mcu)?;
return Ok(());
}
}
Expand Down Expand Up @@ -216,6 +240,13 @@ impl Esp32Framework {

merge_sdk_archive_entries(temp_dir.path(), &tools_dir)?;

for mcu_dir in mcu_sdk_dir_candidates(&tools_dir, mcu) {
if mcu_sdk_complete(&mcu_dir) {
patch_mcu_compatibility(&mcu_dir, mcu)?;
break;
}
}

tracing::info!("ESP32 SDK libs installed");
Ok(())
}
Expand Down Expand Up @@ -413,6 +444,45 @@ mod tests {
);
}

fn assert_bluetooth_controller_header_compatible(mcu: &str) {
let tmp = tempfile::TempDir::new().unwrap();
let mcu_dir = tmp.path().join(mcu);
let source = mcu_dir
.join("include")
.join("bt")
.join("controller")
.join(mcu)
.join("esp_bt_cfg.h");
let expected = mcu_dir
.join("include")
.join("controller")
.join(mcu)
.join("esp_bt_cfg.h");
write(&source, "// packaged Bluetooth controller config\n");

patch_mcu_compatibility(&mcu_dir, mcu).unwrap();

assert_eq!(
std::fs::read_to_string(&expected).unwrap(),
"// packaged Bluetooth controller config\n"
);
patch_mcu_compatibility(&mcu_dir, mcu).unwrap();
assert_eq!(
std::fs::read_to_string(&expected).unwrap(),
std::fs::read_to_string(&source).unwrap()
);
}

#[test]
fn esp32c2_packaged_bluetooth_header_resolves_relative_include() {
assert_bluetooth_controller_header_compatible("esp32c2");
}

#[test]
fn esp32h2_packaged_bluetooth_header_resolves_relative_include() {
assert_bluetooth_controller_header_compatible("esp32h2");
}

#[test]
fn patch_mcu_compatibility_leaves_other_mcus_untouched() {
let tmp = tempfile::TempDir::new().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "fbuild"
version = "2.5.36"
version = "2.5.37"
description = "PlatformIO-compatible embedded build tool (Rust implementation)"
readme = "README.md"
license = "AGPL-3.0-only"
Expand Down
Loading