diff --git a/.github/actions/setup-randblas-deps-windows/setup.ps1 b/.github/actions/setup-randblas-deps-windows/setup.ps1 index 63454e33..44c7cfbe 100644 --- a/.github/actions/setup-randblas-deps-windows/setup.ps1 +++ b/.github/actions/setup-randblas-deps-windows/setup.ps1 @@ -44,25 +44,59 @@ function Find-PackageConfigDirectory { return $config.Directory.FullName } -function Clone-Head { +# Fetch exactly one commit or tag, and record where it came from. +# +# This replaces a clone that took a branch name and returned early whenever the +# destination merely existed. Two problems with that: a branch tip moves, so +# two runs of the same script could build different source; and reuse keyed on +# presence means changing a ref is a silent no-op for anyone who already has +# the directory, so the new pin never takes effect. The stamp is needed +# because a shallow fetch of a tag does not keep the tag ref locally, so git +# cannot be asked afterwards whether a tree is at the pin. +function Clone-Pinned { param( [Parameter(Mandatory = $true)][string] $Url, [Parameter(Mandatory = $true)][string] $Destination, - [string] $Branch = "" + [Parameter(Mandatory = $true)][string] $Ref ) - if (Test-Path -LiteralPath $Destination) { + $stampPath = Join-Path $Destination ".randblas-provenance" + $stamp = "$Url@$Ref" + if ((Test-Path -LiteralPath $stampPath) -and + ((Get-Content -LiteralPath $stampPath -Raw).Trim() -eq $stamp)) { + Write-Host "Reusing $Destination (already at $Ref)" return } - $arguments = @("clone", "--depth", "1") - if ($Branch) { - $arguments += @("--branch", $Branch) + if (Test-Path -LiteralPath $Destination) { + Remove-Item -Recurse -Force -LiteralPath $Destination } - $arguments += @($Url, $Destination) - Invoke-Checked -Program "git" -Arguments $arguments + New-Item -ItemType Directory -Force -Path $Destination | Out-Null + Invoke-Checked -Program "git" -Arguments @("-C", $Destination, "init", "--quiet") + Invoke-Checked -Program "git" -Arguments @("-C", $Destination, "remote", "add", "origin", $Url) + Invoke-Checked -Program "git" -Arguments @("-C", $Destination, "fetch", "--quiet", "--depth", "1", "origin", $Ref) + Invoke-Checked -Program "git" -Arguments @("-C", $Destination, "checkout", "--quiet", "FETCH_HEAD") + Set-Content -LiteralPath $stampPath -Value $stamp -Encoding ascii } +#------------------------------------------------------------------ pins ------ +# Immutable refs only: a tag or a full commit hash, never a branch. These match +# install/install.sh and the refs RandLAPACK validated, so the two installers +# and CI cannot disagree about what they built. +# +# BLAS++ and LAPACK++ previously came from personal forks carrying one-line +# MSVC fixes. Both merged upstream on 2026-08-06 (icl-utk-edu/blaspp#132, +# icl-utk-edu/lapackpp#87), so both now come from icl-utk-edu, pinned to the +# merge commits: the latest release of each, v2025.05.28, predates the fixes. +$BlasppUrl = "https://github.com/icl-utk-edu/blaspp.git" +$BlasppRef = "30571853f980d3a2a1737124ea4789e025a5e045" +$LapackppUrl = "https://github.com/icl-utk-edu/lapackpp.git" +$LapackppRef = "40b9d0daf29b6f1f3fa58bc3f22bd6cfb2c67fe4" +$Random123Url = "https://github.com/DEShawResearch/Random123.git" +$Random123Ref = "v1.14.0" +$GTestUrl = "https://github.com/google/googletest.git" +$GTestRef = "v1.18.0" + function Export-GitHubValue { param( [Parameter(Mandatory = $true)][string] $Name, @@ -188,8 +222,7 @@ $gtestVariant = if ($SanitizeAddress) { "googletest-asan" } else { "googletest" $gtestBuild = Join-Path $DependencyRoot "$gtestVariant-build" $gtestInstall = Join-Path $DependencyRoot "$gtestVariant-install" if (-not (Test-Path -LiteralPath (Join-Path $gtestInstall "lib\cmake\GTest\GTestConfig.cmake"))) { - Clone-Head -Url "https://github.com/google/googletest.git" ` - -Destination $gtestSource -Branch "v1.17.0" + Clone-Pinned -Url $GTestUrl -Destination $gtestSource -Ref $GTestRef $gtestArguments = @( "-S", $gtestSource, "-B", $gtestBuild, @@ -215,8 +248,7 @@ $random123Source = Join-Path $DependencyRoot "Random123" $random123Install = Join-Path $DependencyRoot "Random123-install" $random123Include = Join-Path $random123Install "include" if (-not (Test-Path -LiteralPath (Join-Path $random123Include "Random123\philox.h"))) { - Clone-Head -Url "https://github.com/DEShawResearch/Random123.git" ` - -Destination $random123Source + Clone-Pinned -Url $Random123Url -Destination $random123Source -Ref $Random123Ref New-Item -ItemType Directory -Force -Path $random123Include | Out-Null Copy-Item -LiteralPath (Join-Path $random123Source "include\Random123") ` -Destination $random123Include -Recurse @@ -229,10 +261,7 @@ $blasppConfig = Get-ChildItem -LiteralPath $blasppInstall -Recurse -File ` -Filter "blasppConfig.cmake" -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $blasppConfig) { - Clone-Head ` - -Url "https://github.com/RaphaelArkadyMeyerNYU/blaspp.git" ` - -Destination $blasppSource ` - -Branch "windows-portability" + Clone-Pinned -Url $BlasppUrl -Destination $blasppSource -Ref $BlasppRef $blasLibraryArgument = ($mklLibraries | ForEach-Object { Convert-ToCMakePath $_ }) -join ";" @@ -267,10 +296,7 @@ if ($InstallLapackpp) { -Filter "lapackppConfig.cmake" -ErrorAction SilentlyContinue | Select-Object -First 1 if (-not $lapackppConfig) { - Clone-Head ` - -Url "https://github.com/RaphaelArkadyMeyerNYU/lapackpp.git" ` - -Destination $lapackppSource ` - -Branch "msvc-compatibility" + Clone-Pinned -Url $LapackppUrl -Destination $lapackppSource -Ref $LapackppRef Invoke-Checked -Program "cmake" -Arguments @( "-S", $lapackppSource, diff --git a/.github/scripts/windows/toolchain-arch.ps1 b/.github/scripts/windows/toolchain-arch.ps1 new file mode 100644 index 00000000..906be200 --- /dev/null +++ b/.github/scripts/windows/toolchain-arch.ps1 @@ -0,0 +1,75 @@ +# Toolchain architecture detection, shared by install/install.ps1 (user-facing +# preflight) and .github/actions/setup-randlapack-deps-windows/setup.ps1 (which +# also runs standalone in CI). Dot-source it; it defines functions only. +# +# Why this check exists: RandBLAS and every BLAS backend the installer +# provisions are 64-bit, but the "Developer PowerShell for VS" and "Developer +# Command Prompt for VS" Start-menu entries both default to an *x86* toolchain. +# An x86 linker cannot use an x64 import library, and the failure surfaces +# three layers down as BLAS++ reporting "BLAS library not found" -- which +# blames the libraries when the compiler is at fault. Note that the shell's own +# bitness is not a usable signal: the Developer Command Prompt is a 64-bit +# process that still selects x86 tools. + +function Get-ClTargetArchitecture { + # Returns the compiler's TARGET architecture, lowercased ("x64", "x86", + # "arm64", "arm"), or "" if it genuinely cannot be determined. + # + # Three independent signals, most reliable first -- the same + # probe-several-things approach Find-OneMklLayout uses, and for the same + # reason: a missed detection here fails *open*, which defeats the check. + # 1. VSCMD_ARG_TGT_ARCH, exported by vcvarsall.bat / VsDevCmd (and so + # by ilammy/msvc-dev-cmd in CI). Never localized. + # 2. The toolset path: MSVC lays cl.exe out as + # ...\bin\Host\\cl.exe, a stable convention. + # 3. The banner, last, for anything matching neither of the above. + # On its own this would be wrong on a localized Visual Studio, where + # the words around the architecture are translated. + if ($env:VSCMD_ARG_TGT_ARCH) { return $env:VSCMD_ARG_TGT_ARCH.ToLowerInvariant() } + $cl = Get-Command "cl.exe" -ErrorAction SilentlyContinue + if (-not $cl) { return "" } + if ($cl.Source -match '\\bin\\Host[^\\]+\\([^\\]+)\\cl\.exe$') { + return $Matches[1].ToLowerInvariant() + } + # Native stderr merged via 2>&1 becomes ErrorRecords, which would throw + # under $ErrorActionPreference = "Stop"; relax it for this one call. + $previous = $ErrorActionPreference + $ErrorActionPreference = "Continue" + try { + $banner = (& $cl.Source 2>&1 | Out-String) + } finally { + $ErrorActionPreference = $previous + } + if ($banner -match '\bfor\s+(x64|x86|ARM64|ARM)\b') { return $Matches[1].ToLowerInvariant() } + return "" +} + +function Get-ToolchainArchitectureProblem { + # Returns a description of why $Arch is unusable, or "" if it is fine. + # x86 and ARM64 fail for completely different reasons and deserve + # different advice: x86 means the wrong shell was opened and is a + # one-command fix, ARM64 means the platform is genuinely unsupported. + param([string]$Arch) + if ($Arch -eq "" -or $Arch -eq "x64" -or $Arch -eq "amd64") { return "" } + if ($Arch -eq "x86") { + # Single-quoted: the cmd one-liner contains both double quotes and + # backticks, which are literal here but would need escaping in a + # double-quoted PowerShell string. + $vcvarsHint = 'for /f "usebackq delims=" %i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -property installationPath`) do call "%i\VC\Auxiliary\Build\vcvars64.bat"' + return ("cl.exe targets x86, but RandBLAS and its BLAS backends are 64-bit " + + "(x64).`n" + + " You are in a 32-bit developer shell. 'Developer PowerShell for VS 2022' and " + + "'Developer Command Prompt for VS 2022' both default to x86.`n" + + " Fix: open 'x64 Native Tools Command Prompt for VS 2022' from the Start menu, " + + "or run this in any Command Prompt (any edition or version):`n" + + " $vcvarsHint`n" + + " Then delete the RandNLA-project directory before retrying: dependencies already " + + "configured by the x86 compiler are reused as-is and would keep failing.") + } + return ("cl.exe targets $Arch, which this installer does not support: the Windows build " + + "is x64-only.`n" + + " Intel oneMKL publishes no $Arch build, and the OpenBLAS binaries pinned here are " + + "x64. Supplying an $Arch BLAS/LAPACK through -Backend custom is the only route, and " + + "it is untested.`n" + + " If you meant to build x64, open 'x64 Native Tools Command Prompt for VS 2022'.") +} diff --git a/CMake/RandBLASConfig.cmake.in b/CMake/RandBLASConfig.cmake.in index 3ac3f89a..4b5ee52c 100644 --- a/CMake/RandBLASConfig.cmake.in +++ b/CMake/RandBLASConfig.cmake.in @@ -50,4 +50,11 @@ endif() # MKL sparse BLAS set(RandBLAS_HAS_MKL @RandBLAS_HAS_MKL@) +# Provides randblas_stage_runtime_dlls(), which copies a target's +# imported DLL dependencies next to the executable. Windows searches the +# executable's own directory first and PATH last, so a consumer that links +# installed RandBLAS otherwise cannot find the BLAS DLLs at run time. Exported +# rather than kept build-tree-only so consumers do not each reinvent it. +include("${CMAKE_CURRENT_LIST_DIR}/RuntimeDLLs.cmake") + include(RandBLAS) diff --git a/CMake/rb_config.cmake b/CMake/rb_config.cmake index 83e2c903..ec8ae331 100644 --- a/CMake/rb_config.cmake +++ b/CMake/rb_config.cmake @@ -11,7 +11,14 @@ configure_file(CMake/RandBLASConfigVersion.cmake.in ${CMAKE_INSTALL_LIBDIR}/cmake/RandBLAS/RandBLASConfigVersion.cmake @ONLY) if (PROJECT_NAME STREQUAL "RandBLAS") - install(FILES CMake/FindRandom123.cmake + # RuntimeDLLs.cmake ships with the package because downstream Windows + # consumers need randblas_stage_runtime_dlls() as much as this project + # does: on Windows the loader searches the executable's own directory + # first and PATH last, so an executable linking installed RandBLAS has no + # way to find the BLAS DLLs unless they are staged beside it. Without + # this, the function exists only in the build tree and every consumer has + # to reinvent it. + install(FILES CMake/FindRandom123.cmake CMake/RuntimeDLLs.cmake DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/RandBLAS") endif() diff --git a/install/install.ps1 b/install/install.ps1 new file mode 100644 index 00000000..fd2a43e3 --- /dev/null +++ b/install/install.ps1 @@ -0,0 +1,364 @@ +# RandBLAS autoinstaller for native Windows (MSVC). +# +# Builds RandBLAS and the dependencies it needs into a self-contained +# "RandNLA-project" directory, the same layout install.sh produces on Linux and +# macOS: +# lib: dependency sources +# install: RandBLAS-install and the dependency installs +# build: one build directory per project above +# +# Nothing is installed system-wide, no PATH entry is created, and your +# environment is not modified unless you pass -ModifyEnvironment. +# +# You bring Visual Studio (or the Build Tools), CMake and Git, in an x64 +# developer shell. This script does not install a toolchain; when one is +# missing or wrong it says so and tells you how to fix it. +# +# Prerequisites and supported configurations are in INSTALL.md. + +[CmdletBinding()] +param( + # Where dependencies, builds and installs go. Defaults to + # $env:RANDNLA_PROJECT_DIR when set -- which is what lets this installer + # and RandLAPACK's share one dependency tree -- and otherwise to a + # RandNLA-project directory beside this clone. + [string] $ProjectDir = "", + + # Where the dependency stack lives. Defaults to \install. CI + # points this at a cache shared with the core workflow. + [string] $DependencyRoot = "", + + # Install RandBLAS itself here instead of \install\RandBLAS-install. + # Dependencies still go in the project directory. + [string] $Prefix = "", + + [int] $Jobs = 0, + [switch] $Fresh, + [switch] $SkipTests, + [switch] $Examples, + [switch] $ModifyEnvironment, + [switch] $Yes +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path +$repoDir = Split-Path -Parent $scriptDir + +#============================================================================== +# Toolchain preflight. +# +# The architecture guard is the important one. "Developer PowerShell for VS" +# and "Developer Command Prompt for VS" both default to an *x86* toolchain, and +# an x86 linker cannot use the x64 import libraries every BLAS backend here +# ships. Without this check the failure surfaces three layers down as BLAS++ +# reporting "BLAS library not found", which blames the libraries when the +# compiler is at fault. +#============================================================================== +. (Join-Path $repoDir ".github\scripts\windows\toolchain-arch.ps1") + +$missing = @() +foreach ($tool in @("cl.exe", "cmake.exe", "git.exe")) { + if (-not (Get-Command $tool -ErrorAction SilentlyContinue)) { $missing += $tool } +} +if ($missing.Count -gt 0) { + Write-Host "" + Write-Host "PREREQUISITE MISSING: $($missing -join ', ') not found on PATH." -ForegroundColor Red + Write-Host "" + Write-Host " RandBLAS needs Visual Studio (or the Build Tools) with the C++ workload," + Write-Host " plus CMake and Git, in an x64 developer shell." + Write-Host "" + Write-Host " Open 'x64 Native Tools Command Prompt for VS 2022' from the Start menu," + Write-Host " or run this in any Command Prompt to configure one:" + Write-Host "" + Write-Host ' for /f "usebackq delims=" %i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -property installationPath`) do call "%i\VC\Auxiliary\Build\vcvars64.bat"' + Write-Host "" + Write-Host " See INSTALL.md for the full prerequisite list." + exit 1 +} + +$arch = Get-ClTargetArchitecture +$archProblem = Get-ToolchainArchitectureProblem $arch +if ($archProblem) { + Write-Host "" + Write-Host "WRONG TOOLCHAIN ARCHITECTURE" -ForegroundColor Red + Write-Host "" + Write-Host " $archProblem" + Write-Host "" + exit 1 +} + +#============================================================================== +# Interactivity. +# +# Prompts happen only when someone is there to answer: not with -Yes, and not +# when stdin is redirected. Every question has a defensible unattended default +# so an automated run cannot hang. +#============================================================================== +$script:Interactive = -not $Yes -and -not [Console]::IsInputRedirected ` + -and [Environment]::UserInteractive + +function Read-YesNo { + param([string] $Question, [bool] $Default) + if (-not $script:Interactive) { return $Default } + $suffix = if ($Default) { "[Y/n]" } else { "[y/N]" } + while ($true) { + $reply = (Read-Host "$Question $suffix").Trim() + if ($reply -eq "") { return $Default } + if ($reply -match '^(y|yes)$') { return $true } + if ($reply -match '^(n|no)$') { return $false } + } +} + +if ($Jobs -le 0) { + $Jobs = [Environment]::ProcessorCount +} + +#============================================================================== +# Project layout. +# +# Precedence matches install.sh exactly: the flag, then RANDNLA_PROJECT_DIR, +# then a sibling of this clone. Honouring the environment variable is what +# lets a machine that has already run RandLAPACK's installer reuse its BLAS++ +# rather than building a second copy. +#============================================================================== +if (-not $ProjectDir) { + if ($env:RANDNLA_PROJECT_DIR) { + $ProjectDir = $env:RANDNLA_PROJECT_DIR + } else { + $ProjectDir = Join-Path (Split-Path $repoDir -Parent) "RandNLA-project" + } +} +$ProjectDir = [IO.Path]::GetFullPath($ProjectDir) + +# Deep dependency build trees plus MSVC's own path limits make long project +# paths fail in ways that are hard to attribute, so warn before the build +# rather than after. +if ($ProjectDir.Length -gt 150) { + Write-Warning ("ProjectDir is $($ProjectDir.Length) characters long. Deep dependency " + + "build trees may exceed Windows path limits; consider something shorter, such as C:\RandNLA.") +} + +if (-not $DependencyRoot) { $DependencyRoot = Join-Path $ProjectDir "install" } +$DependencyRoot = [IO.Path]::GetFullPath($DependencyRoot) + +$installDir = if ($Prefix) { + [IO.Path]::GetFullPath($Prefix) +} else { + Join-Path $ProjectDir "install\RandBLAS-install" +} +$buildDir = Join-Path $ProjectDir "build\RandBLAS-build" + +foreach ($d in @($ProjectDir, $DependencyRoot, (Join-Path $ProjectDir "lib"), (Join-Path $ProjectDir "build"))) { + New-Item -ItemType Directory -Force -Path $d | Out-Null +} +if ($Fresh -and (Test-Path -LiteralPath $buildDir)) { + Remove-Item -Recurse -Force -LiteralPath $buildDir +} +New-Item -ItemType Directory -Force -Path $buildDir | Out-Null + +Write-Host "" +Write-Host "RandBLAS installer" -ForegroundColor Cyan +Write-Host " toolchain x64 ($arch)" +Write-Host " project dir $ProjectDir" +Write-Host " dependencies $DependencyRoot" +Write-Host " install to $installDir" +Write-Host "" + +#============================================================================== +# Dependencies. +# +# Delegated to the same provisioner CI uses, so there is one implementation of +# "fetch oneMKL, build BLAS++, build GoogleTest" rather than two that drift. +# It pins every source to an immutable ref and records provenance, so a +# dependency is reused only when it came from what we would fetch now. +#============================================================================== +$setup = Join-Path $repoDir ".github\actions\setup-randblas-deps-windows\setup.ps1" +$setupArgs = @{ DependencyRoot = $DependencyRoot } +if ($Examples) { $setupArgs["InstallLapackpp"] = $true } + +Write-Host "[1/4] Provisioning dependencies (oneMKL, BLAS++, Random123, GoogleTest) ..." +# No $LASTEXITCODE check: setup.ps1 is a PowerShell script that sets +# $ErrorActionPreference = "Stop" and throws, so a failure propagates on its +# own. Reading $LASTEXITCODE here would be worse than redundant -- it is unset +# until some native command runs, and Set-StrictMode turns reading an unset +# variable into an error. That made the whole installer fail on exactly the +# runs where every dependency was already cached and no native command had run. +& $setup @setupArgs + +#============================================================================== +# RandBLAS. +#============================================================================== +$cmakeArgs = @( + "-S", $repoDir, + "-B", $buildDir, + "-G", "Ninja", + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_INSTALL_PREFIX=$($installDir.Replace('\','/'))", + "-Dblaspp_DIR=$($env:blaspp_DIR)", + "-DRandom123_DIR=$($env:Random123_DIR)" +) +if ($SkipTests) { + $cmakeArgs += "-DBUILD_TESTS=OFF" +} else { + $cmakeArgs += @("-DBUILD_TESTS=ON", "-DGTest_ROOT=$($env:googletest_PREFIX)") +} + +# Ninja is not guaranteed present outside a full Visual Studio install; fall +# back to the NMake generator the CI provisioner already uses. +if (-not (Get-Command "ninja.exe" -ErrorAction SilentlyContinue)) { + $cmakeArgs[5] = "NMake Makefiles" +} + +Write-Host "[2/4] Configuring RandBLAS ..." +& cmake @cmakeArgs +if ($LASTEXITCODE -ne 0) { throw "CMake configure failed." } + +Write-Host "[3/4] Building and installing RandBLAS ..." +& cmake --build $buildDir -j $Jobs --target install +if ($LASTEXITCODE -ne 0) { throw "Build failed." } + +#============================================================================== +# Verification. +# +# Compile, link and run a program against the finished install. Configuring is +# not the same as producing something that works: this catches a BLAS that +# resolves at configure time but fails to link, and a runtime DLL that was +# never staged beside the executable. +#============================================================================== +Write-Host "[4/4] Verifying the install links and runs ..." +$conftest = Join-Path $ProjectDir "build\conftest" +if (Test-Path -LiteralPath $conftest) { Remove-Item -Recurse -Force -LiteralPath $conftest } +New-Item -ItemType Directory -Force -Path (Join-Path $conftest "src") | Out-Null + +Set-Content -Path (Join-Path $conftest "src\CMakeLists.txt") -Encoding ascii -Value @( + 'cmake_minimum_required(VERSION 3.21)', + 'project(randblas_conftest CXX)', + 'find_package(RandBLAS REQUIRED)', + 'add_executable(conftest conftest.cc)', + 'target_link_libraries(conftest RandBLAS)', + 'randblas_stage_runtime_dlls(conftest)') + +Set-Content -Path (Join-Path $conftest "src\conftest.cc") -Encoding ascii -Value @( + '#include ', + '#include ', + '#include ', + '#include ', + '#include ', + '#include ', + 'int main() {', + '#if defined(BLAS_ILP64)', + ' std::printf("blas_ilp64=1\n");', + '#else', + ' std::printf("blas_ilp64=0\n");', + '#endif', + ' const int64_t m = 8, n = 4;', + ' std::vector S(m * n);', + ' RandBLAS::DenseDist D(m, n);', + ' RandBLAS::RNGState state(0);', + ' RandBLAS::fill_dense(D, S.data(), state);', + ' std::vector C(n * n, 0.0);', + ' blas::gemm(blas::Layout::ColMajor, blas::Op::Trans, blas::Op::NoTrans,', + ' n, n, m, 1.0, S.data(), m, S.data(), m, 0.0, C.data(), n);', + ' for (int64_t i = 0; i < n; ++i) {', + ' if (!(C[i + i * n] > 0.0) || !std::isfinite(C[i + i * n])) return 1;', + ' }', + ' std::printf("OK\n");', + ' return 0;', + '}') + +$conftestGenerator = if (Get-Command "ninja.exe" -ErrorAction SilentlyContinue) { "Ninja" } else { "NMake Makefiles" } +& cmake -S (Join-Path $conftest "src") -B (Join-Path $conftest "build") -G $conftestGenerator ` + "-DCMAKE_BUILD_TYPE=Release" ` + "-DCMAKE_PREFIX_PATH=$($installDir.Replace('\','/'))" ` + "-Dblaspp_DIR=$($env:blaspp_DIR)" ` + "-DRandom123_DIR=$($env:Random123_DIR)" | Out-Host +if ($LASTEXITCODE -ne 0) { throw "The verification program failed to configure." } +& cmake --build (Join-Path $conftest "build") | Out-Host +if ($LASTEXITCODE -ne 0) { throw "The verification program failed to build." } + +$conftestExe = Get-ChildItem -LiteralPath (Join-Path $conftest "build") -Recurse -Filter "conftest.exe" | + Select-Object -First 1 +if (-not $conftestExe) { throw "The verification program built but produced no executable." } +$conftestOutput = & $conftestExe.FullName +if ($LASTEXITCODE -ne 0 -or ($conftestOutput -notcontains "OK")) { + throw "The verification program ran but did not produce a correct result:`n$($conftestOutput -join "`n")" +} +$observedWidth = if ($conftestOutput -contains "blas_ilp64=1") { + "ILP64 (64-bit BLAS integers)" +} else { + "LP64 (32-bit BLAS integers)" +} + +#============================================================================== +# Optional: persist RANDNLA_PROJECT_DIR. +# +# Opt-in, mirroring install.sh's --modify-rc. SetEnvironmentVariable at User +# scope is the Windows equivalent of appending to a shell profile, and the only +# mechanism that survives a new shell. +#============================================================================== +if ($ModifyEnvironment) { + [Environment]::SetEnvironmentVariable("RANDNLA_PROJECT_DIR", $ProjectDir, "User") + Write-Host "" + Write-Host "Set RANDNLA_PROJECT_DIR=$ProjectDir for your user account (open a new shell to pick it up)." +} + +#============================================================================== +# Summary. +#============================================================================== +Write-Host "" +Write-Host "RandBLAS installed successfully." -ForegroundColor Green +Write-Host "" +Write-Host " Backend oneMKL, $observedWidth" +Write-Host " Project layout $ProjectDir" +Write-Host " Installed library $installDir" +Write-Host "" +if (-not $SkipTests) { + Write-Host " Run the test suite:" + Write-Host " ctest --test-dir $buildDir" + Write-Host "" +} +Write-Host " Consume from CMake with:" +Write-Host " -DRandBLAS_DIR=$($installDir.Replace('\','/'))/lib/cmake/RandBLAS" +if (-not $ModifyEnvironment) { + Write-Host "" + Write-Host " To have other RandNLA installers reuse these dependencies, set:" + Write-Host " setx RANDNLA_PROJECT_DIR `"$ProjectDir`"" + Write-Host " (or re-run with -ModifyEnvironment)" +} + +if (-not $Examples) { + Write-Host "" + Write-Host " The examples are not built by default: they additionally need" + Write-Host " LAPACK++ and fast_matrix_market, and they require OpenMP." + $buildNow = Read-YesNo " Build them now?" $false + if (-not $buildNow) { + Write-Host " To build them later, re-run with -Examples:" + Write-Host " powershell -ExecutionPolicy Bypass -File $scriptDir\install.ps1 -Examples -ProjectDir `"$ProjectDir`"" + Write-Host "" + exit 0 + } + $Examples = $true + & $setup -DependencyRoot $DependencyRoot -InstallLapackpp +} + +if ($Examples) { + $examplesBuild = Join-Path $ProjectDir "build\examples-build" + Write-Host "" + Write-Host "Configuring and building examples ..." + & cmake -S (Join-Path $repoDir "examples") -B $examplesBuild -G $conftestGenerator ` + "-DCMAKE_BUILD_TYPE=Release" ` + "-DCMAKE_PREFIX_PATH=$($installDir.Replace('\','/'))" ` + "-Dblaspp_DIR=$($env:blaspp_DIR)" ` + "-Dlapackpp_DIR=$($env:lapackpp_DIR)" ` + "-DRandom123_DIR=$($env:Random123_DIR)" ` + "-DFETCHCONTENT_BASE_DIR=$($ProjectDir.Replace('\','/'))/build/fetchcontent-cache" | Out-Host + if ($LASTEXITCODE -ne 0) { throw "Examples failed to configure." } + & cmake --build $examplesBuild -j $Jobs | Out-Host + if ($LASTEXITCODE -ne 0) { throw "Examples failed to build." } + Write-Host "" + Write-Host "Examples built: $examplesBuild" -ForegroundColor Green +} + +Write-Host ""