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

Adding OS and arch command line options #18889

Merged
merged 5 commits into from
Jul 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion src/Cli/dotnet/CommonLocalizableStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -674,4 +674,19 @@ setx PATH "%PATH%;{0}"
<data name="CommandPrereleaseOptionDescription" xml:space="preserve">
<value>Allows prerelease packages to be installed.</value>
</data>
</root>
<data name="ArchitectureOptionDescription" xml:space="preserve">
<value>The target architecture.</value>
</data>
<data name="OperatingSystemOptionDescription" xml:space="preserve">
<value>The target operating system.</value>
</data>
<data name="CannotResolveRuntimeIdentifier" xml:space="preserve">
<value>Resolving the current runtime identifier failed.</value>
</data>
<data name="CannotSpecifyBothRuntimeAndArchOptions" xml:space="preserve">
<value>Specifying both the `-r|--runtime` and `-a|--arch` options is not supported.</value>
</data>
<data name="CannotSpecifyBothRuntimeAndOsOptions" xml:space="preserve">
<value>Specifying both the `-r|--runtime` and `-os` options is not supported.</value>
</data>
</root>
86 changes: 85 additions & 1 deletion src/Cli/dotnet/CommonOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
using System.CommandLine;
using System.IO;
using Microsoft.DotNet.Tools.Common;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.Cli.Utils;
using System.CommandLine.Parsing;

namespace Microsoft.DotNet.Cli
{
Expand Down Expand Up @@ -35,7 +39,7 @@ public static Option FrameworkOption(string description) =>
description)
{
ArgumentHelpName = CommonLocalizableStrings.FrameworkArgumentName

}.ForwardAsSingle(o => $"-property:TargetFramework={o}")
.AddSuggestions(Suggest.TargetFrameworksFromProjectFile());

Expand Down Expand Up @@ -91,6 +95,18 @@ public static Option InteractiveOption() =>
"--interactive",
CommonLocalizableStrings.CommandInteractiveOptionDescription);

public static Option ArchitectureOption(bool includeShortVersion = true) =>
new ForwardedOption<string>(
includeShortVersion ? new string[] { "--arch", "-a" } : new string[] { "--arch" },
CommonLocalizableStrings.ArchitectureOptionDescription)
sfoslund marked this conversation as resolved.
Show resolved Hide resolved
.SetForwardingFunction(ResolveArchOptionToRuntimeIdentifier);

public static Option OperatingSystemOption() =>
new ForwardedOption<string>(
"--os",
CommonLocalizableStrings.OperatingSystemOptionDescription)
sfoslund marked this conversation as resolved.
Show resolved Hide resolved
.SetForwardingFunction(ResolveOsOptionToRuntimeIdentifier);

public static Option DebugOption() => new Option<bool>("--debug");

public static bool VerbosityIsDetailedOrDiagnostic(this VerbosityOptions verbosity)
Expand All @@ -100,6 +116,74 @@ public static bool VerbosityIsDetailedOrDiagnostic(this VerbosityOptions verbosi
verbosity.Equals(VerbosityOptions.d) ||
verbosity.Equals(VerbosityOptions.detailed);
}

internal static IEnumerable<string> ResolveArchOptionToRuntimeIdentifier(string arg, ParseResult parseResult)
{
if (parseResult.HasOption(RuntimeOption(string.Empty).Aliases.First()))
{
throw new GracefulException(CommonLocalizableStrings.CannotSpecifyBothRuntimeAndArchOptions);
}

if (parseResult.BothArchAndOsOptionsSpecified())
{
// ResolveOsOptionToRuntimeIdentifier handles resolving the RID when both arch and os are specified
return Array.Empty<string>();
sfoslund marked this conversation as resolved.
Show resolved Hide resolved
}

return ResolveRidShorthandOptions(null, arg);
}

internal static IEnumerable<string> ResolveOsOptionToRuntimeIdentifier(string arg, ParseResult parseResult)
{
if (parseResult.HasOption(RuntimeOption(string.Empty).Aliases.First()))
{
throw new GracefulException(CommonLocalizableStrings.CannotSpecifyBothRuntimeAndOsOptions);
}

if (parseResult.BothArchAndOsOptionsSpecified())
{
return ResolveRidShorthandOptions(arg, parseResult.ValueForOption<string>(CommonOptions.ArchitectureOption().Aliases.First()));
}

return ResolveRidShorthandOptions(arg, null);
}

