Skip to content

Commit e7898e9

Browse files
committed
[api-compat] Provide API diffs around API breakage
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, 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
1 parent 54beb90 commit e7898e9

File tree

6 files changed

+33
-4
lines changed

6 files changed

+33
-4
lines changed

Before.Xamarin.Android.sln.targets

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
<Import Project="$(MSBuildThisFileDirectory)build-tools\scripts\ImportExportDocs.targets" />
44
<Import Project="$(MSBuildThisFileDirectory)build-tools\scripts\PrepareWindows.targets" Condition=" '$(OS)' == 'Windows_NT' " />
55
<Import Project="$(MSBuildThisFileDirectory)build-tools\scripts\RunTests.targets" />
6+
<Import Project="$(MSBuildThisFileDirectory)tests\api-compatibility\api-compatibility.targets" />
67
</Project>

src/Mono.Android/Mono.Android.targets

+19-4
Original file line numberDiff line numberDiff line change
@@ -174,23 +174,38 @@
174174
<ApiCompatibilityFiles Include="$(ApiCompatibilityDir)/reference/*.*" />
175175
</ItemGroup>
176176
<Target
177-
Name="AfterBuild"
177+
Name="_CheckApiCompatibility"
178178
Condition=" '$(DisableApiCompatibilityCheck)' != 'True' "
179+
AfterTargets="Build"
179180
Inputs="$(TargetPath);@(ApiCompatibilityFiles)"
180181
Outputs="$(IntermediateOutputPath)CheckApiCompatibility.stamp">
181182
<CheckApiCompatibility
183+
ContinueOnError="ErrorAndContinue"
182184
ApiCompatPath="$(XAPackagesDir)\microsoft.dotnet.apicompat\5.0.0-beta.20078.1\tools\net472\"
183185
ApiLevel="$(AndroidFrameworkVersion)"
184186
LastStableApiLevel="$(AndroidLatestStableFrameworkVersion)"
185187
TargetImplementationPath="$(OutputPath)"
186188
ApiCompatibilityPath="$(ApiCompatibilityDir)"
187189
/>
188-
<Touch
189-
Files="$(IntermediateOutputPath)CheckApiCompatibility.stamp"
190-
AlwaysCreate="True"
190+
<PropertyGroup>
191+
<_GenAPI>"$(XAPackagesDir)\microsoft.dotnet.genapi\5.0.0-beta.20078.1\tools\net472\Microsoft.DotNet.GenAPI.exe"</_GenAPI>
192+
<_ContractRefSrc>"$(ApiCompatibilityDir)\reference\Mono.Android.dll.cs"</_ContractRefSrc>
193+
<_TargetRefSrc>"$(TargetPath).cs"</_TargetRefSrc>
194+
</PropertyGroup>
195+
<Exec
196+
Command="$(ManagedRuntime) $(ManagedRuntimeArgs) $(_GenAPI) &quot;$(TargetPath)&quot; > $(_TargetRefSrc)"
197+
/>
198+
<Exec
199+
Condition=" '$(HostOS)' != 'Windows' "
200+
ContinueOnError="True"
201+
Command="diff -u $(_ContractRefSrc) $(_TargetRefSrc)"
191202
/>
192203
<ItemGroup>
193204
<FileWrites Include="$(IntermediateOutputPath)CheckApiCompatibility.stamp" />
194205
</ItemGroup>
206+
<Touch
207+
Files="$(IntermediateOutputPath)CheckApiCompatibility.stamp"
208+
AlwaysCreate="True"
209+
/>
195210
</Target>
196211
</Project>

tests/api-compatibility/README.md

+11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ current version of an assembly. We could also called it the V2 assembly.
1616
[mdac]: https://github.com/dotnet/arcade/tree/master/src/Microsoft.DotNet.ApiCompat
1717

1818

19+
## Update Contract Assembly
20+
21+
To update the contract assembly, run the `UpdateMonoAndroidContract` target
22+
and provide the `$(ContractAssembly)` MSBuild property. `$(ContractAssembly)`
23+
should be the path to the new contract assembly to use:
24+
25+
msbuild Xamarin.Android.sln /t:UpdateMonoAndroidContract '/p:ContractAssembly=/Users/example/Downloads/\$ReferenceAssemblies/Microsoft/Framework/MonoAndroid/v10.0/Mono.Android.dll'
26+
27+
At this time this target cannot be run on Windows machines.
28+
29+
1930
## Build Task
2031

2132
We have developed a build task that will wrap *Microsoft.DotNet.ApiCompat.exe*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Mono.Android.dll
2+
Mono.Android.dll.cs
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)