[CmdletBinding()] param() $ErrorActionPreference = "Stop" function Get-AndroidSdkRoot { $candidates = @( $env:ANDROID_SDK_ROOT, $env:ANDROID_HOME, (Join-Path $env:LOCALAPPDATA "Android\Sdk") ) foreach ($candidate in $candidates) { if ($candidate -and (Test-Path -LiteralPath $candidate -PathType Container)) { return (Resolve-Path -LiteralPath $candidate).Path } } throw "Android SDK root was not found. Install command-line tools or set ANDROID_SDK_ROOT." } function Get-LatestAndroidSdkPackageDirectory { param( [Parameter(Mandatory=$true)][string]$SdkRoot, [Parameter(Mandatory=$true)][string]$PackageName ) $packageRoot = Join-Path $SdkRoot $PackageName if (!(Test-Path -LiteralPath $packageRoot -PathType Container)) { throw "Android SDK package directory not found: $packageRoot" } $packages = @(Get-ChildItem -LiteralPath $packageRoot -Directory | Where-Object { $_.Name -match '^\d+(\.\d+)*$' } | Sort-Object { [version]$_.Name } -Descending) if ($packages.Count -eq 0) { throw "No installed Android SDK package versions found under $packageRoot" } return $packages[0] } function Get-AndroidSdkManagerCommand { param([Parameter(Mandatory=$true)][string]$SdkRoot) $candidates = @( (Join-Path $SdkRoot "cmdline-tools\latest\bin\sdkmanager.bat"), (Join-Path $SdkRoot "cmdline-tools\latest\bin\sdkmanager.exe"), (Join-Path $SdkRoot "tools\bin\sdkmanager.bat"), (Join-Path $SdkRoot "tools\bin\sdkmanager.exe") ) $cmdlineToolsRoot = Join-Path $SdkRoot "cmdline-tools" if (Test-Path -LiteralPath $cmdlineToolsRoot -PathType Container) { $toolVersions = @(Get-ChildItem -LiteralPath $cmdlineToolsRoot -Directory | Where-Object { $_.Name -ne "latest" } | Sort-Object { try { [version]$_.Name } catch { [version]"0.0" } } -Descending) foreach ($toolVersion in $toolVersions) { $candidates += (Join-Path $toolVersion.FullName "bin\sdkmanager.bat") $candidates += (Join-Path $toolVersion.FullName "bin\sdkmanager.exe") } } foreach ($candidate in $candidates) { if ($candidate -and (Test-Path -LiteralPath $candidate -PathType Leaf)) { return (Resolve-Path -LiteralPath $candidate).Path } } $pathCommand = Get-Command "sdkmanager" -ErrorAction SilentlyContinue if ($pathCommand) { return $pathCommand.Source } return $null } function Get-LatestAvailableAndroidSdkPackageVersion { param( [Parameter(Mandatory=$true)][string]$SdkRoot, [Parameter(Mandatory=$true)][string]$SdkManagerCommand, [Parameter(Mandatory=$true)][string]$PackageName ) $output = @(& $SdkManagerCommand "--sdk_root=$SdkRoot" "--list" 2>&1) $exitCode = $LASTEXITCODE if ($exitCode -ne 0) { throw "sdkmanager --list failed while checking $PackageName packages: $($output -join [Environment]::NewLine)" } $versions = @() $pattern = "^\s*$([regex]::Escape($PackageName));([0-9]+(?:\.[0-9]+)*)\s*\|" foreach ($line in $output) { $text = $line.ToString() if ($text -match $pattern) { $versions += $Matches[1] } } if ($versions.Count -eq 0) { return $null } return @($versions | Sort-Object { [version]$_ } -Descending | Select-Object -First 1)[0] } function Install-AndroidSdkPackage { param( [Parameter(Mandatory=$true)][string]$SdkRoot, [Parameter(Mandatory=$true)][string]$SdkManagerCommand, [Parameter(Mandatory=$true)][string]$PackageId ) $licenseInput = ("y`n" * 100) $output = @($licenseInput | & $SdkManagerCommand "--sdk_root=$SdkRoot" "--install" $PackageId 2>&1) $exitCode = $LASTEXITCODE if ($exitCode -ne 0) { throw "sdkmanager failed to install $PackageId with exit code ${exitCode}: $($output -join [Environment]::NewLine)" } } function Ensure-LatestAndroidSdkPackageDirectory { param( [Parameter(Mandatory=$true)][string]$SdkRoot, [Parameter(Mandatory=$true)][string]$PackageName, [string]$SdkManagerCommand ) $installedBefore = $null try { $installedBefore = Get-LatestAndroidSdkPackageDirectory -SdkRoot $SdkRoot -PackageName $PackageName } catch { $installedBefore = $null } $availableVersion = $null $action = "using-installed" if ($SdkManagerCommand) { $availableVersion = Get-LatestAvailableAndroidSdkPackageVersion ` -SdkRoot $SdkRoot ` -SdkManagerCommand $SdkManagerCommand ` -PackageName $PackageName $installedVersion = if ($installedBefore) { [version]$installedBefore.Name } else { $null } $availableParsed = if ($availableVersion) { [version]$availableVersion } else { $null } if ($availableParsed -and (!$installedVersion -or $availableParsed -gt $installedVersion)) { Install-AndroidSdkPackage ` -SdkRoot $SdkRoot ` -SdkManagerCommand $SdkManagerCommand ` -PackageId "$PackageName;$availableVersion" $action = "installed-latest-available" } elseif ($availableParsed) { $action = "already-latest-available" } else { $action = "available-version-not-listed" } } elseif (!$installedBefore) { throw "No installed Android SDK package versions found under $(Join-Path $SdkRoot $PackageName), and sdkmanager was not found." } $selected = Get-LatestAndroidSdkPackageDirectory -SdkRoot $SdkRoot -PackageName $PackageName return [ordered]@{ directory = $selected update = [ordered]@{ package = $PackageName installedVersionBefore = if ($installedBefore) { $installedBefore.Name } else { $null } availableVersion = $availableVersion selectedVersion = $selected.Name action = $action } } } function Set-AndroidSdkToolchainEnvironment { $sdkRoot = Get-AndroidSdkRoot $sdkManagerCommand = Get-AndroidSdkManagerCommand -SdkRoot $sdkRoot $ndkSelection = Ensure-LatestAndroidSdkPackageDirectory ` -SdkRoot $sdkRoot ` -PackageName "ndk" ` -SdkManagerCommand $sdkManagerCommand $cmakeSelection = Ensure-LatestAndroidSdkPackageDirectory ` -SdkRoot $sdkRoot ` -PackageName "cmake" ` -SdkManagerCommand $sdkManagerCommand $ndk = $ndkSelection.directory $cmake = $cmakeSelection.directory $cmakeCommand = Join-Path $cmake.FullName "bin\cmake.exe" if (!(Test-Path -LiteralPath $cmakeCommand -PathType Leaf)) { throw "Android SDK CMake executable not found: $cmakeCommand" } $env:ANDROID_HOME = $sdkRoot $env:ANDROID_SDK_ROOT = $sdkRoot $env:ANDROID_NDK_HOME = $ndk.FullName $env:ANDROID_NDK_ROOT = $ndk.FullName return [ordered]@{ sdkRoot = $sdkRoot sdkManagerCommand = $sdkManagerCommand packageUpdates = @($ndkSelection.update, $cmakeSelection.update) ndkVersion = $ndk.Name ndkPath = $ndk.FullName cmakeVersion = $cmake.Name cmakeCommand = $cmakeCommand } }