Compare commits

...
3 changed files with 64 additions and 61 deletions
+15 -33
View File
@@ -27,7 +27,6 @@ import sys
from typing import TYPE_CHECKING, NamedTuple
from esphome.arduino8266.framework import toolchain_tool
from esphome.build_helpers.ccache import effective_ccache_basedir
from esphome.build_helpers.idedata import is_joined_include
from esphome.build_helpers.ninja import (
escape as _e,
@@ -37,13 +36,14 @@ from esphome.build_helpers.ninja import (
from esphome.build_helpers.pch import (
PCH_CORE_HEADER,
PCH_HEADER_NAME,
log_pch_in_use,
mark_pch_emitted,
pch_checksum,
pch_consumer_escalation,
pch_degraded,
pch_disabled_degraded,
pch_enabled,
pch_header_text,
pch_identity,
pch_probe_args,
pch_strict,
)
@@ -1243,39 +1243,21 @@ def write_project(paths: InstalledPaths, ccache: str | None) -> bool:
pch_header = build_dir / PCH_HEADER_NAME
pch_includes = (*src_includes, PCH_CORE_HEADER)
pch_text = pch_header_text(pch_includes)
# The .sum exists only for CCACHE_PCH_EXTSUM; ninja's depfile
# handles staleness
identity_ok = True
checksum = None
try:
if ccache:
# The .sum exists only for CCACHE_PCH_EXTSUM; ninja's depfile
# handles staleness. Strip resolved and raw build paths
# (symlinks) so identical configs share cache entries
flags_id = (
" ".join(cxxflags)
.replace(effective_ccache_basedir(), "")
.replace(str(CORE.build_path), "")
)
# The header text covers include order
checksum = pch_checksum(
src_dir,
pch_includes,
(
pch_text,
str(paths.framework),
str(paths.toolchain),
flags_id,
),
)
except (OSError, UnicodeError) as err:
# Identity unknown: a stale cache entry must never be served
_LOGGER.warning(
"Could not establish the pch identity; compiling without it: %s", err
)
pch_degraded(f"identity unknown: {err}")
else:
_LOGGER.info(
"Compiling with a precompiled header "
"(set ESPHOME_PCH_ENABLE=0 to disable)"
if ccache:
checksum = pch_identity(
cxxflags,
src_dir,
pch_includes,
(str(paths.framework), str(paths.toolchain)),
)
# Identity unknown: pch_identity warned and degraded
identity_ok = checksum is not None
if identity_ok:
log_pch_in_use()
write_file_if_changed(pch_header, pch_text)
sum_path = build_dir / f"{PCH_HEADER_NAME}.gch.sum"
if checksum is not None:
+48 -27
View File
@@ -389,7 +389,50 @@ def pch_compile_command(
return [*args, "-x", "c++-header", "-c", str(header), "-o", str(gch)], cmd_dir
def _log_pch_in_use() -> None:
def _flags_identity(tokens: Iterable[str]) -> str:
"""Flag string normalized for digest use: strip like ccache's rewriting
(user CCACHE_BASEDIR wins); the raw build path covers unresolved
(symlinked) spellings."""
from esphome.core import CORE
return (
" ".join(tokens)
.replace(effective_ccache_basedir(), "")
.replace(str(CORE.build_path), "")
)
def pch_identity(
tokens: Iterable[str],
src_dir: Path,
include_headers: tuple[str, ...],
extra: Iterable[str],
) -> str | None:
"""The .sum digest naming this exact pch build: include closure, header
text, backend identity strings, and the normalized compile command or
flags (``tokens``). None (warned and degraded) when the identity
cannot be established."""
try:
return pch_checksum(
src_dir,
include_headers,
(
# The closure is sorted, so root order only enters via the text
pch_header_text(include_headers),
*extra,
_flags_identity(tokens),
),
)
except (OSError, UnicodeError) as err:
# Identity unknown: a stale cache entry must never be served
_LOGGER.warning(
"Could not establish the pch identity; compiling without it: %s", err
)
pch_degraded(f"identity unknown: {err}")
return None
def log_pch_in_use() -> None:
# The only place a user can discover the knob; emitted only once a
# .gch is actually fresh or being built
_LOGGER.info(
@@ -459,31 +502,9 @@ def prepare_pch(
pch_degraded("no usable compile command")
return
cmd, cmd_dir = cmd_and_dir
# Strip like ccache's rewriting (user CCACHE_BASEDIR wins); the raw
# build path covers unresolved (symlinked) spellings
cmd_id = (
" ".join(cmd)
.replace(effective_ccache_basedir(), "")
.replace(str(CORE.build_path), "")
)
try:
checksum = pch_checksum(
CORE.relative_src_path(),
include_headers,
(
# The closure is sorted, so root order only enters via the text
pch_header_text(include_headers),
*extra,
cmd_id,
),
)
except (OSError, UnicodeError) as err:
# Identity unknown: a stale cache entry must never be served
_LOGGER.warning(
"Could not establish the pch identity; compiling without it: %s", err
)
checksum = pch_identity(cmd, CORE.relative_src_path(), include_headers, extra)
if checksum is None:
discard_pch(build_dir)
pch_degraded(f"identity unknown: {err}")
return
failed_marker = Path(f"{gch}.failed")
@@ -566,7 +587,7 @@ def prepare_pch(
_fail(error, "probe cannot run at all", latch=latch)
if gch.is_file() and _read_stamp(sum_path) == checksum:
_log_pch_in_use()
log_pch_in_use()
if pch_strict():
# Rejection is per-process, so a cached .gch must re-prove
# loadability for the strict gate (CI-only cost); no latch,
@@ -580,7 +601,7 @@ def prepare_pch(
)
pch_degraded("earlier failure latched")
return
_log_pch_in_use()
log_pch_in_use()
result = _run(cmd, "compile")
if result is None:
return
@@ -410,7 +410,7 @@ def test_write_project_pch_identity_unknown_skips_pch(
paths = _make_framework(tmp_path)
_set_flags("-DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH")
with patch(
"esphome.build_gen.arduino8266.pch_checksum",
"esphome.build_helpers.pch.pch_checksum",
side_effect=OSError("stat failed"),
):
content = _write_ninja(paths, ccache="/usr/bin/ccache")