diff --git a/esphome/build_gen/arduino8266.py b/esphome/build_gen/arduino8266.py index bc8c1de3fa..fca0afd809 100644 --- a/esphome/build_gen/arduino8266.py +++ b/esphome/build_gen/arduino8266.py @@ -446,6 +446,22 @@ def _resolve_build_config(defines: dict[str, str]) -> _BuildConfig: _INCOMPLETE_INSTALL = "Arduino toolchain install is incomplete" +def _filter_link_flags(unflags: set[str]) -> list[str]: + """_LINKFLAGS minus ``unflags``, pair-aware: unflagging a symbol also + drops the ``-u`` that carried it, so no dangling operand-less flag + reaches ld as the next token's consumer.""" + out: list[str] = [] + it = iter(_LINKFLAGS) + for tok in it: + if tok == "-u": + symbol = next(it) + if symbol not in unflags: + out += [tok, symbol] + elif tok not in unflags: + out.append(tok) + return out + + def _active_flash_ld_name(flash_ld_name: str) -> str: """The flash linker-script filename the link uses (testing mode renames the surgically patched copy).""" @@ -979,10 +995,10 @@ def write_project(paths: InstalledPaths, ccache: str | None) -> bool: f"build_unflags cannot remove plain linker flag(s) " f"{', '.join(plain)}; unflag the full -Wl, form or the symbol" ) - cflags, cxxflags, asflags, link_flags = ( - [f for f in flags if f not in unflags] - for flags in (cflags, cxxflags, asflags, _LINKFLAGS) + cflags, cxxflags, asflags = ( + [f for f in flags if f not in unflags] for flags in (cflags, cxxflags, asflags) ) + link_flags = _filter_link_flags(unflags) if esp8266_data[KEY_SCANF_FLOAT]: link_flags += ["-u", "_scanf_float"] link_flags += project_link_flags diff --git a/esphome/components/esp8266/const.py b/esphome/components/esp8266/const.py index a724c88ec8..82f7cfbb76 100644 --- a/esphome/components/esp8266/const.py +++ b/esphome/components/esp8266/const.py @@ -16,7 +16,6 @@ KEY_WAVEFORM_REQUIRED = "waveform_required" KEY_SERIAL_REQUIRED = "serial_required" KEY_SERIAL1_REQUIRED = "serial1_required" # Set for the native (non-PlatformIO) toolchain's build generator -KEY_FLASH_MODE = "flash_mode" KEY_SCANF_FLOAT = "scanf_float" # esp8266 namespace is already defined by arduino, manually prefix esphome diff --git a/tests/unit_tests/build_gen/test_arduino8266.py b/tests/unit_tests/build_gen/test_arduino8266.py index 4a75b42fd6..43165f74ce 100644 --- a/tests/unit_tests/build_gen/test_arduino8266.py +++ b/tests/unit_tests/build_gen/test_arduino8266.py @@ -688,6 +688,23 @@ def test_build_config_nonosdk_precedence() -> None: assert _resolve_build_config(_defines()).nonosdk == "NONOSDK221" +def test_write_project_unflagged_symbol_takes_its_dash_u(tmp_path: Path) -> None: + """Unflagging a -u symbol drops the -u that carried it; a dangling -u + would consume the next token and hand ld a symbol as an input file.""" + paths = _make_framework(tmp_path) + _set_flags() + CORE.build_unflags = {"_printf_float"} + content = _write_ninja(paths) + link_line = next( + line for line in content.splitlines() if line.startswith("linkflags = ") + ) + assert "_printf_float" not in link_line + assert "-u -u" not in link_line + # The neighbors survive as intact pairs + assert "-u app_entry" in link_line + assert "-u _DebugExceptionVector" in link_line + + def test_write_project_build_unflags_apply_to_framework_flags(tmp_path: Path) -> None: """build_unflags removes flags from the framework sets, as PlatformIO does.""" paths = _make_framework(tmp_path)