2016-02-25 00:05:55 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using Microsoft.DotNet.ProjectModel;
|
|
|
|
|
using Microsoft.DotNet.ProjectModel.Graph;
|
2016-03-17 18:45:13 +00:00
|
|
|
|
using Microsoft.DotNet.ProjectModel.Compilation;
|
|
|
|
|
using Microsoft.Extensions.DependencyModel;
|
2016-02-25 00:05:55 +00:00
|
|
|
|
using Microsoft.Extensions.PlatformAbstractions;
|
|
|
|
|
using NuGet.Frameworks;
|
|
|
|
|
using NuGet.Packaging;
|
2016-03-17 18:45:13 +00:00
|
|
|
|
using NuGet.ProjectModel;
|
|
|
|
|
|
|
|
|
|
using LockFile = Microsoft.DotNet.ProjectModel.Graph.LockFile;
|
|
|
|
|
using FileFormatException = Microsoft.DotNet.ProjectModel.FileFormatException;
|
2016-02-25 00:05:55 +00:00
|
|
|
|
|
|
|
|
|
namespace Microsoft.DotNet.Cli.Utils
|
|
|
|
|
{
|
|
|
|
|
public class ProjectToolsCommandResolver : ICommandResolver
|
|
|
|
|
{
|
2016-04-07 21:05:35 +00:00
|
|
|
|
private static readonly NuGetFramework s_toolPackageFramework = FrameworkConstants.CommonFrameworks.NetCoreApp10;
|
2016-03-17 18:45:13 +00:00
|
|
|
|
|
2016-02-25 00:05:55 +00:00
|
|
|
|
private static readonly CommandResolutionStrategy s_commandResolutionStrategy =
|
|
|
|
|
CommandResolutionStrategy.ProjectToolsPackage;
|
|
|
|
|
|
2016-03-17 18:45:13 +00:00
|
|
|
|
private static readonly string s_currentRuntimeIdentifier = PlatformServices.Default.Runtime.GetLegacyRestoreRuntimeIdentifier();
|
|
|
|
|
|
|
|
|
|
|
2016-02-25 00:05:55 +00:00
|
|
|
|
private List<string> _allowedCommandExtensions;
|
|
|
|
|
private IPackagedCommandSpecFactory _packagedCommandSpecFactory;
|
|
|
|
|
|
|
|
|
|
public ProjectToolsCommandResolver(IPackagedCommandSpecFactory packagedCommandSpecFactory)
|
|
|
|
|
{
|
|
|
|
|
_packagedCommandSpecFactory = packagedCommandSpecFactory;
|
|
|
|
|
|
|
|
|
|
_allowedCommandExtensions = new List<string>()
|
|
|
|
|
{
|
|
|
|
|
FileNameSuffixes.DotNet.DynamicLib
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public CommandSpec Resolve(CommandResolverArguments commandResolverArguments)
|
|
|
|
|
{
|
2016-03-07 19:50:52 +00:00
|
|
|
|
if (commandResolverArguments.CommandName == null
|
|
|
|
|
|| commandResolverArguments.ProjectDirectory == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-25 00:05:55 +00:00
|
|
|
|
return ResolveFromProjectTools(
|
|
|
|
|
commandResolverArguments.CommandName,
|
2016-03-07 19:50:52 +00:00
|
|
|
|
commandResolverArguments.CommandArguments.OrEmptyIfNull(),
|
2016-02-25 00:05:55 +00:00
|
|
|
|
commandResolverArguments.ProjectDirectory);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CommandSpec ResolveFromProjectTools(
|
|
|
|
|
string commandName,
|
|
|
|
|
IEnumerable<string> args,
|
|
|
|
|
string projectDirectory)
|
|
|
|
|
{
|
2016-03-23 19:40:10 +00:00
|
|
|
|
var projectContext = GetProjectContextFromDirectoryForFirstTarget(projectDirectory);
|
2016-02-25 00:05:55 +00:00
|
|
|
|
|
|
|
|
|
if (projectContext == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-07 19:50:52 +00:00
|
|
|
|
var toolsLibraries = projectContext.ProjectFile.Tools.OrEmptyIfNull();
|
2016-02-25 00:05:55 +00:00
|
|
|
|
|
|
|
|
|
return ResolveCommandSpecFromAllToolLibraries(
|
|
|
|
|
toolsLibraries,
|
|
|
|
|
commandName,
|
|
|
|
|
args,
|
|
|
|
|
projectContext);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CommandSpec ResolveCommandSpecFromAllToolLibraries(
|
|
|
|
|
IEnumerable<LibraryRange> toolsLibraries,
|
|
|
|
|
string commandName,
|
|
|
|
|
IEnumerable<string> args,
|
|
|
|
|
ProjectContext projectContext)
|
|
|
|
|
{
|
|
|
|
|
foreach (var toolLibrary in toolsLibraries)
|
|
|
|
|
{
|
|
|
|
|
var commandSpec = ResolveCommandSpecFromToolLibrary(toolLibrary, commandName, args, projectContext);
|
|
|
|
|
|
|
|
|
|
if (commandSpec != null)
|
|
|
|
|
{
|
|
|
|
|
return commandSpec;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private CommandSpec ResolveCommandSpecFromToolLibrary(
|
2016-03-28 05:24:20 +00:00
|
|
|
|
LibraryRange toolLibraryRange,
|
2016-02-25 00:05:55 +00:00
|
|
|
|
string commandName,
|
|
|
|
|
IEnumerable<string> args,
|
|
|
|
|
ProjectContext projectContext)
|
|
|
|
|
{
|
2016-03-17 18:45:13 +00:00
|
|
|
|
var nugetPackagesRoot = projectContext.PackagesDirectory;
|
2016-03-28 05:24:20 +00:00
|
|
|
|
|
|
|
|
|
var lockFile = GetToolLockFile(toolLibraryRange, nugetPackagesRoot);
|
2016-02-25 00:05:55 +00:00
|
|
|
|
|
2016-03-28 05:24:20 +00:00
|
|
|
|
var toolLibrary = lockFile.Targets
|
|
|
|
|
.FirstOrDefault(t => t.TargetFramework.GetShortFolderName().Equals(s_toolPackageFramework.GetShortFolderName()))
|
|
|
|
|
?.Libraries.FirstOrDefault(l => l.Name == toolLibraryRange.Name);
|
2016-02-25 00:05:55 +00:00
|
|
|
|
|
2016-03-28 05:24:20 +00:00
|
|
|
|
if (toolLibrary == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-17 18:45:13 +00:00
|
|
|
|
var depsFileRoot = Path.GetDirectoryName(lockFile.LockFilePath);
|
2016-03-28 05:24:20 +00:00
|
|
|
|
var depsFilePath = GetToolDepsFilePath(toolLibraryRange, lockFile, depsFileRoot);
|
|
|
|
|
|
2016-03-28 06:54:17 +00:00
|
|
|
|
return _packagedCommandSpecFactory.CreateCommandSpecFromLibrary(
|
2016-03-28 05:24:20 +00:00
|
|
|
|
toolLibrary,
|
2016-02-25 00:05:55 +00:00
|
|
|
|
commandName,
|
|
|
|
|
args,
|
|
|
|
|
_allowedCommandExtensions,
|
|
|
|
|
projectContext.PackagesDirectory,
|
|
|
|
|
s_commandResolutionStrategy,
|
2016-04-07 23:20:51 +00:00
|
|
|
|
depsFilePath,
|
|
|
|
|
null);
|
2016-03-17 18:45:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private LockFile GetToolLockFile(
|
|
|
|
|
LibraryRange toolLibrary,
|
|
|
|
|
string nugetPackagesRoot)
|
|
|
|
|
{
|
|
|
|
|
var lockFilePath = GetToolLockFilePath(toolLibrary, nugetPackagesRoot);
|
|
|
|
|
|
|
|
|
|
if (!File.Exists(lockFilePath))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LockFile lockFile = null;
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2016-03-31 18:15:50 +00:00
|
|
|
|
lockFile = LockFileReader.Read(lockFilePath, designTime: false);
|
2016-03-17 18:45:13 +00:00
|
|
|
|
}
|
|
|
|
|
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);
|
2016-02-25 00:05:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-03-23 19:40:10 +00:00
|
|
|
|
private ProjectContext GetProjectContextFromDirectoryForFirstTarget(string projectRootPath)
|
2016-02-25 00:05:55 +00:00
|
|
|
|
{
|
2016-03-23 19:40:10 +00:00
|
|
|
|
if (projectRootPath == null)
|
2016-02-25 00:05:55 +00:00
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!File.Exists(Path.Combine(projectRootPath, Project.FileName)))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-23 19:40:10 +00:00
|
|
|
|
var projectContext = ProjectContext.CreateContextForEachTarget(projectRootPath).FirstOrDefault();
|
2016-02-25 00:05:55 +00:00
|
|
|
|
|
|
|
|
|
return projectContext;
|
|
|
|
|
}
|
2016-03-17 18:45:13 +00:00
|
|
|
|
|
|
|
|
|
private string GetToolDepsFilePath(
|
|
|
|
|
LibraryRange toolLibrary,
|
|
|
|
|
LockFile toolLockFile,
|
|
|
|
|
string depsPathRoot)
|
|
|
|
|
{
|
|
|
|
|
var depsJsonPath = Path.Combine(
|
|
|
|
|
depsPathRoot,
|
|
|
|
|
toolLibrary.Name + FileNameSuffixes.DepsJson);
|
|
|
|
|
|
2016-04-09 00:07:29 +00:00
|
|
|
|
EnsureToolJsonDepsFileExists(toolLockFile, depsJsonPath);
|
2016-03-17 18:45:13 +00:00
|
|
|
|
|
|
|
|
|
return depsJsonPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void EnsureToolJsonDepsFileExists(
|
|
|
|
|
LockFile toolLockFile,
|
|
|
|
|
string depsPath)
|
|
|
|
|
{
|
|
|
|
|
if (!File.Exists(depsPath))
|
|
|
|
|
{
|
2016-04-09 00:07:29 +00:00
|
|
|
|
GenerateDepsJsonFile(toolLockFile, depsPath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Need to unit test this, so public
|
|
|
|
|
public void GenerateDepsJsonFile(
|
|
|
|
|
LockFile toolLockFile,
|
|
|
|
|
string depsPath)
|
|
|
|
|
{
|
|
|
|
|
Reporter.Verbose.WriteLine($"Generating deps.json at: {depsPath}");
|
|
|
|
|
|
|
|
|
|
var projectContext = new ProjectContextBuilder()
|
|
|
|
|
.WithLockFile(toolLockFile)
|
|
|
|
|
.WithTargetFramework(s_toolPackageFramework.ToString())
|
|
|
|
|
.Build();
|
2016-03-17 18:45:13 +00:00
|
|
|
|
|
2016-04-09 00:07:29 +00:00
|
|
|
|
var exporter = projectContext.CreateExporter(Constants.DefaultConfiguration);
|
|
|
|
|
|
|
|
|
|
var dependencyContext = new DependencyContextBuilder()
|
|
|
|
|
.Build(null,
|
|
|
|
|
null,
|
|
|
|
|
exporter.GetAllExports(),
|
|
|
|
|
true,
|
|
|
|
|
s_toolPackageFramework,
|
|
|
|
|
string.Empty);
|
|
|
|
|
|
|
|
|
|
var tempDepsFile = Path.GetTempFileName();
|
|
|
|
|
using (var fileStream = File.Open(tempDepsFile, FileMode.Open, FileAccess.Write))
|
|
|
|
|
{
|
|
|
|
|
var dependencyContextWriter = new DependencyContextWriter();
|
|
|
|
|
|
|
|
|
|
dependencyContextWriter.Write(dependencyContext, fileStream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
File.Copy(tempDepsFile, depsPath);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
{
|
|
|
|
|
Reporter.Verbose.WriteLine($"unable to generate deps.json, it may have been already generated: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
File.Delete(tempDepsFile);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception e2)
|
|
|
|
|
{
|
|
|
|
|
Reporter.Verbose.WriteLine($"unable to delete temporary deps.json file: {e2.Message}");
|
2016-03-17 18:45:13 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-25 00:05:55 +00:00
|
|
|
|
}
|
|
|
|
|
}
|