Prepend cmake, nvcc, Python Scripts; keep venv Scripts appended

The previous commit switched Add-ToUserPath to append by default so that
installing unsloth would not silently hijack the user's system python /
pip. That was correct for the venv Scripts dir (which contains python.exe
and pip.exe alongside unsloth.exe), but wrong for the three studio/setup
call sites. Those persist cmake, the driver-compatible nvcc, and the
Python user Scripts dir for future shells, and in all three cases an
older tool already earlier in the user PATH would keep winning after the
install finished. The nvcc case is especially load-bearing: setup selects
a driver-compatible CUDA toolkit, then llama.cpp builds against whatever
wins PATH resolution, so a stale older nvcc produces broken builds.

Pass -Position 'Prepend' explicitly at the three setup.ps1 call sites
(cmake at line 754, nvcc bin at line 1025, Python user Scripts at line
1191). None of those directories holds python.exe, so prepending them
does not re-introduce the original hijack problem. Leave the install.ps1
venv Scripts call on the default Append with a comment explaining why.
This commit is contained in:
Daniel Han 2026-04-16 08:33:58 +00:00
commit 2b4bc5eb5e
2 changed files with 21 additions and 5 deletions

View file

@ -1033,6 +1033,11 @@ shell.Run cmd, 0, False
New-StudioShortcuts -UnslothExePath $UnslothExe
# ── Add venv Scripts dir to User PATH so `unsloth studio` works from any terminal ──
# Use the default Append position here: this venv dir holds python.exe and
# pip.exe alongside unsloth.exe, so prepending would silently hijack the
# user's system python / pip in every future shell. Desktop shortcuts and
# the launch-studio wrapper call unsloth.exe by absolute path, so studio
# itself does not rely on this entry winning resolution races.
$ScriptsDir = Join-Path $VenvDir "Scripts"
if (Add-ToUserPath -Directory $ScriptsDir) {
Refresh-SessionPath

View file

@ -752,8 +752,11 @@ if (-not $HasCmake) {
foreach ($d in $cmakeDefaults) {
if (Test-Path (Join-Path $d "cmake.exe")) {
$env:Path = "$d;$env:Path"
# Persist to user PATH so Refresh-Environment does not drop it later
Add-ToUserPath -Directory $d | Out-Null
# Persist to user PATH so Refresh-Environment does not drop it later.
# Prepend so the newly-selected cmake wins over any older cmake
# entry already in the user PATH (this dir has only cmake.exe, no
# python.exe, so prepending does not hijack the user's interpreter).
Add-ToUserPath -Directory $d -Position 'Prepend' | Out-Null
$HasCmake = $null -ne (Get-Command cmake -ErrorAction SilentlyContinue)
if ($HasCmake) {
Write-Host " Found cmake at $d (added to PATH)" -ForegroundColor Gray
@ -1019,8 +1022,12 @@ $nvccBinDir = Split-Path $NvccPath -Parent
if ($env:PATH -notlike "*$nvccBinDir*") {
[Environment]::SetEnvironmentVariable('PATH', "$nvccBinDir;$env:PATH", 'Process')
}
# Persist nvcc bin dir to User PATH so it works in new terminals
if (Add-ToUserPath -Directory $nvccBinDir) {
# Persist nvcc bin dir to User PATH so it works in new terminals.
# Prepend so the toolkit we just selected (driver-compatible) wins over any
# older CUDA bin dir already on the user PATH. Critical for llama.cpp builds:
# a later Refresh-Environment could otherwise reorder the selected nvcc behind
# a stale one. No hijack risk since this dir has only CUDA tools, no python.
if (Add-ToUserPath -Directory $nvccBinDir -Position 'Prepend') {
substep "Persisted CUDA bin dir to user PATH"
}
@ -1181,7 +1188,11 @@ if ($HasPython) {
# Ensure Python Scripts dir is on PATH (so 'unsloth' command works in new terminals)
$ScriptsDir = python -c "import sysconfig; print(sysconfig.get_path('scripts', 'nt_user') if __import__('os').path.exists(sysconfig.get_path('scripts', 'nt_user')) else sysconfig.get_path('scripts'))"
if ($LASTEXITCODE -eq 0 -and $ScriptsDir -and (Test-Path $ScriptsDir)) {
if (Add-ToUserPath -Directory $ScriptsDir) {
# Prepend so the freshly installed `unsloth` console script wins over any
# older pip-installed copy already earlier on the user PATH. This dir holds
# entry points only (python.exe lives one level up), so prepending does not
# hijack the user's python interpreter.
if (Add-ToUserPath -Directory $ScriptsDir -Position 'Prepend') {
# Also add to current process so it's available immediately
$ProcessPathEntries = $env:PATH.Split(';')
if (-not ($ProcessPathEntries | Where-Object { $_.TrimEnd('\') -eq $ScriptsDir })) {