private static IEnumerable<string> ResolveRidShorthandOptions(string os, string arch)
{
var properties = new string[] { $"-property:RuntimeIdentifier={ResolveRidShorthandOptionsToRuntimeIdentifier(os, arch)}" };
return properties;
}

internal static string ResolveRidShorthandOptionsToRuntimeIdentifier(string os, string arch)
{
var currentRid = GetCurrentRuntimeId();
os = string.IsNullOrEmpty(os) ? GetOsFromRid(currentRid) : os;
arch = string.IsNullOrEmpty(arch) ? GetArchFromRid(currentRid) : arch;
return $"{os}-{arch}";
}

private static string GetCurrentRuntimeId()
sfoslund marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

I assume we have some logic in the msbuild part of the SDK which already does this. Wouldn't it be cleaner to just set properties based on the command line options and let msbuild handle the actual RID selection then?

Copy link
Member

Choose a reason for hiding this comment

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

Per the spec:

The shorthand syntax is intended as a CLI and not MSBuild concept. For scenarios that today require specifying RIDs in project file, the intent is that users will continue to specify RIDs. If we find that there is a need for a shorthand RID syntax in project files, then we can consider extending this syntax to MSBuild.

We could pass "private" properties to MSBuild if we had to, but if we can I think we should avoid it, as we'd have to prevent them from flowing across project references as global properties the same way we do with RuntimeIdentifier.

Copy link
Member

Choose a reason for hiding this comment

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

Alternatively - if we have existing C# code which does this today, we should share that between MSBuild and CLI. If we don't, maybe we should consider doing so. Otherwise this will be a prime target for unintentional breaks and inconsistencies.

Copy link
Member

Choose a reason for hiding this comment

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

For MSBuild, it's a property (NETCoreSdkRuntimeIdentifier) which we generate into the Microsoft.NETCoreSdk.BundledVersions.props file when we're building the SDK.

Copy link
Member

Choose a reason for hiding this comment

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

That sounds like something we could do for CLI as well - but it would require us to build the CLI as RID specific, which is not the case today.
Alternative idea - can we instead rely on the dotnet.dll location? It would be easier/safer than trying to compute the path. Options:

  • Simply look "next to dotnet.dll" - not as robust as the other options, but definitely much better than the code here
  • Through a post-build step bake the RID into the binary. This is technically tricky and could have problems with signing - but it's an option.
  • Build CLI as RID specific - the best approach I think (we plan to do that eventually anyway for size reasons)

Copy link
Member

Choose a reason for hiding this comment

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

We already also use the NETCoreSdkRuntimeIdentifierChain.txt file for the workload MSBuild SDK resolver. So I think it's fine to rely on that file. I think looking next to dotnet.dll is a great idea to simplify this code, in what ways do you think it wouldn't be robust?

{
var dotnetRootPath = Path.GetDirectoryName(Environment.ProcessPath);
// When running under test the path does not always contain "dotnet" and Product.Version is empty.
dotnetRootPath = Path.GetFileName(dotnetRootPath).Contains("dotnet") ? dotnetRootPath : Path.Combine(dotnetRootPath, "dotnet");
sfoslund marked this conversation as resolved.
Show resolved Hide resolved
var ridFileName = "NETCoreSdkRuntimeIdentifierChain.txt";
string runtimeIdentifierChainPath = string.IsNullOrEmpty(Product.Version) ?
sfoslund marked this conversation as resolved.
Show resolved Hide resolved
Path.Combine(Directory.GetDirectories(Path.Combine(dotnetRootPath, "sdk"))[0], ridFileName) :
Path.Combine(dotnetRootPath, "sdk", Product.Version, ridFileName);
string[] currentRuntimeIdentifiers = File.Exists(runtimeIdentifierChainPath) ?
File.ReadAllLines(runtimeIdentifierChainPath).Where(l => !string.IsNullOrEmpty(l)).ToArray() :
new string[] { };
if (currentRuntimeIdentifiers == null || !currentRuntimeIdentifiers.Any() || !currentRuntimeIdentifiers[0].Contains("-"))
{
throw new GracefulException(CommonLocalizableStrings.CannotResolveRuntimeIdentifier);
}
return currentRuntimeIdentifiers[0]; // First rid is the most specific (ex win-x64)
}

