|
<# |
|
.SYNOPSIS |
|
Build llama.cpp TurboQuant fork with AMD ROCm support for Windows |
|
|
|
.DESCRIPTION |
|
This script automates the build process for the TurboQuant fork of llama.cpp |
|
with AMD ROCm GPU acceleration support. It follows the build steps from the |
|
llamacpp-rocm GitHub Actions workflow and adapts them for TurboQuant. |
|
|
|
Tested With GPU: AMD Radeon RX 7900 XT (gfx1100 architecture) |
|
|
|
.PARAMETER ROCmVersion |
|
ROCm version to use. Default: "latest" (auto-detect latest nightly) |
|
|
|
.PARAMETER GfxTarget |
|
AMD GPU architecture target. Default: "gfx110X" (for RX 7900 series) |
|
Options: gfx110X, gfx1151, gfx1150, gfx120X, gfx103X |
|
|
|
.PARAMETER BuildDir |
|
Directory for build artifacts. Default: ".\build" |
|
|
|
.PARAMETER CleanBuild |
|
Force clean build by removing existing directories and repositories. When $true, performs a fresh clone; when $false, reuses existing repository if available. Default: $false |
|
|
|
.PARAMETER SkipPrerequisites |
|
Skip installation of prerequisites (Visual Studio, CMake, Ninja). Default: $false |
|
|
|
.PARAMETER DetectGpu |
|
Auto-detect GPU and set appropriate GFX target. Default: $true |
|
|
|
.PARAMETER KeepROCmTarball |
|
Keep the downloaded ROCm tarball after extraction. Default: $true |
|
|
|
.PARAMETER DetectGpuOnly |
|
Only detect GPU information without building. Useful for testing detection. Default: $false |
|
|
|
.EXAMPLE |
|
.\build-llama-turboquant.ps1 |
|
|
|
.EXAMPLE |
|
.\build-llama-turboquant.ps1 -ROCmVersion "7.13.0a20260318" -GfxTarget "gfx110X" |
|
|
|
.EXAMPLE |
|
.\build-llama-turboquant.ps1 -SkipPrerequisites:$true -CleanBuild:$true |
|
|
|
.NOTES |
|
Author: Lukylix/AI Assistant |
|
Date: 2026-04-03 |
|
Requires: Windows 10/11, PowerShell 5.1+, Administrator privileges (for prerequisites) |
|
#> |
|
|
|
[CmdletBinding()] |
|
param( |
|
[Parameter()] |
|
[string]$ROCmVersion = "latest", |
|
|
|
[Parameter()] |
|
[ValidateSet("gfx110X", "gfx1151", "gfx1150", "gfx120X", "gfx103X")] |
|
[string]$GfxTarget = "gfx110X", |
|
|
|
[Parameter()] |
|
[string]$BuildDir = ".\build", |
|
|
|
[Parameter()] |
|
[bool]$CleanBuild = $false, |
|
|
|
[Parameter()] |
|
[bool]$SkipPrerequisites = $false, |
|
|
|
[Parameter()] |
|
[bool]$DetectGpu = $true, |
|
|
|
[Parameter()] |
|
[bool]$KeepROCmTarball = $true, |
|
|
|
[Parameter()] |
|
[bool]$DetectGpuOnly = $false |
|
) |
|
$ProgressPreference = "SilentlyContinue" # Faster downloads |
|
|
|
# Global cache for GPU detection result |
|
$Global:GpuInfoCache = $null |
|
|
|
# Centralized configuration object |
|
$Config = @{ |
|
RepoUrl = "https://github.com/TheTom/llama-cpp-turboquant.git" |
|
RepoBranch = "feature/turboquant-kv-cache" |
|
RocmInstallPath = "C:\\opt\\rocm" |
|
RocmS3BaseUrl = "https://therock-nightly-tarball.s3.amazonaws.com" |
|
CMakeGenerator = "Ninja" |
|
CMakeBuildType = "Release" |
|
FallbackGfxTarget = "gfx1030" |
|
BuildDirName = "build" |
|
} |
|
|
|
# ROCm configuration |
|
$GPU_EXAMPLE = "AMD Radeon RX 7900 XT" |
|
|
|
# Context management |
|
function Initialize-BuildContext { |
|
param( |
|
[string]$GfxTarget, |
|
[string]$ROCmVersion |
|
) |
|
|
|
return @{ |
|
GfxTarget = $GfxTarget |
|
ROCmVersion = $ROCmVersion |
|
RepoPath = "llama.cpp" |
|
CommitHash = $null |
|
S3Target = $null |
|
MappedTargets= $null |
|
RocmInfo = $null |
|
} |
|
} |
|
|
|
function Set-S3Target { |
|
param($Context) |
|
|
|
# Use the new resolution function to find a valid S3 target |
|
$Context.S3Target = Resolve-S3Target -GpuTarget $Context.GfxTarget -BaseUrl $Config.RocmS3BaseUrl |
|
return $Context |
|
} |
|
|
|
function Invoke-DownloadROCm { |
|
param($Context) |
|
|
|
$Context.RocmInfo = Download-ROCm ` |
|
-Version $Context.ROCmVersion ` |
|
-S3Target $Context.S3Target ` |
|
-ReuseExisting $KeepROCmTarball |
|
|
|
return $Context |
|
} |
|
|
|
function Set-MappedTargets { |
|
param($Context) |
|
|
|
$Context.MappedTargets = Get-MappedGfxTargets -GfxTarget $Context.GfxTarget |
|
return $Context |
|
} |
|
|
|
# Logging configuration |
|
$VerboseLogging = $true |
|
|
|
# Unified logging helper |
|
function Write-Log { |
|
param( |
|
[Parameter(Mandatory)] |
|
[string]$Message, |
|
|
|
[ValidateSet("INFO","SUCCESS","WARN","ERROR","STEP","DEBUG")] |
|
[string]$Level = "INFO", |
|
|
|
[switch]$NoTimestamp |
|
) |
|
|
|
$timestamp = Get-Date -Format "HH:mm:ss" |
|
|
|
$prefix = switch ($Level) { |
|
"STEP" { "[STEP]" } |
|
"SUCCESS" { "[OK] " } |
|
"WARN" { "[WARN]" } |
|
"ERROR" { "[FAIL]" } |
|
"DEBUG" { "[DBG] " } |
|
default { "[INFO]" } |
|
} |
|
|
|
$color = switch ($Level) { |
|
"SUCCESS" { "Green" } |
|
"WARN" { "Yellow" } |
|
"ERROR" { "Red" } |
|
"STEP" { "Cyan" } |
|
"DEBUG" { "DarkGray" } |
|
default { "Gray" } |
|
} |
|
|
|
if ($NoTimestamp) { |
|
Write-Host "$prefix $Message" -ForegroundColor $color |
|
} else { |
|
Write-Host "[$timestamp] $prefix $Message" -ForegroundColor $color |
|
} |
|
} |
|
|
|
# Legacy wrapper for backward compatibility (remove after updating all calls) |
|
function Write-ColorOutput { |
|
param( |
|
[Parameter(Mandatory=$true)] |
|
[AllowEmptyString()] |
|
$Message, |
|
[Parameter()] |
|
[ValidateSet("Info", "Success", "Warning", "Error")] |
|
[string]$Level = "Info" |
|
) |
|
|
|
$logLevel = switch ($Level) { |
|
"Info" { "INFO" } |
|
"Success" { "SUCCESS" } |
|
"Warning" { "WARN" } |
|
"Error" { "ERROR" } |
|
default { "INFO" } |
|
} |
|
|
|
Write-Log -Message $Message -Level $logLevel |
|
} |
|
|
|
function Write-Step { |
|
param( |
|
[string]$Title, |
|
[string[]]$Details = @() |
|
) |
|
|
|
Write-Log $Title "STEP" |
|
|
|
foreach ($line in $Details) { |
|
Write-Host " $line" -ForegroundColor Gray |
|
} |
|
} |
|
|
|
# ============================================================================ |
|
# Helper Functions |
|
# ============================================================================ |
|
|
|
function Test-CommandExists { |
|
param([string]$Command) |
|
|
|
$oldPreference = $ErrorActionPreference |
|
$ErrorActionPreference = "SilentlyContinue" |
|
|
|
try { |
|
if (Get-Command $Command -ErrorAction SilentlyContinue) { |
|
return $true |
|
} |
|
return $false |
|
} |
|
finally { |
|
$ErrorActionPreference = $oldPreference |
|
} |
|
} |
|
|
|
function Test-IsAdmin { |
|
$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent() |
|
$principal = New-Object Security.Principal.WindowsPrincipal($currentUser) |
|
return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) |
|
} |
|
|
|
function Install-Prerequisites { |
|
if ($VerboseLogging) { |
|
Write-Log "Checking and installing prerequisites..." "DEBUG" |
|
} |
|
|
|
# Check if running as administrator for installations |
|
if (-not (Test-IsAdmin)) { |
|
Write-Log "Administrator privileges required" "WARN" |
|
Write-Log "Please run as Administrator or use -SkipPrerequisites" "WARN" |
|
exit 1 |
|
} |
|
|
|
# Check for Visual Studio Build Tools |
|
$vsWhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" |
|
if (Test-Path $vsWhere) { |
|
$vsPath = & $vsWhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath |
|
if ($vsPath) { |
|
Write-Log "Visual Studio Build Tools found: $vsPath" "SUCCESS" |
|
} else { |
|
if ($VerboseLogging) { Write-Log "Installing Visual Studio Build Tools..." "DEBUG" } |
|
Install-VisualStudioBuildTools |
|
} |
|
} else { |
|
if ($VerboseLogging) { Write-Log "Installing Visual Studio Build Tools..." "DEBUG" } |
|
Install-VisualStudioBuildTools |
|
} |
|
|
|
# Check for CMake |
|
if (-not (Test-CommandExists "cmake")) { |
|
if ($VerboseLogging) { Write-Log "Installing CMake via winget..." "DEBUG" } |
|
winget install --id Kitware.CMake --silent --accept-source-agreements --accept-package-agreements |
|
# Refresh PATH |
|
Refresh-Path |
|
} else { |
|
$cmakeVersion = (cmake --version | Select-Object -First 1) |
|
Write-Log "CMake found: $cmakeVersion" "SUCCESS" |
|
} |
|
|
|
# Check for Ninja |
|
if (-not (Test-CommandExists "ninja")) { |
|
if ($VerboseLogging) { Write-Log "Installing Ninja build system..." "DEBUG" } |
|
Install-Ninja |
|
} else { |
|
$ninjaVersion = (ninja --version) |
|
Write-Log "Ninja found: version $ninjaVersion" "SUCCESS" |
|
} |
|
|
|
# Check for Perl (required for ROCm) |
|
if (-not (Test-CommandExists "perl")) { |
|
if ($VerboseLogging) { Write-Log "Installing Strawberry Perl..." "DEBUG" } |
|
Install-StrawberryPerl |
|
} else { |
|
Write-Log "Perl found" "SUCCESS" |
|
} |
|
|
|
# Check for Git |
|
if (-not (Test-CommandExists "git")) { |
|
if ($VerboseLogging) { Write-Log "Installing Git..." "DEBUG" } |
|
winget install --id Git.Git --silent --accept-source-agreements --accept-package-agreements |
|
# Refresh PATH |
|
Refresh-Path |
|
} else { |
|
Write-Log "Git found" "SUCCESS" |
|
} |
|
|
|
Write-Log "All prerequisites installed" "SUCCESS" |
|
} |
|
|
|
function Install-VisualStudioBuildTools { |
|
$vsInstallerUrl = "https://aka.ms/vs/17/release/vs_buildtools.exe" |
|
$vsInstallerPath = "$env:TEMP\vs_buildtools.exe" |
|
|
|
if ($VerboseLogging) { Write-Log "Downloading Visual Studio Build Tools..." "DEBUG" } |
|
Invoke-WebRequest -Uri $vsInstallerUrl -OutFile $vsInstallerPath -UseBasicParsing |
|
|
|
if ($VerboseLogging) { Write-Log "Installing Visual Studio Build Tools (this may take several minutes)..." "DEBUG" } |
|
$process = Start-Process -FilePath $vsInstallerPath -ArgumentList @( |
|
"--quiet", |
|
"--wait", |
|
"--norestart", |
|
"--add", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", |
|
"--add", "Microsoft.VisualStudio.Component.VC.CMake.Project", |
|
"--add", "Microsoft.VisualStudio.Component.VC.ATL", |
|
"--add", "Microsoft.VisualStudio.Component.Windows11SDK.22621" |
|
) -Wait -PassThru |
|
|
|
if ($process.ExitCode -ne 0) { |
|
Write-Log "Visual Studio Build Tools installation failed with exit code $($process.ExitCode)" "ERROR" |
|
exit 1 |
|
} |
|
|
|
Remove-Item $vsInstallerPath -Force -ErrorAction SilentlyContinue |
|
Write-Log "Visual Studio Build Tools installed successfully" "SUCCESS" |
|
} |
|
|
|
function Install-Ninja { |
|
$ninjaUrl = "https://github.com/ninja-build/ninja/releases/download/v1.11.1/ninja-win.zip" |
|
$ninjaPath = "$env:TEMP\ninja-win.zip" |
|
$ninjaDir = "C:\ninja" |
|
|
|
New-Item -ItemType Directory -Force -Path $ninjaDir | Out-Null |
|
|
|
Write-ColorOutput "Downloading Ninja..." "Info" |
|
Invoke-WebRequest -Uri $ninjaUrl -OutFile $ninjaPath -UseBasicParsing |
|
|
|
Write-ColorOutput "Extracting Ninja..." "Info" |
|
Expand-Archive -Path $ninjaPath -DestinationPath $ninjaDir -Force |
|
|
|
# Add to system PATH if not already present |
|
$systemPath = [Environment]::GetEnvironmentVariable("Path", "Machine") |
|
if ($systemPath -notlike "*$ninjaDir*") { |
|
[Environment]::SetEnvironmentVariable("Path", "$systemPath;$ninjaDir", "Machine") |
|
} |
|
$env:Path = "$env:Path;$ninjaDir" |
|
|
|
Remove-Item $ninjaPath -Force -ErrorAction SilentlyContinue |
|
Write-ColorOutput "Ninja installed successfully to $ninjaDir" "Success" |
|
} |
|
|
|
function Install-StrawberryPerl { |
|
$perlUrl = "https://strawberryperl.com/download/5.32.1.1/strawberry-perl-5.32.1.1-64bit.msi" |
|
$perlPath = "$env:TEMP\strawberry-perl.msi" |
|
|
|
if ($VerboseLogging) { Write-Log "Downloading Strawberry Perl..." "DEBUG" } |
|
Invoke-WebRequest -Uri $perlUrl -OutFile $perlPath -UseBasicParsing |
|
|
|
if ($VerboseLogging) { Write-Log "Installing Strawberry Perl..." "DEBUG" } |
|
$process = Start-Process msiexec.exe -ArgumentList "/i `"$perlPath`" /quiet /norestart" -Wait -PassThru |
|
|
|
if ($process.ExitCode -ne 0) { |
|
Write-ColorOutput "Strawberry Perl installation failed with exit code $($process.ExitCode)" "Error" |
|
exit 1 |
|
} |
|
|
|
# Add to PATH |
|
$env:Path = "C:\Strawberry\perl\bin;$env:Path" |
|
|
|
Remove-Item $perlPath -Force -ErrorAction SilentlyContinue |
|
Write-Log "Strawberry Perl installed successfully" "SUCCESS" |
|
} |
|
|
|
function Get-ROCmS3Target { |
|
param([string]$GfxTarget) |
|
|
|
# Map GFX targets to S3 naming convention |
|
$s3Target = $GfxTarget |
|
switch ($GfxTarget) { |
|
"gfx103X" { $s3Target = "${GfxTarget}-dgpu" } |
|
"gfx110X" { $s3Target = "${GfxTarget}-all" } |
|
"gfx120X" { $s3Target = "${GfxTarget}-all" } |
|
} |
|
|
|
return $s3Target |
|
} |
|
|
|
function Get-GfxTargetFromArchitecture { |
|
param([string]$Arch) |
|
|
|
if (-not $Arch) { |
|
return $null |
|
} |
|
|
|
# Match gfx + digits (e.g. gfx1100, gfx1030) |
|
if ($Arch -match 'gfx(\d{3})\d*') { |
|
$family = $Matches[1] # e.g. 110, 103 |
|
|
|
return "gfx${family}X-all" |
|
} |
|
|
|
return $null |
|
} |
|
|
|
function Get-MappedGfxTargets { |
|
param([string]$GfxTarget) |
|
|
|
# Map shorthand targets to full architecture list |
|
$mapped = switch ($GfxTarget) { |
|
"gfx110X" { "gfx1100;gfx1101;gfx1102;gfx1103" } |
|
"gfx103X" { "gfx1030;gfx1031;gfx1032;gfx1034" } |
|
"gfx120X" { "gfx1200;gfx1201" } |
|
"gfx1151" { "gfx1151" } |
|
"gfx1150" { "gfx1150" } |
|
default { $GfxTarget } |
|
} |
|
|
|
return $mapped |
|
} |
|
|
|
# S3 Target Candidate Resolution Functions |
|
function Get-S3TargetCandidates { |
|
param([string]$GpuTarget) |
|
|
|
if (-not $GpuTarget) { |
|
return @("gfx110X-all") # final fallback |
|
} |
|
|
|
# If user already provided full target |
|
if ($GpuTarget -match '^gfx\d{3}X-(all|dgpu)$') { |
|
return @($GpuTarget) |
|
} |
|
|
|
# If only family provided (gfx101X) |
|
if ($GpuTarget -match '^gfx\d{3}X$') { |
|
return @( |
|
"$GpuTarget-all", |
|
"$GpuTarget-dgpu" |
|
) |
|
} |
|
|
|
throw "Invalid GPU target format: $GpuTarget" |
|
} |
|
|
|
# Test S3 prefix existence using existing Invoke-WebRequest pattern |
|
function Test-S3TargetExists { |
|
param( |
|
[string]$BaseUrl, |
|
[string]$Prefix |
|
) |
|
|
|
$url = "$BaseUrl/?prefix=$Prefix" |
|
|
|
try { |
|
$response = Invoke-WebRequest -Uri $url -UseBasicParsing |
|
return $response.Content -match '<Key>.+</Key>' |
|
} |
|
catch { |
|
return $false |
|
} |
|
} |
|
|
|
function Resolve-S3Target { |
|
param( |
|
[string]$GpuTarget, |
|
[string]$BaseUrl |
|
) |
|
|
|
$candidates = Get-S3TargetCandidates $GpuTarget |
|
|
|
foreach ($target in $candidates) { |
|
$prefix = "therock-dist-windows-$target" |
|
if (Test-S3TargetExists $BaseUrl $prefix) { |
|
if ($VerboseLogging) { |
|
Write-Log "Selected S3 target: $target" "DEBUG" |
|
} |
|
return $target |
|
} |
|
} |
|
|
|
throw "No valid S3 target found for: $GpuTarget" |
|
} |
|
|
|
function Detect-GpuInfo { |
|
if ($VerboseLogging) { |
|
Write-Log "Detecting GPU via WMI..." "DEBUG" |
|
} |
|
|
|
$gpuList = @() |
|
|
|
try { |
|
$gpuList = Get-CimInstance Win32_VideoController | |
|
Where-Object { $_.Name -match "AMD|Radeon" } |
|
} catch { |
|
Write-Log "WMI detection failed: $_" "WARN" |
|
} |
|
|
|
if (-not $gpuList -or $gpuList.Count -eq 0) { |
|
Write-Log "No AMD GPU detected, using fallback: $($Config.FallbackGfxTarget)" "WARN" |
|
return [PSCustomObject]@{ |
|
Gfx = $Config.FallbackGfxTarget |
|
Name = "Unknown AMD GPU" |
|
} |
|
} |
|
|
|
$gpu = $gpuList[0] |
|
$name = $gpu.Name |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Detected GPU: $name" "DEBUG" |
|
} |
|
|
|
$normalized = $name.ToLower() |
|
|
|
$gfx = switch -Regex ($normalized) { |
|
"7900" { "gfx1100" } |
|
"7800" { "gfx1101" } |
|
"7700" { "gfx1103" } |
|
"7600" { "gfx1102" } |
|
"6950|6900|6800" { "gfx1030" } |
|
"6700|6600" { "gfx1031" } |
|
"5700" { "gfx1031" } |
|
"vega" { "gfx900" } |
|
default { $null } |
|
} |
|
|
|
if (-not $gfx) { |
|
Write-Log "Could not map GPU automatically, using fallback: $($Config.FallbackGfxTarget)" "WARN" |
|
$gfx = $Config.FallbackGfxTarget |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Mapped GFX target: $gfx" "DEBUG" |
|
} |
|
|
|
return [PSCustomObject]@{ |
|
Gfx = $gfx |
|
Name = $name |
|
} |
|
} |
|
|
|
function Get-LatestROCmVersion { |
|
param([string]$S3Target) |
|
|
|
Write-Log "Auto-detecting latest ROCm version for target: $S3Target" "DEBUG" |
|
|
|
try { |
|
if (-not $S3Target) { |
|
throw "S3Target is empty. Cannot query ROCm versions." |
|
} |
|
|
|
$s3Url = "$($Config.RocmS3BaseUrl)/?prefix=therock-dist-windows-$S3Target" |
|
|
|
$response = Invoke-WebRequest -Uri $s3Url -UseBasicParsing |
|
$content = $response.Content |
|
|
|
$files = [regex]::Matches($content, '<Key>([^<]+)</Key>') | ForEach-Object { |
|
$_.Groups[1].Value |
|
} |
|
|
|
if (-not $files -or $files.Count -eq 0) { |
|
throw "No files returned from S3 listing" |
|
} |
|
|
|
# Filter valid tarballs |
|
$versionFiles = foreach ($file in $files) { |
|
if ($file -match "therock-dist-windows-${S3Target}-.*?(\d+\.\d+\.\d+(?:a|rc)\d+)\.tar\.gz"){ |
|
$version = $matches[1] |
|
|
|
if ($version -match '(\d+)\.(\d+)\.(\d+)(?:(a|rc)(\d+))?') { |
|
[PSCustomObject]@{ |
|
File = $file |
|
Version = $version |
|
Major = [int]$matches[1] |
|
Minor = [int]$matches[2] |
|
Patch = [int]$matches[3] |
|
Type = if ($matches[4]) { $matches[4] } else { "release" } |
|
RC = if ($matches[5]) { [int]$matches[5] } else { 0 } |
|
IsAlpha = $version -match 'a' |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (-not $versionFiles) { |
|
throw "No matching ROCm tarballs found for target $S3Target" |
|
} |
|
|
|
# Sort versions properly |
|
$latest = $versionFiles | |
|
Sort-Object Major, Minor, Patch, @{Expression={if($_.IsAlpha){1}else{0}}}, RC -Descending | |
|
Select-Object -First 1 |
|
|
|
Write-Log "Latest ROCm version detected: $($latest.Version)" "SUCCESS" |
|
|
|
return @{ |
|
Version = $latest.Version |
|
Url = "$($Config.RocmS3BaseUrl)/$($latest.File)" |
|
} |
|
} |
|
catch { |
|
Write-Log "Failed to auto-detect ROCm version: $($_.Exception.Message)" "ERROR" |
|
exit 1 |
|
} |
|
} |
|
|
|
function Download-ROCm { |
|
param( |
|
[string]$Version, |
|
[string]$S3Target, |
|
[bool]$ReuseExisting = $true |
|
) |
|
|
|
$rocmTarball = "rocm.tar.gz" |
|
|
|
# Check if tarball already exists |
|
if ($ReuseExisting -and (Test-Path $rocmTarball)) { |
|
if ($VerboseLogging) { |
|
Write-Log "Found existing ROCm tarball: $rocmTarball" "DEBUG" |
|
Write-Log "Skipping download (use -KeepROCmTarball:`$false to force re-download)" "DEBUG" |
|
} |
|
|
|
# Try to determine version from tarball name or use provided version |
|
$detectedVersion = if ($Version -eq "latest") { |
|
$rocmInfo = Get-LatestROCmVersion -S3Target $S3Target |
|
$rocmInfo.Version |
|
} else { |
|
$Version |
|
} |
|
|
|
return $detectedVersion |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Downloading ROCm $Version..." "DEBUG" |
|
} |
|
|
|
$rocmInfo = if ($Version -eq "latest") { |
|
Get-LatestROCmVersion -S3Target $S3Target |
|
} else { |
|
@{ |
|
Version = $Version |
|
Url = "$($Config.RocmS3BaseUrl)/therock-dist-windows-${S3Target}-${Version}.tar.gz" |
|
} |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Downloading from: $($rocmInfo.Url)" "DEBUG" |
|
} |
|
|
|
try { |
|
# Use System.Net.WebClient for better progress reporting |
|
$webClient = New-Object System.Net.WebClient |
|
|
|
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action { |
|
$percent = $EventArgs.ProgressPercentage |
|
Write-Progress -Activity "Downloading ROCm" -Status "$percent% Complete" -PercentComplete $percent |
|
} | Out-Null |
|
|
|
$webClient.DownloadFile($rocmInfo.Url, (Resolve-Path .).Path + "\$rocmTarball") |
|
$webClient.Dispose() |
|
|
|
Write-Progress -Activity "Downloading ROCm" -Completed |
|
if ($VerboseLogging) { |
|
Write-Log "ROCm tarball downloaded successfully" "DEBUG" |
|
} |
|
|
|
return $rocmInfo.Version |
|
} |
|
catch { |
|
Write-Log "Failed to download ROCm: $_" "ERROR" |
|
exit 1 |
|
} |
|
} |
|
|
|
function Extract-ROCm { |
|
param( |
|
[bool]$ReuseExisting = $true |
|
) |
|
|
|
# Check if ROCm is already extracted and valid |
|
if ($ReuseExisting -and (Test-Path $Config.RocmInstallPath)) { |
|
$clangPath = Join-Path $Config.RocmInstallPath "lib\llvm\bin\clang.exe" |
|
if (Test-Path $clangPath) { |
|
if ($VerboseLogging) { |
|
Write-Log "Found existing ROCm installation at: $($Config.RocmInstallPath)" "DEBUG" |
|
Write-Log "Skipping extraction (use -ReuseROCm:`$false to force re-extract)" "DEBUG" |
|
} |
|
return |
|
} else { |
|
Write-Log "Existing ROCm installation appears incomplete, re-extracting..." "WARN" |
|
Remove-Item -Recurse -Force $Config.RocmInstallPath |
|
} |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Extracting ROCm to $Config.RocmInstallPath..." "DEBUG" |
|
} |
|
|
|
# Create directory if it doesn't exist |
|
New-Item -ItemType Directory -Force -Path $Config.RocmInstallPath | Out-Null |
|
|
|
# Extract tarball using tar (built-in on Windows 10+) |
|
$result = tar -xzf "rocm.tar.gz" -C $Config.RocmInstallPath --strip-components=1 2>&1 |
|
|
|
if ($LASTEXITCODE -ne 0) { |
|
Write-Log "Failed to extract ROCm: $result" "ERROR" |
|
exit 1 |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "ROCm extracted successfully" "DEBUG" |
|
} |
|
} |
|
|
|
function Clone-TurboQuant { |
|
param( |
|
[bool]$ReuseExisting = $true |
|
) |
|
|
|
$repoPath = "llama.cpp" |
|
|
|
# Check if repository already exists |
|
if ($ReuseExisting -and (Test-Path $repoPath)) { |
|
if ($VerboseLogging) { |
|
Write-Log "Found existing llama.cpp repository" "DEBUG" |
|
} |
|
|
|
Push-Location $repoPath |
|
|
|
# Check if it's a git repository |
|
$isGitRepo = Test-Path ".git" |
|
|
|
if ($isGitRepo) { |
|
if ($VerboseLogging) { |
|
Write-Log "Checking repository status..." "DEBUG" |
|
} |
|
|
|
# Get current branch |
|
$currentBranch = git rev-parse --abbrev-ref HEAD 2>&1 |
|
|
|
if ($currentBranch -eq $Config.RepoBranch) { |
|
if ($VerboseLogging) { |
|
Write-Log "Already on branch: $($Config.RepoBranch)" "DEBUG" |
|
Write-Log "Fetching latest commit..." "DEBUG" |
|
} |
|
|
|
# 🔧 Always fetch latest state (works with shallow clones) |
|
git fetch origin $Config.RepoBranch --depth=1 2>&1 | Out-Null |
|
|
|
if ($LASTEXITCODE -ne 0) { |
|
Write-Log "Failed to fetch branch" "ERROR" |
|
Pop-Location |
|
exit 1 |
|
} |
|
|
|
git reset --hard FETCH_HEAD 2>&1 | Out-Null |
|
|
|
if ($LASTEXITCODE -ne 0) { |
|
Write-Log "Failed to reset repository to fetched commit" "ERROR" |
|
Pop-Location |
|
exit 1 |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Repository updated to latest commit (FETCH_HEAD)" "DEBUG" |
|
} |
|
} else { |
|
if ($VerboseLogging) { |
|
Write-Log "Repository is on branch '$currentBranch', switching to '$($Config.RepoBranch)'..." "DEBUG" |
|
} |
|
|
|
# Fetch the latest from the target branch first |
|
git fetch origin $Config.RepoBranch 2>&1 | Out-Null |
|
|
|
if ($LASTEXITCODE -ne 0) { |
|
Write-Log "Failed to fetch branch" "ERROR" |
|
Pop-Location |
|
exit 1 |
|
} |
|
|
|
git checkout $Config.RepoBranch --force 2>&1 | Out-Null |
|
|
|
if ($LASTEXITCODE -ne 0) { |
|
Write-Log "Failed to switch branch" "ERROR" |
|
Pop-Location |
|
exit 1 |
|
} |
|
} |
|
|
|
# Get the current commit hash after reset/pull |
|
$commitHash = git rev-parse --short=7 HEAD |
|
$commitDate = git log -1 --format=%cd --date=short |
|
Pop-Location |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Using repository (commit: $commitHash, date: $commitDate)" "DEBUG" |
|
} |
|
return $commitHash |
|
} else { |
|
Pop-Location |
|
if ($VerboseLogging) { |
|
Write-Log "Directory exists but is not a git repository, removing..." "DEBUG" |
|
} |
|
Remove-DirectorySafe -Path $repoPath |
|
} |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Cloning TurboQuant llama.cpp fork..." "DEBUG" |
|
} |
|
|
|
try { |
|
$gitOutput = git clone --quiet --depth 1 --single-branch --branch $Config.RepoBranch $Config.RepoUrl "llama.cpp" 2>&1 |
|
|
|
if ($LASTEXITCODE -ne 0) { |
|
throw "git clone failed: $gitOutput" |
|
} |
|
|
|
Write-Verbose "Repository cloned successfully" |
|
} catch { |
|
Write-Log "Failed to clone TurboQuant repository" "ERROR" |
|
Write-Error $_ |
|
exit 1 |
|
} |
|
|
|
Push-Location $repoPath |
|
|
|
$commitHash = git rev-parse --short=7 HEAD 2>$null |
|
$commitHash = $commitHash.Trim() |
|
|
|
Pop-Location |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "TurboQuant cloned successfully (commit: $commitHash)" "DEBUG" |
|
} |
|
|
|
return $commitHash |
|
} |
|
|
|
function Apply-Patches { |
|
if ($VerboseLogging) { |
|
Write-Log "Checking for patches to apply..." "DEBUG" |
|
} |
|
|
|
# Store the original directory (before entering llama.cpp) |
|
$originalDir = Get-Location |
|
$patchDir = Join-Path $originalDir "patches" |
|
|
|
if (-not (Test-Path $patchDir)) { |
|
if ($VerboseLogging) { |
|
Write-Log "No patches directory found at: $patchDir" "DEBUG" |
|
Write-Log "Skipping patch application" "DEBUG" |
|
} |
|
return |
|
} |
|
|
|
$patchFiles = Get-ChildItem -Path $patchDir -Filter "*.patch" -File | Sort-Object Name |
|
|
|
if ($patchFiles.Count -eq 0) { |
|
if ($VerboseLogging) { |
|
Write-Log "No patch files found in $patchDir" "DEBUG" |
|
} |
|
return |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Found $($patchFiles.Count) patch file(s) to apply" "DEBUG" |
|
} |
|
|
|
Push-Location "llama.cpp" |
|
|
|
foreach ($patchFile in $patchFiles) { |
|
Write-ColorOutput " Applying: $($patchFile.Name)" "Info" |
|
|
|
# Use full path to patch file |
|
$patchPath = $patchFile.FullName |
|
|
|
# Try to apply the patch |
|
$result = git apply "$patchPath" 2>&1 |
|
|
|
if ($LASTEXITCODE -eq 0) { |
|
if ($VerboseLogging) { |
|
Write-Log " ✓ Successfully applied: $($patchFile.Name)" "DEBUG" |
|
} |
|
} else { |
|
# Check if patch is already applied |
|
$checkResult = git apply --check "$patchPath" 2>&1 |
|
|
|
if ($checkResult -match "already exists in working directory" -or |
|
$checkResult -match "patch does not apply") { |
|
Write-Log " ⚠ Patch already applied or not needed: $($patchFile.Name)" "WARN" |
|
} else { |
|
Write-Log " ✗ Failed to apply patch: $($patchFile.Name)" "ERROR" |
|
Write-Log " Error: $result" "ERROR" |
|
Pop-Location |
|
exit 1 |
|
} |
|
} |
|
} |
|
|
|
Pop-Location |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Patch application completed" "DEBUG" |
|
} |
|
} |
|
|
|
function Build-LlamaCpp { |
|
param( |
|
[string]$MappedTargets |
|
) |
|
|
|
# Set up Visual Studio environment |
|
if ($VerboseLogging) { |
|
Write-Log "Setting up Visual Studio environment..." "DEBUG" |
|
} |
|
|
|
$vsPath = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath |
|
|
|
if (-not $vsPath) { |
|
Write-Log "Visual Studio installation not found" "ERROR" |
|
exit 1 |
|
} |
|
|
|
$vcVarsPath = Join-Path $vsPath "VC\Auxiliary\Build\vcvars64.bat" |
|
|
|
if (-not (Test-Path $vcVarsPath)) { |
|
Write-Log "vcvars64.bat not found at: $vcVarsPath" "ERROR" |
|
exit 1 |
|
} |
|
|
|
# Set environment variables for ROCm |
|
$env:HIP_PATH = $Config.RocmInstallPath |
|
$env:HIP_PLATFORM = "amd" |
|
$env:PATH = "$($Config.RocmInstallPath)\lib\llvm\bin;$($Config.RocmInstallPath)\bin;$env:PATH" |
|
|
|
# Create build directory |
|
Push-Location "llama.cpp" |
|
New-Item -ItemType Directory -Force -Path "build" | Out-Null |
|
Push-Location "build" |
|
|
|
# CMake configure |
|
$cmakeArgs = @( |
|
"..", |
|
"-G", $Config.CMakeGenerator, |
|
"-DCMAKE_C_COMPILER=$($Config.RocmInstallPath)\lib\llvm\bin\clang.exe", |
|
"-DCMAKE_CXX_COMPILER=$($Config.RocmInstallPath)\lib\llvm\bin\clang++.exe", |
|
"-DCMAKE_CXX_FLAGS=-I$($Config.RocmInstallPath)\include", |
|
"-DCMAKE_CROSSCOMPILING=ON", |
|
"-DCMAKE_BUILD_TYPE=$($Config.CMakeBuildType)", |
|
"-DGPU_TARGETS=$MappedTargets", |
|
"-DBUILD_SHARED_LIBS=ON", |
|
"-DLLAMA_BUILD_TESTS=OFF", |
|
"-DGGML_HIP=ON", |
|
"-DGGML_OPENMP=OFF", |
|
"-DGGML_CUDA_FORCE_CUBLAS=OFF", |
|
"-DGGML_RPC=ON", |
|
"-DGGML_HIP_ROCWMMA_FATTN=OFF", |
|
"-DLLAMA_BUILD_BORINGSSL=ON", |
|
"-DGGML_NATIVE=OFF", |
|
"-DGGML_STATIC=OFF", |
|
"-DCMAKE_SYSTEM_NAME=Windows" |
|
) |
|
|
|
# Run CMake configure with vcvars environment |
|
if ($VerboseLogging) { |
|
Write-Log "Running CMake configuration..." "DEBUG" |
|
} |
|
|
|
$cmakeCmd = "call `"$vcVarsPath`" && cmake $($cmakeArgs -join ' ')" |
|
|
|
$result = cmd /c $cmakeCmd 2>&1 |
|
|
|
if ($LASTEXITCODE -ne 0) { |
|
Write-Log "CMake configuration failed" "ERROR" |
|
if ($result) { |
|
Write-Host "`nCMake output:" -ForegroundColor Red |
|
Write-Host ($result -join "`n") -ForegroundColor Red |
|
} |
|
Pop-Location |
|
Pop-Location |
|
exit 1 |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "CMake configuration completed successfully" "DEBUG" |
|
Write-Log "Building llama.cpp with Ninja..." "DEBUG" |
|
} |
|
|
|
# Build |
|
if ($VerboseLogging) { |
|
Write-Log "Starting build process..." "DEBUG" |
|
} |
|
|
|
$buildCmd = "call `"$vcVarsPath`" && cmake --build . -j $env:NUMBER_OF_PROCESSORS" |
|
|
|
$result = cmd /c $buildCmd 2>&1 |
|
|
|
if ($LASTEXITCODE -ne 0) { |
|
Write-Log "Build failed" "ERROR" |
|
if ($result) { |
|
Write-Host "`nBuild output:" -ForegroundColor Red |
|
Write-Host ($result -join "`n") -ForegroundColor Red |
|
} |
|
Pop-Location |
|
Pop-Location |
|
exit 1 |
|
} |
|
|
|
Pop-Location |
|
Pop-Location |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Build completed successfully" "DEBUG" |
|
} |
|
} |
|
|
|
function Copy-ROCmLibraries { |
|
if ($VerboseLogging) { |
|
Write-Log "Copying ROCm runtime libraries to build directory..." "DEBUG" |
|
} |
|
|
|
$buildBinPath = "llama.cpp\build\bin" |
|
$rocmBinPath = "$($Config.RocmInstallPath)\bin" |
|
|
|
if (-not (Test-Path $rocmBinPath)) { |
|
Write-Log "ROCm bin directory not found: $rocmBinPath" "ERROR" |
|
exit 1 |
|
} |
|
|
|
# List of DLL patterns to copy |
|
$dllPatterns = @( |
|
"amdhip64_*.dll", |
|
"rocm_kpack.dll", |
|
"amd_comgr*.dll", |
|
"libhipblas.dll", |
|
"rocblas.dll", |
|
"rocsolver.dll", |
|
"hipblaslt.dll", |
|
"libhipblaslt.dll", |
|
"hipblas.dll" |
|
) |
|
|
|
foreach ($pattern in $dllPatterns) { |
|
$matchingFiles = Get-ChildItem -Path $rocmBinPath -Filter $pattern -ErrorAction SilentlyContinue |
|
|
|
if ($matchingFiles) { |
|
foreach ($file in $matchingFiles) { |
|
Copy-Item $file.FullName -Destination $buildBinPath -Force |
|
if ($VerboseLogging) { |
|
Write-Log " Copied: $($file.Name)" "DEBUG" |
|
} |
|
} |
|
} else { |
|
Write-Log " Warning: No files found matching pattern: $pattern" "WARN" |
|
} |
|
} |
|
|
|
# Copy rocblas/library folder |
|
$rocblasLibPath = Join-Path $rocmBinPath "rocblas\library" |
|
if (Test-Path $rocblasLibPath) { |
|
if ($VerboseLogging) { |
|
Write-Log " Copying rocblas\library folder..." "DEBUG" |
|
} |
|
$destRocblasPath = Join-Path $buildBinPath "rocblas\library" |
|
Copy-Item -Path $rocblasLibPath -Destination $destRocblasPath -Recurse -Force |
|
if ($VerboseLogging) { |
|
Write-Log " Copied rocblas\library folder" "DEBUG" |
|
} |
|
} else { |
|
Write-Log " Warning: rocblas\library folder not found at: $rocblasLibPath" "WARN" |
|
} |
|
|
|
# Copy hipblaslt/library folder |
|
$hipblasltLibPath = Join-Path $rocmBinPath "hipblaslt\library" |
|
if (Test-Path $hipblasltLibPath) { |
|
if ($VerboseLogging) { |
|
Write-Log " Copying hipblaslt\library folder..." "DEBUG" |
|
} |
|
$destHipblasltPath = Join-Path $buildBinPath "hipblaslt\library" |
|
Copy-Item -Path $hipblasltLibPath -Destination $destHipblasltPath -Recurse -Force |
|
if ($VerboseLogging) { |
|
Write-Log " Copied hipblaslt\library folder" "DEBUG" |
|
} |
|
} else { |
|
Write-Log " Warning: hipblaslt\library folder not found at: $hipblasltLibPath" "WARN" |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "ROCm libraries copied successfully" "DEBUG" |
|
} |
|
} |
|
|
|
function New-BuildSummary { |
|
param( |
|
[string]$ROCmVersion, |
|
[string]$CommitHash, |
|
[string]$GfxTarget |
|
) |
|
|
|
$summaryPath = "build-summary.txt" |
|
|
|
$summary = @" |
|
================================================================================ |
|
TurboQuant llama.cpp + ROCm Build Summary |
|
================================================================================ |
|
|
|
Build Date: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss") |
|
Platform: Windows |
|
GPU Target: $GfxTarget ($($Global:GpuInfoCache.Name)) |
|
ROCm Version: $ROCmVersion |
|
TurboQuant Commit: $CommitHash |
|
TurboQuant Branch: $($Config.RepoBranch) |
|
Build Type: $($Config.CMakeBuildType) |
|
CMake Generator: $($Config.CMakeGenerator) |
|
|
|
Build Configuration: |
|
HIP Support: Enabled |
|
RPC Support: Enabled |
|
Shared Libraries: Yes |
|
Native Opt: Disabled (for portability) |
|
OpenMP: Disabled |
|
ROCm WMMA: Disabled |
|
|
|
Build Location: |
|
Source: $(Resolve-Path "llama.cpp") |
|
Binaries: $(Resolve-Path "llama.cpp\build\bin") |
|
|
|
ROCm Installation: |
|
Path: $($Config.RocmInstallPath) |
|
|
|
TurboQuant Features: |
|
TurboQuant provides extreme KV cache quantization (up to 4.57x compression) |
|
for efficient inference with llama.cpp. This fork includes optimizations |
|
specifically for memory-constrained scenarios. |
|
|
|
Branch: $($Config.RepoBranch) |
|
More info: https://github.com/TheTom/llama-cpp-turboquant |
|
|
|
Usage Example: |
|
cd llama.cpp\build\bin |
|
.\llama-server.exe -m model.gguf -ngl 99 |
|
|
|
Next Steps: |
|
1. Copy the contents of llama.cpp\build\bin to your desired location |
|
2. All ROCm runtime libraries are included - no separate ROCm installation needed |
|
3. Test with: llama-cli.exe -m <model.gguf> -p "Hello world" -n 50 |
|
|
|
================================================================================ |
|
"@ |
|
|
|
Set-Content -Path $summaryPath -Value $summary |
|
Write-ColorOutput "Build summary saved to: $summaryPath" "Info" |
|
|
|
# Also display summary |
|
Write-Host "" |
|
Write-Host $summary -ForegroundColor Green |
|
} |
|
|
|
function Remove-DirectorySafe { |
|
param( |
|
[Parameter(Mandatory)] |
|
[string]$Path, |
|
|
|
[int]$MaxRetries = 3, |
|
[int]$DelaySeconds = 2 |
|
) |
|
|
|
if (-not (Test-Path $Path)) { |
|
Write-Verbose "Directory does not exist: $Path" |
|
return |
|
} |
|
|
|
for ($i = 1; $i -le $MaxRetries; $i++) { |
|
try { |
|
Write-Verbose "Attempting to remove: $Path (try $i/$MaxRetries)" |
|
Remove-Item -Recurse -Force $Path -ErrorAction Stop |
|
Write-Verbose "Successfully removed: $Path" |
|
return |
|
} |
|
catch { |
|
Write-Log "Failed to remove ${Path}: $($_.Exception.Message)" "WARN" |
|
|
|
if ($i -eq $MaxRetries) { |
|
throw "Cleanup failed after $MaxRetries attempts. File likely locked.`nPath: $Path" |
|
} |
|
|
|
Start-Sleep -Seconds $DelaySeconds |
|
} |
|
} |
|
} |
|
|
|
function Get-LockingProcessHint { |
|
param([string]$Path) |
|
|
|
Write-Warning "File lock detected. Common causes:" |
|
Write-Warning "- CMake still running" |
|
Write-Warning "- Ninja build in background" |
|
Write-Warning "- VSCode terminal holding handle" |
|
Write-Warning "- Explorer open in that folder" |
|
Write-Warning "Try closing related processes or rebooting shell." |
|
} |
|
|
|
function Cleanup { |
|
param( |
|
[bool]$KeepROCmInstallation = $true, |
|
[bool]$KeepTarball = $true |
|
) |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Cleaning up temporary files..." "DEBUG" |
|
} |
|
|
|
# Remove ROCm tarball with proper error handling |
|
if (-not $KeepTarball -and (Test-Path "rocm.tar.gz")) { |
|
try { |
|
Remove-Item "rocm.tar.gz" -Force -ErrorAction Stop |
|
if ($VerboseLogging) { |
|
Write-Log " Removed rocm.tar.gz" "DEBUG" |
|
} |
|
} catch { |
|
Write-Warning "Failed to remove rocm.tar.gz: $($_.Exception.Message)" |
|
} |
|
} elseif (Test-Path "rocm.tar.gz") { |
|
if ($VerboseLogging) { |
|
Write-Log " Keeping rocm.tar.gz for future use" "DEBUG" |
|
} |
|
} |
|
|
|
# Remove ROCm installation with proper error handling |
|
if (-not $KeepROCmInstallation -and (Test-Path $Config.RocmInstallPath)) { |
|
try { |
|
Write-Verbose "Removing ROCm installation..." |
|
Remove-DirectorySafe -Path $Config.RocmInstallPath |
|
if ($VerboseLogging) { |
|
Write-Log " Removed ROCm installation" "DEBUG" |
|
} |
|
} catch { |
|
Get-LockingProcessHint -Path $Config.RocmInstallPath |
|
throw $_ |
|
} |
|
} else { |
|
if ($VerboseLogging) { |
|
Write-Log " Keeping ROCm installation at $($Config.RocmInstallPath)" "DEBUG" |
|
} |
|
} |
|
|
|
if (-not $CleanBuild -and (Test-Path "llama.cpp")) { |
|
try { |
|
Write-Verbose "Removing existing llama.cpp directory..." |
|
Remove-DirectorySafe -Path "llama.cpp" |
|
if ($VerboseLogging) { |
|
Write-Log " Removed llama.cpp directory" "DEBUG" |
|
} |
|
} catch { |
|
Get-LockingProcessHint -Path "llama.cpp" |
|
throw $_ |
|
} |
|
} |
|
|
|
if ($VerboseLogging) { |
|
Write-Log "Cleanup completed" "DEBUG" |
|
} |
|
} |
|
|
|
# ============================================================================ |
|
# Main Build Process |
|
# ============================================================================ |
|
|
|
function Invoke-BuildPipeline { |
|
$context = Initialize-BuildContext -GfxTarget $GfxTarget -ROCmVersion $ROCmVersion |
|
|
|
Write-Host "" |
|
Write-Host "============================================================================" -ForegroundColor Cyan |
|
Write-Host " TurboQuant llama.cpp + ROCm Build Script" -ForegroundColor Cyan |
|
Write-Host "============================================================================" -ForegroundColor Cyan |
|
Write-Host "" |
|
|
|
# STEP 0: GPU detection FIRST |
|
if ($DetectGpu) { |
|
$Global:GpuInfoCache = Detect-GpuInfo |
|
|
|
if ($Global:GpuInfoCache.Gfx) { |
|
$GfxTarget = $Global:GpuInfoCache.Gfx |
|
$GPU_EXAMPLE = $Global:GpuInfoCache.Name |
|
} |
|
} |
|
|
|
try { |
|
# Step 2.5: Detect GPU if enabled |
|
if ($DetectGpuOnly) { |
|
Write-Step "GPU Detection Result" @( |
|
"GPU: $($Global:GpuInfoCache.Name)", |
|
"GFX: $($Global:GpuInfoCache.Gfx)" |
|
) |
|
exit 0 |
|
} |
|
|
|
# Step 2.6: Normalize GFX target from detected GPU |
|
if ($DetectGpu -and $Global:GpuInfoCache.Gfx) { |
|
Write-Step "GPU Architecture Detection" @( |
|
"Detected GPU: $($Global:GpuInfoCache.Name)", |
|
"Raw architecture: $($Global:GpuInfoCache.Gfx)" |
|
) |
|
|
|
$S3Target = Get-GfxTargetFromArchitecture $Global:GpuInfoCache.Gfx |
|
|
|
if (-not $S3Target) { |
|
throw "Unsupported or undetected GPU architecture: $($Global:GpuInfoCache.Gfx)" |
|
} |
|
|
|
# Update context with normalized target |
|
$context = Initialize-BuildContext -GfxTarget $S3Target -ROCmVersion $ROCmVersion |
|
$context = Set-S3Target $context |
|
|
|
# 🔧 Use clean GFX family for compilation |
|
$cleanGfx = $context.GfxTarget -replace "-all|-dgpu", "" |
|
$context.MappedTargets = Get-MappedGfxTargets -GfxTarget $cleanGfx |
|
} |
|
|
|
# Step 1: Clean existing directories if requested |
|
if ($CleanBuild) { |
|
Write-Log "Cleanup: removing existing directories" "STEP" |
|
|
|
try { |
|
if (Test-Path "llama.cpp") { |
|
Write-Verbose "Removing existing llama.cpp directory..." |
|
Remove-DirectorySafe -Path "llama.cpp" |
|
} |
|
|
|
if (-not $KeepROCmTarball -and (Test-Path "rocm.tar.gz")) { |
|
Write-Verbose "Removing ROCm tarball..." |
|
Remove-Item -Force "rocm.tar.gz" -ErrorAction Stop |
|
} |
|
|
|
Write-Verbose "Cleanup completed successfully" |
|
} catch { |
|
Get-LockingProcessHint -Path "llama.cpp" |
|
Write-Log "Cleanup failed: $($_.Exception.Message)" "ERROR" |
|
exit 1 |
|
} |
|
|
|
Write-Host "" |
|
} else { |
|
Write-Log "Cleanup: skipped (incremental)" "STEP" |
|
Write-Host "" |
|
} |
|
|
|
# Step 2: Install prerequisites |
|
if (-not $SkipPrerequisites) { |
|
Write-Log "Prerequisites: installing" "STEP" |
|
Install-Prerequisites |
|
Write-Host "" |
|
} else { |
|
Write-Log "Prerequisites: skipped" "STEP" |
|
Write-Host "" |
|
} |
|
|
|
# Step 3: Determine ROCm S3 target |
|
if (-not $DetectGpuOnly) { |
|
Write-Step "ROCm Setup" @( |
|
"S3 Target: $($context.S3Target)", |
|
"Version: $($context.ROCmVersion)" |
|
) |
|
} |
|
Write-Host "" |
|
|
|
# Step 4: Download ROCm |
|
if (-not $DetectGpuOnly) { |
|
Write-Log "ROCm: downloading" "STEP" |
|
$context = Invoke-DownloadROCm $context |
|
} |
|
Write-Host "" |
|
|
|
# Step 5: Extract ROCm |
|
if (-not $DetectGpuOnly) { |
|
Write-Log "ROCm: extracting" "STEP" |
|
Extract-ROCm -ReuseExisting $KeepROCmTarball |
|
} |
|
Write-Host "" |
|
|
|
# Step 6: Validate and clone TurboQuant |
|
if (-not $DetectGpuOnly) { |
|
Write-Log "Repository: setting up" "STEP" |
|
|
|
# Validate that cleanup was successful |
|
if ($CleanBuild -and (Test-Path "llama.cpp")) { |
|
Write-Log "Target directory 'llama.cpp' still exists after cleanup. Aborting clone." "ERROR" |
|
exit 1 |
|
} |
|
|
|
$commitHash = Clone-TurboQuant -ReuseExisting (-not $CleanBuild) |
|
} |
|
Write-Host "" |
|
|
|
# Step 6.5: Apply patches |
|
if (-not $DetectGpuOnly) { |
|
Write-Log "Patches: applying" "STEP" |
|
Apply-Patches |
|
} |
|
Write-Host "" |
|
|
|
# Step 7: Build llama.cpp |
|
if (-not $DetectGpuOnly) { |
|
Write-Step "Build Configuration" @( |
|
"GPU Targets: $($context.MappedTargets)", |
|
"Generator: $($Config.CMakeGenerator)", |
|
"Type: $($Config.CMakeBuildType)" |
|
) |
|
Build-LlamaCpp -MappedTargets $context.MappedTargets |
|
} |
|
Write-Host "" |
|
|
|
# Step 8: Copy ROCm libraries |
|
if (-not $DetectGpuOnly) { |
|
Write-Log "Libraries: copying ROCm runtime" "STEP" |
|
Copy-ROCmLibraries |
|
} |
|
Write-Host "" |
|
|
|
# Step 9: Create build summary |
|
if (-not $DetectGpuOnly) { |
|
Write-Log "Summary: creating" "STEP" |
|
New-BuildSummary ` |
|
-ROCmVersion $context.RocmInfo.Version ` |
|
-CommitHash $commitHash ` |
|
-GfxTarget $context.GfxTarget |
|
} |
|
Write-Host "" |
|
|
|
# Step 10: Cleanup |
|
if (-not $DetectGpuOnly) { |
|
Write-Log "Cleanup: removing temporary files" "STEP" |
|
Cleanup -KeepROCmInstallation $true -KeepTarball $KeepROCmTarball |
|
} |
|
Write-Host "" |
|
|
|
if (-not $DetectGpuOnly) { |
|
Write-Log "============================================================================" "SUCCESS" |
|
Write-Log "Build complete | Binaries: llama.cpp\build\bin | All ROCm libraries included" "SUCCESS" |
|
Write-Log "============================================================================" "SUCCESS" |
|
} else { |
|
Write-Log "GPU detection complete" "SUCCESS" |
|
Write-Host "" |
|
} |
|
Write-Host "" |
|
|
|
} |
|
catch { |
|
Write-Log "============================================================================" "ERROR" |
|
$errorMsg = if ($_.Exception -and $_.Exception.Message) { |
|
$_.Exception.Message |
|
} else { |
|
$_ | Out-String |
|
} |
|
Write-Log "Build failed: $errorMsg" "ERROR" |
|
Write-Log "============================================================================" "ERROR" |
|
|
|
# Show full error details for debugging |
|
Write-Host "`nFull Error Details:" -ForegroundColor Red |
|
try { |
|
Write-Host ($_ | Format-List -Force | Out-String) -ForegroundColor Red |
|
} catch { |
|
Write-Host $_ -ForegroundColor Red |
|
} |
|
|
|
exit 1 |
|
} |
|
} |
|
|
|
# ============================================================================ |
|
# Script Entry Point |
|
# ============================================================================ |
|
|
|
Invoke-BuildPipeline |