mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 15:27:33 +00:00
Merge branch 'esp32-idf-pch' into platformio-pch-rp2
This commit is contained in:
@@ -322,6 +322,8 @@ def prepare_pch() -> None:
|
||||
"""Build the .gch right before ninja, after every reconfigure, so the
|
||||
compile_commands.json flags and the sdkconfig are the settled ones."""
|
||||
if not pch_enabled():
|
||||
# Self-cleaning escape hatch: drop any previously built .gch
|
||||
pch.discard_pch(CORE.relative_build_path("build"))
|
||||
return
|
||||
sdkconfig_path = CORE.relative_build_path(f"sdkconfig.{CORE.name}")
|
||||
try:
|
||||
|
||||
@@ -156,9 +156,13 @@ _PCH_STRIP_FLAGS_WITH_ARG = frozenset({"-o", "-c", "-MT", "-MF", "-MQ"})
|
||||
_PCH_STRIP_FLAGS = frozenset({"-MD", "-MMD", "-MP", "-MM", "-M"})
|
||||
|
||||
|
||||
def pch_compile_command(build_dir: Path, header: Path, gch: Path) -> list[str] | None:
|
||||
"""The exact src C++ flags from compile_commands.json, retargeted at
|
||||
the header; None (logged) when no configured C++ TU is available yet."""
|
||||
def pch_compile_command(
|
||||
build_dir: Path, header: Path, gch: Path
|
||||
) -> tuple[list[str], Path] | None:
|
||||
"""The exact src C++ flags from compile_commands.json retargeted at the
|
||||
header, with the directory they resolve against (relative -I paths must
|
||||
be expanded and executed from the same root); None (logged) when no
|
||||
configured C++ TU is available yet."""
|
||||
from esphome.core import CORE
|
||||
|
||||
try:
|
||||
@@ -187,9 +191,8 @@ def pch_compile_command(build_dir: Path, header: Path, gch: Path) -> list[str] |
|
||||
if entry is None:
|
||||
_LOGGER.warning("No src C++ entry in the compile database, skipping pch")
|
||||
return None
|
||||
tokens = expand_response_files(
|
||||
split_command(entry.get("command", "")), Path(entry.get("directory", build_dir))
|
||||
)
|
||||
cmd_dir = Path(entry.get("directory", build_dir))
|
||||
tokens = expand_response_files(split_command(entry.get("command", "")), cmd_dir)
|
||||
# A DB recorded with ccache enabled prefixes the compiler with the
|
||||
# launcher; the .gch must be compiled directly
|
||||
if tokens and is_launcher(tokens[0]):
|
||||
@@ -215,7 +218,7 @@ def pch_compile_command(build_dir: Path, header: Path, gch: Path) -> list[str] |
|
||||
args.extend(("-include", inc))
|
||||
continue
|
||||
args.append(tok)
|
||||
return [*args, "-x", "c++-header", "-c", str(header), "-o", str(gch)]
|
||||
return [*args, "-x", "c++-header", "-c", str(header), "-o", str(gch)], cmd_dir
|
||||
|
||||
|
||||
def discard_pch(build_dir: Path) -> None:
|
||||
@@ -247,14 +250,18 @@ def prepare_pch(
|
||||
"""
|
||||
from esphome.core import CORE
|
||||
|
||||
_LOGGER.info(
|
||||
"Compiling with a precompiled header (set ESPHOME_PCH_ENABLE=0 to disable)"
|
||||
)
|
||||
header = build_dir / PCH_HEADER_NAME
|
||||
gch = Path(f"{header}.gch")
|
||||
sum_path = Path(f"{gch}.sum")
|
||||
cmd = pch_compile_command(build_dir, header, gch)
|
||||
if cmd is None:
|
||||
cmd_and_dir = pch_compile_command(build_dir, header, gch)
|
||||
if cmd_and_dir is None:
|
||||
# Freshness cannot be validated; a leftover .gch must not be consumed
|
||||
discard_pch(build_dir)
|
||||
return
|
||||
cmd, cmd_dir = cmd_and_dir
|
||||
# Stripped like ccache's own rewriting (a user CCACHE_BASEDIR wins) so
|
||||
# identical configs hash identically across devices; the raw build path
|
||||
# covers unresolved spellings in the compile DB
|
||||
@@ -291,7 +298,7 @@ def prepare_pch(
|
||||
return
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd, cwd=build_dir, capture_output=True, text=True, check=False, timeout=300
|
||||
cmd, cwd=cmd_dir, capture_output=True, text=True, check=False, timeout=300
|
||||
)
|
||||
error = None
|
||||
if result.returncode != 0:
|
||||
|
||||
@@ -492,6 +492,12 @@ def test_get_component_cmakelists_no_compile_features() -> None:
|
||||
assert "target_compile_features" not in content
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _pch_default_on(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""Pin the knob so a developer's ESPHOME_PCH_ENABLE=0 cannot fail these."""
|
||||
monkeypatch.setenv("ESPHOME_PCH_ENABLE", "1")
|
||||
|
||||
|
||||
def _make_pch_device(tmp_path: Path, name: str) -> Path:
|
||||
"""A device dir with the pch source headers and a stub compile_commands."""
|
||||
from esphome.build_helpers.pch import PCH_DEFAULT_HEADERS
|
||||
@@ -634,7 +640,8 @@ def test_pch_compile_command_variants(tmp_path: Path) -> None:
|
||||
)
|
||||
)
|
||||
# Launcher stripped; -include/-o/-c and depfile flags removed
|
||||
assert pch_compile_command(build, header, gch) == [
|
||||
cmd, cmd_dir = pch_compile_command(build, header, gch)
|
||||
assert cmd == [
|
||||
"g++",
|
||||
"-DX=1",
|
||||
"-x",
|
||||
@@ -644,6 +651,8 @@ def test_pch_compile_command_variants(tmp_path: Path) -> None:
|
||||
"-o",
|
||||
str(gch),
|
||||
]
|
||||
# The compile must run where the flags were resolved
|
||||
assert cmd_dir == build
|
||||
|
||||
|
||||
def test_pch_compile_command_rejects_unusable_entries(tmp_path: Path) -> None:
|
||||
@@ -947,6 +956,6 @@ def test_prepare_pch_keeps_user_force_includes(tmp_path: Path) -> None:
|
||||
]
|
||||
)
|
||||
)
|
||||
cmd = pch_compile_command(build, build / "esphome_pch.h", build / "x.gch")
|
||||
cmd, _ = pch_compile_command(build, build / "esphome_pch.h", build / "x.gch")
|
||||
assert "user.h" in cmd
|
||||
assert "esphome_pch.h" not in " ".join(cmd[:-3])
|
||||
|
||||
@@ -1582,6 +1582,9 @@ def test_ccache_env_default_enabled_when_available(tmp_path: Path) -> None:
|
||||
assert env["CCACHE_NOHASHDIR"] == "true"
|
||||
assert env["CCACHE_DEPEND"] == "1"
|
||||
assert env["CCACHE_BASEDIR"] == str((tmp_path / "build").resolve())
|
||||
# The pch cannot cache under ccache without these
|
||||
assert env["CCACHE_SLOPPINESS"] == "pch_defines,time_macros"
|
||||
assert env["CCACHE_PCH_EXTSUM"] == "true"
|
||||
|
||||
|
||||
def test_ccache_env_disabled_when_binary_missing(tmp_path: Path) -> None:
|
||||
@@ -1991,12 +1994,3 @@ def test_ccache_env_opt_in_with_usable_binary(
|
||||
env = _ccache_env()
|
||||
assert env["IDF_CCACHE_ENABLE"] == "1"
|
||||
assert not [r for r in caplog.records if r.levelno >= logging.WARNING]
|
||||
|
||||
|
||||
def test_ccache_env_exports_pch_settings(tmp_path: Path) -> None:
|
||||
# The pch cannot cache under ccache without these
|
||||
p1, p2, p3 = _ccache_patches(tmp_path, "/usr/bin/ccache", tmp_path / "build")
|
||||
with patch.dict("os.environ", {}, clear=True), p1, p2, p3:
|
||||
env = _ccache_env()
|
||||
assert env["CCACHE_SLOPPINESS"] == "pch_defines,time_macros"
|
||||
assert env["CCACHE_PCH_EXTSUM"] == "true"
|
||||
|
||||
Reference in New Issue
Block a user