Skip to content
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: Add ValidateAssemblies msbuild task and add global tool #26616

Merged

Conversation

ViktorHofer
Copy link
Member

@ViktorHofer ViktorHofer commented Jul 15, 2022

Fixes #18677

Please review by commits.

This change adds a global tool frontend that makes invoking ApiCompat's ValidateAssemblies and ValidatePackage functionality outside of msbuild possible. In addition to that, the ValidateAssemblies feature is now also exposed via an msbuild task.

The commits are grouped by usefulness to review:

  • Commit 1 and 2 are just updating resx and sln(f) files.
  • Commit 3 adds the transport package only files for ValidateAssemblies for a ref<-->impl tuple and exposes the ValidateAssemblies task in the sdk.
  • Commit 4 just updates the project files
  • Commit 5 adds the frontends (task & console + shared)
  • Commit 6 adds the ApiCompatRunner and related files to the ApiCompatibility layer
  • Commit 7 makes the required changes in the PackageValidation layer
  • Commit 8 adds tests and updates the existing ones.
  • Commit 9 updates the CODEOWNERS file

dotnet/runtime#72464 consumes this change and demonstrates that there aren't any regressions in dotnet/runtime.

ValidateAssemblies

Features:

  • Compare a set of given left assemblies with a set of given right assemblies
  • MSBuild task and global tool command
  • The task is exposed directly in the SDK
  • Additional targets in the transport package to fully replace arcade's ApiCompat
  • Able to run in an inner or outer build for max perf
  • Fully incremental, the comparison is only triggered if the inputs or outputs changed.
  • Uses the same CompatibilitySuppression file format as PackageValidation
  • Optionally transforms the left and right input assembly paths that are emitted into the suppression file to avoid environment specific paths.

In discussions with @ericstj we agreed on that it doesn't make sense to expose the ValidateAssemblies targets in the SDK and only include them in the transport package for core stack consumption (i.e. dotnet/runtime, wcf, aspnetcore, etc.).

Differently than arcade's apicompat msbuild targets, these targets allow to run ApiCompat either in an inner or outer build, based on if the project multi-targets. This speeds up the overall time spent in ApiCompat as there's only a single invocation instead of n invocations for each inner build.

image

We don't want to enable the ValidateAssemblies task in dotnet/runtime until #23905 is implemented which @smasher164 and I are currently working on.

@smasher164 smasher164 self-requested a review July 15, 2022 15:45
@ViktorHofer ViktorHofer marked this pull request as ready for review July 19, 2022 15:59
@ViktorHofer ViktorHofer requested review from ericstj, a team and joperezr as code owners July 19, 2022 15:59
@ViktorHofer ViktorHofer self-assigned this Jul 19, 2022
@joperezr
Copy link
Member

Hey @ViktorHofer sorry I didn't get a chance to look at this today. Will review tomorrow.

using System;
using System.Collections.Generic;
using System.IO;
using Jab;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this new dependency? Is this what is getting us Dependency Injection? If so, do you think it is worth it to take on this new dependency on the sdk for an abstraction that strictly speaking doesn't need to be there? I agree it makes testing simpler and it decouples your APIs, but I wonder if it's worth the risk of adding an external dependency for this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Jab is a compile time source generator (AOT) which doesn't use any reflection and hence is a build time only dependency (it's marked as PrivateAssets="all"). Jab is already part of our source build and because of that doesn't introduce a prebuilt. The author is a previous .NET team member.

The source generator creates a composition file that just wraps the instantiation of the various types that are required by other types.

If so, do you think it is worth it to take on this new dependency on the sdk for an abstraction that strictly speaking doesn't need to be there? I agree it makes testing simpler and it decouples your APIs, but I wonder if it's worth the risk of adding an external dependency for this.

Yes, I think this component helps tremendously with managing dependencies inside ApiCompat without sacrificing runtime performance. I reviewed the generated code and there's nothing that stands out in terms of risk or perf penalty.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this component helps tremendously with managing dependencies inside ApiCompat

I'd be interested in getting more details around this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be interested in getting more details around this.

Basically the general advantages of using dependency injection. https://betterprogramming.pub/the-6-benefits-of-dependency-injection-7802b207ec69 summarizes them quite nicely.

Most importantly, the following paragraph:

Probably the main benefit of dependency injection is maintainability. If your classes are loosely coupled and follow the single responsibility principle — the natural result of using DI — then your code will be easier to maintain.

And this section nicely translates to the challenges with the previous implementation of the ApiCompatRunner which created dependencies just in time, which made it impossible to unit test the runner:

If you pass dependencies to classes, it’s quite simple to pass in a test double implementation. If dependencies are hard-coded, it’s impossible to create test doubles for those dependencies.

using Microsoft.DotNet.ApiCompatibility.Abstractions;
using Microsoft.DotNet.ApiCompatibility.Logging;

namespace Microsoft.DotNet.ApiCompatibility.Runner
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was anything changed in these files other than the interface abstraction? It's hard when the diff shows the whole files as this essentially means everything in compat must be re-reviewed.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ones that I already touched on in previous meetings:

  • Removed package specific log to make it usable outside of PackageValidation.
  • Dependencies are moved into the constructor so that the type becomes unit testable (by mocking the external dependencies).

In addition to that:

  • More diagnostics with low importance logged so that they appear in a binlog / high verbosity text log
  • Strongly type work items and store them in a HashSet<T> instead of a Dictionary<T,U>.

@joperezr
Copy link
Member

joperezr commented Jul 28, 2022

I obviously didn't review line by line, but this looks good other than the comments I added. This being such a big refactoring, I would strongly suggest to test all your changes against a few dotnet repos that already consume package validation today in case you haven't done it, as well as try to call the new entry points (like the assemblyvalidation targets or the apicompat tool) on those repos and ensuring things are working.

@ViktorHofer ViktorHofer merged commit c3a7409 into dotnet:main Jul 29, 2022
@ViktorHofer ViktorHofer deleted the BigRefactoringOfCompatibilityTooling branch July 29, 2022 19:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[API Compat] Add MSBuild task to run API Compat standalone
3 participants