diff --git a/build-tools/automation/azure-pipelines-public.yaml b/build-tools/automation/azure-pipelines-public.yaml index f00a09174e2..a2f83418def 100644 --- a/build-tools/automation/azure-pipelines-public.yaml +++ b/build-tools/automation/azure-pipelines-public.yaml @@ -16,12 +16,11 @@ pr: parameters: - name: macTestAgentsUseCleanImages # Test agents we do not need to clean up when finished because they are not reused default: true -- name: skipTests # Skip all test stages (builds only). Use 'auto' to auto-skip for non-fork PRs. +- name: skipTests # Skip all test stages (builds only). Default 'false' so PRs run the full test matrix. displayName: Skip Tests type: string - default: 'auto' + default: 'false' values: - - 'auto' - 'true' - 'false' @@ -33,6 +32,11 @@ resources: name: dotnet/android-tools endpoint: public ref: refs/heads/main + - repository: maui + type: github + name: dotnet/maui + endpoint: public + ref: net11.0 # Global variables variables: @@ -51,14 +55,10 @@ variables: value: windows.vs2022.amd64.open - name: LinuxPoolImageNetCorePublic value: build.Ubuntu.2204.amd64.open -# Skip tests for non-fork PRs (they should use DevDiv pipeline) unless explicitly requested +# Skip tests when explicitly requested; otherwise run full PR test matrix. - name: SkipTestStages ${{ if eq(parameters.skipTests, 'true') }}: value: true - ${{ elseif eq(parameters.skipTests, 'false') }}: - value: false - ${{ elseif and(eq(variables['Build.Reason'], 'PullRequest'), ne(variables['System.PullRequest.IsFork'], 'True')) }}: - value: true ${{ else }}: value: false @@ -67,7 +67,6 @@ stages: # macOS Build Stage - stage: mac_build displayName: Mac - condition: eq(variables['System.PullRequest.IsFork'], 'True') jobs: - job: mac_build_create_installers displayName: macOS > Build @@ -104,7 +103,6 @@ stages: - stage: win_build_test displayName: Windows dependsOn: [] - condition: eq(variables['System.PullRequest.IsFork'], 'True') jobs: - job: win_build_test displayName: Windows > Build & Smoke Test @@ -126,7 +124,6 @@ stages: - stage: linux_build displayName: Linux dependsOn: [] - condition: eq(variables['System.PullRequest.IsFork'], 'True') jobs: - job: linux_build_create_sdk_pack displayName: Linux > Build @@ -174,7 +171,7 @@ stages: dependsOn: - mac_build - linux_build - condition: and(succeeded(), eq(variables['System.PullRequest.IsFork'], 'True'), ne(variables['SkipTestStages'], 'true')) + condition: and(succeeded(), ne(variables['SkipTestStages'], 'true')) jobs: - job: linux_tests_smoke_1 displayName: Linux > Tests > MSBuild 1 @@ -270,7 +267,7 @@ stages: - stage: msbuild_dotnet displayName: MSBuild Tests dependsOn: mac_build - condition: and(succeeded(), eq(variables['System.PullRequest.IsFork'], 'True'), ne(variables['SkipTestStages'], 'true')) + condition: and(succeeded(), ne(variables['SkipTestStages'], 'true')) jobs: - job: mac_msbuild_tests strategy: @@ -362,11 +359,11 @@ stages: - stage: msbuilddevice_tests displayName: MSBuild Emulator Tests dependsOn: mac_build - condition: and(succeeded(), eq(variables['System.PullRequest.IsFork'], 'True'), ne(variables['SkipTestStages'], 'true')) + condition: and(succeeded(), ne(variables['SkipTestStages'], 'true')) jobs: - job: mac_dotnetdevice_tests strategy: - parallel: 8 + parallel: 12 displayName: "macOS > Tests > MSBuild+Emulator" # NOTE: AcesShared pool cannot boot Android emulators, so these jobs must use hosted images pool: @@ -415,3 +412,191 @@ stages: - template: /build-tools/automation/yaml-templates/fail-on-issue.yaml parameters: condition: true + +# MAUI Tests Stage +- stage: maui_tests + displayName: MAUI Tests + dependsOn: mac_build + condition: and(succeeded(), ne(variables['SkipTestStages'], 'true')) + jobs: + - job: maui_tests_integration + displayName: MAUI Integration + pool: + name: $(NetCorePublicPoolName) + demands: + - ImageOverride -equals 1es-windows-2022-open + os: windows + timeoutInMinutes: 180 + workspace: + clean: all + variables: + BuildVersion: $(Build.BuildId) + # Ignore MAUI's public API analyzer errors (RS0016) - dotnet/android does not care about those + PublicApiType: Generate + steps: + - checkout: self + clean: true + submodules: recursive + path: s/android + persistCredentials: false + + - checkout: maui + clean: true + submodules: recursive + path: s/maui + persistCredentials: true + + # Restore ~/.gradle/caches between runs so MAUI's Gradle resolution + # for android-gradle-plugin/sdklib/ddmlib is not gated on the + # dotnet-public-maven Maven feed every build. The dnceng-public Maven + # feed periodically rate-limits ('exceeding usage of resource Concurrency + # in namespace IPAddress', HTTP 500), which deterministically fails + # MAUI's ./build.ps1 --target=dotnet-pack. + # Adapted from upstream dotnet/maui, which adopted the pattern from us: + # https://github.com/dotnet/maui/blob/net11.0/eng/pipelines/common/cache-gradle.yml + - template: /build-tools/automation/yaml-templates/cache-gradle.yaml + parameters: + xaSourcePath: $(Build.SourcesDirectory)/android + + - template: /build-tools/automation/yaml-templates/setup-test-environment-steps.yaml + parameters: + xaSourcePath: $(Build.SourcesDirectory)/android + androidSdkPlatforms: $(DefaultTestSdkPlatforms) + dotnetVersion: $(DotNetSdkVersion) + dotnetQuality: $(DotNetSdkQuality) + useAgentJdkPath: false + use1ESTemplate: false + + - task: DownloadPipelineArtifact@2 + inputs: + artifactName: $(NuGetArtifactName) + downloadPath: $(Build.StagingDirectory)/android-packs + + - pwsh: | + $searchPath = Join-Path $(Build.StagingDirectory) android-packs + $wlmanPack = Get-ChildItem $searchPath -Filter *Android*Manifest*.nupkg | Select-Object -First 1 + $dest = Join-Path $searchPath "tmp-wlman" "$($wlmanPack.BaseName)" + Expand-Archive -LiteralPath $wlmanPack -DestinationPath $dest + $wlmanJsonPath = Join-Path $dest "data" "WorkloadManifest.json" + $json = Get-Content $wlmanJsonPath | ConvertFrom-Json -AsHashtable + Write-Host "Setting variable ANDROID_PACK_VERSION = $($json["version"])" + Write-Host "##vso[task.setvariable variable=ANDROID_PACK_VERSION;]$($json["version"])" + displayName: Set ANDROID_PACK_VERSION + + - pwsh: >- + $(Build.SourcesDirectory)/maui/eng/scripts/update-version-props.ps1 + -xmlFileName "$(Build.SourcesDirectory)/maui/eng/Versions.props" + -androidVersion $(ANDROID_PACK_VERSION) + displayName: Update MAUI's Android dependency + + - task: DotNetCoreCLI@2 + displayName: Update Android SDK band in Workloads.csproj + inputs: + projects: $(Build.SourcesDirectory)/android/Xamarin.Android.sln + arguments: -t:UpdateMauiWorkloadsProj -c $(XA.Build.Configuration) --no-restore -v:n -bl:$(Build.StagingDirectory)/logs/update-maui-workloadsproj.binlog + + - pwsh: ./build.ps1 --target=dotnet --configuration="$(XA.Build.Configuration)" --nugetsource="$(Build.StagingDirectory)\android-packs" --verbosity=diagnostic + displayName: Install .NET + retryCountOnTaskFailure: 3 + workingDirectory: $(Build.SourcesDirectory)/maui + + - task: NodeTool@0 + displayName: Install Node.js + # MAUI's TypeScript build steps for the BlazorWebView project shell out + # to node.exe; the dnceng-public windows.vs2022.amd64.open image does + # not preinstall Node, so install it explicitly. + inputs: + versionSpec: '20.x' + + - pwsh: | + # Match Configuration.props: prefer ANDROID_SDK_ROOT if set+exists, + # else $(HOME)\android-toolchain\sdk, else $env:USERPROFILE fallback. + $candidates = @() + if ($env:ANDROID_SDK_ROOT) { $candidates += $env:ANDROID_SDK_ROOT } + if ($env:ANDROID_HOME) { $candidates += $env:ANDROID_HOME } + if ($env:HOME) { $candidates += "$env:HOME\android-toolchain\sdk" } + if ($env:USERPROFILE) { $candidates += "$env:USERPROFILE\android-toolchain\sdk" } + $sdk = $candidates | Where-Object { $_ -and (Test-Path (Join-Path $_ 'platforms')) } | Select-Object -First 1 + if (-not $sdk) { + Write-Error "Could not find an Android SDK install with a 'platforms' subdir. Tried:`n$($candidates -join "`n")" + exit 1 + } + Write-Host "Setting ANDROID_HOME / ANDROID_SDK_ROOT to '$sdk'" + Get-ChildItem (Join-Path $sdk 'platforms') | ForEach-Object { Write-Host " platforms\$($_.Name)" } + Write-Host "##vso[task.setvariable variable=ANDROID_HOME]$sdk" + Write-Host "##vso[task.setvariable variable=ANDROID_SDK_ROOT]$sdk" + displayName: Point ANDROID_HOME at xa-prepare SDK + # androidsdk.csproj's install path follows Configuration.props rules: + # ANDROID_SDK_ROOT if it exists, otherwise %HOME%\android-toolchain\sdk. + # On the 1es-windows-2022-open image, ANDROID_SDK_ROOT is preset to + # the agent's preinstalled SDK (e.g. C:\Program Files (x86)\Android\android-sdk), + # so xa-prepare ends up extending that location with the requested + # API levels. Whichever path it picked, MAUI's build needs the same + # ANDROID_HOME / ANDROID_SDK_ROOT so it sees API 37, etc. + + - pwsh: ./build.ps1 --target=dotnet-pack --configuration="$(XA.Build.Configuration)" --nugetsource="$(Build.StagingDirectory)\android-packs" --verbosity=diagnostic + displayName: Pack .NET Maui + workingDirectory: $(Build.SourcesDirectory)/maui + + - task: DotNetCoreCLI@2 + displayName: Install MAUI workload packs + retryCountOnTaskFailure: 3 + inputs: + projects: $(Build.SourcesDirectory)/android/Xamarin.Android.sln + arguments: -t:InstallMaui -p:MauiUseLocalPacks=true -p:MauiWorkloadToInstall=maui -p:MauiManifestDiagnosticsPath=$(Build.StagingDirectory)/logs/maui-manifest -c $(XA.Build.Configuration) --no-restore -v:n -bl:$(Build.StagingDirectory)/logs/install-maui.binlog + + - template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + command: new + arguments: maui -o $(Build.StagingDirectory)/MauiTestProj + xaSourcePath: $(Build.SourcesDirectory)/android + displayName: Create MAUI template + continueOnError: false + + - powershell: | + $project = '$(Build.StagingDirectory)/MauiTestProj/MauiTestProj.csproj' + [xml] $xml = Get-Content $project + $node = $xml.SelectSingleNode('/Project/PropertyGroup/TargetFrameworks') + $node.InnerText = '$(DotNetTargetFramework)-android' + $xml.Save($project) + displayName: set TargetFrameworks to Android-only + + - template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + project: $(Build.StagingDirectory)/MauiTestProj/MauiTestProj.csproj + arguments: >- + -f $(DotNetTargetFramework)-android -c Debug + --configfile $(Build.SourcesDirectory)/maui/NuGet.config + -bl:$(Build.StagingDirectory)/logs/MauiTestProj-Debug.binlog + xaSourcePath: $(Build.SourcesDirectory)/android + displayName: Build MAUI template - Debug + + - template: /build-tools/automation/yaml-templates/run-dotnet-preview.yaml + parameters: + project: $(Build.StagingDirectory)/MauiTestProj/MauiTestProj.csproj + arguments: >- + -f $(DotNetTargetFramework)-android -c Release + --configfile $(Build.SourcesDirectory)/maui/NuGet.config + -bl:$(Build.StagingDirectory)/logs/MauiTestProj-Release.binlog + xaSourcePath: $(Build.SourcesDirectory)/android + displayName: Build MAUI template - Release + + - task: CopyFiles@2 + displayName: copy build logs + condition: always() + inputs: + Contents: | + $(Build.SourcesDirectory)/android/bin/*$(XA.Build.Configuration)/*.*log + $(Build.SourcesDirectory)/maui/artifacts/logs/** + TargetFolder: $(Build.StagingDirectory)/logs + flattenFolders: true + + - template: /build-tools/automation/yaml-templates/publish-artifact.yaml + parameters: + displayName: upload build and test results + artifactName: Test Results - MAUI Integration + targetPath: $(Build.StagingDirectory)/logs + condition: or(ne(variables['Agent.JobStatus'], 'Succeeded'), eq(variables['XA.PublishAllLogs'], 'true')) + use1ESTemplate: false + + - template: /build-tools/automation/yaml-templates/fail-on-issue.yaml diff --git a/build-tools/automation/azure-pipelines.yaml b/build-tools/automation/azure-pipelines.yaml index f11fdd1d613..9f9454386e3 100644 --- a/build-tools/automation/azure-pipelines.yaml +++ b/build-tools/automation/azure-pipelines.yaml @@ -9,6 +9,8 @@ trigger: - release/* - feature/* +pr: none + # External sources, scripts, tests, and yaml template files. resources: repositories: diff --git a/build-tools/automation/yaml-templates/build-linux-steps.yaml b/build-tools/automation/yaml-templates/build-linux-steps.yaml index 448999622c0..ce12eb40d86 100644 --- a/build-tools/automation/yaml-templates/build-linux-steps.yaml +++ b/build-tools/automation/yaml-templates/build-linux-steps.yaml @@ -34,6 +34,7 @@ steps: - script: make jenkins PREPARE_CI=1 PREPARE_AUTOPROVISION=1 CONFIGURATION=$(XA.Build.Configuration) MSBUILD_ARGS='${{ parameters.makeMSBuildArgs }}' workingDirectory: ${{ parameters.xaSourcePath }} displayName: make jenkins + retryCountOnTaskFailure: 1 - script: make create-nupkgs CONFIGURATION=$(XA.Build.Configuration) workingDirectory: ${{ parameters.xaSourcePath }} diff --git a/build-tools/automation/yaml-templates/build-macos-steps.yaml b/build-tools/automation/yaml-templates/build-macos-steps.yaml index a578f34addc..e3c7280dd09 100644 --- a/build-tools/automation/yaml-templates/build-macos-steps.yaml +++ b/build-tools/automation/yaml-templates/build-macos-steps.yaml @@ -44,6 +44,7 @@ steps: - script: make jenkins CONFIGURATION=$(XA.Build.Configuration) PREPARE_CI=1 PREPARE_AUTOPROVISION=1 MSBUILD_ARGS='${{ parameters.makeMSBuildArgs }}' workingDirectory: ${{ parameters.xaSourcePath }} displayName: make jenkins + retryCountOnTaskFailure: 1 - script: make create-installers CONFIGURATION=$(XA.Build.Configuration) MSBUILD_ARGS='${{ parameters.makeMSBuildArgs }}' workingDirectory: ${{ parameters.xaSourcePath }} diff --git a/build-tools/automation/yaml-templates/build-windows-steps.yaml b/build-tools/automation/yaml-templates/build-windows-steps.yaml index c8651b02d76..0866bdfa98d 100644 --- a/build-tools/automation/yaml-templates/build-windows-steps.yaml +++ b/build-tools/automation/yaml-templates/build-windows-steps.yaml @@ -29,6 +29,7 @@ steps: - task: DotNetCoreCLI@2 displayName: Prepare Solution + retryCountOnTaskFailure: 1 inputs: projects: Xamarin.Android.sln arguments: '-c $(XA.Build.Configuration) -t:Prepare --no-restore -p:AutoProvision=true -bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\dotnet-build-prepare.binlog' diff --git a/build-tools/create-packs/Directory.Build.targets b/build-tools/create-packs/Directory.Build.targets index 67acf8b5aa9..6608c4181e3 100644 --- a/build-tools/create-packs/Directory.Build.targets +++ b/build-tools/create-packs/Directory.Build.targets @@ -101,6 +101,19 @@ + + + <_WLExtractedDestFiles Include="$(_SdkManifestsFolder)microsoft.net.sdk.android\$(AndroidPackVersionLong)\*" /> + +