Skip to content

Commit

Permalink
Refactor Create-Package.ps1 to support creating multi-architecture nu…
Browse files Browse the repository at this point in the history
…pkg's (#477)
  • Loading branch information
tristanlabelle authored Jan 10, 2025
1 parent 052e1df commit 762fa26
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 97 deletions.
24 changes: 18 additions & 6 deletions Generator/NuGet/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TristanLabelle.SwiftWinRT.0.0.0.nupkg"
if("${PACKAGE_VERSION}" STREQUAL "")
set(PACKAGE_VERSION "0.0.0")
endif()
set(PACKAGE_FILENAME "TristanLabelle.SwiftWinRT.${PACKAGE_VERSION}.nupkg")

if("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "AMD64")
set(EXE_SWITCH "-X64Exe")
else()
set(EXE_SWITCH "-Arm64Exe")
endif()

add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_FILENAME}"
COMMAND powershell.exe -File "${CMAKE_CURRENT_SOURCE_DIR}/Create-Package.ps1"
-NativeExe "$<TARGET_FILE:SwiftWinRT>"
-MscorlibPath "${CMAKE_CURRENT_BINARY_DIR}/../mscorlib.winmd"
-Version 0.0.0
-OutputPath "${CMAKE_CURRENT_BINARY_DIR}/TristanLabelle.SwiftWinRT.0.0.0.nupkg"
"${EXE_SWITCH}" "$<TARGET_FILE:SwiftWinRT>"
-MscorlibWinMD "${CMAKE_CURRENT_BINARY_DIR}/../mscorlib.winmd"
-Version "${PACKAGE_VERSION}"
-IntermediateDir "${CMAKE_CURRENT_BINARY_DIR}"
-OutputPath "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_FILENAME}"
DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/Create-Package.ps1"
SwiftWinRT
"${CMAKE_CURRENT_BINARY_DIR}/../mscorlib.winmd")

add_custom_target(NuGetPackage
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/TristanLabelle.SwiftWinRT.0.0.0.nupkg")
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_FILENAME}")
272 changes: 181 additions & 91 deletions Generator/NuGet/Create-Package.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,120 +4,210 @@ Creates the Swift/WinRT nuget package, including the code generator executable a
#>
[CmdletBinding(PositionalBinding=$false)]
param(
[string] $NativeExe = $null,
[string] $X64BinPath = $null,
[string] $Arm64BinPath = $null,
[string] $MscorlibPath = $null,
[string] $Version = $null,
[string] $StagingDir = $null,
[string] $X64Exe = $null,
[string] $Arm64Exe = $null,
[string] $MscorlibWinMD = $null,
[string] $SwiftRedistDir = $null,
[string] $Version = "0.0.0",
[string] $IntermediateDir = $null,
[string] $NuGetExe = "nuget.exe",
[Parameter(Mandatory=$true)]
[string] $OutputPath)

$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue" # Faster Invoke-WebRequest

