product changes to support portable tools

This commit is contained in:
Bryan Thornbury 2016-03-17 11:45:13 -07:00
parent b59c4333ea
commit f0c2cb2c18
13 changed files with 363 additions and 78 deletions

View file

@ -2,7 +2,9 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Compilation;
namespace Microsoft.DotNet.Cli.Utils
{
@ -15,7 +17,9 @@ namespace Microsoft.DotNet.Cli.Utils
IEnumerable<string> allowedExtensions,
string nugetPackagesRoot,
CommandResolutionStrategy commandResolutionStrategy,
string depsFilePath);
string depsFilePath,
LibraryExporter exporter = null,
bool generateRuntimeConfig = false);
}
}

View file

@ -5,6 +5,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.Extensions.PlatformAbstractions;
using NuGet.Frameworks;
using NuGet.Packaging;
@ -20,7 +21,9 @@ namespace Microsoft.DotNet.Cli.Utils
IEnumerable<string> allowedExtensions,
string nugetPackagesRoot,
CommandResolutionStrategy commandResolutionStrategy,
string depsFilePath)
string depsFilePath,
LibraryExporter exporter = null,
bool generateRuntimeConfig = false)
{
var packageDirectory = GetPackageDirectoryFullPath(library, nugetPackagesRoot);
@ -38,11 +41,15 @@ namespace Microsoft.DotNet.Cli.Utils
var commandPath = Path.Combine(packageDirectory, commandFile);
var isPortable = DetermineIfPortableApp(commandPath);
return CreateCommandSpecWrappingWithCorehostfDll(
commandPath,
commandArguments,
depsFilePath,
commandResolutionStrategy);
commandResolutionStrategy,
nugetPackagesRoot,
isPortable);
}
private string GetPackageDirectoryFullPath(LockFilePackageLibrary library, string nugetPackagesRoot)
@ -69,7 +76,9 @@ namespace Microsoft.DotNet.Cli.Utils
string commandPath,
IEnumerable<string> commandArguments,
string depsFilePath,
CommandResolutionStrategy commandResolutionStrategy)
CommandResolutionStrategy commandResolutionStrategy,
string nugetPackagesRoot,
bool isPortable)
{
var commandExtension = Path.GetExtension(commandPath);
@ -79,7 +88,9 @@ namespace Microsoft.DotNet.Cli.Utils
commandPath,
commandArguments,
depsFilePath,
commandResolutionStrategy);
commandResolutionStrategy,
nugetPackagesRoot,
isPortable);
}
return CreateCommandSpec(commandPath, commandArguments, commandResolutionStrategy);
@ -89,11 +100,30 @@ namespace Microsoft.DotNet.Cli.Utils
string commandPath,
IEnumerable<string> commandArguments,
string depsFilePath,
CommandResolutionStrategy commandResolutionStrategy)
CommandResolutionStrategy commandResolutionStrategy,
string nugetPackagesRoot,
bool isPortable)
{
var corehost = CoreHost.HostExePath;
string host = string.Empty;
var arguments = new List<string>();
if (isPortable)
{
var muxer = new Muxer();
host = muxer.MuxerPath;
if (host == null)
{
throw new Exception("Unable to locate dotnet multiplexer");
}
arguments.Add("exec");
}
else
{
host = CoreHost.LocalHostExePath;
}
arguments.Add(commandPath);
if (depsFilePath != null)
@ -102,9 +132,12 @@ namespace Microsoft.DotNet.Cli.Utils
arguments.Add(depsFilePath);
}
arguments.Add("--additionalprobingpath");
arguments.Add(nugetPackagesRoot);
arguments.AddRange(commandArguments);
return CreateCommandSpec(corehost, arguments, commandResolutionStrategy);
return CreateCommandSpec(host, arguments, commandResolutionStrategy);
}
private CommandSpec CreateCommandSpec(
@ -115,6 +148,16 @@ namespace Microsoft.DotNet.Cli.Utils
var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(commandArguments);
return new CommandSpec(commandPath, escapedArgs, commandResolutionStrategy);
}
}
private bool DetermineIfPortableApp(string commandPath)
{
var commandDir = Path.GetDirectoryName(commandPath);
var runtimeConfig = Directory.EnumerateFiles(commandDir)
.FirstOrDefault(x => x.EndsWith("runtimeconfig.json"));
return runtimeConfig != null;
}
}
}

