From c2b375d9f60bf8102955bb9a264099c98037dee6 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 8 Jul 2026 11:02:46 +0200 Subject: [PATCH 01/10] [ci] Add MAUI full R2R android-arm Helix test Build the MAUI template with CoreCLR full ReadyToRun for android-arm, disable MAUI partial R2R with _MauiPublishReadyToRunPartial=false, and run the APK on the windows.11.amd64.android-armel.open Helix queue. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../automation/azure-pipelines-public.yaml | 7 + build-tools/automation/azure-pipelines.yaml | 7 + .../run-maui-full-r2r-android-arm-helix.yaml | 155 ++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml diff --git a/build-tools/automation/azure-pipelines-public.yaml b/build-tools/automation/azure-pipelines-public.yaml index ef6e0a58408..b7197ff3796 100644 --- a/build-tools/automation/azure-pipelines-public.yaml +++ b/build-tools/automation/azure-pipelines-public.yaml @@ -591,6 +591,13 @@ stages: xaSourcePath: $(Build.SourcesDirectory)/android displayName: Build MAUI template - Release + - template: /build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml + parameters: + project: $(Build.StagingDirectory)/MauiTestProj/MauiTestProj.csproj + xaSourcePath: $(Build.SourcesDirectory)/android + logsDirectory: $(Build.StagingDirectory)/logs + extraBuildArguments: --configfile $(Build.SourcesDirectory)/maui/NuGet.config + - task: CopyFiles@2 displayName: copy build logs condition: always() diff --git a/build-tools/automation/azure-pipelines.yaml b/build-tools/automation/azure-pipelines.yaml index d7406c1f7c0..a98c585ce87 100644 --- a/build-tools/automation/azure-pipelines.yaml +++ b/build-tools/automation/azure-pipelines.yaml @@ -224,6 +224,13 @@ extends: xaSourcePath: $(Build.SourcesDirectory)/android displayName: Build MAUI template - Release + - template: /build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml@self + parameters: + project: $(Build.StagingDirectory)/MauiTestProj/MauiTestProj.csproj + xaSourcePath: $(Build.SourcesDirectory)/android + logsDirectory: $(Build.StagingDirectory)/logs + extraBuildArguments: --configfile $(Build.SourcesDirectory)/maui/NuGet.config + - task: CopyFiles@2 displayName: copy build logs condition: always() diff --git a/build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml b/build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml new file mode 100644 index 00000000000..8ee674371a1 --- /dev/null +++ b/build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml @@ -0,0 +1,155 @@ +parameters: + project: + xaSourcePath: + logsDirectory: + payloadDirectory: $(Build.StagingDirectory)/MauiFullR2RHelix + packageName: com.xamarin.mauifullr2r + extraBuildArguments: '' + +steps: +- powershell: | + $project = '${{ parameters.project }}' + [xml] $xml = Get-Content $project + + function Set-Property([string] $name, [string] $value) { + $node = $xml.SelectSingleNode("/Project/PropertyGroup/$name") + if (-not $node) { + $propertyGroup = $xml.SelectSingleNode('/Project/PropertyGroup') + if (-not $propertyGroup) { + $propertyGroup = $xml.CreateElement('PropertyGroup') + $xml.Project.AppendChild($propertyGroup) | Out-Null + } + $node = $xml.CreateElement($name) + $propertyGroup.AppendChild($node) | Out-Null + } + $node.InnerText = $value + } + + Set-Property 'ApplicationId' '${{ parameters.packageName }}' + Set-Property 'RuntimeIdentifier' 'android-arm' + Set-Property 'UseMonoRuntime' 'false' + Set-Property 'PublishReadyToRun' 'true' + Set-Property 'PublishReadyToRunComposite' 'true' + Set-Property '_MauiPublishReadyToRunPartial' 'false' + Set-Property 'AndroidPackageFormat' 'apk' + + # Assert that $(_MauiPublishReadyToRunPartial)=false disables MAUI's partial + # ReadyToRun inputs, so the test exercises full R2R coverage. + @( + 'DisablePartialReadyToRunForFullR2RTest', + 'AssertFullReadyToRunForFullR2RTest' + ) | ForEach-Object { + $existing = $xml.SelectSingleNode("/Project/Target[@Name='$_']") + if ($existing) { + $existing.ParentNode.RemoveChild($existing) | Out-Null + } + } + + $assertTarget = $xml.CreateElement('Target') + $assertTarget.SetAttribute('Name', 'AssertFullReadyToRunForFullR2RTest') + $assertTarget.SetAttribute('BeforeTargets', '_CreateR2RImages') + + $assertItemGroup = $xml.CreateElement('ItemGroup') + $partialItem = $xml.CreateElement('_PartialReadyToRunCompileList') + $partialItem.SetAttribute('Include', '@(_ReadyToRunCompileList)') + $partialItem.SetAttribute('Condition', "'%(_ReadyToRunCompileList.PartialCompile)' == 'true'") + $assertItemGroup.AppendChild($partialItem) | Out-Null + $assertTarget.AppendChild($assertItemGroup) | Out-Null + + $error = $xml.CreateElement('Error') + $error.SetAttribute('Condition', "'@(_PartialReadyToRunCompileList)' != ''") + $error.SetAttribute('Text', "Full ReadyToRun test expected no partial ReadyToRun inputs with `$(_MauiPublishReadyToRunPartial)=false`, but found '@(_PartialReadyToRunCompileList)'.") + $assertTarget.AppendChild($error) | Out-Null + $compositeRootsError = $xml.CreateElement('Error') + $compositeRootsError.SetAttribute('Condition', "'@(_ReadyToRunCompositeUnrootedBuildInput)' != ''") + $compositeRootsError.SetAttribute('Text', "Full ReadyToRun test expected no unrooted composite ReadyToRun inputs with `$(_MauiPublishReadyToRunPartial)=false`, but found '@(_ReadyToRunCompositeUnrootedBuildInput)'.") + $assertTarget.AppendChild($compositeRootsError) | Out-Null + $xml.Project.AppendChild($assertTarget) | Out-Null + + $xml.Save($project) + displayName: Configure MAUI full R2R android-arm test + +- template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + project: ${{ parameters.project }} + arguments: >- + -f $(DotNetTargetFramework)-android -c Release + -p:RuntimeIdentifier=android-arm + -p:UseMonoRuntime=false + -p:PublishReadyToRun=true + -p:PublishReadyToRunComposite=true + -p:_MauiPublishReadyToRunPartial=false + -p:AndroidPackageFormat=apk + ${{ parameters.extraBuildArguments }} + -bl:${{ parameters.logsDirectory }}/MauiTestProj-FullR2R-android-arm.binlog + xaSourcePath: ${{ parameters.xaSourcePath }} + displayName: Build MAUI template - Full R2R android-arm + continueOnError: false + +- powershell: | + $project = '${{ parameters.project }}' + $projectDirectory = Split-Path -Parent $project + $payloadDirectory = '${{ parameters.payloadDirectory }}' + $packageName = '${{ parameters.packageName }}' + + New-Item -ItemType Directory -Force -Path $payloadDirectory | Out-Null + $apk = Get-ChildItem $projectDirectory -Recurse -Filter '*-Signed.apk' | + Where-Object { $_.FullName -like '*\bin\Release\*android-arm*' } | + Sort-Object LastWriteTime -Descending | + Select-Object -First 1 + if (-not $apk) { + throw "Could not find the android-arm signed APK under $projectDirectory" + } + + Copy-Item -LiteralPath $apk.FullName -Destination (Join-Path $payloadDirectory 'MauiFullR2R-Signed.apk') -Force + + $script = @" + @echo on + setlocal + set "UPLOAD_DIR=%HELIX_WORKITEM_UPLOAD_ROOT%" + if "%UPLOAD_DIR%"=="" set "UPLOAD_DIR=%~dp0" + if not exist "%UPLOAD_DIR%" mkdir "%UPLOAD_DIR%" + where adb + adb devices -l + adb wait-for-device + adb uninstall $packageName + adb install -r "%~dp0MauiFullR2R-Signed.apk" || exit /b 1 + adb shell logcat -c + adb shell monkey -p $packageName -c android.intent.category.LAUNCHER 1 || exit /b 1 + ping -n 11 127.0.0.1 > nul + adb shell pidof $packageName + if errorlevel 1 ( + adb logcat -d > "%UPLOAD_DIR%\logcat.log" + adb uninstall $packageName + exit /b 1 + ) + adb logcat -d > "%UPLOAD_DIR%\logcat.log" + adb uninstall $packageName + exit /b 0 + "@ + + Set-Content -LiteralPath (Join-Path $payloadDirectory 'run-maui-full-r2r.cmd') -Value $script -Encoding ASCII + displayName: Prepare MAUI full R2R Helix payload + +- powershell: > + & "${{ parameters.xaSourcePath }}/eng/common/msbuild.ps1" + "${{ parameters.xaSourcePath }}/eng/common/helixpublish.proj" + /restore + /p:TreatWarningsAsErrors=false + /t:Test + /bl:${{ parameters.logsDirectory }}/SendMauiFullR2RToHelix.binlog + displayName: Run MAUI full R2R android-arm APK on Helix + env: + BuildConfig: $(XA.Build.Configuration) + HelixSource: pr/dotnet/android + HelixType: tests/android/maui-full-r2r/ + HelixBuild: $(Build.BuildNumber) + HelixConfiguration: android-arm + HelixTargetQueues: windows.11.amd64.android-armel.open + HelixAccessToken: '' + WorkItemDirectory: ${{ parameters.payloadDirectory }} + WorkItemCommand: run-maui-full-r2r.cmd + WorkItemTimeout: 00:10:00 + WaitForWorkItemCompletion: true + Creator: dotnet-android + SYSTEM_ACCESSTOKEN: $(System.AccessToken) From 6286c10620b9c4637315e4ba8717d45fa86c70f1 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 8 Jul 2026 13:09:47 +0200 Subject: [PATCH 02/10] [ci] Expand MAUI R2R Helix coverage matrix Run MAUI CoreCLR R2R coverage across android-arm64 and android-arm, with default MAUI R2R settings and _MauiPublishReadyToRunPartial=false full-R2R settings. Rename the helper template and avoid the PowerShell read-only Error variable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../automation/azure-pipelines-public.yaml | 2 +- build-tools/automation/azure-pipelines.yaml | 2 +- .../run-maui-full-r2r-android-arm-helix.yaml | 155 ------------- .../run-maui-r2r-helix-matrix.yaml | 217 ++++++++++++++++++ 4 files changed, 219 insertions(+), 157 deletions(-) delete mode 100644 build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml create mode 100644 build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml diff --git a/build-tools/automation/azure-pipelines-public.yaml b/build-tools/automation/azure-pipelines-public.yaml index b7197ff3796..a495cb94da9 100644 --- a/build-tools/automation/azure-pipelines-public.yaml +++ b/build-tools/automation/azure-pipelines-public.yaml @@ -591,7 +591,7 @@ stages: xaSourcePath: $(Build.SourcesDirectory)/android displayName: Build MAUI template - Release - - template: /build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml + - template: /build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml parameters: project: $(Build.StagingDirectory)/MauiTestProj/MauiTestProj.csproj xaSourcePath: $(Build.SourcesDirectory)/android diff --git a/build-tools/automation/azure-pipelines.yaml b/build-tools/automation/azure-pipelines.yaml index a98c585ce87..6f13ed20793 100644 --- a/build-tools/automation/azure-pipelines.yaml +++ b/build-tools/automation/azure-pipelines.yaml @@ -224,7 +224,7 @@ extends: xaSourcePath: $(Build.SourcesDirectory)/android displayName: Build MAUI template - Release - - template: /build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml@self + - template: /build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml@self parameters: project: $(Build.StagingDirectory)/MauiTestProj/MauiTestProj.csproj xaSourcePath: $(Build.SourcesDirectory)/android diff --git a/build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml b/build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml deleted file mode 100644 index 8ee674371a1..00000000000 --- a/build-tools/automation/yaml-templates/run-maui-full-r2r-android-arm-helix.yaml +++ /dev/null @@ -1,155 +0,0 @@ -parameters: - project: - xaSourcePath: - logsDirectory: - payloadDirectory: $(Build.StagingDirectory)/MauiFullR2RHelix - packageName: com.xamarin.mauifullr2r - extraBuildArguments: '' - -steps: -- powershell: | - $project = '${{ parameters.project }}' - [xml] $xml = Get-Content $project - - function Set-Property([string] $name, [string] $value) { - $node = $xml.SelectSingleNode("/Project/PropertyGroup/$name") - if (-not $node) { - $propertyGroup = $xml.SelectSingleNode('/Project/PropertyGroup') - if (-not $propertyGroup) { - $propertyGroup = $xml.CreateElement('PropertyGroup') - $xml.Project.AppendChild($propertyGroup) | Out-Null - } - $node = $xml.CreateElement($name) - $propertyGroup.AppendChild($node) | Out-Null - } - $node.InnerText = $value - } - - Set-Property 'ApplicationId' '${{ parameters.packageName }}' - Set-Property 'RuntimeIdentifier' 'android-arm' - Set-Property 'UseMonoRuntime' 'false' - Set-Property 'PublishReadyToRun' 'true' - Set-Property 'PublishReadyToRunComposite' 'true' - Set-Property '_MauiPublishReadyToRunPartial' 'false' - Set-Property 'AndroidPackageFormat' 'apk' - - # Assert that $(_MauiPublishReadyToRunPartial)=false disables MAUI's partial - # ReadyToRun inputs, so the test exercises full R2R coverage. - @( - 'DisablePartialReadyToRunForFullR2RTest', - 'AssertFullReadyToRunForFullR2RTest' - ) | ForEach-Object { - $existing = $xml.SelectSingleNode("/Project/Target[@Name='$_']") - if ($existing) { - $existing.ParentNode.RemoveChild($existing) | Out-Null - } - } - - $assertTarget = $xml.CreateElement('Target') - $assertTarget.SetAttribute('Name', 'AssertFullReadyToRunForFullR2RTest') - $assertTarget.SetAttribute('BeforeTargets', '_CreateR2RImages') - - $assertItemGroup = $xml.CreateElement('ItemGroup') - $partialItem = $xml.CreateElement('_PartialReadyToRunCompileList') - $partialItem.SetAttribute('Include', '@(_ReadyToRunCompileList)') - $partialItem.SetAttribute('Condition', "'%(_ReadyToRunCompileList.PartialCompile)' == 'true'") - $assertItemGroup.AppendChild($partialItem) | Out-Null - $assertTarget.AppendChild($assertItemGroup) | Out-Null - - $error = $xml.CreateElement('Error') - $error.SetAttribute('Condition', "'@(_PartialReadyToRunCompileList)' != ''") - $error.SetAttribute('Text', "Full ReadyToRun test expected no partial ReadyToRun inputs with `$(_MauiPublishReadyToRunPartial)=false`, but found '@(_PartialReadyToRunCompileList)'.") - $assertTarget.AppendChild($error) | Out-Null - $compositeRootsError = $xml.CreateElement('Error') - $compositeRootsError.SetAttribute('Condition', "'@(_ReadyToRunCompositeUnrootedBuildInput)' != ''") - $compositeRootsError.SetAttribute('Text', "Full ReadyToRun test expected no unrooted composite ReadyToRun inputs with `$(_MauiPublishReadyToRunPartial)=false`, but found '@(_ReadyToRunCompositeUnrootedBuildInput)'.") - $assertTarget.AppendChild($compositeRootsError) | Out-Null - $xml.Project.AppendChild($assertTarget) | Out-Null - - $xml.Save($project) - displayName: Configure MAUI full R2R android-arm test - -- template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml - parameters: - project: ${{ parameters.project }} - arguments: >- - -f $(DotNetTargetFramework)-android -c Release - -p:RuntimeIdentifier=android-arm - -p:UseMonoRuntime=false - -p:PublishReadyToRun=true - -p:PublishReadyToRunComposite=true - -p:_MauiPublishReadyToRunPartial=false - -p:AndroidPackageFormat=apk - ${{ parameters.extraBuildArguments }} - -bl:${{ parameters.logsDirectory }}/MauiTestProj-FullR2R-android-arm.binlog - xaSourcePath: ${{ parameters.xaSourcePath }} - displayName: Build MAUI template - Full R2R android-arm - continueOnError: false - -- powershell: | - $project = '${{ parameters.project }}' - $projectDirectory = Split-Path -Parent $project - $payloadDirectory = '${{ parameters.payloadDirectory }}' - $packageName = '${{ parameters.packageName }}' - - New-Item -ItemType Directory -Force -Path $payloadDirectory | Out-Null - $apk = Get-ChildItem $projectDirectory -Recurse -Filter '*-Signed.apk' | - Where-Object { $_.FullName -like '*\bin\Release\*android-arm*' } | - Sort-Object LastWriteTime -Descending | - Select-Object -First 1 - if (-not $apk) { - throw "Could not find the android-arm signed APK under $projectDirectory" - } - - Copy-Item -LiteralPath $apk.FullName -Destination (Join-Path $payloadDirectory 'MauiFullR2R-Signed.apk') -Force - - $script = @" - @echo on - setlocal - set "UPLOAD_DIR=%HELIX_WORKITEM_UPLOAD_ROOT%" - if "%UPLOAD_DIR%"=="" set "UPLOAD_DIR=%~dp0" - if not exist "%UPLOAD_DIR%" mkdir "%UPLOAD_DIR%" - where adb - adb devices -l - adb wait-for-device - adb uninstall $packageName - adb install -r "%~dp0MauiFullR2R-Signed.apk" || exit /b 1 - adb shell logcat -c - adb shell monkey -p $packageName -c android.intent.category.LAUNCHER 1 || exit /b 1 - ping -n 11 127.0.0.1 > nul - adb shell pidof $packageName - if errorlevel 1 ( - adb logcat -d > "%UPLOAD_DIR%\logcat.log" - adb uninstall $packageName - exit /b 1 - ) - adb logcat -d > "%UPLOAD_DIR%\logcat.log" - adb uninstall $packageName - exit /b 0 - "@ - - Set-Content -LiteralPath (Join-Path $payloadDirectory 'run-maui-full-r2r.cmd') -Value $script -Encoding ASCII - displayName: Prepare MAUI full R2R Helix payload - -- powershell: > - & "${{ parameters.xaSourcePath }}/eng/common/msbuild.ps1" - "${{ parameters.xaSourcePath }}/eng/common/helixpublish.proj" - /restore - /p:TreatWarningsAsErrors=false - /t:Test - /bl:${{ parameters.logsDirectory }}/SendMauiFullR2RToHelix.binlog - displayName: Run MAUI full R2R android-arm APK on Helix - env: - BuildConfig: $(XA.Build.Configuration) - HelixSource: pr/dotnet/android - HelixType: tests/android/maui-full-r2r/ - HelixBuild: $(Build.BuildNumber) - HelixConfiguration: android-arm - HelixTargetQueues: windows.11.amd64.android-armel.open - HelixAccessToken: '' - WorkItemDirectory: ${{ parameters.payloadDirectory }} - WorkItemCommand: run-maui-full-r2r.cmd - WorkItemTimeout: 00:10:00 - WaitForWorkItemCompletion: true - Creator: dotnet-android - SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml new file mode 100644 index 00000000000..de70065b2f7 --- /dev/null +++ b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml @@ -0,0 +1,217 @@ +parameters: + project: + xaSourcePath: + logsDirectory: + matrixDirectory: $(Build.StagingDirectory)/MauiR2RMatrix + payloadDirectory: $(Build.StagingDirectory)/MauiR2RHelix + extraBuildArguments: '' + testCases: + - name: android-arm64-default + runtimeIdentifier: android-arm64 + packageName: com.xamarin.mauir2r.arm64.default + helixQueue: windows.11.amd64.android.open + displayName: android-arm64 default R2R + - name: android-arm64-fullr2r + runtimeIdentifier: android-arm64 + packageName: com.xamarin.mauir2r.arm64.fullr2r + helixQueue: windows.11.amd64.android.open + displayName: android-arm64 full R2R + - name: android-arm-default + runtimeIdentifier: android-arm + packageName: com.xamarin.mauir2r.arm.default + helixQueue: windows.11.amd64.android-armel.open + displayName: android-arm default R2R + - name: android-arm-fullr2r + runtimeIdentifier: android-arm + packageName: com.xamarin.mauir2r.arm.fullr2r + helixQueue: windows.11.amd64.android-armel.open + displayName: android-arm full R2R + +steps: +- powershell: | + $sourceProject = '${{ parameters.project }}' + $sourceDirectory = Split-Path -Parent $sourceProject + $projectFileName = Split-Path -Leaf $sourceProject + $matrixDirectory = '${{ parameters.matrixDirectory }}' + $cases = @( + @{ + Name = 'android-arm64-default' + RuntimeIdentifier = 'android-arm64' + ApplicationId = 'com.xamarin.mauir2r.arm64.default' + DisablePartialReadyToRun = $false + }, + @{ + Name = 'android-arm64-fullr2r' + RuntimeIdentifier = 'android-arm64' + ApplicationId = 'com.xamarin.mauir2r.arm64.fullr2r' + DisablePartialReadyToRun = $true + }, + @{ + Name = 'android-arm-default' + RuntimeIdentifier = 'android-arm' + ApplicationId = 'com.xamarin.mauir2r.arm.default' + DisablePartialReadyToRun = $false + }, + @{ + Name = 'android-arm-fullr2r' + RuntimeIdentifier = 'android-arm' + ApplicationId = 'com.xamarin.mauir2r.arm.fullr2r' + DisablePartialReadyToRun = $true + } + ) + + function Set-Property([xml] $xml, [string] $name, [string] $value) { + $node = $xml.SelectSingleNode("/Project/PropertyGroup/$name") + if (-not $node) { + $propertyGroup = $xml.SelectSingleNode('/Project/PropertyGroup') + if (-not $propertyGroup) { + $propertyGroup = $xml.CreateElement('PropertyGroup') + $xml.Project.AppendChild($propertyGroup) | Out-Null + } + $node = $xml.CreateElement($name) + $propertyGroup.AppendChild($node) | Out-Null + } + $node.InnerText = $value + } + + function Add-FullReadyToRunAssertion([xml] $xml) { + $existing = $xml.SelectSingleNode("/Project/Target[@Name='AssertFullReadyToRunForFullR2RTest']") + if ($existing) { + $existing.ParentNode.RemoveChild($existing) | Out-Null + } + + $assertTarget = $xml.CreateElement('Target') + $assertTarget.SetAttribute('Name', 'AssertFullReadyToRunForFullR2RTest') + $assertTarget.SetAttribute('BeforeTargets', '_CreateR2RImages') + + $assertItemGroup = $xml.CreateElement('ItemGroup') + $partialItem = $xml.CreateElement('_PartialReadyToRunCompileList') + $partialItem.SetAttribute('Include', '@(_ReadyToRunCompileList)') + $partialItem.SetAttribute('Condition', "'%(_ReadyToRunCompileList.PartialCompile)' == 'true'") + $assertItemGroup.AppendChild($partialItem) | Out-Null + $assertTarget.AppendChild($assertItemGroup) | Out-Null + + $partialError = $xml.CreateElement('Error') + $partialError.SetAttribute('Condition', "'@(_PartialReadyToRunCompileList)' != ''") + $partialError.SetAttribute('Text', "Full ReadyToRun test expected no partial ReadyToRun inputs with `$(_MauiPublishReadyToRunPartial)=false`, but found '@(_PartialReadyToRunCompileList)'.") + $assertTarget.AppendChild($partialError) | Out-Null + + $compositeRootsError = $xml.CreateElement('Error') + $compositeRootsError.SetAttribute('Condition', "'@(_ReadyToRunCompositeUnrootedBuildInput)' != ''") + $compositeRootsError.SetAttribute('Text', "Full ReadyToRun test expected no unrooted composite ReadyToRun inputs with `$(_MauiPublishReadyToRunPartial)=false`, but found '@(_ReadyToRunCompositeUnrootedBuildInput)'.") + $assertTarget.AppendChild($compositeRootsError) | Out-Null + + $xml.Project.AppendChild($assertTarget) | Out-Null + } + + New-Item -ItemType Directory -Force -Path $matrixDirectory | Out-Null + + foreach ($case in $cases) { + $caseDirectory = Join-Path $matrixDirectory $case.Name + if (Test-Path $caseDirectory) { + Remove-Item -LiteralPath $caseDirectory -Recurse -Force + } + New-Item -ItemType Directory -Force -Path $caseDirectory | Out-Null + + Get-ChildItem -LiteralPath $sourceDirectory -Force | + Where-Object { $_.Name -notin @('bin', 'obj') } | + Copy-Item -Destination $caseDirectory -Recurse -Force + + $caseProject = Join-Path $caseDirectory $projectFileName + [xml] $xml = Get-Content $caseProject + Set-Property $xml 'ApplicationId' $case.ApplicationId + Set-Property $xml 'RuntimeIdentifier' $case.RuntimeIdentifier + Set-Property $xml 'UseMonoRuntime' 'false' + Set-Property $xml 'PublishReadyToRun' 'true' + Set-Property $xml 'PublishReadyToRunComposite' 'true' + Set-Property $xml 'AndroidPackageFormat' 'apk' + + if ($case.DisablePartialReadyToRun) { + Set-Property $xml '_MauiPublishReadyToRunPartial' 'false' + Add-FullReadyToRunAssertion $xml + } + + $xml.Save($caseProject) + } + displayName: Configure MAUI R2R matrix projects + +- ${{ each testCase in parameters.testCases }}: + - template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + project: ${{ parameters.matrixDirectory }}/${{ testCase.name }}/MauiTestProj.csproj + arguments: >- + -f $(DotNetTargetFramework)-android -c Release + ${{ parameters.extraBuildArguments }} + -bl:${{ parameters.logsDirectory }}/MauiTestProj-${{ testCase.name }}.binlog + xaSourcePath: ${{ parameters.xaSourcePath }} + displayName: Build MAUI template - ${{ testCase.displayName }} + continueOnError: false + + - powershell: | + $projectDirectory = '${{ parameters.matrixDirectory }}/${{ testCase.name }}' + $payloadDirectory = '${{ parameters.payloadDirectory }}/${{ testCase.name }}' + $runtimeIdentifier = '${{ testCase.runtimeIdentifier }}' + $packageName = '${{ testCase.packageName }}' + $apkName = "MauiR2R-${{ testCase.name }}-Signed.apk" + + New-Item -ItemType Directory -Force -Path $payloadDirectory | Out-Null + $apk = Get-ChildItem $projectDirectory -Recurse -Filter '*-Signed.apk' | + Where-Object { $_.FullName -like "*\bin\Release\*$runtimeIdentifier*" } | + Sort-Object LastWriteTime -Descending | + Select-Object -First 1 + if (-not $apk) { + throw "Could not find the $runtimeIdentifier signed APK under $projectDirectory" + } + + Copy-Item -LiteralPath $apk.FullName -Destination (Join-Path $payloadDirectory $apkName) -Force + + $script = @" + @echo on + setlocal + set "UPLOAD_DIR=%HELIX_WORKITEM_UPLOAD_ROOT%" + if "%UPLOAD_DIR%"=="" set "UPLOAD_DIR=%~dp0" + if not exist "%UPLOAD_DIR%" mkdir "%UPLOAD_DIR%" + where adb + adb devices -l + adb wait-for-device + adb uninstall $packageName + adb install -r "%~dp0$apkName" || exit /b 1 + adb shell logcat -c + adb shell monkey -p $packageName -c android.intent.category.LAUNCHER 1 || exit /b 1 + ping -n 11 127.0.0.1 > nul + adb shell pidof $packageName + if errorlevel 1 ( + adb logcat -d > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" + adb uninstall $packageName + exit /b 1 + ) + adb logcat -d > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" + adb uninstall $packageName + exit /b 0 + "@ + + Set-Content -LiteralPath (Join-Path $payloadDirectory 'run-maui-r2r.cmd') -Value $script -Encoding ASCII + displayName: Prepare MAUI R2R Helix payload - ${{ testCase.displayName }} + + - powershell: > + & "${{ parameters.xaSourcePath }}/eng/common/msbuild.ps1" + "${{ parameters.xaSourcePath }}/eng/common/helixpublish.proj" + /restore + /p:TreatWarningsAsErrors=false + /t:Test + /bl:${{ parameters.logsDirectory }}/SendMauiR2R-${{ testCase.name }}ToHelix.binlog + displayName: Run MAUI ${{ testCase.displayName }} APK on Helix + env: + BuildConfig: $(XA.Build.Configuration) + HelixSource: pr/dotnet/android + HelixType: tests/android/maui-r2r/${{ testCase.name }}/ + HelixBuild: $(Build.BuildNumber) + HelixConfiguration: ${{ testCase.name }} + HelixTargetQueues: ${{ testCase.helixQueue }} + HelixAccessToken: '' + WorkItemDirectory: ${{ parameters.payloadDirectory }}/${{ testCase.name }} + WorkItemCommand: run-maui-r2r.cmd + WorkItemTimeout: 00:10:00 + WaitForWorkItemCompletion: true + Creator: dotnet-android + SYSTEM_ACCESSTOKEN: $(System.AccessToken) From 6ec8e2fd683a62eacaf6fd583be3149d9f877907 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 8 Jul 2026 14:53:38 +0200 Subject: [PATCH 03/10] [ci] Fix MAUI R2R matrix package names Avoid Java keyword package segments in the default R2R matrix cases by using baseline package suffixes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../yaml-templates/run-maui-r2r-helix-matrix.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml index de70065b2f7..e4e87b7589f 100644 --- a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml +++ b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml @@ -8,7 +8,7 @@ parameters: testCases: - name: android-arm64-default runtimeIdentifier: android-arm64 - packageName: com.xamarin.mauir2r.arm64.default + packageName: com.xamarin.mauir2r.arm64.baseline helixQueue: windows.11.amd64.android.open displayName: android-arm64 default R2R - name: android-arm64-fullr2r @@ -18,7 +18,7 @@ parameters: displayName: android-arm64 full R2R - name: android-arm-default runtimeIdentifier: android-arm - packageName: com.xamarin.mauir2r.arm.default + packageName: com.xamarin.mauir2r.arm.baseline helixQueue: windows.11.amd64.android-armel.open displayName: android-arm default R2R - name: android-arm-fullr2r @@ -37,7 +37,7 @@ steps: @{ Name = 'android-arm64-default' RuntimeIdentifier = 'android-arm64' - ApplicationId = 'com.xamarin.mauir2r.arm64.default' + ApplicationId = 'com.xamarin.mauir2r.arm64.baseline' DisablePartialReadyToRun = $false }, @{ @@ -49,7 +49,7 @@ steps: @{ Name = 'android-arm-default' RuntimeIdentifier = 'android-arm' - ApplicationId = 'com.xamarin.mauir2r.arm.default' + ApplicationId = 'com.xamarin.mauir2r.arm.baseline' DisablePartialReadyToRun = $false }, @{ From 160ffe97860e9a16085876c0ae83f794b2933d0e Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 8 Jul 2026 16:14:35 +0200 Subject: [PATCH 04/10] [ci] Use repo dotnet for MAUI Helix submission Invoke helixpublish.proj with the prepared repo-local dotnet msbuild instead of eng/common/msbuild.ps1, which requires tools.dotnet in global.json and fails in this pipeline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../run-maui-r2r-helix-matrix.yaml | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml index e4e87b7589f..cc32239c988 100644 --- a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml +++ b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml @@ -193,25 +193,29 @@ steps: Set-Content -LiteralPath (Join-Path $payloadDirectory 'run-maui-r2r.cmd') -Value $script -Encoding ASCII displayName: Prepare MAUI R2R Helix payload - ${{ testCase.displayName }} - - powershell: > - & "${{ parameters.xaSourcePath }}/eng/common/msbuild.ps1" - "${{ parameters.xaSourcePath }}/eng/common/helixpublish.proj" - /restore - /p:TreatWarningsAsErrors=false - /t:Test - /bl:${{ parameters.logsDirectory }}/SendMauiR2R-${{ testCase.name }}ToHelix.binlog - displayName: Run MAUI ${{ testCase.displayName }} APK on Helix - env: - BuildConfig: $(XA.Build.Configuration) - HelixSource: pr/dotnet/android - HelixType: tests/android/maui-r2r/${{ testCase.name }}/ - HelixBuild: $(Build.BuildNumber) - HelixConfiguration: ${{ testCase.name }} - HelixTargetQueues: ${{ testCase.helixQueue }} - HelixAccessToken: '' - WorkItemDirectory: ${{ parameters.payloadDirectory }}/${{ testCase.name }} - WorkItemCommand: run-maui-r2r.cmd - WorkItemTimeout: 00:10:00 - WaitForWorkItemCompletion: true - Creator: dotnet-android - SYSTEM_ACCESSTOKEN: $(System.AccessToken) + - template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + command: msbuild + project: ${{ parameters.xaSourcePath }}/eng/common/helixpublish.proj + arguments: >- + /restore + /p:TreatWarningsAsErrors=false + /t:Test + /bl:${{ parameters.logsDirectory }}/SendMauiR2R-${{ testCase.name }}ToHelix.binlog + xaSourcePath: ${{ parameters.xaSourcePath }} + displayName: Run MAUI ${{ testCase.displayName }} APK on Helix + continueOnError: false + env: + BuildConfig: $(XA.Build.Configuration) + HelixSource: pr/dotnet/android + HelixType: tests/android/maui-r2r/${{ testCase.name }}/ + HelixBuild: $(Build.BuildNumber) + HelixConfiguration: ${{ testCase.name }} + HelixTargetQueues: ${{ testCase.helixQueue }} + HelixAccessToken: '' + WorkItemDirectory: ${{ parameters.payloadDirectory }}/${{ testCase.name }} + WorkItemCommand: run-maui-r2r.cmd + WorkItemTimeout: 00:10:00 + WaitForWorkItemCompletion: true + Creator: dotnet-android + SYSTEM_ACCESSTOKEN: $(System.AccessToken) From f210f0e4c9512d7867b95d89987c7cd703b81400 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 8 Jul 2026 17:48:34 +0200 Subject: [PATCH 05/10] [ci] Pin Helix MSBuild SDK Add Microsoft.DotNet.Helix.Sdk to global.json so dotnet msbuild can resolve eng/common/helixpublish.proj when submitting MAUI R2R APK work items. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- global.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/global.json b/global.json index 04333540172..8dfbc3a4358 100644 --- a/global.json +++ b/global.json @@ -4,6 +4,7 @@ }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.134", - "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26355.102" + "Microsoft.DotNet.Arcade.Sdk": "11.0.0-beta.26355.102", + "Microsoft.DotNet.Helix.Sdk": "11.0.0-beta.26355.102" } } From 6616c3b601d83839d24ab81477f19affa2274ee9 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 9 Jul 2026 00:11:41 +0200 Subject: [PATCH 06/10] [ci] Include adb in MAUI Helix payload Copy Android SDK platform-tools into each MAUI R2R Helix payload and run the payload-local adb.exe instead of assuming adb is on the Helix queue PATH. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../run-maui-r2r-helix-matrix.yaml | 47 ++++++++++++++----- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml index cc32239c988..7115d3f4cf0 100644 --- a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml +++ b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml @@ -165,28 +165,51 @@ steps: Copy-Item -LiteralPath $apk.FullName -Destination (Join-Path $payloadDirectory $apkName) -Force + $sdkCandidates = @( + $env:ANDROID_HOME, + $env:ANDROID_SDK_ROOT, + $env:AndroidSdkDirectory, + $(if ($env:USERPROFILE) { Join-Path $env:USERPROFILE 'android-toolchain\sdk' }), + $(if ($env:HOME) { Join-Path $env:HOME 'android-toolchain\sdk' }) + ) | Where-Object { $_ -and (Test-Path (Join-Path $_ 'platform-tools\adb.exe')) } + $androidSdk = $sdkCandidates | Select-Object -First 1 + if (-not $androidSdk) { + throw "Could not find Android SDK platform-tools. Tried ANDROID_HOME, ANDROID_SDK_ROOT, AndroidSdkDirectory, and user android-toolchain paths." + } + + $platformTools = Join-Path $androidSdk 'platform-tools' + $payloadPlatformTools = Join-Path $payloadDirectory 'platform-tools' + New-Item -ItemType Directory -Force -Path $payloadPlatformTools | Out-Null + Copy-Item -Path (Join-Path $platformTools '*') -Destination $payloadPlatformTools -Recurse -Force + $script = @" @echo on setlocal set "UPLOAD_DIR=%HELIX_WORKITEM_UPLOAD_ROOT%" if "%UPLOAD_DIR%"=="" set "UPLOAD_DIR=%~dp0" if not exist "%UPLOAD_DIR%" mkdir "%UPLOAD_DIR%" - where adb - adb devices -l - adb wait-for-device - adb uninstall $packageName - adb install -r "%~dp0$apkName" || exit /b 1 - adb shell logcat -c - adb shell monkey -p $packageName -c android.intent.category.LAUNCHER 1 || exit /b 1 + set "ADB=%~dp0platform-tools\adb.exe" + if not exist "%ADB%" ( + echo Could not find payload adb.exe at "%ADB%" + dir "%~dp0" /s + exit /b 1 + ) + "%ADB%" version || exit /b 1 + "%ADB%" devices -l + "%ADB%" wait-for-device || exit /b 1 + "%ADB%" uninstall $packageName + "%ADB%" install -r "%~dp0$apkName" || exit /b 1 + "%ADB%" shell logcat -c + "%ADB%" shell monkey -p $packageName -c android.intent.category.LAUNCHER 1 || exit /b 1 ping -n 11 127.0.0.1 > nul - adb shell pidof $packageName + "%ADB%" shell pidof $packageName if errorlevel 1 ( - adb logcat -d > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" - adb uninstall $packageName + "%ADB%" logcat -d > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" + "%ADB%" uninstall $packageName exit /b 1 ) - adb logcat -d > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" - adb uninstall $packageName + "%ADB%" logcat -d > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" + "%ADB%" uninstall $packageName exit /b 0 "@ From 3eb7027eac32a11c702d4fadcaca5933d0525c64 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 9 Jul 2026 10:08:54 +0200 Subject: [PATCH 07/10] [ci] Improve MAUI R2R Helix diagnostics Use a repo-owned Helix project so each MAUI R2R scenario sets a descriptive Azure Pipelines test run and work item name. Download Helix result files into the MAUI logs artifact and upload scenario, device, package, and full logcat diagnostics from each work item. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build-tools/automation/MauiR2RHelix.proj | 28 +++++++++++++++ .../run-maui-r2r-helix-matrix.yaml | 36 ++++++++++++++----- 2 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 build-tools/automation/MauiR2RHelix.proj diff --git a/build-tools/automation/MauiR2RHelix.proj b/build-tools/automation/MauiR2RHelix.proj new file mode 100644 index 00000000000..28fe3915b84 --- /dev/null +++ b/build-tools/automation/MauiR2RHelix.proj @@ -0,0 +1,28 @@ + + + msbuild + true + false + true + tests/android/maui-r2r/ + WorkItem + $(HelixWorkItemName) + $(BUILD_STAGINGDIRECTORY)\helix-results\$(HelixWorkItemName) + $(MSBuildThisFileDirectory)..\..\artifacts\helix-results\$(HelixWorkItemName) + + + + + $(HelixTestRunName) + + + + + + $(WorkItemDirectory) + $(WorkItemCommand) + $(WorkItemTimeout) + **/* + + + diff --git a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml index 7115d3f4cf0..adfe859da67 100644 --- a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml +++ b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml @@ -10,22 +10,26 @@ parameters: runtimeIdentifier: android-arm64 packageName: com.xamarin.mauir2r.arm64.baseline helixQueue: windows.11.amd64.android.open - displayName: android-arm64 default R2R + displayName: CoreCLR/android-arm64/Default configuration + workItemName: CoreCLR_android-arm64_DefaultConfiguration - name: android-arm64-fullr2r runtimeIdentifier: android-arm64 packageName: com.xamarin.mauir2r.arm64.fullr2r helixQueue: windows.11.amd64.android.open - displayName: android-arm64 full R2R + displayName: CoreCLR/android-arm64/Full R2R + workItemName: CoreCLR_android-arm64_FullR2R - name: android-arm-default runtimeIdentifier: android-arm packageName: com.xamarin.mauir2r.arm.baseline helixQueue: windows.11.amd64.android-armel.open - displayName: android-arm default R2R + displayName: CoreCLR/android-arm/Default configuration + workItemName: CoreCLR_android-arm_DefaultConfiguration - name: android-arm-fullr2r runtimeIdentifier: android-arm packageName: com.xamarin.mauir2r.arm.fullr2r helixQueue: windows.11.amd64.android-armel.open - displayName: android-arm full R2R + displayName: CoreCLR/android-arm/Full R2R + workItemName: CoreCLR_android-arm_FullR2R steps: - powershell: | @@ -188,6 +192,10 @@ steps: set "UPLOAD_DIR=%HELIX_WORKITEM_UPLOAD_ROOT%" if "%UPLOAD_DIR%"=="" set "UPLOAD_DIR=%~dp0" if not exist "%UPLOAD_DIR%" mkdir "%UPLOAD_DIR%" + echo Scenario: ${{ testCase.displayName }} > "%UPLOAD_DIR%\scenario-${{ testCase.name }}.txt" + echo RuntimeIdentifier: $runtimeIdentifier >> "%UPLOAD_DIR%\scenario-${{ testCase.name }}.txt" + echo PackageName: $packageName >> "%UPLOAD_DIR%\scenario-${{ testCase.name }}.txt" + echo APK: $apkName >> "%UPLOAD_DIR%\scenario-${{ testCase.name }}.txt" set "ADB=%~dp0platform-tools\adb.exe" if not exist "%ADB%" ( echo Could not find payload adb.exe at "%ADB%" @@ -195,20 +203,27 @@ steps: exit /b 1 ) "%ADB%" version || exit /b 1 - "%ADB%" devices -l + "%ADB%" devices -l > "%UPLOAD_DIR%\adb-devices-${{ testCase.name }}.log" + type "%UPLOAD_DIR%\adb-devices-${{ testCase.name }}.log" "%ADB%" wait-for-device || exit /b 1 + "%ADB%" shell getprop > "%UPLOAD_DIR%\getprop-${{ testCase.name }}.log" + "%ADB%" shell getprop ro.product.cpu.abi + "%ADB%" shell getprop ro.product.cpu.abilist + "%ADB%" shell getprop ro.build.fingerprint "%ADB%" uninstall $packageName "%ADB%" install -r "%~dp0$apkName" || exit /b 1 + "%ADB%" shell pm path $packageName > "%UPLOAD_DIR%\pm-path-${{ testCase.name }}.log" + "%ADB%" shell dumpsys package $packageName > "%UPLOAD_DIR%\dumpsys-package-${{ testCase.name }}.log" "%ADB%" shell logcat -c "%ADB%" shell monkey -p $packageName -c android.intent.category.LAUNCHER 1 || exit /b 1 ping -n 11 127.0.0.1 > nul "%ADB%" shell pidof $packageName if errorlevel 1 ( - "%ADB%" logcat -d > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" + "%ADB%" logcat -d -b all > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" "%ADB%" uninstall $packageName exit /b 1 ) - "%ADB%" logcat -d > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" + "%ADB%" logcat -d -b all > "%UPLOAD_DIR%\logcat-${{ testCase.name }}.log" "%ADB%" uninstall $packageName exit /b 0 "@ @@ -219,7 +234,7 @@ steps: - template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml parameters: command: msbuild - project: ${{ parameters.xaSourcePath }}/eng/common/helixpublish.proj + project: ${{ parameters.xaSourcePath }}/build-tools/automation/MauiR2RHelix.proj arguments: >- /restore /p:TreatWarningsAsErrors=false @@ -234,7 +249,10 @@ steps: HelixType: tests/android/maui-r2r/${{ testCase.name }}/ HelixBuild: $(Build.BuildNumber) HelixConfiguration: ${{ testCase.name }} - HelixTargetQueues: ${{ testCase.helixQueue }} + HelixTargetQueueForItem: ${{ testCase.helixQueue }} + HelixTestRunName: ${{ testCase.displayName }} + HelixWorkItemName: ${{ testCase.workItemName }} + HelixResultsDestinationDir: ${{ parameters.logsDirectory }}/helix-results/${{ testCase.name }} HelixAccessToken: '' WorkItemDirectory: ${{ parameters.payloadDirectory }}/${{ testCase.name }} WorkItemCommand: run-maui-r2r.cmd From 07066ccaa502ac05d1a84111334ea56896cc1a06 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 9 Jul 2026 11:36:56 +0200 Subject: [PATCH 08/10] [ci] Avoid invalid Helix result download glob Remove DownloadFilesFromResults=**/* from the MAUI R2R Helix work item. The Helix SDK treats it as part of a Windows path while downloading result files, causing a successful work item to fail the submission step. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build-tools/automation/MauiR2RHelix.proj | 1 - 1 file changed, 1 deletion(-) diff --git a/build-tools/automation/MauiR2RHelix.proj b/build-tools/automation/MauiR2RHelix.proj index 28fe3915b84..6abba14c266 100644 --- a/build-tools/automation/MauiR2RHelix.proj +++ b/build-tools/automation/MauiR2RHelix.proj @@ -22,7 +22,6 @@ $(WorkItemDirectory) $(WorkItemCommand) $(WorkItemTimeout) - **/* From 71a2f8cebb4f56fcfac826af0f567ef985465401 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 9 Jul 2026 13:05:48 +0200 Subject: [PATCH 09/10] [ci] Run MAUI R2R Helix submissions in parallel Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../run-maui-r2r-helix-matrix.yaml | 155 ++++++++++++++---- 1 file changed, 126 insertions(+), 29 deletions(-) diff --git a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml index adfe859da67..74d6ec83642 100644 --- a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml +++ b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml @@ -151,6 +151,7 @@ steps: displayName: Build MAUI template - ${{ testCase.displayName }} continueOnError: false +- ${{ each testCase in parameters.testCases }}: - powershell: | $projectDirectory = '${{ parameters.matrixDirectory }}/${{ testCase.name }}' $payloadDirectory = '${{ parameters.payloadDirectory }}/${{ testCase.name }}' @@ -231,32 +232,128 @@ steps: Set-Content -LiteralPath (Join-Path $payloadDirectory 'run-maui-r2r.cmd') -Value $script -Encoding ASCII displayName: Prepare MAUI R2R Helix payload - ${{ testCase.displayName }} - - template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml - parameters: - command: msbuild - project: ${{ parameters.xaSourcePath }}/build-tools/automation/MauiR2RHelix.proj - arguments: >- - /restore - /p:TreatWarningsAsErrors=false - /t:Test - /bl:${{ parameters.logsDirectory }}/SendMauiR2R-${{ testCase.name }}ToHelix.binlog - xaSourcePath: ${{ parameters.xaSourcePath }} - displayName: Run MAUI ${{ testCase.displayName }} APK on Helix - continueOnError: false - env: - BuildConfig: $(XA.Build.Configuration) - HelixSource: pr/dotnet/android - HelixType: tests/android/maui-r2r/${{ testCase.name }}/ - HelixBuild: $(Build.BuildNumber) - HelixConfiguration: ${{ testCase.name }} - HelixTargetQueueForItem: ${{ testCase.helixQueue }} - HelixTestRunName: ${{ testCase.displayName }} - HelixWorkItemName: ${{ testCase.workItemName }} - HelixResultsDestinationDir: ${{ parameters.logsDirectory }}/helix-results/${{ testCase.name }} - HelixAccessToken: '' - WorkItemDirectory: ${{ parameters.payloadDirectory }}/${{ testCase.name }} - WorkItemCommand: run-maui-r2r.cmd - WorkItemTimeout: 00:10:00 - WaitForWorkItemCompletion: true - Creator: dotnet-android - SYSTEM_ACCESSTOKEN: $(System.AccessToken) +- template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + command: msbuild + project: ${{ parameters.xaSourcePath }}/build-tools/automation/MauiR2RHelix.proj + arguments: >- + /t:Restore + /p:TreatWarningsAsErrors=false + /bl:${{ parameters.logsDirectory }}/RestoreMauiR2RHelix.binlog + xaSourcePath: ${{ parameters.xaSourcePath }} + displayName: Restore MAUI R2R Helix project + continueOnError: false + +- powershell: | + $ErrorActionPreference = 'Stop' + $xaSourcePath = '${{ parameters.xaSourcePath }}' + $logsDirectory = '${{ parameters.logsDirectory }}' + $payloadRootDirectory = '${{ parameters.payloadDirectory }}' + New-Item -ItemType Directory -Force -Path $logsDirectory | Out-Null + + if ([Environment]::OSVersion.Platform -eq "Unix") { + $dotnetRoot = Join-Path $xaSourcePath 'bin/$(XA.Build.Configuration)/dotnet' + $dotnetPath = Join-Path $dotnetRoot 'dotnet' + } else { + $dotnetRoot = Join-Path $xaSourcePath 'bin\$(XA.Build.Configuration)\dotnet' + $dotnetPath = Join-Path $dotnetRoot 'dotnet.exe' + } + + if (-not (Test-Path $dotnetPath)) { + throw "Could not find dotnet at '$dotnetPath'." + } + + $helixProject = Join-Path $xaSourcePath 'build-tools/automation/MauiR2RHelix.proj' + $pathSeparator = [IO.Path]::PathSeparator + $powerShellExe = (Get-Process -Id $PID).Path + if (-not $powerShellExe) { + $powerShellExe = 'powershell.exe' + } + + $cases = @' + ${{ convertToJson(parameters.testCases) }} + '@ | ConvertFrom-Json + + $submissions = @() + foreach ($case in @($cases)) { + $scriptPath = Join-Path $logsDirectory "SendMauiR2R-$($case.name)ToHelix.ps1" + $outputPath = Join-Path $logsDirectory "SendMauiR2R-$($case.name)ToHelix.out.log" + $errorPath = Join-Path $logsDirectory "SendMauiR2R-$($case.name)ToHelix.err.log" + $binlogPath = Join-Path $logsDirectory "SendMauiR2R-$($case.name)ToHelix.binlog" + $helixResultsDestinationDir = Join-Path (Join-Path $logsDirectory 'helix-results') $case.name + $workItemDirectory = Join-Path $payloadRootDirectory $case.name + + Remove-Item -LiteralPath $scriptPath, $outputPath, $errorPath -Force -ErrorAction Ignore + + $scriptLines = @( + '$ErrorActionPreference = ''Stop''' + "`$env:DOTNET_ROOT = '$dotnetRoot'" + "`$env:PATH = '$dotnetRoot$pathSeparator' + `$env:PATH" + "`$env:BuildConfig = '$(XA.Build.Configuration)'" + "`$env:HelixSource = 'pr/dotnet/android'" + "`$env:HelixType = 'tests/android/maui-r2r/$($case.name)/'" + "`$env:HelixBuild = '$(Build.BuildNumber)'" + "`$env:HelixConfiguration = '$($case.name)'" + "`$env:HelixTargetQueueForItem = '$($case.helixQueue)'" + "`$env:HelixTestRunName = '$($case.displayName)'" + "`$env:HelixWorkItemName = '$($case.workItemName)'" + "`$env:HelixResultsDestinationDir = '$helixResultsDestinationDir'" + "`$env:HelixAccessToken = ''" + "`$env:WorkItemDirectory = '$workItemDirectory'" + "`$env:WorkItemCommand = 'run-maui-r2r.cmd'" + "`$env:WorkItemTimeout = '00:10:00'" + "`$env:WaitForWorkItemCompletion = 'true'" + "`$env:Creator = 'dotnet-android'" + "& '$dotnetPath' msbuild '$helixProject' /p:TreatWarningsAsErrors=false /t:Test /bl:'$binlogPath'" + 'exit $LASTEXITCODE' + ) + Set-Content -LiteralPath $scriptPath -Value $scriptLines -Encoding ASCII + + Write-Host "Starting MAUI R2R Helix submission: $($case.displayName)" + $process = Start-Process -FilePath $powerShellExe -ArgumentList @('-NoLogo', '-NoProfile', '-File', $scriptPath) -RedirectStandardOutput $outputPath -RedirectStandardError $errorPath -PassThru + $submissions += [pscustomobject]@{ + Name = $case.name + DisplayName = $case.displayName + Process = $process + OutputPath = $outputPath + ErrorPath = $errorPath + } + } + + while (($submissions | Where-Object { -not $_.Process.HasExited }).Count -ne 0) { + $runningNames = $submissions | + Where-Object { -not $_.Process.HasExited } | + ForEach-Object { $_.DisplayName } + Write-Host "Waiting for MAUI R2R Helix submissions: $($runningNames -join ', ')" + Start-Sleep -Seconds 30 + } + + foreach ($submission in $submissions) { + $submission.Process.WaitForExit() + } + + $failedSubmissions = @() + foreach ($submission in $submissions) { + Write-Host "##[group]MAUI R2R Helix submission log - $($submission.DisplayName)" + if (Test-Path $submission.OutputPath) { + Get-Content -LiteralPath $submission.OutputPath + } + if (Test-Path $submission.ErrorPath) { + Get-Content -LiteralPath $submission.ErrorPath + } + Write-Host "##[endgroup]" + + if ($submission.Process.ExitCode -ne 0) { + Write-Host "##vso[task.logissue type=error]MAUI R2R Helix submission failed for $($submission.DisplayName) with exit code $($submission.Process.ExitCode)." + $failedSubmissions += $submission + } + } + + if ($failedSubmissions.Count -ne 0) { + $failedNames = $failedSubmissions | ForEach-Object { $_.DisplayName } + throw "Failed MAUI R2R Helix submissions: $($failedNames -join ', ')" + } + displayName: Run MAUI R2R APKs on Helix + continueOnError: false + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) From af4351bfa9e00226fee27b859672ae524e4cf630 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 9 Jul 2026 14:41:53 +0200 Subject: [PATCH 10/10] [ci] Fix MAUI R2R Helix restore Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../automation/yaml-templates/run-maui-r2r-helix-matrix.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml index 74d6ec83642..69470adbde0 100644 --- a/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml +++ b/build-tools/automation/yaml-templates/run-maui-r2r-helix-matrix.yaml @@ -239,6 +239,8 @@ steps: arguments: >- /t:Restore /p:TreatWarningsAsErrors=false + /p:EnableAzurePipelinesReporter=false + /p:HelixTargetQueueForItem=windows.11.amd64.android.open /bl:${{ parameters.logsDirectory }}/RestoreMauiR2RHelix.binlog xaSourcePath: ${{ parameters.xaSourcePath }} displayName: Restore MAUI R2R Helix project