From 0c3d5283b85090a1099c22d9d1464ef150f3bf0a Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 13 Jul 2026 15:47:53 -0500 Subject: [PATCH 1/5] [build] Streamline BootstrapTasks and Prepare orchestration Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 813d3d45-07e0-428e-83db-2ddec4e6fbd4 --- Makefile | 5 +- Xamarin.Android.BootstrapTasks.sln | 31 ----- ...amarin.Android.Tools.BootstrapTasks.csproj | 1 - .../Android.cs | 67 ----------- .../CheckApiCompatibility.cs | 19 +++- .../Emulator.cs | 67 ----------- .../GenerateSupportedPlatforms.cs | 24 ++-- .../JdkInfo.cs | 107 ------------------ build-tools/automation/azure-pipelines.yaml | 4 +- build-tools/scripts/Prepare.proj | 26 +++++ build-tools/scripts/PrepareWindows.targets | 9 +- 11 files changed, 67 insertions(+), 293 deletions(-) delete mode 100644 Xamarin.Android.BootstrapTasks.sln delete mode 100644 build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/Android.cs delete mode 100644 build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/Emulator.cs delete mode 100644 build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/JdkInfo.cs create mode 100644 build-tools/scripts/Prepare.proj diff --git a/Makefile b/Makefile index e6cfa5044bc..6a2d8966393 100644 --- a/Makefile +++ b/Makefile @@ -81,10 +81,7 @@ include build-tools/scripts/runtime-helpers.mk .PHONY: prepare prepare: install-dotnet - $(call SYSTEM_DOTNET_BINLOG,prepare-bootstrap) Xamarin.Android.BootstrapTasks.sln - $(call SYSTEM_DOTNET_BINLOG,prepare-workloads) src/workloads/workloads.csproj - $(call DOTNET_BINLOG,prepare-java.interop) $(SOLUTION) -t:PrepareJavaInterop - $(call SYSTEM_DOTNET_BINLOG,prepare-build-tools-inventory) build-tools/build-tools-inventory/build-tools-inventory.proj + $(call SYSTEM_DOTNET_BINLOG,prepare) build-tools/scripts/Prepare.proj -t:Prepare .PHONY: install-dotnet install-dotnet: diff --git a/Xamarin.Android.BootstrapTasks.sln b/Xamarin.Android.BootstrapTasks.sln deleted file mode 100644 index 032ccf098ac..00000000000 --- a/Xamarin.Android.BootstrapTasks.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29411.108 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "xa-prep-tasks", "build-tools\xa-prep-tasks\xa-prep-tasks.csproj", "{7CE69551-BD73-4726-ACAA-AAF89C84BAF8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xamarin.Android.Tools.BootstrapTasks", "build-tools\Xamarin.Android.Tools.BootstrapTasks\Xamarin.Android.Tools.BootstrapTasks.csproj", "{E8492EFB-D14A-4F32-AA28-88848322ECEA}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7CE69551-BD73-4726-ACAA-AAF89C84BAF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7CE69551-BD73-4726-ACAA-AAF89C84BAF8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7CE69551-BD73-4726-ACAA-AAF89C84BAF8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7CE69551-BD73-4726-ACAA-AAF89C84BAF8}.Release|Any CPU.Build.0 = Release|Any CPU - {E8492EFB-D14A-4F32-AA28-88848322ECEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E8492EFB-D14A-4F32-AA28-88848322ECEA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E8492EFB-D14A-4F32-AA28-88848322ECEA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E8492EFB-D14A-4F32-AA28-88848322ECEA}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {368B5B8B-7CA0-4D02-B36A-95CF69A960CD} - EndGlobalSection -EndGlobal diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks.csproj b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks.csproj index c53a9f415f1..2c266329791 100644 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks.csproj +++ b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks.csproj @@ -19,7 +19,6 @@ - diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/Android.cs b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/Android.cs deleted file mode 100644 index 65a1f15ad31..00000000000 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/Android.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; - -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -using Xamarin.Android.BuildTools.PrepTasks; - -namespace Xamarin.Android.Tools.BootstrapTasks -{ - public class Android : PathToolTask - { - public string Arguments { get; set; } - - [Output] - public string[] Output { get; set; } - - protected virtual bool LogTaskMessages { - get { return true; } - } - - protected override string ToolBaseName { - get { return "android"; } - } - - List lines; - List Lines { - get { return lines ?? (lines = new List ()); } - } - - public override bool Execute () - { - if (LogTaskMessages) { - Log.LogMessage (MessageImportance.Low, $"Task {nameof (Android)}"); - Log.LogMessage (MessageImportance.Low, $" {nameof (Arguments)}: {Arguments}"); - } - - base.Execute (); - - Output = lines?.ToArray (); - - if (LogTaskMessages) { - Log.LogMessage (MessageImportance.Low, $" [Output] {nameof (Output)}:"); - foreach (var line in (Output ?? Array.Empty ())) - Log.LogMessage (MessageImportance.Low, $" {line}"); - } - - return !Log.HasLoggedErrors; - } - - protected override string GenerateCommandLineCommands () - { - return Arguments; - } - - protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance) - { - base.LogEventsFromTextOutput (singleLine, messageImportance); - Lines.Add (singleLine); - } - } -} - diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/CheckApiCompatibility.cs b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/CheckApiCompatibility.cs index 64bf1e29dd5..fc13f6ca746 100644 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/CheckApiCompatibility.cs +++ b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/CheckApiCompatibility.cs @@ -267,7 +267,7 @@ void dataReceived (object sender, DataReceivedEventArgs args) LogError ($"CheckApiCompatibility found nonacceptable Api breakages for ApiLevel: {ApiLevel}.{Environment.NewLine}{string.Join (Environment.NewLine, lines)}"); ReportMissingLines (acceptableIssuesFile.FullName, lines); - var missingItems = CodeGenDiff.GenerateMissingItems (CodeGenPath, contractAssembly.FullName, implementationAssembly.FullName, JdkInfo.CreateTaskLogger (this)); + var missingItems = CodeGenDiff.GenerateMissingItems (CodeGenPath, contractAssembly.FullName, implementationAssembly.FullName, CreateTaskLogger (this)); if (missingItems.Any ()) { Log.LogMessage (MessageImportance.High, $"{Environment.NewLine}*** CodeGen missing items***{Environment.NewLine}"); var indent = 0; @@ -291,6 +291,23 @@ void dataReceived (object sender, DataReceivedEventArgs args) } } + static Action CreateTaskLogger (Task task) + { + return (level, value) => { + switch (level) { + case TraceLevel.Error: + task.Log.LogError (value); + break; + case TraceLevel.Warning: + task.Log.LogWarning (value); + break; + default: + task.Log.LogMessage (MessageImportance.Low, "{0}", value); + break; + } + }; + } + void ReportMissingLines (string acceptableIssuesFile, List lines) { if (string.IsNullOrWhiteSpace (LinesToAdd)) { diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/Emulator.cs b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/Emulator.cs deleted file mode 100644 index 4e0c40a8252..00000000000 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/Emulator.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; - -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -using Xamarin.Android.BuildTools.PrepTasks; - -namespace Xamarin.Android.Tools.BootstrapTasks -{ - public class Emulator : PathToolTask - { - public string Arguments { get; set; } - - [Output] - public string[] Output { get; set; } - - protected virtual bool LogTaskMessages { - get { return true; } - } - - protected override string ToolBaseName { - get { return "emulator"; } - } - - List lines; - List Lines { - get { return lines ?? (lines = new List ()); } - } - - public override bool Execute () - { - if (LogTaskMessages) { - Log.LogMessage (MessageImportance.Low, $"Task {nameof (Emulator)}"); - Log.LogMessage (MessageImportance.Low, $" {nameof (Arguments)}: {Arguments}"); - } - - base.Execute (); - - Output = lines?.ToArray (); - - if (LogTaskMessages) { - Log.LogMessage (MessageImportance.Low, $" [Output] {nameof (Output)}:"); - foreach (var line in (Output ?? Array.Empty ())) - Log.LogMessage (MessageImportance.Low, $" {line}"); - } - - return !Log.HasLoggedErrors; - } - - protected override string GenerateCommandLineCommands () - { - return Arguments; - } - - protected override void LogEventsFromTextOutput (string singleLine, MessageImportance messageImportance) - { - base.LogEventsFromTextOutput (singleLine, messageImportance); - Lines.Add (singleLine); - } - } -} - diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs index 72ec0abe662..ca309e3a801 100644 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs +++ b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs @@ -2,7 +2,6 @@ using System; using System.IO; -using System.Globalization; using System.Linq; using System.Xml; using Microsoft.Build.Framework; @@ -44,10 +43,14 @@ public override bool Execute () var minVersion = ToVersion (MinimumApiLevel); var targetVersion = ToVersion (TargetApiLevel); - var versions = new AndroidVersions (AndroidApiInfo.Select (ToAndroidVersion)); + var versions = AndroidApiInfo.Select (ToAndroidVersion).ToArray (); + var maxStableVersion = versions + .Where (version => version.Stable) + .OrderBy (version => version.TargetFrameworkVersion) + .Last (); var targetApiLevel = targetVersion != null && targetVersion.Major > 0 ? targetVersion - : versions.MaxStableVersion!.VersionCodeFull; + : maxStableVersion.VersionCodeFull; var settings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true, @@ -81,11 +84,11 @@ public override bool Execute () writer.WriteEndElement (); // writer.WriteStartElement ("ItemGroup"); - foreach (Version versionCode in versions.InstalledBindingVersions - .Where (v => v.VersionCodeFull >= minVersion) - .Select (v => v.VersionCodeFull) + foreach (Version versionCode in versions + .Select (version => version.VersionCodeFull) + .Where (version => version >= minVersion) .Distinct () - .OrderBy (v => v)) { + .OrderBy (version => version)) { writer.WriteStartElement ("AndroidSdkSupportedTargetPlatformVersion"); writer.WriteAttributeString ("Include", versionCode.ToString ()); if (versionCode < targetVersion) { @@ -116,7 +119,7 @@ public override bool Execute () return null; } - static AndroidVersion ToAndroidVersion (ITaskItem item) + static (Version VersionCodeFull, Version TargetFrameworkVersion, bool Stable) ToAndroidVersion (ITaskItem item) { /* @@ -131,9 +134,12 @@ static AndroidVersion ToAndroidVersion (ITaskItem item) if (!Version.TryParse (item.GetMetadata ("VersionCodeFull"), out var versionCodeFull)) { throw new ArgumentException ($"Invalid VersionCodeFull '{item.GetMetadata ("VersionCodeFull")}' for item '{item.ItemSpec}'"); } + if (!Version.TryParse (item.ItemSpec.TrimStart ('v'), out var targetFrameworkVersion)) { + throw new ArgumentException ($"Invalid framework version '{item.ItemSpec}'"); + } bool.TryParse (item.GetMetadata ("Stable"), out bool stable); - return new AndroidVersion (versionCodeFull, item.ItemSpec.TrimStart ('v'), item.GetMetadata ("Name"), item.GetMetadata ("Id"), stable); + return (versionCodeFull, targetFrameworkVersion, stable); } } } diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/JdkInfo.cs b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/JdkInfo.cs deleted file mode 100644 index 2d5d88ecaf0..00000000000 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/JdkInfo.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Text; - -namespace Xamarin.Android.Tools.BootstrapTasks -{ - public class JdkInfo : Task - { - [Required] - public ITaskItem Output { get; set; } - - public string AndroidNdkPath { get; set; } - - public string AndroidSdkPath { get; set; } - - public string JavaSdkPath { get; set; } - - public string MaxJdkVersion { get; set; } - - [Output] - public string JavaSdkDirectory { get; set; } - - public override bool Execute () - { - var logger = CreateTaskLogger (this); - var androidSdk = new AndroidSdkInfo (logger, AndroidSdkPath, AndroidNdkPath, JavaSdkPath); - try { - Log.LogMessage (MessageImportance.Low, $" {nameof (androidSdk.JavaSdkPath)}: {androidSdk.JavaSdkPath}"); - - Version maxVersion; - if (string.IsNullOrEmpty (MaxJdkVersion)) { - maxVersion = new Version ("8.0"); - } else { - maxVersion = new Version (MaxJdkVersion); - } - - var defaultJdk = new [] { new Tools.JdkInfo (androidSdk.JavaSdkPath) }; - var jdk = defaultJdk.Concat (Tools.JdkInfo.GetKnownSystemJdkInfos (logger)) - .Where (j => maxVersion != null ? j.Version <= maxVersion : true) - .Where (j => j.IncludePath.Any ()) - .FirstOrDefault (); - - if (jdk == null) { - Log.LogError ($"Could not determine a valid JavaSdkPath, `{androidSdk.JavaSdkPath}` was not compatible with the .NET for Android build."); - return false; - } else { - Log.LogMessage (MessageImportance.Low, $" {nameof (jdk.HomePath)}: {jdk.HomePath}"); - } - - var includes = new List (jdk.IncludePath); - var includeXmlTags = new StringBuilder (); - foreach (var include in includes) { - includeXmlTags.AppendLine ($""); - } - - Directory.CreateDirectory (Path.GetDirectoryName (Output.ItemSpec)); - File.WriteAllText (Output.ItemSpec, $@" - - - - {jdk.JdkJvmPath} - - - {includeXmlTags} - - - - - {jdk.JavacPath} - {jdk.JarPath} - -"); - - JavaSdkDirectory = jdk.HomePath; - Log.LogMessage (MessageImportance.Low, $" [Output] {nameof (JavaSdkDirectory)}: {JavaSdkDirectory}"); - - return !Log.HasLoggedErrors; - } - finally { - } - } - - internal static Action CreateTaskLogger (Task task) - { - Action logger = (level, value) => { - switch (level) { - case TraceLevel.Error: - task.Log.LogError (value); - break; - case TraceLevel.Warning: - task.Log.LogWarning (value); - break; - default: - task.Log.LogMessage (MessageImportance.Low, "{0}", value); - break; - } - }; - return logger; - } - } -} diff --git a/build-tools/automation/azure-pipelines.yaml b/build-tools/automation/azure-pipelines.yaml index d7406c1f7c0..6f723944fa5 100644 --- a/build-tools/automation/azure-pipelines.yaml +++ b/build-tools/automation/azure-pipelines.yaml @@ -395,9 +395,9 @@ extends: destinationPath: $(Build.StagingDirectory)\nuget-signed - task: DotNetCoreCLI@2 - displayName: build Xamarin.Android.Tools.BootstrapTasks.sln + displayName: build Xamarin.Android.Tools.BootstrapTasks.csproj inputs: - projects: $(System.DefaultWorkingDirectory)\Xamarin.Android.BootstrapTasks.sln + projects: $(System.DefaultWorkingDirectory)\build-tools\Xamarin.Android.Tools.BootstrapTasks\Xamarin.Android.Tools.BootstrapTasks.csproj arguments: -c $(XA.Build.Configuration) -bl:$(System.DefaultWorkingDirectory)\bin\Build$(XA.Build.Configuration)\bootstrap.binlog condition: and(succeeded(), eq('${{ parameters.pushXAPackagesToMaestro }}', 'true')) diff --git a/build-tools/scripts/Prepare.proj b/build-tools/scripts/Prepare.proj new file mode 100644 index 00000000000..898b085a72e --- /dev/null +++ b/build-tools/scripts/Prepare.proj @@ -0,0 +1,26 @@ + + + + <_TopDir>$(MSBuildThisFileDirectory)..\.. + + + + + + + + + + + diff --git a/build-tools/scripts/PrepareWindows.targets b/build-tools/scripts/PrepareWindows.targets index f4d0954e0ed..cf6389bb128 100644 --- a/build-tools/scripts/PrepareWindows.targets +++ b/build-tools/scripts/PrepareWindows.targets @@ -18,9 +18,10 @@ Text="The specified `%24(AndroidToolchainDirectory)` '$(AndroidToolchainDirectory)' contains a space. Android NDK commands do not support this. Please create a Configuration.Override.props file that sets the AndroidToolchainDirectory property to a different path." Condition="$(AndroidToolchainDirectory.Contains (' '))" /> - - - - + From 9aa7690a0046b526d2fe7b692ce83bb407a8e32f Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 13 Jul 2026 15:50:48 -0500 Subject: [PATCH 2/5] [build] Remove the Windows Prepare wrapper Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 813d3d45-07e0-428e-83db-2ddec4e6fbd4 --- before.Xamarin.Android.sln.targets | 3 +-- build-tools/scripts/Prepare.proj | 14 ++++++++++- build-tools/scripts/PrepareWindows.targets | 27 ---------------------- 3 files changed, 14 insertions(+), 30 deletions(-) delete mode 100644 build-tools/scripts/PrepareWindows.targets diff --git a/before.Xamarin.Android.sln.targets b/before.Xamarin.Android.sln.targets index 2df7f564816..6aaaa1674a1 100644 --- a/before.Xamarin.Android.sln.targets +++ b/before.Xamarin.Android.sln.targets @@ -1,7 +1,6 @@ - + - \ No newline at end of file diff --git a/build-tools/scripts/Prepare.proj b/build-tools/scripts/Prepare.proj index 898b085a72e..f2622ff1313 100644 --- a/build-tools/scripts/Prepare.proj +++ b/build-tools/scripts/Prepare.proj @@ -6,7 +6,19 @@ - + + + + + + - - - - Debug - <_TopDir>$(MSBuildThisFileDirectory)..\.. - - - - - - - - - - From 588d23be0c840c4b567c82166af966c314dd6bc1 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Mon, 13 Jul 2026 16:07:20 -0500 Subject: [PATCH 3/5] [build] Report missing stable Android API metadata Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 813d3d45-07e0-428e-83db-2ddec4e6fbd4 --- .../GenerateSupportedPlatforms.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs index ca309e3a801..7420f89ca5d 100644 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs +++ b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs @@ -44,10 +44,15 @@ public override bool Execute () var minVersion = ToVersion (MinimumApiLevel); var targetVersion = ToVersion (TargetApiLevel); var versions = AndroidApiInfo.Select (ToAndroidVersion).ToArray (); - var maxStableVersion = versions + var stableVersions = versions .Where (version => version.Stable) .OrderBy (version => version.TargetFrameworkVersion) - .Last (); + .ToArray (); + if (stableVersions.Length == 0) { + Log.LogError ("No @(AndroidApiInfo) items are marked as stable."); + return false; + } + var maxStableVersion = stableVersions [stableVersions.Length - 1]; var targetApiLevel = targetVersion != null && targetVersion.Major > 0 ? targetVersion : maxStableVersion.VersionCodeFull; From 35bf9a90a4234e2de60cba2590e4162f9a9e2824 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 14 Jul 2026 10:43:52 -0500 Subject: [PATCH 4/5] Apply suggestion from @simonrozsival MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Šimon Rozsíval --- .../GenerateSupportedPlatforms.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs index 7420f89ca5d..3719163b3cf 100644 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs +++ b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs @@ -44,15 +44,13 @@ public override bool Execute () var minVersion = ToVersion (MinimumApiLevel); var targetVersion = ToVersion (TargetApiLevel); var versions = AndroidApiInfo.Select (ToAndroidVersion).ToArray (); - var stableVersions = versions + var maxStableVersion = versions .Where (version => version.Stable) - .OrderBy (version => version.TargetFrameworkVersion) - .ToArray (); - if (stableVersions.Length == 0) { + .MaxBy (version => version.TargetFrameworkVersion); + if (maxStableVersion is null) { Log.LogError ("No @(AndroidApiInfo) items are marked as stable."); return false; } - var maxStableVersion = stableVersions [stableVersions.Length - 1]; var targetApiLevel = targetVersion != null && targetVersion.Major > 0 ? targetVersion : maxStableVersion.VersionCodeFull; From 609c0bc75eaf466412eccb148e0e449576a2e99e Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 15 Jul 2026 10:56:04 -0500 Subject: [PATCH 5/5] [build] Check stable API items before MaxBy Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 813d3d45-07e0-428e-83db-2ddec4e6fbd4 --- .../GenerateSupportedPlatforms.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs index 3719163b3cf..805e5c445e9 100644 --- a/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs +++ b/build-tools/Xamarin.Android.Tools.BootstrapTasks/Xamarin.Android.Tools.BootstrapTasks/GenerateSupportedPlatforms.cs @@ -44,13 +44,14 @@ public override bool Execute () var minVersion = ToVersion (MinimumApiLevel); var targetVersion = ToVersion (TargetApiLevel); var versions = AndroidApiInfo.Select (ToAndroidVersion).ToArray (); - var maxStableVersion = versions + var stableVersions = versions .Where (version => version.Stable) - .MaxBy (version => version.TargetFrameworkVersion); - if (maxStableVersion is null) { + .ToArray (); + if (stableVersions.Length == 0) { Log.LogError ("No @(AndroidApiInfo) items are marked as stable."); return false; } + var maxStableVersion = stableVersions.MaxBy (version => version.TargetFrameworkVersion); var targetApiLevel = targetVersion != null && targetVersion.Major > 0 ? targetVersion : maxStableVersion.VersionCodeFull;