Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Create-Package.ps1 to support creating multi-architecture nupkg's #477

Merged
merged 1 commit into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading