From 929e7012410233e6814af369db582f238ba185ad Mon Sep 17 00:00:00 2001 From: Dean Ellis Date: Tue, 29 Nov 2022 16:33:59 +0000 Subject: [PATCH] [Xamarin.Android.Build.Tasks] Fix XA4215 if AssemblyName is the same (#7477) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/xamarin/xamarin-android/issues/7473 Context: https://github.com/xamarin/monodroid/commit/4c697e5a6b Context: https://github.com/dotnet/runtime/blob/27c19c31f574375fd1651207b2dc28d39fe1225c/src/libraries/System.Private.CoreLib/src/ILLink/ILLink.Substitutions.64bit.xml With the .NET 6+ build system, it is possible for an AnyCPU assembly to become a *set* of assemblies, one per `$(RuntimeIdentifiers)` value. In particular, the linker will now *inline* calls to `System.IntPtr.get_Size()`, replacing them with the appropriate architecture-specific value. This means that a single assembly that uses the `IntPtr.Size` property could become *4* assemblies, one each for android-arm, android-arm64, android-x86, and android-x64. Furthermore, each of these assemblies will have a different MVID. This is "fine", until the assembly contains a `Java.Lang.Object` subclass that needs a Java Callable Wrapper generated for it, at which point the `` task starts emitting XA4214 warnings and XA4215 *errors*: warning XA4214: The managed type `Microsoft.UI.Xaml.Controls.AnimatedIcon` exists in multiple assemblies: Uno.UI, Uno.UI, Uno.UI, Uno.UI. Please refactor the managed type names in these assemblies so that they are not identical. error XA4215: The Java type `crc64a5a37c43dff01024.GridViewHeaderItem` is generated by more than one managed type. Please change the [Register] attribute so that the same Java type is not emitted. **Workaround**: Build with only a single `$(RuntimeIdentifier)`: dotnet build -p:RuntimeIdentifier=android-arm64 … Fix this scenario by updating the XA4214 warning and XA4215 error checks to verify that the module name has changed; if the module name is the same, then it's not a duplicate type. --- .../Tasks/GenerateJavaStubs.cs | 19 ++++++----- .../Tasks/LinkerTests.cs | 34 +++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs index 4053a0f9b1e..d82c2f10b00 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs @@ -217,7 +217,6 @@ void Run (DirectoryAssemblyResolver res, bool useMarshalMethods) if ((!useMarshalMethods && !userAssemblies.ContainsKey (td.Module.Assembly.Name.Name)) || JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (td, cache)) { continue; } - javaTypes.Add (td); } @@ -283,16 +282,20 @@ void Run (DirectoryAssemblyResolver res, bool useMarshalMethods) TypeDefinition conflict; bool hasConflict = false; if (managed.TryGetValue (managedKey, out conflict)) { - if (!managedConflicts.TryGetValue (managedKey, out var list)) - managedConflicts.Add (managedKey, list = new List { conflict.GetPartialAssemblyName (cache) }); - list.Add (type.GetPartialAssemblyName (cache)); + if (!conflict.Module.Name.Equals (type.Module.Name)) { + if (!managedConflicts.TryGetValue (managedKey, out var list)) + managedConflicts.Add (managedKey, list = new List { conflict.GetPartialAssemblyName (cache) }); + list.Add (type.GetPartialAssemblyName (cache)); + } hasConflict = true; } if (java.TryGetValue (javaKey, out conflict)) { - if (!javaConflicts.TryGetValue (javaKey, out var list)) - javaConflicts.Add (javaKey, list = new List { conflict.GetAssemblyQualifiedName (cache) }); - list.Add (type.GetAssemblyQualifiedName (cache)); - success = false; + if (!conflict.Module.Name.Equals (type.Module.Name)) { + if (!javaConflicts.TryGetValue (javaKey, out var list)) + javaConflicts.Add (javaKey, list = new List { conflict.GetAssemblyQualifiedName (cache) }); + list.Add (type.GetAssemblyQualifiedName (cache)); + success = false; + } hasConflict = true; } if (!hasConflict) { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs index 530a3de0f07..0a630a47619 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs @@ -509,5 +509,39 @@ public void AndroidUseNegotiateAuthentication ([Values (true, false, null)] bool } } } + + [Test] + public void DoNotErrorOnPerArchJavaTypeDuplicates () + { + if (!Builder.UseDotNet) + Assert.Ignore ("Test only valid on .NET"); + + var path = Path.Combine (Root, "temp", TestName); + var lib = new XamarinAndroidLibraryProject { IsRelease = true, ProjectName = "Lib1" }; + lib.SetProperty ("IsTrimmable", "true"); + lib.Sources.Add (new BuildItem.Source ("Library1.cs") { + TextContent = () => @" +namespace Lib1; +public class Library1 : Java.Lang.Object { + private static bool Is64Bits = IntPtr.Size >= 8; + + public static bool Is64 () { + return Is64Bits; + } +}", + }); + var proj = new XamarinAndroidApplicationProject { IsRelease = true, ProjectName = "App1" }; + proj.References.Add(new BuildItem.ProjectReference (Path.Combine ("..", "Lib1", "Lib1.csproj"), "Lib1")); + proj.MainActivity = proj.DefaultMainActivity.Replace ( + "base.OnCreate (bundle);", + "base.OnCreate (bundle);\n" + + "if (Lib1.Library1.Is64 ()) Console.WriteLine (\"Hello World!\");"); + + + using var lb = CreateDllBuilder (Path.Combine (path, "Lib1")); + using var b = CreateApkBuilder (Path.Combine (path, "App1")); + Assert.IsTrue (lb.Build (lib), "build should have succeeded."); + Assert.IsTrue (b.Build (proj), "build should have succeeded."); + } } }