2016-01-04 12:36:46 -08: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 ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Text ;
using Microsoft.DotNet.Cli.Utils ;
using Microsoft.Dnx.Runtime.Common.CommandLine ;
2016-01-29 18:21:37 -08:00
using Microsoft.Dotnet.Cli.Compiler.Common ;
2016-01-04 12:36:46 -08:00
using Microsoft.DotNet.ProjectModel ;
using Microsoft.DotNet.ProjectModel.Graph ;
using NuGet.Frameworks ;
2015-12-18 16:39:43 -08:00
using Microsoft.Extensions.PlatformAbstractions ;
2016-01-04 12:36:46 -08:00
namespace Microsoft.DotNet.Tools.Restore
{
2016-01-30 21:47:50 -08:00
public partial class RestoreCommand
2016-01-04 12:36:46 -08:00
{
2015-12-18 16:39:43 -08:00
private static readonly string DefaultRid = PlatformServices . Default . Runtime . GetLegacyRestoreRuntimeIdentifier ( ) ;
2016-01-30 21:47:50 -08:00
public static int Run ( string [ ] args )
2016-01-04 12:36:46 -08:00
{
DebugHelper . HandleDebugSwitch ( ref args ) ;
var app = new CommandLineApplication ( false )
{
Name = "dotnet restore" ,
FullName = ".NET project dependency restorer" ,
Description = "Restores dependencies listed in project.json"
} ;
2016-01-12 16:36:31 -08:00
// Parse --quiet, because we have to handle that specially since NuGet3 has a different
// "--verbosity" switch that goes BEFORE the command
var quiet = args . Any ( s = > s . Equals ( "--quiet" , StringComparison . OrdinalIgnoreCase ) ) ;
args = args . Where ( s = > ! s . Equals ( "--quiet" , StringComparison . OrdinalIgnoreCase ) ) . ToArray ( ) ;
2016-01-04 12:36:46 -08:00
app . OnExecute ( ( ) = >
{
try
{
2016-01-12 16:36:31 -08:00
var projectRestoreResult = NuGet3 . Restore ( args , quiet ) ;
2016-01-04 12:36:46 -08:00
var restoreTasks = GetRestoreTasks ( args ) ;
foreach ( var restoreTask in restoreTasks )
{
var project = ProjectReader . GetProject ( restoreTask . ProjectPath ) ;
2016-01-12 16:36:31 -08:00
RestoreTools ( project , restoreTask , quiet ) ;
2016-01-04 12:36:46 -08:00
}
return projectRestoreResult ;
}
catch ( InvalidOperationException e )
{
Console . WriteLine ( e . Message ) ;
return - 1 ;
}
catch ( Exception e )
{
Console . WriteLine ( e . Message ) ;
return - 2 ;
}
} ) ;
return app . Execute ( args ) ;
}
private static IEnumerable < RestoreTask > GetRestoreTasks ( IEnumerable < string > args )
{
var directory = Directory . GetCurrentDirectory ( ) ;
if ( args . Any ( ) )
{
var firstArg = args . First ( ) ;
if ( IsProjectFile ( firstArg ) )
{
return new [ ] { new RestoreTask { ProjectPath = firstArg , Arguments = args . Skip ( 1 ) } } ;
}
if ( Directory . Exists ( firstArg ) )
{
directory = firstArg ;
args = args . Skip ( 1 ) ;
}
}
return GetAllProjectFiles ( directory )
. Select ( p = > new RestoreTask { ProjectPath = p , Arguments = args } ) ;
}
private static string [ ] GetAllProjectFiles ( string directory )
{
return Directory . GetFiles ( directory , Project . FileName , SearchOption . AllDirectories ) ;
}
private static bool IsProjectFile ( string firstArg )
{
return firstArg . EndsWith ( Project . FileName ) & & File . Exists ( firstArg ) ;
}
2016-01-12 16:36:31 -08:00
private static void RestoreTools ( Project project , RestoreTask restoreTask , bool quiet )
2016-01-04 12:36:46 -08:00
{
foreach ( var tooldep in project . Tools )
{
2016-01-12 16:36:31 -08:00
RestoreTool ( tooldep , restoreTask , quiet ) ;
2016-01-04 12:36:46 -08:00
}
}
2016-01-12 16:36:31 -08:00
private static void RestoreTool ( LibraryRange tooldep , RestoreTask restoreTask , bool quiet )
2016-01-04 12:36:46 -08:00
{
2016-01-20 17:20:38 -08:00
var tempRoot = Path . Combine ( restoreTask . ProjectDirectory , "obj" ) ;
2016-01-15 13:34:05 -08:00
try
{
2016-01-20 17:20:38 -08:00
var tempPath = Path . Combine ( tempRoot , Guid . NewGuid ( ) . ToString ( ) , "bin" ) ;
2016-01-04 12:36:46 -08:00
2016-02-01 17:30:41 -08:00
var restoreSucceded = RestoreToolToPath ( tooldep , restoreTask . Arguments , tempPath , quiet ) ;
if ( restoreSucceded )
{
CreateDepsInPackageCache ( tooldep , tempPath ) ;
PersistLockFile ( tooldep , tempPath , restoreTask . ProjectDirectory ) ;
}
2016-01-15 13:34:05 -08:00
}
finally
{
Directory . Delete ( tempRoot , true ) ;
}
2016-01-04 12:36:46 -08:00
}
2016-01-06 02:27:16 -08:00
private static void PersistLockFile ( LibraryRange tooldep , string tempPath , string projectPath )
2016-01-04 12:36:46 -08:00
{
2016-01-06 02:27:16 -08:00
var sourcePath = Path . Combine ( tempPath , "project.lock.json" ) ;
var targetDir = Path . Combine ( projectPath , "artifacts" , "Tools" , tooldep . Name ) ;
var targetPath = Path . Combine ( targetDir , "project.lock.json" ) ;
if ( Directory . Exists ( targetDir ) ) Directory . Delete ( targetDir , true ) ;
Directory . CreateDirectory ( targetDir ) ;
Console . WriteLine ( $"Writing '{sourcePath}' to '{targetPath}'" ) ;
File . Move ( sourcePath , targetPath ) ;
2016-01-04 12:36:46 -08:00
}
private static void CreateDepsInPackageCache ( LibraryRange toolLibrary , string projectPath )
{
var context = ProjectContext . Create ( projectPath ,
2016-03-01 17:35:32 -06:00
FrameworkConstants . CommonFrameworks . NetStandardApp15 , new [ ] { DefaultRid } ) ;
2016-01-04 12:36:46 -08:00
var toolDescription = context . LibraryManager . GetLibraries ( )
. Select ( l = > l as PackageDescription )
. Where ( l = > l ! = null )
. FirstOrDefault ( l = > l . Identity . Name = = toolLibrary . Name ) ;
var depsPath = Path . Combine (
toolDescription . Path ,
2016-02-23 02:34:27 -08:00
Path . GetDirectoryName ( toolDescription . RuntimeAssemblies . First ( ) . Path ) ,
2016-01-04 12:36:46 -08:00
toolDescription . Identity . Name + FileNameSuffixes . Deps ) ;
2016-01-29 18:21:37 -08:00
2016-03-14 18:41:08 -07:00
var depsJsonPath = Path . Combine (
toolDescription . Path ,
Path . GetDirectoryName ( toolDescription . RuntimeAssemblies . First ( ) . Path ) ,
toolDescription . Identity . Name + FileNameSuffixes . DepsJson ) ;
2016-02-03 10:57:25 -08:00
var calculator = context . GetOutputPaths ( Constants . DefaultConfiguration , buidBasePath : null , outputPath : context . ProjectDirectory ) ;
2016-03-08 16:46:50 -08:00
var executable = new Executable ( context , calculator , context . CreateExporter ( Constants . DefaultConfiguration ) , null ) ;
2016-01-29 18:21:37 -08:00
2016-02-03 10:57:25 -08:00
executable . MakeCompilationOutputRunnable ( ) ;
2016-01-04 12:36:46 -08:00
if ( File . Exists ( depsPath ) ) File . Delete ( depsPath ) ;
2016-03-14 18:41:08 -07:00
if ( File . Exists ( depsJsonPath ) ) File . Delete ( depsJsonPath ) ;
2016-01-04 12:36:46 -08:00
2016-02-03 10:57:25 -08:00
File . Move ( Path . Combine ( calculator . RuntimeOutputPath , "bin" + FileNameSuffixes . Deps ) , depsPath ) ;
2016-03-14 18:41:08 -07:00
File . Move ( Path . Combine ( calculator . RuntimeOutputPath , "bin" + FileNameSuffixes . DepsJson ) , depsJsonPath ) ;
2016-01-04 12:36:46 -08:00
}
2016-02-01 17:30:41 -08:00
private static bool RestoreToolToPath ( LibraryRange tooldep , IEnumerable < string > args , string tempPath , bool quiet )
2016-01-04 12:36:46 -08:00
{
Directory . CreateDirectory ( tempPath ) ;
var projectPath = Path . Combine ( tempPath , Project . FileName ) ;
2016-01-06 02:27:16 -08:00
Console . WriteLine ( $"Restoring Tool '{tooldep.Name}' for '{projectPath}' in '{tempPath}'" ) ;
2016-03-01 17:35:32 -06:00
File . WriteAllText ( projectPath , GenerateProjectJsonContents ( new [ ] { "netstandardapp1.5" } , tooldep ) ) ;
2016-03-07 09:34:18 -08:00
return NuGet3 . Restore ( new [ ] { $"{projectPath}" } . Concat ( args ) , quiet ) = = 0 ;
2016-01-04 12:36:46 -08:00
}
2016-01-12 16:36:31 -08:00
private static string GenerateProjectJsonContents ( IEnumerable < string > frameworks , LibraryRange tooldep )
2016-01-04 12:36:46 -08:00
{
var sb = new StringBuilder ( ) ;
sb . AppendLine ( "{" ) ;
2016-01-12 16:36:31 -08:00
sb . AppendLine ( " \"dependencies\": {" ) ;
sb . AppendLine ( $" \" { tooldep . Name } \ ": \"{tooldep.VersionRange.OriginalString}\"" ) ;
sb . AppendLine ( " }," ) ;
sb . AppendLine ( " \"frameworks\": {" ) ;
foreach ( var framework in frameworks )
2016-01-04 12:36:46 -08:00
{
2016-03-01 17:35:32 -06:00
var importsStatement = "\"imports\": [ \"dnxcore50\", \"portable-net452+win81\" ]" ;
2016-02-09 12:59:01 -08:00
sb . AppendLine ( $" \" { framework } \ ": {{ {importsStatement} }}" ) ;
2016-01-04 12:36:46 -08:00
}
2016-03-07 09:34:18 -08:00
sb . AppendLine ( " }," ) ;
sb . AppendLine ( " \"runtimes\": { " ) ;
sb . AppendLine ( $" \" { DefaultRid } \ ": {{}}" ) ;
2016-01-12 16:36:31 -08:00
sb . AppendLine ( " }" ) ;
2016-01-04 12:36:46 -08:00
sb . AppendLine ( "}" ) ;
var pjContents = sb . ToString ( ) ;
return pjContents ;
}
}
2016-01-12 16:36:31 -08:00
}