Skip to content

Commit

Permalink
[Xamarin.Android.Build.Tasks] Fix XA4215 if AssemblyName is the same (#…
Browse files Browse the repository at this point in the history
…7477)

Fixes: #7473

Context: xamarin/monodroid@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 `<GenerateJavaStubs/>` 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.
  • Loading branch information
dellis1972 authored Nov 29, 2022
1 parent 8e8439f commit 929e701
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 8 deletions.
19 changes: 11 additions & 8 deletions src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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<string> { 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<string> { 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<string> { 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<string> { conflict.GetAssemblyQualifiedName (cache) });
list.Add (type.GetAssemblyQualifiedName (cache));
success = false;
}
hasConflict = true;
}
if (!hasConflict) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
}
}

0 comments on commit 929e701

Please sign in to comment.