Skip to content

Commit 37b7a21

Browse files
committed
fp-stability: address PR review — atomic prebuilt install, verify VERROU_HOME tree, more tests
verrou.sh try_prebuilt() runs as an if-condition (set -e suppressed), so a failed extract could fall through and the source build would install over a half-written tree; now extract+verify in a staging dir and swap into PREFIX atomically with explicit error checks. _find_verrou now verifies the $VERROU_HOME tree actually runs the verrou tool (with VALGRIND_LIB for a relocated prebuilt) so a broken/stale tree reads as absent and gets reinstalled, not used until it fails per-run. Fix comment rot (fp-stability now auto-installs). Add unit tests for _verrou_env (incl. preserve-user-VALGRIND_LIB), _dd_env PYTHONPATH composition, _install_verrou hard-fail guards, _has_verrou_tool OSError, and the broken-VERROU_HOME case.
1 parent 0613913 commit 37b7a21

3 files changed

Lines changed: 130 additions & 12 deletions

File tree

toolchain/bootstrap/verrou.sh

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
# `./mfc.sh fp-stability`). Verrou is NOT a Python/pip package — it is a fork of
55
# Valgrind. By default this downloads a prebuilt, hash-verified artifact (seconds);
66
# if none is available for this tag/arch it falls back to a source build (~20 min).
7-
# Either way it's a deliberate, explicit step, not something fp-stability does silently.
7+
# fp-stability auto-runs this on first use when Verrou is absent (printing what it
8+
# does); it is also safe to run by hand. A failed install aborts, never a silent skip.
89
#
910
# bash toolchain/bootstrap/verrou.sh # install into $HOME/.local/verrou
1011
# VERROU_HOME=/path bash toolchain/bootstrap/verrou.sh
@@ -80,20 +81,34 @@ try_prebuilt() {
8081
rm -rf "$dl"; return 1
8182
fi
8283

83-
mkdir -p "$PREFIX"
84+
# Extract + verify in a staging dir, then swap into $PREFIX atomically. set -e
85+
# is suppressed inside a function used as an `if` condition, so check each step
86+
# explicitly — otherwise a failed extract would fall through and the source
87+
# build would install on top of a half-written tree (or a stale one on --force).
88+
local stage="$dl/stage"
89+
mkdir -p "$stage"
8490
if tar --zstd --help >/dev/null 2>&1; then
85-
tar -C "$PREFIX" --zstd -xf "$dl/$asset"
91+
tar -C "$stage" --zstd -xf "$dl/$asset" || { echo "WARNING: prebuilt extract failed — building from source instead." >&2; rm -rf "$dl"; return 1; }
8692
else
87-
zstd -dc "$dl/$asset" | tar -C "$PREFIX" -xf -
93+
zstd -dc "$dl/$asset" | tar -C "$stage" -xf - || { echo "WARNING: prebuilt extract failed — building from source instead." >&2; rm -rf "$dl"; return 1; }
8894
fi
89-
rm -rf "$dl"
9095

9196
# Valgrind bakes its build prefix into the binary; the artifact's env.sh sets
92-
# VALGRIND_LIB relative to the extracted tree so the relocated install works.
93-
if ! ( . "${PREFIX}/env.sh" && "${PREFIX}/bin/valgrind" --tool=verrou --version >/dev/null 2>&1 ); then
97+
# VALGRIND_LIB relative to the tree so the relocated install works. Verify the
98+
# staged tree runs before committing it.
99+
if ! ( . "${stage}/env.sh" && "${stage}/bin/valgrind" --tool=verrou --version >/dev/null 2>&1 ); then
94100
echo "WARNING: prebuilt did not run — building from source instead." >&2
95-
return 1
101+
rm -rf "$dl"; return 1
102+
fi
103+
104+
# Commit only now: replace any existing $PREFIX atomically.
105+
mkdir -p "$(dirname "$PREFIX")"
106+
rm -rf "$PREFIX"
107+
if ! mv "$stage" "$PREFIX"; then
108+
echo "WARNING: could not install prebuilt to ${PREFIX} — building from source instead." >&2
109+
rm -rf "$dl"; return 1
96110
fi
111+
rm -rf "$dl"
97112
return 0
98113
}
99114

