237 lines
8.8 KiB
PowerShell
237 lines
8.8 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Builds YAMS MSI installer using WiX Toolset v4+
|
|
|
|
.DESCRIPTION
|
|
This script builds an MSI installer for YAMS from the meson install output.
|
|
It requires WiX Toolset v4+ to be installed via: dotnet tool install --global wix
|
|
|
|
.PARAMETER StageDir
|
|
Path to the meson install --destdir output directory.
|
|
Default: .\build\release\stage
|
|
|
|
.PARAMETER Version
|
|
Product version string (e.g., 0.1.0 or nightly-20251126-abc1234).
|
|
Default: extracted from meson.build or "0.0.0"
|
|
|
|
.PARAMETER OutputDir
|
|
Directory where the MSI will be created.
|
|
Default: .\build\release
|
|
|
|
.EXAMPLE
|
|
.\build-msi.ps1 -StageDir .\build\release\stage -Version 0.1.0
|
|
#>
|
|
|
|
param(
|
|
[string]$StageDir = ".\build\release\stage",
|
|
[string]$Version = "",
|
|
[string]$OutputDir = ".\build\release"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$RepoRoot = (Get-Item "$ScriptDir\..\..").FullName
|
|
|
|
# If StageDir doesn't exist, try to create it via meson install
|
|
if (-not (Test-Path $StageDir)) {
|
|
Write-Host "Stage directory not found: $StageDir" -ForegroundColor Yellow
|
|
|
|
# Try to find the build directory
|
|
$buildDir = Split-Path $StageDir -Parent
|
|
if (-not (Test-Path (Join-Path $buildDir "build.ninja"))) {
|
|
# Default to build/release
|
|
$buildDir = Join-Path $RepoRoot "build\release"
|
|
}
|
|
|
|
if (Test-Path (Join-Path $buildDir "build.ninja")) {
|
|
Write-Host "Found build at: $buildDir" -ForegroundColor Cyan
|
|
|
|
# Reconfigure with root prefix for clean MSI staging
|
|
Write-Host "Reconfiguring meson with root prefix for MSI staging..." -ForegroundColor Cyan
|
|
& meson configure $buildDir --prefix=/
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Warning "meson configure failed, continuing with current prefix..."
|
|
}
|
|
|
|
Write-Host "Running meson install to create stage directory..." -ForegroundColor Cyan
|
|
$stageParent = Split-Path $StageDir -Parent
|
|
if (-not (Test-Path $stageParent)) {
|
|
New-Item -ItemType Directory -Path $stageParent -Force | Out-Null
|
|
}
|
|
|
|
& meson install -C $buildDir --destdir $StageDir
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Error "meson install failed. Please build first: .\setup.ps1 -BuildType Release"
|
|
exit 1
|
|
}
|
|
} else {
|
|
Write-Error @"
|
|
Stage directory not found and no build available.
|
|
|
|
Please either:
|
|
1. Build first: .\setup.ps1 -BuildType Release
|
|
2. Then package: .\setup.ps1 -BuildType Release -Package
|
|
|
|
Or manually:
|
|
1. Build: meson compile -C build/release
|
|
2. Stage: meson install -C build/release --destdir build/release/stage
|
|
3. Package: .\packaging\windows\build-msi.ps1 -StageDir build/release/stage
|
|
"@
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# Verify WiX is installed
|
|
Write-Host "Checking for WiX Toolset..." -ForegroundColor Cyan
|
|
$wixPath = Get-Command wix -ErrorAction SilentlyContinue
|
|
if (-not $wixPath) {
|
|
Write-Host "WiX Toolset not found. Installing via dotnet tool..." -ForegroundColor Yellow
|
|
& dotnet tool install --global wix --version 6.0.2
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Error "Failed to install WiX Toolset. Please install manually: dotnet tool install --global wix --version 6.0.2"
|
|
exit 1
|
|
}
|
|
# Refresh PATH
|
|
$env:PATH = [System.Environment]::GetEnvironmentVariable("PATH", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("PATH", "User")
|
|
}
|
|
|
|
# Verify WiX version
|
|
Write-Host "WiX version:" -ForegroundColor Cyan
|
|
& wix --version
|
|
|
|
# Add WiX UI extension (pin to 6.0.2 to match WiX toolset version)
|
|
Write-Host "Adding WiX UI extension..." -ForegroundColor Cyan
|
|
& wix extension add -g WixToolset.UI.wixext/6.0.2
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Warning "WiX UI extension may already be installed, continuing..."
|
|
}
|
|
|
|
# Resolve stage directory
|
|
$StageDir = (Resolve-Path $StageDir -ErrorAction Stop).Path
|
|
Write-Host "Stage directory: $StageDir" -ForegroundColor Cyan
|
|
|
|
# Find yams.exe anywhere in the stage directory
|
|
# meson install with --destdir prepends destdir to prefix, creating nested paths
|
|
Write-Host "Searching for yams.exe in stage directory..." -ForegroundColor Cyan
|
|
$yamsExeFiles = Get-ChildItem -Path $StageDir -Recurse -Filter "yams.exe" -ErrorAction SilentlyContinue
|
|
|
|
if ($yamsExeFiles.Count -eq 0) {
|
|
Write-Error "yams.exe not found anywhere in stage directory: $StageDir"
|
|
exit 1
|
|
}
|
|
|
|
# Use the first found yams.exe and derive the bin directory
|
|
$yamsBin = $yamsExeFiles[0].FullName
|
|
$binDir = Split-Path $yamsBin -Parent
|
|
|
|
# The "effective stage root" is the parent of bin/
|
|
$EffectiveStageDir = Split-Path $binDir -Parent
|
|
|
|
Write-Host "Found yams.exe at: $yamsBin" -ForegroundColor Green
|
|
Write-Host "Effective stage root: $EffectiveStageDir" -ForegroundColor Cyan
|
|
|
|
# Use EffectiveStageDir for WiX (it contains bin/yams.exe)
|
|
$StageDir = $EffectiveStageDir
|
|
|
|
$tbbDll = Join-Path $StageDir "bin\tbb12.dll"
|
|
$includeTbb = if (Test-Path $tbbDll) { "true" } else { "false" }
|
|
if ($includeTbb -eq "true") {
|
|
Write-Host "Including TBB runtime DLL: $tbbDll" -ForegroundColor Cyan
|
|
} else {
|
|
Write-Host "TBB runtime DLL not staged; skipping TBB component." -ForegroundColor Yellow
|
|
}
|
|
|
|
# Create LICENSE.rtf for WiX installer (WiX requires RTF format)
|
|
$licenseSource = Join-Path $RepoRoot "LICENSE"
|
|
$licenseRtf = Join-Path $StageDir "LICENSE.rtf"
|
|
if (Test-Path $licenseSource) {
|
|
Write-Host "Converting LICENSE to RTF format..." -ForegroundColor Cyan
|
|
$licenseText = Get-Content $licenseSource -Raw
|
|
# Simple RTF wrapper - escape special chars and wrap in RTF
|
|
$licenseText = $licenseText -replace '\\', '\\\\' -replace '\{', '\{' -replace '\}', '\}'
|
|
$rtfContent = "{\rtf1\ansi\deff0{\fonttbl{\f0 Consolas;}}\f0\fs18 " + ($licenseText -replace "`r`n", "\par`r`n" -replace "`n", "\par`r`n") + "}"
|
|
$rtfContent | Out-File -FilePath $licenseRtf -Encoding ascii
|
|
Write-Host "License RTF created at: $licenseRtf" -ForegroundColor Cyan
|
|
} else {
|
|
Write-Warning "LICENSE file not found at: $licenseSource"
|
|
}
|
|
|
|
# Extract version if not provided
|
|
if ([string]::IsNullOrEmpty($Version)) {
|
|
$mesonBuild = Join-Path $RepoRoot "meson.build"
|
|
if (Test-Path $mesonBuild) {
|
|
$content = Get-Content $mesonBuild -Raw
|
|
if ($content -match "version\s*:\s*'([^']+)'") {
|
|
$Version = $matches[1]
|
|
Write-Host "Extracted version from meson.build: $Version" -ForegroundColor Cyan
|
|
}
|
|
}
|
|
if ([string]::IsNullOrEmpty($Version)) {
|
|
$Version = "0.0.0"
|
|
Write-Host "Using default version: $Version" -ForegroundColor Yellow
|
|
}
|
|
}
|
|
|
|
# Normalize version for MSI (must be X.Y.Z.W format)
|
|
# Handle nightly-YYYYMMDD-hash format
|
|
if ($Version -match "nightly-(\d{4})(\d{2})(\d{2})-") {
|
|
$year = [int]$matches[1] - 2020 # Offset from 2020
|
|
$month = [int]$matches[2]
|
|
$day = [int]$matches[3]
|
|
$MsiVersion = "$year.$month.$day.0"
|
|
Write-Host "Normalized nightly version to MSI format: $MsiVersion" -ForegroundColor Cyan
|
|
} elseif ($Version -match "^(\d+)\.(\d+)\.(\d+)") {
|
|
$MsiVersion = "$($matches[1]).$($matches[2]).$($matches[3]).0"
|
|
} else {
|
|
$MsiVersion = "0.0.0.0"
|
|
Write-Host "Could not parse version, using: $MsiVersion" -ForegroundColor Yellow
|
|
}
|
|
|
|
# Create output directory
|
|
$OutputDir = (New-Item -ItemType Directory -Force -Path $OutputDir).FullName
|
|
$MsiName = "yams-$Version-windows-x86_64.msi"
|
|
$MsiPath = Join-Path $OutputDir $MsiName
|
|
|
|
Write-Host "`nBuilding MSI..." -ForegroundColor Cyan
|
|
Write-Host " Version: $Version (MSI: $MsiVersion)" -ForegroundColor Gray
|
|
Write-Host " Stage: $StageDir" -ForegroundColor Gray
|
|
Write-Host " Output: $MsiPath" -ForegroundColor Gray
|
|
|
|
# Build MSI using WiX
|
|
$wxsPath = Join-Path $ScriptDir "yams.wxs"
|
|
& wix build $wxsPath `
|
|
-o $MsiPath `
|
|
-d Version=$MsiVersion `
|
|
-d StageDir=$StageDir `
|
|
-d IncludeTbb=$includeTbb `
|
|
-ext WixToolset.UI.wixext
|
|
|
|
if ($LASTEXITCODE -ne 0) {
|
|
Write-Error "WiX build failed with exit code $LASTEXITCODE"
|
|
exit 1
|
|
}
|
|
|
|
# Verify MSI was created
|
|
if (Test-Path $MsiPath) {
|
|
$msiSize = (Get-Item $MsiPath).Length / 1MB
|
|
Write-Host "`nMSI created successfully!" -ForegroundColor Green
|
|
Write-Host " Path: $MsiPath" -ForegroundColor Gray
|
|
Write-Host " Size: $([math]::Round($msiSize, 2)) MB" -ForegroundColor Gray
|
|
|
|
# Generate SHA256 hash
|
|
$hash = (Get-FileHash -Path $MsiPath -Algorithm SHA256).Hash.ToLower()
|
|
Write-Host " SHA256: $hash" -ForegroundColor Gray
|
|
|
|
# Write hash to file
|
|
$hashFile = "$MsiPath.sha256"
|
|
"$hash $MsiName" | Out-File -FilePath $hashFile -Encoding utf8 -NoNewline
|
|
Write-Host " Hash file: $hashFile" -ForegroundColor Gray
|
|
} else {
|
|
Write-Error "MSI file was not created at expected path: $MsiPath"
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "`nTo install:" -ForegroundColor Cyan
|
|
Write-Host " msiexec /i `"$MsiPath`"" -ForegroundColor White
|
|
Write-Host "`nTo uninstall:" -ForegroundColor Cyan
|
|
Write-Host " msiexec /x `"$MsiPath`"" -ForegroundColor White
|