View file

@ -5,18 +5,28 @@ using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.Extensions.DependencyModel;
using Microsoft.Extensions.PlatformAbstractions;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.ProjectModel;
using LockFile = Microsoft.DotNet.ProjectModel.Graph.LockFile;
using FileFormatException = Microsoft.DotNet.ProjectModel.FileFormatException;
namespace Microsoft.DotNet.Cli.Utils
{
public class ProjectToolsCommandResolver : ICommandResolver
{
private static readonly NuGetFramework s_toolPackageFramework = FrameworkConstants.CommonFrameworks.NetStandardApp15;
private static readonly CommandResolutionStrategy s_commandResolutionStrategy =
CommandResolutionStrategy.ProjectToolsPackage;
private static readonly string s_currentRuntimeIdentifier = PlatformServices.Default.Runtime.GetLegacyRestoreRuntimeIdentifier();
private List<string> _allowedCommandExtensions;
private IPackagedCommandSpecFactory _packagedCommandSpecFactory;
@ -90,23 +100,21 @@ namespace Microsoft.DotNet.Cli.Utils
IEnumerable<string> args,
ProjectContext projectContext)
{
//todo: change this for new resolution strategy
var lockFilePath = Path.Combine(
projectContext.ProjectDirectory,
"artifacts", "Tools", toolLibrary.Name,
"project.lock.json");
if (!File.Exists(lockFilePath))
{
return null;
}
var lockFile = LockFileReader.Read(lockFilePath);
var lockFilePackageLibrary = lockFile.PackageLibraries.FirstOrDefault(l => l.Name == toolLibrary.Name);
var nugetPackagesRoot = projectContext.PackagesDirectory;
var lockFile = GetToolLockFile(toolLibrary, nugetPackagesRoot);
var lockFilePackageLibrary = lockFile.PackageLibraries.FirstOrDefault(l => l.Name == toolLibrary.Name);
var depsFileRoot = Path.GetDirectoryName(lockFile.LockFilePath);
var depsFilePath = GetToolDepsFilePath(toolLibrary, lockFile, depsFileRoot);
var toolProjectContext = new ProjectContextBuilder()
.WithLockFile(lockFile)
.WithTargetFramework(s_toolPackageFramework.ToString())
.Build();
var exporter = toolProjectContext.CreateExporter(Constants.DefaultConfiguration);
return _packagedCommandSpecFactory.CreateCommandSpecFromLibrary(
lockFilePackageLibrary,
commandName,
@ -114,7 +122,44 @@ namespace Microsoft.DotNet.Cli.Utils
_allowedCommandExtensions,
projectContext.PackagesDirectory,
s_commandResolutionStrategy,
null);
depsFilePath);
}
private LockFile GetToolLockFile(
LibraryRange toolLibrary,
string nugetPackagesRoot)
{
var lockFilePath = GetToolLockFilePath(toolLibrary, nugetPackagesRoot);
if (!File.Exists(lockFilePath))
{
return null;
}
LockFile lockFile = null;
try
{
lockFile = LockFileReader.Read(lockFilePath);
}
catch (FileFormatException ex)
{
throw ex;
}
return lockFile;
}
private string GetToolLockFilePath(
LibraryRange toolLibrary,
string nugetPackagesRoot)
{
var toolPathCalculator = new ToolPathCalculator(nugetPackagesRoot);
return toolPathCalculator.GetBestLockFilePath(
toolLibrary.Name,
toolLibrary.VersionRange,
s_toolPackageFramework);
}
private ProjectContext GetProjectContextFromDirectory(string directory, NuGetFramework framework)
@ -143,5 +188,50 @@ namespace Microsoft.DotNet.Cli.Utils
return projectContext;
}
private string GetToolDepsFilePath(
LibraryRange toolLibrary,
LockFile toolLockFile,
string depsPathRoot)
{
var depsJsonPath = Path.Combine(
depsPathRoot,
toolLibrary.Name + FileNameSuffixes.DepsJson);
EnsureToolJsonDepsFileExists(toolLibrary, toolLockFile, depsJsonPath);
return depsJsonPath;
}
private void EnsureToolJsonDepsFileExists(
LibraryRange toolLibrary,
LockFile toolLockFile,
string depsPath)
{
if (!File.Exists(depsPath))
{
var projectContext = new ProjectContextBuilder()
.WithLockFile(toolLockFile)
.WithTargetFramework(s_toolPackageFramework.ToString())
.Build();
var exporter = projectContext.CreateExporter(Constants.DefaultConfiguration);
var dependencyContext = new DependencyContextBuilder()
.Build(null,
null,
exporter.GetAllExports(),
true,
s_toolPackageFramework,
string.Empty);
using (var fileStream = File.Create(depsPath))
{
var dependencyContextWriter = new DependencyContextWriter();
dependencyContextWriter.Write(dependencyContext, fileStream);
}
}
}
}
}

