Reraise in strict through the IDF flow, funnel the remaining degrade paths, exempt nobuild

This commit is contained in:
J. Nick Koston
2026-08-26 22:42:03 -05:00
parent 264ccc0989
commit 54bff55642
6 changed files with 57 additions and 3 deletions
+1
View File
@@ -334,6 +334,7 @@ def prepare_pch() -> None:
"Could not read %s; compiling without the pch: %s", sdkconfig_path, err
)
pch.discard_pch(CORE.relative_build_path("build"))
pch.pch_degraded(f"sdkconfig unreadable: {err}")
return
pch.prepare_pch(
CORE.relative_build_path("build"),
+1
View File
@@ -407,6 +407,7 @@ def prepare_pch(
-result.returncode,
)
discard_pch(build_dir)
pch_degraded(f"compile killed by signal {-result.returncode}")
return
if result.returncode != 0:
error = result.stderr.strip() or f"exit code {result.returncode}"
+4
View File
@@ -539,6 +539,10 @@ def run_compile(config, verbose: bool) -> int:
# Discard so a stale .gch can never be consumed
with suppress(OSError):
discard_pch()
from esphome.build_helpers.pch import pch_strict
if pch_strict():
raise
_LOGGER.warning(
"Precompiled header setup failed; compiling without it", exc_info=True
)
+3 -2
View File
@@ -160,9 +160,10 @@ def _read_stamp(path: Path) -> str:
def _setup_pch() -> bool | None:
if projenv is None:
# Expected under -t nobuild; anything else must leave a trail
# Expected under -t nobuild (nothing compiles), so not a degrade
# even in strict mode; anything else still leaves a trail
print(f"ESPHome: projenv unavailable ({_projenv_error}); skipping pch")
return
return True
# Project root: SCons compiles run here, so the relative -include
# resolves; an absolute path would break cross-device ccache sharing.
proj_dir = Path(env.subst("$PROJECT_DIR")) # noqa: F821
+23
View File
@@ -669,6 +669,29 @@ def test_get_core_framework_version_from_core_data():
assert toolchain._get_core_framework_version() == "5.5.4"
def test_run_compile_strict_reraises_pch_failure(
setup_core: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""ESPHOME_PCH_STRICT must reach through the real compile flow."""
from esphome.core import EsphomeError
monkeypatch.setenv("ESPHOME_PCH_ENABLE", "1")
monkeypatch.setenv("ESPHOME_PCH_STRICT", "1")
_setup_build(setup_core)
with (
patch.object(toolchain, "need_reconfigure", return_value=False),
patch.object(toolchain, "run_idf_py", return_value=0),
patch.object(toolchain, "print_summary"),
patch(
"esphome.build_gen.espidf.prepare_pch",
side_effect=EsphomeError("ESPHOME_PCH_STRICT: no usable compile command"),
),
pytest.raises(EsphomeError, match="no usable compile command"),
):
toolchain.run_compile({CONF_ESPHOME: {}}, verbose=False)
def test_run_compile_invokes_prepare_pch_and_survives_failure(
setup_core: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
+25 -1
View File
@@ -462,10 +462,34 @@ def test_pch_script_unreadable_local_header_skips_pch(
def test_pch_script_strict_raises_when_pch_not_used(tmp_path: Path) -> None:
"""ESPHOME_PCH_STRICT fails the build instead of degrading."""
with pytest.raises(Exception, match="ESPHOME_PCH_STRICT|boom"):
with pytest.raises(RuntimeError, match="ESPHOME_PCH_STRICT"):
_run_script(tmp_path, fail=True, env_vars={"ESPHOME_PCH_STRICT": "1"})
def test_pch_script_strict_reraises_internal_errors(tmp_path: Path) -> None:
"""The catch-all must not swallow programming errors in strict mode."""
with pytest.raises(TypeError):
_run_script(tmp_path, env_vars={"ESPHOME_PCH_STRICT": "1"}, platform_cls=None)
def test_pch_script_strict_allows_nobuild_skip(tmp_path: Path) -> None:
"""-t nobuild compiles nothing; the skip is expected even in strict."""
proj = tmp_path / "dev"
(proj / "src").mkdir(parents=True)
def strict_import(*names: str) -> None:
if "projenv" in names:
raise RuntimeError("Import of non-existent variable 'projenv'")
env = _FakeSConsEnv(proj, proj / "src", "g++", ["-DX=1"])
with patch.dict(os.environ, {"ESPHOME_PCH_STRICT": "1"}, clear=True):
exec( # noqa: S102
compile(_SCRIPT.read_text(), "pch.py", "exec"),
{"Import": strict_import, "env": env},
)
assert not (proj / "esphome_pch.h").exists()
def test_pch_script_strict_passes_on_success(tmp_path: Path) -> None:
scons_env = _run_script(tmp_path, env_vars={"ESPHOME_PCH_STRICT": "1"})
assert scons_env.prepended