-
Notifications
You must be signed in to change notification settings - Fork 470
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
Ensure that PlatformCompatibilityAnalyzer better handles various TFMs #7542
Conversation
...tAnalyzers/Core/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzer.cs
Outdated
Show resolved
Hide resolved
Hmmm, tests are failing because I don't quite see where that is normally supposed to be plumbed through/set, is this normally done by Roslyn? Are our tests skipping the normal csproj build setup and so manually setting these config knobs as if they had been set by MSBuild? |
Looks like its supposed to be done by For tests, I believe this means I need to explicitly set |
.../UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.cs
Outdated
Show resolved
Hide resolved
.../UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.cs
Show resolved
Hide resolved
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.
Product changes look good and I validated this shows warning in the command line (not in VS though). Test changes look good once they pass and shape didn't change significantly
.../UnitTests/Microsoft.NetCore.Analyzers/InteropServices/PlatformCompatibilityAnalyzerTests.cs
Outdated
Show resolved
Hide resolved
…w expected properties
if (tfmIdentifier.Equals(NetCoreAppIdentifier, StringComparison.OrdinalIgnoreCase) && | ||
tfmVersion.StartsWith("v", StringComparison.OrdinalIgnoreCase) && | ||
Version.TryParse(tfmVersion[1..], out var version) && | ||
version.Major >= 5) |
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.
Updated this to do the simpler check that matches what is required for the format, as per the discussion.
[*] | ||
build_property.TargetFramework = net5 | ||
build_property.TargetFrameworkIdentifier = .NETCoreApp | ||
build_property.TargetFrameworkVersion = v5.0 |
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.
Since these tests aren't actually going through msbuild, it means the props/targets that would normally automatically provide these don't run; hence the tests need to pass it along explicitly.
In all cases I've checked what msbuild proper would generate for a given TargetFramework
value and ensured the TargetFrameworkIdentifier
and TargetFrameworkVersion
values we pass along match that behavior
yield return new object[] { "build_property.TargetFramework = net5.0\nbuild_property.TargetFrameworkIdentifier=.NETCoreApp\nbuild_property.TargetFrameworkVersion=v5.0", true }; | ||
yield return new object[] { "build_property.TargetFramework = net5.0-windows\nbuild_property.TargetFrameworkIdentifier=.NETCoreApp\nbuild_property.TargetFrameworkVersion=v5.0", true }; | ||
yield return new object[] { "build_property.TargetFramework = net5.0-ios14.0\nbuild_property.TargetFrameworkIdentifier=.NETCoreApp\nbuild_property.TargetFrameworkVersion=v5.0", true }; | ||
yield return new object[] { "build_property.TargetFramework = netcoreapp5\nbuild_property.TargetFrameworkIdentifier=.NETCoreApp\nbuild_property.TargetFrameworkVersion=v5.0", true }; |
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.
A case to note is that TargetFramework=netcoreapp5
was previously treated as "unsupported" by the analyzer.
However, the actual MSBuild path done by our props/targets recognizes this TFM and infers TargetFrameworkIdentifier=.NETCoreApp
and TargetFrameworkVersion=v5.0
hence is equivalent to TargetFramework=net5.0
and thus is handled by the analyzer under the new code
yield return new object[] { "build_property.TargetFramework = netstandard2.1\nbuild_property.TargetFrameworkIdentifier=.NETStandard\nbuild_property.TargetFrameworkVersion=v2.1", false }; | ||
|
||
// .NET 5+ TFMs with a single major version digit | ||
yield return new object[] { "build_property.TargetFramework = net5\nbuild_property.TargetFrameworkIdentifier=.NETCoreApp\nbuild_property.TargetFrameworkVersion=v5.0", true }; |
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.
Another case to note is that while TargetFrameworkVersion
must have two parts, and thus must be v5.0
, the actual TFM does not require this.
Simply doing TargetFramework=net5
is recognized by the inferrence and produces TargetFrameworkIdentifier=.NETCoreApp
and TargetFrameworkVersion=v5.0
hence is equivalent to TargetFramework=net5.0
and thus is handled by the analyzer under the new code
yield return new object[] { "build_property.TargetFramework = net10.0\nbuild_property.TargetFrameworkIdentifier=.NETCoreApp\nbuild_property.TargetFrameworkVersion=v10.0", true }; | ||
yield return new object[] { "build_property.TargetFramework = net11.0\nbuild_property.TargetFrameworkIdentifier=.NETCoreApp\nbuild_property.TargetFrameworkVersion=v11.0", true }; |
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.
These are the cases that were unexpectedly failing and which are being fixed by this PR.
// Custom TFMs with valid user specified Identifier/Version | ||
yield return new object[] { "build_property.TargetFramework = nonesense\nbuild_property.TargetFrameworkIdentifier=.NETCoreApp\nbuild_property.TargetFrameworkVersion=v11.0", true }; | ||
|
||
// Custom TFMs with invalid user specified Identifier/Version | ||
yield return new object[] { "build_property.TargetFramework = nonesense\nbuild_property.TargetFrameworkIdentifier=.NETCoreApp\nbuild_property.TargetFrameworkVersion=v5", false }; | ||
yield return new object[] { "build_property.TargetFramework = nonesense\nbuild_property.TargetFrameworkIdentifier=NETCoreApp\nbuild_property.TargetFrameworkVersion=v5.0", false }; | ||
|
||
// Custom TFMs with inferred Identifier/Version | ||
yield return new object[] { "build_property.TargetFramework = nonesense\nbuild_property.TargetFrameworkIdentifier=Unsupported\nbuild_property.TargetFrameworkVersion=v0.0", false }; |
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.
These are additional cases that cover custom TFMs to help assert we're handling them as expected
public const string TargetFrameworkIdentifier = nameof(TargetFrameworkIdentifier); | ||
public const string TargetFrameworkVersion = nameof(TargetFrameworkVersion); |
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 is what causes the CompilerVisibleProperty
items to be generated in the props/targets that are inserted into the SDK and is what causes the build_property.*
metadata to be automatically visible to analyzers by default.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #7542 +/- ##
=======================================
Coverage 96.50% 96.50%
=======================================
Files 1452 1452
Lines 347566 347574 +8
Branches 11416 11418 +2
=======================================
+ Hits 335415 335425 +10
+ Misses 9255 9252 -3
- Partials 2896 2897 +1 |
int.TryParse(tfmString[3].ToString(), out var major) && | ||
major >= 5) | ||
if (tfmIdentifier.Equals(NetCoreAppIdentifier, StringComparison.OrdinalIgnoreCase) && | ||
tfmVersion.StartsWith("v", StringComparison.OrdinalIgnoreCase) && |
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'm not 100% sure about the leading v
here. What's the value of the check here? I was imagining that we would just do a TrimStart('vV')
before we read from the value. We do that in other places in SDK: https://github.com/dotnet/sdk/blob/0bc6363db69ab830662979d35bfca9f941ab0736/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.BeforeCommon.targets#L191
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 depends on if we expect TargetFrameworkVersion
to be in the format v#.#[.#[.#]]
or [v]#.#[.#[.#]]
The only case we'd not have the v
is users specifying custom values and following the standard format feels much safer, particularly given the existence of _TargetFrameworkVersionWithoutV
and other assumptions we make.
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.
@ericstj any opinions on this?
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 have the https://github.com/dotnet/runtime/blob/1f93ea7763a6391a9063c5e35843253f3d666893/src/libraries/System.Private.CoreLib/src/System/Runtime/Versioning/FrameworkName.cs type that we could use to just parse the value of TargetFrameworkMoniker
property
So we could just use that, but barring that I think trimming is better -- since we do that ourselves.
https://github.com/dotnet/runtime/blob/1f93ea7763a6391a9063c5e35843253f3d666893/src/libraries/System.Private.CoreLib/src/System/Runtime/Versioning/FrameworkName.cs#L163-L170
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.
type that we could use to just parse the value of TargetFrameworkMoniker property
This seems like an overall worse thing to depend on as it would result in various throwing cases we'd have to try/catch in the analyzer to ensure robustness.
It also seems like "extra work" as TargetFrameworkMoniker
is generated from TargetFrameworkIdentifier
+ TargetFrameworkVersion
which are what feed most other things in MSBuild; not the other way around.
So we could just use that, but barring that I think trimming is better -- since we do that ourselves.
The code is already trimming, the question was rather whether TargetFrameworkVersion
should require the v
prefix or not. The code currently looks for it to exist and fallsback to the LowerTargetsEnabled
path if its not present.
None of the normal MSBuild paths will skip specifying the v
, so the only scenario that'd be encounterable is if a user is manually specifying TargetFrameworkIdentifier
+TargetFrameworkVersion
.
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.
Well done on this, @tannergooding. Thank you for the very descriptive PR comments that aided in the review.
Shoot -- I merged and then the page refreshed and showed recent comments. Sorry, @tannergooding and @ericstj. |
Shouldn't be an issue @jeffhandley. The logic here is already significantly better/more robust than what we had and will resolve the issue users were seeing The question is really just whether we want to allow |
It's your call if you want to come back and clean this up. MSBuild properties are the wild west in terms of input validation / extensibility / etc so I usually try to look for existing usage/assumptions when taking a dependency. Agreed that this discussion wasn't blocking - I just chimed in with my take on the existing discussion. FWIW this change has made it to the SDK so it should be in Preview1 builds dotnet/sdk@c2f13b4 |
Sounds good. I'll submit a PR allowing no |
Put up #7544 |
This resolves #7525