From 2d349b8c6774ea4bd18afe9c46694e0c3582ca29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Thu, 24 Sep 2020 15:51:28 -0700 Subject: [PATCH 1/2] translate aliases for lib and contentFiles Fixes: https://github.com/NuGet/Home/issues/10020 --- .../NuGet.Build.Tasks.Pack/PackTaskLogic.cs | 63 ++++++++++++++++--- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs b/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs index aaf4479f778..61ae8c815f0 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs +++ b/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs @@ -55,6 +55,12 @@ public PackArgs GetPackArgs(IPackTaskRequest request) packArgs.MinClientVersion = version; } + LockFile assetsFile = GetAssetsFile(request); + var aliases = new Dictionary(); + foreach (var tfm in assetsFile.PackageSpec.TargetFrameworks) + { + aliases[tfm.TargetAlias] = tfm.FrameworkName.GetShortFolderName(); + } InitCurrentDirectoryAndFileName(request, packArgs); InitNuspecOutputPath(request, packArgs); @@ -76,12 +82,12 @@ public PackArgs GetPackArgs(IPackTaskRequest request) // This only needs to happen when packing via csproj, not nuspec. packArgs.PackTargetArgs.AllowedOutputExtensionsInPackageBuildOutputFolder = InitOutputExtensions(request.AllowedOutputExtensionsInPackageBuildOutputFolder); packArgs.PackTargetArgs.AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder = InitOutputExtensions(request.AllowedOutputExtensionsInSymbolsPackageBuildOutputFolder); - packArgs.PackTargetArgs.TargetPathsToAssemblies = InitLibFiles(request.BuildOutputInPackage); - packArgs.PackTargetArgs.TargetPathsToSymbols = InitLibFiles(request.TargetPathsToSymbols); + packArgs.PackTargetArgs.TargetPathsToAssemblies = InitLibFiles(request.BuildOutputInPackage, aliases); + packArgs.PackTargetArgs.TargetPathsToSymbols = InitLibFiles(request.TargetPathsToSymbols, aliases); packArgs.PackTargetArgs.AssemblyName = request.AssemblyName; packArgs.PackTargetArgs.IncludeBuildOutput = request.IncludeBuildOutput; packArgs.PackTargetArgs.BuildOutputFolder = request.BuildOutputFolders; - packArgs.PackTargetArgs.TargetFrameworks = ParseFrameworks(request); + packArgs.PackTargetArgs.TargetFrameworks = ParseFrameworks(request, aliases); if (request.IncludeSource) { @@ -322,6 +328,28 @@ private static Version GetLicenseExpressionVersion(IPackTaskRequest request) + { + if (request.PackItem == null) + { + throw new PackagingException(NuGetLogCode.NU5028, Strings.NoPackItemProvided); + } + + string assetsFilePath = Path.Combine(request.RestoreOutputPath, LockFileFormat.AssetsFileName); + + if (!File.Exists(assetsFilePath)) + { + throw new InvalidOperationException(string.Format( + CultureInfo.CurrentCulture, + Strings.AssetsFileNotFound, + assetsFilePath)); + } + // The assets file is necessary for project and package references. Pack should not do any traversal, + // so we leave that work up to restore (which produces the assets file). + var lockFileFormat = new LockFileFormat(); + return lockFileFormat.Read(assetsFilePath); + } private void PopulateFrameworkAssemblyReferences(PackageBuilder builder, IPackTaskRequest request) { // First add all the assembly references which are not specific to a certain TFM. @@ -396,7 +424,7 @@ public bool BuildPackage(PackCommandRunner runner) return runner.RunPackageBuild(); } - private IEnumerable InitLibFiles(IMSBuildItem[] libFiles) + private IEnumerable InitLibFiles(IMSBuildItem[] libFiles, IDictionary aliases) { var assemblies = new List(); if (libFiles == null) @@ -410,7 +438,7 @@ private IEnumerable InitLibFiles(IMSBuildItem[] libFiles) var finalOutputPath = assembly.GetProperty("FinalOutputPath"); // Fallback to using Identity if FinalOutputPath is not set. - // See bug https://github.com/NuGet/Home/issues/5408 + // See bug https://github.com/NuGet/Home/issues/5408 if (string.IsNullOrEmpty(finalOutputPath)) { finalOutputPath = assembly.GetProperty(IdentityProperty); @@ -424,6 +452,13 @@ private IEnumerable InitLibFiles(IMSBuildItem[] libFiles) throw new PackagingException(NuGetLogCode.NU5026, string.Format(CultureInfo.CurrentCulture, Strings.Error_FileNotFound, finalOutputPath)); } + string translated = null; + var succeeded = aliases.TryGetValue(targetFramework, out translated); + if (succeeded) + { + targetFramework = translated; + } + // If target path is not set, default it to the file name. Only satellite DLLs have a special target path // where culture is part of the target path. This condition holds true for files like runtimeconfig.json file // in netcore projects. @@ -448,12 +483,20 @@ private IEnumerable InitLibFiles(IMSBuildItem[] libFiles) return assemblies; } - private ISet ParseFrameworks(IPackTaskRequest request) + private ISet ParseFrameworks(IPackTaskRequest request, IDictionary aliases) { var nugetFrameworks = new HashSet(); if (request.TargetFrameworks != null) { - nugetFrameworks = new HashSet(request.TargetFrameworks.Select(t => NuGetFramework.Parse(t))); + nugetFrameworks = new HashSet(request.TargetFrameworks.Select(targetFramework => { + string translated = null; + var succeeded = aliases.TryGetValue(targetFramework, out translated); + if (succeeded) + { + targetFramework = translated; + } + return NuGetFramework.Parse(targetFramework); + })); } return nugetFrameworks; @@ -655,7 +698,7 @@ private IEnumerable GetContentMetadata(IMSBuildItem packageFile var newTargetPath = Path.Combine(targetPath, identity); // We need to do this because evaluated identity in the above line of code can be an empty string // in the case when the original identity string was the absolute path to a file in project directory, and is in - // the same directory as the csproj file. + // the same directory as the csproj file. newTargetPath = PathUtility.EnsureTrailingSlash(newTargetPath); newTargetPaths.Add(newTargetPath); } @@ -813,7 +856,7 @@ private static void InitializeProjectDependencies( var versionToUse = new VersionRange(targetLibrary.Version); - // Use the project reference version obtained at build time if it exists, otherwise fallback to the one in assets file. + // Use the project reference version obtained at build time if it exists, otherwise fallback to the one in assets file. if (projectRefToVersionMap.TryGetValue(projectReference.ProjectPath, out var projectRefVersion)) { versionToUse = VersionRange.Parse(projectRefVersion, allowFloating: false); @@ -872,7 +915,7 @@ private static void InitializePackageDependencies( // Add each package dependency. foreach (var packageDependency in packageDependencies) { - // If we have a floating package dependency like 1.2.3-xyz-*, we + // If we have a floating package dependency like 1.2.3-xyz-*, we // use the version of the package that restore resolved it to. if (packageDependency.LibraryRange.VersionRange.IsFloating) { From 7efef9a316e40e6d139263371f6319169571d7f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Thu, 24 Sep 2020 16:11:15 -0700 Subject: [PATCH 2/2] quick test for the lib part --- .../PackTaskLogicTests.cs | 128 +++++++++++++++++- 1 file changed, 125 insertions(+), 3 deletions(-) diff --git a/test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackTaskLogicTests.cs b/test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackTaskLogicTests.cs index 00f99a1fef8..7781b61648d 100644 --- a/test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackTaskLogicTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Build.Tasks.Pack.Test/PackTaskLogicTests.cs @@ -500,6 +500,121 @@ public void PackTaskLogic_SupportsContentFiles_WithPackageCopyToOutput() } } + [Fact] + public void PackTaskLogic_InfersFrameworkPlatformVersionFromAlias() + { + // Arrange + using (var testDir = TestDirectory.Create()) + { + var tc = new TestContext(testDir, "net5.0-windows"); + + var assetsJson = @"{ + ""version"": 3, + ""targets"": { + ""net5.0"": {}, + ""net5.0-windows7.0"": {} + }, + ""libraries"": {}, + ""projectFileDependencyGroups"": { + ""net5.0"": [], + ""net5.0-windows7.0"": [] + }, + ""project"": { + ""version"": ""0.0.0"", + ""restore"": { + ""projectName"": ""bar"", + ""projectStyle"": ""PackageReference"", + ""crossTargeting"": true, + ""fallbackFolders"": [ + ""C:\\Microsoft\\Xamarin\\NuGet\\"" + ], + ""originalTargetFrameworks"": [ + ""net5.0"", + ""net5.0-windows"" + ], + ""sources"": { + ""https://api.nuget.org/v3/index.json"": {}, + }, + ""frameworks"": { + ""net5.0"": { + ""targetAlias"": ""net5.0"", + ""projectReferences"": {} + }, + ""net5.0-windows7.0"": { + ""targetAlias"": ""net5.0-windows"", + ""projectReferences"": {} + } + }, + ""warningProperties"": { + ""warnAsError"": [ + ""NU1605"" + ] + } + }, + ""frameworks"": { + ""net5.0"": { + ""targetAlias"": ""net5.0"", + ""imports"": [ + ""net461"", + ""net462"", + ""net47"", + ""net471"", + ""net472"", + ""net48"" + ], + ""assetTargetFallback"": true, + ""warn"": true, + ""frameworkReferences"": { + ""Microsoft.NETCore.App"": { + ""privateAssets"": ""all"" + } + }, + }, + ""net5.0-windows7.0"": { + ""targetAlias"": ""net5.0-windows"", + ""imports"": [ + ""net461"", + ""net462"", + ""net47"", + ""net471"", + ""net472"", + ""net48"" + ], + ""assetTargetFallback"": true, + ""warn"": true, + ""frameworkReferences"": { + ""Microsoft.NETCore.App"": { + ""privateAssets"": ""all"" + } + }, + } + } + } + }"; + File.WriteAllText(Path.Combine(testDir, "obj", "project.assets.json"), assetsJson); + + // var msbuildItem = tc.AddContentToProject("", "abc.txt", "hello world"); + // tc.Request.PackageFiles = new MSBuildItem[] { msbuildItem }; + // tc.Request.ContentTargetFolders = new string[] { "content" }; + + // Act + tc.BuildPackage(); + + // Assert + using (var nupkgReader = new PackageArchiveReader(tc.NupkgPath)) + { + var nuspecReader = nupkgReader.NuspecReader; + + // Validate the assets. + var libItems = nupkgReader.GetLibItems().ToList(); + Assert.Equal(1, libItems.Count); + Assert.Equal(NuGetFramework.Parse("net5.0-windows7.0"), libItems[0].TargetFramework); + Assert.Equal(new[] { "lib/net5.0-windows7.0/a.dll" }, libItems[0].Items); + } + } + } + + [PlatformTheory(Platform.Windows)] [InlineData(true)] [InlineData(false)] @@ -625,11 +740,18 @@ public void PackTaskLogic_EmbedInteropAssembly() private class TestContext { + public TestContext(TestDirectory testDir) + : this(testDir, "net45") + { + + } + + public TestContext(TestDirectory testDir, string tfm) { var fullPath = Path.Combine(testDir, "project.csproj"); var rootDir = Path.GetPathRoot(testDir); - var dllDir = Path.Combine(testDir, "bin", "Debug", "net45"); + var dllDir = Path.Combine(testDir, "bin", "Debug", tfm); var dllPath = Path.Combine(dllDir, "a.dll"); Directory.CreateDirectory(dllDir); @@ -664,11 +786,11 @@ public TestContext(TestDirectory testDir) IncludeBuildOutput = true, RestoreOutputPath = Path.Combine(testDir, "obj"), ContinuePackingAfterGeneratingNuspec = true, - TargetFrameworks = new[] { "net45" }, + TargetFrameworks = new[] { tfm }, BuildOutputInPackage = new[] { new MSBuildItem(dllPath, new Dictionary { {"FinalOutputPath", dllPath }, - {"TargetFramework", "net45" } + {"TargetFramework", tfm } })}, Logger = new TestLogger(), SymbolPackageFormat = "symbols.nupkg",