From da046a988686b9cc08d29348992498c8f7260f5d Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 25 Aug 2026 15:16:54 -0500 Subject: [PATCH 1/2] Assert the forced-on working-ccache case emits no warning at all The 'Not decoded' escape hatch could only mask a regression; assert the captured records directly instead. --- tests/unit_tests/test_espidf_framework.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/test_espidf_framework.py b/tests/unit_tests/test_espidf_framework.py index 8ef231aa3a..45a971ca01 100644 --- a/tests/unit_tests/test_espidf_framework.py +++ b/tests/unit_tests/test_espidf_framework.py @@ -1633,7 +1633,7 @@ def test_ccache_env_opt_in_with_working_binary( ): env = _ccache_env() assert env["IDF_CCACHE_ENABLE"] == "1" - assert "ccache" not in caplog.text.lower() or "Not decoded" in caplog.text + assert not [r for r in caplog.records if r.levelno >= logging.WARNING] def test_ccache_env_opt_in_with_rejected_binary( From dc45cd4d3f713b0f29b77a38c436848467be9424 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Tue, 25 Aug 2026 15:27:53 -0500 Subject: [PATCH 2/2] Probe that the toolchain can load the gch before enabling the pch --- esphome/platformio/pch.py.script | 26 +++++++++++++++++++ .../unit_tests/test_platformio_pch_script.py | 11 ++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/esphome/platformio/pch.py.script b/esphome/platformio/pch.py.script index 03f6567285..bb7d35708b 100644 --- a/esphome/platformio/pch.py.script +++ b/esphome/platformio/pch.py.script @@ -167,6 +167,32 @@ def _setup_pch() -> None: error = result.stderr if result.returncode != 0 else None except OSError as err: error = str(err) + if error is None: + # Some toolchains build a .gch they cannot load back (GCC 10 on + # macOS arm64 rejects it per-process: "had text segment at + # different address"); probe once so consumers never pay for a + # pch that every compile would silently reject + probe = subprocess.run( # noqa: PLW1510 + [ + cxx, + *flags, + "-MF", + os.devnull, + "-Winvalid-pch", + "-include", + str(header), + "-fsyntax-only", + "-x", + "c++", + "-", + ], + cwd=proj_dir, + input="", + capture_output=True, + text=True, + ) + if ".gch" in probe.stderr: + error = f"toolchain cannot load the pch: {probe.stderr.strip()}" if error is not None: print("ESPHome: precompiled header failed; compiling without it") print(error) diff --git a/tests/unit_tests/test_platformio_pch_script.py b/tests/unit_tests/test_platformio_pch_script.py index 2ff162b565..ae3ad91099 100644 --- a/tests/unit_tests/test_platformio_pch_script.py +++ b/tests/unit_tests/test_platformio_pch_script.py @@ -56,7 +56,7 @@ class _FakeSConsEnv(dict): def _fake_cxx(tmp_path: Path, fail: bool = False) -> Path: """A compiler stand-in that records its argv and writes the -o target.""" cxx = tmp_path / "fake-gxx" - body = 'printf \'%s\\n\' "$@" >> "$0.argv"\n' + body = 'printf -- ---call---\\\\n >> "$0.argv"; printf \'%s\\n\' "$@" >> "$0.argv"\n' if fail: body += "echo boom >&2\nexit 1\n" else: @@ -109,10 +109,11 @@ def test_pch_script_preserves_spaced_flag_elements(tmp_path: Path) -> None: spaced.mkdir() flags = ['-DUSB_PRODUCT=\\"Pico 2W\\"', "-I", str(spaced), "-include", "other.h"] _run_script(tmp_path, flags=flags) - argv = (tmp_path / "fake-gxx.argv").read_text().splitlines() - assert '-DUSB_PRODUCT="Pico 2W"' in argv - assert str(spaced) in argv - assert "-include" not in argv + calls = (tmp_path / "fake-gxx.argv").read_text().split("---call---\n") + gch_call = next(c for c in calls if "c++-header" in c).splitlines() + assert '-DUSB_PRODUCT="Pico 2W"' in gch_call + assert str(spaced) in gch_call + assert "-include" not in gch_call # The stripped -include header is folded into the prefix header instead pch = (tmp_path / "dev" / "esphome_pch.h").read_text() assert pch.splitlines()[0] == '#include "other.h"'