* studio/setup.ps1: complete Visual Studio 2026 support for the CUDA llama.cpp build Builds on #6038 (VS 2026 / v18 detection). Once the generator is detected as Visual Studio 18 2026, two things still broke the CUDA llama.cpp build: - the CUDA to VS MSBuild integration copied the CUDA .targets into a hardcoded VC\v170 (VS 2022) BuildCustomizations folder, so a VS 2026 (v180) toolchain saw no CUDA toolset and cmake failed with "No CUDA toolset found". - cmake was installed with no version check, but the "Visual Studio 18 2026" generator requires CMake 4.2+. This adds Get-VcBuildCustomizationsDir (derives v160/v170/v180 from the detected generator, falls back to v170), a CMake 4.2 guard for the VS 2026 generator (upgrades via winget once, else fails with a clear message), and routes both the copy target and the failure hint through the derived path. No behavior change for VS 2022/2019/2017: the folder resolves to v170 and the guard is skipped. Adds windows-latest Pester unit tests (tests/studio_setup_ps1) plus a workflow that runs them. * Address review: make VS 2026 self-contained + gate CMake guard to source build - Find-VsBuildTools now detects VS 2026: vswhere catalog_productLineVersion 2026 -> "Visual Studio 18 2026", and the filesystem scan covers the "18"/"2026" dirs (incl. non-standard editions like Preview). Adapted from #6038 by @LeoBorcherding, so the v180 BuildCustomizations path and the CMake guard are actually reachable on a VS 2026-only host. - Move the CMake 4.2 guard out of Phase 1 into the committed-source-build branch. The preferred prebuilt llama.cpp path never reaches it, so a VS 2026 host on CMake < 4.2 is no longer blocked from using the prebuilt. - winget upgrade -> install fallback when the on-PATH cmake is not the Kitware winget package, and log winget failures instead of swallowing them. - Add a windows-latest Find-VsBuildTools VS 2026 discovery regression test. * tests(vs2026): define New-FakeVsTree in BeforeAll so It blocks can see it The Find-VsBuildTools discovery tests are Windows-only (-Skip on non-Windows), so they first ran on the windows-latest Pester job, where New-FakeVsTree raised CommandNotFoundException: it was defined in the Describe body, which Pester 5 executes only during discovery, so the function did not persist into the run-phase It scope. Move it into a BeforeAll block (which runs in the run phase and is visible to the It blocks). No production code change. * Address review: probe cmake generator support, fall back to older VS, fix cmake PATH after winget The VS 2026 CMake guard previously gated only on the cmake version (>= 4.2) and hard-failed otherwise. Review on #6473 raised three real gaps: - A VS-bundled cmake below 4.2 can still drive the VS 2026 generator. Probe cmake --help (Test-CmakeListsGenerator / Test-CmakeCanDriveGenerator) and accept it when the generator is advertised, not just on the version floor. - After winget upgrade/install, an older cmake earlier on PATH kept being resolved. Add-DefaultCmakeToPath prepends the default install dir so the new cmake wins before re-probing. - When cmake cannot drive VS 2026 but an older Visual Studio (2022/2019/2017) is installed and usable, fall back to it (Get-FallbackVsGenerator) instead of hard-failing, preserving the pre-VS-2026 build path. Tests mock the cmake command rather than dropping a shim on PATH: PowerShell caches its application-path table, so a real cmake on the runner (present on windows-latest) wins over a PATH shim. A function mock is resolved first and is cache-proof cross-platform. * Detect VS installed under the Preview edition dir for older versions Find-VsBuildTools already scans every subdir for VS 2026, but the older-version (2017/2019/2022) filesystem fallback and Get-FallbackVsGenerator only checked BuildTools/Community/Professional/Enterprise. A Preview-channel install lives under a 'Preview' edition folder, so it was missed when vswhere was also unavailable. Add 'Preview' to both edition lists and guard each with a Windows Pester test. * Add real-VS integration matrix: detect actual VS 2022 and VS 2026 in parallel The unit tests validate VS detection logic with mocked vswhere and fake install trees (all five versions). This adds a parallel integration job that runs the real Find-VsBuildTools / Get-VcBuildCustomizationsDir against the Visual Studio actually preinstalled on GitHub-hosted runners: - windows-2022 -> real Visual Studio 2022, expect generator v170 - windows-2025-vs2026 -> real Visual Studio 2026, expect generator v180 It asserts our detection matches the real install, the install path exists, the derived toolset matches, and that the derived v-number is a real folder on the VS install. VS 2017/2019/2015 are retired from hosted images, so only 2022 and 2026 can be exercised against a genuine install; the rest stay covered by the mocks. * Detect VS 2026 via vswhere: it reports productLineVersion '18', not '2026' Real-VS CI on the windows-2025-vs2026 runner showed vswhere reports catalog_productLineVersion='18' (the internal major) for Visual Studio 2026, not the marketing year '2026' that VS <= 2022 report. The vswhere map only had '2026', so on a real VS 2026 host the vswhere branch returned null and detection survived only via the filesystem scan (Source='filesystem'); a VS 2026 installed outside the default Program Files location would not be found at all. Extract a pure Resolve-VsGeneratorFromLabel that accepts both the year and the internal-major form ('18'/'17'/'16'/'15' as well as '2026'/'2022'/'2019'/'2017') and use it for both the vswhere and filesystem branches. Add pure unit tests (cross-platform) for the mapping, including the '18' -> VS 2026 case. * ci: dot-source Resolve-VsGeneratorFromLabel in the real-VS integration job Find-VsBuildTools now calls Resolve-VsGeneratorFromLabel, so the integration step must extract it too; without it the job failed with the helper not recognized. * Defer Visual Studio + CMake to the llama.cpp source build (prebuilt path needs no build tools) The Windows installer required Visual Studio Build Tools and CMake eagerly in Phase 1 (winget install + exit 1 if absent), before the llama.cpp prebuilt-vs- source decision. But the preferred path downloads a prebuilt llama.cpp (no compiler), the backend only shells out to the prebuilt llama-server.exe, and PyTorch is pip wheels -- so VS and CMake are only needed for the from-source build last resort. The eager requirement forced every Windows user to install multi-GB Visual Studio + CMake they never use, or the installer failed. Change (mirrors the already-lazy Resolve-CudaToolkit / OpenSSL): - Phase 1c/1d now only DETECT cmake / VS and log; they never winget-install or exit. The prebuilt install runs zero build-tool installs and is unblocked on hosts without build tools. - New Ensure-BuildToolsForLlamaSourceBuild installs CMake (best effort) + VS (hard requirement, exit 1 with the existing guidance if it cannot be found), called only when a source build is actually committed, before Resolve-CudaToolkit. git stays eager (pip needs it for git+ deps). Tests: - Pester: the early probe (Find-VsBuildTools) returns null without exiting when no VS is present; Ensure-BuildToolsForLlamaSourceBuild no-ops when VS is already detected. - New studio-windows-no-vs-smoke.yml: Job A renames Visual Studio + vswhere away and hides cmake, runs the real install.ps1 --local --no-torch, and asserts the prebuilt llama.cpp installed (no source-build fallback, no VS/CMake install), PyTorch CPU imports, the backend is healthy, and a /v1/chat/completions inference returns a reply -- all with no Visual Studio. Job B confirms the GPU CUDA prebuilt is available and the resolver runs without VS. * Fix VS 2026 CUDA source build ordering and fallback VS discovery Same fix as on the stacked base branch (studio-vs2026-cuda-msbuild): - Move Resolve-CudaToolkit below the CMake gate/fallback in the source build path. It copies the CUDA MSBuild .targets into the current VS generator's BuildCustomizations folder, so running it before a VS 2026 to older-VS fallback left the .targets under v180 while cmake configured v170 ("No CUDA toolset found"). It now runs after the final generator is selected. - Get-FallbackVsGenerator now queries vswhere first, matching Find-VsBuildTools, so a VS installed outside the default Program Files roots is found instead of failing with a hard exit. - Add Pester regression tests: the source build resolves CUDA after the fallback, and the fallback queries vswhere. * Ensure the Visual C++ Redistributable is present for the prebuilt llama.cpp and PyTorch The prebuilt llama-server.exe and the PyTorch wheels dynamically link the MSVC runtime (VCRUNTIME140.dll, MSVCP140.dll, VCRUNTIME140_1.dll). The Universal CRT ships with Windows 10+, but the VC++ 2015-2022 redistributable does not, so a clean box can fail to launch llama-server or import torch with a missing VCRUNTIME140.dll. - Add Test-VCRedistInstalled (System32 vcruntime140_1.dll, with a registry fallback gated on version 14.20+) and Ensure-VCRedist (winget Microsoft.VCRedist.2015+.x64, non-fatal), called as Phase 1b.5 so it runs even on the no-build-tools prebuilt path. It is a no-op when the runtime is already present, which is the common case. - Add Pester tests for the detection: present via the DLL, present via the registry, absent, and an old 2015-only redist that is too low. * Add a CI job that validates the VC++ runtime detection on a real Windows runner Runs on windows-latest and windows-2025-vs2026: asserts Test-VCRedistInstalled reports present on the stock image, removes both detection signals (the System32 DLL via a redirected SystemRoot and the HKLM runtime keys, restorably) to confirm detection fires on a genuinely clean box, then does a literal uninstall/reinstall round trip with the official installer and the Ensure-VCRedist winget path. The runtime is restored before the job ends. * Dot-source the full logging closure in the VC++ runtime CI job Ensure-VCRedist calls step/substep, which reach Write-StudioStdoutMirror and Get-StudioAnsi; extract those too so the job does not fail with an unrecognized command. Also note that the runtime is ref-counted by Visual Studio on the hosted image, so the literal package uninstall is a no-op there (the clean-box section already proves detection fires when the runtime is genuinely absent). * Tighten comments in setup.ps1, the VS2026 tests and workflow Comment-only: condense the verbose helper/test/CI comments to one or two lines, drop the obvious ones, keep the non-obvious rationale. Verified comment-only by comparing the PowerShell code-token stream before and after (no code tokens changed); Pester suite still green. * Fold the no-VS and setup.ps1 VS2026 Windows CI into studio-windows-inference-smoke.yml Move the no-vs-cpu/no-vs-gpu-resolve and pester/vs-integration/vcredist-clean-box jobs into the existing Windows GGUF CI workflow and delete the two standalone files, so a studio change triggers one Windows workflow instead of three. Path filter gains tests/studio_setup_ps1/**; job keys and artifact names stay unique. * CI: assert a Windows ROCm prebuilt exists in the no-VS resolve job The no-vs-gpu-resolve job confirmed a Windows CUDA asset but never a ROCm one, and the resolver step resolves to CPU on hosted runners (no AMD GPU), so the AMD no-VS guarantee rode only on shared resolver code. Grep the per-gfx windows-x64-rocm-gfx bundles in the same asset-availability step so a release that drops the Windows ROCm prebuilts fails loudly. --------- Co-authored-by: Daniel Han <michaelhan2050@gmail.com>
368 lines
18 KiB
PowerShell
368 lines
18 KiB
PowerShell
<#
|
|
Pester v5 unit tests for the Visual Studio 2026 completion helpers in
|
|
studio/setup.ps1:
|
|
- Get-VcBuildCustomizationsDir : derive the VC MSBuild BuildCustomizations
|
|
folder (v160 / v170 / v180) from the detected VS generator.
|
|
- Test-CmakeSupportsGenerator : gate the "Visual Studio 18 2026" generator
|
|
on CMake >= 4.2 (no-op for older VS generators).
|
|
|
|
Both are pure functions (no GPU, no Visual Studio, no CUDA, no network), so the
|
|
suite runs on a stock windows-latest runner - and on any pwsh host.
|
|
|
|
The real functions are extracted from setup.ps1 and dot-sourced (the script is
|
|
a top-level installer and cannot be loaded wholesale). Path resolution honors
|
|
$env:SETUP_PS1_PATH (set by the PR-validate workflow) and falls back to the
|
|
repo-relative path. If a target function cannot be found, the suite FAILS
|
|
loudly rather than silently passing.
|
|
#>
|
|
|
|
BeforeAll {
|
|
. (Join-Path $PSScriptRoot 'Get-FunctionSource.ps1')
|
|
|
|
$candidates = @(
|
|
$env:SETUP_PS1_PATH,
|
|
(Join-Path $PSScriptRoot '..\..\studio\setup.ps1')
|
|
) | Where-Object { $_ }
|
|
$script:SetupPs1 = $candidates | Where-Object { Test-Path -LiteralPath $_ } | Select-Object -First 1
|
|
if (-not $script:SetupPs1) { throw "Could not locate studio/setup.ps1 (set SETUP_PS1_PATH)." }
|
|
Write-Host "setup.ps1 under test: $script:SetupPs1"
|
|
|
|
foreach ($fn in @('Resolve-VsGeneratorFromLabel', 'Find-VsBuildTools', 'Get-VcBuildCustomizationsDir',
|
|
'Test-CmakeSupportsGenerator', 'Get-CmakeVersion', 'Test-CmakeListsGenerator',
|
|
'Test-CmakeCanDriveGenerator', 'Get-FallbackVsGenerator',
|
|
'Ensure-BuildToolsForLlamaSourceBuild', 'Test-VCRedistInstalled')) {
|
|
$src = Get-FunctionSource -Path $script:SetupPs1 -Name $fn
|
|
if (-not $src) { throw "Function '$fn' not found in $script:SetupPs1 - cannot test the real code." }
|
|
. ([scriptblock]::Create($src))
|
|
}
|
|
}
|
|
|
|
Describe 'Resolve-VsGeneratorFromLabel (vswhere/dir label -> generator)' {
|
|
# Guards that detection accepts both '18' (the internal major vswhere reports
|
|
# for VS 2026) and the year form.
|
|
It 'maps the VS 2026 internal major "18" to the VS 2026 generator' {
|
|
Resolve-VsGeneratorFromLabel '18' | Should -Be 'Visual Studio 18 2026'
|
|
}
|
|
It 'maps the VS 2026 year label "2026" to the VS 2026 generator' {
|
|
Resolve-VsGeneratorFromLabel '2026' | Should -Be 'Visual Studio 18 2026'
|
|
}
|
|
It 'maps the VS 2022 year "2022" and major "17" to the VS 2022 generator' {
|
|
Resolve-VsGeneratorFromLabel '2022' | Should -Be 'Visual Studio 17 2022'
|
|
Resolve-VsGeneratorFromLabel '17' | Should -Be 'Visual Studio 17 2022'
|
|
}
|
|
It 'maps 2019/2017 (year and major) to their generators' {
|
|
Resolve-VsGeneratorFromLabel '2019' | Should -Be 'Visual Studio 16 2019'
|
|
Resolve-VsGeneratorFromLabel '16' | Should -Be 'Visual Studio 16 2019'
|
|
Resolve-VsGeneratorFromLabel '2017' | Should -Be 'Visual Studio 15 2017'
|
|
Resolve-VsGeneratorFromLabel '15' | Should -Be 'Visual Studio 15 2017'
|
|
}
|
|
It 'trims whitespace (vswhere output can carry a trailing newline)' {
|
|
Resolve-VsGeneratorFromLabel " 18 `n" | Should -Be 'Visual Studio 18 2026'
|
|
}
|
|
It 'returns null for unknown or empty labels' {
|
|
Resolve-VsGeneratorFromLabel '2015' | Should -BeNullOrEmpty
|
|
Resolve-VsGeneratorFromLabel '' | Should -BeNullOrEmpty
|
|
Resolve-VsGeneratorFromLabel $null | Should -BeNullOrEmpty
|
|
}
|
|
}
|
|
|
|
Describe 'Find-VsBuildTools (VS 2026 generator discovery)' {
|
|
# Exercises the real discovery entry point. Windows-only: Find-VsBuildTools builds
|
|
# backslash candidate paths that only resolve as directories on Windows.
|
|
BeforeAll {
|
|
# Define in BeforeAll, not the Describe body: Pester 5 runs the body only at
|
|
# discovery, so body-level functions are not visible in the run-phase It blocks.
|
|
function New-FakeVsTree {
|
|
param([string]$Root, [string]$VersionDir, [string]$Edition = 'BuildTools')
|
|
$clDir = Join-Path $Root "Microsoft Visual Studio\$VersionDir\$Edition\VC\Tools\MSVC\14.50.00000\bin\Hostx64\x64"
|
|
New-Item -ItemType Directory -Path $clDir -Force | Out-Null
|
|
New-Item -ItemType File -Path (Join-Path $clDir 'cl.exe') -Force | Out-Null
|
|
}
|
|
}
|
|
BeforeEach {
|
|
$script:OrigPF = ${env:ProgramFiles}
|
|
$script:OrigPFx86 = ${env:ProgramFiles(x86)}
|
|
}
|
|
AfterEach {
|
|
${env:ProgramFiles} = $script:OrigPF
|
|
${env:ProgramFiles(x86)} = $script:OrigPFx86
|
|
}
|
|
|
|
It 'detects a filesystem-only VS 2026 BuildTools install (dir "18")' -Skip:(-not $IsWindows) {
|
|
$root = Join-Path $TestDrive 'PF'
|
|
New-FakeVsTree -Root $root -VersionDir '18'
|
|
${env:ProgramFiles} = $root
|
|
${env:ProgramFiles(x86)} = Join-Path $TestDrive 'PFx86' # no vswhere here -> filesystem fallback
|
|
$r = Find-VsBuildTools
|
|
$r.Generator | Should -Be 'Visual Studio 18 2026'
|
|
}
|
|
|
|
It 'detects a filesystem-only VS 2026 install under the year dir ("2026")' -Skip:(-not $IsWindows) {
|
|
$root = Join-Path $TestDrive 'PF2026'
|
|
New-FakeVsTree -Root $root -VersionDir '2026'
|
|
${env:ProgramFiles} = $root
|
|
${env:ProgramFiles(x86)} = Join-Path $TestDrive 'PFx86b'
|
|
(Find-VsBuildTools).Generator | Should -Be 'Visual Studio 18 2026'
|
|
}
|
|
|
|
It 'still detects VS 2022 (no regression)' -Skip:(-not $IsWindows) {
|
|
$root = Join-Path $TestDrive 'PF2022'
|
|
New-FakeVsTree -Root $root -VersionDir '2022'
|
|
${env:ProgramFiles} = $root
|
|
${env:ProgramFiles(x86)} = Join-Path $TestDrive 'PFx86c'
|
|
(Find-VsBuildTools).Generator | Should -Be 'Visual Studio 17 2022'
|
|
}
|
|
|
|
It 'detects an older VS installed under the Preview edition dir' -Skip:(-not $IsWindows) {
|
|
# Preview installs under a "Preview" edition folder; the fallback must include it.
|
|
$root = Join-Path $TestDrive 'PF2022prev'
|
|
New-FakeVsTree -Root $root -VersionDir '2022' -Edition 'Preview'
|
|
${env:ProgramFiles} = $root
|
|
${env:ProgramFiles(x86)} = Join-Path $TestDrive 'PFx86d'
|
|
(Find-VsBuildTools).Generator | Should -Be 'Visual Studio 17 2022'
|
|
}
|
|
}
|
|
|
|
Describe 'Get-VcBuildCustomizationsDir (CUDA to VS MSBuild integration path)' {
|
|
# Use TestDrive as the root so Join-Path resolves on any OS; assertions accept
|
|
# either path separator.
|
|
|
|
It 'derives v180 for the VS 2026 generator' {
|
|
Get-VcBuildCustomizationsDir -VsInstallPath "$TestDrive" -Generator 'Visual Studio 18 2026' |
|
|
Should -Match 'VC[\\/]v180[\\/]BuildCustomizations$'
|
|
}
|
|
|
|
It 'derives v170 for VS 2022 (unchanged behavior)' {
|
|
Get-VcBuildCustomizationsDir -VsInstallPath "$TestDrive" -Generator 'Visual Studio 17 2022' |
|
|
Should -Match 'VC[\\/]v170[\\/]BuildCustomizations$'
|
|
}
|
|
|
|
It 'derives v160 for VS 2019' {
|
|
Get-VcBuildCustomizationsDir -VsInstallPath "$TestDrive" -Generator 'Visual Studio 16 2019' |
|
|
Should -Match 'VC[\\/]v160[\\/]BuildCustomizations$'
|
|
}
|
|
|
|
It 'falls back to v170 when the generator is empty/unparseable (backwards compatible)' {
|
|
Get-VcBuildCustomizationsDir -VsInstallPath "$TestDrive" -Generator '' |
|
|
Should -Match 'VC[\\/]v170[\\/]BuildCustomizations$'
|
|
}
|
|
|
|
It 'roots the path under the supplied VS install path' {
|
|
$p = Get-VcBuildCustomizationsDir -VsInstallPath "$TestDrive" -Generator 'Visual Studio 18 2026'
|
|
$p.StartsWith("$TestDrive") | Should -BeTrue
|
|
}
|
|
}
|
|
|
|
Describe 'Test-CmakeSupportsGenerator (CMake 4.2 guard for VS 2026)' {
|
|
|
|
It 'rejects CMake 3.31.0 with the VS 2026 generator' {
|
|
Test-CmakeSupportsGenerator -CmakeVersion '3.31.0' -Generator 'Visual Studio 18 2026' | Should -BeFalse
|
|
}
|
|
|
|
It 'accepts CMake 4.2.1 with the VS 2026 generator' {
|
|
Test-CmakeSupportsGenerator -CmakeVersion '4.2.1' -Generator 'Visual Studio 18 2026' | Should -BeTrue
|
|
}
|
|
|
|
It 'accepts CMake exactly 4.2 with the VS 2026 generator (boundary)' {
|
|
Test-CmakeSupportsGenerator -CmakeVersion '4.2' -Generator 'Visual Studio 18 2026' | Should -BeTrue
|
|
}
|
|
|
|
It 'rejects CMake 4.1.0 with the VS 2026 generator (boundary)' {
|
|
Test-CmakeSupportsGenerator -CmakeVersion '4.1.0' -Generator 'Visual Studio 18 2026' | Should -BeFalse
|
|
}
|
|
|
|
It 'is a no-op (accepts any CMake) for the VS 2022 generator' {
|
|
Test-CmakeSupportsGenerator -CmakeVersion '3.20.0' -Generator 'Visual Studio 17 2022' | Should -BeTrue
|
|
}
|
|
|
|
It 'is a no-op (accepts any CMake) for the VS 2019 generator' {
|
|
Test-CmakeSupportsGenerator -CmakeVersion '3.10.0' -Generator 'Visual Studio 16 2019' | Should -BeTrue
|
|
}
|
|
}
|
|
|
|
Describe 'Test-CmakeListsGenerator (probe cmake --help)' {
|
|
# Mock cmake as a function (resolved before any on-PATH exe): PowerShell caches
|
|
# its app-path table, so a $env:Path shim would not reliably beat a real cmake.
|
|
|
|
It 'returns true when cmake --help lists the generator' {
|
|
Mock cmake { "Generators`n Visual Studio 18 2026 = Generates VS 2026 project files.`n Visual Studio 17 2022 = Generates VS 2022 project files." }
|
|
Test-CmakeListsGenerator -Generator 'Visual Studio 18 2026' | Should -BeTrue
|
|
}
|
|
|
|
It 'returns false when cmake --help does not list the generator' {
|
|
Mock cmake { "Generators`n Visual Studio 17 2022 = Generates VS 2022 project files." }
|
|
Test-CmakeListsGenerator -Generator 'Visual Studio 18 2026' | Should -BeFalse
|
|
}
|
|
|
|
It 'returns false when cmake produces no help output' {
|
|
Mock cmake { $null }
|
|
Test-CmakeListsGenerator -Generator 'Visual Studio 18 2026' | Should -BeFalse
|
|
}
|
|
}
|
|
|
|
Describe 'Test-CmakeCanDriveGenerator (probe OR version floor)' {
|
|
It 'accepts a sub-4.2 cmake that lists the VS 2026 generator (bundled cmake)' {
|
|
# 3.31.0 is below the 4.2 floor but lists the generator, so the help-probe accepts it.
|
|
Mock cmake {
|
|
if ($args -contains '--version') { 'cmake version 3.31.0' }
|
|
else { "Generators`n Visual Studio 18 2026 = Generates VS 2026 project files." }
|
|
}
|
|
Test-CmakeCanDriveGenerator -Generator 'Visual Studio 18 2026' | Should -BeTrue
|
|
}
|
|
|
|
It 'accepts a 4.2 cmake via the version floor when the help probe misses it' {
|
|
# Help omits the generator but 4.2.0 meets the floor, so the version branch accepts it.
|
|
Mock cmake {
|
|
if ($args -contains '--version') { 'cmake version 4.2.0' }
|
|
else { 'Generators' }
|
|
}
|
|
Test-CmakeCanDriveGenerator -Generator 'Visual Studio 18 2026' | Should -BeTrue
|
|
}
|
|
|
|
It 'rejects a sub-4.2 cmake that does not list the VS 2026 generator' {
|
|
Mock cmake {
|
|
if ($args -contains '--version') { 'cmake version 3.31.0' }
|
|
else { "Generators`n Visual Studio 17 2022 = Generates VS 2022 project files." }
|
|
}
|
|
Test-CmakeCanDriveGenerator -Generator 'Visual Studio 18 2026' | Should -BeFalse
|
|
}
|
|
}
|
|
|
|
Describe 'Get-FallbackVsGenerator (older VS the cmake can drive)' {
|
|
BeforeAll {
|
|
function New-FakeVsTree2 {
|
|
param([string]$Root, [string]$VersionDir, [string]$Edition = 'BuildTools')
|
|
$clDir = Join-Path $Root "Microsoft Visual Studio\$VersionDir\$Edition\VC\Tools\MSVC\14.39.00000\bin\Hostx64\x64"
|
|
New-Item -ItemType Directory -Path $clDir -Force | Out-Null
|
|
New-Item -ItemType File -Path (Join-Path $clDir 'cl.exe') -Force | Out-Null
|
|
}
|
|
}
|
|
BeforeEach {
|
|
$script:OrigPF = ${env:ProgramFiles}
|
|
$script:OrigPFx86 = ${env:ProgramFiles(x86)}
|
|
}
|
|
AfterEach {
|
|
${env:ProgramFiles} = $script:OrigPF
|
|
${env:ProgramFiles(x86)} = $script:OrigPFx86
|
|
}
|
|
|
|
It 'returns the VS 2022 generator when VS 2022 is installed and cmake lists it' -Skip:(-not $IsWindows) {
|
|
$root = Join-Path $TestDrive 'PF_fb'
|
|
New-FakeVsTree2 -Root $root -VersionDir '2022'
|
|
${env:ProgramFiles} = $root
|
|
${env:ProgramFiles(x86)} = Join-Path $TestDrive 'PFx86_fb'
|
|
Mock cmake { "Generators`n Visual Studio 17 2022 = Generates VS 2022 project files." }
|
|
$r = Get-FallbackVsGenerator
|
|
$r.Generator | Should -Be 'Visual Studio 17 2022'
|
|
}
|
|
|
|
It 'returns null when the cmake cannot drive any installed older VS' -Skip:(-not $IsWindows) {
|
|
$root = Join-Path $TestDrive 'PF_none'
|
|
New-FakeVsTree2 -Root $root -VersionDir '2022'
|
|
${env:ProgramFiles} = $root
|
|
${env:ProgramFiles(x86)} = Join-Path $TestDrive 'PFx86_none'
|
|
# cmake lists only VS 2026 (not 2022/2019/2017), so no older fallback is usable.
|
|
Mock cmake { "Generators`n Visual Studio 18 2026 = Generates VS 2026 project files." }
|
|
$r = Get-FallbackVsGenerator
|
|
$r | Should -BeNullOrEmpty
|
|
}
|
|
|
|
It 'falls back to an older VS installed under the Preview edition dir' -Skip:(-not $IsWindows) {
|
|
$root = Join-Path $TestDrive 'PF_prev'
|
|
New-FakeVsTree2 -Root $root -VersionDir '2022' -Edition 'Preview'
|
|
${env:ProgramFiles} = $root
|
|
${env:ProgramFiles(x86)} = Join-Path $TestDrive 'PFx86_prev'
|
|
Mock cmake { "Generators`n Visual Studio 17 2022 = Generates VS 2022 project files." }
|
|
(Get-FallbackVsGenerator).Generator | Should -Be 'Visual Studio 17 2022'
|
|
}
|
|
}
|
|
|
|
Describe 'Deferred build tools (prebuilt path needs no VS/CMake)' {
|
|
# Phase-1 detection must be non-fatal (prebuilt path never blocked) and the
|
|
# deferred installer must no-op when VS was already detected. The install +
|
|
# exit-1 path is covered by studio-windows-no-vs-smoke.yml.
|
|
BeforeEach {
|
|
$script:OrigPF = ${env:ProgramFiles}
|
|
$script:OrigPFx86 = ${env:ProgramFiles(x86)}
|
|
}
|
|
AfterEach {
|
|
${env:ProgramFiles} = $script:OrigPF
|
|
${env:ProgramFiles(x86)} = $script:OrigPFx86
|
|
$script:VsInstallPath = $null
|
|
$script:CmakeGenerator = $null
|
|
}
|
|
|
|
It 'Find-VsBuildTools returns null when no VS is present (probe stays non-fatal)' {
|
|
# Empty discovery roots so no VS is found; the probe must return null
|
|
# (then log and continue, never exit).
|
|
${env:ProgramFiles} = (Join-Path $TestDrive 'EmptyPF')
|
|
${env:ProgramFiles(x86)} = (Join-Path $TestDrive 'EmptyPFx86')
|
|
New-Item -ItemType Directory -Force -Path ${env:ProgramFiles}, ${env:ProgramFiles(x86)} | Out-Null
|
|
Find-VsBuildTools | Should -BeNullOrEmpty
|
|
}
|
|
|
|
It 'Ensure-BuildToolsForLlamaSourceBuild no-ops when VS is already detected' {
|
|
# With $VsInstallPath already set, the deferred installer must return without
|
|
# re-scanning or installing.
|
|
$script:VsInstallPath = 'C:\Program Files\Microsoft Visual Studio\2022\BuildTools'
|
|
$script:CmakeGenerator = 'Visual Studio 17 2022'
|
|
{ Ensure-BuildToolsForLlamaSourceBuild } | Should -Not -Throw
|
|
$script:VsInstallPath | Should -Be 'C:\Program Files\Microsoft Visual Studio\2022\BuildTools'
|
|
$script:CmakeGenerator | Should -Be 'Visual Studio 17 2022'
|
|
}
|
|
}
|
|
|
|
Describe 'Source-build ordering invariant: CUDA integration runs AFTER the VS generator is finalized (#6473 review)' {
|
|
# Resolve-CudaToolkit copies the CUDA .targets into the current generator's dir,
|
|
# so it must run after the VS 2026 gate/fallback; otherwise a fallback to VS 2022
|
|
# builds v170 while the .targets went to v180 ("No CUDA toolset found").
|
|
It 'the source-build Resolve-CudaToolkit call appears AFTER the Get-FallbackVsGenerator fallback' {
|
|
$text = Get-Content -Raw -LiteralPath $script:SetupPs1
|
|
$idxFallback = $text.IndexOf('$fallback = Get-FallbackVsGenerator')
|
|
$idxResolve = $text.IndexOf('Resolve-CudaToolkit -RequireOrExit')
|
|
$idxFallback | Should -BeGreaterThan 0
|
|
$idxResolve | Should -BeGreaterThan 0
|
|
$idxResolve | Should -BeGreaterThan $idxFallback
|
|
}
|
|
}
|
|
|
|
Describe 'Get-FallbackVsGenerator discovery is symmetric with Find-VsBuildTools (#6473 review)' {
|
|
# The fallback must also query vswhere, else a VS in a custom location is found
|
|
# as primary but missed as fallback -> avoidable hard exit.
|
|
It 'queries vswhere as part of fallback discovery' {
|
|
$src = Get-FunctionSource -Path $script:SetupPs1 -Name Get-FallbackVsGenerator
|
|
$src | Should -Match 'vswhere'
|
|
}
|
|
}
|
|
|
|
Describe 'Test-VCRedistInstalled (VC++ 2015-2022 runtime needed by the prebuilt llama.cpp + PyTorch)' {
|
|
# The prebuilts link the VC++ runtime DLLs (which the Universal CRT lacks);
|
|
# detection is System32\vcruntime140_1.dll with a registry fallback.
|
|
BeforeEach { $script:OrigSysRoot = $env:SystemRoot }
|
|
AfterEach { $env:SystemRoot = $script:OrigSysRoot }
|
|
|
|
# Probes Test-Path once (System32 DLL), then the registry; mock both.
|
|
It 'returns true when vcruntime140_1.dll is present in System32' {
|
|
$env:SystemRoot = 'C:\Windows'
|
|
Mock Test-Path { $true }
|
|
Test-VCRedistInstalled | Should -BeTrue
|
|
}
|
|
It 'returns true via the registry when the DLL is not found (Installed=1, >= 14.20)' {
|
|
$env:SystemRoot = 'C:\Windows'
|
|
Mock Test-Path { $false }
|
|
Mock Get-ItemProperty { [pscustomobject]@{ Installed = 1; Major = 14; Minor = 29 } }
|
|
Test-VCRedistInstalled | Should -BeTrue
|
|
}
|
|
It 'returns false when neither the DLL nor a >= 14.20 registry entry exists' {
|
|
$env:SystemRoot = 'C:\Windows'
|
|
Mock Test-Path { $false }
|
|
Mock Get-ItemProperty { throw 'no key' }
|
|
Test-VCRedistInstalled | Should -BeFalse
|
|
}
|
|
It 'returns false for an old 2015-only redist (Installed=1 but < 14.20)' {
|
|
$env:SystemRoot = 'C:\Windows'
|
|
Mock Test-Path { $false }
|
|
Mock Get-ItemProperty { [pscustomobject]@{ Installed = 1; Major = 14; Minor = 0 } }
|
|
Test-VCRedistInstalled | Should -BeFalse
|
|
}
|
|
}
|