2016-11-10 20:05:19 -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.Collections.Generic ;
using Microsoft.DotNet.Cli.CommandLine ;
using Microsoft.DotNet.Cli.Utils ;
using System ;
using System.IO ;
using System.Linq ;
using Microsoft.Build.Construction ;
2016-11-16 15:49:25 -08:00
using Microsoft.Build.Evaluation ;
2016-11-10 20:05:19 -08:00
2016-11-15 16:41:29 -08:00
namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
2016-11-10 20:05:19 -08:00
{
2016-11-15 16:41:29 -08:00
public class AddProjectToProjectReferenceCommand
2016-11-10 20:05:19 -08:00
{
public static int Run ( string [ ] args )
{
DebugHelper . HandleDebugSwitch ( ref args ) ;
2016-11-15 16:41:29 -08:00
CommandLineApplication app = new CommandLineApplication ( throwOnUnexpectedArg : false )
{
Name = "dotnet add p2p" ,
FullName = ".NET Add Project to Project (p2p) reference Command" ,
2016-11-16 15:49:25 -08:00
Description = "Command to add project to project (p2p) reference" ,
AllowArgumentSeparator = true ,
ArgumentSeparatorHelpText = "Project to project references to add"
2016-11-15 16:41:29 -08:00
} ;
2016-11-16 15:49:25 -08:00
2016-11-15 16:41:29 -08:00
app . HelpOption ( "-h|--help" ) ;
CommandArgument projectArgument = app . Argument ( "<PROJECT>" ,
2016-11-16 15:49:25 -08:00
"The project file to modify. If a project file is not specified," +
2016-11-15 16:41:29 -08:00
" it searches the current working directory for an MSBuild file that has a file extension that ends in `proj` and uses that file." ) ;
CommandOption frameworkOption = app . Option ( "-f|--framework <FRAMEWORK>" , "Add reference only when targetting a specific framework" , CommandOptionType . SingleValue ) ;
app . OnExecute ( ( ) = > {
2016-11-16 15:49:25 -08:00
if ( projectArgument . Value = = null )
{
throw new GracefulException ( "Argument <Project> is required." ) ;
}
ProjectRootElement project = File . Exists ( projectArgument . Value ) ?
2016-11-15 16:41:29 -08:00
GetProjectFromFileOrThrow ( projectArgument . Value ) :
2016-11-16 15:49:25 -08:00
GetProjectFromDirectoryOrThrow ( projectArgument . Value ) ;
2016-11-15 16:41:29 -08:00
2016-11-16 15:49:25 -08:00
if ( app . RemainingArguments . Count = = 0 )
2016-11-15 16:41:29 -08:00
{
throw new GracefulException ( "You must specify at least one reference to add." ) ;
}
2016-11-21 11:38:25 -08:00
int numberOfAddedReferences = AddProjectToProjectReference (
project ,
frameworkOption . Value ( ) ,
app . RemainingArguments ) ;
2016-11-15 16:41:29 -08:00
2016-11-16 15:49:25 -08:00
if ( numberOfAddedReferences ! = 0 )
{
project . Save ( ) ;
}
2016-11-15 16:41:29 -08:00
return 0 ;
} ) ;
try
{
return app . Execute ( args ) ;
}
catch ( GracefulException e )
{
2016-11-16 15:49:25 -08:00
Reporter . Error . WriteLine ( e . Message . Red ( ) ) ;
app . ShowHelp ( ) ;
2016-11-15 16:41:29 -08:00
return 1 ;
}
}
// There is ProjectRootElement.TryOpen but it does not work as expected
// I.e. it returns null for some valid projects
2016-11-21 11:38:25 -08:00
internal static ProjectRootElement TryOpenProject ( string filename )
2016-11-15 16:41:29 -08:00
{
try
{
2016-11-16 15:49:25 -08:00
return ProjectRootElement . Open ( filename , new ProjectCollection ( ) , preserveFormatting : true ) ;
2016-11-15 16:41:29 -08:00
}
catch ( Microsoft . Build . Exceptions . InvalidProjectFileException )
{
return null ;
}
}
2016-11-21 11:38:25 -08:00
internal static ProjectRootElement GetProjectFromFileOrThrow ( string filename )
2016-11-15 16:41:29 -08:00
{
if ( ! File . Exists ( filename ) )
{
throw new GracefulException ( $"Provided project `{filename}` does not exist." ) ;
}
var project = TryOpenProject ( filename ) ;
if ( project = = null )
{
2016-11-16 15:49:25 -08:00
throw new GracefulException ( $"Invalid project `{filename}`." ) ;
2016-11-15 16:41:29 -08:00
}
return project ;
}
2016-11-16 15:49:25 -08:00
// TODO: improve errors
2016-11-21 11:38:25 -08:00
internal static ProjectRootElement GetProjectFromDirectoryOrThrow ( string directory )
2016-11-15 16:41:29 -08:00
{
2016-11-16 15:49:25 -08:00
DirectoryInfo dir ;
try
2016-11-15 16:41:29 -08:00
{
2016-11-16 15:49:25 -08:00
dir = new DirectoryInfo ( directory ) ;
2016-11-15 16:41:29 -08:00
}
2016-11-16 15:49:25 -08:00
catch ( ArgumentException )
2016-11-15 16:41:29 -08:00
{
2016-11-16 15:49:25 -08:00
throw new GracefulException ( $"Could not find project or directory `{directory}`." ) ;
2016-11-15 16:41:29 -08:00
}
2016-11-16 15:49:25 -08:00
if ( ! dir . Exists )
2016-11-15 16:41:29 -08:00
{
2016-11-16 15:49:25 -08:00
throw new GracefulException ( $"Could not find project or directory `{directory}`." ) ;
2016-11-15 16:41:29 -08:00
}
2016-11-16 15:49:25 -08:00
FileInfo [ ] files = dir . GetFiles ( "*proj" ) ;
if ( files . Length = = 0 )
2016-11-15 16:41:29 -08:00
{
2016-11-16 15:49:25 -08:00
throw new GracefulException ( $"Could not find any project in `{directory}`." ) ;
2016-11-15 16:41:29 -08:00
}
2016-11-16 15:49:25 -08:00
if ( files . Length > 1 )
2016-11-15 16:41:29 -08:00
{
2016-11-21 11:38:25 -08:00
throw new GracefulException ( "Found more than one project in the current directory. Please specify which one to use." ) ;
2016-11-15 16:41:29 -08:00
}
2016-11-16 15:49:25 -08:00
FileInfo projectFile = files . First ( ) ;
2016-11-15 16:41:29 -08:00
2016-11-16 15:49:25 -08:00
if ( ! projectFile . Exists )
2016-11-15 16:41:29 -08:00
{
2016-11-16 15:49:25 -08:00
throw new GracefulException ( $"Could not find any project in `{directory}`." ) ;
2016-11-15 16:41:29 -08:00
}
2016-11-16 15:49:25 -08:00
var ret = TryOpenProject ( projectFile . FullName ) ;
if ( ret = = null )
2016-11-16 14:35:48 -08:00
{
2016-11-16 15:49:25 -08:00
throw new GracefulException ( $"Found a project `{projectFile.FullName}` but it is invalid." ) ;
2016-11-16 14:35:48 -08:00
}
2016-11-15 16:41:29 -08:00
return ret ;
}
2016-11-21 11:38:25 -08:00
internal static int AddProjectToProjectReference ( ProjectRootElement root , string framework , IEnumerable < string > refs )
2016-11-15 16:41:29 -08:00
{
2016-11-16 15:49:25 -08:00
int numberOfAddedReferences = 0 ;
2016-11-15 16:41:29 -08:00
const string ProjectItemElementType = "ProjectReference" ;
ProjectItemGroupElement ig = null ;
foreach ( var @ref in refs )
{
2016-11-16 15:49:25 -08:00
if ( root . HasExistingItemWithCondition ( framework , @ref ) )
2016-11-15 16:41:29 -08:00
{
2016-11-16 15:49:25 -08:00
Reporter . Output . WriteLine ( $"Project already has a reference to `{@ref}`." ) ;
2016-11-15 16:41:29 -08:00
continue ;
}
2016-11-16 15:49:25 -08:00
numberOfAddedReferences + + ;
ig = ig ? ? root . FindUniformOrCreateItemGroupWithCondition ( ProjectItemElementType , framework ) ;
2016-11-15 16:41:29 -08:00
ig . AppendChild ( root . CreateItemElement ( ProjectItemElementType , @ref ) ) ;
2016-11-16 15:49:25 -08:00
Reporter . Output . WriteLine ( $"Reference `{@ref}` added to the project." ) ;
2016-11-15 16:41:29 -08:00
}
2016-11-16 15:49:25 -08:00
return numberOfAddedReferences ;
2016-11-15 16:41:29 -08:00
}
2016-11-10 20:05:19 -08:00
}
}