2016-10-05 11:51:59 -07:00
// 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 ;
2016-02-24 16:05:55 -08:00
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
2016-09-28 14:21:52 -07:00
using Microsoft.DotNet.Tools.Common ;
2016-03-17 11:45:13 -07:00
using Microsoft.Extensions.DependencyModel ;
2016-10-27 18:46:43 -07:00
using NuGet.Configuration ;
2016-02-24 16:05:55 -08:00
using NuGet.Frameworks ;
2016-09-23 15:30:53 -07:00
using NuGet.ProjectModel ;
2016-10-03 10:06:37 -07:00
using NuGet.Versioning ;
2016-02-24 16:05:55 -08:00
2016-10-10 17:13:46 -07:00
namespace Microsoft.DotNet.Cli.Utils
2016-02-24 16:05:55 -08:00
{
public class ProjectToolsCommandResolver : ICommandResolver
{
2016-04-07 14:05:35 -07:00
private static readonly NuGetFramework s_toolPackageFramework = FrameworkConstants . CommonFrameworks . NetCoreApp10 ;
2016-04-28 16:30:32 -07:00
private static readonly CommandResolutionStrategy s_commandResolutionStrategy =
2016-02-24 16:05:55 -08:00
CommandResolutionStrategy . ProjectToolsPackage ;
private List < string > _allowedCommandExtensions ;
private IPackagedCommandSpecFactory _packagedCommandSpecFactory ;
2016-10-10 17:13:46 -07:00
private IEnvironmentProvider _environment ;
public ProjectToolsCommandResolver (
IPackagedCommandSpecFactory packagedCommandSpecFactory ,
IEnvironmentProvider environment )
2016-02-24 16:05:55 -08:00
{
_packagedCommandSpecFactory = packagedCommandSpecFactory ;
2016-10-10 17:13:46 -07:00
_environment = environment ;
2016-02-24 16:05:55 -08:00
2016-04-28 16:30:32 -07:00
_allowedCommandExtensions = new List < string > ( )
2016-02-24 16:05:55 -08:00
{
FileNameSuffixes . DotNet . DynamicLib
} ;
}
public CommandSpec Resolve ( CommandResolverArguments commandResolverArguments )
{
2016-03-07 11:50:52 -08:00
if ( commandResolverArguments . CommandName = = null
| | commandResolverArguments . ProjectDirectory = = null )
{
2016-10-27 18:46:43 -07:00
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: Invalid CommandResolverArguments" ) ;
2016-03-07 11:50:52 -08:00
return null ;
}
2016-04-28 16:30:32 -07:00
2016-10-10 17:13:46 -07:00
return ResolveFromProjectTools ( commandResolverArguments ) ;
2016-02-24 16:05:55 -08:00
}
2016-10-10 17:13:46 -07:00
private CommandSpec ResolveFromProjectTools ( CommandResolverArguments commandResolverArguments )
2016-02-24 16:05:55 -08:00
{
2016-10-10 17:13:46 -07:00
var projectFactory = new ProjectFactory ( _environment ) ;
2016-10-27 18:46:43 -07:00
2016-10-10 17:13:46 -07:00
var project = projectFactory . GetProject (
commandResolverArguments . ProjectDirectory ,
commandResolverArguments . Framework ,
commandResolverArguments . Configuration ,
commandResolverArguments . BuildBasePath ,
commandResolverArguments . OutputPath ) ;
2016-10-27 18:46:43 -07:00
if ( project = = null )
{
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: ProjectFactory did not find Project." ) ;
return null ;
}
2016-10-04 18:58:04 -07:00
var tools = project . GetTools ( ) ;
2016-02-24 16:05:55 -08:00
return ResolveCommandSpecFromAllToolLibraries (
2016-10-03 10:06:37 -07:00
tools ,
2016-10-10 17:13:46 -07:00
commandResolverArguments . CommandName ,
commandResolverArguments . CommandArguments . OrEmptyIfNull ( ) ,
project ) ;
2016-02-24 16:05:55 -08:00
}
private CommandSpec ResolveCommandSpecFromAllToolLibraries (
2016-10-04 18:58:04 -07:00
IEnumerable < SingleProjectInfo > toolsLibraries ,
2016-02-24 16:05:55 -08:00
string commandName ,
IEnumerable < string > args ,
2016-10-10 17:13:46 -07:00
IProject project )
2016-02-24 16:05:55 -08:00
{
2016-10-27 18:46:43 -07:00
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: resolving commandspec from {toolsLibraries.Count()} Tool Libraries." ) ;
2016-02-24 16:05:55 -08:00
foreach ( var toolLibrary in toolsLibraries )
{
2016-10-10 17:13:46 -07:00
var commandSpec = ResolveCommandSpecFromToolLibrary (
toolLibrary ,
commandName ,
args ,
project ) ;
2016-02-24 16:05:55 -08:00
if ( commandSpec ! = null )
{
return commandSpec ;
}
}
2016-10-27 18:46:43 -07:00
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: failed to resolve commandspec from library." ) ;
2016-02-24 16:05:55 -08:00
return null ;
}
private CommandSpec ResolveCommandSpecFromToolLibrary (
2016-10-04 18:58:04 -07:00
SingleProjectInfo toolLibraryRange ,
2016-02-24 16:05:55 -08:00
string commandName ,
IEnumerable < string > args ,
2016-10-10 17:13:46 -07:00
IProject project )
2016-02-24 16:05:55 -08:00
{
2016-10-27 18:46:43 -07:00
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: Attempting to resolve command spec from tool {toolLibraryRange.Name}" ) ;
var nuGetPathContext = NuGetPathContext . Create ( project . ProjectRoot ) ;
var nugetPackagesRoot = nuGetPathContext . UserPackageFolder ;
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: nuget packages root:\n{nugetPackagesRoot}" ) ;
2016-04-28 16:30:32 -07:00
2016-10-03 10:06:37 -07:00
var toolLockFile = GetToolLockFile ( toolLibraryRange , nugetPackagesRoot ) ;
2016-02-24 16:05:55 -08:00
2016-10-27 18:46:43 -07:00
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: found tool lockfile at : {toolLockFile.Path}" ) ;
2016-10-03 10:06:37 -07:00
var toolLibrary = toolLockFile . Targets
. FirstOrDefault (
t = > t . TargetFramework . GetShortFolderName ( ) . Equals ( s_toolPackageFramework . GetShortFolderName ( ) ) )
2016-03-27 22:24:20 -07:00
? . 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 )
{
2016-10-27 18:46:43 -07:00
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: library not found in lock file." ) ;
2016-03-27 22:24:20 -07:00
return null ;
}
2016-04-28 16:30:32 -07:00
2016-10-03 10:06:37 -07:00
var depsFileRoot = Path . GetDirectoryName ( toolLockFile . Path ) ;
2016-10-27 18:46:43 -07:00
2016-10-03 10:06:37 -07:00
var depsFilePath = GetToolDepsFilePath ( toolLibraryRange , toolLockFile , depsFileRoot ) ;
2016-04-28 16:30:32 -07:00
2016-09-28 15:40:38 -07:00
var normalizedNugetPackagesRoot = PathUtility . EnsureNoTrailingDirectorySeparator ( nugetPackagesRoot ) ;
2016-10-27 18:46:43 -07:00
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: attempting to create commandspec" ) ;
2016-10-10 17:13:46 -07:00
var commandSpec = _packagedCommandSpecFactory . CreateCommandSpecFromLibrary (
2016-03-27 22:24:20 -07:00
toolLibrary ,
2016-02-24 16:05:55 -08:00
commandName ,
args ,
_allowedCommandExtensions ,
2016-09-28 15:40:38 -07:00
normalizedNugetPackagesRoot ,
2016-02-24 16:05:55 -08:00
s_commandResolutionStrategy ,
2016-04-07 16:20:51 -07:00
depsFilePath ,
null ) ;
2016-10-10 17:13:46 -07:00
2016-10-27 18:46:43 -07:00
if ( commandSpec = = null )
{
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: commandSpec is null." ) ;
}
2016-10-10 17:13:46 -07:00
commandSpec ? . AddEnvironmentVariablesFromProject ( project ) ;
return commandSpec ;
2016-03-17 11:45:13 -07:00
}
private LockFile GetToolLockFile (
2016-10-04 18:58:04 -07:00
SingleProjectInfo toolLibrary ,
2016-03-17 11:45:13 -07:00
string nugetPackagesRoot )
{
var lockFilePath = GetToolLockFilePath ( toolLibrary , nugetPackagesRoot ) ;
if ( ! File . Exists ( lockFilePath ) )
{
return null ;
}
LockFile lockFile = null ;
try
{
2016-12-13 14:15:35 -08:00
lockFile = new LockFileFormat ( )
. ReadWithLock ( lockFilePath )
. Result ;
2016-03-17 11:45:13 -07:00
}
catch ( FileFormatException ex )
{
throw ex ;
}
return lockFile ;
}
private string GetToolLockFilePath (
2016-10-04 18:58:04 -07:00
SingleProjectInfo toolLibrary ,
2016-03-17 11:45:13 -07:00
string nugetPackagesRoot )
{
var toolPathCalculator = new ToolPathCalculator ( nugetPackagesRoot ) ;
return toolPathCalculator . GetBestLockFilePath (
2016-04-28 16:30:32 -07:00
toolLibrary . Name ,
2016-10-10 17:13:46 -07:00
VersionRange . Parse ( toolLibrary . Version ) ,
2016-03-17 11:45:13 -07:00
s_toolPackageFramework ) ;
2016-02-24 16:05:55 -08:00
}
2016-03-17 11:45:13 -07:00
private string GetToolDepsFilePath (
2016-10-04 18:58:04 -07:00
SingleProjectInfo toolLibrary ,
2016-04-28 16:30:32 -07:00
LockFile toolLockFile ,
2016-03-17 11:45:13 -07:00
string depsPathRoot )
{
var depsJsonPath = Path . Combine (
depsPathRoot ,
toolLibrary . Name + FileNameSuffixes . DepsJson ) ;
2016-10-27 18:46:43 -07:00
Reporter . Verbose . WriteLine ( $"projecttoolscommandresolver: expect deps.json at: {depsJsonPath}" ) ;
2016-10-03 15:36:14 -07:00
EnsureToolJsonDepsFileExists ( toolLockFile , depsJsonPath , toolLibrary ) ;
2016-03-17 11:45:13 -07:00
return depsJsonPath ;
}
private void EnsureToolJsonDepsFileExists (
2016-04-28 16:30:32 -07:00
LockFile toolLockFile ,
2016-10-03 15:36:14 -07:00
string depsPath ,
2016-10-04 18:58:04 -07:00
SingleProjectInfo toolLibrary )
2016-03-17 11:45:13 -07:00
{
if ( ! File . Exists ( depsPath ) )
{
2016-10-03 15:36:14 -07:00
GenerateDepsJsonFile ( toolLockFile , depsPath , toolLibrary ) ;
2016-04-08 17:07:29 -07:00
}
}
2016-10-04 18:58:04 -07:00
internal void GenerateDepsJsonFile (
2016-04-28 16:30:32 -07:00
LockFile toolLockFile ,
2016-10-03 15:36:14 -07:00
string depsPath ,
2016-10-04 18:58:04 -07:00
SingleProjectInfo toolLibrary )
2016-04-08 17:07:29 -07:00
{
Reporter . Verbose . WriteLine ( $"Generating deps.json at: {depsPath}" ) ;
2016-10-03 21:40:24 -07:00
var dependencyContext = new DepsJsonBuilder ( )
2016-10-04 18:58:04 -07:00
. Build ( toolLibrary , null , toolLockFile , s_toolPackageFramework , null ) ;
2016-04-08 17:07:29 -07:00
var tempDepsFile = Path . GetTempFileName ( ) ;
using ( var fileStream = File . Open ( tempDepsFile , FileMode . Open , FileAccess . Write ) )
{
var dependencyContextWriter = new DependencyContextWriter ( ) ;
dependencyContextWriter . Write ( dependencyContext , fileStream ) ;
}
try
{
2016-12-04 11:54:29 -08:00
File . Move ( tempDepsFile , depsPath ) ;
2016-04-08 17:07:29 -07:00
}
catch ( Exception e )
{
Reporter . Verbose . WriteLine ( $"unable to generate deps.json, it may have been already generated: {e.Message}" ) ;
2016-12-04 11:54:29 -08:00
2016-04-08 17:07:29 -07:00
try
{
File . Delete ( tempDepsFile ) ;
}
catch ( Exception e2 )
2016-04-28 16:30:32 -07:00
{
2016-04-08 17:07:29 -07:00
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
}
}