From 2bb168287452512373111045aaca0c1d72f9b0e8 Mon Sep 17 00:00:00 2001 From: wcwxy <26245345+ChaoWao@users.noreply.github.com> Date: Wed, 27 May 2026 11:13:54 +0800 Subject: [PATCH] Fix: clear cmake cache when git HEAD is unavailable _invalidate_cache_if_stale previously returned early when git rev-parse could not produce a commit (no .git, transient subprocess failure), leaving any old marker and build artifacts in place. That silently defeats the invalidation guard whenever git is briefly unreachable. Treat an unavailable HEAD as definitely-stale: wipe the cache dir and do not write a marker, so the next build also rebuilds from scratch. A clean compile is cheap relative to the risk of linking against mismatched objects. --- simpler_setup/runtime_builder.py | 8 +++++ tests/ut/py/test_runtime_builder.py | 48 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/simpler_setup/runtime_builder.py b/simpler_setup/runtime_builder.py index 28d8d7fe8..bcadeb48d 100644 --- a/simpler_setup/runtime_builder.py +++ b/simpler_setup/runtime_builder.py @@ -48,8 +48,16 @@ def _invalidate_cache_if_stale(target_cache_dir: Path, current_commit: str) -> N cannot detect that source files changed. Comparing the HEAD commit stored at last build time against the current HEAD is a reliable signal that sources may have changed and a clean rebuild is needed. + + When the current commit can't be determined (no git, transient failure), + fall through to a clean rebuild — a fresh compile is cheap relative to + the risk of linking against stale objects. """ if not current_commit: + if target_cache_dir.is_dir(): + logger.info("git HEAD unavailable, clearing cmake cache: %s", target_cache_dir) + shutil.rmtree(target_cache_dir) + target_cache_dir.mkdir(parents=True, exist_ok=True) return commit_file = target_cache_dir / _GIT_COMMIT_FILE if commit_file.is_file(): diff --git a/tests/ut/py/test_runtime_builder.py b/tests/ut/py/test_runtime_builder.py index 122cf867b..a04281fa6 100644 --- a/tests/ut/py/test_runtime_builder.py +++ b/tests/ut/py/test_runtime_builder.py @@ -260,6 +260,54 @@ def test_propagates_compiler_error(self, MockCompiler, tmp_path, default_test_pl builder.get_binaries("test_rt", build=True) +# --- _invalidate_cache_if_stale unit tests --- + + +class TestInvalidateCacheIfStale: + """Test cmake cache invalidation behavior under varying git state.""" + + def test_clears_when_commit_mismatches(self, tmp_path): + from simpler_setup.runtime_builder import _GIT_COMMIT_FILE, _invalidate_cache_if_stale # noqa: PLC0415 + + cache_dir = tmp_path / "cache" + cache_dir.mkdir() + (cache_dir / _GIT_COMMIT_FILE).write_text("old_sha\n") + (cache_dir / "stale_artifact.o").write_text("stale") + + _invalidate_cache_if_stale(cache_dir, "new_sha") + + assert not (cache_dir / "stale_artifact.o").exists() + assert (cache_dir / _GIT_COMMIT_FILE).read_text().strip() == "new_sha" + + def test_keeps_when_commit_matches(self, tmp_path): + from simpler_setup.runtime_builder import _GIT_COMMIT_FILE, _invalidate_cache_if_stale # noqa: PLC0415 + + cache_dir = tmp_path / "cache" + cache_dir.mkdir() + (cache_dir / _GIT_COMMIT_FILE).write_text("same_sha\n") + artifact = cache_dir / "kept_artifact.o" + artifact.write_text("fresh") + + _invalidate_cache_if_stale(cache_dir, "same_sha") + + assert artifact.exists() + + def test_clears_when_commit_unavailable(self, tmp_path): + """No discoverable git HEAD should force a clean rebuild, not silently skip.""" + from simpler_setup.runtime_builder import _GIT_COMMIT_FILE, _invalidate_cache_if_stale # noqa: PLC0415 + + cache_dir = tmp_path / "cache" + cache_dir.mkdir() + (cache_dir / _GIT_COMMIT_FILE).write_text("old_sha\n") + (cache_dir / "stale_artifact.o").write_text("stale") + + _invalidate_cache_if_stale(cache_dir, "") + + assert not (cache_dir / "stale_artifact.o").exists() + assert not (cache_dir / _GIT_COMMIT_FILE).exists() + assert cache_dir.is_dir() + + # --- Full integration tests (real compilation) ---