From 2d933009039610a7c29483d248c3b11a053acc75 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Thu, 20 Aug 2026 07:39:14 -0500 Subject: [PATCH] Detect knobs in multi-token build_flags entries and drop a zero flash budget with a warning --- esphome/arduino8266/toolchain.py | 5 +++++ esphome/build_gen/arduino8266.py | 10 +++++++--- tests/unit_tests/build_gen/test_arduino8266.py | 10 ++++++++++ tests/unit_tests/test_arduino8266_toolchain.py | 5 +++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/esphome/arduino8266/toolchain.py b/esphome/arduino8266/toolchain.py index 0d47063b37..2b0785ca3c 100644 --- a/esphome/arduino8266/toolchain.py +++ b/esphome/arduino8266/toolchain.py @@ -126,6 +126,11 @@ def _parse_app_size(build_dir: Path) -> int | None: app_size = segment_length(ld_text, "irom0_0_seg") if app_size is None: _LOGGER.warning("irom0_0_seg not found in %s; skipping Flash summary", ld_path) + elif app_size == 0: + _LOGGER.warning( + "irom0_0_seg has zero length in %s; skipping Flash summary", ld_path + ) + return None return app_size diff --git a/esphome/build_gen/arduino8266.py b/esphome/build_gen/arduino8266.py index 112f90172a..cbb09057f2 100644 --- a/esphome/build_gen/arduino8266.py +++ b/esphome/build_gen/arduino8266.py @@ -176,9 +176,13 @@ def _flag_defines() -> dict[str, str]: """Map define name -> full ``NAME[=VALUE]`` for every -D build flag.""" defines: dict[str, str] = {} for flag in CORE.build_flags: - if flag.startswith("-D"): - body = flag[2:] - defines[body.split("=", 1)[0]] = body + # Shell-lex multi-token entries the way PlatformIO does, so a knob + # in "-DKNOB -DOTHER" is still detected; single tokens pass verbatim + # to keep any quoting in their bodies intact. + for tok in shlex.split(flag) if " " in flag else (flag,): + if tok.startswith("-D"): + body = tok[2:] + defines[body.split("=", 1)[0]] = body return defines diff --git a/tests/unit_tests/build_gen/test_arduino8266.py b/tests/unit_tests/build_gen/test_arduino8266.py index 3ffbf7309d..b656264a01 100644 --- a/tests/unit_tests/build_gen/test_arduino8266.py +++ b/tests/unit_tests/build_gen/test_arduino8266.py @@ -597,3 +597,13 @@ def test_project_flags_lexed_entry_scatters_non_linker_tokens() -> None: assert link_flags == ["-Wl,-Map=m"] assert "stray" in compile_flags assert not libs + + +def test_flag_defines_lexes_multi_token_entries() -> None: + """A knob inside a multi-token entry is detected like PlatformIO does.""" + _set_flags("-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH -DFOO=1") + defines = _flag_defines() + assert "PIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH" in defines + assert defines["FOO"] == "FOO=1" + config = _resolve_build_config(defines) + assert config.lwip_lib == "lwip2-1460" diff --git a/tests/unit_tests/test_arduino8266_toolchain.py b/tests/unit_tests/test_arduino8266_toolchain.py index 9cf59c56e1..a0405110ce 100644 --- a/tests/unit_tests/test_arduino8266_toolchain.py +++ b/tests/unit_tests/test_arduino8266_toolchain.py @@ -139,6 +139,11 @@ def test_parse_app_size(tmp_path: Path) -> None: with patch("esphome.build_gen.arduino8266.get_flash_ld_path", return_value=ld): assert toolchain._parse_app_size(tmp_path) is None + # A zero-length segment is bad data, not a budget; warn and drop it + ld.write_text("MEMORY\n{\n irom0_0_seg : org = 0x40201010, len = 0x0\n}\n") + with patch("esphome.build_gen.arduino8266.get_flash_ld_path", return_value=ld): + assert toolchain._parse_app_size(tmp_path) is None + with patch( "esphome.build_gen.arduino8266.get_flash_ld_path", return_value=tmp_path / "missing.ld",