Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support '--' in a command line argument as a correct switch indicator #5786

Merged
16 changes: 16 additions & 0 deletions src/MSBuild.UnitTests/XMake_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,22 @@ public void ExtractSwitchParametersTest()
doubleQuotesRemovedFromArg.ShouldBe(6);
}

[Fact]
public void GetLengthOfSwitchIndicatorTest()
{
var commandLineSwitchWithSlash = "/Switch";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to have a test validating that building with --help works.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

This might be overkill but: Consider a case for -help, /help, and --help and verifying part of the output is expected. Potentially in the help test just below this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benvillalobos Can you elaborate on:

verifying part of the output is expected

Thing is, that MSBuildApp.Execute() method only returns the ExitType.
How to get the string returned from command line execution?

Or perhaps checking whether each call (command with '--help', '-h, '/h') returns ExitType.Success will be enough?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verifying that it succeeded would be enough for me. I think it's probably more complicated than it's worth to look at the output, but you might be able to capture it with a logger you can pass in alongside the --help switch.

var commandLineSwitchWithSingleDash = "-Switch";
var commandLineSwitchWithDoubleDash = "--Switch";

var commandLineSwitchWithNoneOrIncorrectIndicator = "zSwitch";

MSBuildApp.GetLengthOfSwitchIndicator(commandLineSwitchWithSlash).ShouldBe(1);
MSBuildApp.GetLengthOfSwitchIndicator(commandLineSwitchWithSingleDash).ShouldBe(1);
MSBuildApp.GetLengthOfSwitchIndicator(commandLineSwitchWithDoubleDash).ShouldBe(2);

MSBuildApp.GetLengthOfSwitchIndicator(commandLineSwitchWithNoneOrIncorrectIndicator).ShouldBe(0);
}

[Fact]
public void Help()
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[Fact]
public void Help()
{
[Theory]
[InlineData("-?")]
[InlineData("-h")]
[InlineData("--help")]
[InlineData(@"/h")]
public void Help(string switch)
{

Expand Down
54 changes: 49 additions & 5 deletions src/MSBuild/XMake.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1675,8 +1675,8 @@ internal static void GatherCommandLineSwitches(ArrayList commandLineArgs, Comman
string switchName;
string switchParameters;

// all switches should start with - or / unless a project is being specified
if (!unquotedCommandLineArg.StartsWith("-", StringComparison.Ordinal) && (!unquotedCommandLineArg.StartsWith("/", StringComparison.Ordinal) || FileUtilities.LooksLikeUnixFilePath(unquotedCommandLineArg)))
// all switches should start with - or / or -- unless a project is being specified
if (!ValidateSwitchIndicatorInUnquotedArgument(unquotedCommandLineArg) || FileUtilities.LooksLikeUnixFilePath(unquotedCommandLineArg))
{
switchName = null;
// add a (fake) parameter indicator for later parsing
Expand All @@ -1687,17 +1687,20 @@ internal static void GatherCommandLineSwitches(ArrayList commandLineArgs, Comman
// check if switch has parameters (look for the : parameter indicator)
int switchParameterIndicator = unquotedCommandLineArg.IndexOf(':');

// get the length of the beginning sequence considered as a switch indicator (- or / or --)
int switchIndicatorsLength = GetLengthOfSwitchIndicator(unquotedCommandLineArg);

// extract the switch name and parameters -- the name is sandwiched between the switch indicator (the
// leading - or /) and the parameter indicator (if the switch has parameters); the parameters (if any)
// leading - or / or --) and the parameter indicator (if the switch has parameters); the parameters (if any)
// follow the parameter indicator
if (switchParameterIndicator == -1)
{
switchName = unquotedCommandLineArg.Substring(1);
switchName = unquotedCommandLineArg.Substring(switchIndicatorsLength);
switchParameters = String.Empty;
}
else
{
switchName = unquotedCommandLineArg.Substring(1, switchParameterIndicator - 1);
switchName = unquotedCommandLineArg.Substring(switchIndicatorsLength, switchParameterIndicator - 1);
switchParameters = ExtractSwitchParameters(commandLineArg, unquotedCommandLineArg, doubleQuotesRemovedFromArg, switchName, switchParameterIndicator);
}
}
Expand Down Expand Up @@ -2908,6 +2911,47 @@ private static void ValidateExtensions(string[] projectExtensionsToIgnore)
}
}

/// <summary>
/// Checks whether an argument given as a parameter starts with valid indicator,
/// <br/>which means, whether switch begins with one of: "/", "-", "--"
/// </summary>
/// <param name="unquotedCommandLineArgument">Command line argument with beginning indicator (e.g. --help).
/// <br/>This argument has to be unquoted, otherwise the first character will always be a quote character "</param>
/// <returns>true if argument's beginning matches one of possible indicators
/// <br/>false if argument's beginning doesn't match any of correct indicator
/// </returns>
private static bool ValidateSwitchIndicatorInUnquotedArgument(string unquotedCommandLineArgument)
{
return unquotedCommandLineArgument.StartsWith("--", StringComparison.Ordinal)
|| unquotedCommandLineArgument.StartsWith("-", StringComparison.Ordinal)
BartoszKlonowski marked this conversation as resolved.
Show resolved Hide resolved
|| unquotedCommandLineArgument.StartsWith("/", StringComparison.Ordinal);
}

/// <summary>
/// Gets the length of the switch indicator (- or / or --)
/// <br/>The length returned from this method is deduced from the beginning sequence of unquoted argument.
/// <br/>This way it will "assume" that there's no further error (e.g. // or ---) which would also be considered as a correct indicator.
/// </summary>
/// <param name="unquotedSwitch">Unquoted argument with leading indicator and name</param>
/// <returns>Correct length of used indicator
/// <br/>0 if no leading sequence recognized as correct indicator</returns>
/// Internal for testing purposes
internal static int GetLengthOfSwitchIndicator(string unquotedSwitch)
{
if (unquotedSwitch.StartsWith("--", StringComparison.Ordinal))
{
return "--".Length;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably just put 2, 1, and 0 here to avoid allocating strings unnecessarily.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These strings are constants and so aren't allocated. This compiles to

ldstr "--"
call instance int32 [System.Private.CoreLib]System.String::get_Length()

However, it's probably worth avoiding that call/making the JIT constant-fold it, so I would also prefer the constants.

}
else if (unquotedSwitch.StartsWith("-", StringComparison.Ordinal) || unquotedSwitch.StartsWith("/", StringComparison.Ordinal))
{
return "-".Length;
}
else
{
return "".Length;
}
}

/// <summary>
/// Figures out which targets are to be built.
/// </summary>
Expand Down