Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Xamarin.Android.Build.Tasks] XA4214, XA4215 in GenerateJavaStubs (#2349
) Fixes: #2266 Context: #1560 Java type names are not managed type names, and vice versa; there is a *mapping* between them, e.g. the Java class`java.lang.Object` is mapped (bound) as the managed type `Java.Lang.Object`. Ideally, there would be a 1:1 mapping between these names, e.g. `java.lang.Object` is *always* mapped to `Java.Lang.Object`, but *this is not required*, and occasionally there are "duplicate mappings"; for example, the Java class `java.util.HashSet` is mapped to `Java.Util.HashSet`, `Android.Runtime.JavaSet`, *and* `Android.Runtime.JavaSet<T>`: namespace Java.Util { [Register ("java/util/HashSet", DoNotGenerateAcw=true, ApiSince = 1)] public partial class HashSet {} } namespace Android.Runtime { [Register ("java/util/HashSet", DoNotGenerateAcw=true)] public partial class JavaSet {} [Register ("java/util/HashSet", DoNotGenerateAcw=true)] public partial class JavaSet<T> : JavaSet, ICollection<T> {} } Sometimes such 1:N mapping is acceptable, and sometimes it isn't. When is it acceptable? When Java types *are not generated*, as above. The above results in an *ambiguity* within `Java.Lang.Object.GetObject()` when given an instance of type `java.util.HashSet` -- which managed type should be used? -- but the ambiguity problems can be reduced by using `Object.GetObject<T>()` and various other means. When is it *not* acceptable? When two managed types would result in the same Java type name. For example: namespace Example { [Register ("type/Collision")] public partial class Bad1 : Java.Lang.Object {} [Register ("type/Collision")] public partial class Bad2 : Java.Lang.Object {} } Attempting to build an App project containing such a set of types would result in build errors: error : Duplicate Java type found! Mappings between managed types and Java types must be unique. First Type: 'Example.Bad1, Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'; Second Type: 'Example.Bad2, Assembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' (This doesn't happen with `Java.Util.HashSet` and `Android.Runtime.JavaSet` because they use `[Register(DoNotGenerateAcw=true)]`.) There are three issues that need to be addressed: 1. The error and warning messages do not contain codes or associated documentation. 2. The error and warning messages are not *actionable*. 3. The error checking is occasionally missed. (3) is a problem: Consider if we have two assemblies with the same Java name and same managed name: // This file present in two seprate assemblies namespace Example { [Register ("example.EmptyClass")] public class EmptyClass : Java.Lang.Object {} } Building such an app will result in an ambiguity warning: warning : Duplicate managed type found! Mappings between managed types and Java types must be unique. First Type: 'Example.EmptyClass, Assembly1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'; Second Type: 'Example.EmptyClass, Assembly2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. However, this should *also* elicit an *error* because of the Java- side type name collision, but the error isn't emitted! Fix these three issues: 1. Emit an XA4214 warning when identical managed types in different assemblies are found, such as `Example.EmptyClass`. It's only a warning because certain constructs such as `<fragment type="Example.EmptyClass, Assembly1">` support assembly-qualified names. 2. Emit an XA4215 error when identical Java types are encountered. 3. Improve the messages to be actionable. 4. Add documentation for XA4214 and XA4215. 5. Fix the error checking so that if an XA4214 warning is generated, the XA4215 error condition is *also* checked. The `Example.EmptyClass` scenario will result in warning XA4214: warning XA4214: The managed type `Example.EmptyClass` exists in multiple assemblies: Assembly1, Assembly2. Please refactor the managed type names in these assemblies so that they are not identical. warning XA4214: References to the type `Example.EmptyClass` will refer to `Example.EmptyClass, Assembly1`. The `Example.EmptyClass` scenario will also result in error XA4215: error XA4215: The Jvaa type `example.EmptyClass` is generated by more than one managed type. Please change the [Register] attribute so that the same Java type is not emitted. error XA4215: `example.EmptyClass` generated by: Example.EmptyClass, Assembly1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null error XA4215: `example.EmptyClass` generated by: Example.EmptyClass, Assembly2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null A possible future enhancement could be to move the warning into the `<ConvertCustomView/>` task so that it only appears when a managed type name from an Android resource file matches more than one line in the `acw-map`. That way, if a user wants to solve the issue by using `[Register]` attributes or assembly-qualified type names rather than renaming the managed types, the build process will automatically stop showing the warning.
- Loading branch information