fix(install): rebuild x86_64 (Rosetta) venv as arm64 on Apple Silicon
On Apple Silicon, uv can create the Studio venv from a cached x86_64 Python (for example when uv itself is an x86_64 build) even though the shell reports arm64, so MAC_INTEL stays false. The venv then reports x86_64 to wheel resolvers and, because the PyTorch CPU index ships no macOS wheels for any architecture, the torch install fails with: torch>=2.6.0,... has no wheels with a matching platform tag (e.g. macosx_26_0_x86_64) The existing arm64 guard only handled the Python 3.13.8 torch import bug. Extend it to also detect a venv whose platform.machine() is x86_64 on an arm64 host and recreate it with uv's arch-explicit selector cpython-<ver>-macos-aarch64-none. The two checks run sequentially with a re-inspection between them, so a venv that is x86_64 and lands on 3.13.8 after the arm64 rebuild is still downgraded to 3.12. Both are skipped when the user passed --python. Adds an Apple Silicon venv rebuild section to tests/sh/test_mac_intel_compat.sh. Supersedes #5187; credit to Ramakrishna Bachu (@ramankrishna).
This commit is contained in:
parent
faea796c35
commit
70d4cc4885
2 changed files with 113 additions and 5 deletions
44
install.sh
44
install.sh
|
|
@ -1530,17 +1530,51 @@ if [ -x "$VENV_DIR/bin/python" ]; then
|
|||
: > "$VENV_DIR/.unsloth-studio-owned" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Guard against Python 3.13.8 torch import bug on Apple Silicon
|
||||
# (skip when the user explicitly chose a version via --python)
|
||||
# Guard against two independent Apple Silicon venv problems, in order:
|
||||
# 1. uv may create the venv from a cached x86_64 (Rosetta) Python when a
|
||||
# same-version x86_64 build is already cached (often because uv itself
|
||||
# is an x86_64 build). That venv reports x86_64 to wheel resolvers, and
|
||||
# PyTorch ships no macOS wheels on the CPU index for any architecture,
|
||||
# so the torch install can never resolve. Recreate it with an
|
||||
# arch-explicit arm64 CPython.
|
||||
# 2. Python 3.13.8 has a known torch import bug.
|
||||
# The two are independent: a venv may be x86_64 and, once recreated, still
|
||||
# land on 3.13.8. So we re-inspect the interpreter between the checks instead
|
||||
# of chaining them with elif, guaranteeing both invariants hold on whatever
|
||||
# venv we end up with. Skip both when the user explicitly chose an interpreter
|
||||
# via --python.
|
||||
if [ -z "$_USER_PYTHON" ] && [ "$OS" = "macos" ] && [ "$_ARCH" = "arm64" ]; then
|
||||
_PY_VER=$("$VENV_DIR/bin/python" -c \
|
||||
"import sys; print('{}.{}.{}'.format(*sys.version_info[:3]))" 2>/dev/null || echo "")
|
||||
_inspect_venv() {
|
||||
"$VENV_DIR/bin/python" -c \
|
||||
"import platform, sys; print(platform.machine(), '{}.{}.{}'.format(*sys.version_info[:3]))" \
|
||||
2>/dev/null || echo " "
|
||||
}
|
||||
_info=$(_inspect_venv)
|
||||
_VENV_ARCH=${_info%% *}
|
||||
_PY_VER=${_info##* }
|
||||
|
||||
if [ "$_VENV_ARCH" = "x86_64" ]; then
|
||||
echo " WARNING: venv was created with an x86_64 (Rosetta) Python on Apple Silicon."
|
||||
echo " Recreating venv with native arm64 Python ${PYTHON_VERSION}..."
|
||||
rm -rf "$VENV_DIR"
|
||||
run_install_cmd "recreate venv (arm64)" uv venv "$VENV_DIR" \
|
||||
--python "cpython-${PYTHON_VERSION}-macos-aarch64-none"
|
||||
if [ -x "$VENV_DIR/bin/python" ]; then
|
||||
: > "$VENV_DIR/.unsloth-studio-owned" 2>/dev/null || true
|
||||
fi
|
||||
# Re-inspect: the recreated arm64 venv may still be 3.13.8.
|
||||
_info=$(_inspect_venv)
|
||||
_VENV_ARCH=${_info%% *}
|
||||
_PY_VER=${_info##* }
|
||||
fi
|
||||
|
||||
if [ "$_PY_VER" = "3.13.8" ]; then
|
||||
echo " WARNING: Python 3.13.8 has a known torch import bug."
|
||||
echo " Recreating venv with Python 3.12..."
|
||||
rm -rf "$VENV_DIR"
|
||||
PYTHON_VERSION="3.12"
|
||||
run_install_cmd "recreate venv" uv venv "$VENV_DIR" --python "$PYTHON_VERSION"
|
||||
run_install_cmd "recreate venv" uv venv "$VENV_DIR" \
|
||||
--python "cpython-${PYTHON_VERSION}-macos-aarch64-none"
|
||||
if [ -x "$VENV_DIR/bin/python" ]; then
|
||||
: > "$VENV_DIR/.unsloth-studio-owned" 2>/dev/null || true
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -563,6 +563,80 @@ else
|
|||
FAIL=$((FAIL + 1))
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=== Apple Silicon x86_64 (Rosetta) venv rebuild ==="
|
||||
|
||||
# Extract the real guard block from install.sh so we exercise the shipped logic
|
||||
# (comment header down to its column-0 closing fi).
|
||||
_GUARD_FILE=$(mktemp)
|
||||
awk '/Guard against two independent Apple Silicon venv problems/{f=1} f{print} f&&/^fi$/{exit}' \
|
||||
"$INSTALL_SH" > "$_GUARD_FILE"
|
||||
|
||||
if [ ! -s "$_GUARD_FILE" ]; then
|
||||
echo " FAIL: could not extract Apple Silicon venv guard from install.sh"
|
||||
FAIL=$((FAIL + 1))
|
||||
else
|
||||
# Runner: stub uv (via run_install_cmd) + a fake venv python, source the
|
||||
# guard, then print "<final_arch> <final_ver> | <recreate_selectors>".
|
||||
# The stub maps a uv arm64 selector to the interpreter uv would produce:
|
||||
# cpython-3.12-* -> arm64 3.12.7, cpython-3.13-* -> arm64 $REBUILD_313_VERSION.
|
||||
_RUNNER=$(mktemp)
|
||||
cat > "$_RUNNER" << 'RUNNER_EOF'
|
||||
GUARD="$1"; VENV_DIR="$2"
|
||||
make_python() { # dir machine version
|
||||
mkdir -p "$1/bin"
|
||||
printf '#!/usr/bin/env bash\necho "%s %s"\n' "$2" "$3" > "$1/bin/python"
|
||||
chmod +x "$1/bin/python"
|
||||
}
|
||||
RECREATE_LOG=$(mktemp); : > "$RECREATE_LOG"
|
||||
run_install_cmd() {
|
||||
shift # drop the human label
|
||||
if [ "$1" = "uv" ] && [ "$2" = "venv" ]; then
|
||||
dir="$3"; sel=""; shift 3
|
||||
while [ $# -gt 0 ]; do [ "$1" = "--python" ] && { sel="$2"; shift; }; shift; done
|
||||
echo "$sel" >> "$RECREATE_LOG"
|
||||
case "$sel" in
|
||||
*3.12-macos-aarch64*) make_python "$dir" arm64 "3.12.7" ;;
|
||||
*3.13-macos-aarch64*) make_python "$dir" arm64 "${REBUILD_313_VERSION:-3.13.3}" ;;
|
||||
*) make_python "$dir" arm64 "$sel" ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
[ "$INIT_ARCH" != none ] && make_python "$VENV_DIR" "$INIT_ARCH" "$INIT_VER"
|
||||
PYTHON_VERSION="3.13"
|
||||
. "$GUARD" >&2 # guard's user-facing echoes go to stderr; keep stdout clean
|
||||
final="none"; [ -x "$VENV_DIR/bin/python" ] && final="$("$VENV_DIR/bin/python" -c x)"
|
||||
printf '%s | %s\n' "$final" "$(paste -sd, "$RECREATE_LOG" 2>/dev/null)"
|
||||
rm -f "$RECREATE_LOG"
|
||||
RUNNER_EOF
|
||||
|
||||
_run_guard() { # _USER_PYTHON OS _ARCH INIT_ARCH INIT_VER REBUILD_313_VERSION
|
||||
_vd=$(mktemp -d)
|
||||
env _USER_PYTHON="$1" OS="$2" _ARCH="$3" INIT_ARCH="$4" INIT_VER="$5" \
|
||||
REBUILD_313_VERSION="$6" bash "$_RUNNER" "$_GUARD_FILE" "$_vd/venv"
|
||||
rm -rf "$_vd"
|
||||
}
|
||||
|
||||
assert_eq "clean arm64 venv left untouched" \
|
||||
"arm64 3.13.3 | " "$(_run_guard '' macos arm64 arm64 3.13.3 '')"
|
||||
assert_eq "x86_64 venv rebuilt as arm64" \
|
||||
"arm64 3.13.3 | cpython-3.13-macos-aarch64-none" \
|
||||
"$(_run_guard '' macos arm64 x86_64 3.13.3 '')"
|
||||
assert_eq "x86_64 venv that lands on 3.13.8 is rebuilt then downgraded to 3.12" \
|
||||
"arm64 3.12.7 | cpython-3.13-macos-aarch64-none,cpython-3.12-macos-aarch64-none" \
|
||||
"$(_run_guard '' macos arm64 x86_64 3.13.3 3.13.8)"
|
||||
assert_eq "arm64 3.13.8 venv downgraded to 3.12" \
|
||||
"arm64 3.12.7 | cpython-3.12-macos-aarch64-none" \
|
||||
"$(_run_guard '' macos arm64 arm64 3.13.8 '')"
|
||||
assert_eq "--python override skips the guard entirely" \
|
||||
"x86_64 3.13.3 | " "$(_run_guard 3.11 macos arm64 x86_64 3.13.3 '')"
|
||||
assert_eq "x86_64 host (Intel/Rosetta shell) is a no-op here" \
|
||||
"x86_64 3.13.3 | " "$(_run_guard '' macos x86_64 x86_64 3.13.3 '')"
|
||||
|
||||
rm -f "$_RUNNER"
|
||||
fi
|
||||
rm -f "$_GUARD_FILE"
|
||||
|
||||
echo ""
|
||||
echo "Results: $PASS passed, $FAIL failed"
|
||||
[ "$FAIL" -eq 0 ] || exit 1
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue