diff --git a/packaging/windows/dotnet.wxs b/packaging/windows/dotnet.wxs index 068f5ee92..baaa765f8 100644 --- a/packaging/windows/dotnet.wxs +++ b/packaging/windows/dotnet.wxs @@ -2,7 +2,7 @@ - + diff --git a/src/Microsoft.DotNet.Cli/Program.cs b/src/Microsoft.DotNet.Cli/Program.cs index 5a3193fb2..a1e41d92d 100644 --- a/src/Microsoft.DotNet.Cli/Program.cs +++ b/src/Microsoft.DotNet.Cli/Program.cs @@ -27,7 +27,7 @@ Common Options (passed before the command): Common Commands: new Initialize a basic .NET project restore Restore dependencies specified in the .NET project - compile Compiles a .NET project + build Builds a .NET project publish Publishes a .NET project for deployment (including the runtime) run Compiles and immediately executes a .NET project repl Launch an interactive session (read, eval, print, loop) diff --git a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs index 48d08e4e5..363d6960f 100644 --- a/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs +++ b/src/Microsoft.DotNet.ProjectModel/Compilation/LibraryExporter.cs @@ -288,11 +288,6 @@ namespace Microsoft.DotNet.ProjectModel.Compilation { foreach (var assemblyPath in section) { - if (IsPlaceholderFile(assemblyPath)) - { - continue; - } - assets.Add(new LibraryAsset( Path.GetFileNameWithoutExtension(assemblyPath), assemblyPath, @@ -300,11 +295,6 @@ namespace Microsoft.DotNet.ProjectModel.Compilation } } - private static bool IsPlaceholderFile(string path) - { - return string.Equals(Path.GetFileName(path), "_._", StringComparison.Ordinal); - } - private static bool LibraryIsOfType(LibraryType type, LibraryDescription library) { return type.Equals(LibraryType.Unspecified) || // No type filter was requested diff --git a/src/Microsoft.DotNet.ProjectModel/Graph/LibraryDependencyType.cs b/src/Microsoft.DotNet.ProjectModel/Graph/LibraryDependencyType.cs index d21019dc1..912cd5a51 100644 --- a/src/Microsoft.DotNet.ProjectModel/Graph/LibraryDependencyType.cs +++ b/src/Microsoft.DotNet.ProjectModel/Graph/LibraryDependencyType.cs @@ -7,13 +7,15 @@ namespace Microsoft.DotNet.ProjectModel.Graph { public struct LibraryDependencyType { - private readonly LibraryDependencyTypeFlag _flags; - public static LibraryDependencyType Default = LibraryDependencyType.Parse("default"); + public static LibraryDependencyType Build = LibraryDependencyType.Parse("build"); + + public LibraryDependencyTypeFlag Flags { get; private set; } + private LibraryDependencyType(LibraryDependencyTypeFlag flags) { - _flags = flags; + Flags = flags; } public static LibraryDependencyType Parse(string keyword) @@ -41,7 +43,7 @@ namespace Microsoft.DotNet.ProjectModel.Graph public bool HasFlag(LibraryDependencyTypeFlag flag) { - return (_flags & flag) != 0; + return (Flags & flag) != 0; } } } diff --git a/src/Microsoft.DotNet.ProjectModel/LibraryDescription.cs b/src/Microsoft.DotNet.ProjectModel/LibraryDescription.cs index 171cf38eb..789665c53 100644 --- a/src/Microsoft.DotNet.ProjectModel/LibraryDescription.cs +++ b/src/Microsoft.DotNet.ProjectModel/LibraryDescription.cs @@ -46,7 +46,7 @@ namespace Microsoft.DotNet.ProjectModel public override string ToString() { - return $"{Identity} = {Path}"; + return $"{Identity} ({Identity.Type}) = {Path}"; } // For diagnostics, we don't want to duplicate requested dependencies so we diff --git a/src/Microsoft.DotNet.ProjectModel/ProjectContextBuilder.cs b/src/Microsoft.DotNet.ProjectModel/ProjectContextBuilder.cs index c8cb78683..0484eb5c4 100644 --- a/src/Microsoft.DotNet.ProjectModel/ProjectContextBuilder.cs +++ b/src/Microsoft.DotNet.ProjectModel/ProjectContextBuilder.cs @@ -154,6 +154,7 @@ namespace Microsoft.DotNet.ProjectModel RootDirectory = GlobalSettings?.DirectoryPath ?? RootDirectory; PackagesDirectory = PackagesDirectory ?? PackageDependencyProvider.ResolvePackagesPath(RootDirectory, GlobalSettings); ReferenceAssembliesPath = ReferenceAssembliesPath ?? FrameworkReferenceResolver.GetDefaultReferenceAssembliesPath(); + var frameworkReferenceResolver = new FrameworkReferenceResolver(ReferenceAssembliesPath); LockFileLookup lockFileLookup = null; @@ -185,12 +186,11 @@ namespace Microsoft.DotNet.ProjectModel target = SelectTarget(LockFile); if (target != null) { - var packageResolver = new PackageDependencyProvider(PackagesDirectory); + var packageResolver = new PackageDependencyProvider(PackagesDirectory, frameworkReferenceResolver); ScanLibraries(target, lockFileLookup, libraries, packageResolver, projectResolver); } } - var frameworkReferenceResolver = new FrameworkReferenceResolver(ReferenceAssembliesPath); var referenceAssemblyDependencyResolver = new ReferenceAssemblyDependencyResolver(frameworkReferenceResolver); bool requiresFrameworkAssemblies; @@ -269,8 +269,10 @@ namespace Microsoft.DotNet.ProjectModel { requiresFrameworkAssemblies = false; - foreach (var library in libraries.Values.ToList()) + foreach (var pair in libraries.ToList()) { + var library = pair.Value; + if (Equals(library.Identity.Type, LibraryType.Package) && !Directory.Exists(library.Path)) { @@ -278,6 +280,27 @@ namespace Microsoft.DotNet.ProjectModel library.Resolved = false; } + // The System.* packages provide placeholders on any non netstandard platform + // To make them work seamlessly on those platforms, we fill the gap with a reference + // assembly (if available) + var package = library as PackageDescription; + if (package != null && package.Resolved && !package.Target.CompileTimeAssemblies.Any()) + { + var replacement = referenceAssemblyDependencyResolver.GetDescription(new LibraryRange(library.Identity.Name, LibraryType.ReferenceAssembly), TargetFramework); + if (replacement?.Resolved == true) + { + requiresFrameworkAssemblies = true; + + // Remove the original package reference + libraries.Remove(pair.Key); + + // Add the reference to the refernce assembly. + libraries[new LibraryKey(replacement.Identity.Name)] = replacement; + + continue; + } + } + library.Framework = library.Framework ?? TargetFramework; foreach (var dependency in library.Dependencies) { @@ -335,7 +358,7 @@ namespace Microsoft.DotNet.ProjectModel if (packageEntry != null) { - description = packageResolver.GetDescription(packageEntry, library); + description = packageResolver.GetDescription(TargetFramework, packageEntry, library); } type = LibraryType.Package; diff --git a/src/Microsoft.DotNet.ProjectModel/Resolution/PackageDependencyProvider.cs b/src/Microsoft.DotNet.ProjectModel/Resolution/PackageDependencyProvider.cs index 36b5dc38b..d22dfe87b 100644 --- a/src/Microsoft.DotNet.ProjectModel/Resolution/PackageDependencyProvider.cs +++ b/src/Microsoft.DotNet.ProjectModel/Resolution/PackageDependencyProvider.cs @@ -5,7 +5,10 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection.Metadata; +using System.Reflection.PortableExecutable; using Microsoft.DotNet.ProjectModel.Graph; +using NuGet.Frameworks; using NuGet.Packaging; namespace Microsoft.DotNet.ProjectModel.Resolution @@ -13,13 +16,15 @@ namespace Microsoft.DotNet.ProjectModel.Resolution public class PackageDependencyProvider { private readonly VersionFolderPathResolver _packagePathResolver; + private readonly FrameworkReferenceResolver _frameworkReferenceResolver; - public PackageDependencyProvider(string packagesPath) + public PackageDependencyProvider(string packagesPath, FrameworkReferenceResolver frameworkReferenceResolver) { _packagePathResolver = new VersionFolderPathResolver(packagesPath); + _frameworkReferenceResolver = frameworkReferenceResolver; } - public PackageDescription GetDescription(LockFilePackageLibrary package, LockFileTargetLibrary targetLibrary) + public PackageDescription GetDescription(NuGetFramework targetFramework, LockFilePackageLibrary package, LockFileTargetLibrary targetLibrary) { // If a NuGet dependency is supposed to provide assemblies but there is no assembly compatible with // current target framework, we should mark this dependency as unresolved @@ -37,6 +42,14 @@ namespace Microsoft.DotNet.ProjectModel.Resolution var path = _packagePathResolver.GetInstallPath(package.Name, package.Version); + // Remove place holders + targetLibrary.CompileTimeAssemblies = targetLibrary.CompileTimeAssemblies.Where(item => !IsPlaceholderFile(item.Path)).ToList(); + targetLibrary.RuntimeAssemblies = targetLibrary.RuntimeAssemblies.Where(item => !IsPlaceholderFile(item.Path)).ToList(); + + // If the package's compile time assemblies is for a portable profile then, read the assembly metadata + // and turn System.* references into reference assembly dependencies + PopulateLegacyPortableDependencies(targetFramework, dependencies, path, targetLibrary); + var packageDescription = new PackageDescription( path, package, @@ -47,6 +60,64 @@ namespace Microsoft.DotNet.ProjectModel.Resolution return packageDescription; } + private void PopulateLegacyPortableDependencies(NuGetFramework targetFramework, List dependencies, string packagePath, LockFileTargetLibrary targetLibrary) + { + var seen = new HashSet(); + + foreach (var assembly in targetLibrary.CompileTimeAssemblies) + { + // (ref/lib)/{tfm}/{assembly} + var pathParts = assembly.Path.Split('/'); + + if (pathParts.Length != 3) + { + continue; + } + + var assemblyTargetFramework = NuGetFramework.Parse(pathParts[1]); + + if (!assemblyTargetFramework.IsPCL) + { + continue; + } + + var assemblyPath = Path.Combine(packagePath, assembly.Path); + + foreach (var dependency in GetDependencies(assemblyPath)) + { + if (seen.Add(dependency)) + { + string path; + Version version; + + // If there exists a reference assembly on the requested framework with the same name then turn this into a + // framework assembly dependency + if (_frameworkReferenceResolver.TryGetAssembly(dependency, targetFramework, out path, out version)) + { + dependencies.Add(new LibraryRange(dependency, + LibraryType.ReferenceAssembly, + LibraryDependencyType.Build)); + } + } + } + } + } + + private static IEnumerable GetDependencies(string path) + { + using (var peReader = new PEReader(File.OpenRead(path))) + { + var metadataReader = peReader.GetMetadataReader(); + + foreach (var assemblyReferenceHandle in metadataReader.AssemblyReferences) + { + var assemblyReference = metadataReader.GetAssemblyReference(assemblyReferenceHandle); + + yield return metadataReader.GetString(assemblyReference.Name); + } + } + } + private void PopulateDependencies(List dependencies, LockFileTargetLibrary targetLibrary) { foreach (var dependency in targetLibrary.Dependencies) @@ -67,6 +138,11 @@ namespace Microsoft.DotNet.ProjectModel.Resolution } } + public static bool IsPlaceholderFile(string path) + { + return string.Equals(Path.GetFileName(path), "_._", StringComparison.Ordinal); + } + public static string ResolvePackagesPath(string rootDirectory, GlobalSettings settings) { // Order diff --git a/src/Microsoft.DotNet.ProjectModel/Resolution/ProjectDependencyProvider.cs b/src/Microsoft.DotNet.ProjectModel/Resolution/ProjectDependencyProvider.cs index 9972aa448..e5c40d791 100644 --- a/src/Microsoft.DotNet.ProjectModel/Resolution/ProjectDependencyProvider.cs +++ b/src/Microsoft.DotNet.ProjectModel/Resolution/ProjectDependencyProvider.cs @@ -48,17 +48,17 @@ namespace Microsoft.DotNet.ProjectModel.Resolution if (targetFramework != null && targetFramework.IsDesktop()) { - targetFrameworkDependencies.Add(new LibraryRange("mscorlib", LibraryType.ReferenceAssembly)); + targetFrameworkDependencies.Add(new LibraryRange("mscorlib", LibraryType.ReferenceAssembly, LibraryDependencyType.Build)); - targetFrameworkDependencies.Add(new LibraryRange("System", LibraryType.ReferenceAssembly)); + targetFrameworkDependencies.Add(new LibraryRange("System", LibraryType.ReferenceAssembly, LibraryDependencyType.Build)); if (targetFramework.Version >= new Version(3, 5)) { - targetFrameworkDependencies.Add(new LibraryRange("System.Core", LibraryType.ReferenceAssembly)); + targetFrameworkDependencies.Add(new LibraryRange("System.Core", LibraryType.ReferenceAssembly, LibraryDependencyType.Build)); if (targetFramework.Version >= new Version(4, 0)) { - targetFrameworkDependencies.Add(new LibraryRange("Microsoft.CSharp", LibraryType.ReferenceAssembly)); + targetFrameworkDependencies.Add(new LibraryRange("Microsoft.CSharp", LibraryType.ReferenceAssembly, LibraryDependencyType.Build)); } } } diff --git a/src/Microsoft.DotNet.ProjectModel/project.json b/src/Microsoft.DotNet.ProjectModel/project.json index 87debcb06..4b173febd 100644 --- a/src/Microsoft.DotNet.ProjectModel/project.json +++ b/src/Microsoft.DotNet.ProjectModel/project.json @@ -6,12 +6,13 @@ "description": "Types to model a .NET Project", "dependencies": { "NETStandard.Library": "1.0.0-rc2-23704", + "System.Reflection.Metadata": "1.2.0-rc2-23608", "System.Runtime.Loader": "4.0.0-rc2-23704", "System.Dynamic.Runtime": "4.0.11-rc2-23704", "System.Security.Cryptography.Algorithms": "4.0.0-rc2-23704", "Microsoft.CSharp": "4.0.1-rc2-23704", "System.Xml.XDocument": "4.0.11-rc2-23704", - "NuGet.Packaging": "3.4.0-beta-*", + "NuGet.Packaging": "3.4.0-beta-531", "Microsoft.Extensions.FileSystemGlobbing": "1.0.0-rc2-15975", "Microsoft.Extensions.JsonParser.Sources": { diff --git a/src/Microsoft.DotNet.Tools.Publish/PublishCommand.cs b/src/Microsoft.DotNet.Tools.Publish/PublishCommand.cs index 00e72a2ae..ee3584dc4 100644 --- a/src/Microsoft.DotNet.Tools.Publish/PublishCommand.cs +++ b/src/Microsoft.DotNet.Tools.Publish/PublishCommand.cs @@ -89,7 +89,8 @@ namespace Microsoft.DotNet.Tools.Publish { { "publish:ProjectPath", context.ProjectDirectory }, { "publish:Configuration", configuration }, - { "publish:OutputPath", outputPathCalculator.BaseCompilationOutputPath }, + { "publish:OutputPath", outputPath }, + { "publish:PublishOutputPath", outputPathCalculator.BaseCompilationOutputPath }, { "publish:Framework", context.TargetFramework.Framework }, { "publish:Runtime", context.RuntimeIdentifier }, }; diff --git a/src/dotnet-restore/project.json b/src/dotnet-restore/project.json index 667208dba..278073083 100644 --- a/src/dotnet-restore/project.json +++ b/src/dotnet-restore/project.json @@ -11,7 +11,7 @@ "emitEntryPoint": true }, "dependencies": { - "NuGet.CommandLine.XPlat": "3.4.0-beta-517", + "NuGet.CommandLine.XPlat": "3.4.0-beta-531", "Microsoft.NETCore.Platforms": "1.0.1-rc2-23704", "Microsoft.NETCore.TestHost": "1.0.0-rc2-23704", "NETStandard.Library": "1.0.0-rc2-23704", diff --git a/test/Microsoft.DotNet.Tools.Publish.Tests/Microsoft.DotNet.Tools.Publish.Tests.cs b/test/Microsoft.DotNet.Tools.Publish.Tests/Microsoft.DotNet.Tools.Publish.Tests.cs index 44bd9e5c5..57e039e50 100644 --- a/test/Microsoft.DotNet.Tools.Publish.Tests/Microsoft.DotNet.Tools.Publish.Tests.cs +++ b/test/Microsoft.DotNet.Tools.Publish.Tests/Microsoft.DotNet.Tools.Publish.Tests.cs @@ -138,6 +138,7 @@ namespace Microsoft.DotNet.Tools.Publish.Tests { // create unique directories in the 'temp' folder var root = Temp.CreateDirectory(); + root.CopyFile(Path.Combine(_testProjectsRoot, "global.json")); var testLibDir = root.CreateDirectory("TestLibraryWithRunner"); //copy projects to the temp dir @@ -146,7 +147,7 @@ namespace Microsoft.DotNet.Tools.Publish.Tests RunRestore(testLibDir.Path); var testProject = GetProjectPath(testLibDir); - var publishCommand = new PublishCommand(testProject); + var publishCommand = new PublishCommand(testProject, "net451"); publishCommand.Execute().Should().Pass(); publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryWithRunner.dll"); @@ -155,6 +156,17 @@ namespace Microsoft.DotNet.Tools.Publish.Tests publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryWithRunner.dll.config"); // dependencies should also be copied publishCommand.GetOutputDirectory().Should().HaveFile("Newtonsoft.Json.dll"); + publishCommand.GetOutputDirectory().Delete(true); + + publishCommand = new PublishCommand(testProject, "dnxcore50", PlatformServices.Default.Runtime.GetLegacyRestoreRuntimeIdentifier()); + publishCommand.Execute().Should().Pass(); + + publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryWithRunner.dll"); + publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryWithRunner.pdb"); + publishCommand.GetOutputDirectory().Should().HaveFile("TestLibraryWithRunner.deps"); + publishCommand.GetOutputDirectory().Should().NotHaveFile("TestLibraryWithRunner.dll.config"); + // dependencies should also be copied + publishCommand.GetOutputDirectory().Should().HaveFile("Newtonsoft.Json.dll"); } [Fact] diff --git a/test/TestProjects/TestLibraryWithRunner/NuGet.Config b/test/TestProjects/TestLibraryWithRunner/NuGet.Config index 95143bd09..d35fb92ab 100644 --- a/test/TestProjects/TestLibraryWithRunner/NuGet.Config +++ b/test/TestProjects/TestLibraryWithRunner/NuGet.Config @@ -3,6 +3,18 @@ + + + + + + + + + + + + diff --git a/test/TestProjects/TestLibraryWithRunner/project.json b/test/TestProjects/TestLibraryWithRunner/project.json index 965bc6237..e25684d3a 100644 --- a/test/TestProjects/TestLibraryWithRunner/project.json +++ b/test/TestProjects/TestLibraryWithRunner/project.json @@ -2,12 +2,17 @@ "version": "1.0.0-*", "testRunner": "xunit", "dependencies": { - "Microsoft.AspNet.Mvc.Formatters.Json": "6.0.0-rc1-final", - "Newtonsoft.Json": "3.5.8", - "System.Runtime": "4.0.0" + "Microsoft.Extensions.DependencyModel": "1.0.0-*", + "Newtonsoft.Json": "6.0.0", }, "frameworks": { - "dnx451": { } + "net451": { }, + "dnxcore50": { + "imports" : "portable-net45+wp80+win8", + "dependencies": { + "NETStandard.Library": "1.0.0-rc2-23704" + } + } } }