diff --git a/src/Microsoft.DotNet.Compiler.Common/BindingRedirectGenerator.cs b/src/Microsoft.DotNet.Compiler.Common/BindingRedirectGenerator.cs index 71472b478..deca9b303 100644 --- a/src/Microsoft.DotNet.Compiler.Common/BindingRedirectGenerator.cs +++ b/src/Microsoft.DotNet.Compiler.Common/BindingRedirectGenerator.cs @@ -34,7 +34,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common private static SHA1 Sha1 { get; } = SHA1.Create(); - internal static XDocument GenerateBindingRedirects(this IEnumerable dependencies, XDocument document) + public static XDocument GenerateBindingRedirects(this IEnumerable dependencies, XDocument document) { var redirects = CollectRedirects(dependencies); @@ -107,23 +107,34 @@ namespace Microsoft.DotNet.Cli.Compiler.Common return element; } - private static AssemblyRedirect[] CollectRedirects(IEnumerable dependencies) + internal static AssemblyRedirect[] CollectRedirects(IEnumerable dependencies) { - var allRuntimeAssemblies = dependencies + var runtimeAssemblies = dependencies .SelectMany(d => d.RuntimeAssemblyGroups.GetDefaultAssets()) - .Select(GetAssemblyInfo) - .ToArray(); + .Select(GetAssemblyInfo); - var assemblyLookup = allRuntimeAssemblies.ToDictionary(r => r.Identity.ToLookupKey()); + return CollectRedirects(runtimeAssemblies); + } + + internal static AssemblyRedirect[] CollectRedirects(IEnumerable runtimeAssemblies) + { + var assemblyLookup = runtimeAssemblies.ToLookup(r => r.Identity.ToLookupKey()); var redirectAssemblies = new HashSet(); - foreach (var assemblyReferenceInfo in allRuntimeAssemblies) + foreach (var assemblyReferenceInfo in assemblyLookup) { - foreach (var referenceIdentity in assemblyReferenceInfo.References) + // Using .First here is not exactly valid, but we don't know which one gets copied to + // output so we just use first + var references = assemblyReferenceInfo.First().References; + foreach (var referenceIdentity in references) { - AssemblyReferenceInfo targetAssemblyIdentity; - if (assemblyLookup.TryGetValue(referenceIdentity.ToLookupKey(), out targetAssemblyIdentity) - && targetAssemblyIdentity.Identity.Version != referenceIdentity.Version) + var targetAssemblies = assemblyLookup[referenceIdentity.ToLookupKey()]; + if (!targetAssemblies.Any()) + { + continue; + } + var targetAssemblyIdentity = targetAssemblies.First(); + if (targetAssemblyIdentity.Identity.Version != referenceIdentity.Version) { if (targetAssemblyIdentity.Identity.PublicKeyToken != null) { @@ -200,7 +211,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common return hex.ToString(); } - private struct AssemblyRedirect + internal struct AssemblyRedirect { public AssemblyRedirect(AssemblyIdentity from, AssemblyIdentity to) { @@ -213,7 +224,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common public AssemblyIdentity To { get; set; } } - private struct AssemblyIdentity + internal struct AssemblyIdentity { public AssemblyIdentity(string name, Version version, string culture, string publicKeyToken) { @@ -239,7 +250,7 @@ namespace Microsoft.DotNet.Cli.Compiler.Common } } - private struct AssemblyReferenceInfo + internal struct AssemblyReferenceInfo { public AssemblyReferenceInfo(AssemblyIdentity identity, AssemblyIdentity[] references) { diff --git a/src/Microsoft.DotNet.Compiler.Common/Properties/Properties.cs b/src/Microsoft.DotNet.Compiler.Common/Properties/Properties.cs new file mode 100644 index 000000000..233d59e67 --- /dev/null +++ b/src/Microsoft.DotNet.Compiler.Common/Properties/Properties.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("dotnet-compile.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")] \ No newline at end of file diff --git a/test/dotnet-compile.UnitTests/BindingRedirectGeneratorTests.cs b/test/dotnet-compile.UnitTests/BindingRedirectGeneratorTests.cs new file mode 100644 index 000000000..ac7f3c3ef --- /dev/null +++ b/test/dotnet-compile.UnitTests/BindingRedirectGeneratorTests.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.DotNet.Cli.Compiler.Common; +using Xunit; +using FluentAssertions; + +using AssemblyReferenceInfo = Microsoft.DotNet.Cli.Compiler.Common.BindingRedirectGenerator.AssemblyReferenceInfo; +using AssemblyIdentity = Microsoft.DotNet.Cli.Compiler.Common.BindingRedirectGenerator.AssemblyIdentity; + +namespace Microsoft.DotNet.Tools.Compiler.Tests +{ + public class BindingRedirectGeneratorTests + { + [Fact] + public void ResolvesDuplicatesUsingFirstOccurence() + { + var hash = "01234qwerty"; + var redirects = BindingRedirectGenerator.CollectRedirects(new[] + { + new AssemblyReferenceInfo( + new AssemblyIdentity("A", new Version(1, 5), "en-US", hash), + new [] + { + new AssemblyIdentity("B", new Version(1, 1), "en-US", hash), + } + ), + new AssemblyReferenceInfo( + new AssemblyIdentity("B", new Version(1, 4), "en-US", hash), new AssemblyIdentity[] {} + ), + new AssemblyReferenceInfo( + new AssemblyIdentity("B", new Version(1, 5), "en-US", hash), new AssemblyIdentity[] {} + ), + new AssemblyReferenceInfo( + new AssemblyIdentity("C", new Version(1, 5), "en-US", hash), + new [] + { + new AssemblyIdentity("B", new Version(1, 3), "en-US", hash), + } + ) + }); + + redirects.Should().HaveCount(2); + redirects.Should().Contain(r => + r.From.Version == new Version(1, 1) && + r.From.Name == "B" && + r.To.Version == new Version(1, 4) && + r.To.Name == "B" + ); + redirects.Should().Contain(r => + r.From.Version == new Version(1, 3) && + r.From.Name == "B" && + r.To.Version == new Version(1, 4) && + r.To.Name == "B" + ); + } + } +} diff --git a/test/dotnet-compile.UnitTests/project.json b/test/dotnet-compile.UnitTests/project.json index ee937fc6c..67f59956b 100644 --- a/test/dotnet-compile.UnitTests/project.json +++ b/test/dotnet-compile.UnitTests/project.json @@ -1,9 +1,15 @@ { "version": "1.0.0-*", + "compilationOptions": { + "keyFile": "../../tools/Key.snk" + }, "dependencies": { "Microsoft.DotNet.Cli.Utils": { "target": "project" }, + "Microsoft.DotNet.Compiler.Common": { + "target": "project" + }, "dotnet": { "target": "project" },