SwiftPM rebuilds everything when the environment changes. Each run grew LIB in the caller's shell, and each terminal brings its own variables, so no build was ever a no-op. Build from an allowlisted environment and hand the caller's back afterwards.
237 lines
10 KiB
PowerShell
237 lines
10 KiB
PowerShell
# Set environment variables needed to build LumaGtk on Windows.
|
|
#
|
|
# This script is intended to be dot-sourced from build.ps1 or from an
|
|
# interactive PowerShell prompt when iterating on the build manually.
|
|
#
|
|
# . .\scripts\windows\setup-env.ps1
|
|
#
|
|
# Prefix locations can be overridden via -VcpkgPrefix / -FridaPrefix /
|
|
# -R2Prefix / -PharoPrefix, or via the VCPKG_PREFIX / FRIDA_PREFIX /
|
|
# R2_PREFIX / PHARO_PREFIX env vars.
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string] $VcpkgPrefix,
|
|
[string] $FridaPrefix,
|
|
[string] $R2Prefix,
|
|
[string] $PharoPrefix
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Resolve-Prefix {
|
|
param([string] $Explicit, [string] $EnvName, [string[]] $Candidates)
|
|
if ($Explicit) { return (Resolve-Path -LiteralPath $Explicit).Path }
|
|
$fromEnv = [Environment]::GetEnvironmentVariable($EnvName)
|
|
if ($fromEnv) { return (Resolve-Path -LiteralPath $fromEnv).Path }
|
|
foreach ($c in $Candidates) {
|
|
if (Test-Path -LiteralPath $c) { return (Resolve-Path -LiteralPath $c).Path }
|
|
}
|
|
throw @"
|
|
Could not locate $EnvName.
|
|
Run .\scripts\windows\bootstrap.ps1 to provision the dependencies, or
|
|
pass -$($EnvName.Replace('_PREFIX','Prefix')) / set `$env:$EnvName to an existing prefix.
|
|
"@
|
|
}
|
|
|
|
$deps = Join-Path $env:LOCALAPPDATA 'Luma\windows-deps'
|
|
$triplet = if ($env:PROCESSOR_ARCHITECTURE -eq 'ARM64') { 'arm64-windows-release' } else { 'x64-windows-release' }
|
|
|
|
$vcpkg = Resolve-Prefix $VcpkgPrefix 'VCPKG_PREFIX' @(
|
|
(Join-Path $deps "vcpkg\installed\$triplet"),
|
|
"C:\vcpkg\installed\$triplet",
|
|
"C:\src\vcpkg\installed\$triplet"
|
|
)
|
|
$frida = Resolve-Prefix $FridaPrefix 'FRIDA_PREFIX' @((Join-Path $deps 'frida-prefix'), 'C:\src\dist')
|
|
$r2 = Resolve-Prefix $R2Prefix 'R2_PREFIX' @((Join-Path $deps 'r2-prefix'), 'C:\src\dist')
|
|
$pharo = Resolve-Prefix $PharoPrefix 'PHARO_PREFIX' @((Join-Path $deps 'pharo-prefix'), 'C:\src\pharo')
|
|
|
|
# frida-core, radare2 and pharo-vm install .pc files with an absolute prefix
|
|
# like `prefix=C:/src/dist`. vcpkg's pkgconf trips over the colon in
|
|
# the drive letter and emits "Expected a value for variable 'prefix'",
|
|
# leaving every ${prefix}-derived variable unresolved — which in turn
|
|
# makes Swift's .systemLibrary(pkgConfig:) resolver return no include
|
|
# paths. Rewrite the prefix to a pcfiledir-relative form so the file
|
|
# is relocatable and colon-free. Idempotent.
|
|
function Repair-PkgConfigPrefix {
|
|
param([string] $PkgConfigDir)
|
|
if (-not (Test-Path $PkgConfigDir)) { return }
|
|
Get-ChildItem -Path $PkgConfigDir -Filter '*.pc' -File | ForEach-Object {
|
|
$content = Get-Content -Raw -LiteralPath $_.FullName
|
|
if ($content -match '(?m)^prefix=\$\{pcfiledir\}') { return }
|
|
$patched = $content -replace '(?m)^prefix=.*', 'prefix=${pcfiledir}/../..'
|
|
[System.IO.File]::WriteAllText($_.FullName, $patched, [System.Text.UTF8Encoding]::new($false))
|
|
}
|
|
}
|
|
Repair-PkgConfigPrefix (Join-Path $frida 'lib\pkgconfig')
|
|
Repair-PkgConfigPrefix (Join-Path $r2 'lib\pkgconfig')
|
|
Repair-PkgConfigPrefix (Join-Path $pharo 'lib\pkgconfig')
|
|
|
|
# SwiftPM emits a `-L<dir> -l<lib>` pair for every library, repeating the same
|
|
# few lib dirs hundreds of times. On the GTK/frida/r2 stack the final link
|
|
# overshoots Windows' ~32 KB command-line limit and swift build aborts with
|
|
# "The filename or extension is too long". Mirror each pkgconfig dir into a
|
|
# sibling `pkgconfig-nolibpath` with the `-L` stripped from its Libs lines, and
|
|
# put the real lib dirs on the linker's LIB search path instead. The mirror sits
|
|
# at the same depth (prefix/lib/...), so each file's `prefix=${pcfiledir}/../..`
|
|
# still resolves. The link line then carries only -l flags and fits, so no
|
|
# drive-letter subst or post-link relink is needed. Returns the mirror path.
|
|
function New-PkgConfigMirrorWithoutLibPath {
|
|
param([string] $PkgConfigDir)
|
|
if (-not (Test-Path $PkgConfigDir)) { return $null }
|
|
$mirror = Join-Path (Split-Path -Parent $PkgConfigDir) 'pkgconfig-nolibpath'
|
|
New-Item -ItemType Directory -Force -Path $mirror | Out-Null
|
|
Get-ChildItem -Path $PkgConfigDir -Filter '*.pc' -File | ForEach-Object {
|
|
$dst = Join-Path $mirror $_.Name
|
|
if ((Test-Path $dst) -and (Get-Item $dst).LastWriteTimeUtc -ge $_.LastWriteTimeUtc) { return }
|
|
$patched = Get-Content -LiteralPath $_.FullName | ForEach-Object {
|
|
# -creplace: case-sensitive, so -L (search path) is stripped but the
|
|
# -l library flags are kept.
|
|
if ($_ -match '^Libs(\.private)?\s*:') { ($_ -creplace '(^|\s)"?-L\S+', '$1').TrimEnd() } else { $_ }
|
|
}
|
|
[System.IO.File]::WriteAllLines($dst, [string[]]$patched, [System.Text.UTF8Encoding]::new($false))
|
|
}
|
|
return $mirror
|
|
}
|
|
|
|
# vcpkg ships pkgconf.exe but SwiftGtk's Package.swift invokes
|
|
# "pkg-config". Provide an alias next to pkgconf.exe.
|
|
$pkgconfTools = Join-Path $vcpkg 'tools\pkgconf'
|
|
$pkgconf = Join-Path $pkgconfTools 'pkgconf.exe'
|
|
$pkgAlias = Join-Path $pkgconfTools 'pkg-config.exe'
|
|
if ((Test-Path $pkgconf) -and -not (Test-Path $pkgAlias)) {
|
|
Copy-Item $pkgconf $pkgAlias
|
|
}
|
|
|
|
# vcpkg drops a dirent.h polyfill at $prefix/include that conflicts with
|
|
# Swift's _FoundationCShims. Mirror the root headers into a staging dir
|
|
# excluding dirent.h so clang still finds sqlite3.h, etc.
|
|
$shim = Join-Path $vcpkg 'include\vcpkg-shim'
|
|
New-Item -ItemType Directory -Force -Path $shim | Out-Null
|
|
Get-ChildItem (Join-Path $vcpkg 'include\*.h') -File | Where-Object {
|
|
$_.Name -ne 'dirent.h'
|
|
} | ForEach-Object {
|
|
$dest = Join-Path $shim $_.Name
|
|
if (-not (Test-Path $dest) -or $_.LastWriteTimeUtc -gt (Get-Item $dest).LastWriteTimeUtc) {
|
|
Copy-Item $_.FullName $dest
|
|
}
|
|
}
|
|
|
|
$pkgConfigDirs = @($frida, $r2, $pharo, $vcpkg) |
|
|
ForEach-Object { New-PkgConfigMirrorWithoutLibPath (Join-Path $_ 'lib\pkgconfig') } |
|
|
Where-Object { $_ } |
|
|
ForEach-Object { $_ -replace '\\','/' } |
|
|
Select-Object -Unique
|
|
|
|
# Root-level vcpkg headers (sqlite3.h, ...) aren't
|
|
# reachable through pkg-config, so point clang at the vcpkg-shim
|
|
# staging dir that mirrors $VCPKG_PREFIX/include minus the dirent.h
|
|
# polyfill that clashes with Swift's _FoundationCShims. Everything
|
|
# else (gtk4, libadwaita, frida-core, radare2, ...) propagates
|
|
# through SwiftPM's .systemLibrary(pkgConfig:) resolver.
|
|
$shimDir = (Join-Path $vcpkg 'include\vcpkg-shim') -replace '\\','/'
|
|
|
|
$env:VCPKG_PREFIX = $vcpkg
|
|
$env:FRIDA_PREFIX = $frida
|
|
$env:R2_PREFIX = $r2
|
|
$env:PHARO_PREFIX = $pharo
|
|
$env:PKG_CONFIG_PATH = $pkgConfigDirs -join ';'
|
|
$env:GIR_EXTRA_SEARCH_PATH = (Join-Path $vcpkg 'share\gir-1.0') -replace '\\','/'
|
|
$env:CPATH = $shimDir
|
|
$env:CPLUS_INCLUDE_PATH = $shimDir
|
|
|
|
# The pkgconfig-nolibpath mirror above drops the per-library -L; put the real
|
|
# lib dirs on the linker's LIB search path so lld-link still finds the import
|
|
# libs.
|
|
$libDirs = @($vcpkg, $frida, $r2, $pharo) |
|
|
ForEach-Object { Join-Path $_ 'lib' } |
|
|
Where-Object { Test-Path $_ } |
|
|
Select-Object -Unique
|
|
function Join-DistinctDirectories {
|
|
param([string[]] $Directories)
|
|
$seen = [System.Collections.Generic.HashSet[string]]::new([StringComparer]::OrdinalIgnoreCase)
|
|
($Directories | Where-Object { $_ -and $seen.Add($_.TrimEnd('\')) }) -join ';'
|
|
}
|
|
$env:LIB = Join-DistinctDirectories (@($libDirs) + ($env:LIB -split ';'))
|
|
|
|
# Link frida-swift against the system frida-core devkit rather than
|
|
# downloading one, and point GLib at vcpkg's glib-networking so TLS
|
|
# works — luma-bundle-compiler fetches npm type packages over HTTPS,
|
|
# which fails with "TLS support is not available" without it.
|
|
$env:USE_SYSTEM_FRIDA = '1'
|
|
$env:GIO_EXTRA_MODULES = (Join-Path $vcpkg 'plugins\glib-networking') -replace '\\','/'
|
|
|
|
# gir2swift shells out to sed/awk to patch generated Swift. Git for
|
|
# Windows' cygwin build of sed (typically first on PATH for most
|
|
# Windows devs) crashes silently at fork() time on recent Windows
|
|
# versions, leaving the patches unapplied and the generated GLib
|
|
# Swift unable to compile. Any non-Git sed on disk (MSYS2, GnuWin32,
|
|
# chocolatey shim) works, so probe for one and prepend its dir.
|
|
$sedCandidates = @(
|
|
'C:\msys64\usr\bin\sed.exe',
|
|
'C:\Program Files (x86)\GnuWin32\bin\sed.exe',
|
|
'C:\ProgramData\chocolatey\bin\sed.exe'
|
|
)
|
|
$sedDir = $sedCandidates |
|
|
Where-Object { Test-Path $_ } |
|
|
Select-Object -First 1 |
|
|
ForEach-Object { Split-Path -Parent $_ }
|
|
if ($sedDir) {
|
|
$gitBefore = (Get-Command git -ErrorAction SilentlyContinue).Source
|
|
if (-not (($env:PATH -split ';') -contains $sedDir)) {
|
|
$env:PATH = "$sedDir;$env:PATH"
|
|
}
|
|
$gitAfter = (Get-Command git -ErrorAction SilentlyContinue).Source
|
|
if ($gitBefore -and $gitAfter -ne $gitBefore) {
|
|
$env:PATH = "$(Split-Path -Parent $gitBefore);$env:PATH"
|
|
}
|
|
Write-Host " sed/awk = $sedDir"
|
|
} else {
|
|
Write-Warning @"
|
|
No non-cygwin sed/awk found. gir2swift post-processing will be
|
|
skipped, and the generated GLib Swift will fail to compile.
|
|
Install one of:
|
|
winget install GnuWin32.Sed GnuWin32.Gawk
|
|
or an MSYS2 install under C:\msys64
|
|
"@
|
|
}
|
|
|
|
# Put the dependency prefixes on PATH so pkg-config and the built
|
|
# executable (via run.ps1) can find the runtime DLLs.
|
|
$prefixBins = @(
|
|
$pkgconfTools,
|
|
(Join-Path $vcpkg 'bin'),
|
|
(Join-Path $vcpkg 'tools'),
|
|
(Join-Path $frida 'bin'),
|
|
(Join-Path $r2 'bin'),
|
|
(Join-Path $pharo 'bin')
|
|
) | Where-Object { Test-Path $_ }
|
|
$missing = $prefixBins | Where-Object { ($env:PATH -split ';') -notcontains $_ }
|
|
if ($missing) {
|
|
$env:PATH = (@($missing) + $env:PATH) -join ';'
|
|
}
|
|
|
|
# rc.exe lives in the Windows SDK bin and isn't on PATH in plain
|
|
# cmd / PowerShell. Package.swift uses it to compile the exe icon
|
|
# resource when swift build runs outside a Developer prompt.
|
|
if (-not (Get-Command rc.exe -ErrorAction SilentlyContinue)) {
|
|
$sdkRoot = 'C:\Program Files (x86)\Windows Kits\10\bin'
|
|
if (Test-Path $sdkRoot) {
|
|
$sdkBin = Get-ChildItem $sdkRoot -Directory |
|
|
Where-Object { $_.Name -match '^10\.' } |
|
|
Sort-Object Name -Descending |
|
|
ForEach-Object { Join-Path $_.FullName 'x64' } |
|
|
Where-Object { Test-Path (Join-Path $_ 'rc.exe') } |
|
|
Select-Object -First 1
|
|
if ($sdkBin) { $env:PATH = "$sdkBin;$env:PATH" }
|
|
}
|
|
}
|
|
|
|
$env:PATH = Join-DistinctDirectories ($env:PATH -split ';')
|
|
|
|
Write-Host "LumaGtk build env configured:"
|
|
Write-Host " VCPKG_PREFIX = $vcpkg"
|
|
Write-Host " FRIDA_PREFIX = $frida"
|
|
Write-Host " R2_PREFIX = $r2"
|
|
Write-Host " PHARO_PREFIX = $pharo"
|