diff --git a/esphome/build_gen/build_tool.py b/esphome/build_gen/build_tool.py index d20fe78673..608c316e83 100644 --- a/esphome/build_gen/build_tool.py +++ b/esphome/build_gen/build_tool.py @@ -85,7 +85,11 @@ def _run_copy(src: str, dst: str) -> int: def _run_touch(path: str) -> int: - Path(path).touch() + try: + Path(path).touch() + except OSError as err: + print(f"touch: {path} failed: {err}", file=sys.stderr) + return 1 return 0 diff --git a/tests/unit_tests/build_gen/test_build_tool.py b/tests/unit_tests/build_gen/test_build_tool.py index 5beef92b10..9bc44bc149 100644 --- a/tests/unit_tests/build_gen/test_build_tool.py +++ b/tests/unit_tests/build_gen/test_build_tool.py @@ -254,3 +254,11 @@ def test_touch_creates_and_updates_stamp(tmp_path: Path) -> None: os.utime(stamp, (1, 1)) assert build_tool._run_touch(str(stamp)) == 0 assert stamp.stat().st_mtime > 1 + + +def test_touch_reports_failure( + tmp_path: Path, capsys: pytest.CaptureFixture[str] +) -> None: + missing_dir = tmp_path / "gone" / "stamp" + assert build_tool._run_touch(str(missing_dir)) == 1 + assert "touch:" in capsys.readouterr().err diff --git a/tests/unit_tests/test_platformio_pch_script.py b/tests/unit_tests/test_platformio_pch_script.py index d2eaf80161..214a4f26af 100644 --- a/tests/unit_tests/test_platformio_pch_script.py +++ b/tests/unit_tests/test_platformio_pch_script.py @@ -478,7 +478,10 @@ def test_pch_script_strict_tables_match_helpers(tmp_path: Path) -> None: proj = tmp_path / "dev" (proj / "src").mkdir(parents=True) - env = _FakeSConsEnv(proj, proj / "src", "g++", ["-DX=1"]) + # Hermetic: the constants are module-level, but the exec still runs + # _setup_pch, which must not touch the host toolchain + cxx = _fake_cxx(tmp_path) + env = _FakeSConsEnv(proj, proj / "src", str(cxx), ["-DX=1"]) namespace = {"Import": lambda *_names: None, "env": env, "projenv": env} with patch.dict(os.environ, {}, clear=True): exec(compile(_SCRIPT.read_text(), "pch.py", "exec"), namespace) # noqa: S102