Compare commits
1 commit
main
...
check/wind
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1c3b3b36bd |
2 changed files with 174 additions and 40 deletions
143
install.ps1
143
install.ps1
|
|
@ -1213,24 +1213,124 @@ shell.Run cmd, 0, False
|
||||||
substep "Training and GPU inference require an NVIDIA GPU with drivers installed." "Yellow"
|
substep "Training and GPU inference require an NVIDIA GPU with drivers installed." "Yellow"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Choose the correct PyTorch index URL based on driver CUDA version ──
|
# ── Detect installed CUDA Toolkit version ──
|
||||||
|
# Returns "12.9" / "13.0" / $null. Checks CUDA_PATH and the standard
|
||||||
|
# toolkit install root. The toolkit version is the actual userland CUDA
|
||||||
|
# available to PyTorch; nvidia-smi only reports the driver's *max
|
||||||
|
# supported* CUDA, which can be a release ahead of the userland.
|
||||||
|
function Get-CudaToolkitVersion {
|
||||||
|
$candidates = @()
|
||||||
|
foreach ($scope in @('Process','Machine','User')) {
|
||||||
|
$cudaRoot = [Environment]::GetEnvironmentVariable('CUDA_PATH', $scope)
|
||||||
|
if ($cudaRoot) { $candidates += (Join-Path $cudaRoot 'bin\nvcc.exe') }
|
||||||
|
}
|
||||||
|
$toolkitBase = 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA'
|
||||||
|
if (Test-Path -LiteralPath $toolkitBase) {
|
||||||
|
$highest = Get-ChildItem -Directory -LiteralPath $toolkitBase -ErrorAction SilentlyContinue |
|
||||||
|
Where-Object { $_.Name -match '^v(\d+)\.(\d+)' } |
|
||||||
|
Sort-Object { [version]($_.Name -replace '^v','') } -Descending |
|
||||||
|
Select-Object -First 1
|
||||||
|
if ($highest) { $candidates += (Join-Path $highest.FullName 'bin\nvcc.exe') }
|
||||||
|
}
|
||||||
|
$onPath = Get-Command nvcc -ErrorAction SilentlyContinue
|
||||||
|
if ($onPath) { $candidates += $onPath.Source }
|
||||||
|
foreach ($nvcc in $candidates) {
|
||||||
|
if ($nvcc -and (Test-Path -LiteralPath $nvcc)) {
|
||||||
|
try {
|
||||||
|
$verOut = & $nvcc --version 2>&1 | Out-String
|
||||||
|
if ($verOut -match 'release\s+(\d+\.\d+)') { return $Matches[1] }
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Read driver's max CUDA from nvidia-smi (bound on toolkit install) ──
|
||||||
|
$DriverMaxCuda = $null
|
||||||
|
if ($NvidiaSmiExe) {
|
||||||
|
try {
|
||||||
|
$smiOut = & $NvidiaSmiExe 2>&1 | Out-String
|
||||||
|
if ($smiOut -match 'CUDA Version:\s+(\d+\.\d+)') { $DriverMaxCuda = $Matches[1] }
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Install CUDA Toolkit if missing, so Get-TorchIndexUrl can pick the ──
|
||||||
|
# PyTorch wheel from the *userland* CUDA version instead of nvidia-smi's
|
||||||
|
# driver-max ceiling. Mirrors phase 1e of setup.ps1; setup.ps1 still
|
||||||
|
# runs the full CUDA <-> VS Build Tools integration later. Bounded by
|
||||||
|
# the driver max so we never install a toolkit the driver can't load.
|
||||||
|
function Install-CudaToolkitIfMissing {
|
||||||
|
param([Parameter(Mandatory = $true)][string]$DriverMaxCuda)
|
||||||
|
if (-not (Get-Command winget -ErrorAction SilentlyContinue)) {
|
||||||
|
substep "winget unavailable; skipping CUDA Toolkit install (studio setup will retry later)." "Yellow"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
$drMajor = [int]$DriverMaxCuda.Split('.')[0]
|
||||||
|
$drMinor = [int]$DriverMaxCuda.Split('.')[1]
|
||||||
|
$available = @()
|
||||||
|
try {
|
||||||
|
$rawOutput = winget show Nvidia.CUDA --versions --accept-source-agreements 2>&1 | Out-String
|
||||||
|
foreach ($line in $rawOutput -split "`n") {
|
||||||
|
$line = $line.Trim()
|
||||||
|
if ($line -match '^\d+\.\d+') { $available += $line }
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
$best = $null
|
||||||
|
foreach ($ver in $available) {
|
||||||
|
$parts = $ver.Split('.')
|
||||||
|
$vMajor = [int]$parts[0]; $vMinor = [int]$parts[1]
|
||||||
|
if ($vMajor -lt $drMajor -or ($vMajor -eq $drMajor -and $vMinor -le $drMinor)) {
|
||||||
|
# winget show --versions returns the list in descending order,
|
||||||
|
# so the first compatible entry is the highest <= driver max.
|
||||||
|
$best = $ver
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (-not $best) {
|
||||||
|
substep "no CUDA Toolkit version <= driver $DriverMaxCuda available in winget (studio setup will retry later)." "Yellow"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
substep "no CUDA Toolkit found; installing CUDA $best via winget (~3 GB, several minutes)..."
|
||||||
|
$prevEAP = $ErrorActionPreference
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
try {
|
||||||
|
Invoke-InstallCommand { winget install --id=Nvidia.CUDA --version=$best -e --source winget --accept-package-agreements --accept-source-agreements } | Out-Null
|
||||||
|
} catch {}
|
||||||
|
$ErrorActionPreference = $prevEAP
|
||||||
|
Refresh-SessionPath
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($HasNvidiaSmi -and -not $SkipTorch -and $DriverMaxCuda -and -not (Get-CudaToolkitVersion)) {
|
||||||
|
Write-TauriLog "STEP" "Installing CUDA Toolkit (winget)"
|
||||||
|
Install-CudaToolkitIfMissing -DriverMaxCuda $DriverMaxCuda
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Choose the correct PyTorch index URL based on installed CUDA ──
|
||||||
# Mirrors Get-PytorchCudaTag in setup.ps1.
|
# Mirrors Get-PytorchCudaTag in setup.ps1.
|
||||||
|
#
|
||||||
|
# Prefer the installed CUDA Toolkit version: it is the hard constraint
|
||||||
|
# on which PyTorch wheels can actually load. nvidia-smi's "CUDA Version"
|
||||||
|
# is only the driver's *max supported* CUDA (a ceiling) — picking cu130
|
||||||
|
# off a "CUDA 13.0" driver that actually has toolkit 12.9 ships a torch
|
||||||
|
# that silently falls back to CPU. The toolkit install above eliminates
|
||||||
|
# the no-toolkit case on first-time installs; the nvidia-smi fallback
|
||||||
|
# here only fires when winget is unavailable or has no compatible
|
||||||
|
# toolkit, and setup.ps1 retries the install in phase 1e.
|
||||||
function Get-TorchIndexUrl {
|
function Get-TorchIndexUrl {
|
||||||
$baseUrl = if ($env:UNSLOTH_PYTORCH_MIRROR) { $env:UNSLOTH_PYTORCH_MIRROR.TrimEnd('/') } else { "https://download.pytorch.org/whl" }
|
$baseUrl = if ($env:UNSLOTH_PYTORCH_MIRROR) { $env:UNSLOTH_PYTORCH_MIRROR.TrimEnd('/') } else { "https://download.pytorch.org/whl" }
|
||||||
if (-not $NvidiaSmiExe) { return "$baseUrl/cpu" }
|
if (-not $NvidiaSmiExe) { return "$baseUrl/cpu" }
|
||||||
try {
|
$cudaVer = Get-CudaToolkitVersion
|
||||||
$output = & $NvidiaSmiExe 2>&1 | Out-String
|
if (-not $cudaVer) { $cudaVer = $DriverMaxCuda }
|
||||||
if ($output -match 'CUDA Version:\s+(\d+)\.(\d+)') {
|
if ($cudaVer -match '^(\d+)\.(\d+)$') {
|
||||||
$major = [int]$Matches[1]; $minor = [int]$Matches[2]
|
$major = [int]$Matches[1]; $minor = [int]$Matches[2]
|
||||||
if ($major -ge 13) { return "$baseUrl/cu130" }
|
if ($major -ge 13) { return "$baseUrl/cu130" }
|
||||||
if ($major -eq 12 -and $minor -ge 8) { return "$baseUrl/cu128" }
|
if ($major -eq 12 -and $minor -ge 8) { return "$baseUrl/cu128" }
|
||||||
if ($major -eq 12 -and $minor -ge 6) { return "$baseUrl/cu126" }
|
if ($major -eq 12 -and $minor -ge 6) { return "$baseUrl/cu126" }
|
||||||
if ($major -ge 12) { return "$baseUrl/cu124" }
|
if ($major -ge 12) { return "$baseUrl/cu124" }
|
||||||
if ($major -ge 11) { return "$baseUrl/cu118" }
|
if ($major -ge 11) { return "$baseUrl/cu118" }
|
||||||
return "$baseUrl/cpu"
|
return "$baseUrl/cpu"
|
||||||
}
|
}
|
||||||
} catch {}
|
substep "could not determine CUDA version, defaulting to cu126" "Yellow"
|
||||||
substep "could not determine CUDA version from nvidia-smi, defaulting to cu126" "Yellow"
|
|
||||||
return "$baseUrl/cu126"
|
return "$baseUrl/cu126"
|
||||||
}
|
}
|
||||||
$TorchIndexUrl = Get-TorchIndexUrl
|
$TorchIndexUrl = Get-TorchIndexUrl
|
||||||
|
|
@ -1248,6 +1348,21 @@ shell.Run cmd, 0, False
|
||||||
Write-Host ""
|
Write-Host ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Warn when CUDA wheel was picked from the driver report, not a toolkit ──
|
||||||
|
# Reached only when winget couldn't install a compatible CUDA Toolkit
|
||||||
|
# (no winget, or no <= driver-max version in winget's manifest). The
|
||||||
|
# PyTorch wheel may not load until a matching CUDA Toolkit is present;
|
||||||
|
# studio setup will retry the install, but if that also fails the user
|
||||||
|
# has to install it manually.
|
||||||
|
if ($HasNvidiaSmi -and -not $SkipTorch -and $TorchIndexFamily -like "cu*" -and -not (Get-CudaToolkitVersion)) {
|
||||||
|
Write-Host ""
|
||||||
|
substep "No CUDA Toolkit is installed; PyTorch wheel ($TorchIndexFamily) was picked from" "Yellow"
|
||||||
|
substep "your driver's max-CUDA report. Studio setup will retry the toolkit install;" "Yellow"
|
||||||
|
substep "if that also fails, install a CUDA Toolkit matching $TorchIndexFamily manually:" "Yellow"
|
||||||
|
substep " https://developer.nvidia.com/cuda-toolkit-archive" "Yellow"
|
||||||
|
Write-Host ""
|
||||||
|
}
|
||||||
|
|
||||||
# ── Install PyTorch first, then unsloth separately ──
|
# ── Install PyTorch first, then unsloth separately ──
|
||||||
#
|
#
|
||||||
# Why two steps?
|
# Why two steps?
|
||||||
|
|
|
||||||
|
|
@ -335,36 +335,55 @@ function Get-NvccMaxArch {
|
||||||
return $null
|
return $null
|
||||||
}
|
}
|
||||||
|
|
||||||
# Detect driver's max CUDA version from nvidia-smi and return the highest
|
# Pick the PyTorch CUDA index tag (e.g. "cu128") for the installed stack.
|
||||||
# compatible PyTorch CUDA index tag (e.g. "cu128").
|
|
||||||
# PyTorch on Windows ships CPU-only by default from PyPI; CUDA wheels live at
|
# PyTorch on Windows ships CPU-only by default from PyPI; CUDA wheels live at
|
||||||
# https://download.pytorch.org/whl/<tag>. The tag must not exceed the driver's
|
# https://download.pytorch.org/whl/<tag>.
|
||||||
# capability: e.g. driver "CUDA Version: 12.9" → cu128 (not cu130).
|
#
|
||||||
|
# Prefer the installed CUDA Toolkit version (already resolved into
|
||||||
|
# $script:NvccPath by phase 1e and proven <= driver max). nvidia-smi's
|
||||||
|
# "CUDA Version" line is only the driver's *max-supported* CUDA, which can
|
||||||
|
# be a release ahead of the userland — picking cu130 off a "CUDA 13.0"
|
||||||
|
# driver paired with a 12.9 toolkit ships a torch that silently falls back
|
||||||
|
# to CPU. Fall back to the driver report only when no toolkit is resolved
|
||||||
|
# (e.g. the function is called before phase 1e, or no GPU is present).
|
||||||
function Get-PytorchCudaTag {
|
function Get-PytorchCudaTag {
|
||||||
$smiExe = if ($script:NvidiaSmiExe) { $script:NvidiaSmiExe } else {
|
$major = $null
|
||||||
$cmd = Get-Command nvidia-smi -ErrorAction SilentlyContinue
|
$minor = $null
|
||||||
if ($cmd) { $cmd.Source } else { $null }
|
if ($script:NvccPath -and (Test-Path -LiteralPath $script:NvccPath)) {
|
||||||
|
try {
|
||||||
|
$verOut = & $script:NvccPath --version 2>&1 | Out-String
|
||||||
|
if ($verOut -match 'release\s+(\d+)\.(\d+)') {
|
||||||
|
$major = [int]$Matches[1]
|
||||||
|
$minor = [int]$Matches[2]
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
}
|
}
|
||||||
if (-not $smiExe) { return "cu126" }
|
if ($null -eq $major) {
|
||||||
|
$smiExe = if ($script:NvidiaSmiExe) { $script:NvidiaSmiExe } else {
|
||||||
try {
|
$cmd = Get-Command nvidia-smi -ErrorAction SilentlyContinue
|
||||||
# 2>&1 | Out-String merges stderr into stdout then converts to a single
|
if ($cmd) { $cmd.Source } else { $null }
|
||||||
# string. Plain 2>$null doesn't fully suppress stderr in PS 5.1 --
|
|
||||||
# ErrorRecord objects leak into $output and break the -match.
|
|
||||||
$output = & $smiExe 2>&1 | Out-String
|
|
||||||
if ($output -match 'CUDA Version:\s+(\d+)\.(\d+)') {
|
|
||||||
$major = [int]$Matches[1]
|
|
||||||
$minor = [int]$Matches[2]
|
|
||||||
# PyTorch 2.10 offers: cu124, cu126, cu128, cu130
|
|
||||||
if ($major -ge 13) { return "cu130" }
|
|
||||||
if ($major -eq 12 -and $minor -ge 8) { return "cu128" }
|
|
||||||
if ($major -eq 12 -and $minor -ge 6) { return "cu126" }
|
|
||||||
if ($major -ge 12) { return "cu124" }
|
|
||||||
if ($major -ge 11) { return "cu118" }
|
|
||||||
return "cpu"
|
|
||||||
}
|
}
|
||||||
} catch { }
|
if (-not $smiExe) { return "cu126" }
|
||||||
|
try {
|
||||||
|
# 2>&1 | Out-String merges stderr into stdout then converts to a single
|
||||||
|
# string. Plain 2>$null doesn't fully suppress stderr in PS 5.1 --
|
||||||
|
# ErrorRecord objects leak into $output and break the -match.
|
||||||
|
$output = & $smiExe 2>&1 | Out-String
|
||||||
|
if ($output -match 'CUDA Version:\s+(\d+)\.(\d+)') {
|
||||||
|
$major = [int]$Matches[1]
|
||||||
|
$minor = [int]$Matches[2]
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
if ($null -ne $major) {
|
||||||
|
# PyTorch 2.10 offers: cu118, cu124, cu126, cu128, cu130
|
||||||
|
if ($major -ge 13) { return "cu130" }
|
||||||
|
if ($major -eq 12 -and $minor -ge 8) { return "cu128" }
|
||||||
|
if ($major -eq 12 -and $minor -ge 6) { return "cu126" }
|
||||||
|
if ($major -ge 12) { return "cu124" }
|
||||||
|
if ($major -ge 11) { return "cu118" }
|
||||||
|
return "cpu"
|
||||||
|
}
|
||||||
return "cu126"
|
return "cu126"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue