-
Notifications
You must be signed in to change notification settings - Fork 692
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
net5.0 warnings, errors, and auto-translation of aliases #3678
Conversation
fefb519
to
fe93439
Compare
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.
Not a complete review, have a meeting will come back to it after.
I think there's some extra duplication, but I'm not clear about that 100% yet.
Most of the line specific comments should be actionable though.
src/NuGet.Core/NuGet.Commands/CommandRunners/PackCommandRunner.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Commands/CommandRunners/PackCommandRunner.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/Rules/InvalidUndottedFrameworkRule.cs
Outdated
Show resolved
Hide resolved
@nkolev92 some of the duplication you're seeing is because the code is... a bit of a maze, and I needed to put things in two places just to get them working for both SDK/msbuild scenarios AND nuspec scenarios. |
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.
Most of my comments are coding guidelines issues, but I would like the public APIs to be improved before I approve.
src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/PackageCreation/Resources/NuGetResources.resx
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/Rules/InvalidUndottedFrameworkRule.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/Rules/InvalidUndottedFrameworkRule.cs
Outdated
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.
Comments on NuGet Log codes.
|
||
string targetFrameworkString = Path.GetDirectoryName(path).Split(Path.DirectorySeparatorChar).ElementAt(1); | ||
|
||
var isNet5EraTfm = fw.Version.Major >= 5 && StringComparer.OrdinalIgnoreCase.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, fw.Framework); |
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 create an utility method to test .NET 5 era condition? I have seen this in multiple parts of the codebase
public static bool isNet5Era(Version targetFrameworkVersion, string targetFrameworkIdentifier);
See also:
IsNet5Era = (_frameworkVersion.Major >= Version5 && StringComparer.OrdinalIgnoreCase.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, _frameworkIdentifier)); |
NuGet.Client/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkFactory.cs
Lines 139 to 140 in 0e54137
var isNet5EraTfm = targetFrameworkVersion.Major >= 5 && | |
StringComparer.OrdinalIgnoreCase.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, targetFrameworkIdentifier); |
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.
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 don't like the name IsNet5Era
. It's acceptable for internal use, but public APIs should be 1. as obvious/easy to understand as possible, without reading docs 2. try to be "future proof".
Will a IsNet5Era
API be easy to understand in 5 or 10 years from now when everyone is using .NET 10, .NET 15? Even now, will someone browsing our public APIs via IntelliSense be able to easily guess what the API means, and when they should use it?
But given we do the check in several assemblies, there's a good chance that some customers will want to as well. Maybe DoesTargetFrameworkUsePlatform(...)
? IsPlatformUsed(...)
? UsesPlatform(...)
? UsesPlatformMoniker(..)
?
Also, given the TFM format is {tfi},Version=v{tfv}
, I'd suggest that the parameters should be ordered (string identifier, Version version)
, regardless of what order they're used in the method body.
src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt
Outdated
Show resolved
Hide resolved
@@ -42,6 +42,7 @@ public class PackArgs | |||
public bool Tool { get; set; } | |||
public string Version { get; set; } | |||
public bool Deterministic { get; set; } | |||
public IDictionary<string, string> AliasMappings { get; set; } |
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 be done later: I know most of NuGet doesn't have good XML doc, but since this is part of our public API and therefore customers might use it, I think it would be great to have XML doc to explain public things. It might be obvious for other properties such as Version
, Deterministic
, and so on, but there's no way for customers to guess what this dictionary's key and value represent, so that should be documented. Consider, for example, the xmldocs I wrote for the GetInstalledPackagesAsync model.
src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs
Outdated
Show resolved
Hide resolved
var isNet5EraTfm = framework.Version.Major >= 5 && StringComparer.OrdinalIgnoreCase.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, framework.Framework); | ||
if (isNet5EraTfm) | ||
{ | ||
var dotIndex = targetFramework.IndexOf('.'); |
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'd be nice if this was extracted to a utility so that it can get unit tested properly (said utility can be internal).
src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/Core/FrameworkNameValidatorUtility.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/PackageCreation/Resources/NuGetResources.resx
Outdated
Show resolved
Hide resolved
src/NuGet.Core/NuGet.Packaging/Rules/InvalidUndottedFrameworkRule.cs
Outdated
Show resolved
Hide resolved
test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackTaskLogicTests.cs
Outdated
Show resolved
Hide resolved
@@ -44,6 +44,18 @@ public static class Folders | |||
Analyzers, | |||
Source | |||
}; | |||
|
|||
public static string[] SupportFrameworks { get; } = new string[] { |
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 use ImmutableArray
or IReadOnlyCollection
or IReadOnlyList
instead of string[]
? Right now, someone can do `SupportFrameworks[0] = "whatever" and suddenly NuGet is going to behave differently.
@@ -44,6 +44,18 @@ public static class Folders | |||
Analyzers, | |||
Source | |||
}; | |||
|
|||
public static string[] SupportFrameworks { get; } = new string[] { |
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 make this internal
? It doesn't seem to be used outside of NuGet.Packaging
, and I don't think it's particularly useful for customers using this package.
Tools, | ||
ContentFiles, | ||
Lib, | ||
Native, |
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've never heard of a native/
package folder before, although I see it's listed in the Known
array. Still, I'm not sure we ever look for native/<tfm>/*
.
} | ||
else | ||
{ | ||
frameworkPart = path.Substring(folderPrefix.Length); |
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 just remembered that a class named ManagedCodeConventions
exists, and it lists the path conventions for each. For example, look at the runtimes folder: https://github.com/NuGet/NuGet.Client/blob/9f521755495c05bc03efd5365762e32fde98761f/src/NuGet.Core/NuGet.Packaging/ContentModel/ManagedCodeConventions.cs#L415
also the multi-targetd build files has a deprecated path that is still used:
https://github.com/NuGet/NuGet.Client/blob/9f521755495c05bc03efd5365762e32fde98761f/src/NuGet.Core/NuGet.Packaging/ContentModel/ManagedCodeConventions.cs#L505-L508
I assume that the PatternSet
class has methods to parse the path for you, so you don't need to do all this substring stuff.
I'm really sorry I didn't remember this earlier :( particularly as the current implementation doesn't correctly get the TFM for all package folders.
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 even remember giving this feedback to Joshua a while ago :(
Here's a rule that makes use of that.
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 think a good test would be to run E2E test for all the places where framework is inferred from the path.
In particular I think it's lib + contentFiles folders.
Basically I searched for TfmSpecific
in the pack.targets, https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Build.Tasks.Pack/NuGet.Build.Tasks.Pack.targets.
} | ||
else | ||
{ | ||
packagePath = Path.GetFileName(fullPath); | ||
} | ||
return Path.Combine(targetPath ?? String.Empty, packagePath); | ||
|
||
string path = Path.Combine(targetPath ?? string.Empty, packagePath); |
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.
Looking through PackTaskLogic, there are FrameworkAssemblyRefs that are also gonna have this problem.
var targetFramework = tfmRef.GetProperty("TargetFramework"); |
AssemblyReferences are not a thing in netcore though, so it's only really, really custom scenarios that'll be broken.
It's safe to fix later
var frameworkVersionHasDot = (dashIndex > -1 && dotIndex > -1 && dotIndex < dashIndex) || (dashIndex == -1 && dotIndex > -1); | ||
if (!frameworkVersionHasDot) | ||
{ | ||
logger.LogWarning(string.Format(CultureInfo.CurrentCulture, Strings.MissingRequiredDot, NuGetLogCode.NU5502, targetFramework)); |
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 code block is checking the lib/<tfm>/project.dll
tfm to make sure it has a dot. Since this check is also being done in PackageBuilder
, this code is an unnecessary duplicate.
/// <summary> | ||
/// MissingRequiredDot | ||
/// </summary> | ||
NU5502 = 5502, |
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.
5501 and 5502 are the same scenario, just in different parts of the code. We should only have one code for the scenario.
@@ -34,6 +38,60 @@ internal static bool IsValidFrameworkName(string path) | |||
return fx != null && fx.Framework != NuGetFramework.UnsupportedFramework.Framework; | |||
} | |||
|
|||
internal static bool IsDottedFrameworkVersion(string path) | |||
{ | |||
foreach (string knownFolder in PackagingConstants.Folders.SupportFrameworks) |
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.
The way this works is
- iterate this list of folder names
- generate a path prefix for the folder name
- use
managedCodeConventions.Patterns.AnyTargettedFile
to find a list of all<any>/<tfm>/<any?>
files - filter the list of "any targetted file" with the prefix from 2.
However, if you instead have a list of PatternSet
of interesting folders, then you can call ContenxtExtractor.GetContentForPattern
on this pattern set, and avoid having to filter the results.
collection.Load(new string[] { path }); | ||
|
||
var targetedItems = ContentExtractor.GetContentForPattern(collection, managedCodeConventions.Patterns.AnyTargettedFile); |
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.
you can add all the files in the package, and let ContentExtractor
return the files that match, rather than this one-file-at-a-time approach.
return true; | ||
} | ||
|
||
string targetFrameworkString = Path.GetDirectoryName(path).Split(Path.DirectorySeparatorChar).ElementAt(1); |
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.
runtimes/
does not put tfm at index 1.
{ | ||
var dotIndex = targetFrameworkString.IndexOf('.'); | ||
var dashIndex = targetFrameworkString.IndexOf('-'); | ||
var frameworkVersionHasDots = (dashIndex > -1 && dotIndex > -1 && dotIndex < dashIndex) || (dashIndex == -1 && dotIndex > -1); |
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.
follow up later: Looking at the net5 tfm spec, it looks like we're supposed to ensure that target platform version also has a dot.
string path = Path.Combine(targetPath ?? string.Empty, packagePath); | ||
|
||
// Translate the tfm alias to its actual framework if necessary. | ||
foreach (string knownFolder in PackagingConstants.Folders.Known) |
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.
Folders.Known
doesn't seem right, since there are folders that don't have a TFM folder.
if (knownFolder == PackagingConstants.Folders.ContentFiles && path.Count(sep => sep == Path.DirectorySeparatorChar) > 2) | ||
{ | ||
// ContentFiles might have an extra level of nesting for the | ||
// language, so we have to skip over _that_ in order | ||
// to get to the framework itself. | ||
var languageEndIndex = path.IndexOf(System.IO.Path.DirectorySeparatorChar, folderPrefix.Length + 1); | ||
frameworkPart = path.Substring(folderPrefix.Length + (languageEndIndex - folderPrefix.Length)); | ||
} | ||
else | ||
{ | ||
frameworkPart = path.Substring(folderPrefix.Length); |
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 code doesn't correctly support runtimes/
as the tfm isn't the second folder name. Maybe we can use ManagedCodeConventions here to extract the TFM reliably?
if (AliasFolderNameMapping != null && AliasFolderNameMapping.TryGetValue(aliasString, out frameworkString)) | ||
#pragma warning restore CS0618 | ||
{ | ||
path = folderPrefix + frameworkString + Path.DirectorySeparatorChar + path.Substring(folderPrefix.Length + aliasString.Length + 1); |
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 worried this doesn't correctly use the right folderPrefix for contentFiles
or runtimes
.
@@ -212,4 +212,8 @@ | |||
<value>The element 'icon' cannot be empty.</value> | |||
<comment>Please, don't localize icon</comment> | |||
</data> | |||
<data name="InvalidPlatformVersion" xml:space="preserve"> | |||
<value>Target Framework '{0}' has a platform component, but the platform could not be inferred. This is likely a problem with NuGet itself.</value> |
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.
<value>Target Framework '{0}' has a platform component, but the platform could not be inferred. This is likely a problem with NuGet itself.</value> | |
<value>Target Framework '{0}' has a platform component, but the platform could not be inferred.</value> |
If a customer does <None Include="whatever.txt" PackagePath="lib/net5.0-windows/" />
, then this isn't an internal issue at all.
d5a775b
to
2d349b8
Compare
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're obviously missing tests, but I am ok as long as the product changes stay the same :D
* translate aliases for lib and contentFiles Fixes: NuGet/Home#10020 * quick test for the lib part
Fixes: NuGet/Home#10020