[CmdletBinding()] param( [string]$Preset = "windows-msvc-default", [string]$Configuration = "Debug", [string]$Target = "PanoPainter", [string]$CMakeCommand = "cmake", [switch]$ReadinessOnly, [switch]$AndroidNativeChecks, [string[]]$PackageKinds = @( "windows-appx", "android-standard-apk", "android-quest-apk", "android-focus-apk", "apple-bundle", "linux-app", "webgl" ) ) $ErrorActionPreference = "Stop" $started = Get-Date $root = (Get-Location).Path function Test-CommandAvailable { param([string]$Name) return [bool](Get-Command $Name -ErrorAction SilentlyContinue) } function Expand-ArgumentList { param([string[]]$Values) $expanded = @() foreach ($value in $Values) { foreach ($part in ($value -split ",")) { $trimmed = $part.Trim() if ($trimmed.Length -gt 0) { $expanded += $trimmed } } } return $expanded } function New-ArtifactCheck { param( [string]$Name, [string]$Path, [string]$PathType = "Any" ) $exists = if ($PathType -eq "Container") { Test-Path -LiteralPath $Path -PathType Container } elseif ($PathType -eq "Leaf") { Test-Path -LiteralPath $Path -PathType Leaf } else { Test-Path -LiteralPath $Path } [ordered]@{ name = $Name path = $Path pathType = $PathType exists = $exists } } function New-Prerequisite { param( [string]$Name, [bool]$Available, [string]$Detail ) [ordered]@{ name = $Name available = $Available detail = $Detail } } function New-PackageReadiness { param( [string]$Kind, [string]$Status, [string]$Reason, [object[]]$Prerequisites, [object[]]$Artifacts, [string]$ValidationCommand ) [ordered]@{ kind = $Kind status = $Status reason = $Reason debt = "DEBT-0011" validationCommand = $ValidationCommand prerequisites = $Prerequisites artifacts = $Artifacts } } function Get-AndroidNativeCheckPlan { param([string[]]$Kinds) $packages = @() if ($Kinds -contains "android-standard-apk") { $packages += [ordered]@{ packages = @("standard") configureOnly = $false } } $configureOnlyPackages = @() if ($Kinds -contains "android-quest-apk") { $configureOnlyPackages += "quest" } if ($Kinds -contains "android-focus-apk") { $configureOnlyPackages += "focus" } if ($configureOnlyPackages.Count -gt 0) { $packages += [ordered]@{ packages = $configureOnlyPackages configureOnly = $true } } return $packages } function Invoke-AndroidNativePackageChecks { param([string[]]$Kinds) $plans = @(Get-AndroidNativeCheckPlan -Kinds $Kinds) $results = @() $overallExitCode = 0 foreach ($plan in $plans) { $arguments = @( "-ExecutionPolicy", "Bypass", "-File", (Join-Path $root "scripts/automation/android-legacy-package-build.ps1"), "-Packages", ($plan.packages -join ",") ) if ($plan.configureOnly) { $arguments += "-ConfigureOnly" } $output = @(& powershell @arguments 2>&1) $exitCode = $LASTEXITCODE if ($exitCode -ne 0 -and $overallExitCode -eq 0) { $overallExitCode = $exitCode } $jsonLine = @($output | ForEach-Object { $_.ToString() } | Where-Object { $_.TrimStart().StartsWith("{") } | Select-Object -Last 1) $summary = $null if ($jsonLine.Count -gt 0) { try { $summary = $jsonLine[-1] | ConvertFrom-Json } catch { $summary = $null } } $results += [ordered]@{ packages = $plan.packages configureOnly = [bool]$plan.configureOnly exitCode = $exitCode command = "powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages $($plan.packages -join ',')$(if ($plan.configureOnly) { ' -ConfigureOnly' } else { '' })" summary = $summary } } [ordered]@{ requested = $plans.Count -gt 0 exitCode = $overallExitCode results = $results } } $PackageKinds = @(Expand-ArgumentList -Values $PackageKinds) function Get-PackageReadiness { param([string[]]$Kinds) $readiness = @() foreach ($kind in $Kinds) { switch ($kind) { "windows-appx" { $wapproj = Join-Path $root "PanoPainterPackage/PanoPainterPackage.wapproj" $manifest = Join-Path $root "PanoPainterPackage/Package.appxmanifest" $appPackages = Join-Path $root "PanoPainterPackage/AppPackages" $readiness += New-PackageReadiness ` -Kind $kind ` -Status "blocked" ` -Reason "legacy-wapproj-present-but-root-cmake-package-target-missing" ` -ValidationCommand "msbuild PanoPainterPackage/PanoPainterPackage.wapproj /p:Configuration=$Configuration /p:Platform=x64" ` -Prerequisites @( (New-Prerequisite -Name "legacy-wapproj" -Available (Test-Path -LiteralPath $wapproj -PathType Leaf) -Detail $wapproj), (New-Prerequisite -Name "appx-manifest" -Available (Test-Path -LiteralPath $manifest -PathType Leaf) -Detail $manifest), (New-Prerequisite -Name "makeappx" -Available (Test-CommandAvailable "makeappx") -Detail "Windows SDK packaging tool"), (New-Prerequisite -Name "signtool" -Available (Test-CommandAvailable "signtool") -Detail "Windows SDK signing tool"), (New-Prerequisite -Name "root-cmake-package-target" -Available $false -Detail "Not migrated yet") ) ` -Artifacts @( (New-ArtifactCheck -Name "app-packages" -Path $appPackages -PathType "Container") ) } "android-standard-apk" { $gradle = Join-Path $root "android/android/build.gradle" $manifest = Join-Path $root "android/android/src/main/AndroidManifest.xml" $apkDir = Join-Path $root "android/android/build/outputs/apk" $readiness += New-PackageReadiness ` -Kind $kind ` -Status "blocked" ` -Reason "legacy-gradle-package-not-consuming-root-cmake-targets" ` -ValidationCommand "gradle -p android/android assembleDebug" ` -Prerequisites @( (New-Prerequisite -Name "gradle-build" -Available (Test-Path -LiteralPath $gradle -PathType Leaf) -Detail $gradle), (New-Prerequisite -Name "android-manifest" -Available (Test-Path -LiteralPath $manifest -PathType Leaf) -Detail $manifest), (New-Prerequisite -Name "gradle" -Available (Test-CommandAvailable "gradle") -Detail "Android package builder"), (New-Prerequisite -Name "retained-native-cmake-check" -Available $true -Detail "powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages standard"), (New-Prerequisite -Name "root-cmake-preset" -Available $true -Detail "android-arm64/android-x64"), (New-Prerequisite -Name "root-cmake-package-target" -Available $false -Detail "Not migrated yet") ) ` -Artifacts @( (New-ArtifactCheck -Name "apk-output" -Path $apkDir -PathType "Container") ) } "android-quest-apk" { $gradle = Join-Path $root "android/quest/build.gradle" $manifest = Join-Path $root "android/quest/src/main/AndroidManifest.xml" $apkDir = Join-Path $root "android/quest/build/outputs/apk" $readiness += New-PackageReadiness ` -Kind $kind ` -Status "blocked" ` -Reason "legacy-gradle-package-not-consuming-root-cmake-targets" ` -ValidationCommand "gradle -p android/quest assembleDebug" ` -Prerequisites @( (New-Prerequisite -Name "gradle-build" -Available (Test-Path -LiteralPath $gradle -PathType Leaf) -Detail $gradle), (New-Prerequisite -Name "android-manifest" -Available (Test-Path -LiteralPath $manifest -PathType Leaf) -Detail $manifest), (New-Prerequisite -Name "gradle" -Available (Test-CommandAvailable "gradle") -Detail "Android package builder"), (New-Prerequisite -Name "retained-native-cmake-check" -Available $true -Detail "powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages quest -ConfigureOnly"), (New-Prerequisite -Name "root-cmake-preset" -Available $true -Detail "android-quest-arm64"), (New-Prerequisite -Name "root-cmake-package-target" -Available $false -Detail "Not migrated yet") ) ` -Artifacts @( (New-ArtifactCheck -Name "apk-output" -Path $apkDir -PathType "Container") ) } "android-focus-apk" { $gradle = Join-Path $root "android/focus/build.gradle" $manifest = Join-Path $root "android/focus/src/main/AndroidManifest.xml" $apkDir = Join-Path $root "android/focus/build/outputs/apk" $readiness += New-PackageReadiness ` -Kind $kind ` -Status "blocked" ` -Reason "legacy-gradle-package-not-consuming-root-cmake-targets" ` -ValidationCommand "gradle -p android/focus assembleDebug" ` -Prerequisites @( (New-Prerequisite -Name "gradle-build" -Available (Test-Path -LiteralPath $gradle -PathType Leaf) -Detail $gradle), (New-Prerequisite -Name "android-manifest" -Available (Test-Path -LiteralPath $manifest -PathType Leaf) -Detail $manifest), (New-Prerequisite -Name "gradle" -Available (Test-CommandAvailable "gradle") -Detail "Android package builder"), (New-Prerequisite -Name "retained-native-cmake-check" -Available $true -Detail "powershell -ExecutionPolicy Bypass -File scripts\automation\android-legacy-package-build.ps1 -Packages focus -ConfigureOnly"), (New-Prerequisite -Name "root-cmake-preset" -Available $true -Detail "android-focus-arm64"), (New-Prerequisite -Name "root-cmake-package-target" -Available $false -Detail "Not migrated yet") ) ` -Artifacts @( (New-ArtifactCheck -Name "apk-output" -Path $apkDir -PathType "Container") ) } "apple-bundle" { $xcodeProject = Join-Path $root "PanoPainter.xcodeproj/project.pbxproj" $bundleDir = Join-Path $root "out/package/apple" $readiness += New-PackageReadiness ` -Kind $kind ` -Status "blocked" ` -Reason "legacy-xcode-project-and-host-toolchain-not-aligned-with-root-cmake-package-target" ` -ValidationCommand "xcodebuild -project PanoPainter.xcodeproj -configuration $Configuration" ` -Prerequisites @( (New-Prerequisite -Name "legacy-xcode-project" -Available (Test-Path -LiteralPath $xcodeProject -PathType Leaf) -Detail $xcodeProject), (New-Prerequisite -Name "xcodebuild" -Available (Test-CommandAvailable "xcodebuild") -Detail "Apple package builder"), (New-Prerequisite -Name "root-cmake-preset" -Available $true -Detail "macos/ios-device/ios-simulator"), (New-Prerequisite -Name "root-cmake-package-target" -Available $false -Detail "Not migrated yet") ) ` -Artifacts @( (New-ArtifactCheck -Name "apple-package-output" -Path $bundleDir -PathType "Container") ) } "linux-app" { $linuxCmake = Join-Path $root "linux/CMakeLists.txt" $linuxBinary = Join-Path $root "out/package/linux/panopainter" $readiness += New-PackageReadiness ` -Kind $kind ` -Status "blocked" ` -Reason "retained-linux-cmake-not-consuming-root-cmake-targets" ` -ValidationCommand "cmake -S linux -B out/package/linux-retained && cmake --build out/package/linux-retained --target panopainter" ` -Prerequisites @( (New-Prerequisite -Name "retained-linux-cmake" -Available (Test-Path -LiteralPath $linuxCmake -PathType Leaf) -Detail $linuxCmake), (New-Prerequisite -Name "cmake" -Available (Test-CommandAvailable "cmake") -Detail "Linux retained app CMake configure/build tool"), (New-Prerequisite -Name "retained-platform-cmake-baseline" -Available $true -Detail "python scripts/dev/check_retained_platform_cmake.py"), (New-Prerequisite -Name "root-cmake-preset" -Available $true -Detail "linux-clang"), (New-Prerequisite -Name "root-cmake-package-target" -Available $false -Detail "Not migrated yet") ) ` -Artifacts @( (New-ArtifactCheck -Name "linux-app-output" -Path $linuxBinary -PathType "Leaf") ) } "webgl" { $webglCmake = Join-Path $root "webgl/CMakeLists.txt" $webDir = Join-Path $root "out/package/webgl" $readiness += New-PackageReadiness ` -Kind $kind ` -Status "blocked" ` -Reason "retained-webgl-cmake-not-consuming-root-cmake-targets" ` -ValidationCommand "emcmake cmake -S webgl -B out/package/webgl-retained && cmake --build out/package/webgl-retained --target panopainter" ` -Prerequisites @( (New-Prerequisite -Name "retained-webgl-cmake" -Available (Test-Path -LiteralPath $webglCmake -PathType Leaf) -Detail $webglCmake), (New-Prerequisite -Name "emcc" -Available (Test-CommandAvailable "emcc") -Detail "Emscripten compiler"), (New-Prerequisite -Name "emcmake" -Available (Test-CommandAvailable "emcmake") -Detail "Emscripten CMake wrapper"), (New-Prerequisite -Name "retained-platform-cmake-baseline" -Available $true -Detail "python scripts/dev/check_retained_platform_cmake.py"), (New-Prerequisite -Name "root-cmake-preset" -Available $true -Detail "emscripten"), (New-Prerequisite -Name "root-cmake-package-target" -Available $false -Detail "Not migrated yet") ) ` -Artifacts @( (New-ArtifactCheck -Name "webgl-output" -Path $webDir -PathType "Container") ) } } } return $readiness } $androidNativeValidation = if ($AndroidNativeChecks) { Invoke-AndroidNativePackageChecks -Kinds $PackageKinds } else { [ordered]@{ requested = $false exitCode = 0 results = @() } } if ($ReadinessOnly) { $elapsed = [int]((Get-Date) - $started).TotalMilliseconds [ordered]@{ command = "package-smoke" preset = $Preset configuration = $Configuration target = $Target stage = "readiness" exitCode = 0 elapsedMs = $elapsed androidNativeValidation = $androidNativeValidation packageReadiness = @(Get-PackageReadiness -Kinds $PackageKinds) } | ConvertTo-Json -Compress -Depth 8 exit $androidNativeValidation.exitCode } & $CMakeCommand --build --preset $Preset --config $Configuration --target $Target $buildExitCode = $LASTEXITCODE if ($buildExitCode -ne 0) { $elapsed = [int]((Get-Date) - $started).TotalMilliseconds [ordered]@{ command = "package-smoke" preset = $Preset configuration = $Configuration target = $Target stage = "build" cmakeCommand = $CMakeCommand exitCode = $buildExitCode elapsedMs = $elapsed androidNativeValidation = $androidNativeValidation packageReadiness = @(Get-PackageReadiness -Kinds $PackageKinds) } | ConvertTo-Json -Compress -Depth 8 exit $buildExitCode } $binaryDir = Join-Path (Join-Path (Join-Path (Get-Location) "out/build/$Preset") $Configuration) "$Target.exe" $targetDir = Split-Path -Parent $binaryDir $dataDir = Join-Path $targetDir "data" $curlDll = if ($Configuration -eq "Debug") { "libcurl_debug.dll" } else { "libcurl.dll" } $checks = @( [ordered]@{ name = "executable"; path = $binaryDir; exists = Test-Path -LiteralPath $binaryDir -PathType Leaf }, [ordered]@{ name = "data"; path = $dataDir; exists = Test-Path -LiteralPath $dataDir -PathType Container }, [ordered]@{ name = "BugTrapU-x64.dll"; path = (Join-Path $targetDir "BugTrapU-x64.dll"); exists = Test-Path -LiteralPath (Join-Path $targetDir "BugTrapU-x64.dll") -PathType Leaf }, [ordered]@{ name = $curlDll; path = (Join-Path $targetDir $curlDll); exists = Test-Path -LiteralPath (Join-Path $targetDir $curlDll) -PathType Leaf }, [ordered]@{ name = "libyuv.dll"; path = (Join-Path $targetDir "libyuv.dll"); exists = Test-Path -LiteralPath (Join-Path $targetDir "libyuv.dll") -PathType Leaf }, [ordered]@{ name = "libmp4v2.dll"; path = (Join-Path $targetDir "libmp4v2.dll"); exists = Test-Path -LiteralPath (Join-Path $targetDir "libmp4v2.dll") -PathType Leaf }, [ordered]@{ name = "openh264-2.0.0-win64.dll"; path = (Join-Path $targetDir "openh264-2.0.0-win64.dll"); exists = Test-Path -LiteralPath (Join-Path $targetDir "openh264-2.0.0-win64.dll") -PathType Leaf }, [ordered]@{ name = "openvr_api.dll"; path = (Join-Path $targetDir "openvr_api.dll"); exists = Test-Path -LiteralPath (Join-Path $targetDir "openvr_api.dll") -PathType Leaf } ) $failed = @($checks | Where-Object { -not $_.exists }) $exitCode = if ($failed.Count -eq 0) { 0 } else { 2 } if ($androidNativeValidation.exitCode -ne 0 -and $exitCode -eq 0) { $exitCode = $androidNativeValidation.exitCode } $elapsedMs = [int]((Get-Date) - $started).TotalMilliseconds [ordered]@{ command = "package-smoke" preset = $Preset configuration = $Configuration target = $Target cmakeCommand = $CMakeCommand exitCode = $exitCode elapsedMs = $elapsedMs checks = $checks androidNativeValidation = $androidNativeValidation packageReadiness = @(Get-PackageReadiness -Kinds $PackageKinds) } | ConvertTo-Json -Compress -Depth 8 exit $exitCode