From 035555b24cf3e446a1851c43bada44625d4541c3 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 23 Aug 2026 12:15:04 -0500 Subject: [PATCH] Drop an empty glued flag argument at the lex funnel shlex.split('-D ""') left a bare -D that gcc resolves by eating the next flag; the funnel now warns and drops it for every consumer, which also retires raise_on_empty_arg_flags, whose staged ESP8266 caller no longer has anything to catch. --- esphome/platformio/library.py | 30 ++++++++------------- tests/unit_tests/test_platformio_library.py | 11 ++++---- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/esphome/platformio/library.py b/esphome/platformio/library.py index f3622ffc52..9a1dcda6ea 100644 --- a/esphome/platformio/library.py +++ b/esphome/platformio/library.py @@ -649,27 +649,14 @@ def lex_build_flags(entries: str | list[str], owner: str) -> list[str]: BARE_ARG_FLAGS = frozenset({"-I", "-L", "-l", "-D"}) -def raise_on_empty_arg_flags(tokens: list[str], owner: str) -> None: - """Reject bare ``-I``/``-D``/``-L``/``-l`` tokens left by an empty glued - argument (``-D ""``). - - Consumed by the ESP8266 native build generator (later in this chain) - for user build_flags; library manifests deliberately stay warn-and-drop. - - Lives next to ``join_flag_args`` because the bare token is its - postcondition: a trailing bare flag is warned and dropped there, so a - surviving one always means an empty argument. gcc would eat the next - flag as the argument (or add the CWD for ``-L``); always a typo. - """ - if empty := sorted({tok for tok in tokens if tok in BARE_ARG_FLAGS}): - raise EsphomeError( - f"{owner} contain empty-argument flag(s): {', '.join(empty)}" - ) - - def join_flag_args(tokens: Iterable[str], owner: str) -> list[str]: """Join a bare ``-I``/``-L``/``-l``/``-D`` with its following token, - the way PlatformIO's ParseFlags lexes them.""" + the way PlatformIO's ParseFlags lexes them. + + A trailing or empty argument (``-D ""``) is warned and dropped: the + bare flag would make gcc eat the next flag as its argument (or add + the CWD for ``-L``); always a typo. + """ out: list[str] = [] it = iter(tokens) for tok in it: @@ -678,6 +665,11 @@ def join_flag_args(tokens: Iterable[str], owner: str) -> list[str]: if arg is None: _LOGGER.warning("Ignoring trailing '%s' in %s build flags", tok, owner) break + if not arg: + _LOGGER.warning( + "Ignoring '%s' with empty argument in %s build flags", tok, owner + ) + continue tok += arg out.append(tok) return out diff --git a/tests/unit_tests/test_platformio_library.py b/tests/unit_tests/test_platformio_library.py index 0abba6d71c..b2374a74d7 100644 --- a/tests/unit_tests/test_platformio_library.py +++ b/tests/unit_tests/test_platformio_library.py @@ -689,11 +689,12 @@ def test_prefetch_wave_unknown_size_falls_back_to_sequential( assert "No Content-Length for https://x/b.tar.gz" in caplog.text -def test_raise_on_empty_arg_flags() -> None: - """A surviving bare flag means an empty glued argument; reject by name.""" - with pytest.raises(EsphomeError, match=r"build_flags contain empty-argument"): - lib.raise_on_empty_arg_flags(["-DFOO", "-D", "-l"], "build_flags") - lib.raise_on_empty_arg_flags(["-DFOO", "-Iinc"], "build_flags") +def test_join_flag_args_empty_argument_warns_and_drops( + caplog: pytest.LogCaptureFixture, +) -> None: + """An empty glued argument is dropped: a bare -D would eat the next flag.""" + assert lib.lex_build_flags('-D "" -DFOO', "build_flags") == ["-DFOO"] + assert "Ignoring '-D' with empty argument in build_flags" in caplog.text def test_content_lengths_head_requests(monkeypatch: pytest.MonkeyPatch) -> None: