Skip to content

Define convention to consume and/or package analyzers #69069

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

Merged

Conversation

ViktorHofer
Copy link
Member

@ViktorHofer ViktorHofer commented May 9, 2022

Fixes #61321
Fixes #69111

Until now we required source libraries to define ProjectReferences when
an analyzer should be part of the shared framework. That strategy causes
analyzer projects to leak into the ProjectReference closure and by that
into a solution file.

As an example:
When another library references the source library that references the
analyzer, the analyzer is part of the dependency closure even though it
might not be required.

This change makes it possible to define the shared framework analyzer
projects in the NetCoreAppLibrary.props file for both the .NETCoreApp,
and the AspNetCoreApp shared framework.

Out-of-band projects which ship analyzers inside their produced package,
continue to reference the analyzers via the AnalyzerProject item.

@ViktorHofer ViktorHofer added this to the 7.0.0 milestone May 9, 2022
@ViktorHofer ViktorHofer requested review from ericstj and a team May 9, 2022 19:19
@ViktorHofer ViktorHofer self-assigned this May 9, 2022
@ghost
Copy link

ghost commented May 9, 2022

Tagging subscribers to this area: @dotnet/area-infrastructure-libraries
See info in area-owners.md if you want to be subscribed.

Issue Details

Fixes #61321

Until now we required source libraries to define ProjectReferences when
an analyzer should be part of the shared framework. That strategy causes
analyzer projects to leak into the ProjectReference closure and by that
into a solution file.

As an example:
When another library references the source library that references the
analyzer, the analyzer is part of the dependency closure even though it
might not be required.

This change makes it possible to define the shared framework analyzer
projects in the NetCoreAppLibrary.props file for both the .NETCoreApp,
and the AspNetCoreApp shared framework.

Out-of-band projects which ship analyzers inside their produced package,
continue to reference the analyzers via the AnalyzerProject item.

Author: ViktorHofer
Assignees: ViktorHofer
Labels:

area-Infrastructure-libraries

Milestone: 7.0.0

Fixes dotnet#61321

Until now we required source libraries to define ProjectReferences when
an analyzer should be part of the shared framework. That strategy causes
analyzer projects to leak into the ProjectReference closure and by that
into a solution file.

As an example:
When another library references the source library that references the
analyzer, the analyzer is part of the dependency closure even though it
might not be required.

This change makes it possible to define the shared framework analyzer
projects in the NetCoreAppLibrary.props file for both the .NETCoreApp,
and the AspNetCoreApp shared framework.

Out-of-band projects which ship analyzers inside their produced package,
continue to reference the analyzers via the `AnalyzerProject` item.
@ViktorHofer ViktorHofer force-pushed the DefineGeneratorProjectsConvention branch from 08ec129 to a826907 Compare May 10, 2022 09:09
@ViktorHofer ViktorHofer changed the title Define convention to include analyzers in ref pack Define convention to consume and/or package analyzers May 10, 2022
@ericstj ericstj requested review from joperezr and elinor-fung May 11, 2022 21:24
so we don't apply TargetFramework filters nor do we pass in TargetFramework.
When BuildProjectReferences=false we make sure to set BuildReference=false to make
sure not to try to call GetTargetPath in the outerbuild of the analyzer project. -->
<ProjectReference Include="@(AnalyzerReference)"
Copy link
Member

Choose a reason for hiding this comment

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

Why isn't this needed anymore?

Copy link
Member Author

@ViktorHofer ViktorHofer May 12, 2022

Choose a reason for hiding this comment

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

When you originally added this code, source generators unnecessarily multi-targeted (by using TargetFramework instead of TargetFrameworks). Since then, source generators were updated to not multi-target and we do have general protection that makes sure that they only target netstandard2.0. The TargetFramework.SDK was updated to support single tfms and same as in the SDK, drops the TargetFramework metadata from ProjectReference items (to not cause unnecessary evaluations).

Copy link
Member

Choose a reason for hiding this comment

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

Ok, I couldn't remember exactly why we added this and I tried to trace it back in history and couldn't see it. I trust your judgement. Maybe a few things to try for regression purposes:

  1. Open the S.T.J sln and build it.
  2. dotnet build --no-dependencies System.Text.Json.csproj
  3. dotnet pack System.Text.Json.csproj
  4. dotnet pack ---no-build System.Text.Json.csproj

$(LibrariesProjectRoot)System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
<AnalyzerReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\gen\LibraryImportGenerator\LibraryImportGenerator.csproj;
Copy link
Member

Choose a reason for hiding this comment

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

Do we still need to do this in targets? Could we make libraries in the shared framework use AnalyzerReference just like they have to declare their reference to Interop and then have libraries outside the shared framework just get it (like customer projects will, now that its in the refpack)?

Copy link
Member Author

@ViktorHofer ViktorHofer May 12, 2022

Choose a reason for hiding this comment

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

That's eventually the goal, yes. I just yesterday discussed this with @jkoritzinsky in the infrastructure stand-up. Anything that's OOB should get the analyzers via the targeting pack and anything inside the shared framework should explicitly reference the analyzers. Devil in the details: You still need an explicit analyzer reference when you build pre net7.0 (net6.0, netstandard2.0, net462).
Therefore, we will still need to reference the analyzer for anything that is not NetCoreAppCurrent. In addition to that, Visual Studio's CPS currently has a bug that causes ProjectReferences that aren't referenced by the "main" active configuration to not be built. Because of that, I would prefer to keep the explicit analyzers reference on NetCoreAppCurrent (OOB) for now.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do we still need to do this in targets? Could we make libraries in the shared framework use AnalyzerReference just like they have to declare their reference to Interop

Just to check, you did see that this logic doesn't run inside a target, just in a targets file, right?

What you are proposing could definitely be done BUT the current logic in generators.targets already does that automatically when you either reference System.Runtime.InteropServices or System.Private.CoreLib. IMO, I would treat the LibraryImportGenerator source generator as a transitive analyzer dependency of InteropServices and CoreLib. The current logic already does that.

Copy link
Member

Choose a reason for hiding this comment

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

Just to check, you did see that this logic doesn't run inside a target, just in a targets file, right?

Yes. I was just thinking that if we made it easy enough for projects to opt in it might be better to have them explicitly opt into referencing the analyzer, just like they explicitly reference every other reference. It could avoid that magic/complexity in the condition, and it might reduce the number of places that actually reference the analyzer.

<!-- Support building only the reference assemblies. -->
<ItemGroup Condition="'$(RefOnly)' != 'true'">
<ProjectReference Include="sfx-src.proj" OutputItemType="SharedFrameworkAssembly" />
<ProjectReference Include="sfx-gen.proj" />
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 double check that we still include the analyzers in the ref-pack after this change? Also the ASP.NET transport package.

Copy link
Member Author

Choose a reason for hiding this comment

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

Just verified this locally by building from a clean state and the analyzers are correctly placed into the targeting packs' and the packages' analyzers/dotnet/(cs)/ folder.

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 opened #69233 though as the LibraryImportGenerator might only work for C# projects and is currently not marked as lanague specific.

@ViktorHofer ViktorHofer merged commit d3af492 into dotnet:main May 12, 2022
@ViktorHofer ViktorHofer deleted the DefineGeneratorProjectsConvention branch May 12, 2022 07:22
@ghost ghost locked as resolved and limited conversation to collaborators Jun 12, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

AnalyzerReference vs ProjectReference We need a repo convention to ship source generators as part of the ref pack
3 participants