Improve the help text (#5002)
* WIP Improve help text * Improve help text * Address PR comments * Address PR comments * Address PR comments
This commit is contained in:
parent
148351c12d
commit
76722fd403
20 changed files with 486 additions and 640 deletions
|
@ -48,5 +48,7 @@ namespace Microsoft.DotNet.Cli.Utils
|
|||
};
|
||||
|
||||
public static readonly string MSBUILD_EXE_PATH = "MSBUILD_EXE_PATH";
|
||||
|
||||
public static readonly string ProjectOrSolutionArgumentName = "<PROJECT_OR_SOLUTION>";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,14 @@ namespace Microsoft.DotNet.Cli.CommandLine
|
|||
{
|
||||
internal class CommandLineApplication
|
||||
{
|
||||
private enum ParseOptionResult
|
||||
{
|
||||
Succeeded,
|
||||
ShowHelp,
|
||||
ShowVersion,
|
||||
UnexpectedArgs,
|
||||
}
|
||||
|
||||
// Indicates whether the parser should throw an exception when it runs into an unexpected argument.
|
||||
// If this field is set to false, the parser will stop parsing when it sees an unexpected argument, and all
|
||||
// remaining arguments, including the first unexpected argument, will be stored in RemainingArguments property.
|
||||
|
@ -45,8 +53,14 @@ namespace Microsoft.DotNet.Cli.CommandLine
|
|||
public List<CommandLineApplication> Commands { get; private set; }
|
||||
public bool HandleResponseFiles { get; set; }
|
||||
public bool AllowArgumentSeparator { get; set; }
|
||||
public bool HandleRemainingArguments { get; set; }
|
||||
public string ArgumentSeparatorHelpText { get; set; }
|
||||
|
||||
public CommandLineApplication Command(string name, bool throwOnUnexpectedArg = true)
|
||||
{
|
||||
return Command(name, _ => { }, throwOnUnexpectedArg);
|
||||
}
|
||||
|
||||
public CommandLineApplication Command(string name, Action<CommandLineApplication> configuration,
|
||||
bool throwOnUnexpectedArg = true)
|
||||
{
|
||||
|
@ -103,7 +117,6 @@ namespace Microsoft.DotNet.Cli.CommandLine
|
|||
public int Execute(params string[] args)
|
||||
{
|
||||
CommandLineApplication command = this;
|
||||
CommandOption option = null;
|
||||
IEnumerator<CommandArgument> arguments = null;
|
||||
|
||||
if (HandleResponseFiles)
|
||||
|
@ -114,172 +127,159 @@ namespace Microsoft.DotNet.Cli.CommandLine
|
|||
for (var index = 0; index < args.Length; index++)
|
||||
{
|
||||
var arg = args[index];
|
||||
var processed = false;
|
||||
if (!processed && option == null)
|
||||
|
||||
bool isLongOption = arg.StartsWith("--");
|
||||
if (isLongOption || arg.StartsWith("-"))
|
||||
{
|
||||
string[] longOption = null;
|
||||
string[] shortOption = null;
|
||||
|
||||
if (arg.StartsWith("--"))
|
||||
CommandOption option;
|
||||
var result = ParseOption(isLongOption, command, args, ref index, out option);
|
||||
if (result == ParseOptionResult.ShowHelp)
|
||||
{
|
||||
longOption = arg.Substring(2).Split(new[] { ':', '=' }, 2);
|
||||
command.ShowHelp();
|
||||
return 0;
|
||||
}
|
||||
else if (arg.StartsWith("-"))
|
||||
else if (result == ParseOptionResult.ShowVersion)
|
||||
{
|
||||
shortOption = arg.Substring(1).Split(new[] { ':', '=' }, 2);
|
||||
command.ShowVersion();
|
||||
return 0;
|
||||
}
|
||||
if (longOption != null)
|
||||
else if (result == ParseOptionResult.UnexpectedArgs)
|
||||
{
|
||||
processed = true;
|
||||
string longOptionName = longOption[0];
|
||||
option = command.Options.SingleOrDefault(opt => string.Equals(opt.LongName, longOptionName, StringComparison.Ordinal));
|
||||
|
||||
if (option == null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(longOptionName) && !command._throwOnUnexpectedArg && AllowArgumentSeparator)
|
||||
{
|
||||
// a stand-alone "--" is the argument separator, so skip it and
|
||||
// handle the rest of the args as unexpected args
|
||||
index++;
|
||||
}
|
||||
|
||||
HandleUnexpectedArg(command, args, index, argTypeName: "option");
|
||||
break;
|
||||
}
|
||||
|
||||
// If we find a help/version option, show information and stop parsing
|
||||
if (command.OptionHelp == option)
|
||||
{
|
||||
command.ShowHelp();
|
||||
return 0;
|
||||
}
|
||||
else if (command.OptionVersion == option)
|
||||
{
|
||||
command.ShowVersion();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (longOption.Length == 2)
|
||||
{
|
||||
if (!option.TryParse(longOption[1]))
|
||||
{
|
||||
command.ShowHint();
|
||||
throw new CommandParsingException(command, $"Unexpected value '{longOption[1]}' for option '{option.LongName}'");
|
||||
}
|
||||
option = null;
|
||||
}
|
||||
else if (option.OptionType == CommandOptionType.NoValue || option.OptionType == CommandOptionType.BoolValue)
|
||||
{
|
||||
// No value is needed for this option
|
||||
option.TryParse(null);
|
||||
option = null;
|
||||
}
|
||||
}
|
||||
if (shortOption != null)
|
||||
{
|
||||
processed = true;
|
||||
option = command.Options.SingleOrDefault(opt => string.Equals(opt.ShortName, shortOption[0], StringComparison.Ordinal));
|
||||
|
||||
// If not a short option, try symbol option
|
||||
if (option == null)
|
||||
{
|
||||
option = command.Options.SingleOrDefault(opt => string.Equals(opt.SymbolName, shortOption[0], StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
if (option == null)
|
||||
{
|
||||
HandleUnexpectedArg(command, args, index, argTypeName: "option");
|
||||
break;
|
||||
}
|
||||
|
||||
// If we find a help/version option, show information and stop parsing
|
||||
if (command.OptionHelp == option)
|
||||
{
|
||||
command.ShowHelp();
|
||||
return 0;
|
||||
}
|
||||
else if (command.OptionVersion == option)
|
||||
{
|
||||
command.ShowVersion();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (shortOption.Length == 2)
|
||||
{
|
||||
if (!option.TryParse(shortOption[1]))
|
||||
{
|
||||
command.ShowHint();
|
||||
throw new CommandParsingException(command, $"Unexpected value '{shortOption[1]}' for option '{option.LongName}'");
|
||||
}
|
||||
option = null;
|
||||
}
|
||||
else if (option.OptionType == CommandOptionType.NoValue || option.OptionType == CommandOptionType.BoolValue)
|
||||
{
|
||||
// No value is needed for this option
|
||||
option.TryParse(null);
|
||||
option = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!processed && option != null)
|
||||
else
|
||||
{
|
||||
processed = true;
|
||||
if (!option.TryParse(arg))
|
||||
var subcommand = ParseSubCommand(arg, command);
|
||||
if (subcommand != null)
|
||||
{
|
||||
command.ShowHint();
|
||||
throw new CommandParsingException(command, $"Unexpected value '{arg}' for option '{option.LongName}'");
|
||||
command = subcommand;
|
||||
}
|
||||
option = null;
|
||||
}
|
||||
|
||||
if (!processed && arguments == null)
|
||||
{
|
||||
var currentCommand = command;
|
||||
foreach (var subcommand in command.Commands)
|
||||
else
|
||||
{
|
||||
if (string.Equals(subcommand.Name, arg, StringComparison.OrdinalIgnoreCase))
|
||||
if (arguments == null)
|
||||
{
|
||||
processed = true;
|
||||
command = subcommand;
|
||||
arguments = new CommandArgumentEnumerator(command.Arguments.GetEnumerator());
|
||||
}
|
||||
|
||||
if (arguments.MoveNext())
|
||||
{
|
||||
arguments.Current.Values.Add(arg);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleUnexpectedArg(command, args, index, argTypeName: "command or argument");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we detect a subcommand
|
||||
if (command != currentCommand)
|
||||
{
|
||||
processed = true;
|
||||
}
|
||||
}
|
||||
if (!processed)
|
||||
{
|
||||
if (arguments == null)
|
||||
{
|
||||
arguments = new CommandArgumentEnumerator(command.Arguments.GetEnumerator());
|
||||
}
|
||||
if (arguments.MoveNext())
|
||||
{
|
||||
processed = true;
|
||||
arguments.Current.Values.Add(arg);
|
||||
}
|
||||
}
|
||||
if (!processed)
|
||||
{
|
||||
HandleUnexpectedArg(command, args, index, argTypeName: "command or argument");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (option != null)
|
||||
{
|
||||
command.ShowHint();
|
||||
throw new CommandParsingException(command, $"Missing value for option '{option.LongName}'");
|
||||
}
|
||||
|
||||
return command.Invoke();
|
||||
}
|
||||
|
||||
private ParseOptionResult ParseOption(
|
||||
bool isLongOption,
|
||||
CommandLineApplication command,
|
||||
string[] args,
|
||||
ref int index,
|
||||
out CommandOption option)
|
||||
{
|
||||
option = null;
|
||||
ParseOptionResult result = ParseOptionResult.Succeeded;
|
||||
var arg = args[index];
|
||||
|
||||
int optionPrefixLength = isLongOption ? 2 : 1;
|
||||
string[] optionComponents = arg.Substring(optionPrefixLength).Split(new[] { ':', '=' }, 2);
|
||||
string optionName = optionComponents[0];
|
||||
|
||||
if (isLongOption)
|
||||
{
|
||||
option = command.Options.SingleOrDefault(
|
||||
opt => string.Equals(opt.LongName, optionName, StringComparison.Ordinal));
|
||||
}
|
||||
else
|
||||
{
|
||||
option = command.Options.SingleOrDefault(
|
||||
opt => string.Equals(opt.ShortName, optionName, StringComparison.Ordinal));
|
||||
|
||||
if (option == null)
|
||||
{
|
||||
option = command.Options.SingleOrDefault(
|
||||
opt => string.Equals(opt.SymbolName, optionName, StringComparison.Ordinal));
|
||||
}
|
||||
}
|
||||
|
||||
if (option == null)
|
||||
{
|
||||
if (isLongOption && string.IsNullOrEmpty(optionName) &&
|
||||
!command._throwOnUnexpectedArg && AllowArgumentSeparator)
|
||||
{
|
||||
// a stand-alone "--" is the argument separator, so skip it and
|
||||
// handle the rest of the args as unexpected args
|
||||
index++;
|
||||
}
|
||||
|
||||
HandleUnexpectedArg(command, args, index, argTypeName: "option");
|
||||
result = ParseOptionResult.UnexpectedArgs;
|
||||
}
|
||||
else if (command.OptionHelp == option)
|
||||
{
|
||||
result = ParseOptionResult.ShowHelp;
|
||||
}
|
||||
else if (command.OptionVersion == option)
|
||||
{
|
||||
result = ParseOptionResult.ShowVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (optionComponents.Length == 2)
|
||||
{
|
||||
if (!option.TryParse(optionComponents[1]))
|
||||
{
|
||||
command.ShowHint();
|
||||
throw new CommandParsingException(command,
|
||||
$"Unexpected value '{optionComponents[1]}' for option '{optionName}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (option.OptionType == CommandOptionType.NoValue ||
|
||||
option.OptionType == CommandOptionType.BoolValue)
|
||||
{
|
||||
// No value is needed for this option
|
||||
option.TryParse(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
index++;
|
||||
arg = args[index];
|
||||
if (!option.TryParse(arg))
|
||||
{
|
||||
command.ShowHint();
|
||||
throw new CommandParsingException(command, $"Unexpected value '{arg}' for option '{optionName}'");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CommandLineApplication ParseSubCommand(string arg, CommandLineApplication command)
|
||||
{
|
||||
foreach (var subcommand in command.Commands)
|
||||
{
|
||||
if (string.Equals(subcommand.Name, arg, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return subcommand;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Helper method that adds a help option
|
||||
public CommandOption HelpOption(string template)
|
||||
{
|
||||
|
@ -331,10 +331,19 @@ namespace Microsoft.DotNet.Cli.CommandLine
|
|||
public void ShowHelp(string commandName = null)
|
||||
{
|
||||
var headerBuilder = new StringBuilder("Usage:");
|
||||
var usagePrefixLength = headerBuilder.Length;
|
||||
for (var cmd = this; cmd != null; cmd = cmd.Parent)
|
||||
{
|
||||
cmd.IsShowingInformation = true;
|
||||
headerBuilder.Insert(6, string.Format(" {0}", cmd.Name));
|
||||
if (cmd != this && cmd.Arguments.Any())
|
||||
{
|
||||
var args = string.Join(" ", cmd.Arguments.Select(arg => arg.Name));
|
||||
headerBuilder.Insert(usagePrefixLength, string.Format(" {0} {1}", cmd.Name, args));
|
||||
}
|
||||
else
|
||||
{
|
||||
headerBuilder.Insert(usagePrefixLength, string.Format(" {0}", cmd.Name));
|
||||
}
|
||||
}
|
||||
|
||||
CommandLineApplication target;
|
||||
|
@ -364,18 +373,39 @@ namespace Microsoft.DotNet.Cli.CommandLine
|
|||
var argumentsBuilder = new StringBuilder();
|
||||
var argumentSeparatorBuilder = new StringBuilder();
|
||||
|
||||
if (target.Arguments.Any())
|
||||
int maxArgLen = 0;
|
||||
for (var cmd = target; cmd != null; cmd = cmd.Parent)
|
||||
{
|
||||
headerBuilder.Append(" [arguments]");
|
||||
|
||||
argumentsBuilder.AppendLine();
|
||||
argumentsBuilder.AppendLine("Arguments:");
|
||||
var maxArgLen = MaxArgumentLength(target.Arguments);
|
||||
var outputFormat = string.Format(" {{0, -{0}}}{{1}}", maxArgLen + 2);
|
||||
foreach (var arg in target.Arguments)
|
||||
if (cmd.Arguments.Any())
|
||||
{
|
||||
argumentsBuilder.AppendFormat(outputFormat, arg.Name, arg.Description);
|
||||
argumentsBuilder.AppendLine();
|
||||
if (cmd == target)
|
||||
{
|
||||
headerBuilder.Append(" [arguments]");
|
||||
}
|
||||
|
||||
if (argumentsBuilder.Length == 0)
|
||||
{
|
||||
argumentsBuilder.AppendLine();
|
||||
argumentsBuilder.AppendLine("Arguments:");
|
||||
}
|
||||
|
||||
maxArgLen = Math.Max(maxArgLen, MaxArgumentLength(cmd.Arguments));
|
||||
}
|
||||
}
|
||||
|
||||
for (var cmd = target; cmd != null; cmd = cmd.Parent)
|
||||
{
|
||||
if (cmd.Arguments.Any())
|
||||
{
|
||||
var outputFormat = " {0}{1}";
|
||||
foreach (var arg in cmd.Arguments)
|
||||
{
|
||||
argumentsBuilder.AppendFormat(
|
||||
outputFormat,
|
||||
arg.Name.PadRight(maxArgLen + 2),
|
||||
arg.Description);
|
||||
argumentsBuilder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,9 +446,17 @@ namespace Microsoft.DotNet.Cli.CommandLine
|
|||
}
|
||||
}
|
||||
|
||||
if (target.AllowArgumentSeparator)
|
||||
if (target.AllowArgumentSeparator || target.HandleRemainingArguments)
|
||||
{
|
||||
headerBuilder.Append(" [[--] <arg>...]]");
|
||||
if (target.AllowArgumentSeparator)
|
||||
{
|
||||
headerBuilder.Append(" [[--] <arg>...]]");
|
||||
}
|
||||
else
|
||||
{
|
||||
headerBuilder.Append(" [args]");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(target.ArgumentSeparatorHelpText))
|
||||
{
|
||||
argumentSeparatorBuilder.AppendLine();
|
||||
|
|
|
@ -2,17 +2,9 @@
|
|||
{
|
||||
internal class CommonLocalizableStrings
|
||||
{
|
||||
public const string CouldNotFindAnyProjectInDirectory = "Could not find any project in `{0}`.";
|
||||
public const string MoreThanOneProjectInDirectory = "Found more than one project in `{0}`. Please specify which one to use.";
|
||||
public const string ProjectAlreadyHasAreference = "Project already has a reference to `{0}`.";
|
||||
public const string ProjectReferenceCouldNotBeFound = "Project reference `{0}` could not be found.";
|
||||
public const string ProjectReferenceRemoved = "Project reference `{0}` removed.";
|
||||
|
||||
// General purpose words
|
||||
public const string RequiredArgument = "Required argument";
|
||||
public const string Option = "Option";
|
||||
public const string Argument = "Argument";
|
||||
public const string Help = "Help";
|
||||
|
||||
// Project related
|
||||
public const string Project = "Project";
|
||||
|
@ -30,9 +22,7 @@
|
|||
public const string Library = "Library";
|
||||
public const string Program = "Program";
|
||||
public const string Application = "Application";
|
||||
public const string ReferenceDoesNotExist = "Reference {0} does not exist. If you still want to add it, please use --force option. Please note that this may have adverse effects on the project. ";
|
||||
public const string ReferenceAddedToTheProject = "Reference `{0}` added to the project.";
|
||||
|
||||
|
||||
// Verbs
|
||||
public const string Add = "Add";
|
||||
|
@ -101,9 +91,9 @@
|
|||
|
||||
// dotnet <verb>
|
||||
/// Project
|
||||
public const string CouldNotFindProjectIn = "Could not find any project in `{0}`.";
|
||||
public const string CouldNotFindAnyProjectInDirectory = "Could not find any project in `{0}`.";
|
||||
public const string CouldNotFindProjectOrDirectory = "Could not find project or directory `{0}`.";
|
||||
public const string FoundMoreThanOneProjectIn = "Found more than one project in `{0}`. Please specify which one to use.";
|
||||
public const string MoreThanOneProjectInDirectory = "Found more than one project in `{0}`. Please specify which one to use.";
|
||||
public const string FoundInvalidProject = "Found a project `{0}` but it is invalid.";
|
||||
public const string InvalidProject = "Invalid project `{0}`.";
|
||||
|
||||
|
@ -117,15 +107,15 @@
|
|||
public const string SolutionAlreadyContainsAProject = "Solution {0} already contains project {1}.";
|
||||
|
||||
/// add p2p
|
||||
public const string ReferenceDoesNotExistForce = "Reference {0} does not exist. If you still want to add it, please use --force option. Please note that this may have adverse effects on the project. ";
|
||||
public const string ReferenceDoesNotExist = "Reference {0} does not exist.";
|
||||
public const string ReferenceIsInvalid = "Reference `{0}` is invalid.";
|
||||
public const string SpecifyAtLeastOneReferenceToAdd = "You must specify at least one reference to add. Please run dotnet add --help for more information.";
|
||||
public const string SpecifyAtLeastOneReferenceToAdd = "You must specify at least one reference to add.";
|
||||
public const string ProjectAlreadyHasAReference = "Project {0} already has a reference `{1}`.";
|
||||
|
||||
/// add package
|
||||
public const string PackageReferenceDoesNotExist = "Package reference `{0}` does not exist.";
|
||||
public const string PackageReferenceIsInvalid = "Package reference `{0}` is invalid.";
|
||||
public const string SpecifyAtLeastOnePackageReferenceToAdd = "You must specify at least one reference to add. Please run dotnet add --help for more information.";
|
||||
public const string SpecifyAtLeastOnePackageReferenceToAdd = "You must specify at least one package to add.";
|
||||
public const string PackageReferenceAddedToTheProject = "Package reference `{0}` added to the project.";
|
||||
public const string ProjectAlreadyHasAPackageReference = "Project {0} already has a reference `{1}`.";
|
||||
public const string PleaseSpecifyVersion = "Please specify a version of the package.";
|
||||
|
@ -133,7 +123,7 @@
|
|||
/// add sln
|
||||
public const string ProjectDoesNotExist = "Project `{0}` does not exist.";
|
||||
public const string ProjectIsInvalid = "Project `{0}` is invalid.";
|
||||
public const string SpecifyAtLeastOneProjectToAdd = "You must specify at least one project to add. Please run dotnet add --help for more information.";
|
||||
public const string SpecifyAtLeastOneProjectToAdd = "You must specify at least one project to add.";
|
||||
public const string ProjectAddedToTheSolution = "Project `{0}` added to the solution.";
|
||||
public const string SolutionAlreadyHasAProject = "Solution {0} already contains project {1}.";
|
||||
|
||||
|
@ -162,6 +152,12 @@
|
|||
public const string NoReferencesFound = "There are no {0} references in project {1}. ;; {0} is the type of the item being requested (project, package, p2p) and {1} is the object operated on (a project file or a solution file). ";
|
||||
public const string NoProjectsFound = "No projects found in the solution.";
|
||||
|
||||
/// arguments
|
||||
public const string ArgumentsProjectOrSolutionDescription = "The project or solution to operation on. If a file is not specified, the current directory is searched.";
|
||||
|
||||
/// commands
|
||||
public const string CmdFramework = "FRAMEWORK";
|
||||
|
||||
/// update pkg
|
||||
public const string PleaseSpecifyNewVersion = "Please specify new version of the package.";
|
||||
public const string PleaseSpecifyWhichPackageToUpdate = "Please specify which package to update.";
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
// 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 Microsoft.DotNet.Cli.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.DotNet.Tools
|
||||
{
|
||||
public abstract class DispatchCommand
|
||||
{
|
||||
protected abstract string HelpText { get; }
|
||||
protected abstract Dictionary<string, Func<string[], int>> BuiltInCommands { get; }
|
||||
|
||||
public int Start(string[] args)
|
||||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
||||
if (args.Length == 0 || args[0] == "--help" || args[0] == "-h")
|
||||
{
|
||||
Reporter.Output.WriteLine(HelpText);
|
||||
return 0;
|
||||
}
|
||||
|
||||
string commandObject;
|
||||
string command;
|
||||
if (IsValidCommandName(args[0]))
|
||||
{
|
||||
command = args[0];
|
||||
commandObject = GetCurrentDirectoryWithDirSeparator();
|
||||
args = args.Skip(1).Prepend(commandObject).ToArray();
|
||||
}
|
||||
else if (args.Length == 1)
|
||||
{
|
||||
Reporter.Error.WriteLine(string.Format(CommonLocalizableStrings.RequiredArgumentNotPassed, "<command>").Red());
|
||||
Reporter.Output.WriteLine(HelpText);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
commandObject = args[0];
|
||||
command = args[1];
|
||||
|
||||
args = args.Skip(2).Prepend(commandObject).ToArray();
|
||||
}
|
||||
|
||||
Func<string[], int> builtin;
|
||||
if (BuiltInCommands.TryGetValue(command, out builtin))
|
||||
{
|
||||
return builtin(args);
|
||||
}
|
||||
|
||||
Reporter.Error.WriteLine(string.Format(CommonLocalizableStrings.RequiredArgumentIsInvalid, "<command>").Red());
|
||||
Reporter.Output.WriteLine(HelpText);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private bool IsValidCommandName(string s)
|
||||
{
|
||||
return BuiltInCommands.ContainsKey(s);
|
||||
}
|
||||
|
||||
private static string GetCurrentDirectoryWithDirSeparator()
|
||||
{
|
||||
string ret = Directory.GetCurrentDirectory();
|
||||
if (ret[ret.Length - 1] != Path.DirectorySeparatorChar)
|
||||
{
|
||||
ret += Path.DirectorySeparatorChar;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
57
src/dotnet/DotNetTopLevelCommandBase.cs
Normal file
57
src/dotnet/DotNetTopLevelCommandBase.cs
Normal file
|
@ -0,0 +1,57 @@
|
|||
// 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 Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.Tools;
|
||||
using Microsoft.DotNet.Tools.Add.ProjectToProjectReference;
|
||||
|
||||
namespace Microsoft.DotNet.Cli
|
||||
{
|
||||
public abstract class DotNetTopLevelCommandBase
|
||||
{
|
||||
protected abstract string CommandName { get; }
|
||||
protected abstract string FullCommandNameLocalized { get; }
|
||||
internal abstract List<Func<CommandLineApplication, CommandLineApplication>> SubCommands { get; }
|
||||
|
||||
public int RunCommand(string[] args)
|
||||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
||||
CommandLineApplication app = new CommandLineApplication(throwOnUnexpectedArg: true)
|
||||
{
|
||||
Name = $"dotnet {CommandName}",
|
||||
FullName = FullCommandNameLocalized,
|
||||
};
|
||||
|
||||
app.HelpOption("-h|--help");
|
||||
|
||||
app.Argument(
|
||||
Constants.ProjectOrSolutionArgumentName,
|
||||
CommonLocalizableStrings.ArgumentsProjectOrSolutionDescription);
|
||||
|
||||
foreach (var subCommandCreator in SubCommands)
|
||||
{
|
||||
subCommandCreator(app);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return app.Execute(args);
|
||||
}
|
||||
catch (GracefulException e)
|
||||
{
|
||||
Reporter.Error.WriteLine(e.Message.Red());
|
||||
app.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
catch (CommandParsingException e)
|
||||
{
|
||||
Reporter.Error.WriteLine(e.Message.Red());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,23 +3,5 @@ namespace Microsoft.DotNet.Tools.Add
|
|||
internal class LocalizableStrings
|
||||
{
|
||||
public const string NetAddCommand = ".NET Add Command";
|
||||
|
||||
public const string Usage = "Usage";
|
||||
|
||||
public const string Options = "Options";
|
||||
|
||||
public const string HelpDefinition = "Show help information";
|
||||
|
||||
public const string Arguments = "Arguments";
|
||||
|
||||
public const string ArgumentsObjectDefinition = "The object of the operation. If a project file is not specified, it defaults to the current directory.";
|
||||
|
||||
public const string ArgumentsCommandDefinition = "Command to be executed on <object>.";
|
||||
|
||||
public const string ArgsDefinition = "Any extra arguments passed to the command. Use `dotnet add <command> --help` to get help about these arguments.";
|
||||
|
||||
public const string Commands = "Commands";
|
||||
|
||||
public const string CommandP2PDefinition = "Add project to project (p2p) reference to a project";
|
||||
}
|
||||
}
|
|
@ -1,47 +1,28 @@
|
|||
// 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;
|
||||
using Microsoft.DotNet.ProjectJsonMigration;
|
||||
using NuGet.Frameworks;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.DotNet.Tools.Add.ProjectToProjectReference;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Add
|
||||
{
|
||||
public class AddCommand : DispatchCommand
|
||||
public class AddCommand : DotNetTopLevelCommandBase
|
||||
{
|
||||
protected override string HelpText => $@"{LocalizableStrings.NetAddCommand}
|
||||
|
||||
{LocalizableStrings.Usage}: dotnet add [options] <object> <command> [[--] <arg>...]]
|
||||
|
||||
{LocalizableStrings.Options}:
|
||||
-h|--help {LocalizableStrings.HelpDefinition}
|
||||
|
||||
{LocalizableStrings.Arguments}:
|
||||
<object> {LocalizableStrings.ArgumentsObjectDefinition}
|
||||
<command> {LocalizableStrings.ArgumentsCommandDefinition}
|
||||
|
||||
Args:
|
||||
{LocalizableStrings.ArgsDefinition}
|
||||
|
||||
{LocalizableStrings.Commands}:
|
||||
p2p {LocalizableStrings.CommandP2PDefinition}";
|
||||
|
||||
protected override Dictionary<string, Func<string[], int>> BuiltInCommands => new Dictionary<string, Func<string[], int>>
|
||||
{
|
||||
["p2p"] = AddProjectToProjectReferenceCommand.Run,
|
||||
};
|
||||
protected override string CommandName => "add";
|
||||
protected override string FullCommandNameLocalized => LocalizableStrings.NetAddCommand;
|
||||
internal override List<Func<CommandLineApplication, CommandLineApplication>> SubCommands =>
|
||||
new List<Func<CommandLineApplication, CommandLineApplication>>
|
||||
{
|
||||
AddProjectToProjectReferenceCommand.CreateApplication,
|
||||
};
|
||||
|
||||
public static int Run(string[] args)
|
||||
{
|
||||
var cmd = new AddCommand();
|
||||
return cmd.Start(args);
|
||||
var command = new AddCommand();
|
||||
return command.RunCommand(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,21 +5,9 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
|
|||
public const string AppFullName = ".NET Add Project to Project (p2p) reference Command";
|
||||
|
||||
public const string AppDescription = "Command to add project to project (p2p) reference";
|
||||
|
||||
public const string SpecifyAtLeastOneReferenceToAdd = "You must specify at least one reference to add. Please run dotnet add --help for more information.";
|
||||
|
||||
public const string AppHelpText = "Project to project references to add";
|
||||
|
||||
public const string CmdProject = "PROJECT";
|
||||
|
||||
public const string CmdProjectDescription = "The project file to modify. If a project file is not specified, it searches the current working directory for an MSBuild file that has a file extension that ends in `proj` and uses that file.";
|
||||
|
||||
public const string CmdFramework = "FRAMEWORK";
|
||||
|
||||
public const string CmdFrameworkDescription = "Add reference only when targetting a specific framework";
|
||||
|
||||
public const string CmdForceDescription = "Add reference even if it does not exist, do not convert paths to relative";
|
||||
|
||||
public const string ProjectException = "Project";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
using Microsoft.Build.Evaluation;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.Tools.Common;
|
||||
using NuGet.Frameworks;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
|
@ -13,53 +15,46 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
|
|||
{
|
||||
public class AddProjectToProjectReferenceCommand
|
||||
{
|
||||
public static int Run(string[] args)
|
||||
internal static CommandLineApplication CreateApplication(CommandLineApplication parentApp)
|
||||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
||||
CommandLineApplication app = new CommandLineApplication(throwOnUnexpectedArg: false)
|
||||
{
|
||||
Name = "dotnet add p2p",
|
||||
FullName = LocalizableStrings.AppFullName,
|
||||
Description = LocalizableStrings.AppDescription,
|
||||
AllowArgumentSeparator = true,
|
||||
ArgumentSeparatorHelpText = LocalizableStrings.AppHelpText
|
||||
};
|
||||
CommandLineApplication app = parentApp.Command("p2p", throwOnUnexpectedArg: false);
|
||||
app.FullName = LocalizableStrings.AppFullName;
|
||||
app.Description = LocalizableStrings.AppDescription;
|
||||
app.HandleRemainingArguments = true;
|
||||
app.ArgumentSeparatorHelpText = LocalizableStrings.AppHelpText;
|
||||
|
||||
app.HelpOption("-h|--help");
|
||||
|
||||
CommandArgument projectArgument = app.Argument(
|
||||
$"<{LocalizableStrings.CmdProject}>",
|
||||
LocalizableStrings.CmdProjectDescription);
|
||||
|
||||
CommandOption frameworkOption = app.Option(
|
||||
$"-f|--framework <{LocalizableStrings.CmdFramework}>",
|
||||
$"-f|--framework <{CommonLocalizableStrings.CmdFramework}>",
|
||||
LocalizableStrings.CmdFrameworkDescription,
|
||||
CommandOptionType.SingleValue);
|
||||
|
||||
CommandOption forceOption = app.Option(
|
||||
"--force",
|
||||
LocalizableStrings.CmdForceDescription,
|
||||
CommandOptionType.NoValue);
|
||||
|
||||
app.OnExecute(() => {
|
||||
if (string.IsNullOrEmpty(projectArgument.Value))
|
||||
app.OnExecute(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
throw new GracefulException(CommonLocalizableStrings.RequiredArgumentNotPassed, $"<{LocalizableStrings.ProjectException}>");
|
||||
}
|
||||
if (!parentApp.Arguments.Any())
|
||||
{
|
||||
throw new GracefulException(CommonLocalizableStrings.RequiredArgumentNotPassed, Constants.ProjectOrSolutionArgumentName);
|
||||
}
|
||||
|
||||
var projects = new ProjectCollection();
|
||||
var msbuildProj = MsbuildProject.FromFileOrDirectory(projects, projectArgument.Value);
|
||||
var projectOrDirectory = parentApp.Arguments.First().Value;
|
||||
if (string.IsNullOrEmpty(projectOrDirectory))
|
||||
{
|
||||
projectOrDirectory = PathUtility.EnsureTrailingSlash(Directory.GetCurrentDirectory());
|
||||
}
|
||||
|
||||
if (app.RemainingArguments.Count == 0)
|
||||
{
|
||||
throw new GracefulException(LocalizableStrings.SpecifyAtLeastOneReferenceToAdd);
|
||||
}
|
||||
var projects = new ProjectCollection();
|
||||
var msbuildProj = MsbuildProject.FromFileOrDirectory(projects, projectOrDirectory);
|
||||
|
||||
string frameworkString = frameworkOption.Value();
|
||||
List<string> references = app.RemainingArguments;
|
||||
if (!forceOption.HasValue())
|
||||
{
|
||||
if (app.RemainingArguments.Count == 0)
|
||||
{
|
||||
throw new GracefulException(CommonLocalizableStrings.SpecifyAtLeastOneReferenceToAdd);
|
||||
}
|
||||
|
||||
string frameworkString = frameworkOption.Value();
|
||||
List<string> references = app.RemainingArguments;
|
||||
MsbuildProject.EnsureAllReferencesExist(references);
|
||||
IEnumerable<MsbuildProject> refs = references.Select((r) => MsbuildProject.FromFile(projects, r));
|
||||
|
||||
|
@ -101,30 +96,27 @@ namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
|
|||
}
|
||||
|
||||
msbuildProj.ConvertPathsToRelative(ref references);
|
||||
}
|
||||
|
||||
int numberOfAddedReferences = msbuildProj.AddProjectToProjectReferences(
|
||||
frameworkOption.Value(),
|
||||
references);
|
||||
|
||||
if (numberOfAddedReferences != 0)
|
||||
int numberOfAddedReferences = msbuildProj.AddProjectToProjectReferences(
|
||||
frameworkOption.Value(),
|
||||
references);
|
||||
|
||||
if (numberOfAddedReferences != 0)
|
||||
{
|
||||
msbuildProj.ProjectRootElement.Save();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch (GracefulException e)
|
||||
{
|
||||
msbuildProj.ProjectRootElement.Save();
|
||||
Reporter.Error.WriteLine(e.Message.Red());
|
||||
app.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
return app.Execute(args);
|
||||
}
|
||||
catch (GracefulException e)
|
||||
{
|
||||
Reporter.Error.WriteLine(e.Message.Red());
|
||||
app.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
return app;
|
||||
}
|
||||
|
||||
private static string GetProjectNotCompatibleWithFrameworksDisplayString(MsbuildProject project, IEnumerable<string> frameworksDisplayStrings)
|
||||
|
|
|
@ -2,24 +2,6 @@
|
|||
{
|
||||
internal class LocalizableStrings
|
||||
{
|
||||
public const string Usage = "Usage";
|
||||
|
||||
public const string Arguments = "Arguments";
|
||||
|
||||
public const string ExtraArgs = "Args";
|
||||
|
||||
public const string ListCommandDescription = ".NET List Command";
|
||||
|
||||
public const string Commands = "Commands";
|
||||
|
||||
public const string CommandDefinition = "Command to be executed on <object>.";
|
||||
|
||||
public const string ExtraArgumentsDefinition = "Any extra arguments passed to the command. Use `dotnet list <command> --help` to get help about these arguments.";
|
||||
|
||||
public const string HelpDefinition = "Show help";
|
||||
|
||||
public const string ObjectDefinition = "The object of the operation. If a project file is not specified, it defaults to the current directory.";
|
||||
|
||||
public const string P2PsDefinition = "List project to project (p2p) references from a project";
|
||||
public const string NetListCommand = ".NET List Command";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,38 +3,26 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.DotNet.Tools.List.ProjectToProjectReferences;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.List
|
||||
{
|
||||
public class ListCommand : DispatchCommand
|
||||
public class ListCommand : DotNetTopLevelCommandBase
|
||||
{
|
||||
protected override string HelpText => $@"{LocalizableStrings.ListCommandDescription}
|
||||
|
||||
{LocalizableStrings.Usage}: dotnet list [options] <object> <command> [[--] <arg>...]]
|
||||
|
||||
Options:
|
||||
-h|--help {LocalizableStrings.HelpDefinition}
|
||||
|
||||
{LocalizableStrings.Arguments}:
|
||||
<object> {LocalizableStrings.ObjectDefinition}
|
||||
<command> {LocalizableStrings.CommandDefinition}
|
||||
|
||||
{LocalizableStrings.ExtraArgs}:
|
||||
{LocalizableStrings.ExtraArgumentsDefinition}
|
||||
|
||||
{LocalizableStrings.Commands}:
|
||||
p2ps {LocalizableStrings.P2PsDefinition}";
|
||||
|
||||
protected override Dictionary<string, Func<string[], int>> BuiltInCommands => new Dictionary<string, Func<string[], int>>
|
||||
{
|
||||
["p2ps"] = ListProjectToProjectReferencesCommand.Run,
|
||||
};
|
||||
protected override string CommandName => "list";
|
||||
protected override string FullCommandNameLocalized => LocalizableStrings.NetListCommand;
|
||||
internal override List<Func<CommandLineApplication, CommandLineApplication>> SubCommands =>
|
||||
new List<Func<CommandLineApplication, CommandLineApplication>>
|
||||
{
|
||||
ListProjectToProjectReferencesCommand.CreateApplication,
|
||||
};
|
||||
|
||||
public static int Run(string[] args)
|
||||
{
|
||||
var cmd = new ListCommand();
|
||||
return cmd.Start(args);
|
||||
var command = new ListCommand();
|
||||
return command.RunCommand(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,6 @@ namespace Microsoft.DotNet.Tools.List.ProjectToProjectReferences
|
|||
|
||||
public const string AppDescription = "Command to list project to project (p2p) references";
|
||||
|
||||
public const string ProjectArgumentValueName = "PROJECT";
|
||||
|
||||
public const string ProjectArgumentDescription = "The project file to modify. If a project file is not specified, it searches the current working directory for an MSBuild file that has a file extension that ends in `proj` and uses that file.";
|
||||
|
||||
public const string NoReferencesFound = "There are no {0} references in project {1}.\n{0} is the type of the item being requested (project, package, p2p) and {1} is the object operated on (a project file or a solution file). ";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,63 +4,64 @@
|
|||
using Microsoft.Build.Evaluation;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.Tools.Common;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.List.ProjectToProjectReferences
|
||||
{
|
||||
public class ListProjectToProjectReferencesCommand
|
||||
{
|
||||
public static int Run(string[] args)
|
||||
internal static CommandLineApplication CreateApplication(CommandLineApplication parentApp)
|
||||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
||||
CommandLineApplication app = new CommandLineApplication(throwOnUnexpectedArg: false)
|
||||
{
|
||||
Name = "dotnet list p2ps",
|
||||
FullName = LocalizableStrings.AppFullName,
|
||||
Description = LocalizableStrings.AppDescription
|
||||
};
|
||||
CommandLineApplication app = parentApp.Command("p2ps", throwOnUnexpectedArg: false);
|
||||
app.FullName = LocalizableStrings.AppFullName;
|
||||
app.Description = LocalizableStrings.AppDescription;
|
||||
|
||||
app.HelpOption("-h|--help");
|
||||
|
||||
CommandArgument projectArgument = app.Argument($"<{LocalizableStrings.ProjectArgumentValueName}>", LocalizableStrings.ProjectArgumentDescription);
|
||||
|
||||
app.OnExecute(() => {
|
||||
if (string.IsNullOrEmpty(projectArgument.Value))
|
||||
try
|
||||
{
|
||||
throw new GracefulException(CommonLocalizableStrings.RequiredArgumentNotPassed, $"<{LocalizableStrings.ProjectArgumentValueName}>");
|
||||
}
|
||||
if (!parentApp.Arguments.Any())
|
||||
{
|
||||
throw new GracefulException(CommonLocalizableStrings.RequiredArgumentNotPassed, Constants.ProjectOrSolutionArgumentName);
|
||||
}
|
||||
|
||||
var msbuildProj = MsbuildProject.FromFileOrDirectory(new ProjectCollection(), projectArgument.Value);
|
||||
var projectOrDirectory = parentApp.Arguments.First().Value;
|
||||
if (string.IsNullOrEmpty(projectOrDirectory))
|
||||
{
|
||||
projectOrDirectory = PathUtility.EnsureTrailingSlash(Directory.GetCurrentDirectory());
|
||||
}
|
||||
|
||||
var msbuildProj = MsbuildProject.FromFileOrDirectory(new ProjectCollection(), projectOrDirectory);
|
||||
|
||||
var p2ps = msbuildProj.GetProjectToProjectReferences();
|
||||
if (p2ps.Count() == 0)
|
||||
{
|
||||
Reporter.Output.WriteLine(string.Format(LocalizableStrings.NoReferencesFound, CommonLocalizableStrings.P2P, projectOrDirectory));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reporter.Output.WriteLine($"{CommonLocalizableStrings.ProjectReferenceOneOrMore}");
|
||||
Reporter.Output.WriteLine(new string('-', CommonLocalizableStrings.ProjectReferenceOneOrMore.Length));
|
||||
foreach (var p2p in p2ps)
|
||||
{
|
||||
Reporter.Output.WriteLine(p2p.Include);
|
||||
}
|
||||
|
||||
var p2ps = msbuildProj.GetProjectToProjectReferences();
|
||||
if (p2ps.Count() == 0)
|
||||
{
|
||||
Reporter.Output.WriteLine(string.Format(LocalizableStrings.NoReferencesFound, CommonLocalizableStrings.P2P, projectArgument.Value));
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reporter.Output.WriteLine($"{CommonLocalizableStrings.ProjectReferenceOneOrMore}");
|
||||
Reporter.Output.WriteLine(new string('-', CommonLocalizableStrings.ProjectReferenceOneOrMore.Length));
|
||||
foreach (var p2p in p2ps)
|
||||
catch (GracefulException e)
|
||||
{
|
||||
Reporter.Output.WriteLine(p2p.Include);
|
||||
Reporter.Error.WriteLine(e.Message.Red());
|
||||
app.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
return app.Execute(args);
|
||||
}
|
||||
catch (GracefulException e)
|
||||
{
|
||||
Reporter.Error.WriteLine(e.Message.Red());
|
||||
app.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
return app;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,23 +3,5 @@ namespace Microsoft.DotNet.Tools.Remove
|
|||
internal class LocalizableStrings
|
||||
{
|
||||
public const string NetRemoveCommand = ".NET Remove Command";
|
||||
|
||||
public const string Usage = "Usage";
|
||||
|
||||
public const string Options = "Options";
|
||||
|
||||
public const string HelpDefinition = "Show help information";
|
||||
|
||||
public const string Arguments = "Arguments";
|
||||
|
||||
public const string ArgumentsObjectDefinition = "The object of the operation. If a project file is not specified, it defaults to the current directory.";
|
||||
|
||||
public const string ArgumentsCommandDefinition = "Command to be executed on <object>.";
|
||||
|
||||
public const string ArgsDefinition = "Any extra arguments passed to the command. Use `dotnet add <command> --help` to get help about these arguments.";
|
||||
|
||||
public const string Commands = "Commands";
|
||||
|
||||
public const string CommandP2PDefinition = "Remove project to project (p2p) reference from a project";
|
||||
}
|
||||
}
|
|
@ -1,47 +1,28 @@
|
|||
// 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;
|
||||
using Microsoft.DotNet.ProjectJsonMigration;
|
||||
using NuGet.Frameworks;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.DotNet.Cli;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.DotNet.Tools.Remove.ProjectToProjectReference;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Remove
|
||||
{
|
||||
public class RemoveCommand : DispatchCommand
|
||||
public class RemoveCommand : DotNetTopLevelCommandBase
|
||||
{
|
||||
protected override string HelpText => $@"{LocalizableStrings.NetRemoveCommand};
|
||||
|
||||
{LocalizableStrings.Usage}: dotnet remove [options] <object> <command> [[--] <arg>...]]
|
||||
|
||||
{LocalizableStrings.Options}:
|
||||
-h|--help {LocalizableStrings.HelpDefinition}
|
||||
|
||||
{LocalizableStrings.Arguments}:
|
||||
<object> {LocalizableStrings.ArgumentsObjectDefinition}
|
||||
<command> {LocalizableStrings.ArgumentsCommandDefinition}
|
||||
|
||||
Args:
|
||||
{LocalizableStrings.ArgsDefinition}
|
||||
|
||||
{LocalizableStrings.Commands}:
|
||||
p2p {LocalizableStrings.CommandP2PDefinition}";
|
||||
|
||||
protected override Dictionary<string, Func<string[], int>> BuiltInCommands => new Dictionary<string, Func<string[], int>>
|
||||
{
|
||||
["p2p"] = RemoveProjectToProjectReferenceCommand.Run,
|
||||
};
|
||||
protected override string CommandName => "remove";
|
||||
protected override string FullCommandNameLocalized => LocalizableStrings.NetRemoveCommand;
|
||||
internal override List<Func<CommandLineApplication, CommandLineApplication>> SubCommands =>
|
||||
new List<Func<CommandLineApplication, CommandLineApplication>>
|
||||
{
|
||||
RemoveProjectToProjectReferenceCommand.CreateApplication,
|
||||
};
|
||||
|
||||
public static int Run(string[] args)
|
||||
{
|
||||
var cmd = new RemoveCommand();
|
||||
return cmd.Start(args);
|
||||
var command = new RemoveCommand();
|
||||
return command.RunCommand(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,46 +6,10 @@ namespace Microsoft.DotNet.Tools.Remove.ProjectToProjectReference
|
|||
|
||||
public const string AppDescription = "Command to remove project to project (p2p) reference";
|
||||
|
||||
public const string AppArgumentSeparatorHelpText = "Project to project references to remove";
|
||||
|
||||
public const string CmdArgProject = "PROJECT";
|
||||
|
||||
public const string CmdArgumentDescription = "The project file to modify. If a project file is not specified, it searches the current working directory for an MSBuild file that has a file extension that ends in `proj` and uses that file.";
|
||||
|
||||
public const string CmdFramework = "FRAMEWORK";
|
||||
public const string AppHelpText = "Project to project references to remove";
|
||||
|
||||
public const string CmdFrameworkDescription = "Remove reference only when targetting a specific framework";
|
||||
|
||||
public const string ProjectException = "Project";
|
||||
|
||||
public const string ReferenceNotFoundInTheProject = "Specified reference {0} does not exist in project {1}.";
|
||||
|
||||
public const string ReferenceRemoved = "Reference `{0}` deleted from the project.";
|
||||
|
||||
public const string SpecifyAtLeastOneReferenceToRemove = "You must specify at least one reference to delete. Please run dotnet delete --help for more information.";
|
||||
|
||||
public const string ReferenceDeleted = "Reference `{0}` deleted.";
|
||||
|
||||
public const string SpecifyAtLeastOneReferenceToDelete = "You must specify at least one reference to delete. Please run dotnet delete --help for more information.";
|
||||
|
||||
public const string NetRemoveCommand = ".NET Remove Command";
|
||||
|
||||
public const string Usage = "Usage";
|
||||
|
||||
public const string Options = "Options";
|
||||
|
||||
public const string HelpDefinition = "Show help information";
|
||||
|
||||
public const string Arguments = "Arguments";
|
||||
|
||||
public const string ArgumentsObjectDefinition = "The object of the operation. If a project file is not specified, it defaults to the current directory.";
|
||||
|
||||
public const string ArgumentsCommandDefinition = "Command to be executed on <object>.";
|
||||
|
||||
public const string ArgsDefinition = "Any extra arguments passed to the command. Use `dotnet add <command> --help` to get help about these arguments.";
|
||||
|
||||
public const string Commands = "Commands";
|
||||
|
||||
public const string CommandP2PDefinition = "Remove project to project (p2p) reference from a project";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,73 +4,73 @@
|
|||
using Microsoft.Build.Evaluation;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
using Microsoft.DotNet.Tools.Common;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.DotNet.Tools.Remove.ProjectToProjectReference
|
||||
{
|
||||
public class RemoveProjectToProjectReferenceCommand
|
||||
{
|
||||
public static int Run(string[] args)
|
||||
internal static CommandLineApplication CreateApplication(CommandLineApplication parentApp)
|
||||
{
|
||||
DebugHelper.HandleDebugSwitch(ref args);
|
||||
|
||||
CommandLineApplication app = new CommandLineApplication(throwOnUnexpectedArg: false)
|
||||
{
|
||||
Name = "dotnet remove p2p",
|
||||
FullName = LocalizableStrings.AppFullName,
|
||||
Description = LocalizableStrings.AppDescription,
|
||||
AllowArgumentSeparator = true,
|
||||
ArgumentSeparatorHelpText = LocalizableStrings.AppArgumentSeparatorHelpText
|
||||
};
|
||||
CommandLineApplication app = parentApp.Command("p2p", throwOnUnexpectedArg: false);
|
||||
app.FullName = LocalizableStrings.AppFullName;
|
||||
app.Description = LocalizableStrings.AppDescription;
|
||||
app.HandleRemainingArguments = true;
|
||||
app.ArgumentSeparatorHelpText = LocalizableStrings.AppHelpText;
|
||||
|
||||
app.HelpOption("-h|--help");
|
||||
|
||||
CommandArgument projectArgument = app.Argument(
|
||||
$"<{LocalizableStrings.CmdArgProject}>",
|
||||
LocalizableStrings.CmdArgumentDescription);
|
||||
|
||||
CommandOption frameworkOption = app.Option(
|
||||
$"-f|--framework <{LocalizableStrings.CmdFramework}>",
|
||||
$"-f|--framework <{CommonLocalizableStrings.CmdFramework}>",
|
||||
LocalizableStrings.CmdFrameworkDescription,
|
||||
CommandOptionType.SingleValue);
|
||||
|
||||
app.OnExecute(() => {
|
||||
if (string.IsNullOrEmpty(projectArgument.Value))
|
||||
try
|
||||
{
|
||||
throw new GracefulException(CommonLocalizableStrings.RequiredArgumentNotPassed, $"<{LocalizableStrings.ProjectException}>");
|
||||
if (!parentApp.Arguments.Any())
|
||||
{
|
||||
throw new GracefulException(CommonLocalizableStrings.RequiredArgumentNotPassed, Constants.ProjectOrSolutionArgumentName);
|
||||
}
|
||||
|
||||
var projectOrDirectory = parentApp.Arguments.First().Value;
|
||||
if (string.IsNullOrEmpty(projectOrDirectory))
|
||||
{
|
||||
projectOrDirectory = PathUtility.EnsureTrailingSlash(Directory.GetCurrentDirectory());
|
||||
}
|
||||
|
||||
var msbuildProj = MsbuildProject.FromFileOrDirectory(new ProjectCollection(), projectOrDirectory);
|
||||
|
||||
if (app.RemainingArguments.Count == 0)
|
||||
{
|
||||
throw new GracefulException(LocalizableStrings.SpecifyAtLeastOneReferenceToRemove);
|
||||
}
|
||||
|
||||
List<string> references = app.RemainingArguments;
|
||||
|
||||
int numberOfRemovedReferences = msbuildProj.RemoveProjectToProjectReferences(
|
||||
frameworkOption.Value(),
|
||||
references);
|
||||
|
||||
if (numberOfRemovedReferences != 0)
|
||||
{
|
||||
msbuildProj.ProjectRootElement.Save();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
var msbuildProj = MsbuildProject.FromFileOrDirectory(new ProjectCollection(), projectArgument.Value);
|
||||
|
||||
if (app.RemainingArguments.Count == 0)
|
||||
catch (GracefulException e)
|
||||
{
|
||||
throw new GracefulException(LocalizableStrings.SpecifyAtLeastOneReferenceToRemove);
|
||||
Reporter.Error.WriteLine(e.Message.Red());
|
||||
app.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
|
||||
List<string> references = app.RemainingArguments;
|
||||
|
||||
int numberOfRemovedReferences = msbuildProj.RemoveProjectToProjectReferences(
|
||||
frameworkOption.Value(),
|
||||
references);
|
||||
|
||||
if (numberOfRemovedReferences != 0)
|
||||
{
|
||||
msbuildProj.ProjectRootElement.Save();
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
return app.Execute(args);
|
||||
}
|
||||
catch (GracefulException e)
|
||||
{
|
||||
Reporter.Error.WriteLine(e.Message.Red());
|
||||
app.ShowHelp();
|
||||
return 1;
|
||||
}
|
||||
return app;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,6 +80,16 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests
|
|||
cmd.StdOut.Should().Contain("Usage");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhenTooManyArgumentsArePassedItPrintsError()
|
||||
{
|
||||
var cmd = new AddP2PCommand()
|
||||
.WithProject("one two three")
|
||||
.Execute("proj.csproj");
|
||||
cmd.ExitCode.Should().NotBe(0);
|
||||
cmd.StdErr.Should().Contain("Unrecognized command or argument");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("idontexist.csproj")]
|
||||
[InlineData("ihave?inv@lid/char\\acters")]
|
||||
|
@ -569,25 +579,6 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests
|
|||
lib.CsProjContent().Should().BeEquivalentTo(contentBefore);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhenPassedReferenceDoesNotExistAndForceSwitchIsPassedItAddsIt()
|
||||
{
|
||||
var lib = NewLibWithFrameworks();
|
||||
const string nonExisting = "IDoNotExist.csproj";
|
||||
|
||||
int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition();
|
||||
var cmd = new AddP2PCommand()
|
||||
.WithWorkingDirectory(lib.Path)
|
||||
.WithProject(lib.CsProjName)
|
||||
.Execute($"--force \"{nonExisting}\"");
|
||||
cmd.Should().Pass();
|
||||
cmd.StdOut.Should().Contain("added to the project");
|
||||
cmd.StdErr.Should().BeEmpty();
|
||||
var csproj = lib.CsProj();
|
||||
csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1);
|
||||
csproj.NumberOfProjectReferencesWithIncludeContaining(nonExisting).Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhenPassedReferenceIsUsingSlashesItNormalizesItToBackslashes()
|
||||
{
|
||||
|
@ -598,13 +589,13 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests
|
|||
var cmd = new AddP2PCommand()
|
||||
.WithWorkingDirectory(lib.Path)
|
||||
.WithProject(lib.CsProjName)
|
||||
.Execute($"--force \"{setup.ValidRefCsprojPath.Replace('\\', '/')}\"");
|
||||
.Execute($"\"{setup.ValidRefCsprojPath.Replace('\\', '/')}\"");
|
||||
cmd.Should().Pass();
|
||||
cmd.StdOut.Should().Contain("added to the project");
|
||||
cmd.StdErr.Should().BeEmpty();
|
||||
var csproj = lib.CsProj();
|
||||
csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1);
|
||||
csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojPath.Replace('/', '\\')).Should().Be(1);
|
||||
csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojRelPath.Replace('/', '\\')).Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -626,25 +617,6 @@ namespace Microsoft.DotNet.Cli.Add.P2P.Tests
|
|||
csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojRelToOtherProjPath.Replace('/', '\\')).Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhenReferenceIsRelativeAndProjectIsNotInCurrentDirectoryAndForceSwitchIsPassedItDoesNotChangeIt()
|
||||
{
|
||||
var setup = Setup();
|
||||
var proj = new ProjDir(setup.LibDir);
|
||||
|
||||
int noCondBefore = proj.CsProj().NumberOfItemGroupsWithoutCondition();
|
||||
var cmd = new AddP2PCommand()
|
||||
.WithWorkingDirectory(setup.TestRoot)
|
||||
.WithProject(setup.LibCsprojPath)
|
||||
.Execute($"--force \"{setup.ValidRefCsprojRelPath}\"");
|
||||
cmd.Should().Pass();
|
||||
cmd.StdOut.Should().Contain("added to the project");
|
||||
cmd.StdErr.Should().BeEmpty();
|
||||
var csproj = proj.CsProj();
|
||||
csproj.NumberOfItemGroupsWithoutCondition().Should().Be(noCondBefore + 1);
|
||||
csproj.NumberOfProjectReferencesWithIncludeContaining(setup.ValidRefCsprojRelPath.Replace('/', '\\')).Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItCanAddReferenceWithConditionOnCompatibleFramework()
|
||||
{
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Microsoft.DotNet.Cli.List.P2P.Tests
|
|||
const string ConditionFrameworkNet451 = "== 'net451'";
|
||||
const string FrameworkNetCoreApp10Arg = "-f netcoreapp1.0";
|
||||
const string ConditionFrameworkNetCoreApp10 = "== 'netcoreapp1.0'";
|
||||
const string UsageText = "Usage: dotnet list p2ps";
|
||||
const string UsageText = "Usage: dotnet list <PROJECT_OR_SOLUTION> p2ps";
|
||||
|
||||
[Theory]
|
||||
[InlineData("--help")]
|
||||
|
@ -29,6 +29,16 @@ namespace Microsoft.DotNet.Cli.List.P2P.Tests
|
|||
cmd.StdOut.Should().Contain("Usage");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhenTooManyArgumentsArePassedItPrintsError()
|
||||
{
|
||||
var cmd = new AddP2PCommand()
|
||||
.WithProject("one two three")
|
||||
.Execute("proj.csproj");
|
||||
cmd.ExitCode.Should().NotBe(0);
|
||||
cmd.StdErr.Should().Contain("Unrecognized command or argument");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("idontexist.csproj")]
|
||||
[InlineData("ihave?inv@lid/char\\acters")]
|
||||
|
@ -101,38 +111,38 @@ namespace Microsoft.DotNet.Cli.List.P2P.Tests
|
|||
[Fact]
|
||||
public void ItPrintsSingleReference()
|
||||
{
|
||||
var lib = NewLib();
|
||||
string ref1 = "someref.csproj";
|
||||
AddFakeRef(ref1, lib);
|
||||
var lib = NewLib("ItPrintsSingleReference", "lib");
|
||||
string ref1 = NewLib("ItPrintsSingleReference", "ref").CsProjPath;
|
||||
AddValidRef(ref1, lib);
|
||||
|
||||
var cmd = new ListP2PsCommand()
|
||||
.WithProject(lib.CsProjPath)
|
||||
.Execute();
|
||||
cmd.Should().Pass();
|
||||
cmd.StdOut.Should().Contain("Project reference(s)");
|
||||
cmd.StdOut.Should().Contain(ref1);
|
||||
cmd.StdOut.Should().Contain(@"..\ItPrintsSingleReferenceref\ItPrintsSingleReferenceref.csproj");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItPrintsMultipleReferences()
|
||||
{
|
||||
var lib = NewLib();
|
||||
string ref1 = "someref.csproj";
|
||||
string ref2 = @"..\someref2.csproj";
|
||||
string ref3 = @"..\abc\abc.csproj";
|
||||
var lib = NewLib("ItPrintsSingleReference", "lib");
|
||||
string ref1 = NewLib("ItPrintsSingleReference", "ref1").CsProjPath;
|
||||
string ref2 = NewLib("ItPrintsSingleReference", "ref2").CsProjPath;
|
||||
string ref3 = NewLib("ItPrintsSingleReference", "ref3").CsProjPath;
|
||||
|
||||
AddFakeRef(ref1, lib);
|
||||
AddFakeRef(ref2, lib);
|
||||
AddFakeRef(ref3, lib);
|
||||
AddValidRef(ref1, lib);
|
||||
AddValidRef(ref2, lib);
|
||||
AddValidRef(ref3, lib);
|
||||
|
||||
var cmd = new ListP2PsCommand()
|
||||
.WithProject(lib.CsProjPath)
|
||||
.Execute();
|
||||
cmd.Should().Pass();
|
||||
cmd.StdOut.Should().Contain("Project reference(s)");
|
||||
cmd.StdOut.Should().Contain(ref1);
|
||||
cmd.StdOut.Should().Contain(ref2);
|
||||
cmd.StdOut.Should().Contain(ref3);
|
||||
cmd.StdOut.Should().Contain(@"..\ItPrintsSingleReferenceref1\ItPrintsSingleReferenceref1.csproj");
|
||||
cmd.StdOut.Should().Contain(@"..\ItPrintsSingleReferenceref2\ItPrintsSingleReferenceref2.csproj");
|
||||
cmd.StdOut.Should().Contain(@"..\ItPrintsSingleReferenceref3\ItPrintsSingleReferenceref3.csproj");
|
||||
}
|
||||
|
||||
private TestSetup Setup([System.Runtime.CompilerServices.CallerMemberName] string callingMethod = nameof(Setup), string identifier = "")
|
||||
|
@ -169,11 +179,11 @@ namespace Microsoft.DotNet.Cli.List.P2P.Tests
|
|||
return dir;
|
||||
}
|
||||
|
||||
private void AddFakeRef(string path, ProjDir proj)
|
||||
private void AddValidRef(string path, ProjDir proj)
|
||||
{
|
||||
new AddP2PCommand()
|
||||
.WithProject(proj.CsProjPath)
|
||||
.Execute($"--force \"{path}\"")
|
||||
.Execute($"\"{path}\"")
|
||||
.Should().Pass();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,16 @@ namespace Microsoft.DotNet.Cli.Remove.P2P.Tests
|
|||
cmd.StdOut.Should().Contain("Usage");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WhenTooManyArgumentsArePassedItPrintsError()
|
||||
{
|
||||
var cmd = new AddP2PCommand()
|
||||
.WithProject("one two three")
|
||||
.Execute("proj.csproj");
|
||||
cmd.ExitCode.Should().NotBe(0);
|
||||
cmd.StdErr.Should().Contain("Unrecognized command or argument");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("idontexist.csproj")]
|
||||
[InlineData("ihave?inv@lid/char\\acters")]
|
||||
|
@ -356,7 +366,7 @@ namespace Microsoft.DotNet.Cli.Remove.P2P.Tests
|
|||
{
|
||||
var setup = Setup();
|
||||
var lib = GetLibRef(setup);
|
||||
var libref = AddValidRef(setup, lib, "--force");
|
||||
var libref = AddValidRef(setup, lib);
|
||||
|
||||
int noCondBefore = lib.CsProj().NumberOfItemGroupsWithoutCondition();
|
||||
var cmd = new RemoveP2PCommand()
|
||||
|
|
Loading…
Add table
Reference in a new issue