mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-18 05:39:50 +08:00
865 lines
33 KiB
PowerShell
865 lines
33 KiB
PowerShell
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# Windows MSVC build wrapper used by the `windows:*` mise tasks.
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true, Position = 0)]
|
|
[ValidateSet("check", "lint", "build", "test", "test-precommit", "test-conformance", "test-unsupported", "test-mxc-real", "test-mxc-gb300", "artifacts", "ci")]
|
|
[string] $Action,
|
|
|
|
[Parameter(Position = 1)]
|
|
[ValidateSet("x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc", "native", "all")]
|
|
[string] $Target = "all",
|
|
|
|
[string] $LogDir
|
|
)
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
if (-not [System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows)) {
|
|
throw "windows-msvc.ps1 requires a Windows MSVC host."
|
|
}
|
|
|
|
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path
|
|
if (-not $LogDir) {
|
|
$LogDir = $RepoRoot
|
|
}
|
|
if (-not (Test-Path $LogDir)) {
|
|
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
|
|
}
|
|
$LogDir = (Resolve-Path $LogDir).Path
|
|
|
|
$TargetDir = $env:CARGO_TARGET_DIR
|
|
if ([string]::IsNullOrWhiteSpace($TargetDir)) {
|
|
$TargetDir = Join-Path $RepoRoot "target"
|
|
}
|
|
|
|
$BuildJobsValue = $env:OPENSHELL_WINDOWS_BUILD_JOBS
|
|
if ([string]::IsNullOrWhiteSpace($BuildJobsValue)) {
|
|
$BuildJobsValue = $env:CARGO_BUILD_JOBS
|
|
}
|
|
if ([string]::IsNullOrWhiteSpace($BuildJobsValue)) {
|
|
$BuildJobsValue = "4"
|
|
}
|
|
[int] $WindowsBuildJobs = 0
|
|
if (-not [int]::TryParse($BuildJobsValue, [ref] $WindowsBuildJobs) -or $WindowsBuildJobs -lt 1) {
|
|
throw "OPENSHELL_WINDOWS_BUILD_JOBS or CARGO_BUILD_JOBS must be a positive integer."
|
|
}
|
|
$WindowsCargoMutex = [System.Threading.Mutex]::new($false, "Local\OpenShellWindowsMsvcCargo")
|
|
|
|
$UnsupportedDriverPackageExcludes = "--exclude openshell-driver-docker --exclude openshell-driver-kubernetes --exclude openshell-driver-kubernetes-secrets --exclude openshell-driver-podman --exclude openshell-driver-vault --exclude openshell-driver-vm --exclude openshell-sandbox --exclude openshell-supervisor-process --exclude openshell-vfio"
|
|
$WindowsClippyPackageExcludes = $UnsupportedDriverPackageExcludes
|
|
$WindowsClippyLintArgs = "-D warnings -A dead-code -A unused-imports -A clippy::unused-async"
|
|
$PrebuiltZ3WorkspaceFeatures = "--features openshell-prover/prebuilt-z3"
|
|
$PrebuiltZ3ServerFeatures = "--features openshell-server/prebuilt-z3,openshell-prover/prebuilt-z3"
|
|
$PrebuiltZ3GatewayFeatures = "--features openshell-gateway/prebuilt-z3"
|
|
$PrebuiltZ3Version = "4.16.0"
|
|
$Z3WorkspaceFeatures = $PrebuiltZ3WorkspaceFeatures
|
|
$Z3ServerFeatures = $PrebuiltZ3ServerFeatures
|
|
$Z3GatewayFeatures = $PrebuiltZ3GatewayFeatures
|
|
|
|
function Get-VsInstallRoots {
|
|
$programFiles = @(
|
|
[Environment]::GetEnvironmentVariable("ProgramFiles"),
|
|
[Environment]::GetEnvironmentVariable("ProgramFiles(x86)")
|
|
) | Where-Object { $_ }
|
|
$editions = @("Enterprise", "Professional", "Community", "BuildTools")
|
|
$candidates = @()
|
|
|
|
foreach ($programFilesRoot in $programFiles) {
|
|
$vsRoot = Join-Path $programFilesRoot "Microsoft Visual Studio"
|
|
if (-not (Test-Path $vsRoot -PathType Container)) {
|
|
continue
|
|
}
|
|
foreach ($releaseDir in Get-ChildItem $vsRoot -Directory) {
|
|
foreach ($edition in $editions) {
|
|
$installRoot = Join-Path $releaseDir.FullName $edition
|
|
$vsDevCmd = Join-Path $installRoot "Common7\Tools\VsDevCmd.bat"
|
|
if (-not (Test-Path $vsDevCmd -PathType Leaf)) {
|
|
continue
|
|
}
|
|
|
|
$toolsetVersion = [version] "0.0"
|
|
$versionFile = Join-Path $installRoot "VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"
|
|
if (Test-Path $versionFile -PathType Leaf) {
|
|
try {
|
|
$toolsetVersion = [version] ((Get-Content $versionFile -Raw).Trim())
|
|
} catch {
|
|
$toolsetVersion = [version] "0.0"
|
|
}
|
|
}
|
|
$candidates += [pscustomobject]@{
|
|
Root = $installRoot
|
|
ToolsetVersion = $toolsetVersion
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return @($candidates | Sort-Object ToolsetVersion -Descending | Select-Object -ExpandProperty Root -Unique)
|
|
}
|
|
|
|
function Get-DefaultMsvcToolsetRoot([string] $VsInstallRoot) {
|
|
$versionFile = Join-Path $VsInstallRoot "VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt"
|
|
if (Test-Path $versionFile -PathType Leaf) {
|
|
$version = (Get-Content $versionFile -Raw).Trim()
|
|
$toolsetRoot = Join-Path $VsInstallRoot "VC\Tools\MSVC\$version"
|
|
if (Test-Path $toolsetRoot -PathType Container) {
|
|
return (Resolve-Path $toolsetRoot).Path
|
|
}
|
|
}
|
|
|
|
$toolsetsRoot = Join-Path $VsInstallRoot "VC\Tools\MSVC"
|
|
if (Test-Path $toolsetsRoot -PathType Container) {
|
|
$toolset = Get-ChildItem $toolsetsRoot -Directory |
|
|
Sort-Object { try { [version] $_.Name } catch { [version] "0.0" } } -Descending |
|
|
Select-Object -First 1
|
|
if ($toolset) {
|
|
return $toolset.FullName
|
|
}
|
|
}
|
|
|
|
return $null
|
|
}
|
|
|
|
function Test-VsInstanceSupportsTarget([string] $VsInstallRoot, [string] $RustTarget) {
|
|
$toolsetRoot = Get-DefaultMsvcToolsetRoot $VsInstallRoot
|
|
if (-not $toolsetRoot) {
|
|
return $false
|
|
}
|
|
|
|
$hostToolsDir = switch (Get-HostArch) {
|
|
"arm64" { "Hostarm64" }
|
|
default { "Hostx64" }
|
|
}
|
|
$targetToolsDir = switch (Get-VsTargetArch $RustTarget) {
|
|
"arm64" { "arm64" }
|
|
default { "x64" }
|
|
}
|
|
$compiler = Join-Path $toolsetRoot "bin\$hostToolsDir\$targetToolsDir\cl.exe"
|
|
if (-not (Test-Path $compiler -PathType Leaf)) {
|
|
return $false
|
|
}
|
|
|
|
if ($RustTarget -eq "aarch64-pc-windows-msvc") {
|
|
$spectreLibs = Join-Path $toolsetRoot "lib\spectre\arm64"
|
|
if (-not (Test-Path $spectreLibs -PathType Container)) {
|
|
return $false
|
|
}
|
|
}
|
|
|
|
return $true
|
|
}
|
|
|
|
function Resolve-VsDevCmd([string] $RustTarget) {
|
|
if ($env:OPENSHELL_VSDEVCMD -and (Test-Path $env:OPENSHELL_VSDEVCMD)) {
|
|
return (Resolve-Path $env:OPENSHELL_VSDEVCMD).Path
|
|
}
|
|
|
|
$programFilesX86 = [Environment]::GetEnvironmentVariable("ProgramFiles(x86)")
|
|
if ($programFilesX86) {
|
|
$vswhere = Join-Path $programFilesX86 "Microsoft Visual Studio\Installer\vswhere.exe"
|
|
} else {
|
|
$vswhere = $null
|
|
}
|
|
if ($vswhere -and (Test-Path $vswhere)) {
|
|
$requiredComponents = switch ($RustTarget) {
|
|
"x86_64-pc-windows-msvc" { @("Microsoft.VisualStudio.Component.VC.Tools.x86.x64") }
|
|
"aarch64-pc-windows-msvc" {
|
|
@(
|
|
"Microsoft.VisualStudio.Component.VC.Tools.ARM64",
|
|
"Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre"
|
|
)
|
|
}
|
|
default { throw "Unsupported target: $RustTarget" }
|
|
}
|
|
$found = & $vswhere -latest -products * -requires $requiredComponents -find "Common7\Tools\VsDevCmd.bat" | Select-Object -First 1
|
|
if ($found -and (Test-Path $found)) {
|
|
$resolved = (Resolve-Path $found).Path
|
|
$installRoot = (Resolve-Path (Join-Path (Split-Path -Parent $resolved) "..\..")).Path
|
|
if (Test-VsInstanceSupportsTarget $installRoot $RustTarget) {
|
|
return $resolved
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach ($installRoot in Get-VsInstallRoots) {
|
|
if (Test-VsInstanceSupportsTarget $installRoot $RustTarget) {
|
|
$candidate = Join-Path $installRoot "Common7\Tools\VsDevCmd.bat"
|
|
return (Resolve-Path $candidate).Path
|
|
}
|
|
}
|
|
|
|
if ($RustTarget -eq "aarch64-pc-windows-msvc") {
|
|
throw "Could not find a Visual Studio instance with the ARM64 compiler and ARM64 Spectre-mitigated libraries. Install Microsoft.VisualStudio.Component.VC.Tools.ARM64 and Microsoft.VisualStudio.Component.VC.Runtimes.ARM64.Spectre, or set OPENSHELL_VSDEVCMD."
|
|
}
|
|
throw "Could not find a Visual Studio instance with the x64 compiler. Install Microsoft.VisualStudio.Component.VC.Tools.x86.x64, or set OPENSHELL_VSDEVCMD."
|
|
}
|
|
|
|
function Get-LibclangBinSubdir {
|
|
return ([System.Runtime.InteropServices.RuntimeInformation, mscorlib]::OSArchitecture.ToString())
|
|
}
|
|
|
|
function Resolve-LibclangPath {
|
|
$subdir = Get-LibclangBinSubdir
|
|
|
|
if ($env:LIBCLANG_PATH) {
|
|
$candidate = Join-Path $env:LIBCLANG_PATH "libclang.dll"
|
|
if (Test-Path $candidate) {
|
|
return (Resolve-Path $env:LIBCLANG_PATH).Path
|
|
}
|
|
throw "LIBCLANG_PATH is set but libclang.dll was not found at: $candidate"
|
|
}
|
|
|
|
$programFilesX86 = [Environment]::GetEnvironmentVariable("ProgramFiles(x86)")
|
|
if ($programFilesX86) {
|
|
$vswhere = Join-Path $programFilesX86 "Microsoft Visual Studio\Installer\vswhere.exe"
|
|
} else {
|
|
$vswhere = $null
|
|
}
|
|
if ($vswhere -and (Test-Path $vswhere)) {
|
|
$found = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Llvm.Clang -find "VC\Tools\Llvm\$subdir\bin\libclang.dll" | Select-Object -First 1
|
|
if ($found -and (Test-Path $found)) {
|
|
return (Split-Path -Parent (Resolve-Path $found).Path)
|
|
}
|
|
}
|
|
|
|
foreach ($installRoot in Get-VsInstallRoots) {
|
|
$candidateDir = Join-Path $installRoot "VC\Tools\Llvm\$subdir\bin"
|
|
$candidate = Join-Path $candidateDir "libclang.dll"
|
|
if (Test-Path $candidate -PathType Leaf) {
|
|
return (Resolve-Path $candidateDir).Path
|
|
}
|
|
}
|
|
|
|
$llvmDir = "C:\Program Files\LLVM\bin"
|
|
if (Test-Path (Join-Path $llvmDir "libclang.dll")) {
|
|
return (Resolve-Path $llvmDir).Path
|
|
}
|
|
|
|
throw "Could not find libclang.dll. Install Visual Studio C++ Clang tools, or set LIBCLANG_PATH to the directory containing libclang.dll."
|
|
}
|
|
|
|
function Resolve-NinjaPath {
|
|
$fromPath = Get-Command ninja.exe -ErrorAction SilentlyContinue
|
|
if ($fromPath) {
|
|
return $fromPath.Source
|
|
}
|
|
|
|
$programFilesX86 = [Environment]::GetEnvironmentVariable("ProgramFiles(x86)")
|
|
if ($programFilesX86) {
|
|
$vswhere = Join-Path $programFilesX86 "Microsoft Visual Studio\Installer\vswhere.exe"
|
|
} else {
|
|
$vswhere = $null
|
|
}
|
|
if ($vswhere -and (Test-Path $vswhere)) {
|
|
$found = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.CMake.Project -find "Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe" | Select-Object -First 1
|
|
if ($found -and (Test-Path $found -PathType Leaf)) {
|
|
return (Resolve-Path $found).Path
|
|
}
|
|
}
|
|
|
|
foreach ($installRoot in Get-VsInstallRoots) {
|
|
$candidate = Join-Path $installRoot "Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja\ninja.exe"
|
|
if (Test-Path $candidate -PathType Leaf) {
|
|
return (Resolve-Path $candidate).Path
|
|
}
|
|
}
|
|
|
|
throw "Could not find ninja.exe. Install Microsoft.VisualStudio.Component.VC.CMake.Project."
|
|
}
|
|
|
|
function Add-PathEntry([string] $Directory) {
|
|
if (($env:PATH -split ";") -notcontains $Directory) {
|
|
$env:PATH = "$Directory;$env:PATH"
|
|
}
|
|
}
|
|
|
|
function Configure-Arm64CrossBuild([string[]] $RustTargets) {
|
|
if ((Get-HostArch) -ne "amd64" -or $RustTargets -notcontains "aarch64-pc-windows-msvc") {
|
|
return
|
|
}
|
|
|
|
$clangCl = Join-Path $env:LIBCLANG_PATH "clang-cl.exe"
|
|
if (-not (Test-Path $clangCl -PathType Leaf)) {
|
|
throw "ARM64 cross-compilation requires host-native clang-cl.exe next to libclang.dll. Install Microsoft.VisualStudio.Component.VC.Llvm.Clang."
|
|
}
|
|
Add-PathEntry $env:LIBCLANG_PATH
|
|
|
|
$ninja = Resolve-NinjaPath
|
|
Add-PathEntry (Split-Path -Parent $ninja)
|
|
|
|
Write-Host "==> ARM64 cross-build toolchain"
|
|
Write-Host " clang-cl: $clangCl"
|
|
Write-Host " ninja: $ninja"
|
|
Write-Host " Z3: MSVC cl.exe with the Visual Studio generator"
|
|
}
|
|
|
|
function Get-HostArch {
|
|
switch ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()) {
|
|
"Arm64" { "arm64" }
|
|
default { "amd64" }
|
|
}
|
|
}
|
|
|
|
function Get-VsTargetArch([string] $RustTarget) {
|
|
switch ($RustTarget) {
|
|
"x86_64-pc-windows-msvc" { "amd64" }
|
|
"aarch64-pc-windows-msvc" { "arm64" }
|
|
default { throw "Unsupported target: $RustTarget" }
|
|
}
|
|
}
|
|
|
|
function Assert-NativeTestTarget([string] $RustTarget) {
|
|
$targetArch = Get-VsTargetArch $RustTarget
|
|
$hostArch = Get-HostArch
|
|
if ($targetArch -ne $hostArch) {
|
|
throw "Windows tests require a native runner. Target $RustTarget maps to $targetArch, but the host is $hostArch."
|
|
}
|
|
}
|
|
|
|
function Get-SelectedTargets([string] $RequestedTarget) {
|
|
if ($RequestedTarget -eq "native") {
|
|
switch (Get-HostArch) {
|
|
"arm64" { return @("aarch64-pc-windows-msvc") }
|
|
default { return @("x86_64-pc-windows-msvc") }
|
|
}
|
|
}
|
|
if ($RequestedTarget -eq "all") {
|
|
$targets = @("x86_64-pc-windows-msvc")
|
|
if ($env:OPENSHELL_MXC_SKIP_ARM64 -ne "1") {
|
|
$targets += "aarch64-pc-windows-msvc"
|
|
}
|
|
return $targets
|
|
}
|
|
return @($RequestedTarget)
|
|
}
|
|
|
|
function Resolve-Z3HeaderPath([string] $HeaderPath) {
|
|
if ([string]::IsNullOrWhiteSpace($HeaderPath)) {
|
|
throw "Z3_LIBRARY_PATH_OVERRIDE is set. Set Z3_SYS_Z3_HEADER to the full path of z3.h."
|
|
}
|
|
|
|
if (-not (Test-Path $HeaderPath -PathType Leaf)) {
|
|
throw "Z3_SYS_Z3_HEADER is set but z3.h was not found at: $HeaderPath"
|
|
}
|
|
if ((Split-Path -Leaf $HeaderPath) -ne "z3.h") {
|
|
throw "Z3_SYS_Z3_HEADER must point to z3.h. Got: $HeaderPath"
|
|
}
|
|
|
|
return (Resolve-Path $HeaderPath).Path
|
|
}
|
|
|
|
function Configure-Z3 {
|
|
if ([string]::IsNullOrWhiteSpace($env:Z3_LIBRARY_PATH_OVERRIDE)) {
|
|
Write-Host "==> Z3: prebuilt release"
|
|
$env:Z3_SYS_Z3_VERSION = $PrebuiltZ3Version
|
|
Write-Host " Z3_SYS_Z3_VERSION=$env:Z3_SYS_Z3_VERSION"
|
|
return [pscustomobject]@{
|
|
WorkspaceFeatures = $PrebuiltZ3WorkspaceFeatures
|
|
ServerFeatures = $PrebuiltZ3ServerFeatures
|
|
GatewayFeatures = $PrebuiltZ3GatewayFeatures
|
|
}
|
|
}
|
|
|
|
if (-not (Test-Path $env:Z3_LIBRARY_PATH_OVERRIDE -PathType Container)) {
|
|
throw "Z3_LIBRARY_PATH_OVERRIDE is set but the directory does not exist: $env:Z3_LIBRARY_PATH_OVERRIDE"
|
|
}
|
|
|
|
$libDir = (Resolve-Path $env:Z3_LIBRARY_PATH_OVERRIDE).Path
|
|
$importLib = Join-Path $libDir "libz3.lib"
|
|
if (-not (Test-Path $importLib -PathType Leaf)) {
|
|
throw "Z3_LIBRARY_PATH_OVERRIDE is set but libz3.lib was not found at: $importLib"
|
|
}
|
|
|
|
$env:Z3_LIBRARY_PATH_OVERRIDE = $libDir
|
|
$env:Z3_SYS_Z3_HEADER = Resolve-Z3HeaderPath $env:Z3_SYS_Z3_HEADER
|
|
|
|
if (($env:PATH -split ";") -notcontains $libDir) {
|
|
$env:PATH = "$libDir;$env:PATH"
|
|
}
|
|
|
|
Write-Host "==> Z3: system"
|
|
Write-Host " Z3_LIBRARY_PATH_OVERRIDE=$env:Z3_LIBRARY_PATH_OVERRIDE"
|
|
Write-Host " Z3_SYS_Z3_HEADER=$env:Z3_SYS_Z3_HEADER"
|
|
|
|
return [pscustomobject]@{
|
|
WorkspaceFeatures = ""
|
|
ServerFeatures = ""
|
|
GatewayFeatures = ""
|
|
}
|
|
}
|
|
|
|
function Invoke-VsCargo {
|
|
param(
|
|
[Parameter(Mandatory = $true)] [string] $RustTarget,
|
|
[Parameter(Mandatory = $true)] [string] $CargoArgs,
|
|
[Parameter(Mandatory = $true)] [string] $LogName,
|
|
[switch] $AllowNonZeroExit
|
|
)
|
|
|
|
& rustup target add $RustTarget
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "rustup target add $RustTarget failed"
|
|
}
|
|
|
|
$vsDevCmd = Resolve-VsDevCmd $RustTarget
|
|
$targetArch = Get-VsTargetArch $RustTarget
|
|
$hostArch = Get-HostArch
|
|
$logPath = Join-Path $LogDir $LogName
|
|
$rustcWrapper = ""
|
|
if (-not [string]::IsNullOrWhiteSpace($env:RUSTC_WRAPPER)) {
|
|
$wrapperCommand = Get-Command $env:RUSTC_WRAPPER -ErrorAction SilentlyContinue
|
|
if ($wrapperCommand) {
|
|
$rustcWrapper = $wrapperCommand.Source
|
|
} else {
|
|
Write-Warning "RUSTC_WRAPPER '$env:RUSTC_WRAPPER' was not found; continuing without it."
|
|
}
|
|
}
|
|
$environmentSetup = @(
|
|
"set `"CARGO_TARGET_DIR=$TargetDir`"",
|
|
"set `"CARGO_BUILD_JOBS=$WindowsBuildJobs`"",
|
|
"set `"CARGO_INCREMENTAL=0`"",
|
|
"set `"RUSTC_WRAPPER=$rustcWrapper`""
|
|
)
|
|
if ($hostArch -eq "amd64" -and $RustTarget -eq "aarch64-pc-windows-msvc") {
|
|
# Native ARM64 dependencies select their own compilers. Clear inherited
|
|
# overrides so AWS-LC can select clang-cl inside its build script.
|
|
$environmentSetup += @(
|
|
"set `"CC=`"",
|
|
"set `"CXX=`"",
|
|
"set `"CC_aarch64-pc-windows-msvc=`"",
|
|
"set `"CXX_aarch64-pc-windows-msvc=`"",
|
|
"set `"CC_aarch64_pc_windows_msvc=`"",
|
|
"set `"CXX_aarch64_pc_windows_msvc=`""
|
|
)
|
|
}
|
|
$cmd = "call `"$vsDevCmd`" -arch=$targetArch -host_arch=$hostArch && $($environmentSetup -join ' && ') && $CargoArgs"
|
|
|
|
Write-Host "==> $CargoArgs"
|
|
Write-Host " target: $RustTarget"
|
|
Write-Host " log: $logPath"
|
|
|
|
$lockAcquired = $false
|
|
try {
|
|
try {
|
|
$lockAcquired = $WindowsCargoMutex.WaitOne(0)
|
|
if (-not $lockAcquired) {
|
|
Write-Host " waiting for another Windows Cargo task"
|
|
$lockAcquired = $WindowsCargoMutex.WaitOne([TimeSpan]::FromHours(2))
|
|
}
|
|
} catch [System.Threading.AbandonedMutexException] {
|
|
$lockAcquired = $true
|
|
}
|
|
if (-not $lockAcquired) {
|
|
throw "Timed out waiting for another Windows Cargo task to finish."
|
|
}
|
|
|
|
$cmdWithLog = "$cmd > `"$logPath`" 2>&1"
|
|
& cmd /v:on /d /c $cmdWithLog
|
|
$exitCode = $LASTEXITCODE
|
|
if (Test-Path $logPath) {
|
|
Get-Content $logPath
|
|
}
|
|
if ($exitCode -ne 0) {
|
|
if ($AllowNonZeroExit) {
|
|
Write-Warning "Command completed with exit code $exitCode. See $logPath"
|
|
return
|
|
}
|
|
throw "Command failed with exit code $exitCode. See $logPath"
|
|
}
|
|
} finally {
|
|
if ($lockAcquired) {
|
|
$WindowsCargoMutex.ReleaseMutex()
|
|
}
|
|
}
|
|
}
|
|
|
|
function Invoke-Check([string] $RustTarget) {
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo check --workspace $UnsupportedDriverPackageExcludes --target $RustTarget $Z3WorkspaceFeatures" `
|
|
-LogName "build-$RustTarget-check.log"
|
|
Assert-GatewayExcludesUnsupportedDriverCrates $RustTarget
|
|
}
|
|
|
|
function Assert-GatewayExcludesUnsupportedDriverCrates([string] $RustTarget) {
|
|
$logName = "build-$RustTarget-driver-tree.log"
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo tree -p openshell-gateway --target $RustTarget --prefix none" `
|
|
-LogName $logName
|
|
|
|
$logPath = Join-Path $LogDir $logName
|
|
$unexpected = @(Select-String `
|
|
-Path $logPath `
|
|
-Pattern '^openshell-driver-(docker|kubernetes|podman|vm)\s')
|
|
if ($unexpected.Count -gt 0) {
|
|
$packages = ($unexpected.Line | Sort-Object -Unique) -join ", "
|
|
throw "Unsupported driver crates entered the Windows gateway dependency graph: $packages"
|
|
}
|
|
}
|
|
|
|
function Invoke-Lint([string] $RustTarget) {
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo clippy --workspace --all-targets --no-deps $WindowsClippyPackageExcludes --target $RustTarget $Z3WorkspaceFeatures -- $WindowsClippyLintArgs" `
|
|
-LogName "lint-$RustTarget-workspace.log"
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo clippy --manifest-path e2e/rust/Cargo.toml --all-targets --no-deps --target $RustTarget -- $WindowsClippyLintArgs" `
|
|
-LogName "lint-$RustTarget-e2e.log"
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo check --manifest-path examples/governance-interceptor/Cargo.toml --all-targets --target $RustTarget" `
|
|
-LogName "lint-$RustTarget-governance-interceptor.log"
|
|
}
|
|
|
|
function Invoke-Build([string] $RustTarget) {
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo build --release --target $RustTarget --bin openshell-gateway --bin openshell --bin openshell-supervisor-relay $Z3GatewayFeatures" `
|
|
-LogName "build-$RustTarget-release.log"
|
|
|
|
$z3Runtime = if ([string]::IsNullOrWhiteSpace($env:Z3_LIBRARY_PATH_OVERRIDE)) {
|
|
$buildRoot = Join-Path $TargetDir "$RustTarget\release\build"
|
|
$candidates = @(
|
|
Get-ChildItem -Path $buildRoot -Filter "libz3.dll" -File -Recurse -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.FullName -like "*\z3-$PrebuiltZ3Version\bin\libz3.dll" } |
|
|
Sort-Object LastWriteTimeUtc -Descending
|
|
)
|
|
if ($candidates.Count -eq 0) {
|
|
throw "The prebuilt Z3 runtime was not found under: $buildRoot"
|
|
}
|
|
$candidates[0].FullName
|
|
} else {
|
|
$path = Join-Path $env:Z3_LIBRARY_PATH_OVERRIDE "libz3.dll"
|
|
if (-not (Test-Path $path -PathType Leaf)) {
|
|
throw "Z3_LIBRARY_PATH_OVERRIDE is set but libz3.dll was not found at: $path"
|
|
}
|
|
$path
|
|
}
|
|
|
|
$z3RuntimeDestination = Join-Path $TargetDir "$RustTarget\release\libz3.dll"
|
|
Copy-Item -LiteralPath $z3Runtime -Destination $z3RuntimeDestination -Force
|
|
Write-Host "==> Staged Z3 runtime: $z3RuntimeDestination"
|
|
}
|
|
|
|
function Invoke-Test([string] $RustTarget) {
|
|
Assert-NativeTestTarget $RustTarget
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo nextest run --profile ci --workspace $UnsupportedDriverPackageExcludes --target $RustTarget $Z3WorkspaceFeatures" `
|
|
-LogName "test-$RustTarget.log"
|
|
}
|
|
|
|
function Invoke-PreCommitTest([string] $RustTarget) {
|
|
Assert-NativeTestTarget $RustTarget
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo nextest run --profile ci --workspace $UnsupportedDriverPackageExcludes --target $RustTarget --features openshell-server/test-support $Z3ServerFeatures" `
|
|
-LogName "test-$RustTarget-precommit.log"
|
|
}
|
|
|
|
function Invoke-ConformanceTest([string] $RustTarget) {
|
|
Assert-NativeTestTarget $RustTarget
|
|
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo build --target $RustTarget --bin openshell --bin openshell-gateway $Z3GatewayFeatures" `
|
|
-LogName "build-$RustTarget-conformance.log"
|
|
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo test --locked --manifest-path tests/suites/conformance/Cargo.toml --package openshell-test-conformance-cli --target $RustTarget --no-run" `
|
|
-LogName "build-$RustTarget-conformance-tests.log"
|
|
|
|
$binaryDir = Join-Path $TargetDir "$RustTarget\debug"
|
|
$gateway = Join-Path $binaryDir "openshell-gateway.exe"
|
|
$cli = Join-Path $binaryDir "openshell.exe"
|
|
|
|
$buildRoot = Join-Path $binaryDir "build"
|
|
$z3Candidates = @(
|
|
Get-ChildItem -Path $buildRoot -Filter "libz3.dll" -File -Recurse -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.FullName -like "*\z3-$PrebuiltZ3Version\bin\libz3.dll" } |
|
|
Sort-Object LastWriteTimeUtc -Descending
|
|
)
|
|
if ($z3Candidates.Count -eq 0) {
|
|
throw "The prebuilt Z3 runtime was not found under: $buildRoot"
|
|
}
|
|
Copy-Item -LiteralPath $z3Candidates[0].FullName -Destination (Join-Path $binaryDir "libz3.dll") -Force
|
|
|
|
$tempRoot = $env:RUNNER_TEMP
|
|
if ([string]::IsNullOrWhiteSpace($tempRoot)) {
|
|
$tempRoot = [System.IO.Path]::GetTempPath()
|
|
}
|
|
$testRoot = Join-Path $tempRoot "openshell-conformance-$RustTarget-$([guid]::NewGuid().ToString('N'))"
|
|
$configRoot = Join-Path $testRoot "config"
|
|
$stateRoot = Join-Path $testRoot "state"
|
|
$gatewayConfig = Join-Path $testRoot "gateway.toml"
|
|
$gatewayLog = Join-Path $LogDir "test-$RustTarget-conformance-gateway.log"
|
|
$gatewayErrorLog = Join-Path $LogDir "test-$RustTarget-conformance-gateway.err.log"
|
|
New-Item -ItemType Directory -Force -Path $configRoot, $stateRoot | Out-Null
|
|
@"
|
|
[openshell]
|
|
version = 2
|
|
|
|
[openshell.drivers.mxc]
|
|
wxc_exec_path = "C:\\mxc\\wxc-exec.exe"
|
|
backend = "process_container"
|
|
"@ | Set-Content -LiteralPath $gatewayConfig -Encoding utf8
|
|
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
|
|
$listener.Start()
|
|
$port = ([System.Net.IPEndPoint] $listener.LocalEndpoint).Port
|
|
$listener.Stop()
|
|
|
|
$env:OPENSHELL_GATEWAY_CONFIG = $gatewayConfig
|
|
$env:OPENSHELL_COMPUTE_DRIVER = "mxc"
|
|
$env:OPENSHELL_MXC_MOCK_WXC = "1"
|
|
$env:XDG_CONFIG_HOME = $configRoot
|
|
$env:XDG_STATE_HOME = $stateRoot
|
|
$env:OPENSHELL_BIN = $cli
|
|
$gatewayProcess = $null
|
|
try {
|
|
$gatewayProcess = Start-Process -FilePath $gateway `
|
|
-ArgumentList @("--disable-tls", "--db-url", "sqlite::memory:", "--port", $port) `
|
|
-PassThru -WindowStyle Hidden `
|
|
-RedirectStandardOutput $gatewayLog -RedirectStandardError $gatewayErrorLog
|
|
|
|
$deadline = (Get-Date).AddSeconds(30)
|
|
while ((Get-Date) -lt $deadline) {
|
|
if ($gatewayProcess.HasExited) {
|
|
throw "Conformance gateway exited early with code $($gatewayProcess.ExitCode)."
|
|
}
|
|
if (Get-NetTCPConnection -State Listen -LocalPort $port -ErrorAction SilentlyContinue) {
|
|
break
|
|
}
|
|
Start-Sleep -Milliseconds 250
|
|
}
|
|
if (-not (Get-NetTCPConnection -State Listen -LocalPort $port -ErrorAction SilentlyContinue)) {
|
|
throw "Conformance gateway did not listen on port $port within 30 seconds."
|
|
}
|
|
|
|
& $cli gateway add "http://127.0.0.1:$port" --local --name windows-conformance
|
|
if ($LASTEXITCODE -ne 0) { throw "Failed to register the conformance gateway." }
|
|
& $cli gateway select windows-conformance
|
|
if ($LASTEXITCODE -ne 0) { throw "Failed to select the conformance gateway." }
|
|
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo test --locked --manifest-path tests/suites/conformance/Cargo.toml --package openshell-test-conformance-cli --target $RustTarget --no-fail-fast -- --test-threads=1 --nocapture" `
|
|
-LogName "test-$RustTarget-conformance.log" `
|
|
-AllowNonZeroExit
|
|
} catch {
|
|
Get-Content -LiteralPath $gatewayLog, $gatewayErrorLog -ErrorAction SilentlyContinue
|
|
throw
|
|
} finally {
|
|
if ($gatewayProcess -and -not $gatewayProcess.HasExited) {
|
|
Stop-Process -Id $gatewayProcess.Id -Force -ErrorAction SilentlyContinue
|
|
}
|
|
Remove-Item -LiteralPath $testRoot -Recurse -Force -ErrorAction SilentlyContinue
|
|
}
|
|
}
|
|
|
|
function Invoke-UnsupportedContractTests([string] $RustTarget) {
|
|
Assert-NativeTestTarget $RustTarget
|
|
|
|
$tests = @(
|
|
"windows_builtin_compute_drivers_report_unsupported",
|
|
"default_registry_contains_exactly_the_enabled_compute_drivers"
|
|
)
|
|
foreach ($test in $tests) {
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo test -p openshell-gateway --target $RustTarget $test $Z3GatewayFeatures" `
|
|
-LogName "test-$RustTarget-unsupported-$test.log"
|
|
}
|
|
|
|
foreach ($features in @("", "compute-driver-mxc", "compute-driver-docker", "compute-driver-mxc,compute-driver-docker")) {
|
|
$featureArgs = if ($features) { "--features $features" } else { "" }
|
|
$variant = if ($features) { $features.Replace(",", "-") } else { "protocol-only" }
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo test -p openshell-gateway --lib --target $RustTarget --no-default-features $featureArgs $Z3GatewayFeatures" `
|
|
-LogName "test-$RustTarget-selective-$variant.log"
|
|
}
|
|
}
|
|
|
|
function Invoke-MxcRealTests([string] $RustTarget) {
|
|
Assert-NativeTestTarget $RustTarget
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo test -p openshell-driver-mxc --test wxc_exec_real --target $RustTarget -- --ignored --test-threads=1 --nocapture" `
|
|
-LogName "test-$RustTarget-mxc-real.log"
|
|
}
|
|
|
|
function Invoke-MxcGb300Tests([string] $RustTarget) {
|
|
Assert-NativeTestTarget $RustTarget
|
|
if ($RustTarget -ne "aarch64-pc-windows-msvc") {
|
|
throw "The GB300 qualification contract requires native ARM64; target $RustTarget cannot receive credit."
|
|
}
|
|
|
|
$tests = @(
|
|
"dryrun_accepts_minimal_processcontainer_config",
|
|
"dryrun_accepts_processcontainer_ui_policy_matrix",
|
|
"dryrun_accepts_network_block_without_proxy",
|
|
"dryrun_accepts_localhost_proxy_shape",
|
|
"dryrun_rejects_host_port_proxy_shape",
|
|
"dryrun_rejects_unknown_containment",
|
|
"dryrun_accepts_split_policy_output",
|
|
"pc_oneshot_in_policy_write_succeeds",
|
|
"pc_https_egress_reads_injected_ca_bundle",
|
|
"pc_oneshot_out_of_policy_write_denied"
|
|
)
|
|
$logName = "test-$RustTarget-mxc-gb300.log"
|
|
$logPath = Join-Path $LogDir $logName
|
|
$combinedLog = @()
|
|
foreach ($test in $tests) {
|
|
$testLogName = "test-$RustTarget-mxc-gb300-$test.log"
|
|
Invoke-VsCargo `
|
|
-RustTarget $RustTarget `
|
|
-CargoArgs "cargo test -p openshell-driver-mxc --test wxc_exec_real --target $RustTarget $test -- --ignored --exact --test-threads=1 --nocapture" `
|
|
-LogName $testLogName
|
|
|
|
$testLogPath = Join-Path $LogDir $testLogName
|
|
$testLog = @(Get-Content -LiteralPath $testLogPath)
|
|
$combinedLog += "===== $test ====="
|
|
$combinedLog += $testLog
|
|
if ($testLog -match 'SKIP:') {
|
|
throw "Required GB300 MXC test $test skipped. See $testLogPath"
|
|
}
|
|
$escapedTest = [Regex]::Escape($test)
|
|
if (-not ($testLog -match "^test $escapedTest \.\.\. ok$")) {
|
|
throw "Required GB300 MXC test $test did not report a passing assertion. See $testLogPath"
|
|
}
|
|
}
|
|
$combinedLog | Out-File -LiteralPath $logPath -Encoding utf8
|
|
}
|
|
|
|
function Get-Sha256([string] $Path) {
|
|
$stream = [System.IO.File]::OpenRead($Path)
|
|
try {
|
|
$sha256 = [System.Security.Cryptography.SHA256]::Create()
|
|
try {
|
|
return [BitConverter]::ToString($sha256.ComputeHash($stream)).Replace("-", "")
|
|
} finally {
|
|
$sha256.Dispose()
|
|
}
|
|
} finally {
|
|
$stream.Dispose()
|
|
}
|
|
}
|
|
|
|
function Show-Artifacts([string[]] $RustTargets) {
|
|
$rows = @()
|
|
foreach ($rustTarget in $RustTargets) {
|
|
foreach ($binary in @("openshell-gateway.exe", "openshell.exe", "openshell-supervisor-relay.exe", "libz3.dll")) {
|
|
$path = Join-Path $TargetDir "$rustTarget\release\$binary"
|
|
if (-not (Test-Path $path)) {
|
|
continue
|
|
}
|
|
$item = Get-Item $path
|
|
$rows += [pscustomobject]@{
|
|
Target = $rustTarget
|
|
Binary = $binary
|
|
Size = $item.Length
|
|
SHA256 = Get-Sha256 $item.FullName
|
|
Path = $item.FullName
|
|
}
|
|
}
|
|
}
|
|
if ($rows.Count -eq 0) {
|
|
Write-Warning "No release artifacts found under $TargetDir"
|
|
return
|
|
}
|
|
$rows | Format-Table -AutoSize
|
|
}
|
|
|
|
if ($Action -eq "ci" -and (Get-HostArch) -ne "amd64") {
|
|
throw "windows:ci is an x64-host contract. On ARM64, run windows:check:arm64, windows:build:arm64, windows:test:arm64, windows:test:unsupported:arm64, and windows:artifacts explicitly."
|
|
}
|
|
|
|
$targets = Get-SelectedTargets $Target
|
|
if ($Action -in @("test", "test-precommit", "test-conformance", "test-unsupported", "test-mxc-real", "test-mxc-gb300")) {
|
|
foreach ($rustTarget in $targets) {
|
|
Assert-NativeTestTarget $rustTarget
|
|
}
|
|
}
|
|
|
|
if ($Action -in @("check", "lint", "build", "test", "test-precommit", "test-conformance", "test-unsupported", "test-mxc-real", "test-mxc-gb300", "ci")) {
|
|
$z3Features = Configure-Z3
|
|
$Z3WorkspaceFeatures = $z3Features.WorkspaceFeatures
|
|
$Z3ServerFeatures = $z3Features.ServerFeatures
|
|
$Z3GatewayFeatures = $z3Features.GatewayFeatures
|
|
$env:LIBCLANG_PATH = Resolve-LibclangPath
|
|
Add-PathEntry $env:LIBCLANG_PATH
|
|
Write-Host "==> LIBCLANG_PATH=$env:LIBCLANG_PATH"
|
|
Configure-Arm64CrossBuild $targets
|
|
}
|
|
|
|
switch ($Action) {
|
|
"check" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-Check $rustTarget
|
|
}
|
|
}
|
|
"lint" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-Lint $rustTarget
|
|
}
|
|
}
|
|
"build" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-Build $rustTarget
|
|
}
|
|
Show-Artifacts $targets
|
|
}
|
|
"test" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-Test $rustTarget
|
|
}
|
|
}
|
|
"test-precommit" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-PreCommitTest $rustTarget
|
|
}
|
|
}
|
|
"test-conformance" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-ConformanceTest $rustTarget
|
|
}
|
|
}
|
|
"test-unsupported" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-UnsupportedContractTests $rustTarget
|
|
}
|
|
}
|
|
"test-mxc-real" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-MxcRealTests $rustTarget
|
|
}
|
|
}
|
|
"test-mxc-gb300" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-MxcGb300Tests $rustTarget
|
|
}
|
|
}
|
|
"artifacts" {
|
|
Show-Artifacts $targets
|
|
}
|
|
"ci" {
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-Check $rustTarget
|
|
}
|
|
foreach ($rustTarget in $targets) {
|
|
Invoke-Build $rustTarget
|
|
}
|
|
Invoke-PreCommitTest "x86_64-pc-windows-msvc"
|
|
Invoke-UnsupportedContractTests "x86_64-pc-windows-msvc"
|
|
Show-Artifacts $targets
|
|
}
|
|
}
|