diff --git a/esphome/__main__.py b/esphome/__main__.py index f1b0fb7592..7b1afd464b 100644 --- a/esphome/__main__.py +++ b/esphome/__main__.py @@ -2753,11 +2753,11 @@ def run_esphome(argv): return 2 CORE.config = config - # Fallback for platforms whose validators didn't set the toolchain - # (only the esp32 component reads esp32.framework.toolchain). All - # other platforms only support PlatformIO today. Must run before the - # cache refresh below so its sidecar records the same toolchain a - # compile would. + # Every platform resolves the toolchain during validation now, but the + # compiled-config cache fast path skips validation entirely and a + # sidecar written before the toolchain field existed restores nothing; + # this fallback covers that path. Must run before the cache refresh + # below so its sidecar records the same toolchain a compile would. if CORE.toolchain is None: CORE.toolchain = Toolchain.PLATFORMIO diff --git a/esphome/core/config.py b/esphome/core/config.py index 12f55283ad..1c481894b2 100644 --- a/esphome/core/config.py +++ b/esphome/core/config.py @@ -560,10 +560,11 @@ async def _add_platformio_options(pio_options: dict[str, str | list[str]]) -> No if CORE.using_native_toolchain: # The native builds don't read platformio.ini; honor the options # with a native equivalent and warn about the rest, which would - # otherwise be silently ignored. __main__'s write_cpp_file and - # compile_program dispatch must agree with this gate: a toolchain - # treated as native here must not fall through to the PlatformIO - # project writer there. + # otherwise be silently ignored. Every dispatch site that tests a + # specific using_toolchain_* as a stand-in for "native" (project + # writing, compile, upload, firmware paths) must agree with this + # gate: a toolchain treated as native here must never fall through + # to a PlatformIO code path there. for key, val in pio_options.items(): vals = [val] if isinstance(val, str) else val if key == CONF_BUILD_FLAGS: @@ -576,6 +577,11 @@ async def _add_platformio_options(pio_options: dict[str, str | list[str]]) -> No ) for flag in vals: cg.add_build_flag(flag) + elif key == "build_unflags": + # Native equivalent: add_build_unflag (honored token-level by + # the arduino generator; the IDF generator warns there) + for flag in vals: + CORE.add_build_unflag(flag) elif key == "lib_deps": # Routed through the regular library mechanism so the # libraries reach the native backend's converter (IDF diff --git a/tests/unit_tests/components/esp8266/test_build_surgery.py b/tests/unit_tests/components/esp8266/test_build_surgery.py index 944518b1fa..7e6e7ea680 100644 --- a/tests/unit_tests/components/esp8266/test_build_surgery.py +++ b/tests/unit_tests/components/esp8266/test_build_surgery.py @@ -108,8 +108,8 @@ def test_board_build_covers_every_board() -> None: def test_surgery_fingerprint_covers_module_source() -> None: - """The fingerprint hashes the module source, so any surgery edit - invalidates linker-script caches stamped with it.""" + """Pins the mechanism: the fingerprint is the sha256 of the module + source (behavioral coverage follows from that, not from this test).""" import hashlib import inspect diff --git a/tests/unit_tests/core/test_config.py b/tests/unit_tests/core/test_config.py index 80e7f3b5c2..f322c1d4c9 100644 --- a/tests/unit_tests/core/test_config.py +++ b/tests/unit_tests/core/test_config.py @@ -1285,6 +1285,7 @@ async def test_add_platformio_options_native_idf( await config._add_platformio_options( { "build_flags": "-DSINGLE_FLAG", # string and list forms both valid + "build_unflags": ["-Os"], "lib_deps": ["bblanchon/ArduinoJson@7.4.2"], "lib_ignore": "libsodium", "upload_speed": "115200", @@ -1294,6 +1295,7 @@ async def test_add_platformio_options_native_idf( assert "-DSINGLE_FLAG" in CORE.build_flags assert "ArduinoJson" in CORE.platformio_libraries + assert "-Os" in CORE.build_unflags # lib_ignore is stored (listified) for generate_idf_components to read; # nothing else lands in platformio_options on the native toolchain. assert CORE.platformio_options == {"lib_ignore": ["libsodium"]}