From a3a0cb1606f1041810dba339a1e91ae68d3cb5e2 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Fri, 29 May 2026 05:09:20 -0700 Subject: [PATCH] studio/setup.sh: cope with fresh CUDA toolkits like 13.3 (#5826) * Studio setup.sh: cope with fresh CUDA toolkits like 13.3 CUDA 13.3 shipped today. Three loose ends in studio/setup.sh surfaced during the llama.cpp build path: 1. setup.ps1 already aborts cleanly when the CUDA toolkit is below llama.cpp's minimum (12.4) via #4517, but setup.sh still hit the generic cmake failure described in #4437. Added a min-version check that downgrades to a CPU build for nvcc < 12.4 with a clear message pointing to the toolkit archive. 2. The first day a new CUDA toolkit ships, its host-compiler whitelist lags whatever gcc/clang the distro is on, so nvcc rejects the host compiler with a wall of "#error -- unsupported GNU version" before any real compile runs. NVCC_PREPEND_FLAGS now carries -allow-unsupported-compiler so the build moves on instead. 3. The Linux CUDA/ROCm configure failure path had no symmetry with the macOS Metal fallback: a single nvcc failure left BUILD_OK=false and no llama.cpp at all. Generalised the existing Metal -> CPU fallback to cover any GPU_BACKEND, so a CUDA configure or build failure now transparently retries with the CPU args and the user still ends up with a working llama-server. Pulled the version probe out into _nvcc_meets_llama_minimum so it can be unit-tested. Added tests/sh/test_nvcc_meets_llama_minimum.sh and two extra cases in tests/sh/test_get_torch_index_url.sh covering the legacy "CUDA Version: 13.3" header (driver-reported) and the future 13.7 case. Wired the new test into tests/run_all.sh and the studio-backend CI workflow. * tests: relax pr4562 regression to allow generic GPU fallback label * studio tests: assert setup.sh exports NVCC_PREPEND_FLAGS=-allow-unsupported-compiler The -allow-unsupported-compiler flag is the core of the fresh-CUDA-toolkit fix (it lets nvcc accept a host gcc/clang newer than its release-time whitelist, so CUDA 13.3 day-one builds do not abort on '#error -- unsupported GNU version'), but it had no automated coverage. Add a source-pattern test asserting the flag is present, delivered via NVCC_PREPEND_FLAGS so it also covers cmake's CUDA compiler-id probe, and kept out of CMAKE_ARGS for bash word-splitting safety. * studio/setup.ps1: allow unsupported host compiler for CUDA build (Windows parity) Mirror the Linux setup.sh headline fix from this PR on Windows. A freshly released CUDA toolkit ships with a host-compiler whitelist that lags the installed toolchain, so nvcc can reject the host with "#error -- unsupported Microsoft Visual Studio version!" before any real compile runs (the MSVC analogue of the gcc wall the Linux side hit on CUDA 13.3). Set NVCC_PREPEND_FLAGS=-allow-unsupported-compiler in the CUDA build branch so both cmake's configure-time CUDA compiler-id probe and the cmake --build step proceed. The flag disables the host version check only and is a no-op when the compiler is already supported. Set via the process environment (not the $CmakeArgs array), after the Refresh-Environment calls that re-sanitize CUDA env vars, and appended idempotently to any value the user already set. Validated with PowerShell 7.6.2: full setup.ps1 AST parse is clean and the snippet is idempotent (empty -> set, existing -> append once, no duplicate). Needs real Windows + CUDA CI to exercise the actual nvcc/MSVC build. Adds test_setup_ps1_exports_allow_unsupported_compiler asserting the flag is present, env-delivered, kept out of $CmakeArgs, and scoped to the CUDA-on branch. * studio: tighten code comments added in this PR Shorten the verbose multi-line comments and test docstrings introduced by this PR (setup.sh, setup.ps1, and the shell/python tests) to be succinct while preserving the rationale. No code or test-assertion changes. --- .github/workflows/studio-backend-ci.yml | 1 + studio/setup.ps1 | 11 ++ studio/setup.sh | 123 ++++++++++++++----- tests/run_all.sh | 1 + tests/sh/test_get_torch_index_url.sh | 12 ++ tests/sh/test_nvcc_meets_llama_minimum.sh | 121 ++++++++++++++++++ tests/studio/install/test_pr4562_bugfixes.py | 62 ++++++++-- 7 files changed, 292 insertions(+), 39 deletions(-) create mode 100755 tests/sh/test_nvcc_meets_llama_minimum.sh diff --git a/.github/workflows/studio-backend-ci.yml b/.github/workflows/studio-backend-ci.yml index ee5bbe8633..88c7344683 100644 --- a/.github/workflows/studio-backend-ci.yml +++ b/.github/workflows/studio-backend-ci.yml @@ -222,6 +222,7 @@ jobs: for s in \ tests/sh/test_get_torch_index_url.sh \ tests/sh/test_mac_intel_compat.sh \ + tests/sh/test_nvcc_meets_llama_minimum.sh \ tests/sh/test_tauri_install_exit_order.sh \ tests/sh/test_torch_constraint.sh; do echo "::group::$s" diff --git a/studio/setup.ps1 b/studio/setup.ps1 index 8950627ae8..afa00409fb 100644 --- a/studio/setup.ps1 +++ b/studio/setup.ps1 @@ -2589,6 +2589,17 @@ if (-not $NeedLlamaSourceBuild) { # CUDA flags -- only if GPU available, otherwise explicitly disable if ($HasNvidiaSmi -and $NvccPath) { $CmakeArgs += '-DGGML_CUDA=ON' + # Accept a host MSVC newer than nvcc's whitelist; a fresh toolkit + # (e.g. CUDA 13.3) otherwise aborts with "#error -- unsupported + # Microsoft Visual Studio version!". Mirrors the Linux fix. Via env + # (covers the configure probe + build), after Refresh-Environment, idempotent. + $nvccAllowFlag = '-allow-unsupported-compiler' + if ([string]::IsNullOrEmpty($env:NVCC_PREPEND_FLAGS)) { + $env:NVCC_PREPEND_FLAGS = $nvccAllowFlag + } elseif ($env:NVCC_PREPEND_FLAGS -notlike "*$nvccAllowFlag*") { + $env:NVCC_PREPEND_FLAGS = "$($env:NVCC_PREPEND_FLAGS) $nvccAllowFlag" + } + substep "NVCC_PREPEND_FLAGS = $env:NVCC_PREPEND_FLAGS" $CmakeArgs += "-DCUDAToolkit_ROOT=$CudaToolkitRoot" $CmakeArgs += "-DCUDA_TOOLKIT_ROOT_DIR=$CudaToolkitRoot" $CmakeArgs += "-DCMAKE_CUDA_COMPILER=$NvccPath" diff --git a/studio/setup.sh b/studio/setup.sh index e8fc8f6f13..f2395255c0 100755 --- a/studio/setup.sh +++ b/studio/setup.sh @@ -130,6 +130,30 @@ run_quiet_no_exit() { _run_quiet return "$@" } +_nvcc_meets_llama_minimum() { + # Echo "ok|too_old|unknown" then the parsed "X.Y" version, one per line. + # llama.cpp needs CUDA toolkit >= 12.4 (#4437; setup.ps1 aborts via #4517). + _nvcc_bin=$1 + [ -n "$_nvcc_bin" ] || { echo "unknown"; echo ""; return 0; } + _raw=$("$_nvcc_bin" --version 2>/dev/null \ + | sed -n 's/.*release \([0-9][0-9]*\.[0-9][0-9]*\).*/\1/p' \ + | head -1) + if [ -z "$_raw" ]; then + echo "unknown"; echo ""; return 0 + fi + _maj=${_raw%%.*} + _min_raw=${_raw#*.} + _min=${_min_raw%%.*} + if [ "$_maj" -lt 12 ] 2>/dev/null; then + echo "too_old" + elif [ "$_maj" -eq 12 ] && [ "$_min" -lt 4 ] 2>/dev/null; then + echo "too_old" + else + echo "ok" + fi + echo "$_raw" +} + print_llama_error_log() { local log_file=$1 [ -s "$log_file" ] || return 0 @@ -1005,32 +1029,52 @@ else CPU_FALLBACK_CMAKE_ARGS="$CPU_FALLBACK_CMAKE_ARGS -DGGML_METAL=OFF" _TRY_METAL_CPU_FALLBACK=true elif [ -n "$NVCC_PATH" ]; then - CMAKE_ARGS="$CMAKE_ARGS -DGGML_CUDA=ON" + # Returns "ok|too_old|unknown\nX.Y" on stdout. + _NVCC_CHECK="$(_nvcc_meets_llama_minimum "$NVCC_PATH")" + _NVCC_STATUS="$(printf '%s\n' "$_NVCC_CHECK" | sed -n '1p')" + _NVCC_VER="$(printf '%s\n' "$_NVCC_CHECK" | sed -n '2p')" - CUDA_ARCHS="" - if command -v nvidia-smi &>/dev/null; then - _raw_caps=$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader 2>/dev/null || true) - while IFS= read -r _cap; do - _cap=$(echo "$_cap" | tr -d '[:space:]') - if [[ "$_cap" =~ ^([0-9]+)\.([0-9]+)$ ]]; then - _arch="${BASH_REMATCH[1]}${BASH_REMATCH[2]}" - # Append if not already present - case ";$CUDA_ARCHS;" in - *";$_arch;"*) ;; - *) CUDA_ARCHS="${CUDA_ARCHS:+$CUDA_ARCHS;}$_arch" ;; - esac - fi - done <<< "$_raw_caps" - fi - - if [ -n "$CUDA_ARCHS" ]; then - CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CUDA_ARCHITECTURES=${CUDA_ARCHS}" - _BUILD_DESC="building (CUDA, sm_${CUDA_ARCHS//;/+sm_})" + if [ "$_NVCC_STATUS" = "too_old" ]; then + substep "CUDA toolkit $_NVCC_VER is below llama.cpp minimum (12.4)." "$C_ERR" + substep "install a newer CUDA toolkit: https://developer.nvidia.com/cuda-toolkit-archive" "$C_WARN" + substep "falling back to CPU llama.cpp build for this run." "$C_WARN" + NVCC_PATH="" + GPU_BACKEND="" + _BUILD_DESC="building (CPU, CUDA toolkit < 12.4)" else - _BUILD_DESC="building (CUDA)" - fi + CMAKE_ARGS="$CMAKE_ARGS -DGGML_CUDA=ON" - CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CUDA_FLAGS=--threads=0" + CUDA_ARCHS="" + if command -v nvidia-smi &>/dev/null; then + _raw_caps=$(nvidia-smi --query-gpu=compute_cap --format=csv,noheader 2>/dev/null || true) + while IFS= read -r _cap; do + _cap=$(echo "$_cap" | tr -d '[:space:]') + if [[ "$_cap" =~ ^([0-9]+)\.([0-9]+)$ ]]; then + _arch="${BASH_REMATCH[1]}${BASH_REMATCH[2]}" + # Append if not already present + case ";$CUDA_ARCHS;" in + *";$_arch;"*) ;; + *) CUDA_ARCHS="${CUDA_ARCHS:+$CUDA_ARCHS;}$_arch" ;; + esac + fi + done <<< "$_raw_caps" + fi + + if [ -n "$CUDA_ARCHS" ]; then + CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CUDA_ARCHITECTURES=${CUDA_ARCHS}" + _BUILD_DESC="building (CUDA, sm_${CUDA_ARCHS//;/+sm_})" + else + _BUILD_DESC="building (CUDA)" + fi + + CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_CUDA_FLAGS=--threads=0" + + # Accept a host gcc/clang newer than nvcc's whitelist; a fresh + # toolkit (e.g. CUDA 13.3) otherwise aborts with "#error -- + # unsupported GNU version". Via env, not CMAKE_ARGS, to avoid + # word-splitting. + export NVCC_PREPEND_FLAGS="${NVCC_PREPEND_FLAGS:+$NVCC_PREPEND_FLAGS }-allow-unsupported-compiler" + fi elif [ "$GPU_BACKEND" = "rocm" ]; then # Resolve hipcc symlinks to find the real ROCm root _HIPCC_REAL="$(readlink -f "$ROCM_HIPCC" 2>/dev/null || printf '%s' "$ROCM_HIPCC")" @@ -1100,14 +1144,29 @@ else CMAKE_GENERATOR_ARGS="-G Ninja" fi - if ! run_quiet_no_exit "cmake llama.cpp" cmake $CMAKE_GENERATOR_ARGS -S "$_BUILD_TMP" -B "$_BUILD_TMP/build" $CMAKE_ARGS; then + # GPU label for the CPU-fallback message: Metal, else GPU_BACKEND + # (cuda/rocm). Empty on a bare CPU build (nothing to fall back from). + _gpu_fallback_label() { if [ "$_TRY_METAL_CPU_FALLBACK" = true ]; then + echo "Metal" + elif [ -n "$GPU_BACKEND" ]; then + printf '%s' "$GPU_BACKEND" | tr '[:lower:]' '[:upper:]' + fi + } + + if ! run_quiet_no_exit "cmake llama.cpp" cmake $CMAKE_GENERATOR_ARGS -S "$_BUILD_TMP" -B "$_BUILD_TMP/build" $CMAKE_ARGS; then + _FB_LABEL="$(_gpu_fallback_label)" + if [ -n "$_FB_LABEL" ]; then _TRY_METAL_CPU_FALLBACK=false - substep "Metal configure failed; retrying CPU build..." "$C_WARN" + substep "$_FB_LABEL configure failed; retrying CPU build..." "$C_WARN" rm -rf "$_BUILD_TMP/build" - run_quiet_no_exit "cmake llama.cpp (cpu fallback)" cmake $CMAKE_GENERATOR_ARGS -S "$_BUILD_TMP" -B "$_BUILD_TMP/build" $CPU_FALLBACK_CMAKE_ARGS || BUILD_OK=false - if [ "$BUILD_OK" = true ]; then - _BUILD_DESC="building (CPU fallback)" + if run_quiet_no_exit "cmake llama.cpp (cpu fallback)" cmake $CMAKE_GENERATOR_ARGS -S "$_BUILD_TMP" -B "$_BUILD_TMP/build" $CPU_FALLBACK_CMAKE_ARGS; then + _BUILD_DESC="building (CPU fallback after $_FB_LABEL configure failed)" + # Now configured for CPU; clear GPU_BACKEND so a later + # build-step failure won't re-enter fallback on this config. + GPU_BACKEND="" + else + BUILD_OK=false fi else BUILD_OK=false @@ -1117,12 +1176,14 @@ else if [ "$BUILD_OK" = true ]; then if ! run_quiet_no_exit "build llama-server" cmake --build "$_BUILD_TMP/build" --config Release --target llama-server -j"$NCPU"; then - if [ "$_TRY_METAL_CPU_FALLBACK" = true ]; then + _FB_LABEL="$(_gpu_fallback_label)" + if [ -n "$_FB_LABEL" ]; then _TRY_METAL_CPU_FALLBACK=false - substep "Metal build failed; retrying CPU build..." "$C_WARN" + substep "$_FB_LABEL build failed; retrying CPU build..." "$C_WARN" rm -rf "$_BUILD_TMP/build" if run_quiet_no_exit "cmake llama.cpp (cpu fallback)" cmake $CMAKE_GENERATOR_ARGS -S "$_BUILD_TMP" -B "$_BUILD_TMP/build" $CPU_FALLBACK_CMAKE_ARGS; then - _BUILD_DESC="building (CPU fallback)" + _BUILD_DESC="building (CPU fallback after $_FB_LABEL build failed)" + GPU_BACKEND="" run_quiet_no_exit "build llama-server (cpu fallback)" cmake --build "$_BUILD_TMP/build" --config Release --target llama-server -j"$NCPU" || BUILD_OK=false else BUILD_OK=false diff --git a/tests/run_all.sh b/tests/run_all.sh index 6525263d8f..d84c930392 100755 --- a/tests/run_all.sh +++ b/tests/run_all.sh @@ -8,6 +8,7 @@ echo "=== Bash tests ===" sh "$TESTS_DIR/sh/test_get_torch_index_url.sh" sh "$TESTS_DIR/sh/test_mac_intel_compat.sh" sh "$TESTS_DIR/sh/test_torch_constraint.sh" +sh "$TESTS_DIR/sh/test_nvcc_meets_llama_minimum.sh" echo "" echo "=== Python tests ===" diff --git a/tests/sh/test_get_torch_index_url.sh b/tests/sh/test_get_torch_index_url.sh index a9fafa5359..89ec32fba5 100755 --- a/tests/sh/test_get_torch_index_url.sh +++ b/tests/sh/test_get_torch_index_url.sh @@ -320,6 +320,18 @@ _result=$(run_func "$_dir") assert_eq "CUDA UMD Version 11.8 -> cu118" "https://download.pytorch.org/whl/cu118" "$_result" rm -rf "$_dir" +# 32) Driver-reported "CUDA Version: 13.3" (legacy header) -> cu130. +_dir=$(make_mock_smi "13.3") +_result=$(run_func "$_dir") +assert_eq "CUDA Version 13.3 -> cu130" "https://download.pytorch.org/whl/cu130" "$_result" +rm -rf "$_dir" + +# 33) "CUDA Version: 13.7" -> cu130 (until a cu137 wheel index exists). +_dir=$(make_mock_smi "13.7") +_result=$(run_func "$_dir") +assert_eq "CUDA Version 13.7 -> cu130" "https://download.pytorch.org/whl/cu130" "$_result" +rm -rf "$_dir" + rm -f "$_FUNC_FILE" rm -rf "$_FAKE_SMI_DIR" rm -rf "$_TOOLS_DIR" diff --git a/tests/sh/test_nvcc_meets_llama_minimum.sh b/tests/sh/test_nvcc_meets_llama_minimum.sh new file mode 100755 index 0000000000..f614c0403a --- /dev/null +++ b/tests/sh/test_nvcc_meets_llama_minimum.sh @@ -0,0 +1,121 @@ +#!/bin/bash +# Unit tests for _nvcc_meets_llama_minimum() from studio/setup.sh. +# llama.cpp needs CUDA toolkit >= 12.4 (#4437); setup.ps1 aborts via #4517, +# the Linux side was silent until this fix. +set -e + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SETUP_SH="$SCRIPT_DIR/../../studio/setup.sh" +PASS=0 +FAIL=0 + +# Extract just the helper function. The sed range is the same pattern the +# install.sh tests use. +_FUNC_FILE=$(mktemp) +sed -n '/^_nvcc_meets_llama_minimum()/,/^}/p' "$SETUP_SH" > "$_FUNC_FILE" + +assert_eq() { + _label="$1"; _expected="$2"; _actual="$3" + if [ "$_actual" = "$_expected" ]; then + echo " PASS: $_label" + PASS=$((PASS + 1)) + else + echo " FAIL: $_label (expected '$_expected', got '$_actual')" + FAIL=$((FAIL + 1)) + fi +} + +# Fake nvcc printing "release X.Y" in the canonical nvcc -V layout (the helper +# greps for "release X.Y", stable across CUDA 9.x-13.x). +make_mock_nvcc() { + _ver=$1 + _dir=$(mktemp -d) + cat > "$_dir/nvcc" < ok +_bin=$(make_mock_nvcc "12.4") +_out=$(run_check "$_bin") +assert_eq "12.4 status" "ok" "$(echo "$_out" | sed -n '1p')" +assert_eq "12.4 version" "12.4" "$(echo "$_out" | sed -n '2p')" +rm -rf "$(dirname "$_bin")" + +# 2) CUDA 12.3 is the highest version that should be rejected. +_bin=$(make_mock_nvcc "12.3") +_out=$(run_check "$_bin") +assert_eq "12.3 status" "too_old" "$(echo "$_out" | sed -n '1p')" +rm -rf "$(dirname "$_bin")" + +# 3) CUDA 12.1 (matches the original bug report in #4437). +_bin=$(make_mock_nvcc "12.1") +_out=$(run_check "$_bin") +assert_eq "12.1 status" "too_old" "$(echo "$_out" | sed -n '1p')" +rm -rf "$(dirname "$_bin")" + +# 4) CUDA 11.8 -> too_old (anything < 12.0 is rejected). +_bin=$(make_mock_nvcc "11.8") +_out=$(run_check "$_bin") +assert_eq "11.8 status" "too_old" "$(echo "$_out" | sed -n '1p')" +rm -rf "$(dirname "$_bin")" + +# 5) CUDA 12.8 -> ok (mid-range supported). +_bin=$(make_mock_nvcc "12.8") +_out=$(run_check "$_bin") +assert_eq "12.8 status" "ok" "$(echo "$_out" | sed -n '1p')" +rm -rf "$(dirname "$_bin")" + +# 6) CUDA 13.0 -> ok. +_bin=$(make_mock_nvcc "13.0") +_out=$(run_check "$_bin") +assert_eq "13.0 status" "ok" "$(echo "$_out" | sed -n '1p')" +rm -rf "$(dirname "$_bin")" + +# 7) CUDA 13.3 -> ok (the freshly shipped toolkit this fix targets). +_bin=$(make_mock_nvcc "13.3") +_out=$(run_check "$_bin") +assert_eq "13.3 status" "ok" "$(echo "$_out" | sed -n '1p')" +assert_eq "13.3 version" "13.3" "$(echo "$_out" | sed -n '2p')" +rm -rf "$(dirname "$_bin")" + +# 8) Future CUDA 14.0 -> ok (no upper bound). +_bin=$(make_mock_nvcc "14.0") +_out=$(run_check "$_bin") +assert_eq "14.0 status" "ok" "$(echo "$_out" | sed -n '1p')" +rm -rf "$(dirname "$_bin")" + +# 9) Empty argument -> unknown (defensive; never block the build on detection). +_out=$(run_check "") +assert_eq "empty path status" "unknown" "$(echo "$_out" | sed -n '1p')" + +# 10) Mock nvcc that prints garbage -> unknown. +_dir=$(mktemp -d) +cat > "$_dir/nvcc" <<'MOCK' +#!/bin/sh +echo "totally not nvcc output" +MOCK +chmod +x "$_dir/nvcc" +_out=$(run_check "$_dir/nvcc") +assert_eq "garbage output status" "unknown" "$(echo "$_out" | sed -n '1p')" +rm -rf "$_dir" + +rm -f "$_FUNC_FILE" + +echo "" +echo "Results: $PASS passed, $FAIL failed" +[ "$FAIL" -eq 0 ] || exit 1 diff --git a/tests/studio/install/test_pr4562_bugfixes.py b/tests/studio/install/test_pr4562_bugfixes.py index 4a309036d6..34b144a905 100644 --- a/tests/studio/install/test_pr4562_bugfixes.py +++ b/tests/studio/install/test_pr4562_bugfixes.py @@ -727,16 +727,13 @@ class TestSourceCodePatterns: assert "-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON" in content def test_setup_sh_macos_metal_configure_has_cpu_fallback(self): - """If Metal configure or build fails, setup should retry with CPU fallback.""" + """If Metal/CUDA/ROCm configure or build fails, setup retries a CPU + build. PR #5826 generalised the Metal-only wording via $_FB_LABEL; this + check stays label-agnostic so new GPU backends don't require edits.""" content = SETUP_SH.read_text() assert "_TRY_METAL_CPU_FALLBACK=true" in content - assert ( - 'substep "Metal configure failed; retrying CPU build..." "$C_WARN"' - in content - ) - assert ( - 'substep "Metal build failed; retrying CPU build..." "$C_WARN"' in content - ) + assert 'configure failed; retrying CPU build..." "$C_WARN"' in content + assert 'build failed; retrying CPU build..." "$C_WARN"' in content assert 'run_quiet_no_exit "cmake llama.cpp (cpu fallback)"' in content assert "-DGGML_METAL=OFF" in content # _TRY_METAL_CPU_FALLBACK must be reset to false in both fallback branches @@ -745,6 +742,55 @@ class TestSourceCodePatterns: "_TRY_METAL_CPU_FALLBACK=false should appear at least 3 times " "(init + configure fallback + build fallback)" ) + # The fallback helper must exist and Metal must reach it via the + # _TRY_METAL_CPU_FALLBACK shortcut so the macOS path stays covered. + assert "_gpu_fallback_label()" in content + assert 'echo "Metal"' in content + + def test_setup_sh_exports_allow_unsupported_compiler(self): + """Headline fix for PR #5826: a fresh CUDA toolkit's host-compiler + whitelist lags the distro gcc/clang, so nvcc rejects the host with + "#error -- unsupported GNU version". setup.sh exports + NVCC_PREPEND_FLAGS=-allow-unsupported-compiler (via env, not CMAKE_ARGS, + for word-splitting safety) so the build and compiler-id probe proceed.""" + content = SETUP_SH.read_text() + assert "-allow-unsupported-compiler" in content + # Delivered via NVCC_PREPEND_FLAGS (covers the configure-time compiler + # probe too), not embedded in the word-split CMAKE_ARGS string. + assert "export NVCC_PREPEND_FLAGS=" in content + cmake_args_lines = [ + line for line in content.splitlines() if "CMAKE_ARGS=" in line + ] + assert all( + "-allow-unsupported-compiler" not in line for line in cmake_args_lines + ), "flag must stay out of CMAKE_ARGS (bash word-splitting safety)" + + def test_setup_ps1_exports_allow_unsupported_compiler(self): + """Windows parity for the PR #5826 fix: a fresh CUDA toolkit's whitelist + also lags MSVC, so nvcc can reject the host with "#error -- unsupported + Microsoft Visual Studio version!". setup.ps1 sets + NVCC_PREPEND_FLAGS=-allow-unsupported-compiler in the CUDA branch (via + env, out of $CmakeArgs) so the configure probe + build proceed.""" + content = SETUP_PS1.read_text() + assert "-allow-unsupported-compiler" in content + # Delivered via the process environment, not the $CmakeArgs array, so it + # reaches both the configure-time compiler probe and `cmake --build`. + assert "$env:NVCC_PREPEND_FLAGS" in content + cmake_args_lines = [ + line for line in content.splitlines() if "$CmakeArgs +=" in line + ] + assert all( + "-allow-unsupported-compiler" not in line for line in cmake_args_lines + ), "flag must not be pushed into the $CmakeArgs array" + # Must be scoped to the CUDA branch (guarded by the GPU/nvcc check), + # not set unconditionally for CPU-only builds. + flag_idx = content.index("-allow-unsupported-compiler") + cuda_guard_idx = content.index("if ($HasNvidiaSmi -and $NvccPath)") + cuda_disable_idx = content.index("'-DGGML_CUDA=OFF'") + assert cuda_guard_idx < flag_idx < cuda_disable_idx, ( + "NVCC_PREPEND_FLAGS must be set inside the CUDA-on branch, " + "before the GGML_CUDA=OFF (CPU) branch" + ) def test_macos_arm64_cpu_fallback_args_exclude_rpath(self): """CPU fallback args must NOT contain Metal-only RPATH flags at runtime."""