Skip to content

Commit 8dcd5da

Browse files
committed
Prevent cross-environment PETSc build cache contamination
When switching between pixi environments that use different PETSc installations (e.g. conda-forge 3.24.2 vs custom 3.24.3), the setuptools build/ directory silently reused .so files compiled against the wrong libpetsc, causing PETSc error 73 at solve time. Fix: setup.py now records the PETSc target (PETSC_DIR/PETSC_ARCH) in build/.petsc_target and automatically wipes cached extensions whenever the target changes. This protects all build methods (./uw build, raw pip install, IDE builds). Also: ./uw setup now routes through run_build instead of raw pip; added the missing ./uw clean command. Underworld development team with AI support from Claude Code
1 parent 8e85d18 commit 8dcd5da

2 files changed

Lines changed: 66 additions & 1 deletion

File tree

setup.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,69 @@
77
from Cython.Build import cythonize
88

99
import os
10+
import shutil
1011
import numpy
1112
import petsc4py
1213

1314

15+
def _clean_stale_build_cache():
16+
"""Remove cached Cython extensions when the PETSc target changes.
17+
18+
The setuptools build directory (build/lib.*) caches compiled .so files
19+
and reuses them on subsequent installs — even if the PETSc installation
20+
has changed. When switching between pixi environments that use different
21+
PETSc builds (e.g. conda-forge 3.24.2 vs custom 3.24.3), the stale .so
22+
files silently link against the wrong libpetsc, causing PETSc error 73
23+
("Object is in wrong state") at solve time.
24+
25+
This function records which PETSc was used for the last build and wipes
26+
the extension cache whenever the target changes.
27+
"""
28+
build_dir = os.path.join(os.path.dirname(__file__) or ".", "build")
29+
marker = os.path.join(build_dir, ".petsc_target")
30+
31+
config = petsc4py.get_config()
32+
current = config["PETSC_DIR"] + "/" + config.get("PETSC_ARCH", "")
33+
34+
if os.path.isfile(marker):
35+
with open(marker) as f:
36+
previous = f.read().strip()
37+
if previous == current:
38+
return # Same target — nothing to do
39+
print(f"\n *** PETSc target changed ***")
40+
print(f" was: {previous}")
41+
print(f" now: {current}")
42+
print(f" Cleaning build cache to force recompilation...\n")
43+
elif any(
44+
d.startswith("lib.") or d.startswith("temp.")
45+
for d in os.listdir(build_dir)
46+
if os.path.isdir(os.path.join(build_dir, d))
47+
) if os.path.isdir(build_dir) else False:
48+
# No marker but cached extensions exist — stale from a previous build
49+
print(f"\n *** No PETSc target marker — cleaning build cache...\n")
50+
else:
51+
# No cache at all — first build, just record the target
52+
os.makedirs(build_dir, exist_ok=True)
53+
with open(marker, "w") as f:
54+
f.write(current)
55+
return
56+
57+
# Wipe cached extensions
58+
for name in os.listdir(build_dir):
59+
path = os.path.join(build_dir, name)
60+
if os.path.isdir(path) and (
61+
name.startswith("lib.") or name.startswith("temp.")
62+
):
63+
shutil.rmtree(path)
64+
65+
os.makedirs(build_dir, exist_ok=True)
66+
with open(marker, "w") as f:
67+
f.write(current)
68+
69+
70+
_clean_stale_build_cache()
71+
72+
1473
def configure():
1574

1675
INCLUDE_DIRS = []

uw

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ run_setup() {
481481
read -p "Build underworld3? [Y/n]: " build_uw
482482
build_uw=${build_uw:-y}
483483
if [ "$build_uw" = "y" ] || [ "$build_uw" = "Y" ]; then
484-
$PIXI run -e "$new_env" pip install . --no-build-isolation
484+
run_build
485485
fi
486486

487487
# Optional: Configure extended AI context paths
@@ -889,6 +889,12 @@ case "${1:-}" in
889889
build)
890890
run_build
891891
;;
892+
clean)
893+
echo "Cleaning build artifacts..."
894+
rm -rf "$SCRIPT_DIR"/build/lib.* "$SCRIPT_DIR"/build/temp.* "$SCRIPT_DIR"/build/bdist.*
895+
rm -f "$SCRIPT_DIR/build/.petsc_target"
896+
echo -e "${GREEN}Done${NC}. Run './uw build' to rebuild."
897+
;;
892898
doctor)
893899
run_doctor
894900
;;

0 commit comments

Comments
 (0)