private static string GetOsFromRid(string rid) => rid.Substring(0, rid.LastIndexOf("-"));

private static string GetArchFromRid(string rid) => rid.Substring(rid.LastIndexOf("-") + 1, rid.Length - rid.LastIndexOf("-") - 1);
}

public enum VerbosityOptions
Expand Down
10 changes: 8 additions & 2 deletions src/Cli/dotnet/OptionForwardingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ namespace Microsoft.DotNet.Cli
{
public static class OptionForwardingExtensions
{
public static ForwardedOption<T> Forward<T>(this ForwardedOption<T> option) => option.SetForwardingFunction((o) => new string[] { option.Name });
public static ForwardedOption<T> Forward<T>(this ForwardedOption<T> option) => option.SetForwardingFunction((T o) => new string[] { option.Name });

public static ForwardedOption<T> ForwardAs<T>(this ForwardedOption<T> option, string value) => option.SetForwardingFunction((o) => new string[] { value });
public static ForwardedOption<T> ForwardAs<T>(this ForwardedOption<T> option, string value) => option.SetForwardingFunction((T o) => new string[] { value });

public static ForwardedOption<T> ForwardAsSingle<T>(this ForwardedOption<T> option, Func<T, string> format) => option.SetForwardingFunction(format);

Expand Down Expand Up @@ -81,6 +81,12 @@ public ForwardedOption<T> SetForwardingFunction(Func<T, string> format)
return this;
}

public ForwardedOption<T> SetForwardingFunction(Func<T, ParseResult, IEnumerable<string>> func)
{
ForwardingFunction = (ParseResult parseResult) => parseResult.HasOption(Aliases.First()) ? func(parseResult.ValueForOption<T>(Aliases.First()), parseResult) : Array.Empty<string>();
return this;
}

public Func<ParseResult, IEnumerable<string>> GetForwardingFunction(Func<T, IEnumerable<string>> func)
{
return (ParseResult parseResult) => parseResult.HasOption(Aliases.First()) ? func(parseResult.ValueForOption<T>(Aliases.First())) : Array.Empty<string>();
Expand Down
15 changes: 15 additions & 0 deletions src/Cli/dotnet/ParseResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,20 @@ private static string GetSymbolResultValue(ParseResult parseResult, SymbolResult
return string.Empty;
}
}

public static bool BothArchAndOsOptionsSpecified(this ParseResult parseResult) =>
parseResult.HasOption(CommonOptions.ArchitectureOption().Aliases.First()) &&
parseResult.HasOption(CommonOptions.OperatingSystemOption().Aliases.First());

internal static string GetCommandLineRuntimeIdentifier(this ParseResult parseResult)
{
return parseResult.HasOption(RunCommandParser.RuntimeOption) ?
parseResult.ValueForOption<string>(RunCommandParser.RuntimeOption) :
parseResult.HasOption(CommonOptions.OperatingSystemOption().Aliases.First()) || parseResult.HasOption(CommonOptions.ArchitectureOption().Aliases.First()) ?
CommonOptions.ResolveRidShorthandOptionsToRuntimeIdentifier(
parseResult.ValueForOption<string>(CommonOptions.OperatingSystemOption().Aliases.First()),
parseResult.ValueForOption<string>(CommonOptions.ArchitectureOption().Aliases.First())) :
null;
}
}
}
2 changes: 2 additions & 0 deletions src/Cli/dotnet/commands/dotnet-build/BuildCommandParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public static Command GetCommand()
command.AddOption(NoIncrementalOption);
command.AddOption(NoDependenciesOption);
command.AddOption(NoLogoOption);
command.AddOption(CommonOptions.ArchitectureOption());
command.AddOption(CommonOptions.OperatingSystemOption());

return command;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public static Command GetCommand()
command.AddOption(CommonOptions.InteractiveMsBuildForwardOption());
command.AddOption(NoRestoreOption);
command.AddOption(CommonOptions.VerbosityOption());
command.AddOption(CommonOptions.ArchitectureOption());
command.AddOption(CommonOptions.OperatingSystemOption());

