| File: src\sdk\src\Cli\dotnet\ParserOptionActions.cs | Web Access |
| Project: src\sdk\src\Cli\dotnet-aot\dotnet-aot.csproj (dotnet-aot) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; using System.CommandLine.Invocation; using System.Runtime.InteropServices; #if !CLI_AOT using Microsoft.DotNet.Cli.CommandLine; #endif using Microsoft.DotNet.Cli.Commands.Workload; using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Help; using Microsoft.DotNet.Cli.Utils; #if !CLI_AOT using Microsoft.DotNet.Configurer; #endif using RuntimeEnvironment = Microsoft.DotNet.Cli.Utils.RuntimeEnvironment; namespace Microsoft.DotNet.Cli; /// <summary> /// Represents an option that contains an invocable action. /// These are essentially commands that are only defined as an option. /// </summary> internal abstract class InvocableOptionAction(Option option) : SynchronousCommandLineAction { /// <summary> /// The option for which this action is bound. /// </summary> public Option Option { get; } = option; } #if !CLI_AOT internal class HandleDiagnosticAction(Option<bool> option) : InvocableOptionAction(option) { public override bool Terminating => false; // S.CL will always invoke this option action because: // 1. A boolean option has a default value of `false`. // 2. The option has Terminating set to `false`. // When a default value exists (non-null) and the option is not terminating, the option action is always invoked. public override int Invoke(ParseResult parseResult) { // This check is necessary as per comment above. if (!parseResult.HasOption(Option) || !parseResult.GetValue(option) // Only set verbose output on built-in commands. || !parseResult.IsDotnetBuiltInCommand()) { return 0; } // Determine whether the diagnostic option should be attached to the dotnet command or the subcommand. if (OptionPrecedesSubcommand(parseResult.Tokens.Select(t => t.Value), parseResult.RootSubCommandResult())) { Environment.SetEnvironmentVariable(CommandLoggingContext.Variables.Verbose, bool.TrueString); CommandLoggingContext.SetVerbose(true); Reporter.Reset(); var home = Env.GetEnvironmentVariable(CliFolderPathCalculator.DotnetHomeVariableName); if (!string.IsNullOrEmpty(home)) { // Output DOTNET_CLI_HOME usage when verbosity is enabled. Reporter.Verbose.WriteLine(string.Format(LocalizableStrings.DotnetCliHomeUsed, home, CliFolderPathCalculator.DotnetHomeVariableName)); } } return 0; } private bool OptionPrecedesSubcommand(IEnumerable<string> tokens, string subCommand) { if (string.IsNullOrEmpty(subCommand)) { return true; } foreach (var token in tokens) { if (token == subCommand) { return false; } if (Option.Name == token || Option.Aliases.Contains(token)) { return true; } } return false; } } #endif internal class PrintHelpAction(Option option, HelpBuilder builder) : InvocableOptionAction(option) { public override bool Terminating => true; public override bool ClearsParseErrors => true; private HelpBuilder Builder { get; } = builder; public override int Invoke(ParseResult parseResult) { var command = parseResult.CommandResult.Command; var output = parseResult.InvocationConfiguration.Output; var helpContext = new HelpContext(Builder, command, output, parseResult); Builder.Write(helpContext); return 0; } } internal class PrintVersionAction(Option<bool> option) : InvocableOptionAction(option) { public override bool Terminating => true; public override int Invoke(ParseResult parseResult) { // GetResult(Option) is { Implicit: false } is the AOT-friendly equivalent of parseResult.HasOption(Option); // the HasOption extension lives in Microsoft.DotNet.Cli.CommandLine, which the AOT build doesn't reference. if (parseResult.GetResult(Option) is not { Implicit: false } || !parseResult.GetValue(option) // Only print for top-level commands. || !parseResult.IsTopLevelDotnetCommand()) { return 0; } Reporter.Output.WriteLine(Product.Version); return 0; } } internal class PrintInfoAction(Option<bool> option) : InvocableOptionAction(option) { public override bool Terminating => true; public override int Invoke(ParseResult parseResult) { if (parseResult.GetResult(Option) is not { Implicit: false } || !parseResult.GetValue(option) // Only print for top-level commands. || !parseResult.IsTopLevelDotnetCommand()) { return 0; } DotnetVersionFile versionFile = DotnetFiles.VersionFileObject; var commitSha = versionFile.CommitSha ?? "N/A"; Reporter.Output.WriteLine($"{LocalizableStrings.DotNetSdkInfoLabel}"); Reporter.Output.WriteLine($" Version: {Product.Version}"); Reporter.Output.WriteLine($" Commit: {commitSha}"); Reporter.Output.WriteLine($" Workload version: {WorkloadInfoHelper.GetWorkloadsVersion()}"); Reporter.Output.WriteLine($" MSBuild version: {MSBuildForwardingAppWithoutLogging.MSBuildVersion}"); Reporter.Output.WriteLine(); Reporter.Output.WriteLine($"{LocalizableStrings.DotNetRuntimeInfoLabel}"); Reporter.Output.WriteLine($" OS Name: {RuntimeEnvironment.OperatingSystem}"); Reporter.Output.WriteLine($" OS Version: {RuntimeEnvironment.OperatingSystemVersion}"); Reporter.Output.WriteLine($" OS Platform: {RuntimeEnvironment.OperatingSystemPlatform}"); #if !CLI_AOT Reporter.Output.WriteLine($" RID: {GetDisplayRid(versionFile)}"); #else // GetDisplayRid consults the shared framework's deps file, which isn't available in AOT. Reporter.Output.WriteLine($" RID: {RuntimeInformation.RuntimeIdentifier}"); #endif #if CLI_AOT // In the AOT bubble AppContext.BaseDirectory is the muxer/install root, so report the resolved // versioned SDK directory instead. The managed CLI keeps AppContext.BaseDirectory - it is correct // there and preserves the existing output (including its trailing directory separator). Reporter.Output.WriteLine($" Base Path: {SdkPaths.SdkDirectory}"); #else Reporter.Output.WriteLine($" Base Path: {AppContext.BaseDirectory}"); #endif Reporter.Output.WriteLine(); Reporter.Output.WriteLine($"{LocalizableStrings.DotnetWorkloadInfoLabel}"); new WorkloadInfoHelper(isInteractive: false).ShowWorkloadsInfo(showVersion: false); return 0; } #if !CLI_AOT private static string? GetDisplayRid(DotnetVersionFile versionFile) { FrameworkDependencyFile fxDepsFile = new(); string currentRid = RuntimeInformation.RuntimeIdentifier; // If the current RID isn't supported by the shared framework, display the RID the CLI was built with instead, // so the user knows which RID they should put in their "runtimes" section. return fxDepsFile.IsRuntimeSupported(currentRid) ? currentRid : versionFile.BuildRid; } #endif } internal class PrintCliSchemaAction(Option<bool> option) : InvocableOptionAction(option) { public override bool Terminating => true; public override int Invoke(ParseResult parseResult) { if (parseResult.GetResult(Option) is not { Implicit: false } || !parseResult.GetValue(option)) { return 0; } CliSchema.PrintCliSchema( parseResult, parseResult.InvocationConfiguration.Output, #if CLI_AOT telemetryClient: NativeEntryPoint.TelemetryClient #else telemetryClient: Program.TelemetryInstance #endif ); return 0; } }