diff --git a/script/check_idf_py_equivalence.py b/script/check_idf_py_equivalence.py index fa349e223a..ff7c58a5a6 100755 --- a/script/check_idf_py_equivalence.py +++ b/script/check_idf_py_equivalence.py @@ -15,6 +15,7 @@ from __future__ import annotations import argparse import hashlib import json +import os from pathlib import Path import re import subprocess @@ -106,6 +107,9 @@ def check(build_path: Path) -> list[str]: (build_path / "build" / "project_description.json").read_text(encoding="utf-8") ) name, version = _setup_core(build_path, description) + # ESP-IDF's openthread stamps the configure time into its compile flags; + # pin it before the env is cached so both configures get the same value. + os.environ["SOURCE_DATE_EPOCH"] = "0" env = toolchain._get_idf_env(version) python = toolchain._get_idf_tool("python") idf_py = toolchain._get_idf_path(version) / "tools" / "idf.py" diff --git a/tests/script/test_check_idf_py_equivalence.py b/tests/script/test_check_idf_py_equivalence.py index f8135772d0..6ded9a3a81 100644 --- a/tests/script/test_check_idf_py_equivalence.py +++ b/tests/script/test_check_idf_py_equivalence.py @@ -2,6 +2,7 @@ from collections.abc import Callable, Iterator import json +import os from pathlib import Path import subprocess import sys @@ -55,22 +56,32 @@ def _run_check( side_effect: Callable[[list[str]], None] = lambda cmd: None, rc: int = 0, esphome_rcs: tuple[int, int] = (0, 0), + envs: list[dict[str, str]] | None = None, ) -> tuple[list[str], list[list[str]]]: - """Run check() with idf.py replaced by ``side_effect``; return problems, calls.""" + """Run check() with idf.py replaced by ``side_effect``; return problems, calls. + + ``envs`` collects the env each idf.py call receives. + """ calls: list[list[str]] = [] def run(cmd: list[str], **kwargs: object) -> subprocess.CompletedProcess: calls.append(cmd) + if envs is not None: + envs.append(kwargs["env"]) side_effect(cmd) return subprocess.CompletedProcess(cmd, rc, "out\n", "err\n") with ( - patch.object(toolchain, "_get_idf_env", return_value={}), + # Snapshot at call time like the real cached env. + patch.object( + toolchain, "_get_idf_env", side_effect=lambda *_: dict(os.environ) + ), patch.object(toolchain, "_get_idf_tool", return_value="/py"), patch.object(toolchain, "_get_idf_path", return_value=Path("/idf")), patch.object(toolchain, "run_reconfigure", return_value=esphome_rcs[0]), patch.object(toolchain, "_run_ninja", return_value=esphome_rcs[1]), patch.object(guard.subprocess, "run", side_effect=run), + patch.dict(os.environ), ): return guard.check(tree), calls @@ -86,6 +97,13 @@ def test_check_passes_when_idf_py_changes_nothing(tmp_path: Path) -> None: ] +def test_check_pins_source_date_epoch(tmp_path: Path) -> None: + """ESP-IDF's openthread bakes the configure time into its compile flags.""" + envs: list[dict[str, str]] = [] + _run_check(_make_tree(tmp_path), envs=envs) + assert [env.get("SOURCE_DATE_EPOCH") for env in envs] == ["0", "0"] + + def test_check_reports_changed_files_and_rebuilt_outputs(tmp_path: Path) -> None: tree = _make_tree(tmp_path) build = tree / "build"