diff --git a/src/Tasks/Common/MetadataKeys.cs b/src/Tasks/Common/MetadataKeys.cs index ee3d946bca4f..9e2d95a1db2f 100644 --- a/src/Tasks/Common/MetadataKeys.cs +++ b/src/Tasks/Common/MetadataKeys.cs @@ -141,5 +141,8 @@ internal static class MetadataKeys public const string XmlFilePath = "XmlFilePath"; public const string PdbExtension = ".pdb"; public const string PdbFilePath = "PdbFilePath"; + + // Dependencies design time + public const string Resolved = "Resolved"; } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageDependenciesTask.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageDependenciesTask.cs index 9d078eb466e3..bbacf673e4d8 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageDependenciesTask.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageDependenciesTask.cs @@ -21,9 +21,9 @@ public class GivenAResolvePackageDependenciesTask [Theory] [MemberData(nameof(ItemCounts))] - public void ItRaisesLockFileToMSBuildItems(string projectName, int[] counts, bool emitLegacyAssetsFileItems) + public void ItRaisesLockFileToMSBuildItems(string projectName, int[] counts) { - var task = GetExecutedTaskFromPrefix(projectName, out _, emitLegacyAssetsFileItems); + var task = GetExecutedTaskFromPrefix(projectName, out _); task.PackageDefinitions .Count().Should().Be(counts[0]); task.FileDefinitions .Count().Should().Be(counts[1]); @@ -40,36 +40,16 @@ public static IEnumerable ItemCounts { new object[] { "dotnet.new", - new int[] { 110, 2536, 1, 846, 73 }, - true - }, - new object[] { - "dotnet.new", - new int[] { 110, 0, 0, 846, 0 }, - false - }, - new object[] { - "simple.dependencies", - new int[] { 113, 2613, 1, 878, 94 }, - true + new int[] { 110, 2536, 1, 845, 75 }, }, new object[] { "simple.dependencies", - new int[] { 113, 0, 0, 878, 0 }, - false + new int[] { 113, 2613, 1, 877, 96 }, }, }; } } - [Fact] - public void ItOmitsLegacyItemsByDefault() - { - var task = new ResolvePackageDependencies(); - - task.EmitLegacyAssetsFileItems.Should().Be(false); - } - [Theory] [InlineData("dotnet.new")] [InlineData("simple.dependencies")] @@ -584,8 +564,7 @@ public void ItAddsAnalyzerMetadataAndFileDependencies() { ProjectAssetsFile = lockFile.Path, ProjectPath = null, - ProjectLanguage = projectLanguage, // set language - EmitLegacyAssetsFileItems = true + ProjectLanguage = projectLanguage // set language }; task.Execute().Should().BeTrue(); @@ -669,8 +648,7 @@ public void ItFiltersAnalyzersByProjectLanguage() { ProjectAssetsFile = lockFile.Path, ProjectPath = null, - ProjectLanguage = projectLanguage, // set language - EmitLegacyAssetsFileItems = true + ProjectLanguage = projectLanguage // set language }; task.Execute().Should().BeTrue(); @@ -839,19 +817,19 @@ public void ItDoesNotThrowOnCrossTargetingWithTargetPlatforms() GetExecutedTaskFromContents(lockFileContent, out _); // Task should not fail on matching framework names } - private static ResolvePackageDependencies GetExecutedTaskFromPrefix(string lockFilePrefix, out LockFile lockFile, bool emitLegacyAssetsFileItems = true, string target = null) + private static ResolvePackageDependencies GetExecutedTaskFromPrefix(string lockFilePrefix, out LockFile lockFile, string target = null) { lockFile = TestLockFiles.GetLockFile(lockFilePrefix); - return GetExecutedTask(lockFile, emitLegacyAssetsFileItems, target); + return GetExecutedTask(lockFile, target); } - private static ResolvePackageDependencies GetExecutedTaskFromContents(string lockFileContents, out LockFile lockFile, bool emitLegacyAssetsFileItems = true, string target = null) + private static ResolvePackageDependencies GetExecutedTaskFromContents(string lockFileContents, out LockFile lockFile, string target = null) { lockFile = TestLockFiles.CreateLockFile(lockFileContents); - return GetExecutedTask(lockFile, emitLegacyAssetsFileItems, target); + return GetExecutedTask(lockFile, target); } - private static ResolvePackageDependencies GetExecutedTask(LockFile lockFile, bool emitLegacyAssetsFileItems, string target) + private static ResolvePackageDependencies GetExecutedTask(LockFile lockFile, string target) { var resolver = new MockPackageResolver(_packageRoot); @@ -860,7 +838,6 @@ private static ResolvePackageDependencies GetExecutedTask(LockFile lockFile, boo ProjectAssetsFile = lockFile.Path, ProjectPath = _projectPath, ProjectLanguage = null, - EmitLegacyAssetsFileItems = emitLegacyAssetsFileItems, TargetFramework = target }; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenThatWeWantToGetDependenciesViaDesignTimeBuild.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenThatWeWantToGetDependenciesViaDesignTimeBuild.cs index 84e2088d439a..d708ee4b05cc 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenThatWeWantToGetDependenciesViaDesignTimeBuild.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenThatWeWantToGetDependenciesViaDesignTimeBuild.cs @@ -1,399 +1,656 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System; using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using FluentAssertions; using Microsoft.Build.Framework; +using Microsoft.NET.TestFramework; using Xunit; +using Xunit.Abstractions; +using static Microsoft.NET.Build.Tasks.ResolvePackageAssets; namespace Microsoft.NET.Build.Tasks.UnitTests { - public class GivenThatWeWantToGetDependenciesViaDesignTimeBuild + public class GivenThatWeWantToGetDependenciesViaDesignTimeBuild : SdkTest { - [Fact] - public void ItShouldNotReturnPackagesWithUnknownTypes() + public GivenThatWeWantToGetDependenciesViaDesignTimeBuild(ITestOutputHelper log) : base(log) { - var task = new PreprocessPackageDependenciesDesignTime - { - TargetFramework = "net45", - DefaultImplicitPackages = string.Empty, - PackageDefinitions = new ITaskItem[] - { - new MockTaskItem( - itemSpec: "mockPackageNoType/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageNoType" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some path" } - }), - new MockTaskItem( - itemSpec: "mockPackageUnknown/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageUnknown" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some resolved path" }, - { MetadataKeys.Type, "qqqq" } - }) - }, - PackageDependencies = new ITaskItem[] - { - new MockTaskItem( - itemSpec: "mockPackageNoType/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "mockPackageUnknown/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }) - } - }; - - Assert.True(task.Execute()); - - Assert.Empty(task.PackageDependenciesDesignTime); } [Fact] - public void ItShouldReturnUnresolvedPackageDependenciesWithTypePackage() + public void ItShouldIgnoreAllDependenciesWithTypeNotEqualToPackageOrUnresolved() { - var task = new PreprocessPackageDependenciesDesignTime - { - TargetFramework = "net45", - DefaultImplicitPackages = string.Empty, - PackageDefinitions = new ITaskItem[] - { - new MockTaskItem( - itemSpec: "mockPackageUnresolved/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageUnresolved" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "" }, - { MetadataKeys.Type, "Unresolved" }, - { MetadataKeys.DiagnosticLevel, "Warning" } - }) - }, - PackageDependencies = new ITaskItem[] - { - new MockTaskItem( - itemSpec: "mockPackageUnresolved/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }) - } - }; - - Assert.True(task.Execute()); - - var item = Assert.Single(task.PackageDependenciesDesignTime); - - Assert.Equal("mockPackageUnresolved/1.0.0", item.ItemSpec); - Assert.Equal("mockPackageUnresolved", item.GetMetadata(MetadataKeys.Name)); - Assert.Equal("1.0.0", item.GetMetadata(MetadataKeys.Version)); - Assert.Equal("some path", item.GetMetadata(MetadataKeys.Path)); - Assert.Equal("", item.GetMetadata(MetadataKeys.ResolvedPath)); - Assert.Equal("Warning", item.GetMetadata(MetadataKeys.DiagnosticLevel)); - Assert.False(item.GetBooleanMetadata(MetadataKeys.IsImplicitlyDefined)); - Assert.False(item.GetBooleanMetadata(PreprocessPackageDependenciesDesignTime.ResolvedMetadata)); + var testRoot = _testAssetsManager.CreateTestDirectory().Path; + Log.WriteLine("Test root: " + testRoot); + + string projectAssetsJsonPath = Path.Combine(testRoot, "project.assets.json"); + string projectCacheAssetsJsonPath = Path.Combine(testRoot, "projectassets.cache"); + // project.assets.json + var assetsContent = CreateBasicProjectAssetsFile(testRoot, package2Type: "unresolved", package3Type: "unknown"); + + File.WriteAllText(projectAssetsJsonPath, assetsContent); + var task = InitializeTask(testRoot, out _); + task.ProjectAssetsFile = projectAssetsJsonPath; + task.ProjectAssetsCacheFile = projectCacheAssetsJsonPath; + task.TargetFramework = "net6.0"; + + task.Execute(); + + Assert.Equal(2, task.PackageDependenciesDesignTime.Count()); + + // Verify only + // top.package1 is type 'package' + // top.package2 is type 'unresolved' + // + // top.package3 is type 'unknown'. Should not appear in the list + var item1 = task.PackageDependenciesDesignTime[0]; + Assert.Equal("top.package1/1.0.0", item1.ItemSpec); + + var item2 = task.PackageDependenciesDesignTime[1]; + Assert.Equal("top.package2/1.0.0", item2.ItemSpec); } [Fact] public void ItShouldIdentifyDefaultImplicitPackages() { - var task = new PreprocessPackageDependenciesDesignTime - { - TargetFramework = "net45", - DefaultImplicitPackages = "DefaultImplicit", - PackageDefinitions = new ITaskItem[] - { - new MockTaskItem( - itemSpec: "DefaultImplicit/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "DefaultImplicit" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "" }, - { MetadataKeys.Type, "Package" } - }) - }, - PackageDependencies = new ITaskItem[] - { - new MockTaskItem( - itemSpec: "DefaultImplicit/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }) - } - }; - - Assert.True(task.Execute()); - - var item = Assert.Single(task.PackageDependenciesDesignTime); - - Assert.Equal("DefaultImplicit/1.0.0", item.ItemSpec); - Assert.True(item.GetBooleanMetadata(MetadataKeys.IsImplicitlyDefined)); - } + var testRoot = _testAssetsManager.CreateTestDirectory().Path; + Log.WriteLine("Test root: " + testRoot); - [Fact] - public void ItShouldIgnoreAllDependenciesWithTypeNotEqualToPackageOrUnresolved() - { - var task = new PreprocessPackageDependenciesDesignTime - { - TargetFramework = "net45", - DefaultImplicitPackages = string.Empty, - PackageDefinitions = new ITaskItem[] { - new MockTaskItem( - itemSpec: "mockPackageExternalProject/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageExternalProject" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some path" }, - { MetadataKeys.Type, "ExternalProject" } - }), - new MockTaskItem( - itemSpec: "mockPackageProject/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageProject" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some resolved path" }, - { MetadataKeys.Type, "Project" } - }), - new MockTaskItem( - itemSpec: "mockPackageContent/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageContent" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some resolved path" }, - { MetadataKeys.Type, "Content" } - }), - new MockTaskItem( - itemSpec: "mockPackageAssembly/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageAssembly" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some resolved path" }, - { MetadataKeys.Type, "Assembly" } - }), - new MockTaskItem( - itemSpec: "mockPackageFrameworkAssembly/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageFrameworkAssembly" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some resolved path" }, - { MetadataKeys.Type, "FrameworkAssembly" } - }), - new MockTaskItem( - itemSpec: "mockPackageDiagnostic/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageDiagnostic" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some resolved path" }, - { MetadataKeys.Type, "Diagnostic" } - }), - new MockTaskItem( - itemSpec: "mockPackageWinmd/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageWinmd" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some resolved path" }, - { MetadataKeys.Type, "Winmd" } - }), - new MockTaskItem( - itemSpec: "mockPackageReference/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "mockPackageReference" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some resolved path" }, - { MetadataKeys.Type, "Reference" } - }) - }, - PackageDependencies = new ITaskItem[] { - new MockTaskItem( - itemSpec: "mockPackageExternalProject/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "mockPackageProject/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "mockPackageContent/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "mockPackageAssembly/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "mockPackageFrameworkAssembly/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "mockPackageDiagnostic/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "mockPackageWinmd/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "mockPackageReference/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }) - } - }; - - Assert.True(task.Execute()); - - Assert.Empty(task.PackageDependenciesDesignTime); + string projectAssetsJsonPath = Path.Combine(testRoot, "project.assets.json"); + string projectCacheAssetsJsonPath = Path.Combine(testRoot, "projectassets.cache"); + // project.assets.json + File.WriteAllText(projectAssetsJsonPath, CreateBasicProjectAssetsFile(testRoot)); + var task = InitializeTask(testRoot, out _); + task.ProjectAssetsFile = projectAssetsJsonPath; + task.ProjectAssetsCacheFile = projectCacheAssetsJsonPath; + task.TargetFramework = "net6.0"; + // Set implicit packages + task.DefaultImplicitPackages = "top.package2;top.package3"; + + task.Execute(); + + Assert.Equal(3, task.PackageDependenciesDesignTime.Count()); + + // Verify implicit packages + var item1 = task.PackageDependenciesDesignTime[0]; + Assert.Equal("top.package1/1.0.0", item1.ItemSpec); + Assert.Equal("False", item1.GetMetadata(MetadataKeys.IsImplicitlyDefined)); + + var item2 = task.PackageDependenciesDesignTime[1]; + Assert.Equal("top.package2/1.0.0", item2.ItemSpec); + Assert.Equal("True", item2.GetMetadata(MetadataKeys.IsImplicitlyDefined)); + + var item3 = task.PackageDependenciesDesignTime[2]; + Assert.Equal("top.package3/1.0.0", item3.ItemSpec); + Assert.Equal("True", item3.GetMetadata(MetadataKeys.IsImplicitlyDefined)); } [Fact] public void ItShouldOnlyReturnPackagesInTheSpecifiedTarget() { - var task = new PreprocessPackageDependenciesDesignTime - { - TargetFramework = "net45", - DefaultImplicitPackages = string.Empty, - PackageDefinitions = new ITaskItem[] { - new MockTaskItem( - itemSpec: "Package1/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "Package1" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "" }, - { MetadataKeys.Type, "Package" } - }), - new MockTaskItem( - itemSpec: "Package2/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "Package2" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "" }, - { MetadataKeys.Type, "Package" } - }) - }, - PackageDependencies = new ITaskItem[] { - new MockTaskItem( - itemSpec: "Package1/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "Package2/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net46" } - }) - } - }; - - Assert.True(task.Execute()); - - var item = Assert.Single(task.PackageDependenciesDesignTime); - - Assert.Equal("Package1/1.0.0", item.ItemSpec); + var testRoot = _testAssetsManager.CreateTestDirectory().Path; + Log.WriteLine("Test root: " + testRoot); + + string projectAssetsJsonPath = Path.Combine(testRoot, "project.assets.json"); + string projectCacheAssetsJsonPath = Path.Combine(testRoot, "projectassets.cache"); + // project.assets.json + var assetsContent = +""" +{ + "version" : 3, + "targets" : { + "net6.0" :{ + "top.package1/1.0.0": { + "type": "package", + "dependencies": { + "dependent.package1": "1.0.0", + "dependent.package2": "1.0.0", + }, + "compile": { + "lib/netstandard2.1/top.package1.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.1/top.package1.dll": { + "related": ".xml" + } + } + }, + "dependent.package1/1.0.0": { + "type": "package", + "dependencies": { + "dependent.package3": "1.0.0" + }, + "compile": { + "lib/netcoreapp3.1/_._": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/dependent.package1.dll": { + "related": ".xml" + } + } + }, + "dependent.package2/1.0.0": { + "type": "package", + "compile": { + "lib/netstandard2.0/dependent.package2.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/dependent.package2.dll": { + "related": ".xml" + } + } + }, + "dependent.package3/1.0.0": { + "type": "package", + "compile": { + "lib/netstandard2.1/dependent.package3.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.1/dependent.package3.dll": { + "related": ".xml" + } + } + } + }, + "net7.0" : { + "top.package2/1.0.0" : { + "type" : "package", + "compile" : { + "lib/netstandard2.0/top.package2.dll" : { + "related" : ".pdb;.xml" + } + }, + "runtime" : { + "lib/netstandard2.0/top.package2.dll" : { + "related": ".pdb;.xml" + } + } + }, + "top.package3/1.0.0" : { + "type" : "package", + "dependencies": { + "dependent.package1" : "1.0.1" + }, + "compile" : { + "lib/netstandard2.0/top.package3.dll" : { + "related" : ".pdb;.xml" + } + }, + "runtime" : { + "lib/netstandard2.0/top.package3.dll" : { + "related" : ".pdb;.xml" + } + } + }, + "dependent.package1/1.0.0": { + "type": "package", + "dependencies": { + "dependent.package3": "1.0.0" + }, + "compile": { + "lib/netcoreapp3.1/_._": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/dependent.package1.dll": { + "related": ".xml" + } + } + }, + "dependent.package3/1.0.0": { + "type": "package", + "compile": { + "lib/netstandard2.1/dependent.package3.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.1/dependent.package3.dll": { + "related": ".xml" + } + } + } + } + }, + "libraries" : { + "dependent.package1/1.0.0" : { + "sha512" : "xyz", + "type" : "package", + "path" : "dependent.package1/1.0.0", + "files" : [ + "lib/net461/dependent.package1.dll", + "lib/net461/dependent.package1.xml" + ] + }, + "dependent.package2/1.0.0" : { + "sha512" : "xyz", + "type" : "package", + "path" : "dependent.package2/1.0.0", + "files" : [ + ".nupkg.metadata" + ] + }, + "dependent.package3/1.0.0" : { + "sha512" : "xyz", + "type" : "package", + "path" : "dependent.package3/1.0.0", + "files" : [ + "lib/net472/dependent.package3.dll", + "lib/net472/dependent.package3.xml" + ] + }, + "top.package1/1.0.0" : { + "sha512" : "xyz", + "type" : "package", + "path" : "top.package1/1.0.0", + "files" : [ + "lib/net20/top.package1.dll", + "lib/net20/top.package1.pdb", + "lib/net20/top.package1.xml" + ] + }, + "top.package2/1.0.0" : { + "sha512" : "abc", + "type" : "package", + "path" : "top.package2/1.0.0", + "files" : [ + "lib/net45/top.package2.dll", + "lib/net45/top.package2.pdb", + "lib/net45/top.package2.xml" + ] + }, + "top.package3/1.0.0" : { + "sha512" : "abc", + "type" : "package", + "path" : "top.package3/1.0.0", + "files" : [ + ".nupkg.metadata", + ] + } + }, + "projectFileDependencyGroups": { + "net6.0": [ + "top.package1 >= 1.0.0" + ], + "net7.0": [ + "top.package2 >= 1.0.0", + "top.package3 >= 1.0.0" + ] + }, + "packageFolders": { + "C:\\.nuget\\packages\\" : {} + }, + "project" : { + "version" : "1.0.0", + "restore": { + "projectUniqueName": "C:\\projDir1\\projDir2\\proj.csproj", + "projectName": "proj", + "projectPath": "C:\\projDir1\\projDir2\\proj.csproj", + "packagesPath": "C:\\.nuget\\packages\\", + "outputPath": "C:\\projDir1\\projDir2\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\fallbackDir\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\configDir\\NuGet.Config", + "C:\\configDir2\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\configDir3\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net6.0" + ], + "frameworks": { + "net6.0": { + "targetAlias": "net6.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks" : { + "net6.0": { + "targetAlias" : "net6.0", + "dependencies" : { + "top.package1" : { + "target" : "Package", + "version" : "[1.0.0, )" + } + } + } + } + } +} +"""; + assetsContent = assetsContent.Replace(@"C:\\.nuget", $@"{testRoot.Replace("\\", "\\\\")}\\.nuget"); ; + + File.WriteAllText(projectAssetsJsonPath, assetsContent); + var task = InitializeTask(testRoot, out _); + task.ProjectAssetsFile = projectAssetsJsonPath; + task.ProjectAssetsCacheFile = projectCacheAssetsJsonPath; + // Set target to verify + task.TargetFramework = "net6.0"; + task.Execute(); + + Assert.Single(task.PackageDependenciesDesignTime); + + // Verify top packages in target + var item1 = task.PackageDependenciesDesignTime[0]; + Assert.Equal("top.package1/1.0.0", item1.ItemSpec); } [Fact] public void ItShouldOnlyReturnTopLevelPackages() { - var task = new PreprocessPackageDependenciesDesignTime + var testRoot = _testAssetsManager.CreateTestDirectory().Path; + Log.WriteLine("Test root: " + testRoot); + + string projectAssetsJsonPath = Path.Combine(testRoot, "project.assets.json"); + string projectCacheAssetsJsonPath = Path.Combine(testRoot, "projectassets.cache"); + // project.assets.json + File.WriteAllText(projectAssetsJsonPath, CreateBasicProjectAssetsFile(testRoot)); + var task = InitializeTask(testRoot, out _); + task.ProjectAssetsFile = projectAssetsJsonPath; + task.ProjectAssetsCacheFile = projectCacheAssetsJsonPath; + task.TargetFramework = "net6.0"; + task.Execute(); + + // Verify all top packages are listed here + Assert.Equal(3, task.PackageDependenciesDesignTime.Count()); + + var item1 = task.PackageDependenciesDesignTime[0]; + Assert.Equal("top.package1/1.0.0", item1.ItemSpec); + + var item2 = task.PackageDependenciesDesignTime[1]; + Assert.Equal("top.package2/1.0.0", item2.ItemSpec); + + var item3 = task.PackageDependenciesDesignTime[2]; + Assert.Equal("top.package3/1.0.0", item3.ItemSpec); + } + + private string CreateBasicProjectAssetsFile(string testRoot, string package2Type = "package", string package3Type = "package") + { + var json = +""" +{ + "version" : 3, + "targets" : { + "net6.0" : { + "top.package1/1.0.0": { + "type": "package", + "dependencies": { + "dependent.package1": "1.0.0", + "dependent.package2": "1.0.0", + }, + "compile": { + "lib/netstandard2.1/top.package1.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.1/top.package1.dll": { + "related": ".xml" + } + } + }, + "top.package2/1.0.0" : { + "type" : "PACKAGE2_TYPE", + "compile" : { + "lib/netstandard2.0/top.package2.dll" : { + "related" : ".pdb;.xml" + } + }, + "runtime" : { + "lib/netstandard2.0/top.package2.dll" : { + "related": ".pdb;.xml" + } + } + }, + "top.package3/1.0.0" : { + "type" : "PACKAGE3_TYPE", + "dependencies": { + "dependent.package1" : "1.0.1" + }, + "compile" : { + "lib/netstandard2.0/top.package3.dll" : { + "related" : ".pdb;.xml" + } + }, + "runtime" : { + "lib/netstandard2.0/top.package3.dll" : { + "related" : ".pdb;.xml" + } + } + }, + "dependent.package1/1.0.0": { + "type": "package", + "dependencies": { + "dependent.package3": "1.0.0" + }, + "compile": { + "lib/netcoreapp3.1/_._": { + "related": ".xml" + } + }, + "runtime": { + "lib/netcoreapp3.1/dependent.package1.dll": { + "related": ".xml" + } + } + }, + "dependent.package2/1.0.0": { + "type": "package", + "compile": { + "lib/netstandard2.0/dependent.package2.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.0/dependent.package2.dll": { + "related": ".xml" + } + } + }, + "dependent.package3/1.0.0": { + "type": "package", + "compile": { + "lib/netstandard2.1/dependent.package3.dll": { + "related": ".xml" + } + }, + "runtime": { + "lib/netstandard2.1/dependent.package3.dll": { + "related": ".xml" + } + } + } + } + }, + "libraries" : { + "dependent.package1/1.0.0" : { + "sha512" : "xyz", + "type" : "package", + "path" : "dependent.package1/1.0.0", + "files" : [ + "lib/net461/dependent.package1.dll", + "lib/net461/dependent.package1.xml" + ] + }, + "dependent.package2/1.0.0" : { + "sha512" : "xyz", + "type" : "package", + "path" : "dependent.package2/1.0.0", + "files" : [ + ".nupkg.metadata" + ] + }, + "dependent.package3/1.0.0" : { + "sha512" : "xyz", + "type" : "package", + "path" : "dependent.package3/1.0.0", + "files" : [ + "lib/net472/dependent.package3.dll", + "lib/net472/dependent.package3.xml" + ] + }, + "top.package1/1.0.0" : { + "sha512" : "xyz", + "type" : "package", + "path" : "top.package1/1.0.0", + "files" : [ + "lib/net20/top.package1.dll", + "lib/net20/top.package1.pdb", + "lib/net20/top.package1.xml" + ] + }, + "top.package2/1.0.0" : { + "sha512" : "abc", + "type" : "PACKAGE2_TYPE", + "path" : "top.package2/1.0.0", + "files" : [ + "lib/net45/top.package2.dll", + "lib/net45/top.package2.pdb", + "lib/net45/top.package2.xml" + ] + }, + "top.package3/1.0.0" : { + "sha512" : "abc", + "type" : "PACKAGE3_TYPE", + "path" : "top.package3/1.0.0", + "files" : [ + ".nupkg.metadata", + ] + } + }, + "projectFileDependencyGroups": { + "net6.0": [ + "top.package1 >= 1.0.0", + "top.package2 >= 1.0.0", + "top.package3 >= 1.0.0" + ] + }, + "packageFolders": { + "C:\\.nuget\\packages\\" : {} + }, + "project" : { + "version" : "1.0.0", + "restore": { + "projectUniqueName": "C:\\projDir1\\projDir2\\proj.csproj", + "projectName": "proj", + "projectPath": "C:\\projDir1\\projDir2\\proj.csproj", + "packagesPath": "C:\\.nuget\\packages\\", + "outputPath": "C:\\projDir1\\projDir2\\obj\\", + "projectStyle": "PackageReference", + "fallbackFolders": [ + "C:\\fallbackDir\\NuGetPackages" + ], + "configFilePaths": [ + "C:\\configDir\\NuGet.Config", + "C:\\configDir2\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\configDir3\\Microsoft.VisualStudio.Offline.config" + ], + "originalTargetFrameworks": [ + "net6.0" + ], + "frameworks": { + "net6.0": { + "targetAlias": "net6.0", + "projectReferences": {} + } + }, + "warningProperties": { + "warnAsError": [ + "NU1605" + ] + } + }, + "frameworks" : { + "net6.0": { + "targetAlias" : "net6.0", + "dependencies" : { + "top.package1" : { + "target" : "Package", + "version" : "[1.0.0, )" + } + } + } + } + } +} +"""; + return json.Replace("PACKAGE2_TYPE", package2Type) + .Replace("PACKAGE3_TYPE", package3Type) + .Replace(@"C:\\.nuget", $@"{testRoot.Replace("\\", "\\\\")}\\.nuget"); + } + private ResolvePackageAssets InitializeTask(string testRoot, out IEnumerable inputProperties) + { + inputProperties = typeof(ResolvePackageAssets) + .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public) + .Where(p => !p.IsDefined(typeof(OutputAttribute)) && + p.Name != nameof(ResolvePackageAssets.DesignTimeBuild)) + .OrderBy(p => p.Name, StringComparer.Ordinal); + + var requiredProperties = inputProperties + .Where(p => p.IsDefined(typeof(RequiredAttribute))); + + var task = new ResolvePackageAssets(); + // Initialize all required properties as a genuine task invocation would. We do this + // because HashSettings need not defend against required parameters being null. + foreach (var property in requiredProperties) { - TargetFramework = "net45", - DefaultImplicitPackages = string.Empty, - PackageDefinitions = new ITaskItem[] { - new MockTaskItem( - itemSpec: "Package1/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "Package1" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "" }, - { MetadataKeys.Type, "Package" } - }), - new MockTaskItem( - itemSpec: "ChildPackage1/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.Name, "ChildPackage1" }, - { MetadataKeys.Version, "1.0.0" }, - { MetadataKeys.Path, "some path" }, - { MetadataKeys.ResolvedPath, "some resolved path" }, - { MetadataKeys.Type, "Package" } - }) - }, - PackageDependencies = new ITaskItem[] { - new MockTaskItem( - itemSpec: "Package1/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" } - }), - new MockTaskItem( - itemSpec: "ChildPackage1/1.0.0", - metadata: new Dictionary - { - { MetadataKeys.ParentTarget, "net45" }, - { MetadataKeys.ParentPackage, "Package1/1.0.0" } - }) - } - }; - - Assert.True(task.Execute()); - - var item = Assert.Single(task.PackageDependenciesDesignTime); - - Assert.Equal("Package1/1.0.0", item.ItemSpec); + property.PropertyType.Should().Be( + typeof(string), + because: $"this test hasn't been updated to handle non-string required task parameters like {property.Name}"); + + property.SetValue(task, "_"); + } + + task.BuildEngine = new MockBuildEngine(); + + CreateFolders(testRoot); + + return task; + } + + private void CreateFolders(string testRoot) + { + Directory.CreateDirectory($@"{testRoot}\.nuget\packages\top.package1\1.0.0\"); + Directory.CreateDirectory($@"{testRoot}\.nuget\packages\top.package2\1.0.0\"); + Directory.CreateDirectory($@"{testRoot}\.nuget\packages\top.package3\1.0.0\"); + Directory.CreateDirectory($@"{testRoot}\.nuget\packages\dependent.package1\1.0.0\"); + Directory.CreateDirectory($@"{testRoot}\.nuget\packages\dependent.package2\1.0.0\"); + Directory.CreateDirectory($@"{testRoot}\.nuget\packages\dependent.package3\1.0.0\"); + using (File.Create($@"{testRoot}\.nuget\packages\top.package1\1.0.0\.nupkg.metadata")) { } + using (File.Create($@"{testRoot}\.nuget\packages\top.package1\1.0.0\top.package1.1.0.0.nupkg.sha512")) { } + using (File.Create($@"{testRoot}\.nuget\packages\top.package2\1.0.0\.nupkg.metadata")) { } + using (File.Create($@"{testRoot}\.nuget\packages\top.package2\1.0.0\top.package2.1.0.0.nupkg.sha512")) { } + using (File.Create($@"{testRoot}\.nuget\packages\top.package3\1.0.0\.nupkg.metadata")) { } + using (File.Create($@"{testRoot}\.nuget\packages\top.package3\1.0.0\top.package3.1.0.0.nupkg.sha512")) { } + using (File.Create($@"{testRoot}\.nuget\packages\dependent.package1\1.0.0\.nupkg.metadata")) { } + using (File.Create($@"{testRoot}\.nuget\packages\dependent.package1\1.0.0\dependent.package1.10.0.nupkg.sha512")) { } + using (File.Create($@"{testRoot}\.nuget\packages\dependent.package2\1.0.0\.nupkg.metadata")) { } + using (File.Create($@"{testRoot}\.nuget\packages\dependent.package2\1.0.0\dependent.package2.1.0.0.nupkg.sha512")) { } + using (File.Create($@"{testRoot}\.nuget\packages\dependent.package3\1.0.0\.nupkg.metadata")) { } + using (File.Create($@"{testRoot}\.nuget\packages\dependent.package3\1.0.0\dependent.package3.1.0.0.nupkg.sha512")) { } + } } } + diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/CollectSDKReferencesDesignTime.cs b/src/Tasks/Microsoft.NET.Build.Tasks/CollectSDKReferencesDesignTime.cs index 30a1d7f7a726..3f009bfde6ba 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/CollectSDKReferencesDesignTime.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/CollectSDKReferencesDesignTime.cs @@ -34,8 +34,7 @@ public class CollectSDKReferencesDesignTime : TaskBase protected override void ExecuteCore() { - ImplicitPackageReferences = - PreprocessPackageDependenciesDesignTime.GetImplicitPackageReferences(DefaultImplicitPackages); + ImplicitPackageReferences = GetImplicitPackageReferences(DefaultImplicitPackages); var sdkDesignTimeList = new List(SdkReferences); sdkDesignTimeList.AddRange(GetImplicitPackageReferences()); @@ -43,6 +42,28 @@ protected override void ExecuteCore() SDKReferencesDesignTime = sdkDesignTimeList.ToArray(); } + internal static HashSet GetImplicitPackageReferences(string defaultImplicitPackages) + { + var implicitPackageReferences = new HashSet(StringComparer.OrdinalIgnoreCase); + if (string.IsNullOrEmpty(defaultImplicitPackages)) + { + return implicitPackageReferences; + } + + var packageNames = defaultImplicitPackages.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + if (packageNames.Length == 0) + { + return implicitPackageReferences; + } + + foreach (var packageReference in packageNames) + { + implicitPackageReferences.Add(packageReference); + } + + return implicitPackageReferences; + } + private IEnumerable GetImplicitPackageReferences() { var implicitPackages = new List(); @@ -74,4 +95,4 @@ private IEnumerable GetImplicitPackageReferences() return implicitPackages; } } -} \ No newline at end of file +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/PreprocessPackageDependenciesDesignTime.cs b/src/Tasks/Microsoft.NET.Build.Tasks/PreprocessPackageDependenciesDesignTime.cs deleted file mode 100644 index 2592cda63698..000000000000 --- a/src/Tasks/Microsoft.NET.Build.Tasks/PreprocessPackageDependenciesDesignTime.cs +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using Microsoft.Build.Framework; -using Microsoft.Build.Utilities; - -namespace Microsoft.NET.Build.Tasks -{ - /// - /// Filters and projects items produced by for consumption by - /// the dependencies tree, via design-time builds. - /// - /// - /// Only top-level package references are retained (i.e. those referenced directly by the project, not - /// those only brought in transitively). - /// - /// Only package references applicable to are retained. - /// - /// Changes to the implementation of this class must be coordinated with PackageRuleHandler - /// in the dotnet/project-system repo. - /// - public class PreprocessPackageDependenciesDesignTime : TaskBase - { - public const string ResolvedMetadata = "Resolved"; - - /// - /// Information about each package in the project, with metadata: - /// - Name = "MetadataExtractor" - /// - Path = "metadataextractor/1.0.0" - /// - ResolvedPath = "C:\Users\drnoakes\.nuget\packages\metadataextractor\1.0.0" - /// - Type = "package" - /// - Version = "2.3.0" - /// - DiagnosticLevel = "" - /// - [Required] - public ITaskItem[] PackageDefinitions { get; set; } - - /// - /// Items with metadata "ParentTarget" and "ParentPackage", which allows determining the hierarchy of package references. - /// - [Required] - public ITaskItem[] PackageDependencies { get; set; } - - /// - /// Eg: "Microsoft.NETCore.App;NETStandard.Library" - /// - [Required] - public string DefaultImplicitPackages { get; set; } - - /// - /// The TargetFramework, which may be an alias - /// Eg: "netcoreapp3.1", "net5.0-windows", etc. - /// Only packages targeting this framework will be returned. - /// - [Required] - public string TargetFramework { get; set; } - - [Output] - public ITaskItem[] PackageDependenciesDesignTime { get; private set; } - - protected override void ExecuteCore() - { - var implicitPackageReferences = GetImplicitPackageReferences(DefaultImplicitPackages); - - // We have two types of data: - // - // 1) "PackageDependencies" which place a package in a given target/hierarchy - // 2) "PackageDefinitions" which provide general metadata about a package - // - // First, we scan PackageDependencies to build the set of packages in our target. - - var allowItemSpecs = new HashSet(StringComparer.OrdinalIgnoreCase); - - foreach (var dependency in PackageDependencies) - { - if (dependency.HasMetadataValue(MetadataKeys.ParentPackage)) - { - // ignore non-top-level packages (those with ParentPackage) - continue; - } - - var target = dependency.GetMetadata(MetadataKeys.ParentTarget); - - if (!StringComparer.OrdinalIgnoreCase.Equals(target, TargetFramework)) - { - // skip dependencies for other targets - continue; - } - - allowItemSpecs.Add(dependency.ItemSpec); - } - - // Second, find PackageDefinitions that match our allowed item specs - - var outputItems = new List(allowItemSpecs.Count); - - foreach (var packageDef in PackageDefinitions) - { - if (!allowItemSpecs.Contains(packageDef.ItemSpec)) - { - // We are not interested in this definition (not top-level, or wrong target) - continue; - } - - var dependencyType = GetDependencyType(packageDef.GetMetadata(MetadataKeys.Type)); - - if (dependencyType == DependencyType.Package || - dependencyType == DependencyType.Unresolved) - { - var name = packageDef.GetMetadata(MetadataKeys.Name); - - if (string.IsNullOrEmpty(name)) - { - // Name is required - continue; - } - - var version = packageDef.GetMetadata(MetadataKeys.Version) ?? string.Empty; - var resolvedPath = packageDef.GetMetadata(MetadataKeys.ResolvedPath); - var resolved = !string.IsNullOrEmpty(resolvedPath); - var path = (resolved - ? resolvedPath - : packageDef.GetMetadata(MetadataKeys.Path)) ?? string.Empty; - var isImplicitlyDefined = implicitPackageReferences.Contains(name); - var diagnosticLevel = packageDef.GetMetadata(MetadataKeys.DiagnosticLevel) ?? string.Empty; - - var outputItem = new TaskItem(packageDef.ItemSpec); - outputItem.SetMetadata(MetadataKeys.Name, name); - outputItem.SetMetadata(MetadataKeys.Version, version); - outputItem.SetMetadata(MetadataKeys.Path, path); - outputItem.SetMetadata(MetadataKeys.IsImplicitlyDefined, isImplicitlyDefined.ToString()); - outputItem.SetMetadata(MetadataKeys.DiagnosticLevel, diagnosticLevel); - outputItem.SetMetadata(ResolvedMetadata, resolved.ToString()); - - outputItems.Add(outputItem); - } - } - - PackageDependenciesDesignTime = outputItems.ToArray(); - } - - internal static HashSet GetImplicitPackageReferences(string defaultImplicitPackages) - { - var implicitPackageReferences = new HashSet(StringComparer.OrdinalIgnoreCase); - if (string.IsNullOrEmpty(defaultImplicitPackages)) - { - return implicitPackageReferences; - } - - var packageNames = defaultImplicitPackages.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - if (packageNames.Length == 0) - { - return implicitPackageReferences; - } - - foreach (var packageReference in packageNames) - { - implicitPackageReferences.Add(packageReference); - } - - return implicitPackageReferences; - } - - private static DependencyType GetDependencyType(string dependencyTypeString) - { - Enum.TryParse(dependencyTypeString, ignoreCase: true, out DependencyType dependencyType); - return dependencyType; - } - } -} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs index 48ee5d280c58..74e1e19b2415 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs @@ -8,10 +8,12 @@ using System.Linq; using System.Security.Cryptography; using System.Text; +using System.Xml.Linq; using Microsoft.Build.Evaluation; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using NuGet.Common; +using NuGet.Frameworks; using NuGet.ProjectModel; using NuGet.Versioning; @@ -27,6 +29,8 @@ namespace Microsoft.NET.Build.Tasks /// public sealed class ResolvePackageAssets : TaskBase { + #region Input Items + /// /// Path to assets.json. /// @@ -160,6 +164,15 @@ public sealed class ResolvePackageAssets : TaskBase /// public bool DesignTimeBuild { get; set; } + /// + /// Eg: "Microsoft.NETCore.App;NETStandard.Library" + /// + [Required] + public string DefaultImplicitPackages { get; set; } + + #endregion + + #region Output Items /// /// Full paths to assemblies from packages to pass to compiler as analyzers. /// @@ -233,6 +246,17 @@ public sealed class ResolvePackageAssets : TaskBase [Output] public ITaskItem[] PackageDependencies { get; private set; } + /// + /// Filters and projects items produced by for consumption by + /// the dependencies tree, via design-time builds. + /// + /// + /// Changes to the implementation of output must be coordinated with PackageRuleHandler + /// in the dotnet/project-system repo. + /// + [Output] + public ITaskItem[] PackageDependenciesDesignTime { get; private set; } + /// /// List of symbol files (.pdb) related to NuGet packages. /// @@ -251,6 +275,8 @@ public sealed class ResolvePackageAssets : TaskBase [Output] public ITaskItem[] ReferenceDocumentationFiles { get; private set; } + #endregion + /// /// Messages from the assets file. /// These are logged directly and therefore not returned to the targets (note private here). @@ -304,7 +330,7 @@ public sealed class ResolvePackageAssets : TaskBase //////////////////////////////////////////////////////////////////////////////////////////////////// private const int CacheFormatSignature = ('P' << 0) | ('K' << 8) | ('G' << 16) | ('A' << 24); - private const int CacheFormatVersion = 11; + private const int CacheFormatVersion = 12; private static readonly Encoding TextEncoding = Encoding.UTF8; private const int SettingsHashLength = 256 / 8; private HashAlgorithm CreateSettingsHash() => SHA256.Create(); @@ -336,6 +362,7 @@ private void ReadItemGroups() FrameworkReferences = reader.ReadItemGroup(); NativeLibraries = reader.ReadItemGroup(); PackageDependencies = reader.ReadItemGroup(); + PackageDependenciesDesignTime = reader.ReadItemGroup(); PackageFolders = reader.ReadItemGroup(); ReferenceDocumentationFiles = reader.ReadItemGroup(); ResourceAssemblies = reader.ReadItemGroup(); @@ -694,7 +721,6 @@ public CacheWriter(ResolvePackageAssets task) _lockFile = new LockFileCache(task).GetLockFile(task.ProjectAssetsFile); _packageResolver = NuGetPackageResolver.CreateResolver(_lockFile); - // If we are doing a design-time build, we do not want to fail the build if we can't find the // target framework and/or runtime identifier in the assets file. This is because the design-time // build needs to succeed in order to get the right information in order to run a restore in order @@ -816,6 +842,7 @@ private void WriteItemGroups() WriteItemGroup(WriteFrameworkReferences); WriteItemGroup(WriteNativeLibraries); WriteItemGroup(WritePackageDependencies); + WriteItemGroup(WritePackageDependenciesDesignTime); WriteItemGroup(WritePackageFolders); WriteItemGroup(WriteReferenceDocumentationFiles); WriteItemGroup(WriteResourceAssemblies); @@ -1166,17 +1193,13 @@ private void WriteDebugItems( foreach (string fileExtension in relatedExtensions.Split(RelatedPropertySeparator)) { - if (fileExtension.ToLower() == extension) + if (StringComparer.InvariantCulture.Equals(fileExtension, extension)) { string xmlFilePath = Path.ChangeExtension(itemSpec, fileExtension); if (File.Exists(xmlFilePath)) { WriteItem(xmlFilePath, library); } - else - { - _task.Log.LogWarning(Strings.AssetsFileNotFound, xmlFilePath); - } } } } @@ -1414,6 +1437,110 @@ private void WritePackageDependencies() } } + private void WritePackageDependenciesDesignTime() + { + var implicitPackageReferences = CollectSDKReferencesDesignTime.GetImplicitPackageReferences(_task.DefaultImplicitPackages); + + // Scan PackageDependencies to build the set of packages in our target. + var allowItemSpecs = GetPackageDependencies(); + + foreach (var package in _lockFile.Libraries) + { + var packageVersion = package.Version.ToNormalizedString(); + string packageId = $"{package.Name}/{packageVersion}"; + + // Find PackageDefinitions that match our allowed item specs + if (string.IsNullOrEmpty(package.Name) || !allowItemSpecs.Contains(packageId)) + { + // Only include packages from the allow list. + // This excludes transitive packages and those from other targets. + continue; + } + + var dependencyType = GetDependencyType(package.Type); + + if (dependencyType == DependencyType.Package || + dependencyType == DependencyType.Unresolved) + { + WriteItem(packageId); + WriteMetadata(MetadataKeys.Name, package.Name); + + var version = packageVersion ?? string.Empty; + WriteMetadata(MetadataKeys.Version, version); + + var isImplicitlyDefined = implicitPackageReferences.Contains(package.Name); + WriteMetadata(MetadataKeys.IsImplicitlyDefined, isImplicitlyDefined.ToString()); + + string resolvedPackagePath = _packageResolver.GetPackageDirectory(package.Name, package.Version); + var resolvedPath = resolvedPackagePath ?? string.Empty; + var resolved = !string.IsNullOrEmpty(resolvedPath); + WriteMetadata(MetadataKeys.Resolved, resolved.ToString()); + + string itemPath = package.Path ?? string.Empty; + var path = (resolved + ? resolvedPath + : itemPath) ?? string.Empty; + WriteMetadata(MetadataKeys.Path, path); + + string itemDiagnosticLevel = GetPackageDiagnosticLevel(package); + var diagnosticLevel = itemDiagnosticLevel ?? string.Empty; + WriteMetadata(MetadataKeys.DiagnosticLevel, diagnosticLevel); + } + } + + HashSet GetPackageDependencies() + { + HashSet projectFileDependencies = _lockFile.GetProjectFileDependencySet(_compileTimeTarget.Name); + + HashSet results = new(StringComparer.OrdinalIgnoreCase); + + foreach (var package in _compileTimeTarget.Libraries) + { + if (projectFileDependencies.Contains(package.Name)) + { + string itemSpec = GetPackageId(package); + + bool added = results.Add(itemSpec); + + Debug.Assert(added); + } + } + + return results; + } + + static string GetPackageId(LockFileTargetLibrary package) => $"{package.Name}/{package.Version.ToNormalizedString()}"; + + string GetPackageDiagnosticLevel(LockFileLibrary package) + { + string target = _task.TargetFramework ?? ""; + + var messages = _lockFile.LogMessages.Where(log => + log.LibraryId == package.Name && + log.TargetGraphs.Any(tg => + { + var parsedTargetGraph = NuGetFramework.Parse(tg); + var alias = _lockFile.PackageSpec.TargetFrameworks + .FirstOrDefault(tf => tf.FrameworkName == parsedTargetGraph) + ?.TargetAlias ?? tg; + return alias == target; + })); + + if (!messages.Any()) + { + return string.Empty; + } + + return messages.Max(log => log.Level).ToString(); + } + + static DependencyType GetDependencyType(string dependencyTypeString) + { + Enum.TryParse(dependencyTypeString, ignoreCase: true, out DependencyType dependencyType); + return dependencyType; + } + } + private void WriteResourceAssemblies() { WriteItems( diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageDependencies.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageDependencies.cs index be6e7e20b3da..7785c24105db 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageDependencies.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageDependencies.cs @@ -16,11 +16,11 @@ namespace Microsoft.NET.Build.Tasks /// Raises Nuget LockFile representation to MSBuild items and resolves /// assets specified in the lock file. /// + /// + /// Only called for backwards compatability, when ResolvePackageDependencies is true. + /// public sealed class ResolvePackageDependencies : TaskBase { - /// - /// Only used if is . - /// private readonly Dictionary _fileTypes = new Dictionary(StringComparer.OrdinalIgnoreCase); private HashSet _projectFileDependencies; @@ -37,7 +37,6 @@ public sealed class ResolvePackageDependencies : TaskBase /// /// All the targets in the lock file. - /// Only populated if is . /// [Output] public ITaskItem[] TargetDefinitions @@ -56,7 +55,6 @@ public ITaskItem[] PackageDefinitions /// /// All the files in the lock file. - /// Only populated if is . /// [Output] public ITaskItem[] FileDefinitions @@ -77,7 +75,6 @@ public ITaskItem[] PackageDependencies /// /// All the dependencies between files and packages, labeled by the group containing /// the file (e.g. CompileTimeAssembly, RuntimeAssembly, etc.). - /// Only populated if is . /// [Output] public ITaskItem[] FileDependencies @@ -114,12 +111,6 @@ public string ProjectLanguage get; set; } - /// - /// Setting this property restores pre-16.7 behaviour of populating , - /// and outputs. - /// - public bool EmitLegacyAssetsFileItems { get; set; } = false; - public string TargetFramework { get; set; } #endregion @@ -195,11 +186,6 @@ private void GetPackageAndFileDefinitions() _packageDefinitions.Add(item); - if (!EmitLegacyAssetsFileItems) - { - continue; - } - foreach (var file in package.Files) { if (NuGetUtils.IsPlaceholderFile(file)) @@ -276,18 +262,15 @@ private void RaiseLockFileTargets() { foreach (var target in LockFile.Targets) { - if (EmitLegacyAssetsFileItems) - { - TaskItem item = new TaskItem(target.Name); - item.SetMetadata(MetadataKeys.RuntimeIdentifier, target.RuntimeIdentifier ?? string.Empty); - item.SetMetadata(MetadataKeys.TargetFramework, TargetFramework); - item.SetMetadata(MetadataKeys.TargetFrameworkMoniker, target.TargetFramework.DotNetFrameworkName); - item.SetMetadata(MetadataKeys.FrameworkName, target.TargetFramework.Framework); - item.SetMetadata(MetadataKeys.FrameworkVersion, target.TargetFramework.Version.ToString()); - item.SetMetadata(MetadataKeys.Type, "target"); - - _targetDefinitions.Add(item); - } + TaskItem item = new TaskItem(target.Name); + item.SetMetadata(MetadataKeys.RuntimeIdentifier, target.RuntimeIdentifier ?? string.Empty); + item.SetMetadata(MetadataKeys.TargetFramework, TargetFramework); + item.SetMetadata(MetadataKeys.TargetFrameworkMoniker, target.TargetFramework.DotNetFrameworkName); + item.SetMetadata(MetadataKeys.FrameworkName, target.TargetFramework.Framework); + item.SetMetadata(MetadataKeys.FrameworkVersion, target.TargetFramework.Version.ToString()); + item.SetMetadata(MetadataKeys.Type, "target"); + + _targetDefinitions.Add(item); // raise each library in the target GetPackageAndFileDependencies(target); @@ -323,11 +306,8 @@ private void GetPackageAndFileDependencies(LockFileTarget target) // get sub package dependencies GetPackageDependencies(package, target.Name, resolvedPackageVersions, transitiveProjectRefs); - if (EmitLegacyAssetsFileItems) - { - // get file dependencies on this package - GetFileDependencies(package, target.Name); - } + // get file dependencies on this package + GetFileDependencies(package, target.Name); } } @@ -375,7 +355,7 @@ private void GetFileDependencies(LockFileTargetLibrary package, string targetNam string filePath = entry.Item1; IDictionary properties = entry.Item2; - if (NuGetUtils.IsPlaceholderFile(filePath) || !EmitLegacyAssetsFileItems) + if (NuGetUtils.IsPlaceholderFile(filePath)) { continue; } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.PackageDependencyResolution.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.PackageDependencyResolution.targets index 2456989cbf34..dc048fde8e68 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.PackageDependencyResolution.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.PackageDependencyResolution.targets @@ -192,18 +192,17 @@ Copyright (c) .NET Foundation. All rights reserved. RuntimeIdentifier="$(RuntimeIdentifier)" Condition=" '$(DesignTimeBuild)' != 'true'"/> + + ContinueOnError="ErrorAndContinue" + Condition="'$(EmitLegacyAssetsFileItems)' == 'true'"> - - @@ -290,7 +289,8 @@ Copyright (c) .NET Foundation. All rights reserved. SatelliteResourceLanguages="$(SatelliteResourceLanguages)" DesignTimeBuild="$(DesignTimeBuild)" ContinueOnError="$(ContinueOnError)" - PackageReferences="@(PackageReference)"> + PackageReferences="@(PackageReference)" + DefaultImplicitPackages= "$(DefaultImplicitPackages)"> @@ -309,6 +309,7 @@ Copyright (c) .NET Foundation. All rights reserved. + @@ -339,23 +340,9 @@ Copyright (c) .NET Foundation. All rights reserved. ============================================================ --> - - - - - - - - - + DependsOnTargets="ResolvePackageAssets;RunResolvePackageDependencies;ResolveAssemblyReferencesDesignTime" />