if (!$NativeExe -and !$X64BinPath -and !$Arm64BinPath) {
Write-Error "One executable or binaries path must be specified."
exit 1
# Global constants
$WixVersion = "5.0.2"
$StagingDir = "" # Will be set later
$RepoRoot = (& git.exe -C "$PSScriptRoot" rev-parse --path-format=absolute --show-toplevel).Trim()

function FindSwiftRedistDir([string] $Hint) {
if ($Hint) {
if (!(Test-Path "$Hint\rtl.amd64.msm")) {
throw "Invalid Swift redist directory: $Hint"
}
return $Hint
}

$SwiftCompilerPath = (& where.exe swiftc.exe) | Out-String
$PathMatch = [Regex]::Match($SwiftCompilerPath, "^(?<root>.*)\\Toolchains\\(?<version>\d+\.\d+\.\d+)(\+\w+)?\\")
$SwiftRoot = $PathMatch.Groups["root"].Value
$SwiftVersion = $PathMatch.Groups["version"].Value
return "$SwiftRoot\Redistributables\$SwiftVersion"
}
elseif ([bool]$NativeExe -eq ($X64BinPath -or $Arm64BinPath)) {
Write-Error "The NativeExe and [X64|Arm64]BinPath parameters are mutually exclusive."
exit 1

function DownloadWiX() {
$ExePath = "$IntermediateDir\wix.$WixVersion\tools\net6.0\any\wix.exe"
if (Test-Path $ExePath) { return $ExePath }

if (!(Test-Path "$IntermediateDir\wix.$WixVersion")) {
if (!(Test-Path "$IntermediateDir\wix.$WixVersion.nupkg")) {
Invoke-WebRequest -Uri "https://www.nuget.org/api/v2/package/wix/$WixVersion" -OutFile "$IntermediateDir\wix.$WixVersion.nupkg"
}

Expand-Archive -Path "$IntermediateDir\wix.$WixVersion.nupkg" -DestinationPath "$IntermediateDir\wix.$WixVersion"
}

return $ExePath
}

$OwnStagingDir = $false
if (!$StagingDir) {
$StagingDir = [System.IO.Path]::Combine($Env:TEMP, [Guid]::NewGuid().ToString())
New-Item -ItemType Directory -Path $StagingDir -Force | Out-Null
$OwnStagingDir = $true
function StageSupportModule() {
New-Item -ItemType Directory -Path "$StagingDir\swift" -Force | Out-Null
$PackageSwift = Get-Content -Path "$RepoRoot\Package.swift" -Raw -Encoding UTF8
$PackageSwift = $PackageSwift -replace "Support/Sources/", "" # Flatten directory structure

# Remove test targets
$PackageSwift = [Regex]::Replace($PackageSwift, "
# Match the first line of a test target
^(?<indentation>[ ]+)
\.testTarget\(
.*\n
# Match subsequent lines of the test target (further indented)
(
\k<indentation> .*\n
)*
", "", "CultureInvariant,ExplicitCapture,IgnorePatternWhitespace,Multiline")

Out-File -FilePath "$StagingDir\swift\Package.swift" -InputObject "$PackageSwift" -Encoding UTF8
Copy-Item -Path "$RepoRoot\Package.resolved" -Destination "$StagingDir\swift\" -Force -ErrorAction Ignore | Out-Null # Might not have one
Copy-Item -Path "$RepoRoot\Support\Sources\*" -Destination "$StagingDir\swift\" -Recurse -Force | Out-Null
}

Write-Host "Staging files to $StagingDir..."
function StageMscorlib([string] $TargetDir) {
if (!$MscorlibPath) {
return
function ExtractSwiftRuntime([string] $Arch, [string] $Msm, [string] $WixExe) {
$DestDir = "$IntermediateDir\SwiftRuntime\$Arch"
if (Test-Path "$DestDir\swiftCore.dll") { return $DestDir }

$DecompileDir = "$IntermediateDir\SwiftRuntime\Msms\$Arch"
$WixXmlPath = "$DecompileDir\wix.xml"

if (!(Test-Path $WixXmlPath)) {
New-Item -ItemType Directory -Path $DecompileDir -Force | Out-Null
& $WixExe msi decompile -sct -sdet -sras -sui -type msm -x $DecompileDir -o $WixXmlPath $Msm
}

Write-Host " mscorlib..."
Copy-Item -Path $MscorlibPath -Destination $TargetDir -Force | Out-Null
}
New-Item -ItemType Directory -Path $DestDir -Force | Out-Null

if ($NativeExe) {
Write-Host " native executable..."
$Arch = $Env:PROCESSOR_ARCHITECTURE
if ($Arch -eq "AMD64") { $Arch = "x64" }
New-Item -ItemType Directory -Path $StagingDir\tools\$Arch -Force | Out-Null
Copy-Item -Path $NativeExe -Destination $StagingDir\tools\$Arch\ -Force | Out-Null
[Xml]$WixXml = Get-Content $WixXmlPath
$ns = @{ns="http://wixtoolset.org/schemas/v4/wxs"}
$FileNodes = Select-Xml -Xml $WixXml -XPath '//ns:Component/ns:File' -Namespace $ns
foreach ($FileNode in $FileNodes) {
$SourceFile = "$DecompileDir\" + $FileNode.Node.GetAttribute("Source").Replace("SourceDir\", "")
$DestFile = "$DestDir\" + $FileNode.Node.GetAttribute("Name")
Copy-Item -Path $SourceFile -Destination $DestFile -Force | Out-Null
}

Write-Host " native Swift runtime..."
$SwiftCompilerPath = (& where.exe swiftc.exe) | Out-String
$PathMatch = [Regex]::Match($SwiftCompilerPath, "^(?<root>.*)\\Toolchains\\(?<version>\d+\.\d+\.\d+)(\+\w+)?\\")
$SwiftRoot = $PathMatch.Groups["root"].Value
$SwiftVersion = $PathMatch.Groups["version"].Value
$SwiftRuntimeDir = "$SwiftRoot\Runtimes\$SwiftVersion\usr\bin"
Copy-Item -Path $SwiftRuntimeDir\*.dll -Destination $StagingDir\tools\$Arch\ -Force | Out-Null
# Remove unnecessary binaries
Remove-Item -Path "$DestDir\*.exe" -Force | Out-Null
Remove-Item -Path "$DestDir\FoundationNetworking.dll" -Force | Out-Null

StageMscorlib "$StagingDir\tools\$Arch\"
return $DestDir
}

if ($X64BinPath) {
Write-Host " x64 binaries..."
New-Item -ItemType Directory -Path $StagingDir\tools\x64 -Force | Out-Null
Copy-Item -Path $X64BinPath -Destination $StagingDir\tools\x64\ -Recurse -Force | Out-Null
StageMscorlib "$StagingDir\tools\x64\"
function StageBinaries([string] $Arch, [string] $Exe, [string] $MscorlibWinMD, [string] $SwiftRuntimeMsm, [string] $WixExe) {
$DestDir = "$StagingDir\tools\$Arch"

if (!$MscorlibWinMD) {
$MscorlibWinMD = (Split-Path $Exe) + "\mscorlib.winmd"
}

New-Item -ItemType Directory -Path $DestDir -Force | Out-Null
Copy-Item -Path $Exe -Destination "$DestDir\SwiftWinRT.exe" -Force | Out-Null
Copy-Item -Path $MscorlibWinMD -Destination "$DestDir\mscorlib.winmd" -Force | Out-Null

$SwiftRuntimeDir = & ExtractSwiftRuntime -Arch $Arch -Msm $SwiftRuntimeMsm -WixExe $WixExe
Copy-Item -Path "$SwiftRuntimeDir\*.dll" -Destination "$DestDir\" -Force | Out-Null
}

if ($Arm64BinPath) {
Write-Host " arm64 binaries..."
New-Item -ItemType Directory -Path $StagingDir\tools\arm64 -Force | Out-Null
Copy-Item -Path $Arm64BinPath -Destination $StagingDir\tools\arm64\ -Recurse -Force | Out-Null
StageMscorlib "$StagingDir\tools\arm64\"
function StagePackage() {
$SwiftRedistDir = & FindSwiftRedistDir -Hint $SwiftRedistDir

Write-Host "Downloading WiX..."
$WixExe = & DownloadWiX

Write-Host "Staging files..."
if ($X64Exe) {
Write-Host " x64 binaries..."
StageBinaries `
-Arch "x64" `
-Exe $X64Exe `
-MscorlibWinMD $MscorlibWinMD `
-SwiftRuntimeMsm "$SwiftRedistDir\rtl.amd64.msm" `
-WixExe $WixExe
}

if ($Arm64Exe) {
Write-Host " arm64 binaries..."
StageBinaries `
-Arch "arm64" `
-Exe $Arm64Exe `
-MscorlibWinMD $MscorlibWinMD `
-SwiftRuntimeMsm "$SwiftRedistDir\rtl.arm64.msm" `
-WixExe $WixExe
}

Write-Host " support module..."
& StageSupportModule

Write-Host " json schema..."
New-Item -ItemType Directory "$StagingDir\json" -Force | Out-Null
Copy-Item -Path "$RepoRoot\Generator\Projection.schema.json" -Destination "$StagingDir\json\" -Force | Out-Null

Write-Host " readme..."
Copy-Item -Path "$RepoRoot\Readme.md" -Destination "$StagingDir\" -Force | Out-Null
}

Write-Host " support module sources..."
New-Item -ItemType Directory -Path $StagingDir\swift -Force | Out-Null
$RepoRoot = (& git.exe -C "$PSScriptRoot" rev-parse --path-format=absolute --show-toplevel).Trim()
$PackageSwift = Get-Content -Path $RepoRoot\Package.swift -Raw -Encoding UTF8
$PackageSwift = $PackageSwift -replace "Support/Sources/", "" # Flatten directory structure
# Remove test targets
$PackageSwift = [Regex]::Replace($PackageSwift, "
# Match the first line of a test target
^(?<indentation>[ ]+)
\.testTarget\(
.*\n
# Match subsequent lines of the test target (further indented)
(
\k<indentation> .*\n
)*
", "", "CultureInvariant,ExplicitCapture,IgnorePatternWhitespace,Multiline")
Out-File -FilePath $StagingDir\swift\Package.swift -InputObject $PackageSwift -Encoding UTF8
Copy-Item -Path $RepoRoot\Package.resolved -Destination $StagingDir\swift\ -Force -ErrorAction Ignore | Out-Null # Might not have one
Copy-Item -Path $RepoRoot\Support\Sources\* -Destination $StagingDir\swift\ -Recurse -Force | Out-Null

Write-Host " json schema..."
New-Item -ItemType Directory $StagingDir\json -Force | Out-Null
Copy-Item -Path $PSScriptRoot\..\Projection.schema.json -Destination $StagingDir\json\ -Force | Out-Null

Write-Host " readme..."
Copy-Item -Path $RepoRoot\Readme.md -Destination $StagingDir\ -Force | Out-Null

Write-Host "Creating NuGet package..."
$NuGetArgs = @("pack",
"-NonInteractive",
"-BasePath", $StagingDir,
"-OutputDirectory", $StagingDir)
if ($Version) {
$NuGetArgs += @("-Version", $Version)
function CreatePackage {
$NuGetArgs = @("pack",
"-NonInteractive",
"-BasePath", $StagingDir,
"-OutputDirectory", $IntermediateDir
"-Version", $Version,
"$PSScriptRoot\Package.nuspec")

& $NuGetExe @NuGetArgs | Out-Host

return @(Get-ChildItem -Path $IntermediateDir -Filter "*.nupkg")[0].FullName
}
$NuGetArgs += @("$PSScriptRoot\Package.nuspec")

& $NuGetExe @NuGetArgs
function Main {
if (!$X64Exe -and !$Arm64Exe) {
Write-Error "One executable path must be specified."
exit 1
}

$OwnIntermediateDir = $false
if (!$IntermediateDir) {
$script:IntermediateDir = [System.IO.Path]::Combine($Env:TEMP, [Guid]::NewGuid().ToString())
New-Item -ItemType Directory -Path $IntermediateDir -Force | Out-Null
$OwnIntermediateDir = $true
}
else {
$script:IntermediateDir = (Resolve-Path $IntermediateDir).Path
}

$StagingDir = "$IntermediateDir\Staging"
Remove-Item -Path $StagingDir -Force -Recurse -ErrorAction Ignore | Out-Null
New-Item -ItemType Directory -Path $StagingDir -Force | Out-Null

Write-Host "Copying package to $OutputPath..."
$StagedPackagePath = @(Get-ChildItem -Path $StagingDir -Filter "*.nupkg")[0].FullName
Copy-Item -Path $StagedPackagePath -Destination $OutputPath -Force | Out-Null
try {
StagePackage

Write-Host "Creating NuGet package..."
$PackageFile = CreatePackage

Write-Host "Copying package to $OutputPath..."
$OutputPath = (Resolve-Path $OutputPath).Path
if ($OutputPath -ne $PackageFile) {
Copy-Item -Path $PackageFile -Destination $OutputPath -Force | Out-Null
}
}
finally {
if ($OwnIntermediateDir) {
Write-Host "Cleaning up staged files..."
Remove-Item -Path $IntermediateDir -Force -Recurse | Out-Null
}
}
}

if ($OwnStagingDir) {
Write-Host "Cleaning up staged files..."
Remove-Item -Path $StagingDir -Force -Recurse | Out-Null
}
Main

0 comments on commit 762fa26

Please sign in to comment.