128 lines
5.4 KiB
PowerShell
128 lines
5.4 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ManagedDll,
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$OutputDirectory
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Get-PeMachine {
|
|
param([Parameter(Mandatory = $true)][string]$Path)
|
|
|
|
[byte[]]$bytes = [System.IO.File]::ReadAllBytes($Path)
|
|
if ($bytes.Length -le 0x40 -or $bytes[0] -ne 0x4d -or $bytes[1] -ne 0x5a) {
|
|
throw "Native runtime is not a PE file: $Path"
|
|
}
|
|
|
|
[int]$peOffset = [System.BitConverter]::ToInt32($bytes, 0x3c)
|
|
if ($peOffset -lt 0 -or $peOffset + 6 -gt $bytes.Length -or $bytes[$peOffset] -ne 0x50 -or $bytes[$peOffset + 1] -ne 0x45) {
|
|
throw "Native runtime has an invalid PE header: $Path"
|
|
}
|
|
|
|
return [System.BitConverter]::ToUInt16($bytes, $peOffset + 4)
|
|
}
|
|
|
|
function Test-SamePath {
|
|
param([string]$Left, [string]$Right)
|
|
|
|
return [string]::Equals($Left.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar),
|
|
$Right.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar),
|
|
[System.StringComparison]::OrdinalIgnoreCase)
|
|
}
|
|
|
|
$managedDllPath = [System.IO.Path]::GetFullPath($ManagedDll)
|
|
$outputDirectoryPath = [System.IO.Path]::GetFullPath($OutputDirectory)
|
|
$driveRoot = [System.IO.Path]::GetPathRoot($outputDirectoryPath)
|
|
$workspaceRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..'))
|
|
|
|
if (Test-SamePath $outputDirectoryPath $driveRoot) {
|
|
throw 'Refusing to publish into a drive root.'
|
|
}
|
|
if (Test-SamePath $outputDirectoryPath $workspaceRoot) {
|
|
throw 'Refusing to publish into the workspace root.'
|
|
}
|
|
if (-not [System.IO.File]::Exists($managedDllPath)) {
|
|
throw "Managed DLL does not exist: $managedDllPath"
|
|
}
|
|
$visualizationDll = Join-Path ([System.IO.Path]::GetDirectoryName($managedDllPath)) 'TrajectoryPlanningVisualization.dll'
|
|
if (-not [System.IO.File]::Exists($visualizationDll)) {
|
|
throw "Visualization DLL does not exist beside managed DLL: $visualizationDll"
|
|
}
|
|
if (-not [System.Environment]::Is64BitProcess) {
|
|
throw 'Refusing to publish from a non-x64 PowerShell host.'
|
|
}
|
|
|
|
$clumsyPilotRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
$osqpRoot = Join-Path $clumsyPilotRoot 'ThirdParty\OSQP'
|
|
$nativeDll = Join-Path $osqpRoot 'win-x64\osqp.dll'
|
|
$hashManifest = Join-Path $osqpRoot 'SHA256SUMS'
|
|
$license = Join-Path $osqpRoot 'LICENSE'
|
|
$notice = Join-Path $osqpRoot 'NOTICE'
|
|
$version = Join-Path $osqpRoot 'VERSION'
|
|
$requiredFiles = @($nativeDll, $hashManifest, $license, $notice, $version)
|
|
foreach ($requiredFile in $requiredFiles) {
|
|
if (-not [System.IO.File]::Exists($requiredFile)) {
|
|
throw "Required OSQP package file does not exist: $requiredFile"
|
|
}
|
|
}
|
|
|
|
if ((Get-PeMachine $nativeDll) -ne 0x8664) {
|
|
throw 'Refusing to publish a non-x64 OSQP runtime.'
|
|
}
|
|
|
|
$manifestTokens = @((Get-Content -Raw -LiteralPath $hashManifest) -split '\s+' | Where-Object { $_.Length -gt 0 })
|
|
if ($manifestTokens.Length -lt 2 -or $manifestTokens[1] -ne 'win-x64/osqp.dll') {
|
|
throw 'SHA256SUMS does not describe the pinned win-x64 OSQP runtime.'
|
|
}
|
|
$expectedHash = $manifestTokens[0].ToLowerInvariant()
|
|
$actualHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $nativeDll).Hash.ToLowerInvariant()
|
|
if ($actualHash -ne $expectedHash) {
|
|
throw 'OSQP runtime SHA-256 does not match SHA256SUMS.'
|
|
}
|
|
|
|
[System.IO.Directory]::CreateDirectory($outputDirectoryPath) | Out-Null
|
|
$pluginsDirectory = Join-Path $outputDirectoryPath 'plugins'
|
|
$nonce = [System.Guid]::NewGuid().ToString('N')
|
|
$stagingDirectory = Join-Path $outputDirectoryPath ('.plugins-staging-' + $nonce)
|
|
$backupDirectory = Join-Path $outputDirectoryPath ('.plugins-backup-' + $nonce)
|
|
$movedExistingPlugins = $false
|
|
|
|
try {
|
|
[System.IO.Directory]::CreateDirectory((Join-Path $stagingDirectory 'licenses')) | Out-Null
|
|
[System.IO.File]::Copy($managedDllPath, (Join-Path $stagingDirectory 'ClumsyPilot.dll'), $false)
|
|
[System.IO.File]::Copy($visualizationDll, (Join-Path $stagingDirectory 'TrajectoryPlanningVisualization.dll'), $false)
|
|
[System.IO.File]::Copy($nativeDll, (Join-Path $stagingDirectory 'osqp.dll'), $false)
|
|
[System.IO.File]::Copy($license, (Join-Path $stagingDirectory 'licenses\OSQP-LICENSE.txt'), $false)
|
|
[System.IO.File]::Copy($notice, (Join-Path $stagingDirectory 'licenses\OSQP-NOTICE.txt'), $false)
|
|
[System.IO.File]::Copy($version, (Join-Path $stagingDirectory 'licenses\OSQP-VERSION.txt'), $false)
|
|
|
|
if ([System.IO.Directory]::Exists($pluginsDirectory)) {
|
|
[System.IO.Directory]::Move($pluginsDirectory, $backupDirectory)
|
|
$movedExistingPlugins = $true
|
|
}
|
|
|
|
[System.IO.Directory]::Move($stagingDirectory, $pluginsDirectory)
|
|
|
|
if ($movedExistingPlugins -and [System.IO.Directory]::Exists($backupDirectory)) {
|
|
[System.IO.Directory]::Delete($backupDirectory, $true)
|
|
}
|
|
}
|
|
catch {
|
|
if (-not [System.IO.Directory]::Exists($pluginsDirectory) -and $movedExistingPlugins -and
|
|
[System.IO.Directory]::Exists($backupDirectory)) {
|
|
[System.IO.Directory]::Move($backupDirectory, $pluginsDirectory)
|
|
}
|
|
throw
|
|
}
|
|
finally {
|
|
if ([System.IO.Directory]::Exists($stagingDirectory)) {
|
|
[System.IO.Directory]::Delete($stagingDirectory, $true)
|
|
}
|
|
if ([System.IO.Directory]::Exists($backupDirectory) -and [System.IO.Directory]::Exists($pluginsDirectory)) {
|
|
[System.IO.Directory]::Delete($backupDirectory, $true)
|
|
}
|
|
}
|