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

net5.0 warnings, errors, and auto-translation of aliases #3678

Merged
merged 2 commits into from
Sep 24, 2020

Conversation

zkat
Copy link
Contributor

@zkat zkat commented Sep 21, 2020

@zkat zkat requested a review from a team as a code owner September 21, 2020 03:41
@zkat zkat force-pushed the dev-zkat-net5-pack-changes branch from fefb519 to fe93439 Compare September 21, 2020 03:51
Copy link
Member

@nkolev92 nkolev92 left a 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.

@zkat
Copy link
Contributor Author

zkat commented Sep 21, 2020

@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.

Copy link
Member

@zivkan zivkan left a 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.

Copy link
Contributor

@dominoFire dominoFire left a 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);
Copy link
Contributor

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));

var isNet5EraTfm = targetFrameworkVersion.Major >= 5 &&
StringComparer.OrdinalIgnoreCase.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, targetFrameworkIdentifier);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This came up in review (I originally had this property set in NuGetFramework), but the feedback that I got pretty consistently is that people didn't want to have this as part of that class. /cc @zivkan @nkolev92

Copy link
Member

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.Common/Errors/NuGetLogCode.cs Outdated Show resolved Hide resolved
@zkat zkat requested review from nkolev92 and zivkan September 21, 2020 23:01
@@ -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; }
Copy link
Member

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.

@zkat zkat requested a review from zivkan September 22, 2020 21:57
var isNet5EraTfm = framework.Version.Major >= 5 && StringComparer.OrdinalIgnoreCase.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, framework.Framework);
if (isNet5EraTfm)
{
var dotIndex = targetFramework.IndexOf('.');
Copy link
Member

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.Build.Tasks.Pack/PackTaskLogic.cs Outdated Show resolved Hide resolved
src/NuGet.Core/NuGet.Commands/Strings.resx Outdated Show resolved Hide resolved
src/NuGet.Core/NuGet.Commands/Strings.resx Outdated Show resolved Hide resolved
@zkat zkat requested review from nkolev92 and dominoFire September 23, 2020 23:39
@@ -44,6 +44,18 @@ public static class Folders
Analyzers,
Source
};

public static string[] SupportFrameworks { get; } = new string[] {
Copy link
Member

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[] {
Copy link
Member

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,
Copy link
Member

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);
Copy link
Member

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.

Copy link
Member

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.

https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Packaging/Rules/NoRefOrLibFolderInPackageRule.cs

Copy link
Member

@nkolev92 nkolev92 left a 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);
Copy link
Member

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));
Copy link
Member

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,
Copy link
Member

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)
Copy link
Member

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

  1. iterate this list of folder names
  2. generate a path prefix for the folder name
  3. use managedCodeConventions.Patterns.AnyTargettedFile to find a list of all <any>/<tfm>/<any?> files
  4. 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.

Comment on lines 51 to 53
collection.Load(new string[] { path });

var targetedItems = ContentExtractor.GetContentForPattern(collection, managedCodeConventions.Patterns.AnyTargettedFile);
Copy link
Member

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);
Copy link
Member

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);
Copy link
Member

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)
Copy link
Member

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.

Comment on lines 889 to 899
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);
Copy link
Member

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);
Copy link
Member

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>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<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.

@zkat zkat force-pushed the dev-zkat-net5-pack-changes branch from d5a775b to 2d349b8 Compare September 24, 2020 22:51
Copy link
Member

@nkolev92 nkolev92 left a 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

@aortiz-msft aortiz-msft merged commit 0f052fe into dev Sep 24, 2020
@aortiz-msft aortiz-msft deleted the dev-zkat-net5-pack-changes branch September 24, 2020 23:23
zkat added a commit that referenced this pull request Sep 24, 2020
* translate aliases for lib and contentFiles

Fixes: NuGet/Home#10020

* quick test for the lib part
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

dotnet pack mistakenly uses alias for WPF Library projects
5 participants