View file

@ -0,0 +1,70 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using System.Collections.Generic;
using NuGet.Frameworks;
using NuGet.Versioning;
namespace Microsoft.DotNet.Cli.Utils
{
public class ToolPathCalculator
{
private readonly string _packagesDirectory;
public ToolPathCalculator(string packagesDirectory)
{
_packagesDirectory = packagesDirectory;
}
public string GetBestLockFilePath(string packageId, VersionRange versionRange, NuGetFramework framework)
{
var availableToolVersions = GetAvailableToolVersions(packageId);
var bestVersion = versionRange.FindBestMatch(availableToolVersions);
return GetLockFilePath(packageId, bestVersion, framework);
}
public string GetLockFilePath(string packageId, NuGetVersion version, NuGetFramework framework)
{
return Path.Combine(
GetBaseToolPath(packageId),
version.ToNormalizedString(),
framework.GetShortFolderName(),
"project.lock.json");
}
private string GetBaseToolPath(string packageId)
{
return Path.Combine(
_packagesDirectory,
".tools",
packageId);
}
private IEnumerable<NuGetVersion> GetAvailableToolVersions(string packageId)
{
var availableVersions = new List<NuGetVersion>();
var toolBase = GetBaseToolPath(packageId);
var versionDirectories = Directory.EnumerateDirectories(toolBase);
foreach (var versionDirectory in versionDirectories)
{
var version = Path.GetFileName(versionDirectory);
NuGetVersion nugetVersion = null;
NuGetVersion.TryParse(version, out nugetVersion);
if (nugetVersion != null)
{
availableVersions.Add(nugetVersion);
}
}
return availableVersions;
}
}
}

View file

@ -0,0 +1,34 @@
using System;
using System.IO;
using Microsoft.Extensions.PlatformAbstractions;
namespace Microsoft.DotNet.Cli.Utils
{
public class Muxer
{
private static readonly string s_muxerFileName = "dotnet" + Constants.ExeSuffix;
private string _muxerPath;
public string MuxerPath
{
get
{
return _muxerPath;
}
}
public Muxer()
{
var appBase = new DirectoryInfo(PlatformServices.Default.Application.ApplicationBasePath);
var muxerDir = appBase.Parent.Parent;
var muxerCandidate = Path.Combine(muxerDir.FullName, s_muxerFileName);
if (File.Exists(muxerCandidate))
{
_muxerPath = muxerCandidate;
}
}
}
}

View file

@ -0,0 +1,53 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Compilation;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.DependencyModel;
using NuGet.Frameworks;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
namespace Microsoft.DotNet.Cli.Utils
{
public static class RuntimeConfigGenerator
{
// GROOOOOSS
private static readonly string RedistPackageName = "Microsoft.NETCore.App";
public static void WriteRuntimeConfigToFile(LibraryExporter exporter, string runtimeConfigJsonFile)
{
// TODO: Suppress this file if there's nothing to write? RuntimeOutputFiles would have to be updated
// in order to prevent breaking incremental compilation...
var json = new JObject();
var runtimeOptions = new JObject();
json.Add("runtimeOptions", runtimeOptions);
var redistExport = exporter
.GetAllExports()
.FirstOrDefault(l => l.Library.Identity.Name.Equals(RedistPackageName, StringComparison.OrdinalIgnoreCase));
if (redistExport != null)
{
var framework = new JObject(
new JProperty("name", redistExport.Library.Identity.Name),
new JProperty("version", redistExport.Library.Identity.Version.ToNormalizedString()));
runtimeOptions.Add("framework", framework);
}
using (var writer = new JsonTextWriter(new StreamWriter(File.Create(runtimeConfigJsonFile))))
{
writer.Formatting = Formatting.Indented;
json.WriteTo(writer);
}
}
}
}

