From 427857fc6b919bd71ce5fef0c2f68afb95cbb969 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 28 May 2026 08:46:06 -0500 Subject: [PATCH 1/4] [Xamarin.Android.Build.Tasks] Fix _CreateAar race under parallel build Fixes: https://github.com/dotnet/android/issues/11514 Under parallel build (`-m`), library projects with `true` could fail with: error XARLP7024: System.IO.IOException: The process cannot access the file '.../.aar' because it is being used by another process. at Microsoft.Android.Build.Tasks.Files.HashFile(...) at Xamarin.Android.Tasks.ResolveLibraryProjectImports.Extract(...) `_CreateAar` was being scheduled up to *three* times for a single library build, on two distinct MSBuild project instances: * Once from `BuildDependsOn`. * Once from `_UpdateAndroidResourcesDependsOn` on the regular `Build` chain. * Once more from `_UpdateAndroidResourcesDependsOn` on a Pack-dispatched project instance entered via `_GetFrameworkAssemblyReferences`. Because that instance has different global properties (NuGet pack's per-TFM inner-target dispatch), MSBuild does not dedupe it and may run it on a fresh worker node, opening a write/write or write/read race against consumers that read the `.aar` via `Files.HashFile`. `_UpdateAndroidResources` is the aapt2/resource-designer step; producing the publish artifact (`.aar`) is unrelated. The AAR is still produced because `_CreateAar` remains in `BuildDependsOn` for non-application projects, and Pack depends on `Build`, so a normal build and `dotnet pack` both still create it. Removing `_CreateAar` from `_UpdateAndroidResourcesDependsOn` collapses the count to a single invocation per build, eliminating the race window and the redundant AAR rewrites. Added a regression test in `IncrementalBuildTest` that builds a library with `GeneratePackageOnBuild=true` at `LoggerVerbosity.Detailed` and asserts that `Target "_CreateAar" in file ...` appears exactly once in the build log. Verified against the repro project on the issue: the count drops from 3 to 1 with the fix applied. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Microsoft.Android.Sdk.BuildOrder.targets | 1 - .../IncrementalBuildTest.cs | 23 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets index e8ca1c11d39..9dd944839e9 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets @@ -176,7 +176,6 @@ properties that determine build ordering. _CheckForDeletedResourceFile; _ComputeAndroidResourcePaths; _UpdateAndroidResgen; - _CreateAar; _SetupMSBuildAllProjects; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs index c13c7e400ca..d236d9a72f8 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs @@ -742,6 +742,29 @@ public void LibraryProjectTargetsDoNotBreak ([Values] AndroidRuntime runtime) } } + [Test] + public void CreateAarRunsOnceWithGeneratePackageOnBuild () + { + // https://github.com/dotnet/android/issues/11514 + // `_CreateAar` was being invoked twice for a single library build when + // `GeneratePackageOnBuild=true`: once via `BuildDependsOn`, and again via + // NuGet pack's per-TFM dispatch which entered the project at + // `_GetFrameworkAssemblyReferences`. That target transitively pulled in + // `UpdateAndroidResources` -> `_UpdateAndroidResources` -> `_CreateAar` + // via `_UpdateAndroidResourcesDependsOn`. The second invocation can race + // the first writer when parallel builds (-m) consumers concurrently read + // the .aar via `Files.HashFile`, producing `XARLP7024`. + var proj = new XamarinAndroidLibraryProject (); + proj.SetProperty ("GeneratePackageOnBuild", "true"); + using (var b = CreateDllBuilder ()) { + b.Verbosity = LoggerVerbosity.Detailed; + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + int count = b.LastBuildOutput.Count (l => l.Contains ("Target \"_CreateAar\" in file")); + Assert.AreEqual (1, count, + $"`_CreateAar` should only execute once per build of a library project; was {count}."); + } + } + [Test] public void ManifestMergerIncremental ([Values] AndroidRuntime runtime) { From 3c9b3fb9af0c6c25a500c483ab77c0e42b40f841 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 28 May 2026 11:22:09 -0500 Subject: [PATCH 2/4] [Xamarin.Android.Build.Tasks] Dedup JarFiles in CreateAar The `CreateAar` task writes each item in `@(JarFiles)` into the output `.aar` archive at `libs/.jar`. Today nothing protects against the same archive path being written twice: e.g. `_CompileBindingJava` adds the binding classes zip to both `@(EmbeddedJar)` and `@(AndroidJavaLibrary)`, and the input groups can otherwise accumulate duplicates across target invocations. Calling `aar.AddStream` repeatedly with the same path produces multiple zip entries with identical names, which the `DotNetPack` test now catches as `found 3` jars where only 2 unique paths exist. Track archive paths in a `HashSet` and skip duplicates. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs index a984bc643ba..70befedd912 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs @@ -99,6 +99,7 @@ public override bool RunTask () } } if (JarFiles != null) { + var seenJarPaths = new HashSet (StringComparer.Ordinal); foreach (var jar in JarFiles) { var pack = jar.GetMetadata ("Pack"); if (string.Equals (pack, "false", StringComparison.OrdinalIgnoreCase)) { @@ -106,6 +107,14 @@ public override bool RunTask () continue; } var archivePath = $"libs/{GetHashedFileName (jar)}.jar"; + // @(AndroidJavaLibrary) and @(EmbeddedJar) can overlap (e.g. the binding + // classes zip is added to both by _CompileBindingJava), and either group + // can accumulate duplicates across target invocations. Skip duplicates + // rather than letting libzipsharp add multiple entries with the same name. + if (!seenJarPaths.Add (archivePath)) { + Log.LogDebugMessage ($"Skipping duplicate jar '{jar.ItemSpec}' (archive path '{archivePath}')"); + continue; + } aar.AddStream (File.OpenRead (jar.ItemSpec), archivePath); existingEntries.Remove (archivePath); } From f5e38e6cb337eb6e42c79548fcad5f748daf4095 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 28 May 2026 11:30:54 -0500 Subject: [PATCH 3/4] Revert "[Xamarin.Android.Build.Tasks] Dedup JarFiles in CreateAar" This reverts commit 3c9b3fb9af0c6c25a500c483ab77c0e42b40f841. --- src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs index 70befedd912..a984bc643ba 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateAar.cs @@ -99,7 +99,6 @@ public override bool RunTask () } } if (JarFiles != null) { - var seenJarPaths = new HashSet (StringComparer.Ordinal); foreach (var jar in JarFiles) { var pack = jar.GetMetadata ("Pack"); if (string.Equals (pack, "false", StringComparison.OrdinalIgnoreCase)) { @@ -107,14 +106,6 @@ public override bool RunTask () continue; } var archivePath = $"libs/{GetHashedFileName (jar)}.jar"; - // @(AndroidJavaLibrary) and @(EmbeddedJar) can overlap (e.g. the binding - // classes zip is added to both by _CompileBindingJava), and either group - // can accumulate duplicates across target invocations. Skip duplicates - // rather than letting libzipsharp add multiple entries with the same name. - if (!seenJarPaths.Add (archivePath)) { - Log.LogDebugMessage ($"Skipping duplicate jar '{jar.ItemSpec}' (archive path '{archivePath}')"); - continue; - } aar.AddStream (File.OpenRead (jar.ItemSpec), archivePath); existingEntries.Remove (archivePath); } From a20b8a45dc1a56fc9576ca14e0eed3eb57fa4e8d Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 28 May 2026 11:35:48 -0500 Subject: [PATCH 4/4] [Xamarin.Android.Build.Tasks] Make _IncludeAarInNuGetPackage depend on _CreateAar After removing _CreateAar from _UpdateAndroidResourcesDependsOn (to fix the parallel-build race in #11514), the DotNetPack test started failing because the .aar inside the .nupkg contained an extra .jar. Previously, dotnet pack''s inner _GetFrameworkAssemblyReferences evaluation implicitly re-ran _CreateAar through the resource chain (see #10270). In that inner evaluation _CompileBindingJava had not run, so the binding classes zip was not in @(EmbeddedJar)/@(AndroidJavaLibrary), and the resulting .aar (which overwrote the outer Build evaluation''s .aar) did not contain it. The .nupkg picked up that smaller .aar. Removing _CreateAar from the resource chain eliminated that overwrite, so the outer Build evaluation''s .aar (with the binding zip included in both EmbeddedJar and AndroidJavaLibrary) ended up in the .nupkg. Make _IncludeAarInNuGetPackage explicitly depend on _CreateAar so the inner per-TFM Pack evaluation still produces the .aar layout intended for the .nupkg, without relying on the resource-chain side effect that caused the original race. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../targets/Microsoft.Android.Sdk.BuildOrder.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets index 9dd944839e9..7635a1556c4 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets @@ -245,6 +245,7 @@ properties that determine build ordering. <_IncludeAarInNuGetPackageDependsOn> _BuildAndroidGradleProjects; _CategorizeAndroidLibraries; + _CreateAar;