diff --git a/esphome/platformio/extra_script.py b/esphome/platformio/extra_script.py index d1550a680b..921fe2fd5d 100644 --- a/esphome/platformio/extra_script.py +++ b/esphome/platformio/extra_script.py @@ -13,6 +13,7 @@ from dataclasses import dataclass, field import logging import os from pathlib import Path +import shlex from typing import TYPE_CHECKING from esphome.core import EsphomeError @@ -259,8 +260,13 @@ def captured_as_build_flags( except ValueError: return str(resolved) - flags.extend(f"-I{_anchored(path)}" for path in _strs(result.cpppath, "CPPPATH")) - flags.extend(f"-L{_anchored(path)}" for path in _strs(result.libpath, "LIBPATH")) + # shlex.quote so a spaced path survives lex_build_flags as one token + flags.extend( + f"-I{shlex.quote(_anchored(path))}" for path in _strs(result.cpppath, "CPPPATH") + ) + flags.extend( + f"-L{shlex.quote(_anchored(path))}" for path in _strs(result.libpath, "LIBPATH") + ) flags.extend(f"-l{lib}" for lib in _strs(result.libs, "LIBS")) for define in result.cppdefines: # SCons also accepts dict/list CPPDEFINES; formatting those blind diff --git a/tests/unit_tests/test_platformio_extra_script.py b/tests/unit_tests/test_platformio_extra_script.py index dcb181caa3..9bff5dc892 100644 --- a/tests/unit_tests/test_platformio_extra_script.py +++ b/tests/unit_tests/test_platformio_extra_script.py @@ -329,6 +329,16 @@ def test_extra_script_cpppath_captured_as_include_flags(tmp_path, monkeypatch): assert flags == ["-Iinclude", f"-I{outside.resolve()}"] +def test_extra_script_spaced_paths_survive_relexing(tmp_path): + """-I/-L paths with spaces round-trip through lex_build_flags as one token.""" + from esphome.platformio.library import lex_build_flags + + (tmp_path / "my libs").mkdir() + result = ExtraScriptResult(cpppath=["my libs"], libpath=["my libs"]) + flags = captured_as_build_flags(result, library_dir=tmp_path) + assert lex_build_flags(flags, "test") == ["-Imy libs", "-Lmy libs"] + + def test_run_extra_script_failure_discards_partial_capture(tmp_path, caplog) -> None: """A crashed script yields an empty result: half-applied flags could build wrong-output firmware that links cleanly."""