From 8831bb4a8bd4d2acb5b86500701fedffad63b3c9 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Fri, 1 Apr 2016 21:52:08 -0700 Subject: [PATCH] Do not trim refs --- .../DependencyContextValidator/.noautobuild | 0 .../DependencyContextValidator/Validator.cs | 7 +- .../DependencyContextValidator/project.json | 10 +- .../TestAppFullClr/.noautobuild | 0 .../TestAppFullClr/Program.cs | 16 +++ .../TestAppFullClr/project.json | 14 ++ .../TestAppPortable/.noautobuild | 0 .../TestAppPortable/Program.cs | 16 +++ .../TestAppPortable/project.json | 19 +++ .../TestAppPortableDeps/.noautobuild | 0 .../TestAppPortableDeps/Program.cs | 16 +++ .../TestAppPortableDeps/project.json | 18 +++ .../.noautobuild | 0 .../PortableAppCompilationContext/Program.cs | 12 ++ .../project.json | 21 +++ scripts/dotnet-cli-build/CompileTargets.cs | 43 +++++- scripts/dotnet-cli-build/PrepareTargets.cs | 4 +- scripts/dotnet-cli-build/Utils/FS.cs | 2 +- scripts/dotnet-cli-build/Utils/Utils.cs | 20 ++- .../Compilation/LibraryExporter.cs | 2 +- src/Microsoft.DotNet.ProjectModel/Project.cs | 3 +- .../ProjectContext.cs | 7 +- .../DependencyContextExtensions.cs | 60 ++++++++ .../DependencyContextJsonReader.cs | 3 +- .../commands/dotnet-publish/PublishCommand.cs | 87 +++--------- test/Installer/testmsi.ps1 | 1 + .../TestBase.cs | 8 +- .../FunctionalTests.cs | 133 ++++++++++++++++++ .../project.json | 3 + test/dotnet-compile.Tests/CompilerTests.cs | 20 --- .../PublishPortableTests.cs | 18 +++ 31 files changed, 455 insertions(+), 108 deletions(-) create mode 100644 TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/.noautobuild create mode 100644 TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/.noautobuild create mode 100644 TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/Program.cs create mode 100644 TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/project.json create mode 100644 TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/.noautobuild create mode 100644 TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/Program.cs create mode 100644 TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/project.json create mode 100644 TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/.noautobuild create mode 100644 TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/Program.cs create mode 100644 TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/project.json create mode 100644 TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/.noautobuild create mode 100644 TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/Program.cs create mode 100644 TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/project.json create mode 100644 test/Microsoft.Extensions.DependencyModel.Tests/FunctionalTests.cs diff --git a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/.noautobuild b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/.noautobuild new file mode 100644 index 000000000..e69de29bb diff --git a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/Validator.cs b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/Validator.cs index 4195d928d..b34b4c93b 100644 --- a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/Validator.cs +++ b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/Validator.cs @@ -44,6 +44,7 @@ namespace Microsoft.Extensions.DependencyModel var resolvedPaths = compilationLibrary.ResolveReferencePaths(); foreach (var resolvedPath in resolvedPaths) { + Console.WriteLine($"Compilation {compilationLibrary.Name}:{Path.GetFileName(resolvedPath)}"); if (!File.Exists(resolvedPath)) { Error($"Compilataion library resolved to non existent path {resolvedPath}"); @@ -55,8 +56,10 @@ namespace Microsoft.Extensions.DependencyModel foreach (var runtimeLibrary in context.RuntimeLibraries) { CheckMetadata(runtimeLibrary); - foreach (var assembly in runtimeLibrary.GetDefaultNativeAssets(context)) {} - foreach (var native in runtimeLibrary.GetDefaultAssemblyNames(context)) {} + foreach (var native in runtimeLibrary.GetDefaultNativeAssets(context)) {} + foreach (var assembly in runtimeLibrary.GetDefaultAssemblyNames(context)) { + Console.WriteLine($"Runtime {runtimeLibrary.Name}:{assembly.Name}"); + } } foreach (var native in context.GetDefaultNativeAssets()) {} diff --git a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/project.json b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/project.json index 5501c0f42..c160bb47f 100644 --- a/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/project.json +++ b/TestAssets/TestProjects/DependencyContextValidator/DependencyContextValidator/project.json @@ -1,15 +1,19 @@ { "version": "1.0.0-*", "dependencies": { - "NETStandard.Library": "1.5.0-rc2-23931", "Microsoft.Extensions.DependencyModel": { "target": "project", "version": "1.0.0-*" } }, "frameworks": { - "netstandardapp1.5": { - "imports": "dnxcore50" + "netstandard1.5": { + "imports": "dnxcore50", + "dependencies": { + "NETStandard.Library": "1.5.0-rc2-23931", + } + }, + "net451": { } } } diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/.noautobuild b/TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/.noautobuild new file mode 100644 index 000000000..e69de29bb diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/Program.cs b/TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/Program.cs new file mode 100644 index 000000000..28b8f8435 --- /dev/null +++ b/TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/Program.cs @@ -0,0 +1,16 @@ +// 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.Diagnostics; + +namespace TestApp +{ + public class Program + { + public static void Main(string[] args) + { + Microsoft.Extensions.DependencyModel.DependencyContextValidator.Validate(true); + } + } +} diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/project.json b/TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/project.json new file mode 100644 index 000000000..c5331b23d --- /dev/null +++ b/TestAssets/TestProjects/DependencyContextValidator/TestAppFullClr/project.json @@ -0,0 +1,14 @@ +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + "dependencies": { + "DependencyContextValidator": "1.0.0-*" + }, + "frameworks": { + "net451": { + } + } +} diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/.noautobuild b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/.noautobuild new file mode 100644 index 000000000..e69de29bb diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/Program.cs b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/Program.cs new file mode 100644 index 000000000..28b8f8435 --- /dev/null +++ b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/Program.cs @@ -0,0 +1,16 @@ +// 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.Diagnostics; + +namespace TestApp +{ + public class Program + { + public static void Main(string[] args) + { + Microsoft.Extensions.DependencyModel.DependencyContextValidator.Validate(true); + } + } +} diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/project.json b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/project.json new file mode 100644 index 000000000..f3fc18046 --- /dev/null +++ b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortable/project.json @@ -0,0 +1,19 @@ +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0-rc2-23931" + }, + "DependencyContextValidator": "1.0.0-*" + }, + "frameworks": { + "netstandard1.5": { + "imports" : "dnxcore50" + } + } +} diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/.noautobuild b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/.noautobuild new file mode 100644 index 000000000..e69de29bb diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/Program.cs b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/Program.cs new file mode 100644 index 000000000..28b8f8435 --- /dev/null +++ b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/Program.cs @@ -0,0 +1,16 @@ +// 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.Diagnostics; + +namespace TestApp +{ + public class Program + { + public static void Main(string[] args) + { + Microsoft.Extensions.DependencyModel.DependencyContextValidator.Validate(true); + } + } +} diff --git a/TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/project.json b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/project.json new file mode 100644 index 000000000..2036c6d1a --- /dev/null +++ b/TestAssets/TestProjects/DependencyContextValidator/TestAppPortableDeps/project.json @@ -0,0 +1,18 @@ +{ + "version": "1.0.0-*", + "compilationOptions": { + "emitEntryPoint": true, + }, + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0-rc2-23931" + }, + "DependencyContextValidator": "1.0.0-*" + }, + "frameworks": { + "netstandard1.5": { + "imports" : "dnxcore50" + } + } +} diff --git a/TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/.noautobuild b/TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/.noautobuild new file mode 100644 index 000000000..e69de29bb diff --git a/TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/Program.cs b/TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/Program.cs new file mode 100644 index 000000000..fbe8e9b0e --- /dev/null +++ b/TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/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/PortableAppCompilationContext/project.json b/TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/project.json new file mode 100644 index 000000000..54c2fde9b --- /dev/null +++ b/TestAssets/TestProjects/PortableTests/PortableAppCompilationContext/project.json @@ -0,0 +1,21 @@ +{ + "compilationOptions": { + "emitEntryPoint": true, + "preserveCompilationContext": true + }, + "dependencies": {}, + "frameworks": { + "netstandard1.5": { + "imports": [ + "dnxcore50", + "portable-net45+win8" + ], + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.0-rc2-23931" + } + } + } + } +} diff --git a/scripts/dotnet-cli-build/CompileTargets.cs b/scripts/dotnet-cli-build/CompileTargets.cs index 79fccf0b3..00be6b47a 100644 --- a/scripts/dotnet-cli-build/CompileTargets.cs +++ b/scripts/dotnet-cli-build/CompileTargets.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using Microsoft.DotNet.Cli.Build.Framework; using Microsoft.Extensions.PlatformAbstractions; @@ -9,6 +10,8 @@ using static Microsoft.DotNet.Cli.Build.FS; using static Microsoft.DotNet.Cli.Build.Framework.BuildHelpers; using System.Text.RegularExpressions; using System.Reflection.PortableExecutable; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; namespace Microsoft.DotNet.Cli.Build { @@ -352,6 +355,7 @@ namespace Microsoft.DotNet.Cli.Build // Rename the .deps file var destinationDeps = Path.Combine(SharedFrameworkNameAndVersionRoot, $"{SharedFrameworkName}.deps.json"); File.Move(Path.Combine(SharedFrameworkNameAndVersionRoot, "framework.deps.json"), destinationDeps); + ChangeEntryPointLibraryName(destinationDeps, null); // Generate RID fallback graph string runtimeGraphGeneratorRuntime = null; @@ -445,7 +449,7 @@ namespace Microsoft.DotNet.Cli.Build "--output", outputDir, "--framework", - "netstandard1.5") + "netstandard1.5") .Execute() .EnsureSuccessful(); @@ -468,6 +472,7 @@ namespace Microsoft.DotNet.Cli.Build File.Delete(Path.Combine(binaryToCorehostifyOutDir, $"{binaryToCorehostify}.exe")); File.Copy(compilersDeps, Path.Combine(outputDir, binaryToCorehostify + ".deps.json")); File.Copy(compilersRuntimeConfig, Path.Combine(outputDir, binaryToCorehostify + ".runtimeconfig.json")); + ChangeEntryPointLibraryName(Path.Combine(outputDir, binaryToCorehostify + ".deps.json"), binaryToCorehostify); } catch (Exception ex) { @@ -577,6 +582,42 @@ namespace Microsoft.DotNet.Cli.Build return c.Success(); } + private static void ChangeEntryPointLibraryName(string depsFile, string newName) + { + JToken deps; + using (var file = File.OpenText(depsFile)) + using (JsonTextReader reader = new JsonTextReader(file)) + { + deps = JObject.ReadFrom(reader); + } + + var target = deps["targets"][deps["runtimeTarget"]["name"].Value()]; + var library = target.Children().First(); + var version = library.Name.Substring(library.Name.IndexOf('/') + 1); + if (newName == null) + { + library.Remove(); + } + else + { + library.Replace(new JProperty(newName + '/' + version, library.Value)); + } + library = deps["libraries"].Children().First(); + if (newName == null) + { + library.Remove(); + } + else + { + library.Replace(new JProperty(newName + '/' + version, library.Value)); + } + using (var file = File.CreateText(depsFile)) + using (var writer = new JsonTextWriter(file) { Formatting = Formatting.Indented}) + { + deps.WriteTo(writer); + } + } + private static void DeleteMainPublishOutput(string path, string name) { File.Delete(Path.Combine(path, $"{name}{Constants.ExeSuffix}")); diff --git a/scripts/dotnet-cli-build/PrepareTargets.cs b/scripts/dotnet-cli-build/PrepareTargets.cs index ac44561eb..d029d4f1b 100644 --- a/scripts/dotnet-cli-build/PrepareTargets.cs +++ b/scripts/dotnet-cli-build/PrepareTargets.cs @@ -205,8 +205,8 @@ namespace Microsoft.DotNet.Cli.Build { var dotnet = DotNetCli.Stage0; - dotnet.Restore("--verbosity", "verbose").WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "src")).Execute().EnsureSuccessful(); - dotnet.Restore("--verbosity", "verbose", "--infer-runtimes").WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "tools")).Execute().EnsureSuccessful(); + dotnet.Restore("--verbosity", "verbose", "--disable-parallel", "--infer-runtimes").WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "src")).Execute().EnsureSuccessful(); + dotnet.Restore("--verbosity", "verbose", "--disable-parallel", "--infer-runtimes").WorkingDirectory(Path.Combine(c.BuildContext.BuildDirectory, "tools")).Execute().EnsureSuccessful(); return c.Success(); } diff --git a/scripts/dotnet-cli-build/Utils/FS.cs b/scripts/dotnet-cli-build/Utils/FS.cs index d425c7ce2..80cddcff2 100644 --- a/scripts/dotnet-cli-build/Utils/FS.cs +++ b/scripts/dotnet-cli-build/Utils/FS.cs @@ -111,7 +111,7 @@ namespace Microsoft.DotNet.Cli.Build if (string.Equals(Path.GetFileName(candidate), "bin") || string.Equals(Path.GetFileName(candidate), "obj")) { - Directory.Delete(candidate, recursive: true); + Utils.DeleteDirectory(candidate); } else { diff --git a/scripts/dotnet-cli-build/Utils/Utils.cs b/scripts/dotnet-cli-build/Utils/Utils.cs index bd7e5ad71..799bda56f 100644 --- a/scripts/dotnet-cli-build/Utils/Utils.cs +++ b/scripts/dotnet-cli-build/Utils/Utils.cs @@ -96,8 +96,24 @@ namespace Microsoft.DotNet.Cli.Build File.SetAttributes(file, FileAttributes.Normal); File.Delete(file); } - System.Threading.Thread.Sleep(1); - Directory.Delete(path, true); + var retry = 5; + while (retry >= 0) + { + try + { + Directory.Delete(path, true); + return; + } + catch (IOException ex) + { + if (retry == 0) + { + throw; + } + System.Threading.Thread.Sleep(200); + retry--; + } + } } } diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs index ce43b4b0b..8fc68ba34 100644 --- a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs @@ -424,7 +424,7 @@ namespace Microsoft.DotNet.ProjectModel.Compilation private IEnumerable PopulateAssets(TargetLibraryWithAssets library, IEnumerable section) { - foreach (var assemblyPath in section) + foreach (var assemblyPath in section.Where(a => !PackageDependencyProvider.IsPlaceholderFile(a.Path))) { yield return LibraryAsset.CreateFromRelativePath(library.Path, assemblyPath.Path); } diff --git a/src/Microsoft.DotNet.ProjectModel/Project.cs b/src/Microsoft.DotNet.ProjectModel/Project.cs index 2df027c5b..9e1c26437 100644 --- a/src/Microsoft.DotNet.ProjectModel/Project.cs +++ b/src/Microsoft.DotNet.ProjectModel/Project.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.DotNet.ProjectModel.Files; using Microsoft.DotNet.ProjectModel.Graph; using NuGet.Frameworks; @@ -91,7 +92,7 @@ namespace Microsoft.DotNet.ProjectModel public string RawRuntimeOptions { get; set; } public bool IsTestProject => !string.IsNullOrEmpty(TestRunner); - + public IEnumerable GetTargetFrameworks() { return _targetFrameworks.Values; diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs b/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs index 7fe2e7622..b64c0dcef 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectContext.cs @@ -145,11 +145,8 @@ namespace Microsoft.DotNet.ProjectModel { return this; } - - // Check if there are any runtime targets (i.e. are we portable) - var standalone = LockFile.Targets - .Where(t => t.TargetFramework.Equals(TargetFramework)) - .Any(t => !string.IsNullOrEmpty(t.RuntimeIdentifier)); + + var standalone = !ProjectFile.Dependencies.Any(d => d.Type.Equals(LibraryDependencyType.Platform)); var context = CreateBuilder(ProjectFile.ProjectFilePath, TargetFramework) .WithRuntimeIdentifiers(standalone ? runtimeIdentifiers : Enumerable.Empty()) diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextExtensions.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextExtensions.cs index fd0e7ed39..59f73ea61 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextExtensions.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextExtensions.cs @@ -12,41 +12,101 @@ namespace Microsoft.Extensions.DependencyModel public static IEnumerable GetDefaultNativeAssets(this DependencyContext self) { + if (self == null) + { + throw new ArgumentNullException(nameof(self)); + } return self.RuntimeLibraries.SelectMany(library => library.GetDefaultNativeAssets(self)); } public static IEnumerable GetRuntimeNativeAssets(this DependencyContext self, string runtimeIdentifier) { + if (self == null) + { + throw new ArgumentNullException(nameof(self)); + } + if (runtimeIdentifier == null) + { + throw new ArgumentNullException(nameof(runtimeIdentifier)); + } return self.RuntimeLibraries.SelectMany(library => library.GetRuntimeNativeAssets(self, runtimeIdentifier)); } public static IEnumerable GetDefaultNativeAssets(this RuntimeLibrary self, DependencyContext context) { + if (self == null) + { + throw new ArgumentNullException(nameof(self)); + } return ResolveAssets(context, string.Empty, self.NativeLibraryGroups); } public static IEnumerable GetRuntimeNativeAssets(this RuntimeLibrary self, DependencyContext context, string runtimeIdentifier) { + if (self == null) + { + throw new ArgumentNullException(nameof(self)); + } + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + if (runtimeIdentifier == null) + { + throw new ArgumentNullException(nameof(runtimeIdentifier)); + } return ResolveAssets(context, runtimeIdentifier, self.NativeLibraryGroups); } public static IEnumerable GetDefaultAssemblyNames(this DependencyContext self) { + if (self == null) + { + throw new ArgumentNullException(nameof(self)); + } return self.RuntimeLibraries.SelectMany(library => library.GetDefaultAssemblyNames(self)); } public static IEnumerable GetRuntimeAssemblyNames(this DependencyContext self, string runtimeIdentifier) { + if (self == null) + { + throw new ArgumentNullException(nameof(self)); + } + if (runtimeIdentifier == null) + { + throw new ArgumentNullException(nameof(runtimeIdentifier)); + } return self.RuntimeLibraries.SelectMany(library => library.GetRuntimeAssemblyNames(self, runtimeIdentifier)); } public static IEnumerable GetDefaultAssemblyNames(this RuntimeLibrary self, DependencyContext context) { + if (self == null) + { + throw new ArgumentNullException(nameof(self)); + } + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } return ResolveAssets(context, string.Empty, self.RuntimeAssemblyGroups).Select(GetAssemblyName); } public static IEnumerable GetRuntimeAssemblyNames(this RuntimeLibrary self, DependencyContext context, string runtimeIdentifier) { + if (self == null) + { + throw new ArgumentNullException(nameof(self)); + } + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + if (runtimeIdentifier == null) + { + throw new ArgumentNullException(nameof(runtimeIdentifier)); + } return ResolveAssets(context, runtimeIdentifier, self.RuntimeAssemblyGroups).Select(GetAssemblyName); } diff --git a/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs b/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs index 13bee633d..57a0573a2 100644 --- a/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs +++ b/src/Microsoft.Extensions.DependencyModel/DependencyContextJsonReader.cs @@ -141,7 +141,8 @@ namespace Microsoft.Extensions.DependencyModel } return new CompilationOptions( - compilationOptionsObject[DependencyContextStrings.DefinesPropertyName]?.Values() ?? Enumerable.Empty(), + compilationOptionsObject[DependencyContextStrings.DefinesPropertyName]?.Values().ToArray() ?? Enumerable.Empty(), + // ToArray is here to prevent IEnumerable holding to json object graph compilationOptionsObject[DependencyContextStrings.LanguageVersionPropertyName]?.Value(), compilationOptionsObject[DependencyContextStrings.PlatformPropertyName]?.Value(), compilationOptionsObject[DependencyContextStrings.AllowUnsafePropertyName]?.Value(), diff --git a/src/dotnet/commands/dotnet-publish/PublishCommand.cs b/src/dotnet/commands/dotnet-publish/PublishCommand.cs index abe835442..5c42f70e5 100644 --- a/src/dotnet/commands/dotnet-publish/PublishCommand.cs +++ b/src/dotnet/commands/dotnet-publish/PublishCommand.cs @@ -131,22 +131,26 @@ namespace Microsoft.DotNet.Tools.Publish var isPortable = string.IsNullOrEmpty(context.RuntimeIdentifier); // Collect all exports and organize them - var exports = exporter.GetAllExports() + var packageExports = 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(); + var collectExclusionList = isPortable ? GetExclusionList(context, packageExports) : new HashSet(); - foreach (var export in exporter.GetAllExports().Where(e => !collectExclusionList.Contains(e.Library.Identity.Name))) + var exports = exporter.GetAllExports(); + foreach (var export in exports.Where(e => !collectExclusionList.Contains(e.Library.Identity.Name))) { Reporter.Verbose.WriteLine($"Publishing {export.Library.Identity.ToString().Green().Bold()} ..."); PublishAssetGroups(export.RuntimeAssemblyGroups, outputPath, nativeSubdirectories: false, includeRuntimeGroups: isPortable); PublishAssetGroups(export.NativeLibraryGroups, outputPath, nativeSubdirectories, includeRuntimeGroups: isPortable); export.RuntimeAssets.StructuredCopyTo(outputPath, outputPaths.IntermediateOutputDirectoryPath); + } - if (options.PreserveCompilationContext.GetValueOrDefault()) + if (options.PreserveCompilationContext.GetValueOrDefault()) + { + foreach (var export in exports) { - PublishRefs(export, outputPath); + PublishRefs(export, outputPath, !collectExclusionList.Contains(export.Library.Identity.Name)); } } @@ -248,19 +252,19 @@ namespace Microsoft.DotNet.Tools.Publish } } - private static void PublishRefs(LibraryExport export, string outputPath) + private static void PublishRefs(LibraryExport export, string outputPath, bool deduplicate) { var refsPath = Path.Combine(outputPath, "refs"); if (!Directory.Exists(refsPath)) { Directory.CreateDirectory(refsPath); } - + // Do not copy compilation assembly if it's in runtime assemblies var runtimeAssemblies = new HashSet(export.RuntimeAssemblyGroups.GetDefaultAssets()); foreach (var compilationAssembly in export.CompilationAssemblies) { - if (!runtimeAssemblies.Contains(compilationAssembly)) + if (!deduplicate || !runtimeAssemblies.Contains(compilationAssembly)) { var destFileName = Path.Combine(refsPath, Path.GetFileName(compilationAssembly.ResolvedPath)); File.Copy(compilationAssembly.ResolvedPath, destFileName, overwrite: true); @@ -358,67 +362,16 @@ namespace Microsoft.DotNet.Tools.Publish private IEnumerable SelectContexts(string projectPath, NuGetFramework framework, string runtime) { - var allContexts = ProjectContext.CreateContextForEachTarget(projectPath).ToList(); - var frameworks = framework == null ? - allContexts.Select(c => c.TargetFramework).Distinct().ToArray() : - new[] { framework }; + var allContexts = framework == null ? + ProjectContext.CreateContextForEachFramework(projectPath) : + new[] { ProjectContext.Create(projectPath, framework) }; - if (string.IsNullOrEmpty(runtime)) - { - // For each framework, find the best matching RID item - var candidates = PlatformServices.Default.Runtime.GetAllCandidateRuntimeIdentifiers(); - return frameworks.Select(f => FindBestTarget(f, allContexts, candidates)); - } - else - { - return frameworks.SelectMany(f => allContexts.Where(c => - Equals(c.TargetFramework, f) && - string.Equals(c.RuntimeIdentifier, runtime, StringComparison.Ordinal))); - } + var runtimes = !string.IsNullOrEmpty(runtime) ? + new [] {runtime} : + PlatformServices.Default.Runtime.GetAllCandidateRuntimeIdentifiers(); + return allContexts.Select(c => c.CreateRuntimeContext(runtimes)); } - - private ProjectContext FindBestTarget(NuGetFramework f, List allContexts, IEnumerable candidates) - { - foreach (var candidate in candidates) - { - var target = allContexts.FirstOrDefault(c => - Equals(c.TargetFramework, f) && - string.Equals(c.RuntimeIdentifier, candidate, StringComparison.Ordinal)); - if (target != null) - { - return target; - } - } - - // No RID-specific target found, use the RID-less target and publish portable - return allContexts.FirstOrDefault(c => - Equals(c.TargetFramework, f) && - string.IsNullOrEmpty(c.RuntimeIdentifier)); - } - - /// - /// Return the matching framework/runtime ProjectContext. - /// If 'framework' or 'runtimeIdentifier' is null or empty then it matches with any. - /// - private static IEnumerable GetMatchingProjectContexts(IEnumerable contexts, NuGetFramework framework, string runtimeIdentifier) - { - foreach (var context in contexts) - { - if (context.TargetFramework == null || string.IsNullOrEmpty(context.RuntimeIdentifier)) - { - continue; - } - - if (string.IsNullOrEmpty(runtimeIdentifier) || string.Equals(runtimeIdentifier, context.RuntimeIdentifier, StringComparison.OrdinalIgnoreCase)) - { - if (framework == null || framework.Equals(context.TargetFramework)) - { - yield return context; - } - } - } - } - + private static void CopyContents(ProjectContext context, string outputPath) { var contentFiles = context.ProjectFile.Files.GetContentFiles(); diff --git a/test/Installer/testmsi.ps1 b/test/Installer/testmsi.ps1 index d1b82f67b..f3d12cdbb 100644 --- a/test/Installer/testmsi.ps1 +++ b/test/Installer/testmsi.ps1 @@ -42,6 +42,7 @@ pushd "$Stage2Dir" try { .\dotnet restore ` + --infer-runtimes ` $testDir ` -f https://www.myget.org/F/dotnet-buildtools/api/v3/index.json | Out-Host diff --git a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs index 0d5f4a0a0..a72b7b18f 100644 --- a/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs +++ b/test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs @@ -103,7 +103,7 @@ namespace Microsoft.DotNet.Tools.Test.Utilities string.Equals("on", val, StringComparison.OrdinalIgnoreCase)); } - protected void TestExecutable(string outputDir, + protected CommandResult TestExecutable(string outputDir, string executableName, string expectedOutput) { @@ -123,9 +123,13 @@ namespace Microsoft.DotNet.Tools.Test.Utilities var result = executableCommand.ExecuteWithCapturedOutput(string.Join(" ", args)); - result.Should().HaveStdOut(expectedOutput); + if (!string.IsNullOrEmpty(expectedOutput)) + { + result.Should().HaveStdOut(expectedOutput); + } result.Should().NotHaveStdErr(); result.Should().Pass(); + return result; } protected void TestOutputExecutable( diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/FunctionalTests.cs b/test/Microsoft.Extensions.DependencyModel.Tests/FunctionalTests.cs new file mode 100644 index 000000000..e051f178a --- /dev/null +++ b/test/Microsoft.Extensions.DependencyModel.Tests/FunctionalTests.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.TestFramework; +using Microsoft.DotNet.Tools.Test.Utilities; +using FluentAssertions; +using Xunit; + +namespace Microsoft.Extensions.DependencyModel +{ + public class FunctionalTests : TestBase + { + private readonly string _testProjectsRoot; + + public FunctionalTests() + { + _testProjectsRoot = Path.Combine(AppContext.BaseDirectory, "TestAssets", "TestProjects"); + } + + [Theory] + [InlineData("TestApp", true)] + [InlineData("TestAppPortable", true)] + [InlineData("TestAppDeps", false)] + [InlineData("TestAppPortableDeps", false)] + public void RunTest(string appname, bool checkCompilation) + { + var testProjectPath = Path.Combine(RepoRoot, "TestAssets", "TestProjects", "DependencyContextValidator", appname); + var testProject = Path.Combine(testProjectPath, "project.json"); + + var runCommand = new RunCommand(testProject); + var result = runCommand.ExecuteWithCapturedOutput(); + result.Should().Pass(); + ValidateRuntimeLibrarites(result, appname); + if (checkCompilation) + { + ValidateCompilationLibraries(result, appname); + } + } + + [Theory] + [InlineData("TestApp", false, true)] + [InlineData("TestAppPortable", true, true)] + [InlineData("TestAppDeps", false, false)] + [InlineData("TestAppPortableDeps", true, false)] + public void PublishTest(string appname, bool portable, bool checkCompilation) + { + var testProjectPath = Path.Combine(RepoRoot, "TestAssets", "TestProjects", "DependencyContextValidator", appname); + var testProject = Path.Combine(testProjectPath, "project.json"); + + var publishCommand = new PublishCommand(testProject); + publishCommand.Execute().Should().Pass(); + + var exeName = portable ? publishCommand.GetPortableOutputName() : publishCommand.GetOutputExecutable(); + + var result = TestExecutable(publishCommand.GetOutputDirectory(portable).FullName, exeName, string.Empty); + ValidateRuntimeLibrarites(result, appname); + if (checkCompilation) + { + ValidateCompilationLibraries(result, appname); + } + } + + [WindowsOnlyFact] + public void RunTestFullClr() + { + var testProjectPath = Path.Combine(RepoRoot, "TestAssets", "TestProjects", "DependencyContextValidator", "TestAppFullClr"); + var testProject = Path.Combine(testProjectPath, "project.json"); + + var runCommand = new RunCommand(testProject); + var result = runCommand.ExecuteWithCapturedOutput(); + result.Should().Pass(); + ValidateRuntimeLibraritesFullClr(result, "TestAppFullClr"); + ValidateCompilationLibrariesFullClr(result, "TestAppFullClr"); + } + + [WindowsOnlyFact] + public void PublishTestFullClr() + { + var testProjectPath = Path.Combine(RepoRoot, "TestAssets", "TestProjects", "DependencyContextValidator", "TestAppFullClr"); + var testProject = Path.Combine(testProjectPath, "project.json"); + + var publishCommand = new PublishCommand(testProject); + publishCommand.Execute().Should().Pass(); + + var result = TestExecutable(publishCommand.GetOutputDirectory().FullName, publishCommand.GetOutputExecutable(), string.Empty); + ValidateRuntimeLibraritesFullClr(result, "TestAppFullClr"); + ValidateCompilationLibrariesFullClr(result, "TestAppFullClr"); + } + + private void ValidateRuntimeLibraritesFullClr(CommandResult result, string appname) + { + // entry assembly + result.Should().HaveStdOutContaining($"Runtime {appname}:{appname}"); + // project dependency + result.Should().HaveStdOutContaining("Runtime DependencyContextValidator:DependencyContextValidator"); + } + + private void ValidateCompilationLibrariesFullClr(CommandResult result, string appname) + { + // entry assembly + result.Should().HaveStdOutContaining($"Compilation {appname}:{appname}.exe"); + // project dependency + result.Should().HaveStdOutContaining("Compilation DependencyContextValidator:DependencyContextValidator.dll"); + // system assembly + result.Should().HaveStdOutContaining("Compilation mscorlib:mscorlib.dll"); + } + + + private void ValidateRuntimeLibrarites(CommandResult result, string appname) + { + // entry assembly + result.Should().HaveStdOutContaining($"Runtime {appname}:{appname}"); + // project dependency + result.Should().HaveStdOutContaining("Runtime DependencyContextValidator:DependencyContextValidator"); + // system assembly + result.Should().HaveStdOutContaining("Runtime System.Linq:System.Linq"); + } + + private void ValidateCompilationLibraries(CommandResult result, string appname) + { + // entry assembly + result.Should().HaveStdOutContaining($"Compilation {appname}:{appname}.dll"); + // project dependency + result.Should().HaveStdOutContaining("Compilation DependencyContextValidator:DependencyContextValidator.dll"); + // system assembly + result.Should().HaveStdOutContaining("Compilation System.Linq:System.Linq.dll"); + } + + } +} diff --git a/test/Microsoft.Extensions.DependencyModel.Tests/project.json b/test/Microsoft.Extensions.DependencyModel.Tests/project.json index 82a324b4f..b1ca04ba3 100644 --- a/test/Microsoft.Extensions.DependencyModel.Tests/project.json +++ b/test/Microsoft.Extensions.DependencyModel.Tests/project.json @@ -9,6 +9,9 @@ "Microsoft.DotNet.Tools.Tests.Utilities": { "target": "project" }, + "Microsoft.DotNet.Cli.Utils": { + "target": "project" + }, "FluentAssertions": "4.0.0", "moq.netcore": "4.4.0-beta8", "xunit": "2.1.0", diff --git a/test/dotnet-compile.Tests/CompilerTests.cs b/test/dotnet-compile.Tests/CompilerTests.cs index 3151c0230..efbb5abf9 100644 --- a/test/dotnet-compile.Tests/CompilerTests.cs +++ b/test/dotnet-compile.Tests/CompilerTests.cs @@ -147,26 +147,6 @@ namespace Microsoft.DotNet.Tools.Compiler.Tests result.StdOut.Should().Contain("MyNamespace.Util"); } - [Fact] - public void EmbeddedDependencyContextIsValidOnBuild() - { - var testProjectPath = Path.Combine(RepoRoot, "TestAssets", "TestProjects", "DependencyContextValidator", "TestApp"); - var testProject = Path.Combine(testProjectPath, "project.json"); - - var runCommand = new RunCommand(testProject); - runCommand.Execute().Should().Pass(); - } - - [Fact] - public void DepsDependencyContextIsValidOnBuild() - { - var testProjectPath = Path.Combine(RepoRoot, "TestAssets", "TestProjects", "DependencyContextValidator", "TestAppDeps"); - var testProject = Path.Combine(testProjectPath, "project.json"); - - var runCommand = new RunCommand(testProject); - runCommand.Execute().Should().Pass(); - } - [Fact] public void CanSetOutputAssemblyNameForLibraries() { diff --git a/test/dotnet-publish.Tests/PublishPortableTests.cs b/test/dotnet-publish.Tests/PublishPortableTests.cs index 8a551e6ca..9283b85dd 100644 --- a/test/dotnet-publish.Tests/PublishPortableTests.cs +++ b/test/dotnet-publish.Tests/PublishPortableTests.cs @@ -81,6 +81,24 @@ namespace Microsoft.DotNet.Tools.Publish.Tests publishDir.Should().NotHaveFile("PortableAppWithNative.runtimeconfig.dev.json"); } + [Fact] + public void RefsPublishTest() + { + TestInstance instance = TestAssetsManager.CreateTestInstance("PortableTests") + .WithLockFiles(); + + var publishCommand = new PublishCommand(Path.Combine(instance.TestRoot, "PortableAppCompilationContext")); + publishCommand.Execute().Should().Pass(); + + publishCommand.GetOutputDirectory(true).Should().HaveFile("PortableAppCompilationContext.dll"); + + var refsDirectory = new DirectoryInfo(Path.Combine(publishCommand.GetOutputDirectory(true).FullName, "refs")); + // Should have compilation time assemblies + refsDirectory.Should().HaveFile("System.IO.dll"); + // Libraries in which lib==ref should be deduped + refsDirectory.Should().NotHaveFile("PortableAppCompilationContext.dll"); + } + private DirectoryInfo Publish(TestInstance testInstance) { var publishCommand = new PublishCommand(Path.Combine(testInstance.TestRoot, "PortableAppWithNative"));