diff --git a/esphome/platformio/library.py b/esphome/platformio/library.py index 03ea7bee53..22da9cfc87 100644 --- a/esphome/platformio/library.py +++ b/esphome/platformio/library.py @@ -49,7 +49,10 @@ DEFAULT_BUILD_SRC_DIRS = "src" DEFAULT_BUILD_INCLUDE_DIR = "include" DEFAULT_BUILD_FLAGS = [] # Source suffix -> compiler kind, PlatformIO's CSUFFIXES/CXXSUFFIXES/ASSUFFIXES -# split. Native build generators map the kind to their compile rules. +# split. Native build generators map the kind to their compile rules. "asm" +# deliberately merges SCons's AS (.s/.asm) and ASPP (.S/.spp/.sx) sets: the +# ninja rules compile all of them as assembler-with-cpp, whose asm-mode +# preprocessor passes non-directive text through unchanged. SOURCE_KIND_FOR_SUFFIX: dict[str, str] = { ".c": "c", ".cpp": "cxx", @@ -572,14 +575,14 @@ def lex_build_flags(entries: str | list[str], owner: str) -> list[str]: PlatformIO's ParseFlags does, and bare ``-I``/``-L``/``-l``/``-D`` tokens re-glue to their argument across the whole stream. """ - return join_flag_args( - ( - token - for entry in ensure_list(entries) - for token in split_flag_entry(entry, owner) - ), - owner, - ) + # Join per entry, as SCons's ParseFlags lexes each string independently: + # a dangling -I ending one entry must warn, not absorb the next entry's + # first token. + return [ + token + for entry in ensure_list(entries) + for token in join_flag_args(split_flag_entry(entry, owner), owner) + ] # Flags whose argument may follow as a separate token; ParseFlags glues them diff --git a/tests/unit_tests/build_helpers/test_idedata.py b/tests/unit_tests/build_helpers/test_idedata.py index 4e403b04a6..26fc1d1ee7 100644 --- a/tests/unit_tests/build_helpers/test_idedata.py +++ b/tests/unit_tests/build_helpers/test_idedata.py @@ -3,6 +3,7 @@ # pylint: disable=protected-access import json +import logging import os from pathlib import Path from unittest.mock import MagicMock, patch @@ -290,9 +291,10 @@ def test_parse_entry_recovers_from_unconfigured_launcher( f"{ABS}build/src/esphome/core/application.cpp", "/opt/homebrew/bin/ccache /tools/xtensa-lx106-elf-g++ -c a.cpp -o a.o", ) + caplog.set_level(logging.DEBUG) cxx_path, _, _, _ = idedata.parse_entry(entry) assert cxx_path == "/tools/xtensa-lx106-elf-g++" - assert "WARNING" not in caplog.text + assert "Stripping unconfigured launcher" in caplog.text def test_parse_entry_keeps_launcher_without_program() -> None: diff --git a/tests/unit_tests/test_espidf_component.py b/tests/unit_tests/test_espidf_component.py index f873e35953..1baa22777c 100644 --- a/tests/unit_tests/test_espidf_component.py +++ b/tests/unit_tests/test_espidf_component.py @@ -1084,3 +1084,18 @@ def test_emit_idf_component_wires_esp32_target(tmp_path, monkeypatch): c.data = {"build": {"extraScript": "extra.py"}} _emit_idf_component(c) assert c.data["build"]["flags"] == ["-lesp32"] + + +def test_build_flags_dangling_flag_does_not_cross_entries( + tmp_path, caplog: pytest.LogCaptureFixture +) -> None: + """Each entry is lexed independently, as ParseFlags does: a dangling -I ending one + entry warns instead of absorbing the next entry's first token.""" + (tmp_path / "src").mkdir() + c = IDFComponent("owner/name", "1.0", source=URLSource("http://dummy")) + c.path = tmp_path + c.data = {"build": {"flags": ["-Wall -I", "-DFOO=1"]}} + content = generate_cmakelists_txt(c) + assert "FOO=1" in content + assert "-I-DFOO" not in content + assert "Ignoring trailing '-I'" in caplog.text diff --git a/tests/unit_tests/test_platformio_library.py b/tests/unit_tests/test_platformio_library.py index f1a55dca60..c8f994bb04 100644 --- a/tests/unit_tests/test_platformio_library.py +++ b/tests/unit_tests/test_platformio_library.py @@ -553,3 +553,14 @@ def test_join_flag_args_trailing_bare_flag_warns( assert join_flag_args(["-Os", "-l"], "library x") == ["-Os"] assert "Ignoring trailing '-l'" in caplog.text + + +def test_lex_build_flags_dangling_flag_does_not_cross_entries( + caplog: pytest.LogCaptureFixture, +) -> None: + """Each entry is lexed independently, as ParseFlags does: a dangling -I + ending one entry warns instead of absorbing the next entry's first token.""" + from esphome.platformio.library import lex_build_flags + + assert lex_build_flags(["-Wall -I", "-DFOO=1"], "lib x") == ["-Wall", "-DFOO=1"] + assert "Ignoring trailing '-I'" in caplog.text