From 397ae10c555d0de6056e668bd881274cb0b481bf Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 26 Aug 2026 09:07:38 -0500 Subject: [PATCH 1/2] Demote invalid-pch on PlatformIO too, pin the compile locale, document the .sum invariant --- esphome/build_gen/arduino8266.py | 3 +++ esphome/platformio/pch.py.script | 8 +++++++- tests/unit_tests/test_platformio_pch_script.py | 7 ++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/esphome/build_gen/arduino8266.py b/esphome/build_gen/arduino8266.py index 9b78d4f659..5f377ab2c2 100644 --- a/esphome/build_gen/arduino8266.py +++ b/esphome/build_gen/arduino8266.py @@ -1271,6 +1271,9 @@ def write_project(paths: InstalledPaths, ccache: str | None) -> bool: ) write_file_if_changed(pch_header, pch_text) if checksum is not None: + # Valid only for a ninja run started by write_project: a + # direct ninja invocation can rebuild the .gch via its + # depfile while this generate-time .sum lags behind write_file_if_changed( build_dir / f"{PCH_HEADER_NAME}.gch.sum", checksum + "\n" ) diff --git a/esphome/platformio/pch.py.script b/esphome/platformio/pch.py.script index cf15e7622f..13efa3ff21 100644 --- a/esphome/platformio/pch.py.script +++ b/esphome/platformio/pch.py.script @@ -95,6 +95,8 @@ def _compile_gch(cxx, flags, header: Path, gch: Path, proj_dir: Path): result = subprocess.run( # noqa: PLW1510 [cxx, "-x", "c++-header", *flags, "-c", str(header), "-o", str(gch)], cwd=proj_dir, + # C locale keeps diagnostics matchable by _TRANSIENT_ERRORS + env={**os.environ, "LC_ALL": "C"}, capture_output=True, text=True, ) @@ -318,7 +320,11 @@ def _setup_pch() -> None: # entries: GCC only uses a .gch while no other tokens have been seen. # The relative name also reaches "pio run -t idedata" output; external # consumers replaying cxx_flags must run from the project dir. - projenv.Prepend(CXXFLAGS=["-Winvalid-pch", "-include", header.name]) # noqa: F821 + projenv.Prepend( # noqa: F821 + # -Wno-error: the probe is per-process, so a later cc1plus can still + # reject the .gch; that must stay a warning under user -Werror + CXXFLAGS=["-Winvalid-pch", "-Wno-error=invalid-pch", "-include", header.name] + ) print("ESPHome: Compiling with precompiled header") diff --git a/tests/unit_tests/test_platformio_pch_script.py b/tests/unit_tests/test_platformio_pch_script.py index 1d302c4cbf..54c0ee5972 100644 --- a/tests/unit_tests/test_platformio_pch_script.py +++ b/tests/unit_tests/test_platformio_pch_script.py @@ -156,7 +156,12 @@ def test_pch_script_builds_and_prepends_relative_include(tmp_path: Path) -> None assert (proj / "esphome_pch.h.gch").is_file() assert len((proj / "esphome_pch.h.gch.sum").read_text().strip()) == 64 # Relative include: an absolute path would poison ccache keys - assert scons_env.prepended == ["-Winvalid-pch", "-include", "esphome_pch.h"] + assert scons_env.prepended == [ + "-Winvalid-pch", + "-Wno-error=invalid-pch", + "-include", + "esphome_pch.h", + ] # In production projenv["ENV"] aliases os.environ; only the -include # flags are genuinely scoped to projenv (src compiles) assert scons_env["ENV"]["CCACHE_SLOPPINESS"] == "pch_defines,time_macros" From 3d35a111ac8ad6ba5903ea5d891ffdbfe008d134 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 26 Aug 2026 09:08:02 -0500 Subject: [PATCH 2/2] Pin the compile locale in the generic pch flow too --- esphome/build_helpers/pch.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esphome/build_helpers/pch.py b/esphome/build_helpers/pch.py index aff553a087..f6f03c4458 100644 --- a/esphome/build_helpers/pch.py +++ b/esphome/build_helpers/pch.py @@ -354,7 +354,14 @@ def prepare_pch( _log_pch_in_use() try: result = subprocess.run( - cmd, cwd=cmd_dir, capture_output=True, text=True, check=False, timeout=300 + cmd, + cwd=cmd_dir, + # C locale keeps diagnostics matchable by _TRANSIENT_ERRORS + env={**os.environ, "LC_ALL": "C"}, + capture_output=True, + text=True, + check=False, + timeout=300, ) error = None if result.returncode < 0: