-
Notifications
You must be signed in to change notification settings - Fork 541
[api-compat] Provide API diffs around API breakage #4362
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should consider only run codegen if apicompat reported issues. Perhaps even run codegen form inside the apicompat wrapper.
I would prefer a solution that:
I don't know what that solution is, but some options are:
|
I agree with @jpobst, we should fix the issues on ApiCompat, enhancing the msgs, etc, doing that everyone wins, us and the other teams using the tool as well. |
e7898e9
to
3821f47
Compare
The fundamental question is this: how long will it take to get in any changes to Meanwhile we could really use the additional "API diff" context now. Reviewing PR #4356 and PR #4353 and all future dotnet/java-interop#509 -related API changes to Which is also why this PR uses Getting the But at minimum it would vastly improve the usability of our current workflow. |
We can get the changes in one day, my understand is that they have nightly builds that push the nuget package every night, but I will confirm that with them. |
Another option might be https://www.nuget.org/packages/Mono.ApiTools, which is done by the Xamarin Components team. |
I think maybe we are conflating two issues:
This PR seems to implement Once Long-term, I think what we want is: a report generated that shows any changes to our public API ( Somehow we want to enforce not only that we don't introduce breaking changes, but that any change to our public API is intentional. I think the Components team may have built tooling for these scenarios, since they faced the exact same issue. |
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
3821f47
to
2ba9af5
Compare
PR updated so that it should now execute on Windows -- courtesy |
@jpobst mentioned:
Looking briefly at that, it uses Yes, we could have improved those tools to support checking custom attribute values, but we had wanted to "standardize" on tooling used in the rest of the dotnet ecosystem...not quite realizing how "problematic" that tooling would in turn be. (The grass is always greener on the other side of the fence, right? ;-) |
@jpobst wrote:
Conflating issues is fun? :-) You're absolutely correct: the idea was to introduce API diffs so that we could better understand what happened when API breakage is reported. I think that is of value now, and will be of increasing value as we continue working on API-30. Kind of; see below.
What's "funny" is that "a report that shows changes to our public API" is what I was originally thinking of in Issue #1118, as that's what mono did, using This is what the https://github.com/mono/api-snapshot repo is for. The mono workflow, paraphrasing greatly, is that mono/api-snapshot contains "reference assembly source" for all assemblies they care about, as produced by Much of this seems a tad "heavy handed", particularly as we wanted to move away from our own xamarin/xamarin-android-api-compatibility repo (submodules are occasionally annoying). ...but there might be something to that. Consider what will be happening around dotnet/java-interop#509: every time we add a new Which we want, but we also want to see what those changes are. Currently, there's no easy way to see those changes at all. This PR is thus an improvement. However, as currently architected, this PR will quickly get swamped, because the "reference" is from d16-5. We'll thus always see enormous API diffs (whenever To have saner API diffs, e.g. to see what just happens when Meaning this PR, instead of using references from d16-5, should instead use references from current master. The approach within this PR can support that, but this is a policy discussion that we need to agree on. |
On a related tangent, consider PR #4356 and this build: https://devdiv.visualstudio.com/DevDiv/_build/results?buildId=3529131&view=results It failed at once point, and the fix were various removals to the
But why did those things change? Particularly problematic when a local build didn't encounter those errors (though I didn't fully clean my build tree...). Here again, an "API diff" perspective would be immensely helpful, especially when if the build fails, there's very little to examine: no uploaded assemblies, no source code... |
...and this is where having a more "up-to-date" reference API hanging around would be handy. A previous iteration of PR #4356 had a bug in which neither I had to nuke & rebuild my checkout to figure that out... Having a "reference assembly" from d16-5 is somewhat handy -- no breakages compared to the released version! -- but it means it's somewhat easier to inadvertently break more recent changes. Additionally, there's a Very Important Question about what the reference assembly should be. If it's for the stable v10.0, then how different will it be from d16-5? Fortunately that's easy enough to do with
No idea why We've been Very Good™ and haven't changed v10.0 API between d16-5 and now. Yay. But this also means that, in the context of dotnet/java-interop#509 and API-R bindings, there's no "good" way to say "this set of changes is acceptable", because we (currently) don't have a "reference assembly" for the preview binding, only for the stable binding. Thus, even with API diffs, every API-R binding change will result in continually growing API diffs, and the current diff -- all 3MB of it/27KLOC! -- will only get larger. |
I think this is where my disconnect is happening. The new changes will not be breaking changes (that's the whole point of what we're doing), so they will not show up in this PR. But I do want to see what non-breaking changes my PR's are causing. To that end, I just ran |
@jonpryor should the GenApi, run inside the ApiCompat task as a single task, I say that because perhaps we could do some work on better digest the diff result (pluses and minuses) and print back the content in a little less polluted way. |
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
Overlooked in commit 2cfce14/PR dotnet#4362. Doh!
Context: #4356
Context: 54beb90
Context: a20be39
The use of
Microsoft.DotNet.ApiCompat.exe
added in 07e7477 has onemajor deficiency:
The error messages reported by
Microsoft.DotNet.ApiCompat.exe
areawful and borderline useless or misleading.
For example, consider commit PR #4356, which attempts to bring sanity
and consistency around
$(AndroidPlatformId)
andMono.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 fromCellIdentityGsm
toCellIdentity
:This is clearly a break. How does
Microsoft.DotNet.ApiCompat.exe
report the breakage?
Which is infuriatingly terrible. The message implies that
Android.Telephony.CellInfoGsm.get_CellIdentity()
doesn't exist, butit 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 APIcompatible.
Microsoft.DotNet.ApiCompat.exe
complains as well: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 can be run on anassembly to generate C# source code that shows the same API but no
implementation:
Update the
src/Mono.Android
build so that after every build, afterrunning
Microsoft.DotNet.ApiCompat.exe
we also runMicrosoft.DotNet.GenAPI.exe
on the generated assembly, then rundiff -u
against the recently created assembly and the referenceassembly source for the contract assembly. This allows us to get
useful diffs in the API:
(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
filewhich contains a
UpdateMonoAndroidContract
target which will updatetests/api-compatibility/reference/Mono.Android.zip
with the contentsof a
cil-strip
'dMono.Android.dll
and updated "reference assemblysource".
Mono.Android.zip
contains a contract from Xamarin.Android 10.2.0.100for
$(TargetFrameworkVersion)
v10.0.Note: The
diff -u
invocation andUpdateMonoAndroidContract
targetscurrently only work on non-Windows platforms, due to the use of the
diff(1) and zip(1L) utilities.