[ci] Ignore BUILD_ALWAYS external project byproducts in the idf.py check (#21291)

This commit is contained in:
Jonathan Swoboda
2026-10-21 08:35:58 -10:00
committed by GitHub
parent da37e89b9d
commit 35883270fe
2 changed files with 51 additions and 4 deletions
+23 -4
View File
@@ -51,6 +51,11 @@ def _ninja_logs(skip_bootloader: bool) -> list[str]:
BOOTLOADER_BYPRODUCT = re.compile(r"(^|/build/)bootloader/")
# The build step of any CMake ExternalProject. One with BUILD_ALWAYS (ESP-IDF's
# ulp_embed_binary) re-runs on every build, and ninja re-records all of that
# edge's byproducts with the new end time even though the files are untouched.
# Real work on them still changes the hashed firmware.
EXTERNAL_PROJECT_BUILD_STAMP = re.compile(r"-stamp/[^/]+-build$")
MACRO_CHANGED = (
"IDF changed __build_process_project_includes; update "
"IDF_BOOTLOADER_OVERRIDE in esphome/build_gen/espidf.py"
@@ -96,10 +101,24 @@ def _ninja_mtimes(build_path: Path, logs: list[str]) -> dict[tuple[str, str], st
for name in logs:
log = build_path / name
lines = log.read_text(encoding="utf-8").splitlines() if log.is_file() else []
for fields in (line.split("\t") for line in lines if not line.startswith("#")):
if len(fields) >= 4 and (
name != TOP_NINJA_LOG or not BOOTLOADER_BYPRODUCT.search(fields[3])
):
rows = [
fields
for fields in (
line.split("\t") for line in lines if not line.startswith("#")
)
if len(fields) >= 5
]
# Every output of an edge shares its command hash, so the stamp names
# the whole external project group.
external = {
fields[4]
for fields in rows
if EXTERNAL_PROJECT_BUILD_STAMP.search(fields[3])
}
for fields in rows:
if fields[4] in external:
continue
if name != TOP_NINJA_LOG or not BOOTLOADER_BYPRODUCT.search(fields[3]):
mtimes[name, fields[3]] = fields[2]
return mtimes
@@ -156,6 +156,34 @@ def test_check_reports_changed_files_and_rebuilt_outputs(tmp_path: Path) -> None
]
def test_check_ignores_a_build_always_external_project(tmp_path: Path) -> None:
"""ESP-IDF's ULP app is an ExternalProject with BUILD_ALWAYS: its step
re-runs on every build and ninja re-records its byproducts with the new
end time, so a moved mtime on that edge is not work. The same outputs
under another edge still are."""
tree = _make_tree(tmp_path)
build = tree / "build"
ulp = (
"1\t2\t{t}\tesp-idf/src/ulp_main-prefix/src/ulp_main-stamp/ulp_main-build\tab\n"
"1\t2\t{t}\tesp-idf/src/ulp_main/ulp_main.bin\tab\n"
"1\t2\t{t}\tesp-idf/src/ulp_main/ulp_main.ld\tab\n"
)
(build / ".ninja_log").write_text(
"# ninja log v7\n1\t2\t10\tesp-idf/a.obj\t0\n"
"1\t2\t10\tesp-idf/other.ld\tcd\n" + ulp.format(t=10)
)
def rerun(cmd: list[str]) -> None:
if cmd[-1] == "build":
(build / ".ninja_log").write_text(
"# ninja log v7\n1\t2\t10\tesp-idf/a.obj\t0\n"
"3\t4\t20\tesp-idf/other.ld\tcd\n" + ulp.format(t=30)
)
problems, _ = _run_check(tree, rerun)
assert problems == ["idf.py rebuilt esp-idf/other.ld"]
@pytest.mark.parametrize(
("after_build", "problem"),
[