View file

@ -6,7 +6,11 @@
},
"dependencies": {
"Microsoft.DotNet.ProjectModel": "1.0.0-*",
"Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537"
"Microsoft.Extensions.PlatformAbstractions": "1.0.0-rc2-16537",
"NuGet.Versioning": "3.4.0-rtm-0763",
"NuGet.Packaging": "3.4.0-rtm-0763",
"NuGet.Frameworks": "3.4.0-rtm-0763",
"NuGet.ProjectModel": "3.4.0-rtm-0763"
},
"frameworks": {
"net451": {

View file

@ -126,30 +126,9 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
{
if (!_context.TargetFramework.IsDesktop())
{
// TODO: Suppress this file if there's nothing to write? RuntimeOutputFiles would have to be updated
// in order to prevent breaking incremental compilation...
var json = new JObject();
var runtimeOptions = new JObject();
json.Add("runtimeOptions", runtimeOptions);
var redistExport = exporter
.GetAllExports()
.FirstOrDefault(l => l.Library.Identity.Name.Equals(RedistPackageName, StringComparison.OrdinalIgnoreCase));
if (redistExport != null)
{
var framework = new JObject(
new JProperty("name", redistExport.Library.Identity.Name),
new JProperty("version", redistExport.Library.Identity.Version.ToNormalizedString()));
runtimeOptions.Add("framework", framework);
}
var runtimeConfigJsonFile = Path.Combine(_runtimeOutputPath, _context.ProjectFile.Name + FileNameSuffixes.RuntimeConfigJson);
using (var writer = new JsonTextWriter(new StreamWriter(File.Create(runtimeConfigJsonFile))))
{
writer.Formatting = Formatting.Indented;
json.WriteTo(writer);
}
RuntimeConfigGenerator.WriteRuntimeConfigToFile(exporter, runtimeConfigJsonFile);
}
}
@ -210,7 +189,6 @@ namespace Microsoft.Dotnet.Cli.Compiler.Common
}
}
private static void CreateDirectoryIfNotExists(string path)
{
var depsFile = new FileInfo(path);

View file

@ -22,7 +22,7 @@ namespace Microsoft.DotNet.ProjectModel
public string RuntimeIdentifier { get; }
public Project ProjectFile => RootProject.Project;
public Project ProjectFile => RootProject?.Project;
public LockFile LockFile { get; }

View file

@ -140,7 +140,7 @@ namespace Microsoft.DotNet.ProjectModel
{
ProjectDirectory = Project?.ProjectDirectory ?? ProjectDirectory;
if (GlobalSettings == null)
if (GlobalSettings == null && ProjectDirectory != null)
{
RootDirectory = ProjectRootResolver.ResolveRootDirectory(ProjectDirectory);
@ -157,7 +157,6 @@ namespace Microsoft.DotNet.ProjectModel
var frameworkReferenceResolver = new FrameworkReferenceResolver(ReferenceAssembliesPath);
LockFileLookup lockFileLookup = null;
EnsureProjectLoaded();
LockFile = LockFile ?? LockFileResolver(ProjectDirectory);
@ -167,7 +166,10 @@ namespace Microsoft.DotNet.ProjectModel
if (LockFile != null)
{
validLockFile = LockFile.IsValidForProject(Project, out lockFileValidationMessage);
if (Project != null)
{
validLockFile = LockFile.IsValidForProject(Project, out lockFileValidationMessage);
}
lockFileLookup = new LockFileLookup(LockFile);
}
@ -175,10 +177,14 @@ namespace Microsoft.DotNet.ProjectModel
var libraries = new Dictionary<LibraryKey, LibraryDescription>();
var projectResolver = new ProjectDependencyProvider(ProjectResolver);
var mainProject = projectResolver.GetDescription(TargetFramework, Project, targetLibrary: null);
ProjectDescription mainProject = null;
if (Project != null)
{
mainProject = projectResolver.GetDescription(TargetFramework, Project, targetLibrary: null);
// Add the main project
libraries.Add(new LibraryKey(mainProject.Identity.Name), mainProject);
// Add the main project
libraries.Add(new LibraryKey(mainProject.Identity.Name), mainProject);
}
LockFileTarget target = null;
if (lockFileLookup != null)
@ -251,7 +257,7 @@ namespace Microsoft.DotNet.ProjectModel
}
// Create a library manager
var libraryManager = new LibraryManager(libraries.Values.ToList(), diagnostics, Project.ProjectFilePath);
var libraryManager = new LibraryManager(libraries.Values.ToList(), diagnostics, Project?.ProjectFilePath);
return new ProjectContext(
GlobalSettings,
@ -375,13 +381,9 @@ namespace Microsoft.DotNet.ProjectModel
private void EnsureProjectLoaded()
{
if (Project == null)
if (Project == null && ProjectDirectory != null)
{
Project = ProjectResolver(ProjectDirectory);
if (Project == null)
{
throw new InvalidOperationException($"Unable to resolve project from {ProjectDirectory}");
}
}
}

View file

@ -255,27 +255,33 @@ namespace Microsoft.DotNet.Tools.Build
private void CollectCompilerNamePreconditions(ProjectContext project, IncrementalPreconditions preconditions)
{
var projectCompiler = project.ProjectFile.CompilerName;
if (!KnownCompilers.Any(knownCompiler => knownCompiler.Equals(projectCompiler, StringComparison.Ordinal)))
if (project.ProjectFile != null)
{
preconditions.AddUnknownCompilerPrecondition(project.ProjectName(), projectCompiler);
var projectCompiler = project.ProjectFile.CompilerName;
if (!KnownCompilers.Any(knownCompiler => knownCompiler.Equals(projectCompiler, StringComparison.Ordinal)))
{
preconditions.AddUnknownCompilerPrecondition(project.ProjectName(), projectCompiler);
}
}
}
private void CollectScriptPreconditions(ProjectContext project, IncrementalPreconditions preconditions)
{
var preCompileScripts = project.ProjectFile.Scripts.GetOrEmpty(ScriptNames.PreCompile);
var postCompileScripts = project.ProjectFile.Scripts.GetOrEmpty(ScriptNames.PostCompile);
if (preCompileScripts.Any())
if (project.ProjectFile != null)
{
preconditions.AddPrePostScriptPrecondition(project.ProjectName(), ScriptNames.PreCompile);
}
var preCompileScripts = project.ProjectFile.Scripts.GetOrEmpty(ScriptNames.PreCompile);
var postCompileScripts = project.ProjectFile.Scripts.GetOrEmpty(ScriptNames.PostCompile);
if (postCompileScripts.Any())
{
preconditions.AddPrePostScriptPrecondition(project.ProjectName(), ScriptNames.PostCompile);
if (preCompileScripts.Any())
{
preconditions.AddPrePostScriptPrecondition(project.ProjectName(), ScriptNames.PreCompile);
}
if (postCompileScripts.Any())
{
preconditions.AddPrePostScriptPrecondition(project.ProjectName(), ScriptNames.PostCompile);
}
}
}

View file

@ -122,7 +122,7 @@ namespace Microsoft.DotNet.Tools.Compiler
//used in incremental precondition checks
public static IEnumerable<string> GetCommandsInvokedByCompile(ProjectContext project)
{
return new List<string> {project.ProjectFile.CompilerName, "compile"};
return new List<string> {project.ProjectFile?.CompilerName, "compile"};
}
}
}

View file

@ -94,6 +94,7 @@ namespace Microsoft.DotNet.Tools.Compiler
TryAddOutputFile(context, inputFolder, outputName);
TryAddOutputFile(context, inputFolder, $"{Project.Name}.xml");
TryAddOutputFile(context, inputFolder, $"{Project.Name}.runtimeconfig.json");
}
protected virtual bool GeneratePackage(string nupkg, List<DiagnosticMessage> packDiagnostics)