Merge branch 'esp32-idf-pch' into platformio-pch-rp2

This commit is contained in:
J. Nick Koston
2026-08-26 10:03:31 -05:00
7 changed files with 58 additions and 5 deletions
+2
View File
@@ -36,6 +36,7 @@ from esphome.build_helpers.ninja import (
from esphome.build_helpers.pch import (
PCH_CORE_HEADER,
PCH_HEADER_NAME,
mark_pch_emitted,
pch_checksum,
pch_enabled,
pch_header_text,
@@ -1284,6 +1285,7 @@ def write_project(paths: InstalledPaths, ccache: str | None) -> bool:
]
lines.append(f"srccxxflags = {' '.join(cxx_parts)}")
src_cxx_override = ("$srccxxflags", gch)
mark_pch_emitted()
src_objs = _ninja_compile_edges(
lines,
_collect_sources(src_dir),
+3
View File
@@ -8,6 +8,7 @@ from esphome.build_helpers import pch
from esphome.build_helpers.pch import (
PCH_DEFAULT_HEADERS,
PCH_HEADER_NAME,
mark_pch_emitted,
pch_enabled,
pch_header_text,
)
@@ -371,6 +372,8 @@ def write_project(
CORE.relative_build_path("build", PCH_HEADER_NAME),
pch_header_text(PCH_DEFAULT_HEADERS),
)
# Consumers carry the -include; gate the ccache relaxation on it
mark_pch_emitted()
# Snapshot the exclusion set so has_outdated_files() can trigger a
# discovery reconfigure when it changes. Excluded components never
+26 -3
View File
@@ -8,6 +8,7 @@ it too; Arduino.h visibility there is intended (esphome#8693).
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
import hashlib
import json
import logging
@@ -25,6 +26,27 @@ from esphome.build_helpers.idedata import (
split_command,
)
_DOMAIN = "pch"
@dataclass
class _PCHData:
emitted: bool = False
def _pch_data() -> _PCHData:
from esphome.core import CORE
if _DOMAIN not in CORE.data:
CORE.data[_DOMAIN] = _PCHData()
return CORE.data[_DOMAIN]
def mark_pch_emitted() -> None:
"""Record that this build's consumers reference the pch."""
_pch_data().emitted = True
_LOGGER = logging.getLogger(__name__)
# The header and its .gch/.sum sidecars live in the build directory.
@@ -81,9 +103,10 @@ def pch_enabled() -> bool:
def ccache_pch_env() -> dict[str, str]:
"""Settings ccache needs to cache compiles that consume the .gch;
empty when the pch is disabled. User-set values win. Native backends
export these process-wide; only time_macros affects non-pch TUs."""
if not pch_enabled():
empty unless this build actually emitted one. User-set values win.
Native backends export these process-wide; only time_macros affects
non-pch TUs."""
if not (pch_enabled() and _pch_data().emitted):
return {}
env = {k: v for k, v in _CCACHE_PCH_ENV.items() if k not in os.environ}
user_sloppiness = os.environ.get("CCACHE_SLOPPINESS")
@@ -386,6 +386,10 @@ def test_write_project_pch(tmp_path: Path) -> None:
'#include "esphome/core/defines.h"',
]
assert (build_dir / "esphome_pch.h.gch.sum").read_text().strip()
# Emission recorded so the framework env exports the ccache settings
from esphome.build_helpers.pch import ccache_pch_env
assert "CCACHE_PCH_EXTSUM" in ccache_pch_env()
def test_write_project_pch_sum_only_with_ccache(tmp_path: Path) -> None:
@@ -456,6 +460,10 @@ def test_write_project_pch_skipped_when_user_force_include_precedes(
assert "esphome_pch" not in content
assert "srccxxflags" not in content
assert "prevents the precompiled header" in caplog.text
# No pch emitted: the ccache relaxation must stay off
from esphome.build_helpers.pch import ccache_pch_env
assert ccache_pch_env() == {}
def test_write_project_pch_disabled(
@@ -33,7 +33,14 @@ def test_pch_enabled(value: str | None, expected: bool) -> None:
assert pch.pch_enabled() is expected
def test_ccache_pch_env_empty_until_emitted() -> None:
"""No sloppiness relaxation for a build that skipped the pch."""
with patch.dict(os.environ, {}, clear=True):
assert pch.ccache_pch_env() == {}
def test_ccache_pch_env_enabled() -> None:
pch.mark_pch_emitted()
with patch.dict(os.environ, {}, clear=True):
env = pch.ccache_pch_env()
assert env["CCACHE_SLOPPINESS"] == "pch_defines,time_macros"
@@ -48,6 +55,7 @@ def test_ccache_pch_env_disabled() -> None:
def test_ccache_pch_env_token_check_is_membership_not_substring(
caplog: pytest.LogCaptureFixture,
) -> None:
pch.mark_pch_emitted()
"""A token merely containing ours must not suppress the union."""
with patch.dict(os.environ, {"CCACHE_SLOPPINESS": "pch_defines_extra"}, clear=True):
env = pch.ccache_pch_env()
@@ -57,6 +65,7 @@ def test_ccache_pch_env_token_check_is_membership_not_substring(
def test_ccache_pch_env_unions_user_sloppiness(
caplog: pytest.LogCaptureFixture,
) -> None:
pch.mark_pch_emitted()
"""Without pch_defines/time_macros ccache declines every pch-consuming
compile, so missing tokens are unioned onto the user's value."""
with patch.dict(os.environ, {"CCACHE_SLOPPINESS": "locale"}, clear=True):
@@ -9,6 +9,7 @@ from unittest.mock import MagicMock, patch
import pytest
from esphome.arduino8266 import framework, toolchain
from esphome.build_helpers.pch import mark_pch_emitted
import esphome.config_validation as cv
from esphome.const import (
CONF_COMPILE_PROCESS_LIMIT,
@@ -632,6 +633,7 @@ def test_get_idedata_accepts_preresolved_ccache() -> None:
def test_ccache_env_includes_pch_settings() -> None:
mark_pch_emitted()
"""The native build exports the ccache settings the pch needs."""
with patch.dict(os.environ, {}, clear=True):
env = framework.ccache_env("/usr/bin/ccache")
@@ -647,6 +649,7 @@ def test_ccache_env_pch_disabled() -> None:
def test_ccache_env_respects_user_sloppiness() -> None:
mark_pch_emitted()
with patch.dict(os.environ, {"CCACHE_SLOPPINESS": "locale"}, clear=True):
env = framework.ccache_env("/usr/bin/ccache")
# The user's tokens survive; the ones the pch needs are unioned on
+7 -2
View File
@@ -19,6 +19,7 @@ from unittest.mock import MagicMock, patch
import pytest
from esphome.build_helpers.pch import _PCHData
from esphome.espidf.framework import (
ESPHOME_STAMP_FILE,
STAMP_SCHEMA_VERSION,
@@ -1565,10 +1566,14 @@ def _ccache_patches(tmp_path: Path, which: str | None, build_path: Path | None):
"esphome.espidf.framework.get_idf_tools_path",
return_value=tmp_path / "tools",
),
# ccache_defaults_env (build_helpers.ccache) reads CORE at call time
# ccache_defaults_env and the pch emission flag read CORE at call time
patch(
"esphome.core.CORE",
SimpleNamespace(build_path=build_path),
SimpleNamespace(
build_path=build_path,
# Pre-marked: these env tests model a pch-emitting build
data={"pch": _PCHData(emitted=True)},
),
),
)