2016-02-24 16:05:55 -08: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 11:45:13 -07:00
using Microsoft.DotNet.ProjectModel.Compilation ;
using Microsoft.Extensions.DependencyModel ;
2016-02-24 16:05:55 -08:00
using Microsoft.Extensions.PlatformAbstractions ;
using NuGet.Frameworks ;
using NuGet.Packaging ;
2016-03-17 11:45:13 -07:00
using NuGet.ProjectModel ;
using LockFile = Microsoft . DotNet . ProjectModel . Graph . LockFile ;
using FileFormatException = Microsoft . DotNet . ProjectModel . FileFormatException ;
2016-02-24 16:05:55 -08:00
namespace Microsoft.DotNet.Cli.Utils
{
public class ProjectToolsCommandResolver : ICommandResolver
{
2016-04-07 14:05:35 -07:00
private static readonly NuGetFramework s_toolPackageFramework = FrameworkConstants . CommonFrameworks . NetCoreApp10 ;
2016-03-17 11:45:13 -07:00
2016-02-24 16:05:55 -08:00
private static readonly CommandResolutionStrategy s_commandResolutionStrategy =
CommandResolutionStrategy . ProjectToolsPackage ;
2016-03-17 11:45:13 -07:00
private static readonly string s_currentRuntimeIdentifier = PlatformServices . Default . Runtime . GetLegacyRestoreRuntimeIdentifier ( ) ;
2016-02-24 16:05:55 -08: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 11:50:52 -08:00
if ( commandResolverArguments . CommandName = = null
| | commandResolverArguments . ProjectDirectory = = null )
{
return null ;
}
2016-02-24 16:05:55 -08:00
return ResolveFromProjectTools (
commandResolverArguments . CommandName ,
2016-03-07 11:50:52 -08:00
commandResolverArguments . CommandArguments . OrEmptyIfNull ( ) ,
2016-02-24 16:05:55 -08:00
commandResolverArguments . ProjectDirectory ) ;
}
private CommandSpec ResolveFromProjectTools (
string commandName ,
IEnumerable < string > args ,
string projectDirectory )
{
2016-03-23 12:40:10 -07:00
var projectContext = GetProjectContextFromDirectoryForFirstTarget ( projectDirectory ) ;
2016-02-24 16:05:55 -08:00
if ( projectContext = = null )
{
return null ;
}
2016-03-07 11:50:52 -08:00
var toolsLibraries = projectContext . ProjectFile . Tools . OrEmptyIfNull ( ) ;
2016-02-24 16:05:55 -08: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-27 22:24:20 -07:00
LibraryRange toolLibraryRange ,
2016-02-24 16:05:55 -08:00
string commandName ,
IEnumerable < string > args ,
ProjectContext projectContext )
{
2016-03-17 11:45:13 -07:00
var nugetPackagesRoot = projectContext . PackagesDirectory ;
2016-03-27 22:24:20 -07:00
var lockFile = GetToolLockFile ( toolLibraryRange , nugetPackagesRoot ) ;
2016-02-24 16:05:55 -08:00
2016-03-27 22:24:20 -07:00
var toolLibrary = lockFile . Targets
. FirstOrDefault ( t = > t . TargetFramework . GetShortFolderName ( ) . Equals ( s_toolPackageFramework . GetShortFolderName ( ) ) )
? . Libraries . FirstOrDefault ( l = > l . Name = = toolLibraryRange . Name ) ;
2016-02-24 16:05:55 -08:00
2016-03-27 22:24:20 -07:00
if ( toolLibrary = = null )
{
return null ;
}
2016-03-17 11:45:13 -07:00
var depsFileRoot = Path . GetDirectoryName ( lockFile . LockFilePath ) ;
2016-03-27 22:24:20 -07:00
var depsFilePath = GetToolDepsFilePath ( toolLibraryRange , lockFile , depsFileRoot ) ;
2016-03-27 23:54:17 -07:00
return _packagedCommandSpecFactory . CreateCommandSpecFromLibrary (
2016-03-27 22:24:20 -07:00
toolLibrary ,
2016-02-24 16:05:55 -08:00
commandName ,
args ,
_allowedCommandExtensions ,
projectContext . PackagesDirectory ,
s_commandResolutionStrategy ,
2016-04-07 16:20:51 -07:00
depsFilePath ,
null ) ;
2016-03-17 11:45:13 -07: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 11:15:50 -07:00
lockFile = LockFileReader . Read ( lockFilePath , designTime : false ) ;
2016-03-17 11:45:13 -07: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-24 16:05:55 -08:00
}
2016-03-23 12:40:10 -07:00
private ProjectContext GetProjectContextFromDirectoryForFirstTarget ( string projectRootPath )
2016-02-24 16:05:55 -08:00
{
2016-03-23 12:40:10 -07:00
if ( projectRootPath = = null )
2016-02-24 16:05:55 -08:00
{
return null ;
}
if ( ! File . Exists ( Path . Combine ( projectRootPath , Project . FileName ) ) )
{
return null ;
}
2016-03-23 12:40:10 -07:00
var projectContext = ProjectContext . CreateContextForEachTarget ( projectRootPath ) . FirstOrDefault ( ) ;
2016-02-24 16:05:55 -08:00
return projectContext ;
}
2016-03-17 11:45:13 -07:00
private string GetToolDepsFilePath (
LibraryRange toolLibrary ,
LockFile toolLockFile ,
string depsPathRoot )
{
var depsJsonPath = Path . Combine (
depsPathRoot ,
toolLibrary . Name + FileNameSuffixes . DepsJson ) ;
2016-04-08 17:07:29 -07:00
EnsureToolJsonDepsFileExists ( toolLockFile , depsJsonPath ) ;
2016-03-17 11:45:13 -07:00
return depsJsonPath ;
}
private void EnsureToolJsonDepsFileExists (
LockFile toolLockFile ,
string depsPath )
{
if ( ! File . Exists ( depsPath ) )
{
2016-04-08 17:07:29 -07: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 11:45:13 -07:00
2016-04-08 17:07:29 -07: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 11:45:13 -07:00
}
}
}
2016-02-24 16:05:55 -08:00
}
}