-
Notifications
You must be signed in to change notification settings - Fork 1.1k
[APICompat - PackageValidation] Enable baseline assembly references #32930
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
[APICompat - PackageValidation] Enable baseline assembly references #32930
Conversation
I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label. |
What I was imagining we'd do was use the right-hand-side dependencies when the left-hand-side dependencies were missing. This was what we did previously with API Compat. Nearest might be OK - so long as you're going forward and not back (I always get mixed up with NuGet's terms around this), since the nearest forward should be compatible and should be a parallel operation to the compatibility tuples we're creating between frameworks. |
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.
Can we add a test case for this somehow?
Yes, that's what this PR is doing.
I'm slightly confused what you refer to with going back. The current package (right hand side) usually uses the same or a newer TFM and presumably rarely an older TFM. In my example above, would we want to allow the following behavior or not (?):
...
Non trivial as many of our package validation integration tests are currently disabled because of #23533. I'm unsure if I can come up with a new test without deeply looking and with that fixing the test issue. I did run my changes on top off the msbuild change and the error went away. I also verified in the debugger that the assembly references are considered on the left hand side - as expected. |
Not quite, it's computing which references to use independently based on the TFM of each side. I meant something more specific. Given a tuple comparing {A} to {B, b-references} use b-references for A -- without any calculation involving TFM at all. Its a bit mechanical and leaves less up to chance. For example, in this method:
Yeah, we'd want that behavior. I'm agreeing with you -- I'm just asking to double check that |
What makes this hard with the current architecture is that the component that loads symbols and compares left against rights does have a 1:n relationship. A single left is often compared against multiple rights. As an example:
APICompat / PackageValidation validates the
In that scenario, the customer decides to drop the netstandard2.0 asset but still expects APICompat to validate that the API between the netstandard2.0 and the net6.0 isn't changed in an incompatible way. It's not entirely clear to me if we want to pass the
OK, I now understand what you mean by that. Yes, right now we are going backwards instead of forward. Will see if I can change that and avoid the additional compatibility check. |
If assembly references aren't provided for the baseline package via the MSBuild task or the CLI frontend, use the current package's assembly references as an approximation. As current assembly references are grouped by TFMs, use the nearest compatible TFM for which assembly references are provided. This guarantees that assembly references from current are used even if TFMs don't match.
a4eb4b8
to
c1172a2
Compare
I found a place that enqueues package validation assemblies with an 1:1 mapping (TFM to TFM) which allows to re-use the assembly references from the right. Let's see what CI says. I re-ran the tests on top of the msbuild PR and the errors disappeared. |
Think about API Compat baseline check as doing an experiment on framework supported by the latest package. It's doing a what if check between the previous version and the new version on a supported framework. Imagine all the references as the "control" in that experiment. So API Compat is answering the question of what will break if I updated the baseline package to the latest package in a project targeting a specific framework? The references in that case are not the variable under test, so treating them like a control is reasonable -- it does mean we might miss breaking changes in those references but we're already excluding them from the comparison rules so IMO it's an acceptable heuristic. |
With these changes, I do see quite a lot of errors in a runtime allconfigurations build. I just verified that at least one of them is legit and due to dotnet/runtime#75235. Looking at the other ones now. |
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.
This looks better, just one small suggestion.
leftContentItems[leftIndex], | ||
displayString: options.IsBaselineComparison ? Resources.Baseline + " " + leftContentItems[leftIndex].Path : null, | ||
// Use the assembly references from the right package if the left package doesn't provide them. | ||
assemblyReferences: rightPackage is not null ? right[right.Length > leftIndex ? leftIndex : 0].References : null); |
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.
Are the references actually specific to some index here? I thought this index was iterating through multiple content items from the package, multiple assemblies in the same folder, so I would expect all the references to be the same for a given call of this method.
Ultimately it looks like a work item loads all assemblies in the same loader (CSharpCompilation), so associating references with individual content items is probably unnecessary overhead:
https://github.com/dotnet/sdk/blob/5f6b9be62ba8952410d2b181fe5a2ac3fee4c03a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Runner/ApiCompatRunner.cs#LL123C31-L123C31
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.
It's unnecessary, yes but I felt like keeping the index in sync (as in most cases left and right count is in sync) makes more sense than just always defaulting to the first item.
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.
Fair, but it was confusing to me trying to understand the relationship between the indexes of these two lists. I was trying to understand why we would want to align them but would also be OK with just substituting the first. Perhaps a future refactoring might move references up to the work-item instead of associate with the individual files.
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.
I will remove that indexing as you are right that it's unnecessary overhead and adds complexity.
Ultimately it looks like a work item loads all assemblies in the same loader (CSharpCompilation), so associating references with individual content items is probably unnecessary overhead:
Just to clarify, a workitem doesn't share the compilation context as this would otherwise result in reference assembly clashes. The individual elements (lefts and rights) each create a compilation context and load the assemblies into that container. Not relevant for this discussion but this domain has some perf optimization potentials. I.e. the assembly symbol loader could respect assembly versions (and public key tokens) and cache the loaded assembly symbols.
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.
Change seems OK. My feedback is more around a future refactoring. Thank you for fixing this, by the way. I think this fix will help quite a bit.
All the errors that appeared in runtime with this change are correct. There is one odd case though:
|
... and suppress the new errors that are flagged because of breaking changes introduced in .NET 8. See dotnet/sdk#32930 for more details on the added validation in APICompat.
* Update APICompat.Task to latest version ... and suppress the new errors that are flagged because of breaking changes introduced in .NET 8. See dotnet/sdk#32930 for more details on the added validation in APICompat. * Suppress new errors that were intentionally made * Update Version.Details.xml * Update CompatibilitySuppressions.xml * Update CompatibilitySuppressions.xml
Fixes #32922, #18676, #31271
If assembly references aren't provided for the baseline package via the MSBuild task or the CLI frontend, use the current package's assembly references as an approximation.
As current assembly references are grouped by TFMs, use the nearest compatible TFM for which assembly references are provided. This guarantees that assembly references from current are used even if TFMs don't match.
@ericstj what do you think about the assembly reference change related to picking the nearest compatible TFM? Do we want a baseline package with a
lib/net7.0/a.dll
asset to use the assembly references from the current packagelib/net8.0/a.dll
asset? Note that the current assembly symbol loader ignored versions.