diff --git a/src/Tasks/Common/MetadataKeys.cs b/src/Tasks/Common/MetadataKeys.cs index 71d66a3d8f69..5355f3f964d1 100644 --- a/src/Tasks/Common/MetadataKeys.cs +++ b/src/Tasks/Common/MetadataKeys.cs @@ -101,6 +101,8 @@ internal static class MetadataKeys public const string DestinationSubPath = "DestinationSubPath"; public const string AssetType = "AssetType"; - public const string ReferenceOnly = "ReferenceOnly"; + public const string ReferenceOnly = "ReferenceOnly"; + + public const string Aliases = "Aliases"; } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs index 830a94ac80ea..66210eb70217 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection.Metadata; using System.Security.Cryptography; using System.Text; using Microsoft.Build.Evaluation; @@ -869,7 +868,15 @@ private void WriteCompileTimeAssemblies() { WriteItems( _compileTimeTarget, - package => package.CompileTimeAssemblies); + package => package.CompileTimeAssemblies, + filter: null, + writeMetadata: (package, asset) => + { + if (asset.Properties.TryGetValue(LockFileItem.AliasesProperty, out var aliases)) + { + WriteMetadata(MetadataKeys.Aliases, aliases); + } + }); } private void WriteContentFilesToPreprocess() diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeHaveAPackageReferenceWithAliases.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeHaveAPackageReferenceWithAliases.cs new file mode 100644 index 000000000000..a9308086dfec --- /dev/null +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeHaveAPackageReferenceWithAliases.cs @@ -0,0 +1,249 @@ +// 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 FluentAssertions; +using Microsoft.NET.TestFramework; +using Microsoft.NET.TestFramework.Assertions; +using Microsoft.NET.TestFramework.Commands; +using Microsoft.NET.TestFramework.ProjectConstruction; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.NET.Build.Tests +{ + public class GivenThatWeHaveAPackageReferenceWithAliases : SdkTest + { + + public GivenThatWeHaveAPackageReferenceWithAliases(ITestOutputHelper log) : base(log) + { } + + [CoreMSBuildOnlyFact] + public void CanBuildProjectWithPackageReferencesWithConflictingTypes() + { + var targetFramework = "netcoreapp3.1"; + var packageReferences = GetPackageReferencesWithConflictingTypes(targetFramework, "A", "B"); + + TestProject testProject = new TestProject() + { + Name = "Project", + IsSdkProject = true, + IsExe = false, + TargetFrameworks = targetFramework, + }; + + testProject.PackageReferences.Add(packageReferences.First()); + testProject.PackageReferences.Add( + new TestPackageReference( + packageReferences.Last().ID, + packageReferences.Last().Version, + packageReferences.Last().NupkgPath, + packageReferences.Last().PrivateAssets, + aliases: "Special")); + var packagesPaths = packageReferences.Select(e => Path.GetDirectoryName(e.NupkgPath)); + + testProject.AdditionalProperties.Add("RestoreSources", + "$(RestoreSources);" + string.Join(";", packagesPaths)); + + // Use a test-specific packages folder + testProject.AdditionalProperties["RestorePackagesPath"] = @"$(MSBuildProjectDirectory)\..\pkg"; + testProject.SourceFiles[$"{testProject.Name}.cs"] = ConflictingClassLibUsage; + var testAsset = _testAssetsManager.CreateTestProject(testProject); + + var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + buildCommand.Execute() + .Should() + .Pass(); + } + + [CoreMSBuildOnlyFact] + public void CanBuildProjectWithMultiplePackageReferencesWithAliases() + { + var targetFramework = "netcoreapp3.1"; + + var packageReferenceA = GetPackageReference(targetFramework, "A", ClassLibClassA); + var packageReferenceB = GetPackageReference(targetFramework, "B", ClassLibClassB); + + TestProject testProject = new TestProject() + { + Name = "Project", + IsSdkProject = true, + IsExe = false, + TargetFrameworks = targetFramework, + }; + + testProject.PackageReferences.Add( + new TestPackageReference( + packageReferenceA.ID, + packageReferenceA.Version, + packageReferenceA.NupkgPath, + packageReferenceA.PrivateAssets, + aliases: "First")); + testProject.PackageReferences.Add( + new TestPackageReference( + packageReferenceB.ID, + packageReferenceB.Version, + packageReferenceB.NupkgPath, + packageReferenceB.PrivateAssets, + aliases: "Second")); + + testProject.AdditionalProperties.Add("RestoreSources", + "$(RestoreSources);" + Path.GetDirectoryName(packageReferenceA.NupkgPath) + ";" + Path.GetDirectoryName(packageReferenceB.NupkgPath)); + + // Use a test-specific packages folder + testProject.AdditionalProperties["RestorePackagesPath"] = @"$(MSBuildProjectDirectory)\..\pkg"; + testProject.SourceFiles[$"{testProject.Name}.cs"] = ClassLibAandBUsage; + var testAsset = _testAssetsManager.CreateTestProject(testProject); + + var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + buildCommand.Execute() + .Should() + .Pass(); + } + + [CoreMSBuildOnlyFact] + public void CanBuildProjectWithAPackageReferenceWithMultipleAliases() + { + var targetFramework = "netcoreapp3.1"; + + var packageReferenceA = GetPackageReference(targetFramework, "A", ClassLibMultipleClasses); + + TestProject testProject = new TestProject() + { + Name = "Project", + IsSdkProject = true, + IsExe = false, + TargetFrameworks = targetFramework, + }; + + testProject.PackageReferences.Add( + new TestPackageReference( + packageReferenceA.ID, + packageReferenceA.Version, + packageReferenceA.NupkgPath, + packageReferenceA.PrivateAssets, + aliases: "First,Second")); + + testProject.AdditionalProperties.Add("RestoreSources", + "$(RestoreSources);" + Path.GetDirectoryName(packageReferenceA.NupkgPath)); + + // Use a test-specific packages folder + testProject.AdditionalProperties["RestorePackagesPath"] = @"$(MSBuildProjectDirectory)\..\pkg"; + testProject.SourceFiles[$"{testProject.Name}.cs"] = ClassLibAandBUsage; + var testAsset = _testAssetsManager.CreateTestProject(testProject); + + var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + buildCommand.Execute() + .Should() + .Pass(); + } + + private IEnumerable GetPackageReferencesWithConflictingTypes(string targetFramework, params string[] packageNames) + { + foreach (var packageName in packageNames) + { + yield return GetPackageReference(targetFramework, packageName, ClassLibConflictingMethod); + } + } + + private TestPackageReference GetPackageReference(string targetFramework, string packageName, string projectFileContent) + { + var project = GetProject(targetFramework, packageName, projectFileContent); + var packCommand = new PackCommand(Log, _testAssetsManager.CreateTestProject(project).TestRoot, packageName); + + packCommand + .Execute() + .Should() + .Pass(); + return new TestPackageReference(packageName, "1.0.0", packCommand.GetNuGetPackage(packageName)); + } + + private static TestProject GetProject(string targetFramework, string referenceProjectName, string projectFileContent) + { + var project = new TestProject() + { + Name = referenceProjectName, + TargetFrameworks = targetFramework, + IsSdkProject = true + }; + project.SourceFiles[$"{referenceProjectName}.cs"] = projectFileContent; + return project; + } + + private static string ClassLibConflictingMethod = @" +using System; +public class ClassLib +{ + public void ConflictingMethod() + { + } +} +"; + + private static string ClassLibClassA = @" +using System; +public class A +{ + public void AMethod() + { + } +} +"; + + private static string ClassLibMultipleClasses = @" +using System; +public class A +{ + public void AMethod() + { + } +} + +public class B +{ + public void BMethod() + { + } +} +"; + + private static string ClassLibClassB = @" +using System; +public class B +{ + public void BMethod() + { + } +} +"; + + private static string ClassLibAandBUsage = @" +extern alias First; +extern alias Second; +using System; +public class ClassLibUsage +{ + public void UsageMethod() + { + new First.A().AMethod(); + new Second.B().BMethod(); + } +} +"; + + private static string ConflictingClassLibUsage = @" +extern alias Special; +using System; +public class ClassLibUsage +{ + public void UsageMethod() + { + new Special.ClassLib().ConflictingMethod(); + } +} +"; + } +} diff --git a/src/Tests/Microsoft.NET.TestFramework/ProjectConstruction/TestProject.cs b/src/Tests/Microsoft.NET.TestFramework/ProjectConstruction/TestProject.cs index ea0e3a67f8a9..bb59bd78fb09 100644 --- a/src/Tests/Microsoft.NET.TestFramework/ProjectConstruction/TestProject.cs +++ b/src/Tests/Microsoft.NET.TestFramework/ProjectConstruction/TestProject.cs @@ -170,6 +170,10 @@ internal void Create(TestAsset targetTestAsset, string testProjectsSourceFolder, { packageReferenceElement.Add(new XAttribute("PrivateAssets", packageReference.PrivateAssets)); } + if (packageReference.Aliases != null) + { + packageReferenceElement.Add(new XAttribute("Aliases", packageReference.Aliases)); + } packageReferenceItemGroup.Add(packageReferenceElement); } diff --git a/src/Tests/Microsoft.NET.TestFramework/TestPackageReference.cs b/src/Tests/Microsoft.NET.TestFramework/TestPackageReference.cs index cb5ee4997b89..3a046ad96f98 100644 --- a/src/Tests/Microsoft.NET.TestFramework/TestPackageReference.cs +++ b/src/Tests/Microsoft.NET.TestFramework/TestPackageReference.cs @@ -11,19 +11,20 @@ namespace Microsoft.NET.TestFramework { public class TestPackageReference { - public TestPackageReference(string id, string version = null, string nupkgPath = null, string privateAssets = null) + public TestPackageReference(string id, string version = null, string nupkgPath = null, string privateAssets = null, string aliases = null) { ID = id; Version = version; NupkgPath = nupkgPath; PrivateAssets = privateAssets; + Aliases = aliases; } public string ID { get; private set; } public string Version { get; private set; } public string NupkgPath { get; private set; } public string PrivateAssets { get; private set; } - + public string Aliases { get; private set; } public bool NuGetPackageExists() { return File.Exists(Path.Combine(this.NupkgPath, String.Concat(this.ID + "." + this.Version + ".nupkg")));