2016-08-22 19:21:52 +00: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-09-26 21:16:17 +00:00
using System.Collections.Generic ;
2016-08-22 19:21:52 +00:00
using System.IO ;
2016-10-04 17:39:55 +00:00
using System.Linq ;
2016-10-04 21:59:04 +00:00
using System.Text ;
2016-08-22 19:21:52 +00:00
using Microsoft.Build.Construction ;
2016-10-12 23:22:58 +00:00
using Microsoft.Build.Evaluation ;
2016-12-07 21:49:15 +00:00
using Microsoft.DotNet.Cli.Sln.Internal ;
2016-10-04 21:59:04 +00:00
using Microsoft.DotNet.Cli.Utils ;
2016-08-22 19:21:52 +00:00
using Microsoft.DotNet.ProjectJsonMigration ;
2016-10-28 01:46:43 +00:00
using Microsoft.DotNet.Internal.ProjectModel ;
2016-10-29 08:58:37 +00:00
using Project = Microsoft . DotNet . Internal . ProjectModel . Project ;
using Microsoft.DotNet.Tools.Common ;
2016-08-22 19:21:52 +00:00
namespace Microsoft.DotNet.Tools.Migrate
{
public partial class MigrateCommand
{
2016-12-07 21:49:15 +00:00
private const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}" ;
private SlnFile _slnFile ;
2016-10-29 08:58:37 +00:00
private readonly DirectoryInfo _workspaceDirectory ;
private readonly DirectoryInfo _backupDirectory ;
2016-08-23 20:50:05 +00:00
private readonly string _templateFile ;
2016-09-26 21:16:17 +00:00
private readonly string _projectArg ;
2016-08-23 20:50:05 +00:00
private readonly string _sdkVersion ;
2016-09-22 00:27:02 +00:00
private readonly string _xprojFilePath ;
2016-09-23 07:30:41 +00:00
private readonly bool _skipProjectReferences ;
2016-10-04 21:59:04 +00:00
private readonly string _reportFile ;
private readonly bool _reportFormatJson ;
2016-10-29 08:58:37 +00:00
private readonly bool _skipBackup ;
2016-08-22 19:21:52 +00:00
2016-10-04 21:59:04 +00:00
public MigrateCommand (
string templateFile ,
string projectArg ,
string sdkVersion ,
string xprojFilePath ,
string reportFile ,
bool skipProjectReferences ,
2016-10-29 08:58:37 +00:00
bool reportFormatJson ,
bool skipBackup )
{
2016-08-22 19:21:52 +00:00
_templateFile = templateFile ;
2016-09-26 21:16:17 +00:00
_projectArg = projectArg ? ? Directory . GetCurrentDirectory ( ) ;
2016-10-29 08:58:37 +00:00
_workspaceDirectory = File . Exists ( _projectArg )
? new FileInfo ( _projectArg ) . Directory
: new DirectoryInfo ( _projectArg ) ;
_backupDirectory = new DirectoryInfo ( Path . Combine ( _workspaceDirectory . FullName , "backup" ) ) ;
2016-08-22 19:21:52 +00:00
_sdkVersion = sdkVersion ;
2016-09-22 00:27:02 +00:00
_xprojFilePath = xprojFilePath ;
2016-09-23 07:30:41 +00:00
_skipProjectReferences = skipProjectReferences ;
2016-10-04 21:59:04 +00:00
_reportFile = reportFile ;
_reportFormatJson = reportFormatJson ;
2016-10-29 08:58:37 +00:00
_skipBackup = skipBackup ;
2016-08-22 19:21:52 +00:00
}
2016-08-23 20:50:05 +00:00
public int Execute ( )
2016-08-22 19:21:52 +00:00
{
2016-10-25 05:46:15 +00:00
var temporaryDotnetNewProject = new TemporaryDotnetNewTemplateProject ( ) ;
2016-09-26 21:16:17 +00:00
var projectsToMigrate = GetProjectsToMigrate ( _projectArg ) ;
2016-08-22 19:21:52 +00:00
2016-10-25 05:46:15 +00:00
var msBuildTemplatePath = _templateFile ? ? temporaryDotnetNewProject . MSBuildProjectPath ;
2016-08-22 19:21:52 +00:00
2016-10-04 21:59:04 +00:00
MigrationReport migrationReport = null ;
2016-09-26 21:16:17 +00:00
foreach ( var project in projectsToMigrate )
{
var projectDirectory = Path . GetDirectoryName ( project ) ;
var outputDirectory = projectDirectory ;
2016-10-25 05:46:15 +00:00
var migrationSettings = new MigrationSettings (
projectDirectory ,
outputDirectory ,
msBuildTemplatePath ,
2016-12-07 21:49:15 +00:00
_xprojFilePath ,
null ,
_slnFile ) ;
2016-10-04 21:59:04 +00:00
var projectMigrationReport = new ProjectMigrator ( ) . Migrate ( migrationSettings , _skipProjectReferences ) ;
if ( migrationReport = = null )
{
migrationReport = projectMigrationReport ;
}
else
{
migrationReport = migrationReport . Merge ( projectMigrationReport ) ;
}
}
WriteReport ( migrationReport ) ;
2016-10-25 05:46:15 +00:00
temporaryDotnetNewProject . Clean ( ) ;
2016-12-07 21:49:15 +00:00
UpdateSolutionFile ( migrationReport ) ;
2016-10-29 08:58:37 +00:00
MoveProjectJsonArtifactsToBackup ( migrationReport ) ;
2016-10-04 21:59:04 +00:00
return migrationReport . FailedProjectsCount ;
}
2016-12-07 21:49:15 +00:00
private void UpdateSolutionFile ( MigrationReport migrationReport )
{
if ( _slnFile = = null )
{
return ;
}
if ( migrationReport . FailedProjectsCount > 0 )
{
return ;
}
foreach ( var project in _slnFile . Projects )
{
var projectDirectory = Path . Combine (
_slnFile . BaseDirectory . FullPath ,
Path . GetDirectoryName ( project . FilePath ) ) ;
var csprojFiles = new DirectoryInfo ( projectDirectory )
. EnumerateFiles ( )
. Where ( f = > f . Extension = = ".csproj" ) ;
if ( csprojFiles . Count ( ) = = 1 )
{
project . FilePath = Path . Combine ( Path . GetDirectoryName ( project . FilePath ) , csprojFiles . First ( ) . Name ) ;
project . TypeGuid = CSharpProjectTypeGuid ;
}
}
_slnFile . Write ( Path . Combine ( _slnFile . BaseDirectory . FullPath ,
Path . GetFileName ( _slnFile . FileName ) ) ) ;
}
2016-10-29 08:58:37 +00:00
private void MoveProjectJsonArtifactsToBackup ( MigrationReport migrationReport )
{
if ( _skipBackup )
{
return ;
}
if ( migrationReport . FailedProjectsCount > 0 )
{
return ;
}
BackupGlobalJson ( ) ;
BackupProjects ( migrationReport ) ;
}
private void BackupGlobalJson ( )
{
_backupDirectory . Create ( ) ;
var globalJson = Path . Combine ( _workspaceDirectory . FullName , GlobalSettings . FileName ) ;
if ( File . Exists ( globalJson ) )
{
File . Move ( globalJson , Path . Combine ( _backupDirectory . FullName , GlobalSettings . FileName ) ) ;
}
}
private void BackupProjects ( MigrationReport migrationReport )
2016-10-04 21:59:04 +00:00
{
2016-10-29 08:58:37 +00:00
foreach ( var report in migrationReport . ProjectMigrationReports )
{
MigrateProject ( report ) ;
}
}
private void MigrateProject ( ProjectMigrationReport report )
{
var projectDirectory = PathUtility . EnsureTrailingSlash ( report . ProjectDirectory ) ;
var relativeDirectory = PathUtility . GetRelativePath ( PathUtility . EnsureTrailingSlash ( _workspaceDirectory . FullName ) , projectDirectory ) ;
var targetDirectory = String . IsNullOrEmpty ( relativeDirectory )
? _backupDirectory . FullName
: Path . Combine ( _backupDirectory . FullName , relativeDirectory ) ;
PathUtility . EnsureDirectory ( PathUtility . EnsureTrailingSlash ( targetDirectory ) ) ;
2016-10-04 21:59:04 +00:00
2016-10-29 08:58:37 +00:00
var movableFiles = new DirectoryInfo ( projectDirectory )
. EnumerateFiles ( )
2016-11-23 17:44:38 +00:00
. Where ( f = > f . Name = = Project . FileName
| | f . Extension = = ".xproj"
| | f . FullName . EndsWith ( ".xproj.user" )
| | f . FullName . EndsWith ( ".lock.json" ) ) ;
2016-10-29 08:58:37 +00:00
foreach ( var movableFile in movableFiles )
{
movableFile . MoveTo ( Path . Combine ( targetDirectory , movableFile . Name ) ) ;
}
}
private void WriteReport ( MigrationReport migrationReport )
{
2016-10-04 21:59:04 +00:00
if ( ! string . IsNullOrEmpty ( _reportFile ) )
{
using ( var outputTextWriter = GetReportFileOutputTextWriter ( ) )
{
outputTextWriter . Write ( GetReportContent ( migrationReport ) ) ;
}
}
WriteReportToStdOut ( migrationReport ) ;
}
private void WriteReportToStdOut ( MigrationReport migrationReport )
{
StringBuilder sb = new StringBuilder ( ) ;
foreach ( var projectMigrationReport in migrationReport . ProjectMigrationReports )
{
var errorContent = GetProjectReportErrorContent ( projectMigrationReport , colored : true ) ;
var successContent = GetProjectReportSuccessContent ( projectMigrationReport , colored : true ) ;
if ( ! string . IsNullOrEmpty ( errorContent ) )
{
Reporter . Error . WriteLine ( errorContent ) ;
}
else
{
Reporter . Output . WriteLine ( successContent ) ;
}
}
Reporter . Output . WriteLine ( GetReportSummary ( migrationReport ) ) ;
}
private string GetReportContent ( MigrationReport migrationReport , bool colored = false )
{
if ( _reportFormatJson )
{
return Newtonsoft . Json . JsonConvert . SerializeObject ( migrationReport ) ;
}
StringBuilder sb = new StringBuilder ( ) ;
foreach ( var projectMigrationReport in migrationReport . ProjectMigrationReports )
{
var errorContent = GetProjectReportErrorContent ( projectMigrationReport , colored : colored ) ;
var successContent = GetProjectReportSuccessContent ( projectMigrationReport , colored : colored ) ;
if ( ! string . IsNullOrEmpty ( errorContent ) )
{
sb . AppendLine ( errorContent ) ;
}
else
{
sb . AppendLine ( successContent ) ;
}
}
sb . AppendLine ( GetReportSummary ( migrationReport ) ) ;
return sb . ToString ( ) ;
}
private string GetReportSummary ( MigrationReport migrationReport )
{
StringBuilder sb = new StringBuilder ( ) ;
sb . AppendLine ( "Summary" ) ;
sb . AppendLine ( $"Total Projects: {migrationReport.MigratedProjectsCount}" ) ;
sb . AppendLine ( $"Succeeded Projects: {migrationReport.SucceededProjectsCount}" ) ;
sb . AppendLine ( $"Failed Projects: {migrationReport.FailedProjectsCount}" ) ;
return sb . ToString ( ) ;
}
private string GetProjectReportSuccessContent ( ProjectMigrationReport projectMigrationReport , bool colored )
{
Func < string , string > GreenIfColored = ( str ) = > colored ? str . Green ( ) : str ;
return GreenIfColored ( $"Project {projectMigrationReport.ProjectName} migration succeeded ({projectMigrationReport.ProjectDirectory})" ) ;
}
private string GetProjectReportErrorContent ( ProjectMigrationReport projectMigrationReport , bool colored )
{
StringBuilder sb = new StringBuilder ( ) ;
Func < string , string > RedIfColored = ( str ) = > colored ? str . Red ( ) : str ;
if ( projectMigrationReport . Errors . Any ( ) )
{
sb . AppendLine ( RedIfColored ( $"Project {projectMigrationReport.ProjectName} migration failed ({projectMigrationReport.ProjectDirectory})" ) ) ;
foreach ( var error in projectMigrationReport . Errors . Select ( e = > e . GetFormattedErrorMessage ( ) ) )
{
sb . AppendLine ( RedIfColored ( error ) ) ;
}
2016-09-26 21:16:17 +00:00
}
2016-08-23 20:50:05 +00:00
2016-10-04 21:59:04 +00:00
return sb . ToString ( ) ;
}
private TextWriter GetReportFileOutputTextWriter ( )
{
return File . CreateText ( _reportFile ) ;
2016-08-22 19:21:52 +00:00
}
2016-09-26 21:16:17 +00:00
private IEnumerable < string > GetProjectsToMigrate ( string projectArg )
{
2016-10-04 17:39:55 +00:00
IEnumerable < string > projects = null ;
2016-10-29 08:58:37 +00:00
if ( projectArg . EndsWith ( Project . FileName , StringComparison . OrdinalIgnoreCase ) )
2016-10-04 17:39:55 +00:00
{
projects = Enumerable . Repeat ( projectArg , 1 ) ;
}
else if ( projectArg . EndsWith ( GlobalSettings . FileName , StringComparison . OrdinalIgnoreCase ) )
2016-09-26 21:16:17 +00:00
{
2016-12-07 21:49:15 +00:00
projects = GetProjectsFromGlobalJson ( projectArg ) ;
2016-10-29 08:58:37 +00:00
2016-10-10 22:38:25 +00:00
if ( ! projects . Any ( ) )
{
throw new Exception ( "Unable to find any projects in global.json" ) ;
}
2016-09-26 21:16:17 +00:00
}
2016-12-07 21:49:15 +00:00
else if ( File . Exists ( projectArg ) & &
string . Equals ( Path . GetExtension ( projectArg ) , ".sln" , StringComparison . OrdinalIgnoreCase ) )
{
projects = GetProjectsFromSolution ( projectArg ) ;
if ( ! projects . Any ( ) )
{
throw new Exception ( $"Unable to find any projects in {projectArg}" ) ;
}
}
2016-09-26 21:16:17 +00:00
else if ( Directory . Exists ( projectArg ) )
{
2016-10-29 08:58:37 +00:00
projects = Directory . EnumerateFiles ( projectArg , Project . FileName , SearchOption . AllDirectories ) ;
2016-10-10 22:38:25 +00:00
if ( ! projects . Any ( ) )
{
throw new Exception ( $"No project.json file found in '{projectArg}'" ) ;
}
2016-09-26 21:16:17 +00:00
}
else
{
2016-12-07 21:49:15 +00:00
throw new Exception ( $"Invalid project argument - '{projectArg}' is not a project.json, global.json, or solution.sln file and a directory named '{projectArg}' doesn't exist." ) ;
2016-10-04 17:39:55 +00:00
}
2016-10-10 22:38:25 +00:00
2016-10-04 17:39:55 +00:00
foreach ( var project in projects )
{
yield return GetProjectJsonPath ( project ) ;
2016-09-26 21:16:17 +00:00
}
}
2016-08-22 19:21:52 +00:00
private void EnsureNotNull ( string variable , string message )
{
if ( variable = = null )
{
throw new Exception ( message ) ;
}
}
private string GetProjectJsonPath ( string projectJson )
{
2016-08-23 20:50:05 +00:00
projectJson = ProjectPathHelper . NormalizeProjectFilePath ( projectJson ) ;
2016-08-22 19:21:52 +00:00
if ( File . Exists ( projectJson ) )
{
return projectJson ;
}
throw new Exception ( $"Unable to find project file at {projectJson}" ) ;
}
2016-10-04 17:39:55 +00:00
private IEnumerable < string > GetProjectsFromGlobalJson ( string globalJson )
{
if ( ! File . Exists ( globalJson ) )
{
throw new Exception ( $"Unable to find global settings file at {globalJson}" ) ;
}
var searchPaths = ProjectDependencyFinder . GetGlobalPaths ( Path . GetDirectoryName ( globalJson ) ) ;
foreach ( var searchPath in searchPaths )
{
var directory = new DirectoryInfo ( searchPath ) ;
if ( ! directory . Exists )
{
continue ;
}
foreach ( var projectDirectory in directory . EnumerateDirectories ( ) )
{
2016-12-07 21:49:15 +00:00
var projectFilePath = Path . Combine ( projectDirectory . FullName , Project . FileName ) ;
2016-10-04 17:39:55 +00:00
if ( File . Exists ( projectFilePath ) )
{
yield return projectFilePath ;
}
}
}
}
2016-12-07 21:49:15 +00:00
private IEnumerable < string > GetProjectsFromSolution ( string slnPath )
{
if ( ! File . Exists ( slnPath ) )
{
throw new Exception ( $"Unable to find the solution file at {slnPath}" ) ;
}
_slnFile = new SlnFile ( ) ;
_slnFile . Read ( slnPath ) ;
foreach ( var project in _slnFile . Projects )
{
var projectFilePath = Path . Combine ( _slnFile . BaseDirectory . FullPath ,
Path . Combine ( Path . GetDirectoryName ( project . FilePath ) , Project . FileName ) ) ;
if ( File . Exists ( projectFilePath ) )
{
yield return projectFilePath ;
}
}
}
2016-08-22 19:21:52 +00:00
}
}