Skip to content

Commit f1833f8

Browse files
committed
reduce false positive warnings
1 parent 32a39bc commit f1833f8

4 files changed

Lines changed: 66 additions & 5 deletions

File tree

Changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
### v1.1-dev
2+
- C/C++ "build is CACHED" warning is confirmed against the artifact's mtime, so a
3+
single up-to-date subdirectory in a recursive `make` no longer triggers a false
4+
cache warning when the analyzed artifact was actually rebuilt.
25
- Indirect resolution precision: escape value-flow follows exact operands
36
(PHI/select/GEP/aggregate) and the conservative address-taken fallback fires
47
only for genuinely unresolved cast flows, trimming spurious low-confidence

driver/reachability/acquire_c.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
import time
3737

3838
from reachability import diagnostics
39-
from reachability.errors import build_looks_cached, decode, tail
39+
from reachability.errors import build_is_cached, build_looks_cached, decode, tail
4040

4141

4242
class AcquireError(RuntimeError):
@@ -559,10 +559,6 @@ def acquire_c_bitcode(project_dir, tc, artifact=None, build_cmd=None,
559559
raise AcquireError(f"build failed (exit {rc}){detail}")
560560

561561
cached = _build_looks_cached(build_output)
562-
if cached:
563-
print("warning: the build is CACHED (nothing was recompiled); the "
564-
"extracted bitcode reflects the existing artifact, not this run. "
565-
"Rebuild from clean if the target or its flags changed.")
566562

567563
errors = []
568564

@@ -622,6 +618,11 @@ def _no_bitcode(base):
622618
"get-bc could not extract bitcode from any detected artifact:\n "
623619
+ "\n ".join(errors))
624620

621+
if build_is_cached(build_output, art, before):
622+
print("warning: the build is CACHED (nothing was recompiled); the "
623+
"extracted bitcode reflects the existing artifact, not this run. "
624+
"Rebuild from clean if the target or its flags changed.")
625+
625626
if static_libs != "none":
626627
expanded = _include_static_libs(
627628
project_dir, art, kind, primary, static_libs,

driver/reachability/errors.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import os
2+
3+
14
def decode(value):
25
if value is None:
36
return ""
@@ -21,3 +24,21 @@ def build_looks_cached(output):
2124
)):
2225
return True
2326
return "Finished" in text and "Compiling " not in text
27+
28+
29+
def build_is_cached(output, artifact_path, build_started_at, tolerance=2.0):
30+
"""Whether the analyzed artifact predates this build (a genuine cache hit).
31+
32+
``build_looks_cached`` only scans build text, so a single up-to-date
33+
subdirectory in a recursive ``make`` makes it fire even when other targets
34+
recompiled. Confirm the text verdict against the artifact's mtime: if it was
35+
written during this build it is fresh, so the marker was a partial-cache
36+
false positive. ``tolerance`` absorbs coarse filesystem mtime granularity.
37+
Fall back to the text verdict when the artifact cannot be stat'd.
38+
"""
39+
if not build_looks_cached(output):
40+
return False
41+
try:
42+
return os.path.getmtime(artifact_path) < build_started_at - tolerance
43+
except OSError:
44+
return True

driver/tests/test_acquire_c.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,42 @@ def test_build_looks_cached():
9090
assert not acquire_c._build_looks_cached("cc -c foo.c -o foo.o")
9191

9292

93+
def test_build_is_cached_stale_artifact(tmp_path):
94+
art = tmp_path / "harness.o"
95+
_write(art, b"x")
96+
old = os.path.getmtime(art) - 100
97+
os.utime(art, (old, old))
98+
started = old + 50
99+
assert acquire_c.build_is_cached(
100+
"make: Nothing to be done for 'all'.", str(art), started)
101+
102+
103+
def test_build_is_cached_fresh_artifact_recursive_subdir(tmp_path):
104+
# Recursive make: one subdir is up-to-date but the artifact was rebuilt.
105+
art = tmp_path / "harness.o"
106+
_write(art, b"x")
107+
started = os.path.getmtime(art) - 100
108+
output = (" CC gjobread.o\n"
109+
"make[2]: Nothing to be done for 'all'.\n"
110+
" CCLD harness\n")
111+
assert not acquire_c.build_is_cached(output, str(art), started)
112+
113+
114+
def test_build_is_cached_requires_marker(tmp_path):
115+
art = tmp_path / "harness.o"
116+
_write(art, b"x")
117+
old = os.path.getmtime(art) - 100
118+
os.utime(art, (old, old))
119+
assert not acquire_c.build_is_cached(" CC harness.o", str(art), old + 50)
120+
121+
122+
def test_build_is_cached_missing_artifact_falls_back(tmp_path):
123+
missing = str(tmp_path / "nope.o")
124+
assert acquire_c.build_is_cached(
125+
"make: Nothing to be done for 'all'.", missing, 0.0)
126+
assert not acquire_c.build_is_cached("cc -c foo.c -o foo.o", missing, 0.0)
127+
128+
93129
def test_detect_build_cmd_none(tmp_path):
94130
assert acquire_c.detect_build_cmd(str(tmp_path)) is None
95131

0 commit comments

Comments
 (0)