toolchain/mfc/fp_stability_runners.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,24 @@
3232
from .printer import cons
3333

3434

35-
def _has_verrou_tool(valgrind_bin: str) -> bool:
35+
def _has_verrou_tool(valgrind_bin: str, env: dict = None) -> bool:
3636
"""True if this valgrind actually provides the 'verrou' tool. A plain system
37-
valgrind does not — accepting one would only fail later at run time."""
37+
valgrind does not — accepting one would only fail later at run time. Pass env
38+
(with VALGRIND_LIB) to verify a relocated prebuilt tree, which cannot load its
39+
tool without it."""
3840
try:
39-
return subprocess.run([valgrind_bin, "--tool=verrou", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False).returncode == 0
41+
return subprocess.run([valgrind_bin, "--tool=verrou", "--version"], env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False).returncode == 0
4042
except OSError:
4143
return False
4244

4345

4446
def _find_verrou() -> str:
4547
verrou_home = os.environ.get("VERROU_HOME", os.path.join(os.path.expanduser("~"), ".local", "verrou"))
4648
candidate = os.path.join(verrou_home, "bin", "valgrind")
47-
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
49+
# Require the $VERROU_HOME tree to actually run the verrou tool (with VALGRIND_LIB
50+
# for a relocated prebuilt). A broken/stale/non-Verrou tree there must read as
51+
# "absent" so it gets reinstalled, not used until it fails on every run.
52+
if os.path.isfile(candidate) and os.access(candidate, os.X_OK) and _has_verrou_tool(candidate, _verrou_env(candidate)):
4853
return candidate
4954
# Fall back to a valgrind on PATH only if it is Verrou-enabled; a bare system
5055
# valgrind must read as "Verrou absent" so it gets installed, not misused.

toolchain/mfc/test_fp_stability.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,9 +392,27 @@ def test_find_verrou_prefers_verrou_home_candidate(tmp_path, monkeypatch):
392392
vbin.write_text("#!/bin/sh\n")
393393
vbin.chmod(0o755)
394394
monkeypatch.setenv("VERROU_HOME", str(tmp_path))
395+
# The candidate must also verify as Verrou-enabled; stub that so the test
396+
# exercises precedence, not a real valgrind invocation.
397+
monkeypatch.setattr(runners, "_has_verrou_tool", lambda _bin, _env=None: True)
395398
assert runners._find_verrou() == str(vbin)
396399

397400

401+
def test_find_verrou_rejects_broken_verrou_home_tree(tmp_path, monkeypatch):
402+
from mfc import fp_stability_runners as runners
403+
404+
# A valgrind exists at $VERROU_HOME but does not actually run the verrou tool
405+
# (broken/stale/non-Verrou): it must read as absent, not be returned.
406+
vbin = tmp_path / "bin" / "valgrind"
407+
vbin.parent.mkdir(parents=True)
408+
vbin.write_text("#!/bin/sh\n")
409+
vbin.chmod(0o755)
410+
monkeypatch.setenv("VERROU_HOME", str(tmp_path))
411+
monkeypatch.setattr(runners, "_has_verrou_tool", lambda _bin, _env=None: False)
412+
monkeypatch.setattr(runners.shutil, "which", lambda _name: None)
413+
assert runners._find_verrou() == ""
414+
415+
398416
def test_find_verrou_rejects_non_verrou_path_valgrind(tmp_path, monkeypatch):
399417
from mfc import fp_stability_runners as runners
400418

@@ -425,3 +443,83 @@ def __init__(self, rc):
425443
assert runners._has_verrou_tool("/any/valgrind") is True
426444
monkeypatch.setattr(runners.subprocess, "run", lambda *a, **k: _R(1))
427445
assert runners._has_verrou_tool("/any/valgrind") is False
446+
447+
def _boom(*a, **k):
448+
raise OSError("not executable")
449+
450+
monkeypatch.setattr(runners.subprocess, "run", _boom)
451+
assert runners._has_verrou_tool("/stale/valgrind") is False
452+
453+
454+
# --- env composition for relocated (prebuilt) Verrou trees ---
455+
456+
457+
def test_verrou_env_sets_valgrind_lib_when_libexec_present(tmp_path, monkeypatch):
458+
from mfc import fp_stability_runners as runners
459+
460+
(tmp_path / "libexec" / "valgrind").mkdir(parents=True)
461+
monkeypatch.delenv("VALGRIND_LIB", raising=False)
462+
env = runners._verrou_env(str(tmp_path / "bin" / "valgrind"))
463+
assert env["VALGRIND_LIB"] == str(tmp_path / "libexec" / "valgrind")
464+
465+
466+
def test_verrou_env_omits_valgrind_lib_when_libexec_absent(tmp_path, monkeypatch):
467+
from mfc import fp_stability_runners as runners
468+
469+
monkeypatch.delenv("VALGRIND_LIB", raising=False)
470+
env = runners._verrou_env(str(tmp_path / "bin" / "valgrind"))
471+
assert "VALGRIND_LIB" not in env
472+
473+
474+
def test_verrou_env_preserves_user_valgrind_lib(tmp_path, monkeypatch):
475+
from mfc import fp_stability_runners as runners
476+
477+
(tmp_path / "libexec" / "valgrind").mkdir(parents=True)
478+
monkeypatch.setenv("VALGRIND_LIB", "/user/chosen/lib")
479+
env = runners._verrou_env(str(tmp_path / "bin" / "valgrind"))
480+
assert env["VALGRIND_LIB"] == "/user/chosen/lib" # not clobbered
481+
482+
483+
def test_dd_env_prepends_pythonpath_and_inherits_valgrind_lib(tmp_path, monkeypatch):
484+
from mfc import fp_stability_runners as runners
485+
486+
(tmp_path / "libexec" / "valgrind").mkdir(parents=True)
487+
monkeypatch.delenv("VALGRIND_LIB", raising=False)
488+
monkeypatch.setenv("PYTHONPATH", "/pre/existing")
489+
monkeypatch.setattr(runners, "_verrou_pythonpath", lambda _b: "/vg/site-packages/valgrind")
490+
env = runners._dd_env(str(tmp_path / "bin" / "valgrind"))
491+
assert env["PYTHONPATH"] == "/vg/site-packages/valgrind:/pre/existing"
492+
assert env["VALGRIND_LIB"] == str(tmp_path / "libexec" / "valgrind")
493+
494+
495+
def test_dd_env_no_leading_colon_when_pythonpath_empty(tmp_path, monkeypatch):
496+
from mfc import fp_stability_runners as runners
497+
498+
monkeypatch.delenv("PYTHONPATH", raising=False)
499+
monkeypatch.setattr(runners, "_verrou_pythonpath", lambda _b: "/vg/valgrind")
500+
env = runners._dd_env(str(tmp_path / "bin" / "valgrind"))
501+
assert env["PYTHONPATH"] == "/vg/valgrind" # no stray leading ':'
502+
503+
504+
# --- auto-install hard-fail guards ---
505+
506+
507+
def test_install_verrou_raises_when_bootstrap_fails(monkeypatch):
508+
import pytest
509+
510+
from mfc import fp_stability as fps
511+
512+
monkeypatch.setattr(fps.subprocess, "run", lambda *a, **k: type("R", (), {"returncode": 1})())
513+
with pytest.raises(fps.MFCException, match="Verrou install failed"):
514+
fps._install_verrou()
515+
516+
517+
def test_install_verrou_raises_when_no_binary_appears(monkeypatch):
518+
import pytest
519+
520+
from mfc import fp_stability as fps
521+
522+
monkeypatch.setattr(fps.subprocess, "run", lambda *a, **k: type("R", (), {"returncode": 0})())
523+
monkeypatch.setattr(fps, "_find_verrou", lambda: "")
524+
with pytest.raises(fps.MFCException, match="no valgrind binary"):
525+
fps._install_verrou()

0 commit comments

Comments
 (0)