-
Notifications
You must be signed in to change notification settings - Fork 564
[build] Use $(AndroidPlatformId) when appropriate
#4356
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Perhaps unsurprisingly, it doesn't build: https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=3524234&view=results Because of the API Compat checks: Looks like the |
|
So, "funny thing", the $ diff -u ~/android-toolchain/sdk/platforms/android-29/data/api-versions.xml ~/android-toolchain/sdk/platforms/android-R/data/api-versions.xml
...
- <method name="trimToSize()V"/>
...
$ diff -u ~/android-toolchain/sdk/platforms/android-29/data/api-versions.xml ~/android-toolchain/sdk/platforms/android-R/data/api-versions.xml | grep '^-' | wc -l
312Related: #4012 (comment) 😭 SO! We could update (I still fear we'll need to do the "use |
I'm wrong/wasn't interpreting things correctly. They are being used. I am thus further confused by Background: API-R adds These do not have the same return type: public class CellInfo {
public CellIdentity getCellIdentity();
}
public class CellInfoCdma extends CellInfo {
public CellIdentityCdma getCellIdentity();
}This is fine in Java because of covariant return types. This isn't fine in C#, which is why we have those This in turn means that the property types changed, which is a compatibility break: API-29: namespace Android.Telephony {
public sealed partial class CellInfoCdma : Android.Telephony.CellInfo, Android.OS.IParcelable {
public unsafe Android.Telephony.CellIdentityCdma CellIdentity {
...
}
}API-R: namespace Android.Telephony {
public sealed partial class CellInfoCdma : Android.Telephony.CellInfo, Android.OS.IParcelable {
public unsafe Android.Telephony.CellIdentity CellIdentity {
...
}
}In a One Mono.Android.dll World Order...I'm not sure how we actually support this. We'll need to figure that out later. In the meantime, we have a property type change between API-29/v10.0 and API-R/v10.0.99! This is clearly a compatibility break! ...and it's exactly this type change which the Yet Why aren't the altered property types of |
|
Additionally, the This says to me that Yet it does exist: $ monodis --method bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0.99/Mono.Android.dll
...
########## Android.Telephony.CellInfoGsm
44223: default class Android.OS.IParcelableCreator get_Creator () (param: 51479 impl_flags: cil managed )
44224: default native int get_class_ref () (param: 51479 impl_flags: cil managed )
44225: failed to parse due to Could not load file or assembly 'Java.Interop, Version=0.1.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065' or one of its dependencies.
44226: instance default native int get_ThresholdClass () (param: 51479 impl_flags: cil managed )
44227: instance default class [mscorlib]System.Type get_ThresholdType () (param: 51479 impl_flags: cil managed )
44228: instance default void '.ctor' (native int javaReference, valuetype Android.Runtime.JniHandleOwnership transfer) (param: 51479 impl_flags: cil managed )
44229: instance default class Android.Telephony.CellIdentity get_CellIdentity () (param: 51481 impl_flags: cil managed )
44230: instance default class Android.Telephony.CellSignalStrength get_CellSignalStrength () (param: 51481 impl_flags: cil managed )
|
ff14c04 to
3a1462a
Compare
Context: dotnet/android#4356 (comment) A "funny" thing was found with API-R: the Android SDK `platforms/android-R/data/api-versions.xml` file is *missing* members present in previous API versions, e.g. `java.lang.StringBuilder.trimToSize()` is not mentioned in API-R, while it *is* mentioned in API-29 and the "global" `platform-tools/api/api-versions.xml`. Try to improve sanity for this conundrum by allowing `generator --apiversions` to be specified multiple times, e.g. this diff to apply to xamarin-android: diff --git a/src/Mono.Android/Mono.Android.targets b/src/Mono.Android/Mono.Android.targets index 8735c2ae..baae759b 100644 --- a/src/Mono.Android/Mono.Android.targets +++ b/src/Mono.Android/Mono.Android.targets @@ -78,10 +78,13 @@ Inputs="metadata;enumflags;map.csv;methodmap.csv;$(IntermediateOutputPath)mcw\api.xml" Outputs="$(IntermediateOutputPath)mcw\Mono.Android.projitems"> <MakeDir Directories="$(IntermediateOutputPath)mcw" /> - <PropertyGroup> - <_ApiVersions Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')">"$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml"</_ApiVersions> - <_ApiVersions Condition="'$(_ApiVersions)'==''">"$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml"</_ApiVersions> - </PropertyGroup> + <ItemGroup> + <_ApiVersion Include="$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml" /> + <_ApiVersion + Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')" + Include="$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml" + /> + </ItemGroup> <PropertyGroup> <Generator>"$(XAInstallPrefix)xbuild\Xamarin\Android\generator.exe"</Generator> <_GenFlags>--public --product-version=7</_GenFlags> @@ -91,7 +94,7 @@ <_Fixup>--fixup=metadata</_Fixup> <_Enums1>--preserve-enums --enumflags=enumflags --enumfields=map.csv --enummethods=methodmap.csv</_Enums1> <_Enums2>--enummetadata=$(IntermediateOutputPath)mcw\enummetadata</_Enums2> - <_Versions>--apiversions=$(_ApiVersions)</_Versions> + <_Versions>@(_ApiVersion->'--apiversions="%(Identity)"', ' ')</_Versions> <_Annotations>--annotations="$(AndroidSdkDirectory)\platform-tools\api\annotations.zip"</_Annotations> <_Assembly>--assembly="Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"</_Assembly> <_TypeMap>--type-map-report=$(IntermediateOutputPath)mcw\type-mapping.txt</_TypeMap> The `generator --apiversions` files are applied in order, with *later* versions overriding/replacing earlier versions. This is why `platform-tools/api/api-versions.xml` is present *first*.
Context: dotnet/android#4356 (comment) A "funny" thing was found with API-R: the Android SDK `platforms/android-R/data/api-versions.xml` file is *missing* members present in previous API versions, e.g. `java.lang.StringBuilder.trimToSize()` is not mentioned in API-R, while it *is* mentioned in API-29 and the "global" `platform-tools/api/api-versions.xml`. Try to improve sanity for this conundrum by allowing `generator --apiversions` to be specified multiple times, e.g. this diff to apply to xamarin-android: diff --git a/src/Mono.Android/Mono.Android.targets b/src/Mono.Android/Mono.Android.targets index 8735c2ae..baae759b 100644 --- a/src/Mono.Android/Mono.Android.targets +++ b/src/Mono.Android/Mono.Android.targets @@ -78,10 +78,13 @@ Inputs="metadata;enumflags;map.csv;methodmap.csv;$(IntermediateOutputPath)mcw\api.xml" Outputs="$(IntermediateOutputPath)mcw\Mono.Android.projitems"> <MakeDir Directories="$(IntermediateOutputPath)mcw" /> - <PropertyGroup> - <_ApiVersions Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')">"$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml"</_ApiVersions> - <_ApiVersions Condition="'$(_ApiVersions)'==''">"$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml"</_ApiVersions> - </PropertyGroup> + <ItemGroup> + <_ApiVersion Include="$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml" /> + <_ApiVersion + Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')" + Include="$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml" + /> + </ItemGroup> <PropertyGroup> <Generator>"$(XAInstallPrefix)xbuild\Xamarin\Android\generator.exe"</Generator> <_GenFlags>--public --product-version=7</_GenFlags> @@ -91,7 +94,7 @@ <_Fixup>--fixup=metadata</_Fixup> <_Enums1>--preserve-enums --enumflags=enumflags --enumfields=map.csv --enummethods=methodmap.csv</_Enums1> <_Enums2>--enummetadata=$(IntermediateOutputPath)mcw\enummetadata</_Enums2> - <_Versions>--apiversions=$(_ApiVersions)</_Versions> + <_Versions>@(_ApiVersion->'--apiversions="%(Identity)"', ' ')</_Versions> <_Annotations>--annotations="$(AndroidSdkDirectory)\platform-tools\api\annotations.zip"</_Annotations> <_Assembly>--assembly="Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"</_Assembly> <_TypeMap>--type-map-report=$(IntermediateOutputPath)mcw\type-mapping.txt</_TypeMap> The `generator --apiversions` files are applied in order, with *later* versions overriding/replacing earlier versions. This is why `platform-tools/api/api-versions.xml` is present *first*.
|
A fix to allow multiple Relevant patch to apply here once that exists: diff --git a/src/Mono.Android/Mono.Android.targets b/src/Mono.Android/Mono.Android.targets
index 8735c2ae..baae759b 100644
--- a/src/Mono.Android/Mono.Android.targets
+++ b/src/Mono.Android/Mono.Android.targets
@@ -78,10 +78,13 @@
Inputs="metadata;enumflags;map.csv;methodmap.csv;$(IntermediateOutputPath)mcw\api.xml"
Outputs="$(IntermediateOutputPath)mcw\Mono.Android.projitems">
<MakeDir Directories="$(IntermediateOutputPath)mcw" />
- <PropertyGroup>
- <_ApiVersions Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')">"$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml"</_ApiVersions>
- <_ApiVersions Condition="'$(_ApiVersions)'==''">"$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml"</_ApiVersions>
- </PropertyGroup>
+ <ItemGroup>
+ <_ApiVersion Include="$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml" />
+ <_ApiVersion
+ Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')"
+ Include="$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml"
+ />
+ </ItemGroup>
<PropertyGroup>
<Generator>"$(XAInstallPrefix)xbuild\Xamarin\Android\generator.exe"</Generator>
<_GenFlags>--public --product-version=7</_GenFlags>
@@ -91,7 +94,7 @@
<_Fixup>--fixup=metadata</_Fixup>
<_Enums1>--preserve-enums --enumflags=enumflags --enumfields=map.csv --enummethods=methodmap.csv</_Enums1>
<_Enums2>--enummetadata=$(IntermediateOutputPath)mcw\enummetadata</_Enums2>
- <_Versions>--apiversions=$(_ApiVersions)</_Versions>
+ <_Versions>@(_ApiVersion->'--apiversions="%(Identity)"', ' ')</_Versions>
<_Annotations>--annotations="$(AndroidSdkDirectory)\platform-tools\api\annotations.zip"</_Annotations>
<_Assembly>--assembly="Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"</_Assembly>
<_TypeMap>--type-map-report=$(IntermediateOutputPath)mcw\type-mapping.txt</_TypeMap> |
|
Earlier I asked:
Turns Out™, that's the message which is reported when property types change, as also noted earlier. These error messages are so meaningful and useful! |
The issue here is that it does not exist with the same return type, would bbe nice ifthe tool, would report the message showing the name but also the return type. |
Context: dotnet/android#4356 (comment) A "funny" thing was found with API-R: the Android SDK `platforms/android-R/data/api-versions.xml` file is *missing* members present in previous API versions, e.g. `java.lang.StringBuilder.trimToSize()` is not mentioned in API-R, while it *is* mentioned in API-29 and the "global" `platform-tools/api/api-versions.xml`. Try to improve sanity for this conundrum by allowing `generator --apiversions` to be specified multiple times, e.g. this diff to apply to xamarin-android: diff --git a/src/Mono.Android/Mono.Android.targets b/src/Mono.Android/Mono.Android.targets index 8735c2ae..baae759b 100644 --- a/src/Mono.Android/Mono.Android.targets +++ b/src/Mono.Android/Mono.Android.targets @@ -78,10 +78,13 @@ Inputs="metadata;enumflags;map.csv;methodmap.csv;$(IntermediateOutputPath)mcw\api.xml" Outputs="$(IntermediateOutputPath)mcw\Mono.Android.projitems"> <MakeDir Directories="$(IntermediateOutputPath)mcw" /> - <PropertyGroup> - <_ApiVersions Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')">"$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml"</_ApiVersions> - <_ApiVersions Condition="'$(_ApiVersions)'==''">"$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml"</_ApiVersions> - </PropertyGroup> + <ItemGroup> + <_ApiVersion Include="$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml" /> + <_ApiVersion + Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')" + Include="$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml" + /> + </ItemGroup> <PropertyGroup> <Generator>"$(XAInstallPrefix)xbuild\Xamarin\Android\generator.exe"</Generator> <_GenFlags>--public --product-version=7</_GenFlags> @@ -91,7 +94,7 @@ <_Fixup>--fixup=metadata</_Fixup> <_Enums1>--preserve-enums --enumflags=enumflags --enumfields=map.csv --enummethods=methodmap.csv</_Enums1> <_Enums2>--enummetadata=$(IntermediateOutputPath)mcw\enummetadata</_Enums2> - <_Versions>--apiversions=$(_ApiVersions)</_Versions> + <_Versions>@(_ApiVersion->'--apiversions="%(Identity)"', ' ')</_Versions> <_Annotations>--annotations="$(AndroidSdkDirectory)\platform-tools\api\annotations.zip"</_Annotations> <_Assembly>--assembly="Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"</_Assembly> <_TypeMap>--type-map-report=$(IntermediateOutputPath)mcw\type-mapping.txt</_TypeMap> The `generator --apiversions` files are applied in order, with *later* versions overriding/replacing earlier versions. This is why `platform-tools/api/api-versions.xml` is present *first*.
Context: dotnet#4356 Context: 54beb90 Context: a20be39 The use of `Microsoft.DotNet.ApiCompat.exe` added in 07e7477 has one major deficiency: The error messages reported by `Microsoft.DotNet.ApiCompat.exe` are *awful* and borderline useless or misleading. For example, consider commit PR dotnet#4356, which attempts to bring sanity and consistency around `$(AndroidPlatformId)` and `Mono.Android.dll` builds. It contains an API break, which we'll hand wave away and accept for preview release purposes, in which the property type for `Android.Telephony.CellInfoGsm.CellIdentity` changes from `CellIdentityGsm` to `CellIdentity`: // API-29 namespace Android.Telephony { public sealed partial class CellInfoGsm: Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentityGsm CellIdentity { } } // API-R namespace Android.Telephony { public sealed partial class CellInfoGsm : Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentity CellIdentity { } } This is clearly a break. How does `Microsoft.DotNet.ApiCompat.exe` report the breakage? error : MembersMustExist : Member 'Android.Telephony.CellInfoGsm.CellIdentity.get()' does not exist in the implementation but it does exist in the contract. Which is infuriatingly terrible. The message *implies* that `Android.Telephony.CellInfoGsm.get_CellIdentity()` doesn't exist, but it *does* exist. The problem is that the return type changed! Or consider 54beb90, in which we now emit a slew of default interface members within the `Mono.Android.dll` binding, which *should* be API compatible. `Microsoft.DotNet.ApiCompat.exe` complains as well: InterfacesShouldHaveSameMembers : Interface member 'Java.Util.Functions.IUnaryOperator.Identity()' is present in the implementation but not in the contract. What these messages have in common is that they provide no context, lack important types, and in no way suggest how to *fix* the error other than to just ignore it. Overhaul this infrastructure so that crucial context is provided. The context is provided by using "reference assembly source": the [`Microsoft.DotNet.GenAPI.exe` utility][0] can be run on an assembly to generate C# source code that shows the same API but no implementation: namespace Android.Accounts { [Android.Runtime.RegisterAttribute("android/accounts/AbstractAccountAuthenticator", DoNotGenerateAcw=true, ApiSince=5)] public abstract partial class AbstractAccountAuthenticator : Java.Lang.Object { [Android.Runtime.RegisterAttribute("KEY_CUSTOM_TOKEN_EXPIRY", ApiSince=23)] public const string KeyCustomTokenExpiry = "android.accounts.expiry"; [Android.Runtime.RegisterAttribute(".ctor", "(Landroid/content/Context;)V", "")] public AbstractAccountAuthenticator(Android.Content.Context context) { } Update the `src/Mono.Android` build so that after every build, after running `Microsoft.DotNet.ApiCompat.exe` we *also* run `Microsoft.DotNet.GenAPI.exe` on the generated assembly, then run `diff -u` against the recently created assembly and the reference assembly source for the contract assembly. This allows us to get *useful diffs* in the API: Task "Exec" (TaskId:570) Task Parameter:Command=diff -u "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) diff -u "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) --- ../../tests/api-compatibility/reference/Mono.Android.dll.cs 2020-03-05 13:20:59.000000000 -0500 (TaskId:570) +++ /Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs 2020-03-05 13:40:12.000000000 -0500 (TaskId:570) @@ -27,7 +27,7 @@ (TaskId:570) { (TaskId:570) [Android.Runtime.RegisterAttribute("ACCEPT_HANDOVER", ApiSince=28)] (TaskId:570) public const string AcceptHandover = "android.permission.ACCEPT_HANDOVER"; (TaskId:570) - [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION")] (TaskId:570) + [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION", ApiSince=29)] (TaskId:570) (The above is courtesy commmit 4cd2060, which added `RegisterAttribute.ApiSince` on a large number of members.) Finally, how do we update the "contract" `Mono.Android.dll` assembly? Add a new `tests/api-compatibility/api-compatibility.targets` file which contains a `UpdateMonoAndroidContract` target which will update `tests/api-compatibility/reference/Mono.Android.zip` with the contents of a `cil-strip`'d `Mono.Android.dll` and updated "reference assembly source". `Mono.Android.zip` contains a contract from Xamarin.Android 10.2.0.100 for `$(TargetFrameworkVersion)` v10.0. Note: The `diff -u` invocation and `UpdateMonoAndroidContract` targets currently only work on non-Windows platforms, due to the use of the **diff**(1) and **zip**(1L) utilities. [0]: https://github.com/dotnet/arcade/tree/bc4fa8e7149769db4efd466f160417a32b11f0bf/src/Microsoft.DotNet.GenAPI
Context: dotnet#4356 Context: 54beb90 Context: a20be39 The use of `Microsoft.DotNet.ApiCompat.exe` added in 07e7477 has one major deficiency: The error messages reported by `Microsoft.DotNet.ApiCompat.exe` are *awful* and borderline useless or misleading. For example, consider commit PR dotnet#4356, which attempts to bring sanity and consistency around `$(AndroidPlatformId)` and `Mono.Android.dll` builds. It contains an API break, which we'll hand wave away and accept for preview release purposes, in which the property type for `Android.Telephony.CellInfoGsm.CellIdentity` changes from `CellIdentityGsm` to `CellIdentity`: // API-29 namespace Android.Telephony { public sealed partial class CellInfoGsm: Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentityGsm CellIdentity { } } // API-R namespace Android.Telephony { public sealed partial class CellInfoGsm : Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentity CellIdentity { } } This is clearly a break. How does `Microsoft.DotNet.ApiCompat.exe` report the breakage? error : MembersMustExist : Member 'Android.Telephony.CellInfoGsm.CellIdentity.get()' does not exist in the implementation but it does exist in the contract. Which is infuriatingly terrible. The message *implies* that `Android.Telephony.CellInfoGsm.get_CellIdentity()` doesn't exist, but it *does* exist. The problem is that the return type changed! Or consider 54beb90, in which we now emit a slew of default interface members within the `Mono.Android.dll` binding, which *should* be API compatible. `Microsoft.DotNet.ApiCompat.exe` complains as well: InterfacesShouldHaveSameMembers : Interface member 'Java.Util.Functions.IUnaryOperator.Identity()' is present in the implementation but not in the contract. What these messages have in common is that they provide no context, lack important types, and in no way suggest how to *fix* the error other than to just ignore it. Overhaul this infrastructure so that crucial context is provided. The context is provided by using "reference assembly source": the [`Microsoft.DotNet.GenAPI.exe` utility][0] can be run on an assembly to generate C# source code that shows the same API but no implementation: namespace Android.Accounts { [Android.Runtime.RegisterAttribute("android/accounts/AbstractAccountAuthenticator", DoNotGenerateAcw=true, ApiSince=5)] public abstract partial class AbstractAccountAuthenticator : Java.Lang.Object { [Android.Runtime.RegisterAttribute("KEY_CUSTOM_TOKEN_EXPIRY", ApiSince=23)] public const string KeyCustomTokenExpiry = "android.accounts.expiry"; [Android.Runtime.RegisterAttribute(".ctor", "(Landroid/content/Context;)V", "")] public AbstractAccountAuthenticator(Android.Content.Context context) { } Update the `src/Mono.Android` build so that after every build, after running `Microsoft.DotNet.ApiCompat.exe` we *also* run `Microsoft.DotNet.GenAPI.exe` on the generated assembly, then run `diff -u` against the recently created assembly and the reference assembly source for the contract assembly. This allows us to get *useful diffs* in the API: Task "Exec" (TaskId:570) Task Parameter:Command=diff -u "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) diff -u "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) --- ../../tests/api-compatibility/reference/Mono.Android.dll.cs 2020-03-05 13:20:59.000000000 -0500 (TaskId:570) +++ /Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs 2020-03-05 13:40:12.000000000 -0500 (TaskId:570) @@ -27,7 +27,7 @@ (TaskId:570) { (TaskId:570) [Android.Runtime.RegisterAttribute("ACCEPT_HANDOVER", ApiSince=28)] (TaskId:570) public const string AcceptHandover = "android.permission.ACCEPT_HANDOVER"; (TaskId:570) - [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION")] (TaskId:570) + [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION", ApiSince=29)] (TaskId:570) (The above is courtesy commmit 4cd2060, which added `RegisterAttribute.ApiSince` on a large number of members.) Finally, how do we update the "contract" `Mono.Android.dll` assembly? Add a new `tests/api-compatibility/api-compatibility.targets` file which contains a `UpdateMonoAndroidContract` target which will update `tests/api-compatibility/reference/Mono.Android.zip` with the contents of a `cil-strip`'d `Mono.Android.dll` and updated "reference assembly source". `Mono.Android.zip` contains a contract from Xamarin.Android 10.2.0.100 for `$(TargetFrameworkVersion)` v10.0. Note: The `diff -u` invocation and `UpdateMonoAndroidContract` targets currently only work on non-Windows platforms, due to the use of the **diff**(1) and **zip**(1L) utilities. [0]: https://github.com/dotnet/arcade/tree/bc4fa8e7149769db4efd466f160417a32b11f0bf/src/Microsoft.DotNet.GenAPI
3a1462a to
7dd1f3d
Compare
Context: dotnet#4356 Context: 54beb90 Context: a20be39 The use of `Microsoft.DotNet.ApiCompat.exe` added in 07e7477 has one major deficiency: The error messages reported by `Microsoft.DotNet.ApiCompat.exe` are *awful* and borderline useless or misleading. For example, consider commit PR dotnet#4356, which attempts to bring sanity and consistency around `$(AndroidPlatformId)` and `Mono.Android.dll` builds. It contains an API break, which we'll hand wave away and accept for preview release purposes, in which the property type for `Android.Telephony.CellInfoGsm.CellIdentity` changes from `CellIdentityGsm` to `CellIdentity`: // API-29 namespace Android.Telephony { public sealed partial class CellInfoGsm: Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentityGsm CellIdentity { } } // API-R namespace Android.Telephony { public sealed partial class CellInfoGsm : Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentity CellIdentity { } } This is clearly a break. How does `Microsoft.DotNet.ApiCompat.exe` report the breakage? error : MembersMustExist : Member 'Android.Telephony.CellInfoGsm.CellIdentity.get()' does not exist in the implementation but it does exist in the contract. Which is infuriatingly terrible. The message *implies* that `Android.Telephony.CellInfoGsm.get_CellIdentity()` doesn't exist, but it *does* exist. The problem is that the return type changed! Or consider 54beb90, in which we now emit a slew of default interface members within the `Mono.Android.dll` binding, which *should* be API compatible. `Microsoft.DotNet.ApiCompat.exe` complains as well: InterfacesShouldHaveSameMembers : Interface member 'Java.Util.Functions.IUnaryOperator.Identity()' is present in the implementation but not in the contract. What these messages have in common is that they provide no context, lack important types, and in no way suggest how to *fix* the error other than to just ignore it. Overhaul this infrastructure so that crucial context is provided. The context is provided by using "reference assembly source": the [`Microsoft.DotNet.GenAPI.exe` utility][0] can be run on an assembly to generate C# source code that shows the same API but no implementation: namespace Android.Accounts { [Android.Runtime.RegisterAttribute("android/accounts/AbstractAccountAuthenticator", DoNotGenerateAcw=true, ApiSince=5)] public abstract partial class AbstractAccountAuthenticator : Java.Lang.Object { [Android.Runtime.RegisterAttribute("KEY_CUSTOM_TOKEN_EXPIRY", ApiSince=23)] public const string KeyCustomTokenExpiry = "android.accounts.expiry"; [Android.Runtime.RegisterAttribute(".ctor", "(Landroid/content/Context;)V", "")] public AbstractAccountAuthenticator(Android.Content.Context context) { } Update the `src/Mono.Android` build so that after every build, when `Microsoft.DotNet.ApiCompat.exe` fails we *also* run `Microsoft.DotNet.GenAPI.exe` on the generated assembly, then run `git diff -u` against the recently created assembly and the reference assembly source for the contract assembly. This allows us to get *useful diffs* in the API: Task "Exec" (TaskId:570) Task Parameter:Command=git diff --no-index "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) diff -u "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) --- ../../tests/api-compatibility/reference/Mono.Android.dll.cs 2020-03-05 13:20:59.000000000 -0500 (TaskId:570) +++ /Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs 2020-03-05 13:40:12.000000000 -0500 (TaskId:570) @@ -27,7 +27,7 @@ (TaskId:570) { (TaskId:570) [Android.Runtime.RegisterAttribute("ACCEPT_HANDOVER", ApiSince=28)] (TaskId:570) public const string AcceptHandover = "android.permission.ACCEPT_HANDOVER"; (TaskId:570) - [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION")] (TaskId:570) + [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION", ApiSince=29)] (TaskId:570) (The above changes are courtesy commmit 4cd2060, which added `RegisterAttribute.ApiSince` on a large number of members.) Finally, how do we update the "contract" `Mono.Android.dll` assembly? Add a new `tests/api-compatibility/api-compatibility.targets` file which contains a `UpdateMonoAndroidContract` target which will update `tests/api-compatibility/reference/Mono.Android.zip` with the contents of a `cil-strip`'d `Mono.Android.dll` and updated "reference assembly source". `Mono.Android.zip` contains a contract from Xamarin.Android 10.2.0.100 for `$(TargetFrameworkVersion)` v10.0. [0]: https://github.com/dotnet/arcade/tree/bc4fa8e7149769db4efd466f160417a32b11f0bf/src/Microsoft.DotNet.GenAPI
e3a452a to
3a39d22
Compare
Context: 485e39b Context: eb08bb4 ...and doubtless others... Three MSBuild properties control the `android.jar` which is bound and the `$(TargetFrameworkVersion)` of `Mono.Android.dll`: * `$(AndroidApiLevel)`: The API level that is bound. Must be an int. * `$(AndroidFrameworkVersion)`: The `$(TargetFrameworkVersion)` of the generated `Mono.Android.dll`. Must be *mostly* parseable by `System.Version` except with a leading `v`, e.g. `v10.0`. * `$(AndroidPlatformId)`: The "ID" of the API level. *Most* of the time, `$(AndroidApiLevel)` and `$(AndroidPlatformId)` will be *identical*: for API-29, they're both `29`. Where they differ is for new *preview* API levels, such as API-R: `$(AndroidApiLevel)` will be 30, but `$(AndroidPlatformId)` is `R`. The distinction is important because various filesystem paths within the Android SDK use the "id" and *not* the API level when they differ, e.g. the API-R `android.jar` is installed into: $(AndroidSdkDirectory)/platforms/android-R/android.jar We thus need to be *careful* when distinguishing between `$(AndroidApiLevel)` and `$(AndroidPlatformId)`, using the former when an integer is *required*, and using the latter whenever it refers to filesystem paths. Unfortunately, we *haven't* been careful, because these values really only differ for ~4 months out of the year, and for only one `$(TargetFrameworkVersion)` version. Start bringing some sanity...and finding bugs while we do so: `api-xml-adjuster.targets` should use `%(AndroidApiInfo.Id)` and *not* `%(AndroidApiLevel.Level)`, as it references filesystem locations. Consequently, `src/Mono.Android/Profiles/api-30.params.txt` must be renamed to `src/Mono.Android/Profiles/api-R.params.txt` so that it correctly embeds the `$(AndroidPlatformId)` value. `Mono.Android.targets` should likewise use `$(AndroidPlatformId)` and not `$(AndroidApiLevel)` when using filesystem paths from the SDK. For good measure, `Mono.Android.csproj` now overrides `$(IntermediateOutputPath)` to contain `$(AndroidPlatformId)`, because why not (MOAR CONSISTENCY!). These changes, unfortunately, introduce breakage, which will need to be addressed: *Because* API-R was installed into `$(AndroidSdkDirectory)/platforms/android-R`, `api-versions.xml` *was not previously used* because `Mono.Android.targets` was using `$(AndroidApiLevel)`, and `platforms/android-30/data/api-version.xml` does not yet exist. (It will come June! But not now.) As it didn't exist, it hit the fallback path and used `platform-tools/api/api-versions.xml` (4cd2060). You would *think* this wouldn't be a problem, but the API-R `api-versions.xml` is *missing* members relative to platform-tools, resulting in members *missing* `RegisterAttribute.ApiSince` values, which `Microsoft.DotNet.ApiCompat.exe` reports, e.g.: CannotChangeAttribute : Attribute 'Android.Runtime.RegisterAttribute' on 'Java.Lang.StringBuilder.TrimToSize()' changed from '[RegisterAttribute("trimToSize", "()V", "", ApiSince=9)]' in the contract to '[RegisterAttribute("trimToSize", "()V", "")]' in the implementation dotnet/java-interop@568d24ac added support to allow `generator --apiversions` to be specified multiple times. Take advantage of this new support to pass in the `api-versions.xml` files from *both* `platforms/android-R` *and* `platform-tools/api` when binding API levels > API-29. (Attempting to do this for *all* versions which have both resulted in bizarre API compat errors, as the `RegisterAttribute.ApiSince` value was *cleared*. ?!) This works around the deficiency in API-R's `api-versions.xml` and allows us to retain correct `RegisterAttribute.ApiSince` values. Aside: to manually build the API-R binding, use: msbuild /p:AndroidPlatformId=R /p:AndroidApiLevel=30 /p:AndroidFrameworkVersion=v10.0.99 src/Mono.Android/Mono.Android.csproj /v:diag > b.txt
3a39d22 to
5ee304c
Compare
$(AndroidPlatformId)$(AndroidPlatformId) when appropriate
Context: dotnet/android#4356 (comment) A "funny" thing was found with API-R: the Android SDK `platforms/android-R/data/api-versions.xml` file is *missing* members present in previous API versions, e.g. `java.lang.StringBuilder.trimToSize()` is not mentioned in API-R, while it *is* mentioned in API-29 and the "global" `platform-tools/api/api-versions.xml`. Try to improve sanity for this conundrum by allowing `generator --apiversions` to be specified multiple times, e.g. this diff to apply to xamarin-android: diff --git a/src/Mono.Android/Mono.Android.targets b/src/Mono.Android/Mono.Android.targets index 8735c2ae..baae759b 100644 --- a/src/Mono.Android/Mono.Android.targets +++ b/src/Mono.Android/Mono.Android.targets @@ -78,10 +78,13 @@ Inputs="metadata;enumflags;map.csv;methodmap.csv;$(IntermediateOutputPath)mcw\api.xml" Outputs="$(IntermediateOutputPath)mcw\Mono.Android.projitems"> <MakeDir Directories="$(IntermediateOutputPath)mcw" /> - <PropertyGroup> - <_ApiVersions Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')">"$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml"</_ApiVersions> - <_ApiVersions Condition="'$(_ApiVersions)'==''">"$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml"</_ApiVersions> - </PropertyGroup> + <ItemGroup> + <_ApiVersion Include="$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml" /> + <_ApiVersion + Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')" + Include="$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml" + /> + </ItemGroup> <PropertyGroup> <Generator>"$(XAInstallPrefix)xbuild\Xamarin\Android\generator.exe"</Generator> <_GenFlags>--public --product-version=7</_GenFlags> @@ -91,7 +94,7 @@ <_Fixup>--fixup=metadata</_Fixup> <_Enums1>--preserve-enums --enumflags=enumflags --enumfields=map.csv --enummethods=methodmap.csv</_Enums1> <_Enums2>--enummetadata=$(IntermediateOutputPath)mcw\enummetadata</_Enums2> - <_Versions>--apiversions=$(_ApiVersions)</_Versions> + <_Versions>@(_ApiVersion->'--apiversions="%(Identity)"', ' ')</_Versions> <_Annotations>--annotations="$(AndroidSdkDirectory)\platform-tools\api\annotations.zip"</_Annotations> <_Assembly>--assembly="Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"</_Assembly> <_TypeMap>--type-map-report=$(IntermediateOutputPath)mcw\type-mapping.txt</_TypeMap> The `generator --apiversions` files are applied in order, with *later* versions overriding/replacing earlier versions. This is why `platform-tools/api/api-versions.xml` is present *first*.
Context: 485e39b Context: eb08bb4 ...and doubtless others... Three MSBuild properties control the `android.jar` which is bound and the `$(TargetFrameworkVersion)` of `Mono.Android.dll`: * `$(AndroidApiLevel)`: The API level that is bound. Must be an int. * `$(AndroidFrameworkVersion)`: The `$(TargetFrameworkVersion)` of the generated `Mono.Android.dll`. Must be *mostly* parseable by `System.Version` except with a leading `v`, e.g. `v10.0`. * `$(AndroidPlatformId)`: The "ID" of the API level. *Most* of the time, `$(AndroidApiLevel)` and `$(AndroidPlatformId)` will be *identical*: for API-29, they're both `29`. Where they differ is for new *preview* API levels, such as API-R: `$(AndroidApiLevel)` will be 30, but `$(AndroidPlatformId)` is `R`. The distinction is important because various filesystem paths within the Android SDK use the "id" and *not* the API level when they differ, e.g. the API-R `android.jar` is installed into: $(AndroidSdkDirectory)/platforms/android-R/android.jar We thus need to be *careful* when distinguishing between `$(AndroidApiLevel)` and `$(AndroidPlatformId)`, using the former when an integer is *required*, and using the latter whenever it refers to filesystem paths. Unfortunately, we *haven't* been careful, because these values really only differ for ~4 months out of the year, and for only one `$(TargetFrameworkVersion)` version. Start bringing some sanity...and finding bugs while we do so: `api-xml-adjuster.targets` should use `%(AndroidApiInfo.Id)` and *not* `%(AndroidApiLevel.Level)`, as it references filesystem locations. Consequently, `src/Mono.Android/Profiles/api-30.params.txt` must be renamed to `src/Mono.Android/Profiles/api-R.params.txt` so that it correctly embeds the `$(AndroidPlatformId)` value. `Mono.Android.targets` should likewise use `$(AndroidPlatformId)` and not `$(AndroidApiLevel)` when using filesystem paths from the SDK. For good measure, `Mono.Android.csproj` now overrides `$(IntermediateOutputPath)` to contain `$(AndroidPlatformId)`, because why not (MOAR CONSISTENCY!). These changes, unfortunately, introduce breakage, which will need to be addressed: *Because* API-R was installed into `$(AndroidSdkDirectory)/platforms/android-R`, `api-versions.xml` *was not previously used* because `Mono.Android.targets` was using `$(AndroidApiLevel)`, and `platforms/android-30/data/api-version.xml` does not yet exist. (It will come June! But not now.) As it didn't exist, it hit the fallback path and used `platform-tools/api/api-versions.xml` (4cd2060). You would *think* this wouldn't be a problem, but the API-R `api-versions.xml` is *missing* members relative to platform-tools, resulting in members *missing* `RegisterAttribute.ApiSince` values, which `Microsoft.DotNet.ApiCompat.exe` reports, e.g.: CannotChangeAttribute : Attribute 'Android.Runtime.RegisterAttribute' on 'Java.Lang.StringBuilder.TrimToSize()' changed from '[RegisterAttribute("trimToSize", "()V", "", ApiSince=9)]' in the contract to '[RegisterAttribute("trimToSize", "()V", "")]' in the implementation dotnet/java-interop@568d24ac added support to allow `generator --apiversions` to be specified multiple times. Take advantage of this new support to pass in the `api-versions.xml` files from *both* `platforms/android-R` *and* `platform-tools/api` when binding API levels > API-29. (Attempting to do this for *all* versions which have both resulted in bizarre API compat errors, as the `RegisterAttribute.ApiSince` value was *cleared*. ?!) This works around the deficiency in API-R's `api-versions.xml` and allows us to retain correct `RegisterAttribute.ApiSince` values. Aside: to manually build the API-R binding, use: msbuild /p:AndroidPlatformId=R /p:AndroidApiLevel=30 /p:AndroidFrameworkVersion=v10.0.99 src/Mono.Android/Mono.Android.csproj /v:diag > b.txt
Context: #4356 Context: 54beb90 Context: a20be39 The use of `Microsoft.DotNet.ApiCompat.exe` added in 07e7477 has one major deficiency: The error messages reported by `Microsoft.DotNet.ApiCompat.exe` are *awful* and borderline useless or misleading. For example, consider commit PR #4356, which attempts to bring sanity and consistency around `$(AndroidPlatformId)` and `Mono.Android.dll` builds. It contains an API break, which we'll hand wave away and accept for preview release purposes, in which the property type for `Android.Telephony.CellInfoGsm.CellIdentity` changes from `CellIdentityGsm` to `CellIdentity`: // API-29 namespace Android.Telephony { public sealed partial class CellInfoGsm: Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentityGsm CellIdentity { } } // API-R namespace Android.Telephony { public sealed partial class CellInfoGsm : Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentity CellIdentity { } } This is clearly a break. How does `Microsoft.DotNet.ApiCompat.exe` report the breakage? error : MembersMustExist : Member 'Android.Telephony.CellInfoGsm.CellIdentity.get()' does not exist in the implementation but it does exist in the contract. Which is infuriatingly terrible. The message *implies* that `Android.Telephony.CellInfoGsm.get_CellIdentity()` doesn't exist, but it *does* exist. The problem is that the return type changed! Or consider 54beb90, in which we now emit a slew of default interface members within the `Mono.Android.dll` binding, which *should* be API compatible. `Microsoft.DotNet.ApiCompat.exe` complains as well: InterfacesShouldHaveSameMembers : Interface member 'Java.Util.Functions.IUnaryOperator.Identity()' is present in the implementation but not in the contract. What these messages have in common is that they provide no context, lack important types, and in no way suggest how to *fix* the error other than to just ignore it. Overhaul this infrastructure so that crucial context is provided. The context is provided by using "reference assembly source": the [`Microsoft.DotNet.GenAPI.exe` utility][0] can be run on an assembly to generate C# source code that shows the same API but no implementation: namespace Android.Accounts { [Android.Runtime.RegisterAttribute("android/accounts/AbstractAccountAuthenticator", DoNotGenerateAcw=true, ApiSince=5)] public abstract partial class AbstractAccountAuthenticator : Java.Lang.Object { [Android.Runtime.RegisterAttribute("KEY_CUSTOM_TOKEN_EXPIRY", ApiSince=23)] public const string KeyCustomTokenExpiry = "android.accounts.expiry"; [Android.Runtime.RegisterAttribute(".ctor", "(Landroid/content/Context;)V", "")] public AbstractAccountAuthenticator(Android.Content.Context context) { } Update the `src/Mono.Android` build so that after every build, when `Microsoft.DotNet.ApiCompat.exe` fails we *also* run `Microsoft.DotNet.GenAPI.exe` on the generated assembly, then run `git diff -u` against the recently created assembly and the reference assembly source for the contract assembly. This allows us to get *useful diffs* in the API: Task "Exec" (TaskId:570) Task Parameter:Command=git diff --no-index "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) diff -u "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) --- ../../tests/api-compatibility/reference/Mono.Android.dll.cs 2020-03-05 13:20:59.000000000 -0500 (TaskId:570) +++ /Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs 2020-03-05 13:40:12.000000000 -0500 (TaskId:570) @@ -27,7 +27,7 @@ (TaskId:570) { (TaskId:570) [Android.Runtime.RegisterAttribute("ACCEPT_HANDOVER", ApiSince=28)] (TaskId:570) public const string AcceptHandover = "android.permission.ACCEPT_HANDOVER"; (TaskId:570) - [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION")] (TaskId:570) + [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION", ApiSince=29)] (TaskId:570) (The above changes are courtesy commmit 4cd2060, which added `RegisterAttribute.ApiSince` on a large number of members.) Finally, how do we update the "contract" `Mono.Android.dll` assembly? Add a new `tests/api-compatibility/api-compatibility.targets` file which contains a `UpdateMonoAndroidContract` target which will update `tests/api-compatibility/reference/Mono.Android.zip` with the contents of a `cil-strip`'d `Mono.Android.dll` and updated "reference assembly source". `Mono.Android.zip` contains a contract from Xamarin.Android 10.2.0.100 for `$(TargetFrameworkVersion)` v10.0. [0]: https://github.com/dotnet/arcade/tree/bc4fa8e7149769db4efd466f160417a32b11f0bf/src/Microsoft.DotNet.GenAPI
Context: #4356 Context: 54beb90 Context: a20be39 The use of `Microsoft.DotNet.ApiCompat.exe` added in 07e7477 has one major deficiency: The error messages reported by `Microsoft.DotNet.ApiCompat.exe` are *awful* and borderline useless or misleading. For example, consider commit PR #4356, which attempts to bring sanity and consistency around `$(AndroidPlatformId)` and `Mono.Android.dll` builds. It contains an API break, which we'll hand wave away and accept for preview release purposes, in which the property type for `Android.Telephony.CellInfoGsm.CellIdentity` changes from `CellIdentityGsm` to `CellIdentity`: // API-29 namespace Android.Telephony { public sealed partial class CellInfoGsm: Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentityGsm CellIdentity { } } // API-R namespace Android.Telephony { public sealed partial class CellInfoGsm : Android.Telephony.CellInfo, Android.OS.IParcelable { public unsafe Android.Telephony.CellIdentity CellIdentity { } } This is clearly a break. How does `Microsoft.DotNet.ApiCompat.exe` report the breakage? error : MembersMustExist : Member 'Android.Telephony.CellInfoGsm.CellIdentity.get()' does not exist in the implementation but it does exist in the contract. Which is infuriatingly terrible. The message *implies* that `Android.Telephony.CellInfoGsm.get_CellIdentity()` doesn't exist, but it *does* exist. The problem is that the return type changed! Or consider 54beb90, in which we now emit a slew of default interface members within the `Mono.Android.dll` binding, which *should* be API compatible. `Microsoft.DotNet.ApiCompat.exe` complains as well: InterfacesShouldHaveSameMembers : Interface member 'Java.Util.Functions.IUnaryOperator.Identity()' is present in the implementation but not in the contract. What these messages have in common is that they provide no context, lack important types, and in no way suggest how to *fix* the error other than to just ignore it. Overhaul this infrastructure so that crucial context is provided. The context is provided by using "reference assembly source": the [`Microsoft.DotNet.GenAPI.exe` utility][0] can be run on an assembly to generate C# source code that shows the same API but no implementation: namespace Android.Accounts { [Android.Runtime.RegisterAttribute("android/accounts/AbstractAccountAuthenticator", DoNotGenerateAcw=true, ApiSince=5)] public abstract partial class AbstractAccountAuthenticator : Java.Lang.Object { [Android.Runtime.RegisterAttribute("KEY_CUSTOM_TOKEN_EXPIRY", ApiSince=23)] public const string KeyCustomTokenExpiry = "android.accounts.expiry"; [Android.Runtime.RegisterAttribute(".ctor", "(Landroid/content/Context;)V", "")] public AbstractAccountAuthenticator(Android.Content.Context context) { } Update the `src/Mono.Android` build so that after every build, when `Microsoft.DotNet.ApiCompat.exe` fails we *also* run `Microsoft.DotNet.GenAPI.exe` on the generated assembly, then run `git diff -u` against the recently created assembly and the reference assembly source for the contract assembly. This allows us to get *useful diffs* in the API: Task "Exec" (TaskId:570) Task Parameter:Command=git diff --no-index "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) diff -u "../../tests/api-compatibility/reference/Mono.Android.dll.cs" "/Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs" (TaskId:570) --- ../../tests/api-compatibility/reference/Mono.Android.dll.cs 2020-03-05 13:20:59.000000000 -0500 (TaskId:570) +++ /Volumes/Xamarin-Work/xamarin-android/bin/Debug/lib/xamarin.android/xbuild-frameworks/MonoAndroid/v10.0/Mono.Android.dll.cs 2020-03-05 13:40:12.000000000 -0500 (TaskId:570) @@ -27,7 +27,7 @@ (TaskId:570) { (TaskId:570) [Android.Runtime.RegisterAttribute("ACCEPT_HANDOVER", ApiSince=28)] (TaskId:570) public const string AcceptHandover = "android.permission.ACCEPT_HANDOVER"; (TaskId:570) - [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION")] (TaskId:570) + [Android.Runtime.RegisterAttribute("ACCESS_BACKGROUND_LOCATION", ApiSince=29)] (TaskId:570) (The above changes are courtesy commmit 4cd2060, which added `RegisterAttribute.ApiSince` on a large number of members.) Finally, how do we update the "contract" `Mono.Android.dll` assembly? Add a new `tests/api-compatibility/api-compatibility.targets` file which contains a `UpdateMonoAndroidContract` target which will update `tests/api-compatibility/reference/Mono.Android.zip` with the contents of a `cil-strip`'d `Mono.Android.dll` and updated "reference assembly source". `Mono.Android.zip` contains a contract from Xamarin.Android 10.2.0.100 for `$(TargetFrameworkVersion)` v10.0. [0]: https://github.com/dotnet/arcade/tree/bc4fa8e7149769db4efd466f160417a32b11f0bf/src/Microsoft.DotNet.GenAPI
Context: dotnet/android#4356 (comment) A "funny" thing was found with API-R: the Android SDK `platforms/android-R/data/api-versions.xml` file is *missing* members present in previous API versions, e.g. `java.lang.StringBuilder.trimToSize()` is not mentioned in API-R, while it *is* mentioned in API-29 and the "global" `platform-tools/api/api-versions.xml`. Try to improve sanity for this conundrum by allowing `generator --apiversions` to be specified multiple times, e.g. this diff to apply to xamarin-android: diff --git a/src/Mono.Android/Mono.Android.targets b/src/Mono.Android/Mono.Android.targets index 8735c2ae..baae759b 100644 --- a/src/Mono.Android/Mono.Android.targets +++ b/src/Mono.Android/Mono.Android.targets @@ -78,10 +78,13 @@ Inputs="metadata;enumflags;map.csv;methodmap.csv;$(IntermediateOutputPath)mcw\api.xml" Outputs="$(IntermediateOutputPath)mcw\Mono.Android.projitems"> <MakeDir Directories="$(IntermediateOutputPath)mcw" /> - <PropertyGroup> - <_ApiVersions Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')">"$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml"</_ApiVersions> - <_ApiVersions Condition="'$(_ApiVersions)'==''">"$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml"</_ApiVersions> - </PropertyGroup> + <ItemGroup> + <_ApiVersion Include="$(AndroidSdkDirectory)\platform-tools\api\api-versions.xml" /> + <_ApiVersion + Condition="Exists('$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml')" + Include="$(AndroidSdkDirectory)\platforms\android-$(AndroidPlatformId)\data\api-versions.xml" + /> + </ItemGroup> <PropertyGroup> <Generator>"$(XAInstallPrefix)xbuild\Xamarin\Android\generator.exe"</Generator> <_GenFlags>--public --product-version=7</_GenFlags> @@ -91,7 +94,7 @@ <_Fixup>--fixup=metadata</_Fixup> <_Enums1>--preserve-enums --enumflags=enumflags --enumfields=map.csv --enummethods=methodmap.csv</_Enums1> <_Enums2>--enummetadata=$(IntermediateOutputPath)mcw\enummetadata</_Enums2> - <_Versions>--apiversions=$(_ApiVersions)</_Versions> + <_Versions>@(_ApiVersion->'--apiversions="%(Identity)"', ' ')</_Versions> <_Annotations>--annotations="$(AndroidSdkDirectory)\platform-tools\api\annotations.zip"</_Annotations> <_Assembly>--assembly="Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"</_Assembly> <_TypeMap>--type-map-report=$(IntermediateOutputPath)mcw\type-mapping.txt</_TypeMap> The `generator --apiversions` files are applied in order, with *later* versions overriding/replacing earlier versions. This is why `platform-tools/api/api-versions.xml` is present *first*.
Context: 485e39b
Context: eb08bb4
...and doubtless others...
Three MSBuild properties control the
android.jarwhich is bound andthe
$(TargetFrameworkVersion)ofMono.Android.dll:$(AndroidApiLevel): The API level that is bound. Must be an int.$(AndroidFrameworkVersion): The$(TargetFrameworkVersion)ofthe generated
Mono.Android.dll. Must be mostly parseable bySystem.Versionexcept with a leadingv, e.g.v10.0.$(AndroidPlatformId): The "ID" of the API level.Most of the time,
$(AndroidApiLevel)and$(AndroidPlatformId)will be identical: for API-29, they're both
29.Where they differ is for new preview API levels, such as API-R:
$(AndroidApiLevel)will be 30, but$(AndroidPlatformId)isR.The distinction is important because various filesystem paths within
the Android SDK use the "id" and not the API level when they differ,
e.g. the API-R
android.jaris installed into:We thus need to be careful when distinguishing between
$(AndroidApiLevel)and$(AndroidPlatformId), using the former whenan integer is required, and using the latter whenever it refers to
filesystem paths.
Unfortunately, we haven't been careful, because these values really
only differ for ~4 months out of the year, and for only one
$(TargetFrameworkVersion)version.Start bringing some sanity...and finding bugs while we do so:
api-xml-adjuster.targetsshould use%(AndroidApiInfo.Id)and not%(AndroidApiLevel.Level), as it references filesystem locations.Consequently,
src/Mono.Android/Profiles/api-30.params.txtmust berenamed to
src/Mono.Android/Profiles/api-R.params.txtso that itcorrectly embeds the
$(AndroidPlatformId)value.Mono.Android.targetsshould likewise use$(AndroidPlatformId)andnot
$(AndroidApiLevel)when using filesystem paths from the SDK.For good measure,
Mono.Android.csprojnow overrides$(IntermediateOutputPath)to contain$(AndroidPlatformId), becausewhy not (MOAR CONSISTENCY!).
These changes, unfortunately, introduce breakage, which will need to
be addressed:
Because API-R was installed into
$(AndroidSdkDirectory)/platforms/android-R,api-versions.xmlwas not previously used because
Mono.Android.targetswas using$(AndroidApiLevel), andplatforms/android-30/data/api-version.xmldoes not yet exist. (It will come June! But not now.) As it didn't
exist, it hit the fallback path and used
platform-tools/api/api-versions.xml(4cd2060). You would thinkthis wouldn't be a problem, but the API-R
api-versions.xmlismissing members relative to platform-tools, resulting in members
missing
RegisterAttribute.ApiSincevalues, whichMicrosoft.DotNet.ApiCompat.exereports, e.g.:dotnet/java-interop@568d24ac added support to allow
generator --apiversionsto be specified multiple times. Takeadvantage of this new support to pass in the
api-versions.xmlfilesfrom both
platforms/android-Randplatform-tools/apiwhenbinding API levels > API-29. (Attempting to do this for all
versions which have both resulted in bizarre API compat errors, as
the
RegisterAttribute.ApiSincevalue was cleared. ?!)This works around the deficiency in API-R's
api-versions.xmlandallows us to retain correct
RegisterAttribute.ApiSincevalues.Aside: to manually build the API-R binding, use: