diff --git a/TestAssets/TestProjects/PortableTests/PortableAppWithIntentionalManagedDowngrade/.noautobuild b/TestAssets/TestProjects/PortableTests/PortableAppWithIntentionalManagedDowngrade/.noautobuild new file mode 100644 index 000000000..e69de29bb diff --git a/TestAssets/TestProjects/PortableTests/PortableAppWithIntentionalManagedDowngrade/Program.cs b/TestAssets/TestProjects/PortableTests/PortableAppWithIntentionalManagedDowngrade/Program.cs new file mode 100644 index 000000000..fbe8e9b0e --- /dev/null +++ b/TestAssets/TestProjects/PortableTests/PortableAppWithIntentionalManagedDowngrade/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace PortableApp +{ + public static class Program + { + public static void Main(string[] args) + { + Console.WriteLine("Hello, World!"); + } + } +} diff --git a/TestAssets/TestProjects/PortableTests/PortableAppWithIntentionalManagedDowngrade/project.json b/TestAssets/TestProjects/PortableTests/PortableAppWithIntentionalManagedDowngrade/project.json new file mode 100644 index 000000000..d72d145d5 --- /dev/null +++ b/TestAssets/TestProjects/PortableTests/PortableAppWithIntentionalManagedDowngrade/project.json @@ -0,0 +1,21 @@ +{ + "compilationOptions": { + "emitEntryPoint": true + }, + "dependencies": {}, + "frameworks": { + "netstandard1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ], + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0-rc2-23925" + }, + "System.Linq": "4.0.0" + } + } + } +} diff --git a/src/dotnet/commands/dotnet-publish/PublishCommand.cs b/src/dotnet/commands/dotnet-publish/PublishCommand.cs index aaf29b4ee..5e91a6dce 100644 --- a/src/dotnet/commands/dotnet-publish/PublishCommand.cs +++ b/src/dotnet/commands/dotnet-publish/PublishCommand.cs @@ -14,6 +14,8 @@ using Microsoft.Extensions.PlatformAbstractions; using Microsoft.DotNet.Files; using Microsoft.DotNet.Tools.Common; using Microsoft.DotNet.ProjectModel.Utilities; +using Microsoft.DotNet.ProjectModel.Graph; +using NuGet.Versioning; namespace Microsoft.DotNet.Tools.Publish { @@ -153,7 +155,14 @@ namespace Microsoft.DotNet.Tools.Publish var exporter = context.CreateExporter(configuration); var isPortable = string.IsNullOrEmpty(context.RuntimeIdentifier); - foreach (var export in exporter.GetAllExports()) + + // Collect all exports and organize them + var exports = exporter.GetAllExports() + .Where(e => e.Library.Identity.Type.Equals(LibraryType.Package)) + .ToDictionary(e => e.Library.Identity.Name); + var collectExclusionList = isPortable ? GetExclusionList(context, exports) : new HashSet(); + + foreach (var export in exporter.GetAllExports().Where(e => !collectExclusionList.Contains(e.Library.Identity.Name))) { Reporter.Verbose.WriteLine($"Publishing {export.Library.Identity.ToString().Green().Bold()} ..."); @@ -198,6 +207,40 @@ namespace Microsoft.DotNet.Tools.Publish return true; } + private HashSet GetExclusionList(ProjectContext context, Dictionary exports) + { + var exclusionList = new HashSet(); + var redistPackages = context.RootProject.Dependencies + .Where(r => r.Type.Equals(LibraryDependencyType.Platform)) + .ToList(); + if (redistPackages.Count == 0) + { + return exclusionList; + } + else if (redistPackages.Count > 1) + { + throw new InvalidOperationException("Multiple packages with type: \"platform\" were specified!"); + } + var redistExport = exports[redistPackages[0].Name]; + + exclusionList.Add(redistExport.Library.Identity.Name); + CollectDependencies(exports, redistExport.Library.Dependencies, exclusionList); + return exclusionList; + } + + private void CollectDependencies(Dictionary exports, IEnumerable dependencies, HashSet exclusionList) + { + foreach (var dependency in dependencies) + { + var export = exports[dependency.Name]; + if(export.Library.Identity.Version.Equals(dependency.VersionRange.MinVersion)) + { + exclusionList.Add(export.Library.Identity.Name); + CollectDependencies(exports, export.Library.Dependencies, exclusionList); + } + } + } + private static void PublishRefs(LibraryExport export, string outputPath) { var refsPath = Path.Combine(outputPath, "refs"); diff --git a/test/dotnet-publish.Tests/PublishPortableTests.cs b/test/dotnet-publish.Tests/PublishPortableTests.cs index bb2e82c80..5b48e9907 100644 --- a/test/dotnet-publish.Tests/PublishPortableTests.cs +++ b/test/dotnet-publish.Tests/PublishPortableTests.cs @@ -36,6 +36,9 @@ namespace Microsoft.DotNet.Tools.Publish.Tests "PortableAppWithNative.deps.json" }); + // Prior to `type:platform` trimming, this would have been published. + publishDir.Should().NotHaveFile("System.Linq.dll"); + var runtimesOutput = publishDir.Sub("runtimes"); runtimesOutput.Should().Exist(); @@ -50,5 +53,26 @@ namespace Microsoft.DotNet.Tools.Publish.Tests nativeDir.Should().HaveFile(output.Item2); } } + + [Fact] + public void PortableAppWithIntentionalDowngradePublishesDowngradedManagedCode() + { + var testInstance = TestAssetsManager.CreateTestInstance("PortableTests") + .WithLockFiles(); + + var publishCommand = new PublishCommand(Path.Combine(testInstance.TestRoot, "PortableAppWithIntentionalManagedDowngrade")); + var publishResult = publishCommand.Execute(); + + publishResult.Should().Pass(); + + var publishDir = publishCommand.GetOutputDirectory(portable: true); + publishDir.Should().HaveFiles(new[] + { + "PortableAppWithIntentionalManagedDowngrade.dll", + "PortableAppWithIntentionalManagedDowngrade.deps", + "PortableAppWithIntentionalManagedDowngrade.deps.json", + "System.Linq.dll" + }); + } } }