diff --git a/images/macos/helpers/Xcode.Helpers.psm1 b/images/macos/helpers/Xcode.Helpers.psm1 index 46e5330c1..e6411ce0c 100644 --- a/images/macos/helpers/Xcode.Helpers.psm1 +++ b/images/macos/helpers/Xcode.Helpers.psm1 @@ -151,4 +151,158 @@ function Invoke-XCVersion { if ($result.ExitCode -ne 0) { throw "Command [$Command] has finished with non-zero exit code." } +} + +function Get-BrokenXcodeSimulatorsList { + return @( + # tvOS Simulators + @{ + SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-15-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-15-2"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-15-4"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple TV 4K (at 1080p) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-2nd-generation-1080p"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.tvOS-16-0"; + XcodeVersion = "14.2" + }, + # watchOS-8-0 Simulators + @{ + SimulatorName = "Apple Watch Series 5 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 5 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 41mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-41mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 45mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-45mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-0"; + XcodeVersion = "13.1" + }, + # watchOS-8-3 Simulators + @{ + SimulatorName = "Apple Watch Series 5 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 5 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 41mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-41mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 45mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-45mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-3"; + XcodeVersion = "13.2.1" + }, + # watchOS-8-5 Simulators + @{ + SimulatorName = "Apple Watch Series 5 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 5 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 40mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-40mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 6 - 44mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-6-44mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 41mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-41mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + @{ + SimulatorName = "Apple Watch Series 7 - 45mm" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-7-45mm"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-8-5"; + XcodeVersion = "13.4.1" + }, + # watchOS-9-0 Simulators + @{ + SimulatorName = "Apple Watch SE (40mm) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-SE-40mm-2nd-generation"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-9-0"; + XcodeVersion = "14.2" + }, + @{ + SimulatorName = "Apple Watch SE (44mm) (2nd generation)" + DeviceId = "com.apple.CoreSimulator.SimDeviceType.Apple-Watch-SE-44mm-2nd-generation"; + RuntimeId = "com.apple.CoreSimulator.SimRuntime.watchOS-9-0"; + XcodeVersion = "14.2" + } + ) } \ No newline at end of file diff --git a/images/macos/helpers/Xcode.Installer.psm1 b/images/macos/helpers/Xcode.Installer.psm1 index a08ee3b56..5f1812e75 100644 --- a/images/macos/helpers/Xcode.Installer.psm1 +++ b/images/macos/helpers/Xcode.Installer.psm1 @@ -160,6 +160,22 @@ function Invoke-XcodeRunFirstLaunch { Invoke-ValidateCommand "sudo $xcodeRootPath -runFirstLaunch" } +function Install-AdditionalSimulatorRuntimes { + param( + [Parameter(Mandatory)] + [string]$Version + ) + + if (-not $Version.StartsWith("14.")) { + # Additional simulator runtimes are included by default for Xcode < 14 + return + } + + Write-Host "Installing Simulator Runtimes for Xcode $($_.link) ..." + $xcodebuildPath = Get-XcodeToolPath -Version $Version -ToolName "xcodebuild" + Invoke-ValidateCommand "$xcodebuildPath -downloadAllPlatforms" +} + function Build-XcodeSymlinks { param( [Parameter(Mandatory)] diff --git a/images/macos/provision/core/fix-xcode-simulators.ps1 b/images/macos/provision/core/fix-xcode-simulators.ps1 new file mode 100644 index 000000000..98b1061e0 --- /dev/null +++ b/images/macos/provision/core/fix-xcode-simulators.ps1 @@ -0,0 +1,51 @@ +$ErrorActionPreference = "Stop" + +Import-Module "$env:HOME/image-generation/helpers/Xcode.Helpers.psm1" -DisableNameChecking +Import-Module "$env:HOME/image-generation/software-report/SoftwareReport.Xcode.psm1" -DisableNameChecking + +function Ensure-SimulatorInstalled { + param( + [Parameter(Mandatory)] + [string]$RuntimeId, + [Parameter(Mandatory)] + [string]$DeviceId, + [Parameter(Mandatory)] + [string]$SimulatorName, + [Parameter(Mandatory)] + [string]$XcodeVersion + ) + + $simctlPath = Get-XcodeToolPath -Version $XcodeVersion -ToolName "simctl" + if (-not (Test-Path $simctlPath)) { + Write-Host "Skip validating simulator '$SimulatorName [$RuntimeId]' because Xcode $XcodeVersion is not installed" + return + } + + $simulatorFullNameDebug = "$SimulatorName [$RuntimeId]" + Write-Host "Checking Xcode simulator '$simulatorFullNameDebug' (Xcode $XcodeVersion)..." + + # Get all available devices + [string]$rawDevicesInfo = Invoke-Expression "$simctlPath list devices --json" + $jsonDevicesInfo = ($rawDevicesInfo | ConvertFrom-Json).devices + + # Checking if simulator already exists + $existingSimulator = $jsonDevicesInfo.$RuntimeId | Where-Object { $_.deviceTypeIdentifier -eq $DeviceId } | Select-Object -First 1 + + if ($null -eq $existingSimulator) { + Write-Host "Simulator '$simulatorFullNameDebug' is missed. Creating it..." + Invoke-Expression "$simctlPath create '$SimulatorName' '$DeviceId' '$RuntimeId'" + } elseif ($existingSimulator.name -ne $SimulatorName) { + Write-Host "Simulator '$simulatorFullNameDebug' is named incorrectly. Renaming it from '$($existingSimulator.name)' to '$SimulatorName'..." + Invoke-Expression "$simctlPath rename '$($existingSimulator.udid)' '$SimulatorName'" + } else { + Write-Host "Simulator '$simulatorFullNameDebug' is installed correctly." + } +} + +# First run doesn't provide full data about devices +Get-XcodeInfoList | Out-Null + +Write-Host "Validating and fixing Xcode simulators..." +Get-BrokenXcodeSimulatorsList | ForEach-Object { + Ensure-SimulatorInstalled -RuntimeId $_.RuntimeId -DeviceId $_.DeviceId -SimulatorName $_.SimulatorName -XcodeVersion $_.XcodeVersion +} \ No newline at end of file diff --git a/images/macos/provision/core/xcode.ps1 b/images/macos/provision/core/xcode.ps1 index 2b0a709dc..d3175f387 100644 --- a/images/macos/provision/core/xcode.ps1 +++ b/images/macos/provision/core/xcode.ps1 @@ -33,8 +33,12 @@ $xcodeVersions | ForEach-Object -ThrottleLimit $threadCount -Parallel { Write-Host "Configuring Xcode versions..." $xcodeVersions | ForEach-Object { + Write-Host "Configuring Xcode $($_.link) ..." + Invoke-XcodeRunFirstLaunch -Version $_.link + Install-AdditionalSimulatorRuntimes -Version $_.link } + Invoke-XcodeRunFirstLaunch -Version $defaultXcode Write-Host "Configuring Xcode symlinks..." @@ -47,16 +51,6 @@ $xcodeVersions | ForEach-Object { } } -$xcodeVersions | ForEach-Object { - if ($_.link.StartsWith("14.")) { - Write-Host "Installing Simulator Runtimes..." - - # tvOS and watchOS simulators are not included by default - $xcodebuildPath = Get-XcodeToolPath -Version $_.link -ToolName "xcodebuild" - Invoke-ValidateCommand "$xcodebuildPath -downloadAllPlatforms" - } -} - Write-Host "Rebuilding Launch Services database ..." $xcodeVersions | ForEach-Object { Rebuild-XcodeLaunchServicesDb -Version $_.link diff --git a/images/macos/software-report/SoftwareReport.Xcode.psm1 b/images/macos/software-report/SoftwareReport.Xcode.psm1 index f1ae3e74c..f3032df51 100644 --- a/images/macos/software-report/SoftwareReport.Xcode.psm1 +++ b/images/macos/software-report/SoftwareReport.Xcode.psm1 @@ -171,8 +171,6 @@ function Format-XcodeSimulatorName { ) $formattedDeviceName = $Device.Replace("ʀ", "R") - # Convert "Apple Watch Series 5 (44mm)" -> "Apple Watch Series 5 - 44mm" to make all Xcode versions consistent - $formattedDeviceName = $formattedDeviceName -replace '\((\d+mm)\)', '- $1' return $formattedDeviceName } diff --git a/images/macos/templates/macOS-12.json b/images/macos/templates/macOS-12.json index 0ca2661aa..52954340a 100644 --- a/images/macos/templates/macOS-12.json +++ b/images/macos/templates/macOS-12.json @@ -235,6 +235,11 @@ "./provision/core/delete-duplicate-sims.rb" ] }, + { + "type": "shell", + "execute_command": "chmod +x {{ .Path }}; {{ .Vars }} pwsh -f {{ .Path }}", + "script": "./provision/core/fix-xcode-simulators.ps1" + }, { "type": "shell", "inline": [ diff --git a/images/macos/tests/Xcode.Tests.ps1 b/images/macos/tests/Xcode.Tests.ps1 index 411eb041e..8c5973e08 100644 --- a/images/macos/tests/Xcode.Tests.ps1 +++ b/images/macos/tests/Xcode.Tests.ps1 @@ -111,4 +111,21 @@ Describe "Xcode simulators" { $defaultXcode = Get-ToolsetValue "xcode.default" Switch-Xcode -Version $defaultXcode } +} + +Describe "Xcode Simulators Naming" -Skip:(-not $os.IsMonterey) { + $testCases = Get-BrokenXcodeSimulatorsList + It "Simulator ' []'" -TestCases $testCases { + $simctlPath = Get-XcodeToolPath -Version $XcodeVersion -ToolName "simctl" + [string]$rawDevicesInfo = Invoke-Expression "$simctlPath list devices --json" + $jsonDevicesInfo = ($rawDevicesInfo | ConvertFrom-Json).devices + + $foundSimulators = $jsonDevicesInfo.$RuntimeId | Where-Object { $_.deviceTypeIdentifier -eq $DeviceId } + $foundSimulators | Should -HaveCount 1 + $foundSimulators[0].name | Should -Be $SimulatorName + + $foundSimulators = $jsonDevicesInfo.$RuntimeId | Where-Object { $_.name -eq $SimulatorName } + $foundSimulators | Should -HaveCount 1 + $foundSimulators[0].deviceTypeIdentifier | Should -Be $DeviceId + } } \ No newline at end of file diff --git a/images/macos/toolsets/toolset-12.json b/images/macos/toolsets/toolset-12.json index d5d025c42..72c1ed9a1 100644 --- a/images/macos/toolsets/toolset-12.json +++ b/images/macos/toolsets/toolset-12.json @@ -2,8 +2,8 @@ "xcode": { "default": "14.2", "versions": [ - {"link": "14.2", "version": "14.2.0"}, - { "link": "14.1", "version": "14.1.0"}, + { "link": "14.2", "version": "14.2.0" }, + { "link": "14.1", "version": "14.1.0" }, { "link": "14.0.1", "version": "14.0.1", "symlinks": ["14.0"] }, { "link": "13.4.1", "version": "13.4.1", "symlinks": ["13.4"] }, { "link": "13.3.1", "version": "13.3.1", "symlinks": ["13.3"] },