From 11c2bcb5d186b27b4839b6a8e2f7f4a98ba13e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Wed, 21 Oct 2020 15:08:12 -0700 Subject: [PATCH] raise error when missing tpv and platform present (#3691) Fixes: https://github.com/NuGet/Home/issues/9441 --- .../RestoreCommand/RestoreCommand.cs | 28 ++++- .../NuGet.Commands/Strings.Designer.cs | 9 ++ src/NuGet.Core/NuGet.Commands/Strings.resx | 4 + .../NuGet.Common/Errors/NuGetLogCode.cs | 17 ++- .../PublicAPI/net45/PublicAPI.Unshipped.txt | 1 + .../PublicAPI/net472/PublicAPI.Unshipped.txt | 1 + .../netstandard2.0/PublicAPI.Unshipped.txt | 1 + .../Authoring/PackageBuilder.cs | 106 +++++++++++++++- .../NuGet.Packaging/Strings.Designer.cs | 45 +++++++ src/NuGet.Core/NuGet.Packaging/Strings.resx | 22 +++- .../RestoreCommandTests.cs | 66 ++++++++++ .../PackageBuilderTest.cs | 116 +++++++++++++++++- 12 files changed, 401 insertions(+), 15 deletions(-) diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs index c0b5b8bfe9b..571733f3229 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs @@ -181,6 +181,8 @@ public async Task ExecuteAsync(CancellationToken token) _success = false; } + _success &= HasValidPlatformVersions(); + // evaluate packages.lock.json file var packagesLockFilePath = PackagesLockFileUtilities.GetNuGetLockFilePath(_request.Project); var isLockFileValid = false; @@ -404,6 +406,26 @@ public async Task ExecuteAsync(CancellationToken token) } } + private bool HasValidPlatformVersions() + { + IEnumerable badPlatforms = _request.Project.TargetFrameworks + .Select(frameworkInfo => frameworkInfo.FrameworkName) + .Where(framework => !string.IsNullOrEmpty(framework.Platform) && (framework.PlatformVersion == FrameworkConstants.EmptyVersion)); + + if (badPlatforms.Any()) + { + _logger.Log(RestoreLogMessage.CreateError( + NuGetLogCode.NU1012, + string.Format(CultureInfo.CurrentCulture, Strings.Error_PlatformVersionNotPresent, string.Join(", ", badPlatforms)) + )); + return false; + } + else + { + return true; + } + } + private async Task AreCentralVersionRequirementsSatisfiedAsync() { // The dependencies should not have versions explicitelly defined if cpvm is enabled. @@ -627,7 +649,7 @@ private bool ValidatePackagesSha512(PackagesLockFile lockFile, LockFile assetsFi { if (!noOp) { - // Clean up to preserve the pre no-op behavior. This should not be used, but we want to be cautious. + // Clean up to preserve the pre no-op behavior. This should not be used, but we want to be cautious. _request.LockFilePath = null; _request.Project.RestoreMetadata.CacheFilePath = null; } @@ -727,7 +749,7 @@ private LockFile BuildAssetsFile( /// /// Check if the given graphs are valid and log errors/warnings. /// If fatal errors are encountered the rest of the errors/warnings - /// are not logged. This is to avoid flooding the log with long + /// are not logged. This is to avoid flooding the log with long /// dependency chains for every package. /// private async Task ValidateRestoreGraphsAsync(IEnumerable graphs, ILogger logger) @@ -1108,7 +1130,7 @@ private List GetProjectReferences(RemoteWalkContext co else { // External references were passed, but the top level project wasn't found. - // This is always due to an internal issue and typically caused by errors + // This is always due to an internal issue and typically caused by errors // building the project closure. Debug.Fail("RestoreRequest.ExternalProjects contains references, but does not contain the top level references. Add the project we are restoring for."); throw new InvalidOperationException($"Missing external reference metadata for {_request.Project.Name}"); diff --git a/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs b/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs index ef8dac6a044..90571007e14 100644 --- a/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs @@ -672,6 +672,15 @@ internal static string Error_PackFailed { } } + /// + /// Looks up a localized string similar to Platform version is not present for one or more target frameworks, even though they have specified a platform: {0}. + /// + internal static string Error_PlatformVersionNotPresent { + get { + return ResourceManager.GetString("Error_PlatformVersionNotPresent", resourceCulture); + } + } + /// /// Looks up a localized string similar to Error occurred when processing file '{0}': {1}. /// diff --git a/src/NuGet.Core/NuGet.Commands/Strings.resx b/src/NuGet.Core/NuGet.Commands/Strings.resx index 6fe849ff6aa..f4ed86956b5 100644 --- a/src/NuGet.Core/NuGet.Commands/Strings.resx +++ b/src/NuGet.Core/NuGet.Commands/Strings.resx @@ -1114,4 +1114,8 @@ For more information, visit https://docs.nuget.org/docs/reference/command-line-r {0} contains more than one path + + Platform version is not present for one or more target frameworks, even though they have specified a platform: {0} + 0 - comma-separated list of TFMs missing a platform version + \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs b/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs index bb8068e5d7b..ea0c78d6c10 100644 --- a/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs +++ b/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs @@ -4,18 +4,18 @@ namespace NuGet.Common { /// - /// This enum is used to quantify NuGet error and warning codes. + /// This enum is used to quantify NuGet error and warning codes. /// Format - NUxyzw where NU is the prefix indicating NuGet and xyzw is a 4 digit code /// /// Numbers - xyzw /// x - 'x' is the largest digit and should be used to quantify a set of errors. /// For example 1yzw are set of restore related errors and no other code path should use the range 1000 to 1999 for errors or warnings. - /// + /// /// y - 'y' is the second largest digit and should be used for sub sections withing a broad category. - /// + /// /// For example 12zw cvould be http related errors. /// Further 'y' = 0-4 should be used for errors and 'y' = 5-9 should be warnings. - /// + /// /// zw - 'zw' are the least two digit. /// These could be used for different errors or warnings within the broad categories set by digits 'xy'. /// @@ -31,9 +31,9 @@ namespace NuGet.Common /// 1200/1700 - Compat /// 1300/1800 - Feed /// 1400/1900 - Package - /// + /// /// All new codes need a corresponding MarkDown file under https://github.com/NuGet/docs.microsoft.com-nuget/tree/master/docs/reference/errors-and-warnings. - /// + /// /// public enum NuGetLogCode { @@ -102,6 +102,11 @@ public enum NuGetLogCode /// NU1011 = 1011, + /// + /// Platform version not found. + /// + NU1012 = 1012, + /// /// Unable to resolve package, generic message for unknown type constraints. /// diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt index ea407d9c998..27e57301d51 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/net45/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode +NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt index ea407d9c998..27e57301d51 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode +NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode diff --git a/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index ea407d9c998..27e57301d51 100644 --- a/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Common/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,3 +1,4 @@ NuGet.Common.NuGetLogCode.NU1010 = 1010 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU1011 = 1011 -> NuGet.Common.NuGetLogCode NuGet.Common.NuGetLogCode.NU5501 = 5501 -> NuGet.Common.NuGetLogCode +NuGet.Common.NuGetLogCode.NU1012 = 1012 -> NuGet.Common.NuGetLogCode diff --git a/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs b/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs index 37d151cb480..a9f46c75114 100644 --- a/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs +++ b/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs @@ -12,11 +12,14 @@ using System.Reflection; using System.Reflection.Emit; using System.Xml.Linq; +using NuGet.Client; using NuGet.Common; +using NuGet.ContentModel; using NuGet.Frameworks; using NuGet.Packaging.Core; using NuGet.Packaging.PackageCreation.Resources; using NuGet.Packaging.Rules; +using NuGet.RuntimeModel; using NuGet.Versioning; namespace NuGet.Packaging @@ -226,7 +229,7 @@ public ISet Tags } /// - /// Exposes the additional properties extracted by the metadata + /// Exposes the additional properties extracted by the metadata /// extractor or received from the command line. /// public Dictionary Properties @@ -377,8 +380,10 @@ public void Save(Stream stream) ValidateDependencies(Version, DependencyGroups); ValidateReferenceAssemblies(Files, PackageAssemblyReferences); + ValidateFrameworkAssemblies(FrameworkReferences, FrameworkReferenceGroups); ValidateLicenseFile(Files, LicenseMetadata); ValidateIconFile(Files, Icon); + ValidateFileFrameworks(Files); using (var package = new ZipArchive(stream, ZipArchiveMode.Create, leaveOpen: true)) { @@ -543,6 +548,15 @@ private static bool HasXdtTransformFile(ICollection contentFiles) private static void ValidateDependencies(SemanticVersion version, IEnumerable dependencies) { + var frameworksMissingPlatformVersion = new HashSet(dependencies + .Select(group => group.TargetFramework) + .Where(groupFramework => groupFramework.HasPlatform && groupFramework.PlatformVersion == FrameworkConstants.EmptyVersion) + .Select(framework => framework.GetShortFolderName())); + if (frameworksMissingPlatformVersion.Any()) + { + throw new PackagingException(NuGetLogCode.NU1012, String.Format(CultureInfo.CurrentCulture, Strings.MissingTargetPlatformVersionsFromDependencyGroups, string.Join(", ", frameworksMissingPlatformVersion.OrderBy(str => str)))); + } + if (version == null) { // We have independent validation for null-versions. @@ -557,6 +571,15 @@ private static void ValidateDependencies(SemanticVersion version, public static void ValidateReferenceAssemblies(IEnumerable files, IEnumerable packageAssemblyReferences) { + var frameworksMissingPlatformVersion = new HashSet(packageAssemblyReferences + .Select(group => group.TargetFramework) + .Where(groupFramework => groupFramework.HasPlatform && groupFramework.PlatformVersion == FrameworkConstants.EmptyVersion) + .Select(framework => framework.GetShortFolderName())); + if (frameworksMissingPlatformVersion.Any()) + { + throw new PackagingException(NuGetLogCode.NU1012, string.Format(CultureInfo.CurrentCulture, Strings.MissingTargetPlatformVersionsFromReferenceGroups, string.Join(", ", frameworksMissingPlatformVersion.OrderBy(str => str)))); + } + var libFiles = new HashSet(from file in files where !String.IsNullOrEmpty(file.Path) && file.Path.StartsWith("lib", StringComparison.OrdinalIgnoreCase) select Path.GetFileName(file.Path), StringComparer.OrdinalIgnoreCase); @@ -573,6 +596,30 @@ public static void ValidateReferenceAssemblies(IEnumerable files, } } + private static void ValidateFrameworkAssemblies(IEnumerable references, IEnumerable referenceGroups) + { + // Check standalone references + var frameworksMissingPlatformVersion = new HashSet(references + .SelectMany(reference => reference.SupportedFrameworks) + .Where(framework => framework.HasPlatform && framework.PlatformVersion == FrameworkConstants.EmptyVersion) + .Select(framework => framework.GetShortFolderName()) + ); + if (frameworksMissingPlatformVersion.Any()) + { + throw new PackagingException(NuGetLogCode.NU1012, string.Format(CultureInfo.CurrentCulture, Strings.MissingTargetPlatformVersionsFromFrameworkAssemblyReferences, string.Join(", ", frameworksMissingPlatformVersion.OrderBy(str => str)))); + } + + // Check reference groups too + frameworksMissingPlatformVersion = new HashSet(referenceGroups + .Select(group => group.TargetFramework) + .Where(groupFramework => groupFramework.HasPlatform && groupFramework.PlatformVersion == FrameworkConstants.EmptyVersion) + .Select(framework => framework.GetShortFolderName())); + if (frameworksMissingPlatformVersion.Any()) + { + throw new PackagingException(NuGetLogCode.NU1012, string.Format(CultureInfo.CurrentCulture, Strings.MissingTargetPlatformVersionsFromFrameworkAssemblyGroups, string.Join(", ", frameworksMissingPlatformVersion.OrderBy(str => str)))); + } + } + private void ValidateLicenseFile(IEnumerable files, LicenseMetadata licenseMetadata) { if (!PackageTypes.Contains(PackageType.SymbolsPackage) && licenseMetadata?.Type == LicenseType.File) @@ -649,6 +696,63 @@ private void ValidateIconFile(IEnumerable files, string iconPath) } } + private static void ValidateFileFrameworks(IEnumerable files) + { + var set = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var file in files.Where(t => t.Path != null).Select(t => PathUtility.GetPathWithDirectorySeparator(t.Path))) + { + set.Add(file); + } + + var managedCodeConventions = new ManagedCodeConventions(new RuntimeGraph()); + var collection = new ContentItemCollection(); + collection.Load(set.Select(path => path.Replace('\\', '/')).ToArray()); + + var patterns = managedCodeConventions.Patterns; + + var frameworkPatterns = new List() + { + patterns.RuntimeAssemblies, + patterns.CompileRefAssemblies, + patterns.CompileLibAssemblies, + patterns.NativeLibraries, + patterns.ResourceAssemblies, + patterns.MSBuildFiles, + patterns.ContentFiles, + patterns.ToolsAssemblies, + patterns.EmbedAssemblies, + patterns.MSBuildTransitiveFiles + }; + var warnPaths = new HashSet(); + + var frameworksMissingPlatformVersion = new HashSet(); + foreach (var pattern in frameworkPatterns) + { + IEnumerable targetedItemGroups = ContentExtractor.GetContentForPattern(collection, pattern); + foreach (ContentItemGroup group in targetedItemGroups) + { + foreach (ContentItem item in group.Items) + { + var framework = (NuGetFramework)item.Properties["tfm"]; + if (framework == null) + { + continue; + } + + if (framework.HasPlatform && framework.PlatformVersion == FrameworkConstants.EmptyVersion) + { + frameworksMissingPlatformVersion.Add(framework.GetShortFolderName()); + } + } + } + } + + if (frameworksMissingPlatformVersion.Any()) + { + throw new PackagingException(NuGetLogCode.NU1012, string.Format(CultureInfo.CurrentCulture, Strings.MissingTargetPlatformVersionsFromIncludedFiles, string.Join(", ", frameworksMissingPlatformVersion.OrderBy(str => str)))); + } + } + private void ReadManifest(Stream stream, string basePath, Func propertyProvider) { // Deserialize the document and extract the metadata diff --git a/src/NuGet.Core/NuGet.Packaging/Strings.Designer.cs b/src/NuGet.Core/NuGet.Packaging/Strings.Designer.cs index 8225a8209b6..df74cb33094 100644 --- a/src/NuGet.Core/NuGet.Packaging/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.Packaging/Strings.Designer.cs @@ -789,6 +789,51 @@ internal static string MissingPackageTypeName { } } + /// + /// Looks up a localized string similar to Some dependency group TFMs are missing a platform version: {0}. + /// + internal static string MissingTargetPlatformVersionsFromDependencyGroups { + get { + return ResourceManager.GetString("MissingTargetPlatformVersionsFromDependencyGroups", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Some reference assembly group TFMs are missing a platform version: {0}. + /// + internal static string MissingTargetPlatformVersionsFromFrameworkAssemblyGroups { + get { + return ResourceManager.GetString("MissingTargetPlatformVersionsFromFrameworkAssemblyGroups", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Some framework assembly reference TFMs are missing a platform version: {0}. + /// + internal static string MissingTargetPlatformVersionsFromFrameworkAssemblyReferences { + get { + return ResourceManager.GetString("MissingTargetPlatformVersionsFromFrameworkAssemblyReferences", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Some included files are included under TFMs which are missing a platform version: {0}. + /// + internal static string MissingTargetPlatformVersionsFromIncludedFiles { + get { + return ResourceManager.GetString("MissingTargetPlatformVersionsFromIncludedFiles", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Some reference group TFMs are missing a platform version: {0}. + /// + internal static string MissingTargetPlatformVersionsFromReferenceGroups { + get { + return ResourceManager.GetString("MissingTargetPlatformVersionsFromReferenceGroups", resourceCulture); + } + } + /// /// Looks up a localized string similar to Multiple {0} attributes are not allowed.. /// diff --git a/src/NuGet.Core/NuGet.Packaging/Strings.resx b/src/NuGet.Core/NuGet.Packaging/Strings.resx index 45c41490349..846d2a39e37 100644 --- a/src/NuGet.Core/NuGet.Packaging/Strings.resx +++ b/src/NuGet.Core/NuGet.Packaging/Strings.resx @@ -839,4 +839,24 @@ Valid from: The timestamp service responded with HTTP status code '{0}' ('{1}'). {0} is the httpResponse.StatusCode, {1} is the httpResponse.ReasonPhrase. - + + Some dependency group TFMs are missing a platform version: {0} + 0 - comma-separated list of dependency group TFMs + + + Some reference assembly group TFMs are missing a platform version: {0} + 0 - comma-separated list of reference assembly group TFMs + + + Some included files are included under TFMs which are missing a platform version: {0} + 0 - comma-separated list of files with bad TFMs + + + Some reference group TFMs are missing a platform version: {0} + 0 - comma-separated list of reference group TFMs + + + Some framework assembly reference TFMs are missing a platform version: {0} + 0 - comma-separated list of reference group TFMs + + \ No newline at end of file diff --git a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs index 9b1e57c70ae..a0496becf86 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs @@ -10,6 +10,7 @@ using FluentAssertions; using Newtonsoft.Json.Linq; using NuGet.Commands.Test; +using NuGet.Common; using NuGet.Configuration; using NuGet.Frameworks; using NuGet.LibraryModel; @@ -2989,6 +2990,71 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( } } + [Fact] + public async Task RestoreCommand_WhenPlatformVersionIsEmpty_ThrowsError() + { + using (var pathContext = new SimpleTestPathContext()) + using (var context = new SourceCacheContext()) + { + var configJson = JObject.Parse(@" + { + ""frameworks"": { + ""net5.0-windows"": { + ""dependencies"": { + ""A"": { + ""version"" : ""1.0.0"", + } + } + } + } + }"); + + // Arrange + var packageA = new SimpleTestPackageContext("a", "1.0.0"); + packageA.Files.Clear(); + packageA.AddFile("lib/net5.0-windows/a.dll"); + + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + PackageSaveMode.Defaultv3, + packageA); + + var sources = new List + { + new PackageSource(pathContext.PackageSource) + }; + var logger = new TestLogger(); + + var projectDirectory = Path.Combine(pathContext.SolutionRoot, "TestProject"); + var cachingSourceProvider = new CachingSourceProvider(new PackageSourceProvider(NullSettings.Instance)); + + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.csproj")).WithTestRestoreMetadata(); + var dgSpec = new DependencyGraphSpec(); + dgSpec.AddProject(spec); + dgSpec.AddRestore(spec.Name); + + var request = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) + { + ProjectStyle = ProjectStyle.PackageReference, + DependencyGraphSpec = dgSpec, + AllowNoOp = true, + }; + var command = new RestoreCommand(request); + + // Act + var result = await command.ExecuteAsync(); + await result.CommitAsync(logger, CancellationToken.None); + + // Assert + Assert.False(result.Success); + Assert.Equal(1, logger.ErrorMessages.Count); + logger.ErrorMessages.TryDequeue(out var errorMessage); + Assert.True(errorMessage.Contains("Platform version")); + var messagesForNU1012 = result.LockFile.LogMessages.Where(m => m.Code == NuGetLogCode.NU1012); + Assert.Equal(1, messagesForNU1012.Count()); + } + } + private static byte[] GetTestUtilityResource(string name) { return ResourceTestUtility.GetResourceBytes( diff --git a/test/NuGet.Core.Tests/NuGet.Packaging.Test/PackageBuilderTest.cs b/test/NuGet.Core.Tests/NuGet.Packaging.Test/PackageBuilderTest.cs index d6770eda8cd..eccda8c3848 100644 --- a/test/NuGet.Core.Tests/NuGet.Packaging.Test/PackageBuilderTest.cs +++ b/test/NuGet.Core.Tests/NuGet.Packaging.Test/PackageBuilderTest.cs @@ -139,7 +139,6 @@ public void CreatePackageWithNuspecIncludeExcludeAnyGroup() [InlineData(".NETFramework,Version=v4.7.2", ".NETFramework4.7.2")] [InlineData(".NETFramework,Version=v4.7.2,Profile=foo", ".NETFramework4.7.2-foo")] [InlineData("net5.0", "net5.0")] - [InlineData("net5.0-windows", "net5.0-windows")] [InlineData("net5.0-macos10.8", "net5.0-macos10.8")] [InlineData("net6.0", "net6.0")] public void CreatePackageTFMFormatting(string from, string to) @@ -214,8 +213,8 @@ public void CreatePackageWithNuspecIncludeExclude() var net45 = new PackageDependencyGroup(new NuGetFramework(".NETFramework", new Version(4, 5)), dependencies45); builder.DependencyGroups.Add(net45); - var net50win = new PackageDependencyGroup(new NuGetFramework(".NETCoreApp", new Version(5, 0), "windows", new Version(0, 0)), dependencies50); - builder.DependencyGroups.Add(net50win); + var net50win7 = new PackageDependencyGroup(new NuGetFramework(".NETCoreApp", new Version(5, 0), "windows", new Version(7, 0)), dependencies50); + builder.DependencyGroups.Add(net50win7); using (var ms = new MemoryStream()) { @@ -240,7 +239,7 @@ public void CreatePackageWithNuspecIncludeExclude() - + @@ -1318,6 +1317,115 @@ public void SavingPackageValidatesReferences() "Invalid assembly reference 'Bar.dll'. Ensure that a file named 'Bar.dll' exists in the lib directory."); } + [Fact] + public void SavingPackageValidatesMissingTPVInReferences() + { + // Arrange + var builder = new PackageBuilder + { + Id = "A", + Version = NuGetVersion.Parse("1.0"), + Description = "Test", + }; + builder.Authors.Add("Test"); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"lib\net5.0-windows\Foo.dll" }); + builder.PackageAssemblyReferences = new[] { new PackageReferenceSet(NuGetFramework.Parse("net5.0-windows"), new string[] { "Foo.dll" }) }; + + ExceptionAssert.Throws(() => builder.Save(new MemoryStream()), + "Some reference group TFMs are missing a platform version: net5.0-windows"); + } + + [Fact] + public void SavingPackageValidatesMissingTPVInFiles() + { + // Arrange + var builder = new PackageBuilder + { + Id = "A", + Version = NuGetVersion.Parse("1.0"), + Description = "Test", + }; + builder.Authors.Add("Test"); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"lib\net5.0-windows\Foo.dll" }); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"ref\net6.0-windows\Foo.dll" }); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"runtimes\win7-x64\lib\net7.0-windows\Foo.dll" }); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"runtimes\win7-x64\nativeassets\net8.0-windows\Foo.dll" }); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"build\net9.0-windows\foo.props" }); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"contentFiles\csharp\net10.0-windows\Foo.txt" }); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"tools\net11.0-windows\win7-x64\Foo.exe" }); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"embed\net12.0-windows\Foo.dll" }); + builder.Files.Add(new PhysicalPackageFile { TargetPath = @"buildTransitive\net13.0-windows\foo.props" }); + + ExceptionAssert.Throws(() => builder.Save(new MemoryStream()), + "Some included files are included under TFMs which are missing a platform version: " + string.Join(", ", new string[] + { + "net5.0-windows", + "net6.0-windows", + "net7.0-windows", + "net8.0-windows", + "net9.0-windows", + "net10.0-windows", + "net11.0-windows", + "net12.0-windows", + "net13.0-windows" + }.OrderBy(str => str))); + } + + [Fact] + public void SavingPackageValidatesMissingTPVInFrameworkReferences() + { + // Arrange + var builder = new PackageBuilder + { + Id = "A", + Version = NuGetVersion.Parse("1.0"), + Description = "Test", + }; + builder.Authors.Add("Test"); + builder.FrameworkReferences.Add(new FrameworkAssemblyReference("System.Web", new[] { NuGetFramework.Parse("net5.0-windows") })); + + ExceptionAssert.Throws(() => builder.Save(new MemoryStream()), + "Some framework assembly reference TFMs are missing a platform version: net5.0-windows"); + } + + [Fact] + public void SavingPackageValidatesMissingTPVInFrameworkReferenceGroups() + { + // Arrange + var builder = new PackageBuilder + { + Id = "A", + Version = NuGetVersion.Parse("1.0"), + Description = "Test", + }; + builder.Authors.Add("Test"); + builder.FrameworkReferenceGroups.Add(new FrameworkReferenceGroup(NuGetFramework.Parse("net5.0-windows"), new List())); + + ExceptionAssert.Throws(() => builder.Save(new MemoryStream()), + "Some reference assembly group TFMs are missing a platform version: net5.0-windows"); + } + + [Fact] + public void SavingPackageValidatesMissingTPVInDependencyGroups() + { + // Arrange + var builder = new PackageBuilder + { + Id = "A", + Version = NuGetVersion.Parse("1.0"), + Description = "Test", + }; + builder.Authors.Add("Test"); + var dependencySet = new PackageDependencyGroup(NuGetFramework.Parse("net5.0-windows"), new[] { + new PackageDependency("B", new VersionRange(NuGetVersion.Parse("2.0"), true, NuGetVersion.Parse("2.0"))) + }); + + builder.DependencyGroups.Add(dependencySet); + + ExceptionAssert.Throws(() => builder.Save(new MemoryStream()), + "Some dependency group TFMs are missing a platform version: net5.0-windows"); + } + [Fact] public void SavingPackageWithInvalidDependencyVersionMaxLessThanMinThrows() {