| param( |
| [Parameter(Mandatory = $true)] |
| [string]$OutputDir, |
| [string]$AndroidSdk, |
| [string]$NdkRoot |
| ) |
|
|
| $ErrorActionPreference = 'Stop' |
| $repo = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path |
| if (-not $AndroidSdk) { |
| if (-not $env:LOCALAPPDATA) { throw 'pass -AndroidSdk when LOCALAPPDATA is unavailable' } |
| $AndroidSdk = Join-Path $env:LOCALAPPDATA 'Android\Sdk' |
| } |
| $sdk = (Resolve-Path $AndroidSdk).Path |
| if (-not $NdkRoot) { $NdkRoot = Join-Path $sdk 'ndk\27.0.12077973' } |
| $ndk = (Resolve-Path $NdkRoot).Path |
| $clang = Join-Path $ndk 'toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android34-clang.cmd' |
| $ar = Join-Path $ndk 'toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-ar.exe' |
| foreach ($tool in @($clang, $ar)) { |
| if (-not (Test-Path -LiteralPath $tool -PathType Leaf)) { throw "missing NDK tool: $tool" } |
| } |
| $sourceProperties = Get-Content -Raw -LiteralPath (Join-Path $ndk 'source.properties') |
| if ($sourceProperties -notmatch '(?m)^Pkg\.Revision\s*=\s*27\.') { |
| throw "DinoVision's API-34 camera build requires NDK r27; got $sourceProperties" |
| } |
|
|
| $destination = [System.IO.Path]::GetFullPath($OutputDir) |
| if (Test-Path -LiteralPath $destination) { |
| if (@(Get-ChildItem -LiteralPath $destination -Force).Count -ne 0) { |
| throw "Refusing to merge Android build output into non-empty directory: $destination" |
| } |
| } else { |
| New-Item -ItemType Directory -Path $destination | Out-Null |
| } |
|
|
| $env:ANDROID_HOME = $sdk |
| $env:ANDROID_SDK_ROOT = $sdk |
| $env:ANDROID_NDK_ROOT = $ndk |
| $env:ANDROID_NDK_HOME = $ndk |
| $env:CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER = $clang |
| $env:CC_aarch64_linux_android = $clang |
| $env:AR_aarch64_linux_android = $ar |
| $cargo = (Get-Command cargo).Source |
|
|
| function Run-Cargo([string[]]$Arguments, [string]$Name) { |
| $process = Start-Process ` |
| -FilePath $cargo ` |
| -ArgumentList $Arguments ` |
| -NoNewWindow ` |
| -Wait ` |
| -PassThru ` |
| -RedirectStandardOutput (Join-Path $destination "$Name.stdout.log") ` |
| -RedirectStandardError (Join-Path $destination "$Name.stderr.log") |
| if ($process.ExitCode -ne 0) { |
| throw "$Name failed with code $($process.ExitCode)" |
| } |
| } |
|
|
| Push-Location $repo |
| try { |
| Run-Cargo @( |
| 'build', '--locked', '--release', '--target', 'aarch64-linux-android', |
| '--example', 'bench', '--example', 'evaluate_decoder' |
| ) 'native-build' |
| Run-Cargo @('apk', 'build', '--release', '--package', 'dinovision-xr') 'apk-build' |
| } finally { |
| Pop-Location |
| } |
|
|
| $built = [ordered]@{ |
| 'bench' = Join-Path $repo 'target\aarch64-linux-android\release\examples\bench' |
| 'evaluate_decoder' = Join-Path $repo 'target\aarch64-linux-android\release\examples\evaluate_decoder' |
| 'dinovision_xr.apk' = Join-Path $repo 'target\release\apk\dinovision_xr.apk' |
| } |
| $artifacts = @() |
| foreach ($entry in $built.GetEnumerator()) { |
| if (-not (Test-Path -LiteralPath $entry.Value -PathType Leaf)) { |
| throw "expected Android artifact is missing: $($entry.Value)" |
| } |
| $target = Join-Path $destination $entry.Key |
| Copy-Item -LiteralPath $entry.Value -Destination $target |
| $artifacts += [ordered]@{ |
| name = $entry.Key |
| bytes = (Get-Item -LiteralPath $target).Length |
| sha256 = (Get-FileHash -LiteralPath $target -Algorithm SHA256).Hash.ToLowerInvariant() |
| } |
| } |
|
|
| $buildTools = Get-ChildItem -LiteralPath (Join-Path $sdk 'build-tools') -Directory | |
| Sort-Object { [version]$_.Name } -Descending | |
| Select-Object -First 1 |
| $aapt = Join-Path $buildTools.FullName 'aapt.exe' |
| if (-not (Test-Path -LiteralPath $aapt -PathType Leaf)) { throw 'aapt.exe was not found' } |
| & $aapt dump badging (Join-Path $destination 'dinovision_xr.apk') | |
| Set-Content -LiteralPath (Join-Path $destination 'apk-badging.txt') -Encoding utf8 |
| if ($LASTEXITCODE -ne 0) { throw 'could not inspect the XR APK manifest' } |
| $badging = Get-Content -Raw -LiteralPath (Join-Path $destination 'apk-badging.txt') |
| if ($badging -notmatch "package: name='rust\.dinovision_xr'" -or |
| $badging -notmatch "launchable-activity: name='android\.app\.NativeActivity'") { |
| throw 'XR APK package/activity does not match the device harness defaults' |
| } |
|
|
| $cargoApk = (Get-Command cargo-apk).Source |
| $record = [ordered]@{ |
| schema_version = 1 |
| completed_utc = [DateTime]::UtcNow.ToString('o') |
| target = 'aarch64-linux-android' |
| android_api = 34 |
| android_sdk = $sdk |
| ndk = $ndk |
| ndk_source_properties = $sourceProperties.Trim() |
| rustc = (& rustc --version --verbose) -join "`n" |
| cargo = (& $cargo --version --verbose) -join "`n" |
| cargo_apk_sha256 = (Get-FileHash -LiteralPath $cargoApk -Algorithm SHA256).Hash.ToLowerInvariant() |
| package = 'rust.dinovision_xr' |
| activity = 'android.app.NativeActivity' |
| artifacts = $artifacts |
| } |
| $record | ConvertTo-Json -Depth 6 | |
| Set-Content -LiteralPath (Join-Path $destination 'android-build.json') -Encoding utf8 |
| Write-Output "wrote checked Android artifacts to $destination" |
|
|