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) ---