return command;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Cli/dotnet/commands/dotnet-run/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static RunCommand FromArgs(string[] args)
var command = new RunCommand(
configuration: parseResult.ValueForOption<string>(RunCommandParser.ConfigurationOption),
framework: parseResult.ValueForOption<string>(RunCommandParser.FrameworkOption),
runtime: parseResult.ValueForOption<string>(RunCommandParser.RuntimeOption),
runtime: parseResult.GetCommandLineRuntimeIdentifier(),
noBuild: parseResult.HasOption(RunCommandParser.NoBuildOption),
project: project,
launchProfile: parseResult.ValueForOption<string>(RunCommandParser.LaunchProfileOption),
Expand Down
2 changes: 2 additions & 0 deletions src/Cli/dotnet/commands/dotnet-run/RunCommandParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public static Command GetCommand()
command.AddOption(InteractiveOption);
command.AddOption(NoRestoreOption);
command.AddOption(CommonOptions.VerbosityOption());
command.AddOption(CommonOptions.ArchitectureOption());
command.AddOption(CommonOptions.OperatingSystemOption());
sfoslund marked this conversation as resolved.
Show resolved Hide resolved
Comment on lines +50 to +51
Copy link
Member

Choose a reason for hiding this comment

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

Does does the fact that these options have a forwarding function interfere here, since the runtime is passed to the RunCommand as a parameter instead of as additional MSBuild arguments like the other commands?

Copy link
Member Author

Choose a reason for hiding this comment

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

We account for these options here: https://github.com/dotnet/sdk/pull/18889/files#diff-c59ee7ae48403cce44671ddc3549eb921e7c23bda26840c883b29d9f7e7942e5R38, the forwarding function shouldn't interfere.

command.TreatUnmatchedTokensAsErrors = false;

return command;
Expand Down
2 changes: 2 additions & 0 deletions src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ public static Command GetCommand()
command.AddOption(NoRestoreOption);
command.AddOption(CommonOptions.InteractiveMsBuildForwardOption());
command.AddOption(CommonOptions.VerbosityOption());
command.AddOption(CommonOptions.ArchitectureOption(false));
command.AddOption(CommonOptions.OperatingSystemOption());
sfoslund marked this conversation as resolved.
Show resolved Hide resolved

return command;
}
Expand Down
25 changes: 25 additions & 0 deletions src/Cli/dotnet/xlf/CommonLocalizableStrings.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="cs" original="../CommonLocalizableStrings.resx">
<body>
<trans-unit id="ArchitectureOptionDescription">
<source>The target architecture.</source>
<target state="new">The target architecture.</target>
<note />
</trans-unit>
<trans-unit id="CannotResolveRuntimeIdentifier">
<source>Resolving the current runtime identifier failed.</source>
<target state="new">Resolving the current runtime identifier failed.</target>
<note />
</trans-unit>
<trans-unit id="CannotSpecifyBothRuntimeAndArchOptions">
<source>Specifying both the `-r|--runtime` and `-a|--arch` options is not supported.</source>
<target state="new">Specifying both the `-r|--runtime` and `-a|--arch` options is not supported.</target>
<note />
</trans-unit>
<trans-unit id="CannotSpecifyBothRuntimeAndOsOptions">
<source>Specifying both the `-r|--runtime` and `-os` options is not supported.</source>
<target state="new">Specifying both the `-r|--runtime` and `-os` options is not supported.</target>
<note />
</trans-unit>
<trans-unit id="CommandInteractiveOptionDescription">
<source>Allows the command to stop and wait for user input or action (for example to complete authentication).</source>
<target state="translated">Umožňuje, aby se příkaz zastavil a počkal na vstup nebo akci uživatele (například na dokončení ověření).</target>
Expand Down Expand Up @@ -80,6 +100,11 @@ export PATH="$PATH:{0}"
<target state="translated">V {0} se našlo několik projektů. Vyberte, který z nich chcete použít.</target>
<note />
</trans-unit>
<trans-unit id="OperatingSystemOptionDescription">
<source>The target operating system.</source>
<target state="new">The target operating system.</target>
<note />
</trans-unit>
<trans-unit id="ProjectAlreadyHasAreference">
<source>Project already has a reference to `{0}`.</source>
<target state="translated">Projekt už obsahuje odkaz na {0}.</target>
Expand Down
25 changes: 25 additions & 0 deletions src/Cli/dotnet/xlf/CommonLocalizableStrings.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="de" original="../CommonLocalizableStrings.resx">
<body>
<trans-unit id="ArchitectureOptionDescription">
<source>The target architecture.</source>
<target state="new">The target architecture.</target>
<note />
</trans-unit>
<trans-unit id="CannotResolveRuntimeIdentifier">
<source>Resolving the current runtime identifier failed.</source>
<target state="new">Resolving the current runtime identifier failed.</target>
<note />
</trans-unit>
<trans-unit id="CannotSpecifyBothRuntimeAndArchOptions">
<source>Specifying both the `-r|--runtime` and `-a|--arch` options is not supported.</source>
<target state="new">Specifying both the `-r|--runtime` and `-a|--arch` options is not supported.</target>
<note />
</trans-unit>
<trans-unit id="CannotSpecifyBothRuntimeAndOsOptions">
<source>Specifying both the `-r|--runtime` and `-os` options is not supported.</source>
<target state="new">Specifying both the `-r|--runtime` and `-os` options is not supported.</target>
<note />
</trans-unit>
<trans-unit id="CommandInteractiveOptionDescription">
<source>Allows the command to stop and wait for user input or action (for example to complete authentication).</source>
<target state="translated">Hiermit wird zugelassen, dass der Befehl anhält und auf eine Benutzereingabe oder Aktion wartet (beispielsweise auf den Abschluss der Authentifizierung).</target>
Expand Down Expand Up @@ -80,6 +100,11 @@ export PATH="$PATH:{0}"
<target state="translated">In "{0}" wurden mehrere Projekte gefunden. Geben Sie an, welches davon verwendet werden soll.</target>
<note />
</trans-unit>
<trans-unit id="OperatingSystemOptionDescription">
<source>The target operating system.</source>
<target state="new">The target operating system.</target>
<note />
</trans-unit>
<trans-unit id="ProjectAlreadyHasAreference">
<source>Project already has a reference to `{0}`.</source>
<target state="translated">Für das Projekt ist bereits ein Verweis auf "{0}" vorhanden.</target>
Expand Down
25 changes: 25 additions & 0 deletions src/Cli/dotnet/xlf/CommonLocalizableStrings.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,26 @@
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd">
<file datatype="xml" source-language="en" target-language="es" original="../CommonLocalizableStrings.resx">
<body>
<trans-unit id="ArchitectureOptionDescription">
<source>The target architecture.</source>
<target state="new">The target architecture.</target>
<note />
</trans-unit>
<trans-unit id="CannotResolveRuntimeIdentifier">
<source>Resolving the current runtime identifier failed.</source>
<target state="new">Resolving the current runtime identifier failed.</target>
<note />
</trans-unit>
<trans-unit id="CannotSpecifyBothRuntimeAndArchOptions">
<source>Specifying both the `-r|--runtime` and `-a|--arch` options is not supported.</source>
<target state="new">Specifying both the `-r|--runtime` and `-a|--arch` options is not supported.</target>
<note />
</trans-unit>
<trans-unit id="CannotSpecifyBothRuntimeAndOsOptions">
<source>Specifying both the `-r|--runtime` and `-os` options is not supported.</source>
<target state="new">Specifying both the `-r|--runtime` and `-os` options is not supported.</target>
<note />
</trans-unit>
<trans-unit id="CommandInteractiveOptionDescription">
<source>Allows the command to stop and wait for user input or action (for example to complete authentication).</source>
<target state="translated">Permite que el comando se detenga y espere la entrada o acción del usuario (por ejemplo, para autenticarse).</target>
Expand Down Expand Up @@ -80,6 +100,11 @@ export PATH="$PATH:{0}"
<target state="translated">Se han encontrado varios proyectos en "{0}". Especifique el que debe usarse.</target>
<note />
</trans-unit>
<trans-unit id="OperatingSystemOptionDescription">
<source>The target operating system.</source>
<target state="new">The target operating system.</target>
<note />
</trans-unit>
<trans-unit id="ProjectAlreadyHasAreference">
<source>Project already has a reference to `{0}`.</source>
<target state="translated">El proyecto ya tiene una referencia a "{0}".</target>
Expand Down
Loading