diff --git a/exclusion.dic b/exclusion.dic index 3073f4e5eeea..d6aba193f5c8 100644 --- a/exclusion.dic +++ b/exclusion.dic @@ -1,9 +1,12 @@ +ansi awaiter +commandline crossgen csharp cshtml deserializer msbuild +muxer nuget nupkg nuspec @@ -14,6 +17,7 @@ tfm tfms url urls +uuid xamarin xunit yaml \ No newline at end of file diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/AnsiConsole.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/AnsiConsole.cs index ec85798f8740..74df09331cb5 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/AnsiConsole.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/AnsiConsole.cs @@ -1,156 +1,155 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public class AnsiConsole { - public class AnsiConsole - { - private const int Light = 0x08; - private readonly bool _ansiEnabled; + private const int Light = 0x08; + private readonly bool _ansiEnabled; - private AnsiConsole(TextWriter writer) - { - Writer = writer; + private AnsiConsole(TextWriter writer) + { + Writer = writer; - OriginalForegroundColor = Console.ForegroundColor; - _boldRecursion = ((int)OriginalForegroundColor & Light) != 0 ? 1 : 0; + OriginalForegroundColor = Console.ForegroundColor; + _boldRecursion = ((int)OriginalForegroundColor & Light) != 0 ? 1 : 0; - _ansiEnabled = string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("NO_COLOR")); - } + _ansiEnabled = string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("NO_COLOR")); + } - private int _boldRecursion; + private int _boldRecursion; - public static AnsiConsole GetOutput() - { - return new AnsiConsole(Console.Out); - } + public static AnsiConsole GetOutput() + { + return new AnsiConsole(Console.Out); + } - public static AnsiConsole GetError() - { - return new AnsiConsole(Console.Error); - } + public static AnsiConsole GetError() + { + return new AnsiConsole(Console.Error); + } - public TextWriter Writer { get; } + public TextWriter Writer { get; } - public ConsoleColor OriginalForegroundColor { get; } + public ConsoleColor OriginalForegroundColor { get; } - private void SetColor(ConsoleColor color) + private void SetColor(ConsoleColor color) + { + if (!_ansiEnabled) { - if (!_ansiEnabled) - { - return; - } - - int c = (int)color; - - Console.ForegroundColor = - c < 0 ? color : // unknown, just use it - _boldRecursion > 0 ? (ConsoleColor)(c | Light) : // ensure color is light - (ConsoleColor)(c & ~Light); // ensure color is dark + return; } - private void SetBold(bool bold) - { - if (!_ansiEnabled) - { - return; - } + int c = (int)color; - _boldRecursion += bold ? 1 : -1; - if (_boldRecursion > 1 || (_boldRecursion == 1 && !bold)) - { - return; - } + Console.ForegroundColor = + c < 0 ? color : // unknown, just use it + _boldRecursion > 0 ? (ConsoleColor)(c | Light) : // ensure color is light + (ConsoleColor)(c & ~Light); // ensure color is dark + } - // switches on _boldRecursion to handle boldness - SetColor(Console.ForegroundColor); + private void SetBold(bool bold) + { + if (!_ansiEnabled) + { + return; } - public void WriteLine(string message) + _boldRecursion += bold ? 1 : -1; + if (_boldRecursion > 1 || (_boldRecursion == 1 && !bold)) { - Write(message); - Writer.WriteLine(); + return; } + // switches on _boldRecursion to handle boldness + SetColor(Console.ForegroundColor); + } - public void Write(string message) + public void WriteLine(string message) + { + Write(message); + Writer.WriteLine(); + } + + + public void Write(string message) + { + var escapeScan = 0; + for (; ; ) { - var escapeScan = 0; - for (; ; ) + var escapeIndex = message.IndexOf("\x1b[", escapeScan, StringComparison.Ordinal); + if (escapeIndex == -1) { - var escapeIndex = message.IndexOf("\x1b[", escapeScan, StringComparison.Ordinal); - if (escapeIndex == -1) + var text = message.Substring(escapeScan); + Writer.Write(text); + break; + } + else + { + var startIndex = escapeIndex + 2; + var endIndex = startIndex; + while (endIndex != message.Length && + message[endIndex] >= 0x20 && + message[endIndex] <= 0x3f) { - var text = message.Substring(escapeScan); - Writer.Write(text); - break; + endIndex += 1; } - else + + var text = message.Substring(escapeScan, escapeIndex - escapeScan); + Writer.Write(text); + if (endIndex == message.Length) { - var startIndex = escapeIndex + 2; - var endIndex = startIndex; - while (endIndex != message.Length && - message[endIndex] >= 0x20 && - message[endIndex] <= 0x3f) - { - endIndex += 1; - } - - var text = message.Substring(escapeScan, escapeIndex - escapeScan); - Writer.Write(text); - if (endIndex == message.Length) - { - break; - } + break; + } - switch (message[endIndex]) - { - case 'm': - int value; - if (int.TryParse(message.Substring(startIndex, endIndex - startIndex), out value)) + switch (message[endIndex]) + { + case 'm': + int value; + if (int.TryParse(message.Substring(startIndex, endIndex - startIndex), out value)) + { + switch (value) { - switch (value) - { - case 1: - SetBold(true); - break; - case 22: - SetBold(false); - break; - case 30: - SetColor(ConsoleColor.Black); - break; - case 31: - SetColor(ConsoleColor.Red); - break; - case 32: - SetColor(ConsoleColor.Green); - break; - case 33: - SetColor(ConsoleColor.Yellow); - break; - case 34: - SetColor(ConsoleColor.Blue); - break; - case 35: - SetColor(ConsoleColor.Magenta); - break; - case 36: - SetColor(ConsoleColor.Cyan); - break; - case 37: - SetColor(ConsoleColor.Gray); - break; - case 39: - Console.ForegroundColor = OriginalForegroundColor; - break; - } + case 1: + SetBold(true); + break; + case 22: + SetBold(false); + break; + case 30: + SetColor(ConsoleColor.Black); + break; + case 31: + SetColor(ConsoleColor.Red); + break; + case 32: + SetColor(ConsoleColor.Green); + break; + case 33: + SetColor(ConsoleColor.Yellow); + break; + case 34: + SetColor(ConsoleColor.Blue); + break; + case 35: + SetColor(ConsoleColor.Magenta); + break; + case 36: + SetColor(ConsoleColor.Cyan); + break; + case 37: + SetColor(ConsoleColor.Gray); + break; + case 39: + Console.ForegroundColor = OriginalForegroundColor; + break; } - break; - } - - escapeScan = endIndex + 1; + } + break; } + + escapeScan = endIndex + 1; } } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/ArgumentEscaper.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/ArgumentEscaper.cs index 9b0f2382cffc..703ccf2d605b 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/ArgumentEscaper.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/ArgumentEscaper.cs @@ -179,21 +179,14 @@ private static string EscapeArgForCmd(string argument) return sb.ToString(); } - internal static bool ShouldSurroundWithQuotes(string argument) - { + internal static bool ShouldSurroundWithQuotes(string argument) => // Only quote if whitespace exists in the string - return ArgumentContainsWhitespace(argument); - } + ArgumentContainsWhitespace(argument); - internal static bool IsSurroundedWithQuotes(string argument) - { - return argument.StartsWith("\"", StringComparison.Ordinal) && - argument.EndsWith("\"", StringComparison.Ordinal); - } + internal static bool IsSurroundedWithQuotes(string argument) => + argument.StartsWith("\"", StringComparison.Ordinal) && argument.EndsWith("\"", StringComparison.Ordinal); - internal static bool ArgumentContainsWhitespace(string argument) - { - return argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n"); - } + internal static bool ArgumentContainsWhitespace(string argument) => + argument.Contains(" ") || argument.Contains("\t") || argument.Contains("\n"); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/BlockingMemoryStream.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/BlockingMemoryStream.cs index eca1bbbc9c9b..60d3597d5c95 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/BlockingMemoryStream.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/BlockingMemoryStream.cs @@ -10,7 +10,7 @@ namespace Microsoft.DotNet.Cli.Utils /// public sealed class BlockingMemoryStream : Stream { - private readonly BlockingCollection _buffers = new(); + private readonly BlockingCollection _buffers = []; private ArraySegment _remaining; public override void Write(byte[] buffer, int offset, int count) diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs index b6cbc01c78c2..6b0d3ff90997 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/BuiltInCommand.cs @@ -3,183 +3,182 @@ using System.Diagnostics; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +/// +/// A Command that is capable of running in the current process. +/// +public class BuiltInCommand : ICommand { - /// - /// A Command that is capable of running in the current process. - /// - public class BuiltInCommand : ICommand + private readonly IEnumerable _commandArgs; + private readonly Func _builtInCommand; + private readonly IBuiltInCommandEnvironment _environment; + private readonly StreamForwarder _stdOut; + private readonly StreamForwarder _stdErr; + private string? _workingDirectory; + + public string CommandName { get; } + public string CommandArgs => string.Join(" ", _commandArgs); + + public BuiltInCommand(string commandName, IEnumerable commandArgs, Func builtInCommand) + : this(commandName, commandArgs, builtInCommand, new BuiltInCommandEnvironment()) { - private readonly IEnumerable _commandArgs; - private readonly Func _builtInCommand; - private readonly IBuiltInCommandEnvironment _environment; - private readonly StreamForwarder _stdOut; - private readonly StreamForwarder _stdErr; - private string? _workingDirectory; - - public string CommandName { get; } - public string CommandArgs => string.Join(" ", _commandArgs); - - public BuiltInCommand(string commandName, IEnumerable commandArgs, Func builtInCommand) - : this(commandName, commandArgs, builtInCommand, new BuiltInCommandEnvironment()) - { - } + } - internal BuiltInCommand(string commandName, IEnumerable commandArgs, Func builtInCommand, IBuiltInCommandEnvironment environment) - { - CommandName = commandName; - _commandArgs = commandArgs; - _builtInCommand = builtInCommand; - _environment = environment; + internal BuiltInCommand(string commandName, IEnumerable commandArgs, Func builtInCommand, IBuiltInCommandEnvironment environment) + { + CommandName = commandName; + _commandArgs = commandArgs; + _builtInCommand = builtInCommand; + _environment = environment; - _stdOut = new StreamForwarder(); - _stdErr = new StreamForwarder(); - } + _stdOut = new StreamForwarder(); + _stdErr = new StreamForwarder(); + } - public CommandResult Execute() - { - TextWriter originalConsoleOut = _environment.GetConsoleOut(); - TextWriter originalConsoleError = _environment.GetConsoleError(); - string originalWorkingDirectory = _environment.GetWorkingDirectory(); + public CommandResult Execute() + { + TextWriter originalConsoleOut = _environment.GetConsoleOut(); + TextWriter originalConsoleError = _environment.GetConsoleError(); + string originalWorkingDirectory = _environment.GetWorkingDirectory(); - try + try + { + // redirecting the standard out and error so we can forward + // the output to the caller + using (BlockingMemoryStream outStream = new()) + using (BlockingMemoryStream errorStream = new()) { - // redirecting the standard out and error so we can forward - // the output to the caller - using (BlockingMemoryStream outStream = new()) - using (BlockingMemoryStream errorStream = new()) - { - _environment.SetConsoleOut(new StreamWriter(outStream) { AutoFlush = true }); - _environment.SetConsoleError(new StreamWriter(errorStream) { AutoFlush = true }); - - // Reset the Reporters to the new Console Out and Error. - Reporter.Reset(); + _environment.SetConsoleOut(new StreamWriter(outStream) { AutoFlush = true }); + _environment.SetConsoleError(new StreamWriter(errorStream) { AutoFlush = true }); - if (_workingDirectory is not null && _workingDirectory.Length != 0) - { - _environment.SetWorkingDirectory(_workingDirectory); - } + // Reset the Reporters to the new Console Out and Error. + Reporter.Reset(); - var taskOut = _stdOut.BeginRead(new StreamReader(outStream)); - var taskErr = _stdErr.BeginRead(new StreamReader(errorStream)); + if (_workingDirectory is not null && _workingDirectory.Length != 0) + { + _environment.SetWorkingDirectory(_workingDirectory); + } - int exitCode = _builtInCommand(_commandArgs.ToArray()); + var taskOut = _stdOut.BeginRead(new StreamReader(outStream)); + var taskErr = _stdErr.BeginRead(new StreamReader(errorStream)); - outStream.DoneWriting(); - errorStream.DoneWriting(); + int exitCode = _builtInCommand(_commandArgs.ToArray()); - Task.WaitAll(taskOut, taskErr); + outStream.DoneWriting(); + errorStream.DoneWriting(); - // fake out a ProcessStartInfo using the Muxer command name, since this is a built-in command - ProcessStartInfo startInfo = new(new Muxer().MuxerPath, $"{CommandName} {CommandArgs}"); - return new CommandResult(startInfo, exitCode, null, null); - } - } - finally - { - _environment.SetConsoleOut(originalConsoleOut); - _environment.SetConsoleError(originalConsoleError); - _environment.SetWorkingDirectory(originalWorkingDirectory); + Task.WaitAll(taskOut, taskErr); - Reporter.Reset(); + // fake out a ProcessStartInfo using the Muxer command name, since this is a built-in command + ProcessStartInfo startInfo = new(new Muxer().MuxerPath, $"{CommandName} {CommandArgs}"); + return new CommandResult(startInfo, exitCode, null, null); } } - - public ICommand OnOutputLine(Action handler) + finally { - if (handler == null) - { - throw new ArgumentNullException(nameof(handler)); - } - - _stdOut.ForwardTo(writeLine: handler); + _environment.SetConsoleOut(originalConsoleOut); + _environment.SetConsoleError(originalConsoleError); + _environment.SetWorkingDirectory(originalWorkingDirectory); - return this; + Reporter.Reset(); } + } - public ICommand OnErrorLine(Action handler) + public ICommand OnOutputLine(Action handler) + { + if (handler == null) { - if (handler == null) - { - throw new ArgumentNullException(nameof(handler)); - } - - _stdErr.ForwardTo(writeLine: handler); - - return this; + throw new ArgumentNullException(nameof(handler)); } - public ICommand WorkingDirectory(string workingDirectory) - { - _workingDirectory = workingDirectory; + _stdOut.ForwardTo(writeLine: handler); - return this; - } + return this; + } - private class BuiltInCommandEnvironment : IBuiltInCommandEnvironment + public ICommand OnErrorLine(Action handler) + { + if (handler == null) { - public TextWriter GetConsoleOut() - { - return Console.Out; - } - - public void SetConsoleOut(TextWriter newOut) - { - Console.SetOut(newOut); - } + throw new ArgumentNullException(nameof(handler)); + } - public TextWriter GetConsoleError() - { - return Console.Error; - } + _stdErr.ForwardTo(writeLine: handler); - public void SetConsoleError(TextWriter newError) - { - Console.SetError(newError); - } + return this; + } - public string GetWorkingDirectory() - { - return Directory.GetCurrentDirectory(); - } + public ICommand WorkingDirectory(string workingDirectory) + { + _workingDirectory = workingDirectory; - public void SetWorkingDirectory(string path) - { - Directory.SetCurrentDirectory(path); - } - } + return this; + } - public ICommand CaptureStdErr() + private class BuiltInCommandEnvironment : IBuiltInCommandEnvironment + { + public TextWriter GetConsoleOut() { - _stdErr.Capture(); - - return this; + return Console.Out; } - public ICommand CaptureStdOut() + public void SetConsoleOut(TextWriter newOut) { - _stdOut.Capture(); - - return this; + Console.SetOut(newOut); } - public ICommand EnvironmentVariable(string name, string? value) + public TextWriter GetConsoleError() { - throw new NotImplementedException(); + return Console.Error; } - public ICommand ForwardStdErr(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true) + public void SetConsoleError(TextWriter newError) { - throw new NotImplementedException(); + Console.SetError(newError); } - public ICommand ForwardStdOut(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true) + public string GetWorkingDirectory() { - throw new NotImplementedException(); + return Directory.GetCurrentDirectory(); } - public ICommand SetCommandArgs(string commandArgs) + + public void SetWorkingDirectory(string path) { - throw new NotImplementedException(); + Directory.SetCurrentDirectory(path); } } + + public ICommand CaptureStdErr() + { + _stdErr.Capture(); + + return this; + } + + public ICommand CaptureStdOut() + { + _stdOut.Capture(); + + return this; + } + + public ICommand EnvironmentVariable(string name, string? value) + { + throw new NotImplementedException(); + } + + public ICommand ForwardStdErr(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true) + { + throw new NotImplementedException(); + } + + public ICommand ForwardStdOut(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true) + { + throw new NotImplementedException(); + } + public ICommand SetCommandArgs(string commandArgs) + { + throw new NotImplementedException(); + } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Command.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Command.cs index c216d4acaede..ca09f20c1960 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Command.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Command.cs @@ -5,225 +5,224 @@ using System.Runtime.CompilerServices; using Microsoft.DotNet.Cli.Utils.Extensions; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public class Command : ICommand { - public class Command : ICommand + private readonly Process _process; + + private StreamForwarder? _stdOut; + + private StreamForwarder? _stdErr; + + private bool _running = false; + + private bool _trimTrailingNewlines = false; + + public Command(Process? process, bool trimTrailingNewlines = false) { - private readonly Process _process; + _trimTrailingNewlines = trimTrailingNewlines; + _process = process ?? throw new ArgumentNullException(nameof(process)); + } - private StreamForwarder? _stdOut; + public CommandResult Execute() + { + return Execute(null); + } + public CommandResult Execute(Action? processStarted) + { + Reporter.Verbose.WriteLine(string.Format( + LocalizableStrings.RunningFileNameArguments, + _process.StartInfo.FileName, + _process.StartInfo.Arguments)); - private StreamForwarder? _stdErr; + ThrowIfRunning(); - private bool _running = false; + _running = true; - private bool _trimTrailingNewlines = false; + _process.EnableRaisingEvents = true; - public Command(Process? process, bool trimTrailingNewlines = false) + Stopwatch? sw = null; + if (CommandLoggingContext.IsVerbose) { - _trimTrailingNewlines = trimTrailingNewlines; - _process = process ?? throw new ArgumentNullException(nameof(process)); - } + sw = Stopwatch.StartNew(); - public CommandResult Execute() - { - return Execute(null); + Reporter.Verbose.WriteLine($"> {Command.FormatProcessInfo(_process.StartInfo)}".White()); } - public CommandResult Execute(Action? processStarted) + + using (var reaper = new ProcessReaper(_process)) { + _process.Start(); + if (processStarted != null) + { + processStarted(_process); + } + reaper.NotifyProcessStarted(); + Reporter.Verbose.WriteLine(string.Format( - LocalizableStrings.RunningFileNameArguments, - _process.StartInfo.FileName, - _process.StartInfo.Arguments)); + LocalizableStrings.ProcessId, + _process.Id)); - ThrowIfRunning(); + var taskOut = _stdOut?.BeginRead(_process.StandardOutput); + var taskErr = _stdErr?.BeginRead(_process.StandardError); + _process.WaitForExit(); - _running = true; + taskOut?.Wait(); + taskErr?.Wait(); + } - _process.EnableRaisingEvents = true; + var exitCode = _process.ExitCode; - Stopwatch? sw = null; - if (CommandLoggingContext.IsVerbose) + if (CommandLoggingContext.IsVerbose) + { + Debug.Assert(sw is not null); + var message = string.Format( + LocalizableStrings.ProcessExitedWithCode, + Command.FormatProcessInfo(_process.StartInfo), + exitCode, + sw.ElapsedMilliseconds); + if (exitCode == 0) { - sw = Stopwatch.StartNew(); - - Reporter.Verbose.WriteLine($"> {FormatProcessInfo(_process.StartInfo)}".White()); + Reporter.Verbose.WriteLine(message.Green()); } - - using (var reaper = new ProcessReaper(_process)) + else { - _process.Start(); - if (processStarted != null) - { - processStarted(_process); - } - reaper.NotifyProcessStarted(); - - Reporter.Verbose.WriteLine(string.Format( - LocalizableStrings.ProcessId, - _process.Id)); - - var taskOut = _stdOut?.BeginRead(_process.StandardOutput); - var taskErr = _stdErr?.BeginRead(_process.StandardError); - _process.WaitForExit(); - - taskOut?.Wait(); - taskErr?.Wait(); + Reporter.Verbose.WriteLine(message.Red().Bold()); } + } - var exitCode = _process.ExitCode; + return new CommandResult( + _process.StartInfo, + exitCode, + _stdOut?.CapturedOutput, + _stdErr?.CapturedOutput); + } - if (CommandLoggingContext.IsVerbose) - { - Debug.Assert(sw is not null); - var message = string.Format( - LocalizableStrings.ProcessExitedWithCode, - FormatProcessInfo(_process.StartInfo), - exitCode, - sw.ElapsedMilliseconds); - if (exitCode == 0) - { - Reporter.Verbose.WriteLine(message.Green()); - } - else - { - Reporter.Verbose.WriteLine(message.Red().Bold()); - } - } + public ICommand WorkingDirectory(string? projectDirectory) + { + _process.StartInfo.WorkingDirectory = projectDirectory; + return this; + } - return new CommandResult( - _process.StartInfo, - exitCode, - _stdOut?.CapturedOutput, - _stdErr?.CapturedOutput); - } + public ICommand EnvironmentVariable(string name, string? value) + { + _process.StartInfo.Environment[name] = value; + return this; + } - public ICommand WorkingDirectory(string? projectDirectory) - { - _process.StartInfo.WorkingDirectory = projectDirectory; - return this; - } + public ICommand CaptureStdOut() + { + ThrowIfRunning(); + EnsureStdOut(); + _stdOut?.Capture(_trimTrailingNewlines); + return this; + } - public ICommand EnvironmentVariable(string name, string? value) - { - _process.StartInfo.Environment[name] = value; - return this; - } + public ICommand CaptureStdErr() + { + ThrowIfRunning(); + EnsureStdErr(); + _stdErr?.Capture(_trimTrailingNewlines); + return this; + } - public ICommand CaptureStdOut() + public ICommand ForwardStdOut(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true) + { + ThrowIfRunning(); + if (!onlyIfVerbose || CommandLoggingContext.IsVerbose) { - ThrowIfRunning(); EnsureStdOut(); - _stdOut?.Capture(_trimTrailingNewlines); - return this; + + if (to == null) + { + _stdOut?.ForwardTo(writeLine: Reporter.Output.WriteLine); + EnvironmentVariable(CommandLoggingContext.Variables.AnsiPassThru, ansiPassThrough.ToString()); + } + else + { + _stdOut?.ForwardTo(writeLine: to.WriteLine); + } } + return this; + } - public ICommand CaptureStdErr() + public ICommand ForwardStdErr(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true) + { + ThrowIfRunning(); + if (!onlyIfVerbose || CommandLoggingContext.IsVerbose) { - ThrowIfRunning(); EnsureStdErr(); - _stdErr?.Capture(_trimTrailingNewlines); - return this; - } - public ICommand ForwardStdOut(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true) - { - ThrowIfRunning(); - if (!onlyIfVerbose || CommandLoggingContext.IsVerbose) + if (to == null) { - EnsureStdOut(); - - if (to == null) - { - _stdOut?.ForwardTo(writeLine: Reporter.Output.WriteLine); - EnvironmentVariable(CommandLoggingContext.Variables.AnsiPassThru, ansiPassThrough.ToString()); - } - else - { - _stdOut?.ForwardTo(writeLine: to.WriteLine); - } + _stdErr?.ForwardTo(writeLine: Reporter.Error.WriteLine); + EnvironmentVariable(CommandLoggingContext.Variables.AnsiPassThru, ansiPassThrough.ToString()); } - return this; - } - - public ICommand ForwardStdErr(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true) - { - ThrowIfRunning(); - if (!onlyIfVerbose || CommandLoggingContext.IsVerbose) + else { - EnsureStdErr(); - - if (to == null) - { - _stdErr?.ForwardTo(writeLine: Reporter.Error.WriteLine); - EnvironmentVariable(CommandLoggingContext.Variables.AnsiPassThru, ansiPassThrough.ToString()); - } - else - { - _stdErr?.ForwardTo(writeLine: to.WriteLine); - } + _stdErr?.ForwardTo(writeLine: to.WriteLine); } - return this; } + return this; + } - public ICommand OnOutputLine(Action handler) - { - ThrowIfRunning(); - EnsureStdOut(); + public ICommand OnOutputLine(Action handler) + { + ThrowIfRunning(); + EnsureStdOut(); - _stdOut?.ForwardTo(writeLine: handler); - return this; - } + _stdOut?.ForwardTo(writeLine: handler); + return this; + } - public ICommand OnErrorLine(Action handler) - { - ThrowIfRunning(); - EnsureStdErr(); + public ICommand OnErrorLine(Action handler) + { + ThrowIfRunning(); + EnsureStdErr(); - _stdErr?.ForwardTo(writeLine: handler); - return this; - } + _stdErr?.ForwardTo(writeLine: handler); + return this; + } - public string CommandName => _process.StartInfo.FileName; + public string CommandName => _process.StartInfo.FileName; - public string CommandArgs => _process.StartInfo.Arguments; + public string CommandArgs => _process.StartInfo.Arguments; - public ICommand SetCommandArgs(string commandArgs) - { - _process.StartInfo.Arguments = commandArgs; - return this; - } + public ICommand SetCommandArgs(string commandArgs) + { + _process.StartInfo.Arguments = commandArgs; + return this; + } - private string FormatProcessInfo(ProcessStartInfo info) + private static string FormatProcessInfo(ProcessStartInfo info) + { + if (string.IsNullOrWhiteSpace(info.Arguments)) { - if (string.IsNullOrWhiteSpace(info.Arguments)) - { - return info.FileName; - } - - return info.FileName + " " + info.Arguments; + return info.FileName; } - private void EnsureStdOut() - { - _stdOut ??= new StreamForwarder(); - _process.StartInfo.RedirectStandardOutput = true; - } + return info.FileName + " " + info.Arguments; + } - private void EnsureStdErr() - { - _stdErr ??= new StreamForwarder(); - _process.StartInfo.RedirectStandardError = true; - } + private void EnsureStdOut() + { + _stdOut ??= new StreamForwarder(); + _process.StartInfo.RedirectStandardOutput = true; + } + + private void EnsureStdErr() + { + _stdErr ??= new StreamForwarder(); + _process.StartInfo.RedirectStandardError = true; + } - private void ThrowIfRunning([CallerMemberName] string? memberName = null) + private void ThrowIfRunning([CallerMemberName] string? memberName = null) + { + if (_running) { - if (_running) - { - throw new InvalidOperationException(string.Format( - LocalizableStrings.UnableToInvokeMemberNameAfterCommand, - memberName)); - } + throw new InvalidOperationException(string.Format( + LocalizableStrings.UnableToInvokeMemberNameAfterCommand, + memberName)); } } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/CommandAvailableAsDotNetToolException.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/CommandAvailableAsDotNetToolException.cs index d0f98f1b3889..fe95d9b762d6 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/CommandAvailableAsDotNetToolException.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/CommandAvailableAsDotNetToolException.cs @@ -1,27 +1,26 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public class CommandAvailableAsDotNetToolException : GracefulException { - public class CommandAvailableAsDotNetToolException : GracefulException + public CommandAvailableAsDotNetToolException(string commandName) : base(GetMessage(commandName)) { - public CommandAvailableAsDotNetToolException(string commandName) : base(GetMessage(commandName)) - { - } + } - public CommandAvailableAsDotNetToolException(string commandName, Exception innerException) : base( - GetMessage(commandName), innerException) - { - } + public CommandAvailableAsDotNetToolException(string commandName, Exception innerException) : base( + GetMessage(commandName), innerException) + { + } - private static string GetMessage(string commandName) - { - var commandRemoveLeadingDotnet = commandName.Replace("dotnet-", string.Empty); - var packageName = "dotnet-" + commandRemoveLeadingDotnet.ToLower(); + private static string GetMessage(string commandName) + { + var commandRemoveLeadingDotnet = commandName.Replace("dotnet-", string.Empty); + var packageName = "dotnet-" + commandRemoveLeadingDotnet.ToLower(); - return string.Format(LocalizableStrings.CannotFindCommandAvailableAsTool, - commandRemoveLeadingDotnet, - packageName); - } + return string.Format(LocalizableStrings.CannotFindCommandAvailableAsTool, + commandRemoveLeadingDotnet, + packageName); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/CommandLoggingContext.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/CommandLoggingContext.cs index 2b572377c17e..eb9b9585cc85 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/CommandLoggingContext.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/CommandLoggingContext.cs @@ -1,75 +1,74 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +/// +/// Defines settings for logging. +/// +public static class CommandLoggingContext { - /// - /// Defines settings for logging. - /// - public static class CommandLoggingContext + public static class Variables { - public static class Variables - { - private const string Prefix = "DOTNET_CLI_CONTEXT_"; - public static readonly string Verbose = Prefix + "VERBOSE"; - internal static readonly string Output = Prefix + "OUTPUT"; - internal static readonly string Error = Prefix + "ERROR"; - internal static readonly string AnsiPassThru = Prefix + "ANSI_PASS_THRU"; - } + private const string Prefix = "DOTNET_CLI_CONTEXT_"; + public static readonly string Verbose = Prefix + "VERBOSE"; + internal static readonly string Output = Prefix + "OUTPUT"; + internal static readonly string Error = Prefix + "ERROR"; + internal static readonly string AnsiPassThru = Prefix + "ANSI_PASS_THRU"; + } - private static Lazy s_verbose = new(() => Env.GetEnvironmentVariableAsBool(Variables.Verbose)); - private static Lazy s_output = new(() => Env.GetEnvironmentVariableAsBool(Variables.Output, true)); - private static Lazy s_error = new(() => Env.GetEnvironmentVariableAsBool(Variables.Error, true)); - private static readonly Lazy s_ansiPassThru = new(() => Env.GetEnvironmentVariableAsBool(Variables.AnsiPassThru)); + private static Lazy s_verbose = new(() => Env.GetEnvironmentVariableAsBool(Variables.Verbose)); + private static Lazy s_output = new(() => Env.GetEnvironmentVariableAsBool(Variables.Output, true)); + private static Lazy s_error = new(() => Env.GetEnvironmentVariableAsBool(Variables.Error, true)); + private static readonly Lazy s_ansiPassThru = new(() => Env.GetEnvironmentVariableAsBool(Variables.AnsiPassThru)); - /// - /// True if the verbose output is enabled. - /// - public static bool IsVerbose => s_verbose.Value; + /// + /// True if the verbose output is enabled. + /// + public static bool IsVerbose => s_verbose.Value; - public static bool ShouldPassAnsiCodesThrough => s_ansiPassThru.Value; + public static bool ShouldPassAnsiCodesThrough => s_ansiPassThru.Value; - /// - /// Sets or resets the verbose output. - /// - /// - /// After calling, consider calling to apply change to reporter. - /// - public static void SetVerbose(bool value) - { - s_verbose = new Lazy(() => value); - } + /// + /// Sets or resets the verbose output. + /// + /// + /// After calling, consider calling to apply change to reporter. + /// + public static void SetVerbose(bool value) + { + s_verbose = new Lazy(() => value); + } - /// - /// Sets or resets the normal output. - /// - /// - /// After calling, consider calling to apply change to reporter. - /// - public static void SetOutput(bool value) - { - s_output = new Lazy(() => value); - } + /// + /// Sets or resets the normal output. + /// + /// + /// After calling, consider calling to apply change to reporter. + /// + public static void SetOutput(bool value) + { + s_output = new Lazy(() => value); + } - /// - /// Sets or resets the error output. - /// - /// - /// After calling, consider calling to apply change to reporter. - /// - public static void SetError(bool value) - { - s_error = new Lazy(() => value); - } + /// + /// Sets or resets the error output. + /// + /// + /// After calling, consider calling to apply change to reporter. + /// + public static void SetError(bool value) + { + s_error = new Lazy(() => value); + } - /// - /// True if normal output is enabled. - /// - internal static bool OutputEnabled => s_output.Value; + /// + /// True if normal output is enabled. + /// + internal static bool OutputEnabled => s_output.Value; - /// - /// True if error output is enabled. - /// - internal static bool ErrorEnabled => s_error.Value; - } + /// + /// True if error output is enabled. + /// + internal static bool ErrorEnabled => s_error.Value; } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/CommandResult.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/CommandResult.cs index 6700a22b26ee..f1d66b71920d 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/CommandResult.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/CommandResult.cs @@ -3,23 +3,14 @@ using System.Diagnostics; -namespace Microsoft.DotNet.Cli.Utils -{ - public struct CommandResult - { - public static readonly CommandResult Empty = new(); +namespace Microsoft.DotNet.Cli.Utils; - public ProcessStartInfo StartInfo { get; } - public int ExitCode { get; } - public string? StdOut { get; } - public string? StdErr { get; } +public readonly struct CommandResult(ProcessStartInfo startInfo, int exitCode, string? stdOut, string? stdErr) +{ + public static readonly CommandResult Empty = new(); - public CommandResult(ProcessStartInfo startInfo, int exitCode, string? stdOut, string? stdErr) - { - StartInfo = startInfo; - ExitCode = exitCode; - StdOut = stdOut; - StdErr = stdErr; - } - } + public ProcessStartInfo StartInfo { get; } = startInfo; + public int ExitCode { get; } = exitCode; + public string? StdOut { get; } = stdOut; + public string? StdErr { get; } = stdErr; } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/CommandUnknownException.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/CommandUnknownException.cs index 66a162f11c39..1990d2cc8576 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/CommandUnknownException.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/CommandUnknownException.cs @@ -1,26 +1,25 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public class CommandUnknownException : GracefulException { - public class CommandUnknownException : GracefulException - { - public string InstructionMessage { get; } = string.Empty; + public string InstructionMessage { get; } = string.Empty; - public CommandUnknownException(string commandName) : base( - LocalizableStrings.NoExecutableFoundMatchingCommandErrorMessage) - { - InstructionMessage = string.Format( - LocalizableStrings.NoExecutableFoundMatchingCommand, - commandName); - } + public CommandUnknownException(string commandName) : base( + LocalizableStrings.NoExecutableFoundMatchingCommandErrorMessage) + { + InstructionMessage = string.Format( + LocalizableStrings.NoExecutableFoundMatchingCommand, + commandName); + } - public CommandUnknownException(string commandName, Exception innerException) : base( - LocalizableStrings.NoExecutableFoundMatchingCommandErrorMessage) - { - InstructionMessage = string.Format( - LocalizableStrings.NoExecutableFoundMatchingCommand, - commandName); - } + public CommandUnknownException(string commandName, Exception innerException) : base( + LocalizableStrings.NoExecutableFoundMatchingCommandErrorMessage) + { + InstructionMessage = string.Format( + LocalizableStrings.NoExecutableFoundMatchingCommand, + commandName); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs index 5fff0de94751..b98e32204600 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs @@ -1,33 +1,32 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public static class Constants { - public static class Constants - { - public const string DefaultConfiguration = "Debug"; + public const string DefaultConfiguration = "Debug"; - public static readonly string ProjectFileName = "project.json"; - public static readonly string ToolManifestFileName = "dotnet-tools.json"; - public static readonly string DotConfigDirectoryName = ".config"; - public static readonly string ExeSuffix = - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty; + public static readonly string ProjectFileName = "project.json"; + public static readonly string ToolManifestFileName = "dotnet-tools.json"; + public static readonly string DotConfigDirectoryName = ".config"; + public static readonly string ExeSuffix = + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty; - public static readonly string BinDirectoryName = "bin"; - public static readonly string ObjDirectoryName = "obj"; - public static readonly string GitDirectoryName = ".git"; + public static readonly string BinDirectoryName = "bin"; + public static readonly string ObjDirectoryName = "obj"; + public static readonly string GitDirectoryName = ".git"; - public static readonly string MSBUILD_EXE_PATH = "MSBUILD_EXE_PATH"; - public static readonly string MSBuildExtensionsPath = "MSBuildExtensionsPath"; - public static readonly string EnableDefaultItems = "EnableDefaultItems"; + public static readonly string MSBUILD_EXE_PATH = "MSBUILD_EXE_PATH"; + public static readonly string MSBuildExtensionsPath = "MSBuildExtensionsPath"; + public static readonly string EnableDefaultItems = "EnableDefaultItems"; - public static readonly string ProjectArgumentName = ""; - public static readonly string SolutionArgumentName = ""; - public static readonly string ToolPackageArgumentName = ""; + public static readonly string ProjectArgumentName = ""; + public static readonly string SolutionArgumentName = ""; + public static readonly string ToolPackageArgumentName = ""; - public static readonly string AnyRid = "any"; + public static readonly string AnyRid = "any"; - public static readonly string RestoreInteractiveOption = "--interactive"; - public static readonly string workloadSetVersionFileName = "workloadVersion.txt"; - } + public static readonly string RestoreInteractiveOption = "--interactive"; + public static readonly string workloadSetVersionFileName = "workloadVersion.txt"; } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/DangerousFileDetector.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/DangerousFileDetector.cs index c95e71562daf..1868136eaf6c 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/DangerousFileDetector.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/DangerousFileDetector.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NETCOREAPP using System.Runtime.Versioning; +#endif namespace Microsoft.DotNet.Cli.Utils { diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/DebugHelper.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/DebugHelper.cs index 8344c7d6cbc6..052fb025a1a5 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/DebugHelper.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/DebugHelper.cs @@ -3,31 +3,30 @@ using System.Diagnostics; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public static class DebugHelper { - public static class DebugHelper + [Conditional("DEBUG")] + public static void HandleDebugSwitch(ref string[] args) { - [Conditional("DEBUG")] - public static void HandleDebugSwitch(ref string[] args) + if (args.Length > 0 && string.Equals("--debug", args[0], StringComparison.OrdinalIgnoreCase)) { - if (args.Length > 0 && string.Equals("--debug", args[0], StringComparison.OrdinalIgnoreCase)) - { - args = args.Skip(1).ToArray(); - WaitForDebugger(); - } + args = args.Skip(1).ToArray(); + WaitForDebugger(); } + } - public static void WaitForDebugger() - { + public static void WaitForDebugger() + { #if NET5_0_OR_GREATER - int processId = Environment.ProcessId; + int processId = Environment.ProcessId; #else - int processId = Process.GetCurrentProcess().Id; + int processId = Process.GetCurrentProcess().Id; #endif - Console.WriteLine(LocalizableStrings.WaitingForDebuggerToAttach); - Console.WriteLine(string.Format(LocalizableStrings.ProcessId, processId)); - Console.ReadLine(); - } + Console.WriteLine(LocalizableStrings.WaitingForDebuggerToAttach); + Console.WriteLine(string.Format(LocalizableStrings.ProcessId, processId)); + Console.ReadLine(); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/DependencyProvider.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/DependencyProvider.cs index 1f4279fb1d1f..fa8ad29e1754 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/DependencyProvider.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/DependencyProvider.cs @@ -1,198 +1,200 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET using System.Runtime.Versioning; +#endif + using Microsoft.Win32; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +/// +/// +/// Represents an installer dependency provider used to manage reference counts against an installer (MSI). +/// A dependency provider is an artificial construct introduced in WiX v3.6 to support reference counting installation +/// packages. MSIs that support this include a custom table, WixDependencyProvider, and a registry entry that writes the +/// provider key to SOFTWARE\Classes\Installer\Dependencies\{provider key name} under HKLM or HKCU. +/// +/// +/// Installers like chainers register a reference by writing a value under the Dependents subkey of the provider key. When the +/// MSI is removed, a custom action is executed to determine if there are any remaining dependents and block the uninstall. +/// The check can be bypassed by setting IGNOREDEPENDENCIES=ALL. When a chainer removes the MSI, it first removes its +/// dependent entry. If there are no other dependents, it can proceed to remove the MSI, otherwise it should do nothing. +/// +/// +#if NET +[SupportedOSPlatform("windows")] +#endif +public sealed class DependencyProvider { /// - /// - /// Represents an installer dependency provider used to manage reference counts against an installer (MSI). - /// A dependency provider is an artificial construct introduced in WiX v3.6 to support reference counting installation - /// packages. MSIs that support this include a custom table, WixDependencyProvider, and a registry entry that writes the - /// provider key to SOFTWARE\Classes\Installer\Dependencies\{provider key name} under HKLM or HKCU. - /// - /// - /// Installers like chainers register a reference by writing a value under the Dependents subkey of the provider key. When the - /// MSI is removed, a custom action is executed to determine if there are any remaining dependents and block the uninstall. - /// The check can be bypassed by setting IGNOREDEPENDENCIES=ALL. When a chainer removes the MSI, it first removes its - /// dependent entry. If there are no other dependents, it can proceed to remove the MSI, otherwise it should do nothing. - /// + /// The key name used by Visual Studio 2015 and later to register a dependency. /// -#if NET - [SupportedOSPlatform("windows")] -#endif - public sealed class DependencyProvider - { - /// - /// The key name used by Visual Studio 2015 and later to register a dependency. - /// - internal const string VisualStudioDependentKeyName = "VS.{AEF703B8-D2CC-4343-915C-F54A30B90937}"; - - /// - /// The relative path from the to the Dependencies key. - /// - internal const string DependenciesKeyRelativePath = @"SOFTWARE\Classes\Installer\Dependencies"; - - /// - /// if the dependency provider is associated with a per-machine - /// installation; otherwise. - /// - public readonly bool AllUsers; - - /// - /// Returns the root key to use: for per-machine installations or - /// for per-user installations. - /// - public readonly RegistryKey BaseKey; - - /// - /// Gets all dependents associated with the provider key. The property always enumerates the - /// provider's dependent entries in the registry. - /// - public IEnumerable Dependents => GetDependents(); - - /// - /// The path of the key where the provider's dependents are stored, relative to the . - /// - public readonly string DependentsKeyPath; - - /// - /// if Visual Studio 2015 or later is registered as a dependent. Visual Studio only - /// writes a single entry, regardless of how many instances have taken dependencies. - /// - public bool HasVisualStudioDependency => Dependents.Contains(VisualStudioDependentKeyName); - - /// - /// The name of the dependency provider key used for tracking reference counts. - /// - public readonly string ProviderKeyName; - - /// - /// The product code of the MSI associated with the dependency provider. - /// - public string? ProductCode => GetProductCode(); - - /// - /// The path of the provider key, relative to the . - /// - public readonly string ProviderKeyPath; - - /// - /// Creates a new instance. - /// - /// The name of the dependency provider key. - /// if the provider belongs to a per-machine installation; - /// otherwise. - public DependencyProvider(string providerKeyName, bool allUsers = true) - { - if (providerKeyName is null) - { - throw new ArgumentNullException(nameof(providerKeyName)); - } + internal const string VisualStudioDependentKeyName = "VS.{AEF703B8-D2CC-4343-915C-F54A30B90937}"; - if (string.IsNullOrWhiteSpace(providerKeyName)) - { - throw new ArgumentException($"{nameof(providerKeyName)} cannot be empty."); - } + /// + /// The relative path from the to the Dependencies key. + /// + internal const string DependenciesKeyRelativePath = @"SOFTWARE\Classes\Installer\Dependencies"; - ProviderKeyName = providerKeyName; - AllUsers = allUsers; - BaseKey = AllUsers ? Registry.LocalMachine : Registry.CurrentUser; - ProviderKeyPath = $@"{DependenciesKeyRelativePath}\{ProviderKeyName}"; - DependentsKeyPath = $@"{ProviderKeyPath}\Dependents"; - } + /// + /// if the dependency provider is associated with a per-machine + /// installation; otherwise. + /// + public readonly bool AllUsers; - /// - /// Adds the specified dependent to the provider key. The dependent is stored as a subkey under the Dependents key of - /// the provider."/> - /// - /// The dependent to add. - public void AddDependent(string dependent) - { - if (dependent is null) - { - throw new ArgumentNullException(nameof(dependent)); - } + /// + /// Returns the root key to use: for per-machine installations or + /// for per-user installations. + /// + public readonly RegistryKey BaseKey; - if (string.IsNullOrWhiteSpace(dependent)) - { - throw new ArgumentException($"{nameof(dependent)} cannot be empty."); - } + /// + /// Gets all dependents associated with the provider key. The property always enumerates the + /// provider's dependent entries in the registry. + /// + public IEnumerable Dependents => GetDependents(); - using RegistryKey dependentsKey = BaseKey.CreateSubKey(Path.Combine(DependentsKeyPath, dependent), writable: true); - } + /// + /// The path of the key where the provider's dependents are stored, relative to the . + /// + public readonly string DependentsKeyPath; + + /// + /// if Visual Studio 2015 or later is registered as a dependent. Visual Studio only + /// writes a single entry, regardless of how many instances have taken dependencies. + /// + public bool HasVisualStudioDependency => Dependents.Contains(VisualStudioDependentKeyName); - /// - /// Remove the specified dependent from the provider key. Optionally, if this is the final dependent, - /// the provider key can also be removed. This is typically done during an uninstall. - /// - /// The dependent to remove. - /// When , delete the provider key if the dependent being - /// removed is the last dependent. - public void RemoveDependent(string dependent, bool removeProvider) + /// + /// The name of the dependency provider key used for tracking reference counts. + /// + public readonly string ProviderKeyName; + + /// + /// The product code of the MSI associated with the dependency provider. + /// + public string? ProductCode => GetProductCode(); + + /// + /// The path of the provider key, relative to the . + /// + public readonly string ProviderKeyPath; + + /// + /// Creates a new instance. + /// + /// The name of the dependency provider key. + /// if the provider belongs to a per-machine installation; + /// otherwise. + public DependencyProvider(string providerKeyName, bool allUsers = true) + { + if (providerKeyName is null) { - if (dependent is null) - { - throw new ArgumentNullException(nameof(dependent)); - } + throw new ArgumentNullException(nameof(providerKeyName)); + } - if (string.IsNullOrWhiteSpace(dependent)) - { - throw new ArgumentException($"{nameof(dependent)} cannot be empty."); - } + if (string.IsNullOrWhiteSpace(providerKeyName)) + { + throw new ArgumentException($"{nameof(providerKeyName)} cannot be empty."); + } - using RegistryKey? dependentsKey = BaseKey.OpenSubKey(DependentsKeyPath, writable: true); - dependentsKey?.DeleteSubKeyTree(dependent); + ProviderKeyName = providerKeyName; + AllUsers = allUsers; + BaseKey = AllUsers ? Registry.LocalMachine : Registry.CurrentUser; + ProviderKeyPath = $@"{DependenciesKeyRelativePath}\{ProviderKeyName}"; + DependentsKeyPath = $@"{ProviderKeyPath}\Dependents"; + } - if ((removeProvider) && (Dependents.Count() == 0)) - { - using RegistryKey? providerKey = BaseKey.OpenSubKey(DependenciesKeyRelativePath, writable: true); - providerKey?.DeleteSubKeyTree(ProviderKeyName); - } + /// + /// Adds the specified dependent to the provider key. The dependent is stored as a subkey under the Dependents key of + /// the provider."/> + /// + /// The dependent to add. + public void AddDependent(string dependent) + { + if (dependent is null) + { + throw new ArgumentNullException(nameof(dependent)); } - /// - /// Gets all the dependents associated with the provider key. - /// - /// All dependents of the provider key. - private IEnumerable GetDependents() + if (string.IsNullOrWhiteSpace(dependent)) { - using RegistryKey? dependentsKey = BaseKey.OpenSubKey(DependentsKeyPath); + throw new ArgumentException($"{nameof(dependent)} cannot be empty."); + } + + using RegistryKey dependentsKey = BaseKey.CreateSubKey(Path.Combine(DependentsKeyPath, dependent), writable: true); + } - return dependentsKey?.GetSubKeyNames() ?? Enumerable.Empty(); + /// + /// Remove the specified dependent from the provider key. Optionally, if this is the final dependent, + /// the provider key can also be removed. This is typically done during an uninstall. + /// + /// The dependent to remove. + /// When , delete the provider key if the dependent being + /// removed is the last dependent. + public void RemoveDependent(string dependent, bool removeProvider) + { + if (dependent is null) + { + throw new ArgumentNullException(nameof(dependent)); } - /// - /// Gets the ProductCode associated with this dependency provider. The ProductCode is stored in the default - /// value. - /// - /// The ProductCode associated with this dependency provider or if it does not exist. - private string? GetProductCode() + if (string.IsNullOrWhiteSpace(dependent)) { - using RegistryKey? providerKey = BaseKey.OpenSubKey(ProviderKeyPath); - return providerKey?.GetValue(null) as string ?? null; + throw new ArgumentException($"{nameof(dependent)} cannot be empty."); } - public override string ToString() => ProviderKeyName; + using RegistryKey? dependentsKey = BaseKey.OpenSubKey(DependentsKeyPath, writable: true); + dependentsKey?.DeleteSubKeyTree(dependent); - public static DependencyProvider? GetFromProductCode(string productCode, bool allUsers = true) + if ((removeProvider) && (Dependents.Count() == 0)) { - var baseKey = allUsers ? Registry.LocalMachine : Registry.CurrentUser; - using RegistryKey? dependenciesKey = baseKey.OpenSubKey(DependenciesKeyRelativePath); + using RegistryKey? providerKey = BaseKey.OpenSubKey(DependenciesKeyRelativePath, writable: true); + providerKey?.DeleteSubKeyTree(ProviderKeyName); + } + } - foreach (var providerKeyName in dependenciesKey?.GetSubKeyNames() ?? []) + /// + /// Gets all the dependents associated with the provider key. + /// + /// All dependents of the provider key. + private IEnumerable GetDependents() + { + using RegistryKey? dependentsKey = BaseKey.OpenSubKey(DependentsKeyPath); + + return dependentsKey?.GetSubKeyNames() ?? Enumerable.Empty(); + } + + /// + /// Gets the ProductCode associated with this dependency provider. The ProductCode is stored in the default + /// value. + /// + /// The ProductCode associated with this dependency provider or if it does not exist. + private string? GetProductCode() + { + using RegistryKey? providerKey = BaseKey.OpenSubKey(ProviderKeyPath); + return providerKey?.GetValue(null) as string ?? null; + } + + public override string ToString() => ProviderKeyName; + + public static DependencyProvider? GetFromProductCode(string productCode, bool allUsers = true) + { + var baseKey = allUsers ? Registry.LocalMachine : Registry.CurrentUser; + using RegistryKey? dependenciesKey = baseKey.OpenSubKey(DependenciesKeyRelativePath); + + foreach (var providerKeyName in dependenciesKey?.GetSubKeyNames() ?? []) + { + using RegistryKey? providerKey = dependenciesKey?.OpenSubKey(providerKeyName); + var thisProductCode = providerKey?.GetValue(null) as string ?? null; + if (string.Equals(thisProductCode, productCode, StringComparison.OrdinalIgnoreCase)) { - using RegistryKey? providerKey = dependenciesKey?.OpenSubKey(providerKeyName); - var thisProductCode = providerKey?.GetValue(null) as string ?? null; - if (string.Equals(thisProductCode, productCode, StringComparison.OrdinalIgnoreCase)) - { - return new DependencyProvider(providerKeyName, allUsers); - } + return new DependencyProvider(providerKeyName, allUsers); } - - return null; } + + return null; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/DotDefaultPathCorrector.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/DotDefaultPathCorrector.cs index 2a248a03d167..5d16540a49e3 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/DotDefaultPathCorrector.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/DotDefaultPathCorrector.cs @@ -1,68 +1,67 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +/// +/// This class is to correct .default user's PATH caused by https://github.com/dotnet/sdk/issues/9762 +/// It will be run during elevated installer install. +/// For example and the usage reference the test DotDefaultPathCorrectorTests +/// +public static class DotDefaultPathCorrector { - /// - /// This class is to correct .default user's PATH caused by https://github.com/dotnet/sdk/issues/9762 - /// It will be run during elevated installer install. - /// For example and the usage reference the test DotDefaultPathCorrectorTests - /// - public static class DotDefaultPathCorrector - { - private const string DotnetToolsSuffix = @"\.dotnet\tools"; + private const string DotnetToolsSuffix = @"\.dotnet\tools"; - public static void Correct() + public static void Correct() + { + var pathEditor = new WindowsRegistryEnvironmentPathEditor(); + var dotDefaultPath = pathEditor.Get(SdkEnvironmentVariableTarget.DotDefault); + if (NeedCorrection(dotDefaultPath, out var correctedPath)) { - var pathEditor = new WindowsRegistryEnvironmentPathEditor(); - var dotDefaultPath = pathEditor.Get(SdkEnvironmentVariableTarget.DotDefault); - if (NeedCorrection(dotDefaultPath, out var correctedPath)) - { - pathEditor.Set(correctedPath, SdkEnvironmentVariableTarget.DotDefault); - } + pathEditor.Set(correctedPath, SdkEnvironmentVariableTarget.DotDefault); } + } - internal static bool NeedCorrection(string? existingPath, out string correctedPath) + internal static bool NeedCorrection(string? existingPath, out string correctedPath) + { + correctedPath = string.Empty; + if (existingPath is null || string.IsNullOrWhiteSpace(existingPath)) { - correctedPath = string.Empty; - if (existingPath is null || string.IsNullOrWhiteSpace(existingPath)) - { - return false; - } + return false; + } - IEnumerable paths = existingPath.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); + IEnumerable paths = existingPath.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); - var inCorrectToolsPaths = - paths.Where(p => p.EndsWith(DotnetToolsSuffix, StringComparison.OrdinalIgnoreCase)); + var inCorrectToolsPaths = + paths.Where(p => p.EndsWith(DotnetToolsSuffix, StringComparison.OrdinalIgnoreCase)); - if (!inCorrectToolsPaths.Any()) - { - return false; - } + if (!inCorrectToolsPaths.Any()) + { + return false; + } - var correctedPaths = paths - .Where(p => !p.EndsWith(DotnetToolsSuffix, StringComparison.OrdinalIgnoreCase)) - .Select(p => ReplaceExpandedUserProfile(p, inCorrectToolsPaths)); + var correctedPaths = paths + .Where(p => !p.EndsWith(DotnetToolsSuffix, StringComparison.OrdinalIgnoreCase)) + .Select(p => ReplaceExpandedUserProfile(p, inCorrectToolsPaths)); - correctedPath = string.Join(";", correctedPaths); + correctedPath = string.Join(";", correctedPaths); - return true; - } + return true; + } - private static string ReplaceExpandedUserProfile(string path, IEnumerable inCorrectToolsPaths) + private static string ReplaceExpandedUserProfile(string path, IEnumerable inCorrectToolsPaths) + { + foreach (var inCorrectToolsPath in inCorrectToolsPaths) { - foreach (var inCorrectToolsPath in inCorrectToolsPaths) - { - var expandedUserProfile = - inCorrectToolsPath.Substring(0, inCorrectToolsPath.Length - DotnetToolsSuffix.Length); + var expandedUserProfile = + inCorrectToolsPath.Substring(0, inCorrectToolsPath.Length - DotnetToolsSuffix.Length); - if (path.StartsWith(expandedUserProfile, StringComparison.OrdinalIgnoreCase)) - { - return path.Replace(expandedUserProfile, "%USERPROFILE%"); - } + if (path.StartsWith(expandedUserProfile, StringComparison.OrdinalIgnoreCase)) + { + return path.Replace(expandedUserProfile, "%USERPROFILE%"); } - - return path; } + + return path; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/DotnetFiles.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/DotnetFiles.cs index 3cb4555e26c1..a48329c7419a 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/DotnetFiles.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/DotnetFiles.cs @@ -2,25 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; -using Microsoft.DotNet.Cli.Utils; -namespace Microsoft.DotNet.Cli +namespace Microsoft.DotNet.Cli.Utils; + +internal static class DotnetFiles { - internal static class DotnetFiles - { - private static string SdkRootFolder => Path.Combine(typeof(DotnetFiles).GetTypeInfo().Assembly.Location, ".."); + private static string SdkRootFolder => Path.Combine(typeof(DotnetFiles).GetTypeInfo().Assembly.Location, ".."); - private static Lazy s_versionFileObject = - new(() => new DotnetVersionFile(VersionFile)); + private static readonly Lazy s_versionFileObject = + new(() => new DotnetVersionFile(VersionFile)); - /// - /// The SDK ships with a .version file that stores the commit information and SDK version - /// - public static string VersionFile => Path.GetFullPath(Path.Combine(SdkRootFolder, ".version")); + /// + /// The SDK ships with a .version file that stores the commit information and SDK version + /// + public static string VersionFile => Path.GetFullPath(Path.Combine(SdkRootFolder, ".version")); - internal static DotnetVersionFile VersionFileObject - { - get { return s_versionFileObject.Value; } - } - } + internal static DotnetVersionFile VersionFileObject => s_versionFileObject.Value; } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/DotnetVersionFile.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/DotnetVersionFile.cs index a3aa77fb1dc5..2794142d48f6 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/DotnetVersionFile.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/DotnetVersionFile.cs @@ -1,69 +1,68 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +internal class DotnetVersionFile { - internal class DotnetVersionFile - { - public bool Exists { get; init; } + public bool Exists { get; init; } - public string? CommitSha { get; init; } + public string? CommitSha { get; init; } - public string? BuildNumber { get; init; } + public string? BuildNumber { get; init; } - public string? FullNugetVersion { get; init; } + public string? FullNugetVersion { get; init; } - public string? SdkFeatureBand { get; init; } + public string? SdkFeatureBand { get; init; } - /// - /// The runtime identifier (rid) that this CLI was built for. - /// - /// - /// This is different than RuntimeInformation.RuntimeIdentifier because the - /// BuildRid is a RID that is guaranteed to exist and works on the current machine. The - /// RuntimeInformation.RuntimeIdentifier may be for a new version of the OS that - /// doesn't have full support yet. - /// - public string? BuildRid { get; init; } + /// + /// The runtime identifier (rid) that this CLI was built for. + /// + /// + /// This is different than RuntimeInformation.RuntimeIdentifier because the + /// BuildRid is a RID that is guaranteed to exist and works on the current machine. The + /// RuntimeInformation.RuntimeIdentifier may be for a new version of the OS that + /// doesn't have full support yet. + /// + public string? BuildRid { get; init; } - public DotnetVersionFile(string versionFilePath) + public DotnetVersionFile(string versionFilePath) + { + Exists = File.Exists(versionFilePath); + + if (Exists) { - Exists = File.Exists(versionFilePath); + IEnumerable lines = File.ReadLines(versionFilePath); - if (Exists) + int index = 0; + foreach (string line in lines) { - IEnumerable lines = File.ReadLines(versionFilePath); - - int index = 0; - foreach (string line in lines) + if (index == 0) { - if (index == 0) - { - CommitSha = line.Substring(0, 10); - } - else if (index == 1) - { - BuildNumber = line; - } - else if (index == 2) - { - BuildRid = line; - } - else if (index == 3) - { - FullNugetVersion = line; - } - else if (index == 4) - { - SdkFeatureBand = line; - } - else - { - break; - } - - index++; + CommitSha = line.Substring(0, 10); + } + else if (index == 1) + { + BuildNumber = line; } + else if (index == 2) + { + BuildRid = line; + } + else if (index == 3) + { + FullNugetVersion = line; + } + else if (index == 4) + { + SdkFeatureBand = line; + } + else + { + break; + } + + index++; } } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Env.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Env.cs index 8593276385fd..dbedb133bfc0 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Env.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Env.cs @@ -1,48 +1,29 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public static class Env { - public static class Env - { - private static IEnvironmentProvider _environment = new EnvironmentProvider(); - - public static IEnumerable ExecutableExtensions - { - get - { - return _environment.ExecutableExtensions; - } - } - - public static string? GetCommandPath(string commandName, params string[] extensions) - { - return _environment.GetCommandPath(commandName, extensions); - } - - public static string? GetCommandPathFromRootPath(string rootPath, string commandName, params string[] extensions) - { - return _environment.GetCommandPathFromRootPath(rootPath, commandName, extensions); - } - - public static string? GetCommandPathFromRootPath(string rootPath, string commandName, IEnumerable extensions) - { - return _environment.GetCommandPathFromRootPath(rootPath, commandName, extensions); - } - - public static bool GetEnvironmentVariableAsBool(string name, bool defaultValue = false) - { - return _environment.GetEnvironmentVariableAsBool(name, defaultValue); - } - - public static int? GetEnvironmentVariableAsNullableInt(string name) - { - return _environment.GetEnvironmentVariableAsNullableInt(name); - } - - public static string? GetEnvironmentVariable(string name) - { - return _environment.GetEnvironmentVariable(name); - } - } + private static readonly IEnvironmentProvider s_environment = new EnvironmentProvider(); + + public static IEnumerable ExecutableExtensions => s_environment.ExecutableExtensions; + + public static string? GetCommandPath(string commandName, params string[] extensions) => + s_environment.GetCommandPath(commandName, extensions); + + public static string? GetCommandPathFromRootPath(string rootPath, string commandName, params string[] extensions) => + s_environment.GetCommandPathFromRootPath(rootPath, commandName, extensions); + + public static string? GetCommandPathFromRootPath(string rootPath, string commandName, IEnumerable extensions) => + s_environment.GetCommandPathFromRootPath(rootPath, commandName, extensions); + + public static bool GetEnvironmentVariableAsBool(string name, bool defaultValue = false) => + s_environment.GetEnvironmentVariableAsBool(name, defaultValue); + + public static int? GetEnvironmentVariableAsNullableInt(string name) => + s_environment.GetEnvironmentVariableAsNullableInt(name); + + public static string? GetEnvironmentVariable(string name) => + s_environment.GetEnvironmentVariable(name); } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs index 11d823755e86..88fc2e64b328 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/EnvironmentProvider.cs @@ -3,158 +3,157 @@ using Microsoft.DotNet.Cli.Utils.Extensions; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public class EnvironmentProvider : IEnvironmentProvider { - public class EnvironmentProvider : IEnvironmentProvider - { - private static char[] s_pathSeparator = new char[] { Path.PathSeparator }; - private static char[] s_quote = new char[] { '"' }; - private IEnumerable? _searchPaths; - private readonly Lazy _userHomeDirectory = new(() => Environment.GetEnvironmentVariable("HOME") ?? string.Empty); - private IEnumerable? _executableExtensions; + private static char[] s_pathSeparator = new char[] { Path.PathSeparator }; + private static char[] s_quote = new char[] { '"' }; + private IEnumerable? _searchPaths; + private readonly Lazy _userHomeDirectory = new(() => Environment.GetEnvironmentVariable("HOME") ?? string.Empty); + private IEnumerable? _executableExtensions; - public IEnumerable ExecutableExtensions + public IEnumerable ExecutableExtensions + { + get { - get + if (_executableExtensions == null) { - if (_executableExtensions == null) - { - _executableExtensions = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) - ? Environment.GetEnvironmentVariable("PATHEXT")? - .Split(';') - .Select(e => e.ToLower().Trim('"')) ?? [string.Empty] - : [string.Empty]; - } - - return _executableExtensions; + _executableExtensions = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? Environment.GetEnvironmentVariable("PATHEXT")? + .Split(';') + .Select(e => e.ToLower().Trim('"')) ?? [string.Empty] + : [string.Empty]; } + + return _executableExtensions; } + } - private IEnumerable SearchPaths + private IEnumerable SearchPaths + { + get { - get + if (_searchPaths == null) { - if (_searchPaths == null) - { - var searchPaths = new List { AppContext.BaseDirectory }; + var searchPaths = new List { AppContext.BaseDirectory }; - searchPaths.AddRange(Environment - .GetEnvironmentVariable("PATH")? - .Split(s_pathSeparator) - .Select(p => p.Trim(s_quote)) - .Where(p => !string.IsNullOrWhiteSpace(p)) - .Select(p => ExpandTildeSlash(p)) ?? []); + searchPaths.AddRange(Environment + .GetEnvironmentVariable("PATH")? + .Split(s_pathSeparator) + .Select(p => p.Trim(s_quote)) + .Where(p => !string.IsNullOrWhiteSpace(p)) + .Select(p => ExpandTildeSlash(p)) ?? []); - _searchPaths = searchPaths; - } - - return _searchPaths; + _searchPaths = searchPaths; } + + return _searchPaths; } + } - private string ExpandTildeSlash(string path) + private string ExpandTildeSlash(string path) + { + const string tildeSlash = "~/"; + if (path.StartsWith(tildeSlash, StringComparison.Ordinal) && !string.IsNullOrEmpty(_userHomeDirectory.Value)) { - const string tildeSlash = "~/"; - if (path.StartsWith(tildeSlash, StringComparison.Ordinal) && !string.IsNullOrEmpty(_userHomeDirectory.Value)) - { - return Path.Combine(_userHomeDirectory.Value, path.Substring(tildeSlash.Length)); - } - else - { - return path; - } + return Path.Combine(_userHomeDirectory.Value, path.Substring(tildeSlash.Length)); } - - public EnvironmentProvider( - IEnumerable? extensionsOverride = null, - IEnumerable? searchPathsOverride = null) + else { - _executableExtensions = extensionsOverride; - _searchPaths = searchPathsOverride; + return path; } + } + + public EnvironmentProvider( + IEnumerable? extensionsOverride = null, + IEnumerable? searchPathsOverride = null) + { + _executableExtensions = extensionsOverride; + _searchPaths = searchPathsOverride; + } - public string? GetCommandPath(string commandName, params string[] extensions) + public string? GetCommandPath(string commandName, params string[] extensions) + { + if (!extensions.Any()) { - if (!extensions.Any()) - { - extensions = ExecutableExtensions.ToArray(); - } + extensions = ExecutableExtensions.ToArray(); + } - var commandPath = SearchPaths.Join( - extensions, - p => true, s => true, - (p, s) => Path.Combine(p, commandName + s)) - .FirstOrDefault(File.Exists); + var commandPath = SearchPaths.Join( + extensions, + p => true, s => true, + (p, s) => Path.Combine(p, commandName + s)) + .FirstOrDefault(File.Exists); - return commandPath; - } + return commandPath; + } - public string? GetCommandPathFromRootPath(string rootPath, string commandName, params string[] extensions) + public string? GetCommandPathFromRootPath(string rootPath, string commandName, params string[] extensions) + { + if (!extensions.Any()) { - if (!extensions.Any()) - { - extensions = ExecutableExtensions.ToArray(); - } + extensions = ExecutableExtensions.ToArray(); + } - var commandPath = extensions.Select(e => Path.Combine(rootPath, commandName + e)) - .FirstOrDefault(File.Exists); + var commandPath = extensions.Select(e => Path.Combine(rootPath, commandName + e)) + .FirstOrDefault(File.Exists); - return commandPath; - } + return commandPath; + } - public string? GetCommandPathFromRootPath(string rootPath, string commandName, IEnumerable extensions) - { - var extensionsArr = extensions.OrEmptyIfNull().ToArray(); + public string? GetCommandPathFromRootPath(string rootPath, string commandName, IEnumerable extensions) + { + var extensionsArr = extensions.OrEmptyIfNull().ToArray(); - return GetCommandPathFromRootPath(rootPath, commandName, extensionsArr); - } + return GetCommandPathFromRootPath(rootPath, commandName, extensionsArr); + } - public string? GetEnvironmentVariable(string name) + public string? GetEnvironmentVariable(string name) + { + return Environment.GetEnvironmentVariable(name); + } + + public bool GetEnvironmentVariableAsBool(string name, bool defaultValue) + { + var str = Environment.GetEnvironmentVariable(name); + if (string.IsNullOrEmpty(str)) { - return Environment.GetEnvironmentVariable(name); + return defaultValue; } - public bool GetEnvironmentVariableAsBool(string name, bool defaultValue) + switch (str.ToLowerInvariant()) { - var str = Environment.GetEnvironmentVariable(name); - if (string.IsNullOrEmpty(str)) - { + case "true": + case "1": + case "yes": + return true; + case "false": + case "0": + case "no": + return false; + default: return defaultValue; - } - - switch (str.ToLowerInvariant()) - { - case "true": - case "1": - case "yes": - return true; - case "false": - case "0": - case "no": - return false; - default: - return defaultValue; - } } + } - public string? GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) - { - return Environment.GetEnvironmentVariable(variable, target); - } + public string? GetEnvironmentVariable(string variable, EnvironmentVariableTarget target) + { + return Environment.GetEnvironmentVariable(variable, target); + } - public void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target) - { - Environment.SetEnvironmentVariable(variable, value, target); - } + public void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target) + { + Environment.SetEnvironmentVariable(variable, value, target); + } - public int? GetEnvironmentVariableAsNullableInt(string variable) + public int? GetEnvironmentVariableAsNullableInt(string variable) + { + if (Environment.GetEnvironmentVariable(variable) is string strValue && int.TryParse(strValue, out int intValue)) { - if (Environment.GetEnvironmentVariable(variable) is string strValue && int.TryParse(strValue, out int intValue)) - { - return intValue; - } - - return null; + return intValue; } + + return null; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/ExponentialRetry.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/ExponentialRetry.cs index 9c702c971917..b5c66fd68233 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/ExponentialRetry.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/ExponentialRetry.cs @@ -1,84 +1,83 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public static class ExponentialRetry { - public static class ExponentialRetry + public static IEnumerable Intervals { - public static IEnumerable Intervals + get { - get + yield return TimeSpan.FromSeconds(0); // first retry immediately + var seconds = 5; + while (true) { - yield return TimeSpan.FromSeconds(0); // first retry immediately - var seconds = 5; - while (true) - { - yield return TimeSpan.FromSeconds(seconds); - seconds *= 10; - } + yield return TimeSpan.FromSeconds(seconds); + seconds *= 10; } } + } - public static IEnumerable TestingIntervals + public static IEnumerable TestingIntervals + { + get { - get + while (true) { - while (true) - { - yield return TimeSpan.FromSeconds(0); - } + yield return TimeSpan.FromSeconds(0); } } + } - public static async Task ExecuteAsyncWithRetry(Func> action, - Func shouldStopRetry, - int maxRetryCount, - Func> timer, - string taskDescription = "") + public static async Task ExecuteAsyncWithRetry(Func> action, + Func shouldStopRetry, + int maxRetryCount, + Func> timer, + string taskDescription = "") + { + var count = 0; + foreach (var t in timer()) { - var count = 0; - foreach (var t in timer()) - { - await t; - T? result = default; - count++; + await t; + T? result = default; + count++; - result = await action(); + result = await action(); - if (shouldStopRetry(result)) - { - return result; - } + if (shouldStopRetry(result)) + { + return result; + } - if (count == maxRetryCount) - { - return result; - } + if (count == maxRetryCount) + { + return result; } - throw new Exception("Timer should not be exhausted"); } + throw new Exception("Timer should not be exhausted"); + } - public static async Task ExecuteWithRetry(Func action, - Func shouldStopRetry, - int maxRetryCount, - Func> timer, - string taskDescription = "") - { - Func> asyncAction = () => Task.FromResult(action()); - return await ExecuteAsyncWithRetry(asyncAction, shouldStopRetry, maxRetryCount, timer, taskDescription); - } + public static async Task ExecuteWithRetry(Func action, + Func shouldStopRetry, + int maxRetryCount, + Func> timer, + string taskDescription = "") + { + Func> asyncAction = () => Task.FromResult(action()); + return await ExecuteAsyncWithRetry(asyncAction, shouldStopRetry, maxRetryCount, timer, taskDescription); + } - public static async Task ExecuteWithRetryOnFailure(Func> action, - int maxRetryCount = 3, - Func>? timer = null) - { - timer = timer == null ? () => Timer(Intervals) : timer; - return await ExecuteAsyncWithRetry(action, result => result != null && !result.Equals(default), maxRetryCount, timer); - } + public static async Task ExecuteWithRetryOnFailure(Func> action, + int maxRetryCount = 3, + Func>? timer = null) + { + timer = timer == null ? () => Timer(Intervals) : timer; + return await ExecuteAsyncWithRetry(action, result => result != null && !result.Equals(default), maxRetryCount, timer); + } - public static IEnumerable Timer(IEnumerable interval) - { - return interval.Select(Task.Delay); - } + public static IEnumerable Timer(IEnumerable interval) + { + return interval.Select(Task.Delay); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/AnsiExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/AnsiExtensions.cs index be65fa03d7d7..703a6b9f53b8 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/AnsiExtensions.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/AnsiExtensions.cs @@ -1,77 +1,76 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils.Extensions +namespace Microsoft.DotNet.Cli.Utils.Extensions; + +public static class AnsiExtensions { - public static class AnsiExtensions - { - private static readonly Lazy _xtermEnabled = new( - () => + private static readonly Lazy _xtermEnabled = new( + () => + { + var environment = Environment.GetEnvironmentVariable("TERM"); + if (!string.IsNullOrWhiteSpace(environment)) { - var environment = Environment.GetEnvironmentVariable("TERM"); - if (!string.IsNullOrWhiteSpace(environment)) - { - return environment.IndexOf("xterm", StringComparison.OrdinalIgnoreCase) >= 0; - } + return environment.IndexOf("xterm", StringComparison.OrdinalIgnoreCase) >= 0; + } - return false; - }); + return false; + }); - public static string Black(this string text) - { - return "\x1B[30m" + text + "\x1B[39m"; - } + public static string Black(this string text) + { + return "\x1B[30m" + text + "\x1B[39m"; + } - public static string Red(this string text) - { - return "\x1B[31m" + text + "\x1B[39m"; - } - public static string Green(this string text) - { - return "\x1B[32m" + text + "\x1B[39m"; - } + public static string Red(this string text) + { + return "\x1B[31m" + text + "\x1B[39m"; + } + public static string Green(this string text) + { + return "\x1B[32m" + text + "\x1B[39m"; + } - public static string Yellow(this string text) - { - return "\x1B[33m" + text + "\x1B[39m"; - } + public static string Yellow(this string text) + { + return "\x1B[33m" + text + "\x1B[39m"; + } - public static string Blue(this string text) - { - return "\x1B[34m" + text + "\x1B[39m"; - } + public static string Blue(this string text) + { + return "\x1B[34m" + text + "\x1B[39m"; + } - public static string Magenta(this string text) - { - return "\x1B[35m" + text + "\x1B[39m"; - } + public static string Magenta(this string text) + { + return "\x1B[35m" + text + "\x1B[39m"; + } - public static string Cyan(this string text) - { - return "\x1B[36m" + text + "\x1B[39m"; - } + public static string Cyan(this string text) + { + return "\x1B[36m" + text + "\x1B[39m"; + } - public static string White(this string text) - { - return "\x1B[37m" + text + "\x1B[39m"; - } + public static string White(this string text) + { + return "\x1B[37m" + text + "\x1B[39m"; + } - public static string Bold(this string text) - { - return "\x1B[1m" + text + "\x1B[22m"; - } + public static string Bold(this string text) + { + return "\x1B[1m" + text + "\x1B[22m"; + } - /// - /// Wraps a string with ANSI escape codes to display it as a clickable URL in supported terminals. - /// - /// The URL to be wrapped. - /// The URL display text. - /// A string containing the URL wrapped with ANSI escape codes. - public static string Url(this string url, string displayText) - { - return _xtermEnabled.Value - ? "\x1B]8;;" + url + "\x1b\\" + displayText + "\x1b]8;;\x1b\\" - : url; - } + /// + /// Wraps a string with ANSI escape codes to display it as a clickable URL in supported terminals. + /// + /// The URL to be wrapped. + /// The URL display text. + /// A string containing the URL wrapped with ANSI escape codes. + public static string Url(this string url, string displayText) + { + return _xtermEnabled.Value + ? "\x1B]8;;" + url + "\x1b\\" + displayText + "\x1b]8;;\x1b\\" + : url; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/CollectionsExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/CollectionsExtensions.cs index 611a8fb08a44..2f2a74fac9d4 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/CollectionsExtensions.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/CollectionsExtensions.cs @@ -1,15 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils.Extensions +namespace Microsoft.DotNet.Cli.Utils.Extensions; + +public static class CollectionsExtensions { - public static class CollectionsExtensions + public static IEnumerable OrEmptyIfNull(this IEnumerable enumerable) { - public static IEnumerable OrEmptyIfNull(this IEnumerable enumerable) - { - return enumerable == null - ? Enumerable.Empty() - : enumerable; - } + return enumerable == null + ? Enumerable.Empty() + : enumerable; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ExceptionExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ExceptionExtensions.cs index ca35c9c786be..8ea857636334 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ExceptionExtensions.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ExceptionExtensions.cs @@ -1,25 +1,24 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils.Extensions +namespace Microsoft.DotNet.Cli.Utils.Extensions; + +internal static class ExceptionExtensions { - internal static class ExceptionExtensions + public static TException DisplayAsError(this TException exception) + where TException : Exception { - public static TException DisplayAsError(this TException exception) - where TException : Exception - { - exception.Data.Add(CLI_User_Displayed_Exception, true); - return exception; - } + exception.Data.Add(CLI_User_Displayed_Exception, true); + return exception; + } - public static void ReportAsWarning(this Exception e) - { - Reporter.Verbose.WriteLine($"Warning: Ignoring exception: {e.ToString().Yellow()}"); - } + public static void ReportAsWarning(this Exception e) + { + Reporter.Verbose.WriteLine($"Warning: Ignoring exception: {e.ToString().Yellow()}"); + } - public static bool ShouldBeDisplayedAsError(this Exception e) => - e.Data.Contains(CLI_User_Displayed_Exception); + public static bool ShouldBeDisplayedAsError(this Exception e) => + e.Data.Contains(CLI_User_Displayed_Exception); - internal const string CLI_User_Displayed_Exception = "CLI_User_Displayed_Exception"; - } + internal const string CLI_User_Displayed_Exception = "CLI_User_Displayed_Exception"; } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/LockFileExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/LockFileExtensions.cs index 8c9f7e5affd4..bdf8b624a2eb 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/LockFileExtensions.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/LockFileExtensions.cs @@ -1,32 +1,30 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Tools.Common; using NuGet.Packaging; using NuGet.ProjectModel; -namespace Microsoft.DotNet.Cli.Utils.Extensions +namespace Microsoft.DotNet.Cli.Utils.Extensions; + +internal static class LockFileExtensions { - internal static class LockFileExtensions + public static string GetPackageDirectory(this LockFile lockFile, LockFileTargetLibrary library) { - public static string GetPackageDirectory(this LockFile lockFile, LockFileTargetLibrary library) - { - var packageFolders = lockFile.GetNormalizedPackageFolders(); + var packageFolders = lockFile.GetNormalizedPackageFolders(); - var packageFoldersCount = packageFolders.Count(); - var userPackageFolder = packageFoldersCount == 1 ? string.Empty : packageFolders.First(); - var fallbackPackageFolders = packageFoldersCount > 1 ? packageFolders.Skip(1) : packageFolders; + var packageFoldersCount = packageFolders.Count(); + var userPackageFolder = packageFoldersCount == 1 ? string.Empty : packageFolders.First(); + var fallbackPackageFolders = packageFoldersCount > 1 ? packageFolders.Skip(1) : packageFolders; - var packageDirectory = new FallbackPackagePathResolver(userPackageFolder, fallbackPackageFolders) - .GetPackageDirectory(library.Name, library.Version); + var packageDirectory = new FallbackPackagePathResolver(userPackageFolder, fallbackPackageFolders) + .GetPackageDirectory(library.Name, library.Version); - return packageDirectory; - } + return packageDirectory; + } - public static IEnumerable GetNormalizedPackageFolders(this LockFile lockFile) - { - return lockFile.PackageFolders.Select(p => - PathUtility.EnsureNoTrailingDirectorySeparator(p.Path)); - } + public static IEnumerable GetNormalizedPackageFolders(this LockFile lockFile) + { + return lockFile.PackageFolders.Select(p => + PathUtility.EnsureNoTrailingDirectorySeparator(p.Path)); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/LockFileFormatExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/LockFileFormatExtensions.cs index c4f1241a8081..eec603000473 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/LockFileFormatExtensions.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/LockFileFormatExtensions.cs @@ -4,29 +4,28 @@ using NuGet.Common; using NuGet.ProjectModel; -namespace Microsoft.DotNet.Cli.Utils.Extensions +namespace Microsoft.DotNet.Cli.Utils.Extensions; + +public static class LockFileFormatExtensions { - public static class LockFileFormatExtensions + public static async Task ReadWithLock(this LockFileFormat subject, string path) { - public static async Task ReadWithLock(this LockFileFormat subject, string path) - { - return await ConcurrencyUtilities.ExecuteWithFileLockedAsync( - path, - lockedToken => + return await ConcurrencyUtilities.ExecuteWithFileLockedAsync( + path, + lockedToken => + { + if (!File.Exists(path)) { - if (!File.Exists(path)) - { - throw new GracefulException(string.Join( - Environment.NewLine, - string.Format(LocalizableStrings.FileNotFound, path), - LocalizableStrings.ProjectNotRestoredOrRestoreFailed)); - } + throw new GracefulException(string.Join( + Environment.NewLine, + string.Format(LocalizableStrings.FileNotFound, path), + LocalizableStrings.ProjectNotRestoredOrRestoreFailed)); + } - var lockFile = FileAccessRetrier.RetryOnFileAccessFailure(() => subject.Read(path), LocalizableStrings.CouldNotAccessAssetsFile); + var lockFile = FileAccessRetrier.RetryOnFileAccessFailure(() => subject.Read(path), LocalizableStrings.CouldNotAccessAssetsFile); - return lockFile; - }, - CancellationToken.None); - } + return lockFile; + }, + CancellationToken.None); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/MSBuildProjectExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/MSBuildProjectExtensions.cs index 9bc6b3113469..76a1fdb7c8e1 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/MSBuildProjectExtensions.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/MSBuildProjectExtensions.cs @@ -2,135 +2,133 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Construction; -using Microsoft.DotNet.Tools.Common; -namespace Microsoft.DotNet.Cli.Utils.Extensions +namespace Microsoft.DotNet.Cli.Utils.Extensions; + +internal static class MSBuildProjectExtensions { - internal static class MSBuildProjectExtensions + public static bool IsConditionalOnFramework(this ProjectElement el, string framework) { - public static bool IsConditionalOnFramework(this ProjectElement el, string framework) + string? conditionStr; + if (!TryGetFrameworkConditionString(framework, out conditionStr)) { - string? conditionStr; - if (!TryGetFrameworkConditionString(framework, out conditionStr)) - { - return el.ConditionChain().Count == 0; - } - - var condChain = el.ConditionChain(); - return condChain.Count == 1 && condChain.First().Trim() == conditionStr; + return el.ConditionChain().Count == 0; } - public static ISet ConditionChain(this ProjectElement projectElement) - { - var conditionChainSet = new HashSet(); - - if (!string.IsNullOrEmpty(projectElement.Condition)) - { - conditionChainSet.Add(projectElement.Condition); - } - - foreach (var parent in projectElement.AllParents) - { - if (!string.IsNullOrEmpty(parent.Condition)) - { - conditionChainSet.Add(parent.Condition); - } - } + var condChain = el.ConditionChain(); + return condChain.Count == 1 && condChain.First().Trim() == conditionStr; + } - return conditionChainSet; - } + public static ISet ConditionChain(this ProjectElement projectElement) + { + var conditionChainSet = new HashSet(); - public static ProjectItemGroupElement? LastItemGroup(this ProjectRootElement root) + if (!string.IsNullOrEmpty(projectElement.Condition)) { - return root.ItemGroupsReversed.FirstOrDefault(); + conditionChainSet.Add(projectElement.Condition); } - public static ProjectItemGroupElement FindUniformOrCreateItemGroupWithCondition(this ProjectRootElement root, string projectItemElementType, string framework) + foreach (var parent in projectElement.AllParents) { - var lastMatchingItemGroup = FindExistingUniformItemGroupWithCondition(root, projectItemElementType, framework); - - if (lastMatchingItemGroup != null) - { - return lastMatchingItemGroup; - } - - ProjectItemGroupElement ret = root.CreateItemGroupElement(); - string? condStr; - if (TryGetFrameworkConditionString(framework, out condStr)) + if (!string.IsNullOrEmpty(parent.Condition)) { - ret.Condition = condStr; + conditionChainSet.Add(parent.Condition); } - - root.InsertAfterChild(ret, root.LastItemGroup()); - return ret; } - public static ProjectItemGroupElement? FindExistingUniformItemGroupWithCondition(this ProjectRootElement root, string projectItemElementType, string framework) - { - return root.ItemGroupsReversed.FirstOrDefault((itemGroup) => itemGroup.IsConditionalOnFramework(framework) && itemGroup.IsUniformItemElementType(projectItemElementType)); - } + return conditionChainSet; + } - public static bool IsUniformItemElementType(this ProjectItemGroupElement group, string projectItemElementType) - { - return group.Items.All((it) => it.ItemType == projectItemElementType); - } + public static ProjectItemGroupElement? LastItemGroup(this ProjectRootElement root) + { + return root.ItemGroupsReversed.FirstOrDefault(); + } - public static IEnumerable FindExistingItemsWithCondition(this ProjectRootElement root, string framework, string include) - { - return root.Items.Where((el) => el.IsConditionalOnFramework(framework) && el.HasInclude(include)); - } + public static ProjectItemGroupElement FindUniformOrCreateItemGroupWithCondition(this ProjectRootElement root, string projectItemElementType, string framework) + { + var lastMatchingItemGroup = FindExistingUniformItemGroupWithCondition(root, projectItemElementType, framework); - public static bool HasExistingItemWithCondition(this ProjectRootElement root, string framework, string include) + if (lastMatchingItemGroup != null) { - return root.FindExistingItemsWithCondition(framework, include).Count() != 0; + return lastMatchingItemGroup; } - public static IEnumerable GetAllItemsWithElementType(this ProjectRootElement root, string projectItemElementType) + ProjectItemGroupElement ret = root.CreateItemGroupElement(); + string? condStr; + if (TryGetFrameworkConditionString(framework, out condStr)) { - return root.Items.Where((it) => it.ItemType == projectItemElementType); + ret.Condition = condStr; } - public static bool HasInclude(this ProjectItemElement el, string include) - { - include = NormalizeIncludeForComparison(include); - foreach (var i in el.Includes()) - { - if (include == NormalizeIncludeForComparison(i)) - { - return true; - } - } + root.InsertAfterChild(ret, root.LastItemGroup()); + return ret; + } - return false; - } + public static ProjectItemGroupElement? FindExistingUniformItemGroupWithCondition(this ProjectRootElement root, string projectItemElementType, string framework) + { + return root.ItemGroupsReversed.FirstOrDefault((itemGroup) => itemGroup.IsConditionalOnFramework(framework) && itemGroup.IsUniformItemElementType(projectItemElementType)); + } - public static IEnumerable Includes( - this ProjectItemElement item) - { - return SplitSemicolonDelimitedValues(item.Include); - } + public static bool IsUniformItemElementType(this ProjectItemGroupElement group, string projectItemElementType) + { + return group.Items.All((it) => it.ItemType == projectItemElementType); + } - private static IEnumerable SplitSemicolonDelimitedValues(string combinedValue) - { - return string.IsNullOrEmpty(combinedValue) ? Enumerable.Empty() : combinedValue.Split(';'); - } + public static IEnumerable FindExistingItemsWithCondition(this ProjectRootElement root, string framework, string include) + { + return root.Items.Where((el) => el.IsConditionalOnFramework(framework) && el.HasInclude(include)); + } + + public static bool HasExistingItemWithCondition(this ProjectRootElement root, string framework, string include) + { + return root.FindExistingItemsWithCondition(framework, include).Count() != 0; + } + public static IEnumerable GetAllItemsWithElementType(this ProjectRootElement root, string projectItemElementType) + { + return root.Items.Where((it) => it.ItemType == projectItemElementType); + } - private static bool TryGetFrameworkConditionString(string framework, out string? condition) + public static bool HasInclude(this ProjectItemElement el, string include) + { + include = NormalizeIncludeForComparison(include); + foreach (var i in el.Includes()) { - if (string.IsNullOrEmpty(framework)) + if (include == NormalizeIncludeForComparison(i)) { - condition = null; - return false; + return true; } - - condition = $"'$(TargetFramework)' == '{framework}'"; - return true; } - private static string NormalizeIncludeForComparison(string include) + return false; + } + + public static IEnumerable Includes( + this ProjectItemElement item) + { + return SplitSemicolonDelimitedValues(item.Include); + } + + private static IEnumerable SplitSemicolonDelimitedValues(string combinedValue) + { + return string.IsNullOrEmpty(combinedValue) ? Enumerable.Empty() : combinedValue.Split(';'); + } + + + private static bool TryGetFrameworkConditionString(string framework, out string? condition) + { + if (string.IsNullOrEmpty(framework)) { - return PathUtility.GetPathWithBackSlashes(include.ToLower()); + condition = null; + return false; } + + condition = $"'$(TargetFramework)' == '{framework}'"; + return true; + } + + private static string NormalizeIncludeForComparison(string include) + { + return PathUtility.GetPathWithBackSlashes(include.ToLower()); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ProcessExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ProcessExtensions.cs index 10e9e74c4377..5da549d9ddd1 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ProcessExtensions.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ProcessExtensions.cs @@ -1,51 +1,53 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Diagnostics; +#if NET using System.Runtime.Versioning; +#endif + +using System.Diagnostics; using Microsoft.Win32.SafeHandles; -namespace Microsoft.DotNet.Cli.Utils.Extensions +namespace Microsoft.DotNet.Cli.Utils.Extensions; + +/// +/// Extensions methods for components. +/// +#if NET +[SupportedOSPlatform("windows")] +#endif +public static class ProcessExtensions { +#pragma warning disable CA1416 /// - /// Extensions methods for components. + /// Returns the parent process of this process by querying the Win32_Process class. /// -#if NET - [SupportedOSPlatform("windows")] -#endif - public static class ProcessExtensions + /// The process component. + /// The parent process or if the parent process cannot be found. + public static Process? GetParentProcess(this Process process) { -#pragma warning disable CA1416 - /// - /// Returns the parent process of this process by querying the Win32_Process class. - /// - /// The process component. - /// The parent process or if the parent process cannot be found. - public static Process? GetParentProcess(this Process process) - { - int ppid = process.GetParentProcessId(); - - return ppid != -1 ? Process.GetProcessById(ppid) : null; - } + int ppid = process.GetParentProcessId(); - /// - /// Returns the parent process ID of this process by querying the Win32_Process class. - /// - /// The process component. - /// The process ID of the parent process, or -1 if the parent process could not be found. - public static unsafe int GetParentProcessId(this Process process) - { - SafeProcessHandle handle = process.SafeHandle; - NativeMethods.Windows.PROCESS_BASIC_INFORMATION info; + return ppid != -1 ? Process.GetProcessById(ppid) : null; + } - if (NativeMethods.Windows.NtQueryInformationProcess(handle, NativeMethods.Windows.ProcessBasicInformation, - &info, (uint)sizeof(NativeMethods.Windows.PROCESS_BASIC_INFORMATION), out _) != 0) - { - return -1; - } + /// + /// Returns the parent process ID of this process by querying the Win32_Process class. + /// + /// The process component. + /// The process ID of the parent process, or -1 if the parent process could not be found. + public static unsafe int GetParentProcessId(this Process process) + { + SafeProcessHandle handle = process.SafeHandle; + NativeMethods.Windows.PROCESS_BASIC_INFORMATION info; - return (int)info.InheritedFromUniqueProcessId; + if (NativeMethods.Windows.NtQueryInformationProcess(handle, NativeMethods.Windows.ProcessBasicInformation, + &info, (uint)sizeof(NativeMethods.Windows.PROCESS_BASIC_INFORMATION), out _) != 0) + { + return -1; } -#pragma warning restore CA1416 + + return (int)info.InheritedFromUniqueProcessId; } +#pragma warning restore CA1416 } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ProcessStartInfoExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ProcessStartInfoExtensions.cs index 31d66252ac81..3828ca586a54 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ProcessStartInfoExtensions.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/ProcessStartInfoExtensions.cs @@ -3,64 +3,63 @@ using System.Diagnostics; -namespace Microsoft.DotNet.Cli.Utils.Extensions +namespace Microsoft.DotNet.Cli.Utils.Extensions; + +internal static class ProcessStartInfoExtensions { - internal static class ProcessStartInfoExtensions + public static int Execute(this ProcessStartInfo startInfo) { - public static int Execute(this ProcessStartInfo startInfo) + if (startInfo == null) { - if (startInfo == null) - { - throw new ArgumentNullException(nameof(startInfo)); - } - - var process = new Process - { - StartInfo = startInfo - }; + throw new ArgumentNullException(nameof(startInfo)); + } - using (var reaper = new ProcessReaper(process)) - { - process.Start(); - reaper.NotifyProcessStarted(); - process.WaitForExit(); - } + var process = new Process + { + StartInfo = startInfo + }; - return process.ExitCode; + using (var reaper = new ProcessReaper(process)) + { + process.Start(); + reaper.NotifyProcessStarted(); + process.WaitForExit(); } - public static int ExecuteAndCaptureOutput(this ProcessStartInfo startInfo, out string? stdOut, out string? stdErr) - { - var outStream = new StreamForwarder().Capture(); - var errStream = new StreamForwarder().Capture(); + return process.ExitCode; + } - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = true; + public static int ExecuteAndCaptureOutput(this ProcessStartInfo startInfo, out string? stdOut, out string? stdErr) + { + var outStream = new StreamForwarder().Capture(); + var errStream = new StreamForwarder().Capture(); - var process = new Process - { - StartInfo = startInfo, - EnableRaisingEvents = true - }; + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; - using (var reaper = new ProcessReaper(process)) - { - process.Start(); - reaper.NotifyProcessStarted(); + var process = new Process + { + StartInfo = startInfo, + EnableRaisingEvents = true + }; - var taskOut = outStream.BeginRead(process.StandardOutput); - var taskErr = errStream.BeginRead(process.StandardError); + using (var reaper = new ProcessReaper(process)) + { + process.Start(); + reaper.NotifyProcessStarted(); - process.WaitForExit(); + var taskOut = outStream.BeginRead(process.StandardOutput); + var taskErr = errStream.BeginRead(process.StandardError); - taskOut.Wait(); - taskErr.Wait(); + process.WaitForExit(); - stdOut = outStream.CapturedOutput; - stdErr = errStream.CapturedOutput; - } + taskOut.Wait(); + taskErr.Wait(); - return process.ExitCode; + stdOut = outStream.CapturedOutput; + stdErr = errStream.CapturedOutput; } + + return process.ExitCode; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/StringExtensions.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/StringExtensions.cs index 9c03f3efb233..c6bd2d3213b9 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/StringExtensions.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Extensions/StringExtensions.cs @@ -1,34 +1,29 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils.Extensions +namespace Microsoft.DotNet.Cli.Utils.Extensions; + +public static class StringExtensions { - public static class StringExtensions + public static string RemovePrefix(this string name) { - public static string RemovePrefix(this string name) - { - int prefixLength = GetPrefixLength(name); + int prefixLength = GetPrefixLength(name); - return prefixLength > 0 - ? name.Substring(prefixLength) - : name; + return prefixLength > 0 ? name.Substring(prefixLength) : name; - static int GetPrefixLength(string name) + static int GetPrefixLength(string name) + { + if (name[0] == '-') { - if (name[0] == '-') - { - return name.Length > 1 && name[1] == '-' - ? 2 - : 1; - } - - if (name[0] == '/') - { - return 1; - } + return name.Length > 1 && name[1] == '-' ? 2 : 1; + } - return 0; + if (name[0] == '/') + { + return 1; } + + return 0; } } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/FileAccessRetrier.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/FileAccessRetrier.cs new file mode 100644 index 000000000000..31fb2f58fb60 --- /dev/null +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/FileAccessRetrier.cs @@ -0,0 +1,105 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.DotNet.Cli.Utils; + +public static class FileAccessRetrier +{ + public static async Task RetryOnFileAccessFailure( + Func func, + string errorMessage, + int maxRetries = 3000, + TimeSpan sleepDuration = default) + { + var attemptsLeft = maxRetries; + + if (sleepDuration == default) + { + sleepDuration = TimeSpan.FromMilliseconds(10); + } + + while (true) + { + if (attemptsLeft < 1) + { + throw new InvalidOperationException(errorMessage); + } + + attemptsLeft--; + + try + { + return func(); + } + catch (UnauthorizedAccessException) + { + // This can occur when the file is being deleted + // Or when an admin user has locked the file + await Task.Delay(sleepDuration); + + continue; + } + catch (IOException) + { + await Task.Delay(sleepDuration); + + continue; + } + } + } + + /// + /// Run Directory.Move and File.Move in Windows has a chance to get IOException with + /// HResult 0x80070005 due to Indexer. But this error is transient. + /// + internal static void RetryOnMoveAccessFailure(Action action) + { + const int ERROR_HRESULT_ACCESS_DENIED = unchecked((int)0x80070005); + int nextWaitTime = 10; + int remainRetry = 10; + + while (true) + { + try + { + action(); + break; + } + catch (IOException e) when (e.HResult == ERROR_HRESULT_ACCESS_DENIED) + { + Thread.Sleep(nextWaitTime); + nextWaitTime *= 2; + remainRetry--; + if (remainRetry == 0) + { + throw; + } + } + } + } + + internal static void RetryOnIOException(Action action) + { + int nextWaitTime = 10; + int remainRetry = 10; + + while (true) + { + try + { + action(); + break; + } + catch (IOException) + { + Task.Run(() => Task.Delay(nextWaitTime)).Wait(); + nextWaitTime *= 2; + remainRetry--; + if (remainRetry == 0) + { + throw; + } + } + } + } +} diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/FileAccessRetryer.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/FileAccessRetryer.cs deleted file mode 100644 index 4fdd40c9537a..000000000000 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/FileAccessRetryer.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.DotNet.Cli.Utils -{ - public static class FileAccessRetrier - { - public static async Task RetryOnFileAccessFailure( - Func func, - string errorMessage, - int maxRetries = 3000, - TimeSpan sleepDuration = default(TimeSpan)) - { - var attemptsLeft = maxRetries; - - if (sleepDuration == default(TimeSpan)) - { - sleepDuration = TimeSpan.FromMilliseconds(10); - } - - while (true) - { - if (attemptsLeft < 1) - { - throw new InvalidOperationException(errorMessage); - } - - attemptsLeft--; - - try - { - return func(); - } - catch (UnauthorizedAccessException) - { - // This can occur when the file is being deleted - // Or when an admin user has locked the file - await Task.Delay(sleepDuration); - - continue; - } - catch (IOException) - { - await Task.Delay(sleepDuration); - - continue; - } - } - } - - /// - /// Run Directory.Move and File.Move in Windows has a chance to get IOException with - /// HResult 0x80070005 due to Indexer. But this error is transient. - /// - internal static void RetryOnMoveAccessFailure(Action action) - { - const int ERROR_HRESULT_ACCESS_DENIED = unchecked((int)0x80070005); - int nextWaitTime = 10; - int remainRetry = 10; - - while (true) - { - try - { - action(); - break; - } - catch (IOException e) when (e.HResult == ERROR_HRESULT_ACCESS_DENIED) - { - Thread.Sleep(nextWaitTime); - nextWaitTime *= 2; - remainRetry--; - if (remainRetry == 0) - { - throw; - } - } - } - } - - internal static void RetryOnIOException(Action action) - { - int nextWaitTime = 10; - int remainRetry = 10; - - while (true) - { - try - { - action(); - break; - } - catch (IOException) - { - Task.Run(() => Task.Delay(nextWaitTime)).Wait(); - nextWaitTime *= 2; - remainRetry--; - if (remainRetry == 0) - { - throw; - } - } - } - } - } -} diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/FileNameSuffixes.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/FileNameSuffixes.cs index 0f8fde87d7fb..2713b15a995b 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/FileNameSuffixes.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/FileNameSuffixes.cs @@ -1,76 +1,75 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public static class FileNameSuffixes { - public static class FileNameSuffixes - { - public const string DepsJson = ".deps.json"; - public const string RuntimeConfigJson = ".runtimeconfig.json"; - public const string RuntimeConfigDevJson = ".runtimeconfig.dev.json"; + public const string DepsJson = ".deps.json"; + public const string RuntimeConfigJson = ".runtimeconfig.json"; + public const string RuntimeConfigDevJson = ".runtimeconfig.dev.json"; - public static PlatformFileNameSuffixes CurrentPlatform + public static PlatformFileNameSuffixes CurrentPlatform + { + get { - get + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Windows; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return Windows; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return OSX; - } - else - { - // assume everything else is Unix to avoid modifying this file - // everytime a new platform is introduced in runtime. - return Unix; - } + return OSX; + } + else + { + // assume everything else is Unix to avoid modifying this file + // every time a new platform is introduced in runtime. + return Unix; } } + } - public static PlatformFileNameSuffixes DotNet { get; } = new PlatformFileNameSuffixes - { - DynamicLib = ".dll", - Exe = ".exe", - ProgramDatabase = ".pdb", - StaticLib = ".lib" - }; + public static PlatformFileNameSuffixes DotNet { get; } = new PlatformFileNameSuffixes + { + DynamicLib = ".dll", + Exe = ".exe", + ProgramDatabase = ".pdb", + StaticLib = ".lib" + }; - public static PlatformFileNameSuffixes Windows { get; } = new PlatformFileNameSuffixes - { - DynamicLib = ".dll", - Exe = ".exe", - ProgramDatabase = ".pdb", - StaticLib = ".lib" - }; + public static PlatformFileNameSuffixes Windows { get; } = new PlatformFileNameSuffixes + { + DynamicLib = ".dll", + Exe = ".exe", + ProgramDatabase = ".pdb", + StaticLib = ".lib" + }; - public static PlatformFileNameSuffixes OSX { get; } = new PlatformFileNameSuffixes - { - DynamicLib = ".dylib", - Exe = string.Empty, - ProgramDatabase = ".pdb", - StaticLib = ".a" - }; + public static PlatformFileNameSuffixes OSX { get; } = new PlatformFileNameSuffixes + { + DynamicLib = ".dylib", + Exe = string.Empty, + ProgramDatabase = ".pdb", + StaticLib = ".a" + }; - public static PlatformFileNameSuffixes Unix { get; } = new PlatformFileNameSuffixes - { - DynamicLib = ".so", - Exe = string.Empty, - ProgramDatabase = ".pdb", - StaticLib = ".a" - }; + public static PlatformFileNameSuffixes Unix { get; } = new PlatformFileNameSuffixes + { + DynamicLib = ".so", + Exe = string.Empty, + ProgramDatabase = ".pdb", + StaticLib = ".a" + }; - public struct PlatformFileNameSuffixes - { - public string DynamicLib { get; internal set; } + public struct PlatformFileNameSuffixes + { + public string DynamicLib { get; internal set; } - public string Exe { get; internal set; } + public string Exe { get; internal set; } - public string ProgramDatabase { get; internal set; } + public string ProgramDatabase { get; internal set; } - public string StaticLib { get; internal set; } - } + public string StaticLib { get; internal set; } } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/FilePermissionSettingException.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/FilePermissionSettingException.cs index 5da70b4d5088..e8bb85be01b9 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/FilePermissionSettingException.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/FilePermissionSettingException.cs @@ -1,20 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +internal class FilePermissionSettingException : Exception { - internal class FilePermissionSettingException : Exception + public FilePermissionSettingException() { - public FilePermissionSettingException() - { - } + } - public FilePermissionSettingException(string message) : base(message) - { - } + public FilePermissionSettingException(string message) : base(message) + { + } - public FilePermissionSettingException(string message, Exception innerException) : base(message, innerException) - { - } + public FilePermissionSettingException(string message, Exception innerException) : base(message, innerException) + { } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/ForwardingAppImplementation.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/ForwardingAppImplementation.cs index 0ec2dfbea6b5..fd9cbaa25af0 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/ForwardingAppImplementation.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/ForwardingAppImplementation.cs @@ -6,96 +6,95 @@ using System.Diagnostics; using Microsoft.DotNet.Cli.Utils.Extensions; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +/// +/// A class which encapsulates logic needed to forward arguments from the current process to another process +/// invoked with the dotnet.exe host. +/// +internal class ForwardingAppImplementation { - /// - /// A class which encapsulates logic needed to forward arguments from the current process to another process - /// invoked with the dotnet.exe host. - /// - internal class ForwardingAppImplementation + private readonly string _forwardApplicationPath; + private readonly IEnumerable _argsToForward; + private readonly string? _depsFile; + private readonly string? _runtimeConfig; + private readonly string? _additionalProbingPath; + private Dictionary _environmentVariables; + + private readonly string[] _allArgs; + + public ForwardingAppImplementation( + string forwardApplicationPath, + IEnumerable argsToForward, + string? depsFile = null, + string? runtimeConfig = null, + string? additionalProbingPath = null, + Dictionary? environmentVariables = null) { - private readonly string _forwardApplicationPath; - private readonly IEnumerable _argsToForward; - private readonly string? _depsFile; - private readonly string? _runtimeConfig; - private readonly string? _additionalProbingPath; - private Dictionary _environmentVariables; - - private readonly string[] _allArgs; - - public ForwardingAppImplementation( - string forwardApplicationPath, - IEnumerable argsToForward, - string? depsFile = null, - string? runtimeConfig = null, - string? additionalProbingPath = null, - Dictionary? environmentVariables = null) + _forwardApplicationPath = forwardApplicationPath; + _argsToForward = argsToForward; + _depsFile = depsFile; + _runtimeConfig = runtimeConfig; + _additionalProbingPath = additionalProbingPath; + _environmentVariables = environmentVariables ?? new Dictionary(); + + var allArgs = new List(); + allArgs.Add("exec"); + + if (_depsFile != null) { - _forwardApplicationPath = forwardApplicationPath; - _argsToForward = argsToForward; - _depsFile = depsFile; - _runtimeConfig = runtimeConfig; - _additionalProbingPath = additionalProbingPath; - _environmentVariables = environmentVariables ?? new Dictionary(); - - var allArgs = new List(); - allArgs.Add("exec"); - - if (_depsFile != null) - { - allArgs.Add("--depsfile"); - allArgs.Add(_depsFile); - } - - if (_runtimeConfig != null) - { - allArgs.Add("--runtimeconfig"); - allArgs.Add(_runtimeConfig); - } - - if (_additionalProbingPath != null) - { - allArgs.Add("--additionalprobingpath"); - allArgs.Add(_additionalProbingPath); - } - - allArgs.Add(_forwardApplicationPath); - allArgs.AddRange(_argsToForward); - - _allArgs = allArgs.ToArray(); + allArgs.Add("--depsfile"); + allArgs.Add(_depsFile); } - public int Execute() + if (_runtimeConfig != null) { - return GetProcessStartInfo().Execute(); + allArgs.Add("--runtimeconfig"); + allArgs.Add(_runtimeConfig); } - public ProcessStartInfo GetProcessStartInfo() + if (_additionalProbingPath != null) { - var processInfo = new ProcessStartInfo - { - FileName = GetHostExeName(), - Arguments = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(_allArgs), - UseShellExecute = false - }; - - foreach (var entry in _environmentVariables) - { - processInfo.Environment[entry.Key] = entry.Value; - } - - return processInfo; + allArgs.Add("--additionalprobingpath"); + allArgs.Add(_additionalProbingPath); } - public ForwardingAppImplementation WithEnvironmentVariable(string name, string value) + allArgs.Add(_forwardApplicationPath); + allArgs.AddRange(_argsToForward); + + _allArgs = allArgs.ToArray(); + } + + public int Execute() + { + return GetProcessStartInfo().Execute(); + } + + public ProcessStartInfo GetProcessStartInfo() + { + var processInfo = new ProcessStartInfo { - _environmentVariables.Add(name, value); + FileName = GetHostExeName(), + Arguments = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(_allArgs), + UseShellExecute = false + }; - return this; + foreach (var entry in _environmentVariables) + { + processInfo.Environment[entry.Key] = entry.Value; } - private string GetHostExeName() => new Muxer().MuxerPath; + return processInfo; + } + + public ForwardingAppImplementation WithEnvironmentVariable(string name, string value) + { + _environmentVariables.Add(name, value); + + return this; } + + private string GetHostExeName() => new Muxer().MuxerPath; } #endif diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/FrameworkDependencyFile.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/FrameworkDependencyFile.cs index d74005dcd6be..9bfbe6ec6c3d 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/FrameworkDependencyFile.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/FrameworkDependencyFile.cs @@ -3,117 +3,116 @@ using Microsoft.Extensions.DependencyModel; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +/// +/// Represents the .deps.json file in the shared framework +/// that the CLI is running against. +/// +internal class FrameworkDependencyFile { - /// - /// Represents the .deps.json file in the shared framework - /// that the CLI is running against. - /// - internal class FrameworkDependencyFile + private readonly string? _depsFilePath; + private readonly Lazy _dependencyContext; + + private DependencyContext DependencyContext => _dependencyContext.Value; + + public FrameworkDependencyFile() { - private readonly string? _depsFilePath; - private readonly Lazy _dependencyContext; + _depsFilePath = Muxer.GetDataFromAppDomain("FX_DEPS_FILE"); + _dependencyContext = new Lazy(CreateDependencyContext); + } - private DependencyContext DependencyContext => _dependencyContext.Value; + public bool IsRuntimeSupported(string runtimeIdentifier) + { + return DependencyContext.RuntimeGraph.Any(g => g.Runtime == runtimeIdentifier); + } - public FrameworkDependencyFile() - { - _depsFilePath = Muxer.GetDataFromAppDomain("FX_DEPS_FILE"); - _dependencyContext = new Lazy(CreateDependencyContext); - } + public string? GetNetStandardLibraryVersion() + { + return DependencyContext + .RuntimeLibraries + .FirstOrDefault(l => "netstandard.library".Equals(l.Name, StringComparison.OrdinalIgnoreCase)) + ?.Version; + } - public bool IsRuntimeSupported(string runtimeIdentifier) +#if NETCOREAPP + public bool TryGetMostFitRuntimeIdentifier( + string alternativeCurrentRuntimeIdentifier, + string[] candidateRuntimeIdentifiers, + out string? mostFitRuntimeIdentifier) + { + return TryGetMostFitRuntimeIdentifier( + RuntimeInformation.RuntimeIdentifier, + alternativeCurrentRuntimeIdentifier, + DependencyContext.RuntimeGraph, + candidateRuntimeIdentifiers, + out mostFitRuntimeIdentifier); + } +#endif + + internal static bool TryGetMostFitRuntimeIdentifier( + string currentRuntimeIdentifier, + string alternativeCurrentRuntimeIdentifier, + IReadOnlyList runtimeGraph, + string[] candidateRuntimeIdentifiers, + out string? mostFitRuntimeIdentifier) + { + mostFitRuntimeIdentifier = null; + RuntimeFallbacks[] runtimeFallbacksCandidates; + + if (!string.IsNullOrEmpty(currentRuntimeIdentifier)) { - return DependencyContext.RuntimeGraph.Any(g => g.Runtime == runtimeIdentifier); + runtimeFallbacksCandidates = + runtimeGraph + .Where(g => string.Equals(g.Runtime, currentRuntimeIdentifier, StringComparison.OrdinalIgnoreCase)) + .ToArray(); } - - public string? GetNetStandardLibraryVersion() + else { - return DependencyContext - .RuntimeLibraries - .FirstOrDefault(l => "netstandard.library".Equals(l.Name, StringComparison.OrdinalIgnoreCase)) - ?.Version; + runtimeFallbacksCandidates = Array.Empty(); } -#if NETCOREAPP - public bool TryGetMostFitRuntimeIdentifier( - string alternativeCurrentRuntimeIdentifier, - string[] candidateRuntimeIdentifiers, - out string? mostFitRuntimeIdentifier) + if (runtimeFallbacksCandidates.Length == 0 && !string.IsNullOrEmpty(alternativeCurrentRuntimeIdentifier)) { - return TryGetMostFitRuntimeIdentifier( - RuntimeInformation.RuntimeIdentifier, - alternativeCurrentRuntimeIdentifier, - DependencyContext.RuntimeGraph, - candidateRuntimeIdentifiers, - out mostFitRuntimeIdentifier); + runtimeFallbacksCandidates = + runtimeGraph + .Where(g => string.Equals(g.Runtime, alternativeCurrentRuntimeIdentifier, StringComparison.OrdinalIgnoreCase)) + .ToArray(); } -#endif - internal static bool TryGetMostFitRuntimeIdentifier( - string currentRuntimeIdentifier, - string alternativeCurrentRuntimeIdentifier, - IReadOnlyList runtimeGraph, - string[] candidateRuntimeIdentifiers, - out string? mostFitRuntimeIdentifier) + if (runtimeFallbacksCandidates.Length == 0) { - mostFitRuntimeIdentifier = null; - RuntimeFallbacks[] runtimeFallbacksCandidates; - - if (!string.IsNullOrEmpty(currentRuntimeIdentifier)) - { - runtimeFallbacksCandidates = - runtimeGraph - .Where(g => string.Equals(g.Runtime, currentRuntimeIdentifier, StringComparison.OrdinalIgnoreCase)) - .ToArray(); - } - else - { - runtimeFallbacksCandidates = Array.Empty(); - } - - if (runtimeFallbacksCandidates.Length == 0 && !string.IsNullOrEmpty(alternativeCurrentRuntimeIdentifier)) - { - runtimeFallbacksCandidates = - runtimeGraph - .Where(g => string.Equals(g.Runtime, alternativeCurrentRuntimeIdentifier, StringComparison.OrdinalIgnoreCase)) - .ToArray(); - } - - if (runtimeFallbacksCandidates.Length == 0) - { - return false; - } + return false; + } - RuntimeFallbacks runtimeFallbacks = runtimeFallbacksCandidates[0]; + RuntimeFallbacks runtimeFallbacks = runtimeFallbacksCandidates[0]; - var runtimeFallbacksIncludesRuntime = new List(); - runtimeFallbacksIncludesRuntime.Add(runtimeFallbacks.Runtime); - runtimeFallbacksIncludesRuntime.AddRange(runtimeFallbacks.Fallbacks); + var runtimeFallbacksIncludesRuntime = new List(); + runtimeFallbacksIncludesRuntime.Add(runtimeFallbacks.Runtime); + runtimeFallbacksIncludesRuntime.AddRange(runtimeFallbacks.Fallbacks); - var candidateMap = candidateRuntimeIdentifiers - .Distinct(comparer: StringComparer.OrdinalIgnoreCase) - .ToDictionary(x => x, StringComparer.OrdinalIgnoreCase); + var candidateMap = candidateRuntimeIdentifiers + .Distinct(comparer: StringComparer.OrdinalIgnoreCase) + .ToDictionary(x => x, StringComparer.OrdinalIgnoreCase); - foreach (var fallback in runtimeFallbacksIncludesRuntime) + foreach (var fallback in runtimeFallbacksIncludesRuntime) + { + if (fallback is not null && candidateMap.TryGetValue(fallback, out string? match)) { - if (fallback is not null && candidateMap.TryGetValue(fallback, out string? match)) - { - mostFitRuntimeIdentifier = match; - return true; - } + mostFitRuntimeIdentifier = match; + return true; } - - return false; } - private DependencyContext CreateDependencyContext() + return false; + } + + private DependencyContext CreateDependencyContext() + { + using (Stream depsFileStream = File.OpenRead(_depsFilePath ?? string.Empty)) + using (DependencyContextJsonReader reader = new()) { - using (Stream depsFileStream = File.OpenRead(_depsFilePath ?? string.Empty)) - using (DependencyContextJsonReader reader = new()) - { - return reader.Read(depsFileStream); - } + return reader.Read(depsFileStream); } } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/GracefulException.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/GracefulException.cs index 6b7bbfc13a65..f1a9c99b47ee 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/GracefulException.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/GracefulException.cs @@ -3,41 +3,40 @@ using Microsoft.DotNet.Cli.Utils.Extensions; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public class GracefulException : Exception { - public class GracefulException : Exception - { - public bool IsUserError { get; } = true; - public string VerboseMessage { get; } = string.Empty; + public bool IsUserError { get; } = true; + public string VerboseMessage { get; } = string.Empty; - public GracefulException() - { - } + public GracefulException() + { + } - public GracefulException(string message) : base(message) - { - Data.Add(ExceptionExtensions.CLI_User_Displayed_Exception, true); - } + public GracefulException(string message) : base(message) + { + Data.Add(ExceptionExtensions.CLI_User_Displayed_Exception, true); + } - public GracefulException(IEnumerable messages, IEnumerable? verboseMessages = null, - bool isUserError = true) - : this(string.Join(Environment.NewLine, messages), isUserError: isUserError) + public GracefulException(IEnumerable messages, IEnumerable? verboseMessages = null, + bool isUserError = true) + : this(string.Join(Environment.NewLine, messages), isUserError: isUserError) + { + if (verboseMessages != null) { - if (verboseMessages != null) - { - VerboseMessage = string.Join(Environment.NewLine, verboseMessages); - } + VerboseMessage = string.Join(Environment.NewLine, verboseMessages); } + } - public GracefulException(string format, params string[] args) : this(string.Format(format, args)) - { - } + public GracefulException(string format, params string[] args) : this(string.Format(format, args)) + { + } - public GracefulException(string message, Exception? innerException = null, bool isUserError = true) : base(message, innerException) - { - IsUserError = isUserError; - Data.Add(ExceptionExtensions.CLI_User_Displayed_Exception, isUserError); - } + public GracefulException(string message, Exception? innerException = null, bool isUserError = true) : base(message, innerException) + { + IsUserError = isUserError; + Data.Add(ExceptionExtensions.CLI_User_Displayed_Exception, isUserError); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/IBuiltInCommandEnvironment.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/IBuiltInCommandEnvironment.cs index d954dbd9b1d6..3b6bfac3294b 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/IBuiltInCommandEnvironment.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/IBuiltInCommandEnvironment.cs @@ -1,17 +1,16 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +internal interface IBuiltInCommandEnvironment { - internal interface IBuiltInCommandEnvironment - { - TextWriter GetConsoleOut(); - void SetConsoleOut(TextWriter newOut); + TextWriter GetConsoleOut(); + void SetConsoleOut(TextWriter newOut); - TextWriter GetConsoleError(); - void SetConsoleError(TextWriter newError); + TextWriter GetConsoleError(); + void SetConsoleError(TextWriter newError); - string GetWorkingDirectory(); - void SetWorkingDirectory(string path); - } + string GetWorkingDirectory(); + void SetWorkingDirectory(string path); } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/ICommand.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/ICommand.cs index e7fe553a8757..0d33f0379d59 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/ICommand.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/ICommand.cs @@ -1,32 +1,31 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public interface ICommand { - public interface ICommand - { - CommandResult Execute(); + CommandResult Execute(); - ICommand WorkingDirectory(string projectDirectory); + ICommand WorkingDirectory(string projectDirectory); - ICommand EnvironmentVariable(string name, string? value); + ICommand EnvironmentVariable(string name, string? value); - ICommand CaptureStdOut(); + ICommand CaptureStdOut(); - ICommand CaptureStdErr(); + ICommand CaptureStdErr(); - ICommand ForwardStdOut(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true); + ICommand ForwardStdOut(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true); - ICommand ForwardStdErr(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true); + ICommand ForwardStdErr(TextWriter? to = null, bool onlyIfVerbose = false, bool ansiPassThrough = true); - ICommand OnOutputLine(Action handler); + ICommand OnOutputLine(Action handler); - ICommand OnErrorLine(Action handler); + ICommand OnErrorLine(Action handler); - ICommand SetCommandArgs(string commandArgs); + ICommand SetCommandArgs(string commandArgs); - string CommandName { get; } + string CommandName { get; } - string CommandArgs { get; } - } + string CommandArgs { get; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/IDangerousFileDetector.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/IDangerousFileDetector.cs index a31c5200436f..8802e4093ac9 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/IDangerousFileDetector.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/IDangerousFileDetector.cs @@ -1,10 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +internal interface IDangerousFileDetector { - internal interface IDangerousFileDetector - { - bool IsDangerous(string filePath); - } + bool IsDangerous(string filePath); } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentPath.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentPath.cs index 50553b29c72c..757c697eda65 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentPath.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentPath.cs @@ -1,10 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public interface IEnvironmentPath : IEnvironmentPathInstruction { - public interface IEnvironmentPath : IEnvironmentPathInstruction - { - void AddPackageExecutablePathToUserPath(); - } + void AddPackageExecutablePathToUserPath(); } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentPathInstruction.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentPathInstruction.cs index aaee4ee4fa5a..f80bf6fb93e0 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentPathInstruction.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentPathInstruction.cs @@ -1,10 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public interface IEnvironmentPathInstruction { - public interface IEnvironmentPathInstruction - { - void PrintAddPathInstructionIfPathDoesNotExist(); - } + void PrintAddPathInstructionIfPathDoesNotExist(); } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs index 7c48a4711a64..385f989d4990 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/IEnvironmentProvider.cs @@ -1,26 +1,25 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public interface IEnvironmentProvider { - public interface IEnvironmentProvider - { - IEnumerable ExecutableExtensions { get; } + IEnumerable ExecutableExtensions { get; } - string? GetCommandPath(string commandName, params string[] extensions); + string? GetCommandPath(string commandName, params string[] extensions); - string? GetCommandPathFromRootPath(string rootPath, string commandName, params string[] extensions); + string? GetCommandPathFromRootPath(string rootPath, string commandName, params string[] extensions); - string? GetCommandPathFromRootPath(string rootPath, string commandName, IEnumerable extensions); + string? GetCommandPathFromRootPath(string rootPath, string commandName, IEnumerable extensions); - bool GetEnvironmentVariableAsBool(string name, bool defaultValue); + bool GetEnvironmentVariableAsBool(string name, bool defaultValue); - int? GetEnvironmentVariableAsNullableInt(string name); + int? GetEnvironmentVariableAsNullableInt(string name); - string? GetEnvironmentVariable(string name); + string? GetEnvironmentVariable(string name); - string? GetEnvironmentVariable(string variable, EnvironmentVariableTarget target); + string? GetEnvironmentVariable(string variable, EnvironmentVariableTarget target); - void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target); - } + void SetEnvironmentVariable(string variable, string value, EnvironmentVariableTarget target); } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/IReporter.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/IReporter.cs index d74c3bfe5f00..1a71ace2b192 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/IReporter.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/IReporter.cs @@ -1,16 +1,15 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public interface IReporter { - public interface IReporter - { - void WriteLine(string message); + void WriteLine(string message); - void WriteLine(); + void WriteLine(); - void WriteLine(string format, params object?[] args); + void WriteLine(string format, params object?[] args); - void Write(string message); - } + void Write(string message); } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs index 6d734394ad08..2e4ff73d8935 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs @@ -1,10 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public interface ITelemetryFilter { - public interface ITelemetryFilter - { - IEnumerable Filter(object o); - } + IEnumerable Filter(object o); } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/IWindowsRegistryEnvironmentPathEditor.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/IWindowsRegistryEnvironmentPathEditor.cs index bf3b8d760a43..9e2b625b6d82 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/IWindowsRegistryEnvironmentPathEditor.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/IWindowsRegistryEnvironmentPathEditor.cs @@ -1,18 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +internal interface IWindowsRegistryEnvironmentPathEditor { - internal interface IWindowsRegistryEnvironmentPathEditor - { - string? Get(SdkEnvironmentVariableTarget sdkEnvironmentVariableTarget); - void Set(string value, SdkEnvironmentVariableTarget sdkEnvironmentVariableTarget); - } + string? Get(SdkEnvironmentVariableTarget sdkEnvironmentVariableTarget); + void Set(string value, SdkEnvironmentVariableTarget sdkEnvironmentVariableTarget); +} - internal enum SdkEnvironmentVariableTarget - { - DotDefault, - // the current user could be DotDefault if it is running under System - CurrentUser - } +internal enum SdkEnvironmentVariableTarget +{ + DotDefault, + // the current user could be DotDefault if it is running under System + CurrentUser } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Interop.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Interop.cs index c06f427b247a..240ca6d4095c 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Interop.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Interop.cs @@ -4,71 +4,70 @@ // UrlMonTypeLib.IInternetSecurityManager using System.Runtime.CompilerServices; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +[ComImport] +[Guid("79EAC9EE-BAF9-11CE-8C82-00AA004BA90B")] +[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +[ComConversionLoss] +internal interface IInternetSecurityManager { - [ComImport] - [Guid("79EAC9EE-BAF9-11CE-8C82-00AA004BA90B")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [ComConversionLoss] - internal interface IInternetSecurityManager - { - void SetSecuritySite([In][MarshalAs(UnmanagedType.Interface)] IInternetSecurityMgrSite pSite); + void SetSecuritySite([In][MarshalAs(UnmanagedType.Interface)] IInternetSecurityMgrSite pSite); - void GetSecuritySite([MarshalAs(UnmanagedType.Interface)] out IInternetSecurityMgrSite ppSite); + void GetSecuritySite([MarshalAs(UnmanagedType.Interface)] out IInternetSecurityMgrSite ppSite); - void MapUrlToZone([In][MarshalAs(UnmanagedType.LPWStr)] string pwszUrl, out int pdwZone, [In] int dwFlags); + void MapUrlToZone([In][MarshalAs(UnmanagedType.LPWStr)] string pwszUrl, out int pdwZone, [In] int dwFlags); - void GetSecurityId([In][MarshalAs(UnmanagedType.LPWStr)] string pwszUrl, out byte pbSecurityId, [In][Out] ref int pcbSecurityId, [In][ComAliasName("UrlMonTypeLib.ULONG_PTR")] int dwReserved); + void GetSecurityId([In][MarshalAs(UnmanagedType.LPWStr)] string pwszUrl, out byte pbSecurityId, [In][Out] ref int pcbSecurityId, [In][ComAliasName("UrlMonTypeLib.ULONG_PTR")] int dwReserved); - void ProcessUrlAction([In][MarshalAs(UnmanagedType.LPWStr)] string pwszUrl, [In] int dwAction, out byte pPolicy, [In] int cbPolicy, [In] ref byte pContext, [In] int cbContext, [In] int dwFlags, [In] int dwReserved); + void ProcessUrlAction([In][MarshalAs(UnmanagedType.LPWStr)] string pwszUrl, [In] int dwAction, out byte pPolicy, [In] int cbPolicy, [In] ref byte pContext, [In] int cbContext, [In] int dwFlags, [In] int dwReserved); - void QueryCustomPolicy([In][MarshalAs(UnmanagedType.LPWStr)] string pwszUrl, [In][ComAliasName("UrlMonTypeLib.GUID")] ref GUID guidKey, [Out] IntPtr ppPolicy, out int pcbPolicy, [In] ref byte pContext, [In] int cbContext, [In] int dwReserved); + void QueryCustomPolicy([In][MarshalAs(UnmanagedType.LPWStr)] string pwszUrl, [In][ComAliasName("UrlMonTypeLib.GUID")] ref GUID guidKey, [Out] IntPtr ppPolicy, out int pcbPolicy, [In] ref byte pContext, [In] int cbContext, [In] int dwReserved); - void SetZoneMapping([In] int dwZone, [In][MarshalAs(UnmanagedType.LPWStr)] string lpszPattern, [In] int dwFlags); + void SetZoneMapping([In] int dwZone, [In][MarshalAs(UnmanagedType.LPWStr)] string lpszPattern, [In] int dwFlags); - void GetZoneMappings([In] int dwZone, [MarshalAs(UnmanagedType.Interface)] out IEnumString ppenumString, [In] int dwFlags); - } + void GetZoneMappings([In] int dwZone, [MarshalAs(UnmanagedType.Interface)] out IEnumString ppenumString, [In] int dwFlags); +} - // UrlMonTypeLib.IInternetSecurityMgrSite - [ComImport] - [ComConversionLoss] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("79EAC9ED-BAF9-11CE-8C82-00AA004BA90B")] - interface IInternetSecurityMgrSite - { - void GetWindow([Out][ComAliasName("UrlMonTypeLib.wireHWND")] IntPtr phwnd); +// UrlMonTypeLib.IInternetSecurityMgrSite +[ComImport] +[ComConversionLoss] +[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +[Guid("79EAC9ED-BAF9-11CE-8C82-00AA004BA90B")] +interface IInternetSecurityMgrSite +{ + void GetWindow([Out][ComAliasName("UrlMonTypeLib.wireHWND")] IntPtr phwnd); - void EnableModeless([In] int fEnable); - } + void EnableModeless([In] int fEnable); +} - [StructLayout(LayoutKind.Sequential, Pack = 4)] - struct GUID - { - public int Data1; +[StructLayout(LayoutKind.Sequential, Pack = 4)] +struct GUID +{ + public int Data1; - public ushort Data2; + public ushort Data2; - public ushort Data3; + public ushort Data3; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public byte[] Data4; - } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] Data4; +} - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("00000101-0000-0000-C000-000000000046")] - interface IEnumString - { - [MethodImpl(MethodImplOptions.InternalCall)] - void RemoteNext([In] int celt, [MarshalAs(UnmanagedType.LPWStr)] out string rgelt, out int pceltFetched); +[ComImport] +[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +[Guid("00000101-0000-0000-C000-000000000046")] +interface IEnumString +{ + [MethodImpl(MethodImplOptions.InternalCall)] + void RemoteNext([In] int celt, [MarshalAs(UnmanagedType.LPWStr)] out string rgelt, out int pceltFetched); - [MethodImpl(MethodImplOptions.InternalCall)] - void Skip([In] int celt); + [MethodImpl(MethodImplOptions.InternalCall)] + void Skip([In] int celt); - [MethodImpl(MethodImplOptions.InternalCall)] - void Reset(); + [MethodImpl(MethodImplOptions.InternalCall)] + void Reset(); - [MethodImpl(MethodImplOptions.InternalCall)] - void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumString ppenum); - } + [MethodImpl(MethodImplOptions.InternalCall)] + void Clone([MarshalAs(UnmanagedType.Interface)] out IEnumString ppenum); } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs index cf4df631d8b4..6ecc1f539a3c 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs @@ -3,216 +3,209 @@ #if NET -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; using Microsoft.DotNet.Cli.Utils.Extensions; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +internal class MSBuildForwardingAppWithoutLogging { - internal class MSBuildForwardingAppWithoutLogging + private static readonly bool AlwaysExecuteMSBuildOutOfProc = Env.GetEnvironmentVariableAsBool("DOTNET_CLI_RUN_MSBUILD_OUTOFPROC"); + private static readonly bool UseMSBuildServer = Env.GetEnvironmentVariableAsBool("DOTNET_CLI_USE_MSBUILD_SERVER", false); + private static readonly string? TerminalLoggerDefault = Env.GetEnvironmentVariable("DOTNET_CLI_CONFIGURE_MSBUILD_TERMINAL_LOGGER"); + + public static string MSBuildVersion { - private static readonly bool AlwaysExecuteMSBuildOutOfProc = Env.GetEnvironmentVariableAsBool("DOTNET_CLI_RUN_MSBUILD_OUTOFPROC"); - private static readonly bool UseMSBuildServer = Env.GetEnvironmentVariableAsBool("DOTNET_CLI_USE_MSBUILD_SERVER", false); - private static readonly string? TerminalLoggerDefault = Env.GetEnvironmentVariable("DOTNET_CLI_CONFIGURE_MSBUILD_TERMINAL_LOGGER"); + get => Microsoft.Build.Evaluation.ProjectCollection.DisplayVersion; + } + private const string MSBuildExeName = "MSBuild.dll"; - public static string MSBuildVersion - { - get => Microsoft.Build.Evaluation.ProjectCollection.DisplayVersion; - } - private const string MSBuildExeName = "MSBuild.dll"; + private const string SdksDirectoryName = "Sdks"; - private const string SdksDirectoryName = "Sdks"; + // Null if we're running MSBuild in-proc. + private ForwardingAppImplementation? _forwardingApp; - // Null if we're running MSBuild in-proc. - private ForwardingAppImplementation? _forwardingApp; + // Command line arguments we're asked to forward to MSBuild. + private readonly IEnumerable _argsToForward; - // Command line arguments we're asked to forward to MSBuild. - private readonly IEnumerable _argsToForward; + internal static string? MSBuildExtensionsPathTestHook = null; - internal static string? MSBuildExtensionsPathTestHook = null; + // Path to the MSBuild binary to use. + public string MSBuildPath { get; } - // Path to the MSBuild binary to use. - public string MSBuildPath { get; } + // True if, given current state of the class, MSBuild would be executed in its own process. + public bool ExecuteMSBuildOutOfProc => _forwardingApp != null; - // True if, given current state of the class, MSBuild would be executed in its own process. - public bool ExecuteMSBuildOutOfProc => _forwardingApp != null; + private readonly Dictionary _msbuildRequiredEnvironmentVariables = + new() + { + { "MSBuildExtensionsPath", MSBuildExtensionsPathTestHook ?? AppContext.BaseDirectory }, + { "MSBuildSDKsPath", GetMSBuildSDKsPath() }, + { "DOTNET_HOST_PATH", GetDotnetPath() }, + }; - private readonly Dictionary _msbuildRequiredEnvironmentVariables = - new() - { - { "MSBuildExtensionsPath", MSBuildExtensionsPathTestHook ?? AppContext.BaseDirectory }, - { "MSBuildSDKsPath", GetMSBuildSDKsPath() }, - { "DOTNET_HOST_PATH", GetDotnetPath() }, - }; + private readonly List _msbuildRequiredParameters = + [ "-maxcpucount", "-verbosity:m" ]; - private readonly List _msbuildRequiredParameters = - [ "-maxcpucount", "-verbosity:m" ]; + public MSBuildForwardingAppWithoutLogging(IEnumerable argsToForward, string? msbuildPath = null, bool includeLogo = false) + { + string defaultMSBuildPath = GetMSBuildExePath(); - public MSBuildForwardingAppWithoutLogging(IEnumerable argsToForward, string? msbuildPath = null, bool includeLogo = false) + _argsToForward = includeLogo ? argsToForward : ["-nologo", ..argsToForward]; + string? tlpDefault = TerminalLoggerDefault; + // new for .NET 9 - default TL to auto (aka enable in non-CI scenarios) + if (string.IsNullOrWhiteSpace(tlpDefault)) { - string defaultMSBuildPath = GetMSBuildExePath(); - - _argsToForward = includeLogo ? argsToForward : ["-nologo", ..argsToForward]; - string? tlpDefault = TerminalLoggerDefault; - // new for .NET 9 - default TL to auto (aka enable in non-CI scenarios) - if (string.IsNullOrWhiteSpace(tlpDefault)) - { - tlpDefault = "auto"; - } - - if (!string.IsNullOrWhiteSpace(tlpDefault)) - { - _msbuildRequiredParameters.Add($"-tlp:default={tlpDefault}"); - } + tlpDefault = "auto"; + } - MSBuildPath = msbuildPath ?? defaultMSBuildPath; + if (!string.IsNullOrWhiteSpace(tlpDefault)) + { + _msbuildRequiredParameters.Add($"-tlp:default={tlpDefault}"); + } - EnvironmentVariable("MSBUILDUSESERVER", UseMSBuildServer ? "1" : "0"); + MSBuildPath = msbuildPath ?? defaultMSBuildPath; - // If DOTNET_CLI_RUN_MSBUILD_OUTOFPROC is set or we're asked to execute a non-default binary, call MSBuild out-of-proc. - if (AlwaysExecuteMSBuildOutOfProc || !string.Equals(MSBuildPath, defaultMSBuildPath, StringComparison.OrdinalIgnoreCase)) - { - InitializeForOutOfProcForwarding(); - } - } + EnvironmentVariable("MSBUILDUSESERVER", UseMSBuildServer ? "1" : "0"); - private void InitializeForOutOfProcForwarding() + // If DOTNET_CLI_RUN_MSBUILD_OUTOFPROC is set or we're asked to execute a non-default binary, call MSBuild out-of-proc. + if (AlwaysExecuteMSBuildOutOfProc || !string.Equals(MSBuildPath, defaultMSBuildPath, StringComparison.OrdinalIgnoreCase)) { - _forwardingApp = new ForwardingAppImplementation( - MSBuildPath, - GetAllArguments(), - environmentVariables: _msbuildRequiredEnvironmentVariables); + InitializeForOutOfProcForwarding(); } + } + + private void InitializeForOutOfProcForwarding() + { + _forwardingApp = new ForwardingAppImplementation( + MSBuildPath, + GetAllArguments(), + environmentVariables: _msbuildRequiredEnvironmentVariables); + } + + public ProcessStartInfo GetProcessStartInfo() + { + Debug.Assert(_forwardingApp != null, "Can't get ProcessStartInfo when not executing out-of-proc"); + return _forwardingApp.GetProcessStartInfo(); + } - public ProcessStartInfo GetProcessStartInfo() + public string[] GetAllArguments() + { + return _msbuildRequiredParameters.Concat(_argsToForward.Select(Escape)).ToArray(); + } + + public void EnvironmentVariable(string name, string value) + { + if (_forwardingApp != null) { - Debug.Assert(_forwardingApp != null, "Can't get ProcessStartInfo when not executing out-of-proc"); - return _forwardingApp.GetProcessStartInfo(); + _forwardingApp.WithEnvironmentVariable(name, value); } - - public string[] GetAllArguments() + else { - return _msbuildRequiredParameters.Concat(_argsToForward.Select(Escape)).ToArray(); + _msbuildRequiredEnvironmentVariables.Add(name, value); } - public void EnvironmentVariable(string name, string value) + if (value == string.Empty || value == "\0") { - if (_forwardingApp != null) - { - _forwardingApp.WithEnvironmentVariable(name, value); - } - else - { - _msbuildRequiredEnvironmentVariables.Add(name, value); - } + // Unlike ProcessStartInfo.EnvironmentVariables, Environment.SetEnvironmentVariable can't set a variable + // to an empty value, so we just fall back to calling MSBuild out-of-proc if we encounter this case. + // https://github.com/dotnet/runtime/issues/50554 + InitializeForOutOfProcForwarding(); - if (value == string.Empty || value == "\0") - { - // Unlike ProcessStartInfo.EnvironmentVariables, Environment.SetEnvironmentVariable can't set a variable - // to an empty value, so we just fall back to calling MSBuild out-of-proc if we encounter this case. - // https://github.com/dotnet/runtime/issues/50554 - InitializeForOutOfProcForwarding(); + // Disable MSBUILDUSESERVER if any env vars are null as those are not properly transferred to build nodes + _msbuildRequiredEnvironmentVariables["MSBUILDUSESERVER"] = "0"; + } + } - // Disable MSBUILDUSESERVER if any env vars are null as those are not properly transferred to build nodes - _msbuildRequiredEnvironmentVariables["MSBUILDUSESERVER"] = "0"; - } + public int Execute() + { + if (_forwardingApp != null) + { + return GetProcessStartInfo().Execute(); + } + else + { + return ExecuteInProc(GetAllArguments()); } + } - public int Execute() + public int ExecuteInProc(string[] arguments) + { + // Save current environment variables before overwriting them. + Dictionary savedEnvironmentVariables = new(); + try { - if (_forwardingApp != null) - { - return GetProcessStartInfo().Execute(); - } - else + foreach (KeyValuePair kvp in _msbuildRequiredEnvironmentVariables) { - return ExecuteInProc(GetAllArguments()); + savedEnvironmentVariables[kvp.Key] = Environment.GetEnvironmentVariable(kvp.Key); + Environment.SetEnvironmentVariable(kvp.Key, kvp.Value); } - } - public int ExecuteInProc(string[] arguments) - { - // Save current environment variables before overwriting them. - Dictionary savedEnvironmentVariables = new(); try { - foreach (KeyValuePair kvp in _msbuildRequiredEnvironmentVariables) - { - savedEnvironmentVariables[kvp.Key] = Environment.GetEnvironmentVariable(kvp.Key); - Environment.SetEnvironmentVariable(kvp.Key, kvp.Value); - } - - try - { - // Execute MSBuild in the current process by calling its Main method. - return Build.CommandLine.MSBuildApp.Main(arguments); - } - catch (Exception exception) - { - // MSBuild, like all well-behaved CLI tools, handles all exceptions. In the unlikely case - // that something still escapes, we print the exception and fail the call. Non-localized - // string is OK here. - Console.Error.Write("Unhandled exception: "); - Console.Error.WriteLine(exception.ToString()); - - return unchecked((int)0xe0434352); // EXCEPTION_COMPLUS - } + // Execute MSBuild in the current process by calling its Main method. + return Build.CommandLine.MSBuildApp.Main(arguments); } - finally + catch (Exception exception) { - // Restore saved environment variables. - foreach (KeyValuePair kvp in savedEnvironmentVariables) - { - Environment.SetEnvironmentVariable(kvp.Key, kvp.Value); - } + // MSBuild, like all well-behaved CLI tools, handles all exceptions. In the unlikely case + // that something still escapes, we print the exception and fail the call. Non-localized + // string is OK here. + Console.Error.Write("Unhandled exception: "); + Console.Error.WriteLine(exception.ToString()); + + return unchecked((int)0xe0434352); // EXCEPTION_COMPLUS } } - - private static string Escape(string arg) => - // this is a workaround for https://github.com/Microsoft/msbuild/issues/1622 - IsRestoreSources(arg) ? - arg.Replace(";", "%3B") - .Replace("://", ":%2F%2F") : - arg; - - private static string GetMSBuildExePath() + finally { - return Path.Combine( - AppContext.BaseDirectory, - MSBuildExeName); + // Restore saved environment variables. + foreach (KeyValuePair kvp in savedEnvironmentVariables) + { + Environment.SetEnvironmentVariable(kvp.Key, kvp.Value); + } } + } - private static string GetMSBuildSDKsPath() - { - var envMSBuildSDKsPath = Environment.GetEnvironmentVariable("MSBuildSDKsPath"); + private static string Escape(string arg) => + // this is a workaround for https://github.com/Microsoft/msbuild/issues/1622 + IsRestoreSources(arg) ? + arg.Replace(";", "%3B") + .Replace("://", ":%2F%2F") : + arg; - if (envMSBuildSDKsPath != null) - { - return envMSBuildSDKsPath; - } + private static string GetMSBuildExePath() + { + return Path.Combine( + AppContext.BaseDirectory, + MSBuildExeName); + } - return Path.Combine( - AppContext.BaseDirectory, - SdksDirectoryName); - } + private static string GetMSBuildSDKsPath() + { + var envMSBuildSDKsPath = Environment.GetEnvironmentVariable("MSBuildSDKsPath"); - private static string GetDotnetPath() + if (envMSBuildSDKsPath != null) { - return new Muxer().MuxerPath; + return envMSBuildSDKsPath; } - private static bool IsRestoreSources(string arg) - { - return arg.StartsWith("/p:RestoreSources=", StringComparison.OrdinalIgnoreCase) || - arg.StartsWith("/property:RestoreSources=", StringComparison.OrdinalIgnoreCase) || - arg.StartsWith("-p:RestoreSources=", StringComparison.OrdinalIgnoreCase) || - arg.StartsWith("-property:RestoreSources=", StringComparison.OrdinalIgnoreCase); - } + return Path.Combine( + AppContext.BaseDirectory, + SdksDirectoryName); + } + + private static string GetDotnetPath() + { + return new Muxer().MuxerPath; + } + + private static bool IsRestoreSources(string arg) + { + return arg.StartsWith("/p:RestoreSources=", StringComparison.OrdinalIgnoreCase) || + arg.StartsWith("/property:RestoreSources=", StringComparison.OrdinalIgnoreCase) || + arg.StartsWith("-p:RestoreSources=", StringComparison.OrdinalIgnoreCase) || + arg.StartsWith("-property:RestoreSources=", StringComparison.OrdinalIgnoreCase); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs index f4f799111624..9e42dbd2f57a 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Muxer.cs @@ -1,7 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if !NET6_0_OR_GREATER using System.Diagnostics; +#endif namespace Microsoft.DotNet.Cli.Utils { diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/NativeMethods.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/NativeMethods.cs index 7f6f766049dd..ec812c793eac 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/NativeMethods.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/NativeMethods.cs @@ -3,97 +3,96 @@ using Microsoft.Win32.SafeHandles; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +internal static class NativeMethods { - internal static class NativeMethods + internal static class Windows { - internal static class Windows + internal enum JobObjectInfoClass : uint + { + JobObjectExtendedLimitInformation = 9, + } + + [Flags] + internal enum JobObjectLimitFlags : uint { - internal enum JobObjectInfoClass : uint - { - JobObjectExtendedLimitInformation = 9, - } - - [Flags] - internal enum JobObjectLimitFlags : uint - { - JobObjectLimitKillOnJobClose = 0x2000, - } - - [StructLayout(LayoutKind.Sequential)] - internal struct JobObjectBasicLimitInformation - { - public long PerProcessUserTimeLimit; - public long PerJobUserTimeLimit; - public JobObjectLimitFlags LimitFlags; - public UIntPtr MinimumWorkingSetSize; - public UIntPtr MaximumWorkingSetSize; - public uint ActiveProcessLimit; - public UIntPtr Affinity; - public uint PriorityClass; - public uint SchedulingClass; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct IoCounters - { - public ulong ReadOperationCount; - public ulong WriteOperationCount; - public ulong OtherOperationCount; - public ulong ReadTransferCount; - public ulong WriteTransferCount; - public ulong OtherTransferCount; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct JobObjectExtendedLimitInformation - { - public JobObjectBasicLimitInformation BasicLimitInformation; - public IoCounters IoInfo; - public UIntPtr ProcessMemoryLimit; - public UIntPtr JobMemoryLimit; - public UIntPtr PeakProcessMemoryUsed; - public UIntPtr PeakJobMemoryUsed; - } - - internal const int ProcessBasicInformation = 0; - - [StructLayout(LayoutKind.Sequential)] - internal struct PROCESS_BASIC_INFORMATION - { - public uint ExitStatus; - public IntPtr PebBaseAddress; - public UIntPtr AffinityMask; - public int BasePriority; - public UIntPtr UniqueProcessId; - public UIntPtr InheritedFromUniqueProcessId; - } - - [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern SafeWaitHandle CreateJobObjectW(IntPtr lpJobAttributes, string? lpName); - - [DllImport("kernel32.dll", SetLastError = true)] - internal static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoClass jobObjectInformationClass, IntPtr lpJobObjectInformation, uint cbJobObjectInformationLength); - - [DllImport("kernel32.dll", SetLastError = true)] - internal static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess); - - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - internal static extern IntPtr GetCommandLine(); - - [DllImport("ntdll.dll", SetLastError = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - internal static extern unsafe uint NtQueryInformationProcess(SafeProcessHandle ProcessHandle, int ProcessInformationClass, void* ProcessInformation, uint ProcessInformationLength, out uint ReturnLength); + JobObjectLimitKillOnJobClose = 0x2000, } - internal static class Posix + [StructLayout(LayoutKind.Sequential)] + internal struct JobObjectBasicLimitInformation { - [DllImport("libc", SetLastError = true)] - internal static extern int kill(int pid, int sig); + public long PerProcessUserTimeLimit; + public long PerJobUserTimeLimit; + public JobObjectLimitFlags LimitFlags; + public UIntPtr MinimumWorkingSetSize; + public UIntPtr MaximumWorkingSetSize; + public uint ActiveProcessLimit; + public UIntPtr Affinity; + public uint PriorityClass; + public uint SchedulingClass; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct IoCounters + { + public ulong ReadOperationCount; + public ulong WriteOperationCount; + public ulong OtherOperationCount; + public ulong ReadTransferCount; + public ulong WriteTransferCount; + public ulong OtherTransferCount; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct JobObjectExtendedLimitInformation + { + public JobObjectBasicLimitInformation BasicLimitInformation; + public IoCounters IoInfo; + public UIntPtr ProcessMemoryLimit; + public UIntPtr JobMemoryLimit; + public UIntPtr PeakProcessMemoryUsed; + public UIntPtr PeakJobMemoryUsed; + } - internal const int SIGINT = 2; - internal const int SIGTERM = 15; + internal const int ProcessBasicInformation = 0; + + [StructLayout(LayoutKind.Sequential)] + internal struct PROCESS_BASIC_INFORMATION + { + public uint ExitStatus; + public IntPtr PebBaseAddress; + public UIntPtr AffinityMask; + public int BasePriority; + public UIntPtr UniqueProcessId; + public UIntPtr InheritedFromUniqueProcessId; } + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern SafeWaitHandle CreateJobObjectW(IntPtr lpJobAttributes, string? lpName); + + [DllImport("kernel32.dll", SetLastError = true)] + internal static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoClass jobObjectInformationClass, IntPtr lpJobObjectInformation, uint cbJobObjectInformationLength); + + [DllImport("kernel32.dll", SetLastError = true)] + internal static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + internal static extern IntPtr GetCommandLine(); + + [DllImport("ntdll.dll", SetLastError = true)] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + internal static extern unsafe uint NtQueryInformationProcess(SafeProcessHandle ProcessHandle, int ProcessInformationClass, void* ProcessInformation, uint ProcessInformationLength, out uint ReturnLength); + } + + internal static class Posix + { + [DllImport("libc", SetLastError = true)] + internal static extern int kill(int pid, int sig); + + internal const int SIGINT = 2; + internal const int SIGTERM = 15; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs index 75e20189e680..9527cee0282b 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs @@ -2,420 +2,418 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Text.RegularExpressions; -using Microsoft.DotNet.Cli.Utils; using NuGet.Configuration; -namespace Microsoft.DotNet.Tools.Common +namespace Microsoft.DotNet.Cli.Utils; + +public static class PathUtility { - public static class PathUtility + public static bool CheckForNuGetInNuGetConfig() { - public static bool CheckForNuGetInNuGetConfig() - { - var otherFiles = SettingsUtility.GetEnabledSources(Settings.LoadDefaultSettings(Directory.GetCurrentDirectory())); - return otherFiles.Any(source => source.SourceUri.Equals("https://api.nuget.org/v3/index.json")); - } + var otherFiles = SettingsUtility.GetEnabledSources(Settings.LoadDefaultSettings(Directory.GetCurrentDirectory())); + return otherFiles.Any(source => source.SourceUri.Equals("https://api.nuget.org/v3/index.json")); + } + + public static bool IsPlaceholderFile(string path) + { + return string.Equals(Path.GetFileName(path), "_._", StringComparison.Ordinal); + } - public static bool IsPlaceholderFile(string path) + public static bool IsChildOfDirectory(string dir, string candidate) + { + if (dir == null) { - return string.Equals(Path.GetFileName(path), "_._", StringComparison.Ordinal); + throw new ArgumentNullException(nameof(dir)); } - - public static bool IsChildOfDirectory(string dir, string candidate) + if (candidate == null) { - if (dir == null) - { - throw new ArgumentNullException(nameof(dir)); - } - if (candidate == null) - { - throw new ArgumentNullException(nameof(candidate)); - } - dir = Path.GetFullPath(dir); - dir = EnsureTrailingSlash(dir); - candidate = Path.GetFullPath(candidate); - return candidate.StartsWith(dir, StringComparison.OrdinalIgnoreCase); + throw new ArgumentNullException(nameof(candidate)); } + dir = Path.GetFullPath(dir); + dir = EnsureTrailingSlash(dir); + candidate = Path.GetFullPath(candidate); + return candidate.StartsWith(dir, StringComparison.OrdinalIgnoreCase); + } + + public static string EnsureTrailingSlash(string path) + { + return EnsureTrailingCharacter(path, Path.DirectorySeparatorChar); + } - public static string EnsureTrailingSlash(string path) + public static string EnsureTrailingForwardSlash(string path) + { + return EnsureTrailingCharacter(path, '/'); + } + + private static string EnsureTrailingCharacter(string path, char trailingCharacter) + { + if (path == null) { - return EnsureTrailingCharacter(path, Path.DirectorySeparatorChar); + throw new ArgumentNullException(nameof(path)); } - public static string EnsureTrailingForwardSlash(string path) + // if the path is empty, we want to return the original string instead of a single trailing character. + if (path.Length == 0 || path[path.Length - 1] == trailingCharacter) { - return EnsureTrailingCharacter(path, '/'); + return path; } - private static string EnsureTrailingCharacter(string path, char trailingCharacter) + return path + trailingCharacter; + } + + public static string EnsureNoTrailingDirectorySeparator(string path) + { + if (!string.IsNullOrEmpty(path)) { - if (path == null) + char lastChar = path[path.Length - 1]; + if (lastChar == Path.DirectorySeparatorChar) { - throw new ArgumentNullException(nameof(path)); + path = path.Substring(0, path.Length - 1); } + } - // if the path is empty, we want to return the original string instead of a single trailing character. - if (path.Length == 0 || path[path.Length - 1] == trailingCharacter) - { - return path; - } + return path; + } - return path + trailingCharacter; - } + public static void EnsureParentDirectoryExists(string filePath) + { + string? directory = Path.GetDirectoryName(filePath); + EnsureDirectoryExists(directory); + } - public static string EnsureNoTrailingDirectorySeparator(string path) + public static void EnsureDirectoryExists(string? directoryPath) + { + if (directoryPath is not null && !Directory.Exists(directoryPath)) { - if (!string.IsNullOrEmpty(path)) - { - char lastChar = path[path.Length - 1]; - if (lastChar == Path.DirectorySeparatorChar) - { - path = path.Substring(0, path.Length - 1); - } - } - - return path; + Directory.CreateDirectory(directoryPath); } + } - public static void EnsureParentDirectoryExists(string filePath) + public static bool TryDeleteDirectory(string directoryPath) + { + try { - string? directory = Path.GetDirectoryName(filePath); - EnsureDirectoryExists(directory); + Directory.Delete(directoryPath, true); + return true; } - - public static void EnsureDirectoryExists(string? directoryPath) + catch { - if (directoryPath is not null && !Directory.Exists(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } + return false; } + } - public static bool TryDeleteDirectory(string directoryPath) + /// + /// Deletes the provided file. Then deletes the parent directory if empty + /// and continues to its parent until it fails. Returns whether it succeeded + /// in deleting the file it was intended to delete. + /// + public static bool DeleteFileAndEmptyParents(string path, int maxDirectoriesToDelete = int.MaxValue) + { + if (!File.Exists(path)) { - try - { - Directory.Delete(directoryPath, true); - return true; - } - catch - { - return false; - } + return false; } - /// - /// Deletes the provided file. Then deletes the parent directory if empty - /// and continues to its parent until it fails. Returns whether it succeeded - /// in deleting the file it was intended to delete. - /// - public static bool DeleteFileAndEmptyParents(string path, int maxDirectoriesToDelete = int.MaxValue) + File.Delete(path); + var dir = Path.GetDirectoryName(path); + + int directoriesDeleted = 0; + + while (dir is not null && !Directory.EnumerateFileSystemEntries(dir).Any() && + directoriesDeleted < maxDirectoriesToDelete) { - if (!File.Exists(path)) - { - return false; - } + Directory.Delete(dir); + directoriesDeleted++; + dir = Path.GetDirectoryName(dir); + } - File.Delete(path); - var dir = Path.GetDirectoryName(path); + return !File.Exists(path); + } - int directoriesDeleted = 0; + /// + /// Returns childItem relative to directory, with Path.DirectorySeparatorChar as separator + /// + public static string GetRelativePath(DirectoryInfo directory, FileSystemInfo childItem) + { + var path1 = EnsureTrailingSlash(directory.FullName); - while (dir is not null && !Directory.EnumerateFileSystemEntries(dir).Any() && - directoriesDeleted < maxDirectoriesToDelete) - { - Directory.Delete(dir); - directoriesDeleted++; - dir = Path.GetDirectoryName(dir); - } + var path2 = childItem.FullName; - return !File.Exists(path); - } + return GetRelativePath(path1, path2, Path.DirectorySeparatorChar, true); + } - /// - /// Returns childItem relative to directory, with Path.DirectorySeparatorChar as separator - /// - public static string GetRelativePath(DirectoryInfo directory, FileSystemInfo childItem) + /// + /// Returns path2 relative to path1, with Path.DirectorySeparatorChar as separator + /// + public static string GetRelativePath(string path1, string path2) + { + if (!Path.IsPathRooted(path1) || !Path.IsPathRooted(path2)) { - var path1 = EnsureTrailingSlash(directory.FullName); + throw new ArgumentException("both paths need to be rooted/full path"); + } - var path2 = childItem.FullName; + return GetRelativePath(path1, path2, Path.DirectorySeparatorChar, true); + } - return GetRelativePath(path1, path2, Path.DirectorySeparatorChar, true); - } + /// + /// Returns path2 relative to path1, with Path.DirectorySeparatorChar as separator but ignoring directory + /// traversals. + /// + public static string GetRelativePathIgnoringDirectoryTraversals(string path1, string path2) + { + return GetRelativePath(path1, path2, Path.DirectorySeparatorChar, false); + } - /// - /// Returns path2 relative to path1, with Path.DirectorySeparatorChar as separator - /// - public static string GetRelativePath(string path1, string path2) + /// + /// Returns path2 relative to path1, with given path separator + /// + public static string GetRelativePath(string path1, string path2, char separator, bool includeDirectoryTraversals) + { + if (string.IsNullOrEmpty(path1)) { - if (!Path.IsPathRooted(path1) || !Path.IsPathRooted(path2)) - { - throw new ArgumentException("both paths need to be rooted/full path"); - } - - return GetRelativePath(path1, path2, Path.DirectorySeparatorChar, true); + throw new ArgumentException("Path must have a value", nameof(path1)); } - /// - /// Returns path2 relative to path1, with Path.DirectorySeparatorChar as separator but ignoring directory - /// traversals. - /// - public static string GetRelativePathIgnoringDirectoryTraversals(string path1, string path2) + if (string.IsNullOrEmpty(path2)) { - return GetRelativePath(path1, path2, Path.DirectorySeparatorChar, false); + throw new ArgumentException("Path must have a value", nameof(path2)); } - /// - /// Returns path2 relative to path1, with given path separator - /// - public static string GetRelativePath(string path1, string path2, char separator, bool includeDirectoryTraversals) + StringComparison compare; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (string.IsNullOrEmpty(path1)) - { - throw new ArgumentException("Path must have a value", nameof(path1)); - } - - if (string.IsNullOrEmpty(path2)) - { - throw new ArgumentException("Path must have a value", nameof(path2)); - } - - StringComparison compare; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + compare = StringComparison.OrdinalIgnoreCase; + // check if paths are on the same volume + if (!string.Equals(Path.GetPathRoot(path1), Path.GetPathRoot(path2), compare)) { - compare = StringComparison.OrdinalIgnoreCase; - // check if paths are on the same volume - if (!string.Equals(Path.GetPathRoot(path1), Path.GetPathRoot(path2), compare)) - { - // on different volumes, "relative" path is just path2 - return path2; - } + // on different volumes, "relative" path is just path2 + return path2; } - else - { - compare = StringComparison.Ordinal; - } - - var index = 0; - var path1Segments = path1.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - var path2Segments = path2.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); - // if path1 does not end with / it is assumed the end is not a directory - // we will assume that is isn't a directory by ignoring the last split - var len1 = path1Segments.Length - 1; - var len2 = path2Segments.Length; - - // find largest common absolute path between both paths - var min = Math.Min(len1, len2); - while (min > index) - { - if (!string.Equals(path1Segments[index], path2Segments[index], compare)) - { - break; - } - // Handle scenarios where folder and file have same name (only if os supports same name for file and directory) - // e.g. /file/name /file/name/app - else if ((len1 == index && len2 > index + 1) || (len1 > index && len2 == index + 1)) - { - break; - } - ++index; - } - - var path = ""; + } + else + { + compare = StringComparison.Ordinal; + } - // check if path2 ends with a non-directory separator and if path1 has the same non-directory at the end - if (len1 + 1 == len2 && !string.IsNullOrEmpty(path1Segments[index]) && - string.Equals(path1Segments[index], path2Segments[index], compare)) + var index = 0; + var path1Segments = path1.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + var path2Segments = path2.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + // if path1 does not end with / it is assumed the end is not a directory + // we will assume that is isn't a directory by ignoring the last split + var len1 = path1Segments.Length - 1; + var len2 = path2Segments.Length; + + // find largest common absolute path between both paths + var min = Math.Min(len1, len2); + while (min > index) + { + if (!string.Equals(path1Segments[index], path2Segments[index], compare)) { - return path; + break; } - - if (includeDirectoryTraversals) + // Handle scenarios where folder and file have same name (only if os supports same name for file and directory) + // e.g. /file/name /file/name/app + else if ((len1 == index && len2 > index + 1) || (len1 > index && len2 == index + 1)) { - for (var i = index; len1 > i; ++i) - { - path += ".." + separator; - } + break; } + ++index; + } - for (var i = index; len2 - 1 > i; ++i) - { - path += path2Segments[i] + separator; - } - // if path2 doesn't end with an empty string it means it ended with a non-directory name, so we add it back - if (!string.IsNullOrEmpty(path2Segments[len2 - 1])) - { - path += path2Segments[len2 - 1]; - } + var path = ""; + // check if path2 ends with a non-directory separator and if path1 has the same non-directory at the end + if (len1 + 1 == len2 && !string.IsNullOrEmpty(path1Segments[index]) && + string.Equals(path1Segments[index], path2Segments[index], compare)) + { return path; } - [Obsolete("Use System.IO.Path.GetFullPath(string, string) instead, or PathUtility.GetFullPath(string) if the base path is the current working directory.")] - public static string GetAbsolutePath(string basePath, string relativePath) + if (includeDirectoryTraversals) { - if (basePath == null) + for (var i = index; len1 > i; ++i) { - throw new ArgumentNullException(nameof(basePath)); - } - - if (relativePath == null) - { - throw new ArgumentNullException(nameof(relativePath)); + path += ".." + separator; } + } - Uri resultUri = new(new Uri(basePath), new Uri(relativePath, UriKind.Relative)); - return resultUri.LocalPath; + for (var i = index; len2 - 1 > i; ++i) + { + path += path2Segments[i] + separator; + } + // if path2 doesn't end with an empty string it means it ended with a non-directory name, so we add it back + if (!string.IsNullOrEmpty(path2Segments[len2 - 1])) + { + path += path2Segments[len2 - 1]; } - public static string GetDirectoryName(string path) + return path; + } + + [Obsolete("Use System.IO.Path.GetFullPath(string, string) instead, or PathUtility.GetFullPath(string) if the base path is the current working directory.")] + public static string GetAbsolutePath(string basePath, string relativePath) + { + if (basePath == null) { - path = path.TrimEnd(Path.DirectorySeparatorChar); - return path.Substring(Path.GetDirectoryName(path)?.Length ?? 0).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + throw new ArgumentNullException(nameof(basePath)); } - public static string GetPathWithForwardSlashes(string path) + if (relativePath == null) { - return path.Replace('\\', '/'); + throw new ArgumentNullException(nameof(relativePath)); } - public static string GetPathWithBackSlashes(string path) + Uri resultUri = new(new Uri(basePath), new Uri(relativePath, UriKind.Relative)); + return resultUri.LocalPath; + } + + public static string GetDirectoryName(string path) + { + path = path.TrimEnd(Path.DirectorySeparatorChar); + return path.Substring(Path.GetDirectoryName(path)?.Length ?? 0).Trim(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); + } + + public static string GetPathWithForwardSlashes(string path) + { + return path.Replace('\\', '/'); + } + + public static string GetPathWithBackSlashes(string path) + { + return path.Replace('/', '\\'); + } + + public static string GetPathWithDirectorySeparator(string path) + { + if (Path.DirectorySeparatorChar == '/') + { + return GetPathWithForwardSlashes(path); + } + else { - return path.Replace('/', '\\'); + return GetPathWithBackSlashes(path); } + } - public static string GetPathWithDirectorySeparator(string path) + public static string RemoveExtraPathSeparators(string path) + { + if (string.IsNullOrEmpty(path)) { - if (Path.DirectorySeparatorChar == '/') - { - return GetPathWithForwardSlashes(path); - } - else - { - return GetPathWithBackSlashes(path); - } + return path; } - public static string RemoveExtraPathSeparators(string path) + var components = path.Split(Path.DirectorySeparatorChar); + var result = string.Empty; + + foreach (var component in components) { - if (string.IsNullOrEmpty(path)) + if (string.IsNullOrEmpty(component)) { - return path; + continue; } - var components = path.Split(Path.DirectorySeparatorChar); - var result = string.Empty; - - foreach (var component in components) + if (string.IsNullOrEmpty(result)) { - if (string.IsNullOrEmpty(component)) - { - continue; - } - - if (string.IsNullOrEmpty(result)) - { - result = component; + result = component; - // On Windows, manually append a separator for drive references because Path.Combine won't do so - if (result.EndsWith(":") && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - result += Path.DirectorySeparatorChar; - } - } - else + // On Windows, manually append a separator for drive references because Path.Combine won't do so + if (result.EndsWith(":") && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - result = Path.Combine(result, component); + result += Path.DirectorySeparatorChar; } } - - if (path[path.Length - 1] == Path.DirectorySeparatorChar) + else { - result += Path.DirectorySeparatorChar; + result = Path.Combine(result, component); } - - return result; } - public static bool HasExtension(this string filePath, string extension) + if (path[path.Length - 1] == Path.DirectorySeparatorChar) { - var comparison = StringComparison.Ordinal; + result += Path.DirectorySeparatorChar; + } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - comparison = StringComparison.OrdinalIgnoreCase; - } + return result; + } - return Path.GetExtension(filePath).Equals(extension, comparison); - } + public static bool HasExtension(this string filePath, string extension) + { + var comparison = StringComparison.Ordinal; - /// - /// Gets the fully-qualified path without failing if the - /// path is empty. - /// - public static string GetFullPath(string path) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (string.IsNullOrWhiteSpace(path)) - { - return path; - } - - return Path.GetFullPath(path); + comparison = StringComparison.OrdinalIgnoreCase; } - public static void EnsureAllPathsExist( - IReadOnlyCollection paths, - string pathDoesNotExistLocalizedFormatString, - bool allowDirectories = false) + return Path.GetExtension(filePath).Equals(extension, comparison); + } + + /// + /// Gets the fully-qualified path without failing if the + /// path is empty. + /// + public static string GetFullPath(string path) + { + if (string.IsNullOrWhiteSpace(path)) { - var notExisting = new List(); + return path; + } - foreach (var p in paths) - { - if (!File.Exists(p) && (!allowDirectories || !Directory.Exists(p))) - { - notExisting.Add(p); - } - } + return Path.GetFullPath(path); + } - if (notExisting.Count > 0) + public static void EnsureAllPathsExist( + IReadOnlyCollection paths, + string pathDoesNotExistLocalizedFormatString, + bool allowDirectories = false) + { + var notExisting = new List(); + + foreach (var p in paths) + { + if (!File.Exists(p) && (!allowDirectories || !Directory.Exists(p))) { - throw new GracefulException( - string.Join( - Environment.NewLine, - notExisting.Select(p => string.Format(pathDoesNotExistLocalizedFormatString, p)))); + notExisting.Add(p); } } - public static bool IsDirectory(this string path) => - File.GetAttributes(path).HasFlag(FileAttributes.Directory); - - public static string FixFilePath(string path) + if (notExisting.Count > 0) { - return string.IsNullOrEmpty(path) || Path.DirectorySeparatorChar == '\\' ? path : path.Replace('\\', '/'); + throw new GracefulException( + string.Join( + Environment.NewLine, + notExisting.Select(p => string.Format(pathDoesNotExistLocalizedFormatString, p)))); } + } - public static string GetDirectorySeparatorChar() - { - return Regex.Escape(Path.DirectorySeparatorChar.ToString()); - } + public static bool IsDirectory(this string path) => + File.GetAttributes(path).HasFlag(FileAttributes.Directory); - public static string? FindFileInParentDirectories(string startDirectory, string relativeFilePath) - { - var directory = new DirectoryInfo(startDirectory); + public static string FixFilePath(string path) + { + return string.IsNullOrEmpty(path) || Path.DirectorySeparatorChar == '\\' ? path : path.Replace('\\', '/'); + } - while (directory != null) - { - var filePath = Path.Combine(directory.FullName, relativeFilePath); - if (File.Exists(filePath)) - { - return filePath; - } + public static string GetDirectorySeparatorChar() + { + return Regex.Escape(Path.DirectorySeparatorChar.ToString()); + } - directory = directory.Parent; + public static string? FindFileInParentDirectories(string startDirectory, string relativeFilePath) + { + var directory = new DirectoryInfo(startDirectory); + + while (directory != null) + { + var filePath = Path.Combine(directory.FullName, relativeFilePath); + if (File.Exists(filePath)) + { + return filePath; } - return null; + directory = directory.Parent; } + + return null; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Polyfills.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Polyfills.cs index bb696ec3436b..419305315ff6 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Polyfills.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Polyfills.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - #if NET472 namespace System.Runtime.CompilerServices; diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/ProcessReaper.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/ProcessReaper.cs index f9d97e8242d4..32af1993bf08 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/ProcessReaper.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/ProcessReaper.cs @@ -4,190 +4,189 @@ using System.Diagnostics; using Microsoft.Win32.SafeHandles; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +/// +/// Responsible for reaping a target process if the current process terminates. +/// +/// +/// On Windows, a job object will be used to ensure the termination of the target +/// process (and its tree) even if the current process is rudely terminated. +/// +/// On POSIX systems, the reaper will handle SIGTERM and attempt to forward the +/// signal to the target process only. +/// +/// The reaper also suppresses SIGINT in the current process to allow the target +/// process to handle the signal. +/// +internal class ProcessReaper : IDisposable { /// - /// Responsible for reaping a target process if the current process terminates. + /// Creates a new process reaper. /// - /// - /// On Windows, a job object will be used to ensure the termination of the target - /// process (and its tree) even if the current process is rudely terminated. - /// - /// On POSIX systems, the reaper will handle SIGTERM and attempt to forward the - /// signal to the target process only. - /// - /// The reaper also suppresses SIGINT in the current process to allow the target - /// process to handle the signal. - /// - internal class ProcessReaper : IDisposable + /// The target process to reap if the current process terminates. The process should not yet be started. + public ProcessReaper(Process process) { - /// - /// Creates a new process reaper. - /// - /// The target process to reap if the current process terminates. The process should not yet be started. - public ProcessReaper(Process process) - { - _process = process; + _process = process; - // The tests need the event handlers registered prior to spawning the child to prevent a race - // where the child writes output the test expects before the intermediate dotnet process - // has registered the event handlers to handle the signals the tests will generate. - Console.CancelKeyPress += HandleCancelKeyPress; - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - _shutdownMutex = new Mutex(); - AppDomain.CurrentDomain.ProcessExit += HandleProcessExit; - } + // The tests need the event handlers registered prior to spawning the child to prevent a race + // where the child writes output the test expects before the intermediate dotnet process + // has registered the event handlers to handle the signals the tests will generate. + Console.CancelKeyPress += HandleCancelKeyPress; + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + _shutdownMutex = new Mutex(); + AppDomain.CurrentDomain.ProcessExit += HandleProcessExit; } + } - /// - /// Call to notify the reaper that the process has started. - /// - public void NotifyProcessStarted() + /// + /// Call to notify the reaper that the process has started. + /// + public void NotifyProcessStarted() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + // Limit the use of job objects to versions of Windows that support nested jobs (i.e. Windows 8/2012 or later). + // Ideally, we would check for some new API export or OS feature instead of the OS version, + // but nested jobs are transparently implemented with respect to the Job Objects API. + // Note: Windows 8.1 and later may report as Windows 8 (see https://docs.microsoft.com/en-us/windows/desktop/sysinfo/operating-system-version). + // However, for the purpose of this check that is still sufficient. + if (Environment.OSVersion.Version.Major > 6 || + (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2)) { - // Limit the use of job objects to versions of Windows that support nested jobs (i.e. Windows 8/2012 or later). - // Ideally, we would check for some new API export or OS feature instead of the OS version, - // but nested jobs are transparently implemented with respect to the Job Objects API. - // Note: Windows 8.1 and later may report as Windows 8 (see https://docs.microsoft.com/en-us/windows/desktop/sysinfo/operating-system-version). - // However, for the purpose of this check that is still sufficient. - if (Environment.OSVersion.Version.Major > 6 || - (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor >= 2)) - { - _job = AssignProcessToJobObject(_process.Handle); - } + _job = AssignProcessToJobObject(_process.Handle); } } + } - public void Dispose() + public void Dispose() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (_job != null) { - if (_job != null) - { - // Clear the kill on close flag because the child process terminated successfully - // If this fails, then we have no choice but to terminate any remaining processes in the job - SetKillOnJobClose(_job.DangerousGetHandle(), false); - - _job.Dispose(); - _job = null; - } + // Clear the kill on close flag because the child process terminated successfully + // If this fails, then we have no choice but to terminate any remaining processes in the job + SetKillOnJobClose(_job.DangerousGetHandle(), false); + + _job.Dispose(); + _job = null; } - else + } + else + { + AppDomain.CurrentDomain.ProcessExit -= HandleProcessExit; + + // If there's been a shutdown via the process exit handler, + // this will block the current thread so we don't race with the CLR shutdown + // from the signal handler. + if (_shutdownMutex != null) { - AppDomain.CurrentDomain.ProcessExit -= HandleProcessExit; - - // If there's been a shutdown via the process exit handler, - // this will block the current thread so we don't race with the CLR shutdown - // from the signal handler. - if (_shutdownMutex != null) - { - _shutdownMutex.WaitOne(); - _shutdownMutex.ReleaseMutex(); - _shutdownMutex.Dispose(); - _shutdownMutex = null; - } + _shutdownMutex.WaitOne(); + _shutdownMutex.ReleaseMutex(); + _shutdownMutex.Dispose(); + _shutdownMutex = null; } - - Console.CancelKeyPress -= HandleCancelKeyPress; } - private static void HandleCancelKeyPress(object? sender, ConsoleCancelEventArgs e) + Console.CancelKeyPress -= HandleCancelKeyPress; + } + + private static void HandleCancelKeyPress(object? sender, ConsoleCancelEventArgs e) + { + // Ignore SIGINT/SIGQUIT so that the process can handle the signal + e.Cancel = true; + } + + private static SafeWaitHandle? AssignProcessToJobObject(IntPtr process) + { + var job = NativeMethods.Windows.CreateJobObjectW(IntPtr.Zero, null); + if (job == null || job.IsInvalid) { - // Ignore SIGINT/SIGQUIT so that the process can handle the signal - e.Cancel = true; + return null; } - private static SafeWaitHandle? AssignProcessToJobObject(IntPtr process) + if (!SetKillOnJobClose(job.DangerousGetHandle(), true)) { - var job = NativeMethods.Windows.CreateJobObjectW(IntPtr.Zero, null); - if (job == null || job.IsInvalid) - { - return null; - } + job.Dispose(); + return null; + } - if (!SetKillOnJobClose(job.DangerousGetHandle(), true)) - { - job.Dispose(); - return null; - } + if (!NativeMethods.Windows.AssignProcessToJobObject(job.DangerousGetHandle(), process)) + { + job.Dispose(); + return null; + } - if (!NativeMethods.Windows.AssignProcessToJobObject(job.DangerousGetHandle(), process)) - { - job.Dispose(); - return null; - } + return job; + } - return job; + private void HandleProcessExit(object? sender, EventArgs args) + { + int processId; + try + { + processId = _process.Id; } - - private void HandleProcessExit(object? sender, EventArgs args) + catch (InvalidOperationException) { - int processId; - try - { - processId = _process.Id; - } - catch (InvalidOperationException) - { - // The process hasn't started yet; nothing to signal - return; - } + // The process hasn't started yet; nothing to signal + return; + } - // Take ownership of the shutdown mutex; this will ensure that the other - // thread also waiting on the process to exit won't complete CLR shutdown before - // this one does. - _shutdownMutex?.WaitOne(); + // Take ownership of the shutdown mutex; this will ensure that the other + // thread also waiting on the process to exit won't complete CLR shutdown before + // this one does. + _shutdownMutex?.WaitOne(); - if (!_process.WaitForExit(0) && NativeMethods.Posix.kill(processId, NativeMethods.Posix.SIGTERM) != 0) - { - // Couldn't send the signal, don't wait - return; - } + if (!_process.WaitForExit(0) && NativeMethods.Posix.kill(processId, NativeMethods.Posix.SIGTERM) != 0) + { + // Couldn't send the signal, don't wait + return; + } - // If SIGTERM was ignored by the target, then we'll still wait - _process.WaitForExit(); + // If SIGTERM was ignored by the target, then we'll still wait + _process.WaitForExit(); - Environment.ExitCode = _process.ExitCode; - } + Environment.ExitCode = _process.ExitCode; + } - private static bool SetKillOnJobClose(IntPtr job, bool value) + private static bool SetKillOnJobClose(IntPtr job, bool value) + { + var information = new NativeMethods.Windows.JobObjectExtendedLimitInformation { - var information = new NativeMethods.Windows.JobObjectExtendedLimitInformation + BasicLimitInformation = new NativeMethods.Windows.JobObjectBasicLimitInformation { - BasicLimitInformation = new NativeMethods.Windows.JobObjectBasicLimitInformation - { - LimitFlags = (value ? NativeMethods.Windows.JobObjectLimitFlags.JobObjectLimitKillOnJobClose : 0) - } - }; + LimitFlags = (value ? NativeMethods.Windows.JobObjectLimitFlags.JobObjectLimitKillOnJobClose : 0) + } + }; - var length = Marshal.SizeOf(typeof(NativeMethods.Windows.JobObjectExtendedLimitInformation)); - var informationPtr = Marshal.AllocHGlobal(length); + var length = Marshal.SizeOf(typeof(NativeMethods.Windows.JobObjectExtendedLimitInformation)); + var informationPtr = Marshal.AllocHGlobal(length); - try - { - Marshal.StructureToPtr(information, informationPtr, false); - - if (!NativeMethods.Windows.SetInformationJobObject( - job, - NativeMethods.Windows.JobObjectInfoClass.JobObjectExtendedLimitInformation, - informationPtr, - (uint)length)) - { - return false; - } - - return true; - } - finally + try + { + Marshal.StructureToPtr(information, informationPtr, false); + + if (!NativeMethods.Windows.SetInformationJobObject( + job, + NativeMethods.Windows.JobObjectInfoClass.JobObjectExtendedLimitInformation, + informationPtr, + (uint)length)) { - Marshal.FreeHGlobal(informationPtr); + return false; } - } - private Process _process; - private SafeWaitHandle? _job; - private Mutex? _shutdownMutex; + return true; + } + finally + { + Marshal.FreeHGlobal(informationPtr); + } } + + private Process _process; + private SafeWaitHandle? _job; + private Mutex? _shutdownMutex; } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Product.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Product.cs index faa03e1c6e80..8d61c427aace 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Product.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Product.cs @@ -3,21 +3,20 @@ using System.Reflection; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public class Product { - public class Product - { - public static string LongName => LocalizableStrings.DotNetSdkInfo; - public static readonly string Version = GetProductVersion(); + public static string LongName => LocalizableStrings.DotNetSdkInfo; + public static readonly string Version = GetProductVersion(); - private static string GetProductVersion() - { - DotnetVersionFile versionFile = DotnetFiles.VersionFileObject; - return versionFile.BuildNumber ?? - System.Diagnostics.FileVersionInfo.GetVersionInfo( - typeof(Product).GetTypeInfo().Assembly.Location) - .ProductVersion ?? - string.Empty; - } + private static string GetProductVersion() + { + DotnetVersionFile versionFile = DotnetFiles.VersionFileObject; + return versionFile.BuildNumber ?? + System.Diagnostics.FileVersionInfo.GetVersionInfo( + typeof(Product).GetTypeInfo().Assembly.Location) + .ProductVersion ?? + string.Empty; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Reporter.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Reporter.cs index 4fd35a563a68..212b41aa6dfa 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Reporter.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Reporter.cs @@ -1,163 +1,162 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +// Simple console manager +public class Reporter : IReporter { - // Stupid-simple console manager - public class Reporter : IReporter - { - private static SpinLock s_spinlock = new(); + private static SpinLock s_spinlock = new(); - //cannot use auto properties, as those are static + //cannot use auto properties, as those are static #pragma warning disable IDE0032 // Use auto property - private static readonly Reporter s_consoleOutReporter = new(AnsiConsole.GetOutput()); - private static readonly Reporter s_consoleErrReporter = new(AnsiConsole.GetError()); + private static readonly Reporter s_consoleOutReporter = new(AnsiConsole.GetOutput()); + private static readonly Reporter s_consoleErrReporter = new(AnsiConsole.GetError()); #pragma warning restore IDE0032 // Use auto property - private static IReporter s_errorReporter = s_consoleErrReporter; - private static IReporter s_outputReporter = s_consoleOutReporter; - private static IReporter s_verboseReporter = s_consoleOutReporter; + private static IReporter s_errorReporter = s_consoleErrReporter; + private static IReporter s_outputReporter = s_consoleOutReporter; + private static IReporter s_verboseReporter = s_consoleOutReporter; - private readonly AnsiConsole? _console; + private readonly AnsiConsole? _console; - static Reporter() - { - Reset(); - } + static Reporter() + { + Reset(); + } - private Reporter(AnsiConsole? console) - { - _console = console; - } + private Reporter(AnsiConsole? console) + { + _console = console; + } - public static Reporter NullReporter { get; } = new(console: null); - public static Reporter ConsoleOutReporter => s_consoleOutReporter; - public static Reporter ConsoleErrReporter => s_consoleErrReporter; + public static Reporter NullReporter { get; } = new(console: null); + public static Reporter ConsoleOutReporter => s_consoleOutReporter; + public static Reporter ConsoleErrReporter => s_consoleErrReporter; - public static IReporter Output { get; private set; } = NullReporter; - public static IReporter Error { get; private set; } = NullReporter; - public static IReporter Verbose { get; private set; } = NullReporter; + public static IReporter Output { get; private set; } = NullReporter; + public static IReporter Error { get; private set; } = NullReporter; + public static IReporter Verbose { get; private set; } = NullReporter; - /// - /// Resets the reporters to write to the current reporters based on settings. - /// - public static void Reset() + /// + /// Resets the reporters to write to the current reporters based on settings. + /// + public static void Reset() + { + UseSpinLock(() => { - UseSpinLock(() => - { - ResetOutput(); - ResetError(); - ResetVerbose(); - }); - } + ResetOutput(); + ResetError(); + ResetVerbose(); + }); + } - /// - /// Sets the output reporter to . - /// The reporter won't be applied if disabled in . - /// - /// - public static void SetOutput(IReporter reporter) + /// + /// Sets the output reporter to . + /// The reporter won't be applied if disabled in . + /// + /// + public static void SetOutput(IReporter reporter) + { + UseSpinLock(() => { - UseSpinLock(() => - { - s_outputReporter = reporter; - ResetOutput(); - }); - } + s_outputReporter = reporter; + ResetOutput(); + }); + } - /// - /// Sets the error reporter to . - /// The reporter won't be applied if disabled in . - /// - public static void SetError(IReporter reporter) + /// + /// Sets the error reporter to . + /// The reporter won't be applied if disabled in . + /// + public static void SetError(IReporter reporter) + { + UseSpinLock(() => { - UseSpinLock(() => - { - s_errorReporter = reporter; - ResetError(); - }); - } + s_errorReporter = reporter; + ResetError(); + }); + } - /// - /// Sets the verbose reporter to . - /// The reporter won't be applied if disabled in . - /// - public static void SetVerbose(IReporter reporter) + /// + /// Sets the verbose reporter to . + /// The reporter won't be applied if disabled in . + /// + public static void SetVerbose(IReporter reporter) + { + UseSpinLock(() => { - UseSpinLock(() => - { - s_verboseReporter = reporter; - ResetVerbose(); - }); - } + s_verboseReporter = reporter; + ResetVerbose(); + }); + } - private static void ResetOutput() - { - Output = CommandLoggingContext.OutputEnabled ? s_outputReporter : NullReporter; - } + private static void ResetOutput() + { + Output = CommandLoggingContext.OutputEnabled ? s_outputReporter : NullReporter; + } - private static void ResetError() - { - Error = CommandLoggingContext.ErrorEnabled ? s_errorReporter : NullReporter; - } + private static void ResetError() + { + Error = CommandLoggingContext.ErrorEnabled ? s_errorReporter : NullReporter; + } - private static void ResetVerbose() - { - Verbose = CommandLoggingContext.IsVerbose ? s_verboseReporter : NullReporter; - } + private static void ResetVerbose() + { + Verbose = CommandLoggingContext.IsVerbose ? s_verboseReporter : NullReporter; + } - public void WriteLine(string message) + public void WriteLine(string message) + { + UseSpinLock(() => { - UseSpinLock(() => + if (CommandLoggingContext.ShouldPassAnsiCodesThrough) { - if (CommandLoggingContext.ShouldPassAnsiCodesThrough) - { - _console?.Writer?.WriteLine(message); - } - else - { - _console?.WriteLine(message); - } - }); - } + _console?.Writer?.WriteLine(message); + } + else + { + _console?.WriteLine(message); + } + }); + } - public void WriteLine() - { - UseSpinLock(() => _console?.Writer?.WriteLine()); - } + public void WriteLine() + { + UseSpinLock(() => _console?.Writer?.WriteLine()); + } - public void Write(string message) + public void Write(string message) + { + UseSpinLock(() => { - UseSpinLock(() => + if (CommandLoggingContext.ShouldPassAnsiCodesThrough) { - if (CommandLoggingContext.ShouldPassAnsiCodesThrough) - { - _console?.Writer?.Write(message); - } - else - { - _console?.Write(message); - } - }); - } + _console?.Writer?.Write(message); + } + else + { + _console?.Write(message); + } + }); + } - public void WriteLine(string format, params object?[] args) => WriteLine(string.Format(format, args)); + public void WriteLine(string format, params object?[] args) => WriteLine(string.Format(format, args)); - public static void UseSpinLock(Action action) + public static void UseSpinLock(Action action) + { + bool lockTaken = false; + try { - bool lockTaken = false; - try - { - s_spinlock.Enter(ref lockTaken); - action(); - } - finally + s_spinlock.Enter(ref lockTaken); + action(); + } + finally + { + if (lockTaken) { - if (lockTaken) - { - s_spinlock.Exit(false); - } + s_spinlock.Exit(false); } } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/RuntimeEnvironment.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/RuntimeEnvironment.cs index afd6154ca2b1..c221a7da3687 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/RuntimeEnvironment.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/RuntimeEnvironment.cs @@ -1,251 +1,250 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +internal enum Platform +{ + Unknown = 0, + Windows = 1, + Linux = 2, + Darwin = 3, + FreeBSD = 4, + illumos = 5, + Solaris = 6, + Haiku = 7 +} + +internal static class RuntimeEnvironment { - internal enum Platform + private static readonly Lazy _platform = new(DetermineOSPlatform); + private static readonly Lazy _distroInfo = new(LoadDistroInfo); + + public static Platform OperatingSystemPlatform { get; } = GetOSPlatform(); + public static string OperatingSystemVersion { get; } = GetOSVersion(); + public static string OperatingSystem { get; } = GetOSName(); + + private class DistroInfo { - Unknown = 0, - Windows = 1, - Linux = 2, - Darwin = 3, - FreeBSD = 4, - illumos = 5, - Solaris = 6, - Haiku = 7 + public string? Id; + public string? VersionId; } - internal static class RuntimeEnvironment + private static string GetOSName() { - private static readonly Lazy _platform = new(DetermineOSPlatform); - private static readonly Lazy _distroInfo = new(LoadDistroInfo); - - public static Platform OperatingSystemPlatform { get; } = GetOSPlatform(); - public static string OperatingSystemVersion { get; } = GetOSVersion(); - public static string OperatingSystem { get; } = GetOSName(); - - private class DistroInfo + switch (GetOSPlatform()) { - public string? Id; - public string? VersionId; + case Platform.Windows: + return nameof(Platform.Windows); + case Platform.Linux: + return GetDistroId() ?? nameof(Platform.Linux); + case Platform.Darwin: + return "Mac OS X"; + case Platform.FreeBSD: + return nameof(Platform.FreeBSD); + case Platform.illumos: + return GetDistroId() ?? nameof(Platform.illumos); + case Platform.Solaris: + return nameof(Platform.Solaris); + case Platform.Haiku: + return nameof(Platform.Haiku); + default: + return nameof(Platform.Unknown); } + } - private static string GetOSName() + private static string GetOSVersion() + { + switch (GetOSPlatform()) { - switch (GetOSPlatform()) - { - case Platform.Windows: - return nameof(Platform.Windows); - case Platform.Linux: - return GetDistroId() ?? nameof(Platform.Linux); - case Platform.Darwin: - return "Mac OS X"; - case Platform.FreeBSD: - return nameof(Platform.FreeBSD); - case Platform.illumos: - return GetDistroId() ?? nameof(Platform.illumos); - case Platform.Solaris: - return nameof(Platform.Solaris); - case Platform.Haiku: - return nameof(Platform.Haiku); - default: - return nameof(Platform.Unknown); - } + case Platform.Windows: + return Environment.OSVersion.Version.ToString(3); + case Platform.Linux: + case Platform.illumos: + return GetDistroVersionId() ?? string.Empty; + case Platform.Darwin: + return Environment.OSVersion.Version.ToString(2); + case Platform.Solaris: + // RuntimeInformation.OSDescription example on Solaris 11.3: SunOS 5.11 11.3 + // we only need the major version; 11 + return RuntimeInformation.OSDescription.Split(' ')[2].Split('.')[0]; + case Platform.FreeBSD: + case Platform.Haiku: + // only the major version + return Environment.OSVersion.Version.ToString(1); + default: + return string.Empty; } + } - private static string GetOSVersion() - { - switch (GetOSPlatform()) - { - case Platform.Windows: - return Environment.OSVersion.Version.ToString(3); - case Platform.Linux: - case Platform.illumos: - return GetDistroVersionId() ?? string.Empty; - case Platform.Darwin: - return Environment.OSVersion.Version.ToString(2); - case Platform.Solaris: - // RuntimeInformation.OSDescription example on Solaris 11.3: SunOS 5.11 11.3 - // we only need the major version; 11 - return RuntimeInformation.OSDescription.Split(' ')[2].Split('.')[0]; - case Platform.FreeBSD: - case Platform.Haiku: - // only the major version - return Environment.OSVersion.Version.ToString(1); - default: - return string.Empty; - } - } + private static Platform GetOSPlatform() + { + return _platform.Value; + } - private static Platform GetOSPlatform() - { - return _platform.Value; - } + private static string? GetDistroId() + { + return _distroInfo.Value?.Id; + } - private static string? GetDistroId() - { - return _distroInfo.Value?.Id; - } + private static string? GetDistroVersionId() + { + return _distroInfo.Value?.VersionId; + } - private static string? GetDistroVersionId() + private static DistroInfo? LoadDistroInfo() + { + switch (GetOSPlatform()) { - return _distroInfo.Value?.VersionId; + case Platform.Linux: + return LoadDistroInfoFromLinux(); + case Platform.illumos: + return LoadDistroInfoFromIllumos(); } - private static DistroInfo? LoadDistroInfo() + return null; + } + + private static DistroInfo? LoadDistroInfoFromLinux() + { + DistroInfo? result = null; + + // Sample os-release file: + // NAME="Ubuntu" + // VERSION = "14.04.3 LTS, Trusty Tahr" + // ID = ubuntu + // ID_LIKE = debian + // PRETTY_NAME = "Ubuntu 14.04.3 LTS" + // VERSION_ID = "14.04" + // HOME_URL = "http://www.ubuntu.com/" + // SUPPORT_URL = "http://help.ubuntu.com/" + // BUG_REPORT_URL = "http://bugs.launchpad.net/ubuntu/" + // We use ID and VERSION_ID + + if (File.Exists("/etc/os-release")) { - switch (GetOSPlatform()) + var lines = File.ReadAllLines("/etc/os-release"); + result = new DistroInfo(); + foreach (var line in lines) { - case Platform.Linux: - return LoadDistroInfoFromLinux(); - case Platform.illumos: - return LoadDistroInfoFromIllumos(); + if (line.StartsWith("ID=", StringComparison.Ordinal)) + { + result.Id = line.Substring(3).Trim('"', '\''); + } + else if (line.StartsWith("VERSION_ID=", StringComparison.Ordinal)) + { + result.VersionId = line.Substring(11).Trim('"', '\''); + } } + } - return null; + if (result != null) + { + result = NormalizeDistroInfo(result); } - private static DistroInfo? LoadDistroInfoFromLinux() + return result; + } + + private static DistroInfo? LoadDistroInfoFromIllumos() + { + DistroInfo? result = null; + // examples: + // on OmniOS + // SunOS 5.11 omnios-r151018-95eaa7e + // on OpenIndiana Hipster: + // SunOS 5.11 illumos-63878f749f + // on SmartOS: + // SunOS 5.11 joyent_20200408T231825Z + var versionDescription = RuntimeInformation.OSDescription.Split(' ')[2]; + switch (versionDescription) { - DistroInfo? result = null; - - // Sample os-release file: - // NAME="Ubuntu" - // VERSION = "14.04.3 LTS, Trusty Tahr" - // ID = ubuntu - // ID_LIKE = debian - // PRETTY_NAME = "Ubuntu 14.04.3 LTS" - // VERSION_ID = "14.04" - // HOME_URL = "http://www.ubuntu.com/" - // SUPPORT_URL = "http://help.ubuntu.com/" - // BUG_REPORT_URL = "http://bugs.launchpad.net/ubuntu/" - // We use ID and VERSION_ID - - if (File.Exists("/etc/os-release")) - { - var lines = File.ReadAllLines("/etc/os-release"); - result = new DistroInfo(); - foreach (var line in lines) + case string version when version.StartsWith("omnios"): + result = new DistroInfo { - if (line.StartsWith("ID=", StringComparison.Ordinal)) - { - result.Id = line.Substring(3).Trim('"', '\''); - } - else if (line.StartsWith("VERSION_ID=", StringComparison.Ordinal)) - { - result.VersionId = line.Substring(11).Trim('"', '\''); - } - } - } + Id = "OmniOS", + VersionId = version.Substring("omnios-r".Length, 2) // e.g. 15 + }; + break; + case string version when version.StartsWith("joyent"): + result = new DistroInfo + { + Id = "SmartOS", + VersionId = version.Substring("joyent_".Length, 4) // e.g. 2020 + }; + break; + case string version when version.StartsWith("illumos"): + result = new DistroInfo + { + Id = "OpenIndiana" + // version-less + }; + break; + } - if (result != null) - { - result = NormalizeDistroInfo(result); - } + return result; + } - return result; - } + // For some distros, we don't want to use the full version from VERSION_ID. One example is + // Red Hat Enterprise Linux, which includes a minor version in their VERSION_ID but minor + // versions are backwards compatable. + // + // In this case, we'll normalized RIDs like 'rhel.7.2' and 'rhel.7.3' to a generic + // 'rhel.7'. This brings RHEL in line with other distros like CentOS or Debian which + // don't put minor version numbers in their VERSION_ID fields because all minor versions + // are backwards compatible. + private static DistroInfo NormalizeDistroInfo(DistroInfo distroInfo) + { + // Handle if VersionId is null by just setting the index to -1. + int lastVersionNumberSeparatorIndex = distroInfo.VersionId?.IndexOf('.') ?? -1; - private static DistroInfo? LoadDistroInfoFromIllumos() + if (lastVersionNumberSeparatorIndex != -1 && distroInfo.Id == "alpine") { - DistroInfo? result = null; - // examples: - // on OmniOS - // SunOS 5.11 omnios-r151018-95eaa7e - // on OpenIndiana Hipster: - // SunOS 5.11 illumos-63878f749f - // on SmartOS: - // SunOS 5.11 joyent_20200408T231825Z - var versionDescription = RuntimeInformation.OSDescription.Split(' ')[2]; - switch (versionDescription) - { - case string version when version.StartsWith("omnios"): - result = new DistroInfo - { - Id = "OmniOS", - VersionId = version.Substring("omnios-r".Length, 2) // e.g. 15 - }; - break; - case string version when version.StartsWith("joyent"): - result = new DistroInfo - { - Id = "SmartOS", - VersionId = version.Substring("joyent_".Length, 4) // e.g. 2020 - }; - break; - case string version when version.StartsWith("illumos"): - result = new DistroInfo - { - Id = "OpenIndiana" - // version-less - }; - break; - } - - return result; + // For Alpine, the version reported has three components, so we need to find the second version separator + lastVersionNumberSeparatorIndex = distroInfo.VersionId?.IndexOf('.', lastVersionNumberSeparatorIndex + 1) ?? -1; } - // For some distros, we don't want to use the full version from VERSION_ID. One example is - // Red Hat Enterprise Linux, which includes a minor version in their VERSION_ID but minor - // versions are backwards compatable. - // - // In this case, we'll normalized RIDs like 'rhel.7.2' and 'rhel.7.3' to a generic - // 'rhel.7'. This brings RHEL in line with other distros like CentOS or Debian which - // don't put minor version numbers in their VERSION_ID fields because all minor versions - // are backwards compatible. - private static DistroInfo NormalizeDistroInfo(DistroInfo distroInfo) + if (lastVersionNumberSeparatorIndex != -1 && (distroInfo.Id == "rhel" || distroInfo.Id == "alpine")) { - // Handle if VersionId is null by just setting the index to -1. - int lastVersionNumberSeparatorIndex = distroInfo.VersionId?.IndexOf('.') ?? -1; - - if (lastVersionNumberSeparatorIndex != -1 && distroInfo.Id == "alpine") - { - // For Alpine, the version reported has three components, so we need to find the second version separator - lastVersionNumberSeparatorIndex = distroInfo.VersionId?.IndexOf('.', lastVersionNumberSeparatorIndex + 1) ?? -1; - } + distroInfo.VersionId = distroInfo.VersionId?.Substring(0, lastVersionNumberSeparatorIndex); + } - if (lastVersionNumberSeparatorIndex != -1 && (distroInfo.Id == "rhel" || distroInfo.Id == "alpine")) - { - distroInfo.VersionId = distroInfo.VersionId?.Substring(0, lastVersionNumberSeparatorIndex); - } + return distroInfo; + } - return distroInfo; + private static Platform DetermineOSPlatform() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Platform.Windows; } - - private static Platform DetermineOSPlatform() + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return Platform.Windows; - } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return Platform.Linux; - } - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return Platform.Darwin; - } + return Platform.Linux; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return Platform.Darwin; + } #if NET - if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) - { - return Platform.FreeBSD; - } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("ILLUMOS"))) - { - return Platform.illumos; - } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS"))) - { - return Platform.Solaris; - } - if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("HAIKU"))) - { - return Platform.Haiku; - } + if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + { + return Platform.FreeBSD; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("ILLUMOS"))) + { + return Platform.illumos; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("SOLARIS"))) + { + return Platform.Solaris; + } + if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("HAIKU"))) + { + return Platform.Haiku; + } #endif - return Platform.Unknown; - } + return Platform.Unknown; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Sha256Hasher.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Sha256Hasher.cs index 55d5a671bc14..33fe02237329 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Sha256Hasher.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Sha256Hasher.cs @@ -3,32 +3,30 @@ #if NET -using System; using System.Security.Cryptography; -using System.Text; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public static class Sha256Hasher { - public static class Sha256Hasher + /// + /// The hashed mac address needs to be the same hashed value as produced by the other distinct sources given the same input. (e.g. VsCode) + /// + public static string Hash(string text) { - /// - /// The hashed mac address needs to be the same hashed value as produced by the other distinct sources given the same input. (e.g. VsCode) - /// - public static string Hash(string text) - { - byte[] bytes = Encoding.UTF8.GetBytes(text); - byte[] hash = SHA256.HashData(bytes); + byte[] bytes = Encoding.UTF8.GetBytes(text); + byte[] hash = SHA256.HashData(bytes); #if NET9_0_OR_GREATER - return Convert.ToHexStringLower(hash); + return Convert.ToHexStringLower(hash); #else - return Convert.ToHexString(hash).ToLowerInvariant(); + return Convert.ToHexString(hash).ToLowerInvariant(); #endif - } + } - public static string HashWithNormalizedCasing(string text) - { - return Hash(text.ToUpperInvariant()); - } + public static string HashWithNormalizedCasing(string text) + { + return Hash(text.ToUpperInvariant()); } } + #endif diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs index 0e137f111207..07cd6d41e1e6 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/StreamForwarder.cs @@ -1,132 +1,131 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public sealed class StreamForwarder { - public sealed class StreamForwarder - { - private static readonly char[] s_ignoreCharacters = new char[] { '\r' }; - private static readonly char s_flushBuilderCharacter = '\n'; + private static readonly char[] s_ignoreCharacters = new char[] { '\r' }; + private static readonly char s_flushBuilderCharacter = '\n'; - private StringBuilder? _builder; - private StringWriter? _capture; - private Action? _writeLine; - private bool _trimTrailingCapturedNewline; + private StringBuilder? _builder; + private StringWriter? _capture; + private Action? _writeLine; + private bool _trimTrailingCapturedNewline; - public string? CapturedOutput + public string? CapturedOutput + { + get { - get + var capture = _capture?.GetStringBuilder()?.ToString(); + if (_trimTrailingCapturedNewline) { - var capture = _capture?.GetStringBuilder()?.ToString(); - if (_trimTrailingCapturedNewline) - { - capture = capture?.TrimEnd('\r', '\n'); - } - return capture; + capture = capture?.TrimEnd('\r', '\n'); } + return capture; } + } - public StreamForwarder Capture(bool trimTrailingNewline = false) - { - ThrowIfCaptureSet(); + public StreamForwarder Capture(bool trimTrailingNewline = false) + { + ThrowIfCaptureSet(); - _capture = new StringWriter(); - _trimTrailingCapturedNewline = trimTrailingNewline; + _capture = new StringWriter(); + _trimTrailingCapturedNewline = trimTrailingNewline; - return this; - } + return this; + } - public StreamForwarder ForwardTo(Action writeLine) - { - ThrowIfNull(writeLine); + public StreamForwarder ForwardTo(Action writeLine) + { + ThrowIfNull(writeLine); - ThrowIfForwarderSet(); + ThrowIfForwarderSet(); - _writeLine = writeLine; + _writeLine = writeLine; - return this; - } + return this; + } - public Task BeginRead(TextReader reader) - { - return Task.Run(() => Read(reader)); - } + public Task BeginRead(TextReader reader) + { + return Task.Run(() => Read(reader)); + } - public void Read(TextReader reader) - { - var bufferSize = 1; + public void Read(TextReader reader) + { + var bufferSize = 1; - int readCharacterCount; - char currentCharacter; + int readCharacterCount; + char currentCharacter; - var buffer = new char[bufferSize]; - _builder = new StringBuilder(); + var buffer = new char[bufferSize]; + _builder = new StringBuilder(); - // Using Read with buffer size 1 to prevent looping endlessly - // like we would when using Read() with no buffer - while ((readCharacterCount = reader.Read(buffer, 0, bufferSize)) > 0) - { - currentCharacter = buffer[0]; - - if (currentCharacter == s_flushBuilderCharacter) - { - WriteBuilder(); - } - else if (!s_ignoreCharacters.Contains(currentCharacter)) - { - _builder.Append(currentCharacter); - } - } + // Using Read with buffer size 1 to prevent looping endlessly + // like we would when using Read() with no buffer + while ((readCharacterCount = reader.Read(buffer, 0, bufferSize)) > 0) + { + currentCharacter = buffer[0]; - // Flush anything else when the stream is closed - // Which should only happen if someone used console.Write - if (_builder.Length > 0) + if (currentCharacter == s_flushBuilderCharacter) { WriteBuilder(); } + else if (!s_ignoreCharacters.Contains(currentCharacter)) + { + _builder.Append(currentCharacter); + } } - private void WriteBuilder() + // Flush anything else when the stream is closed + // Which should only happen if someone used console.Write + if (_builder.Length > 0) { - WriteLine(_builder?.ToString() ?? string.Empty); - _builder?.Clear(); + WriteBuilder(); } + } + + private void WriteBuilder() + { + WriteLine(_builder?.ToString() ?? string.Empty); + _builder?.Clear(); + } - private void WriteLine(string str) + private void WriteLine(string str) + { + if (_capture != null) { - if (_capture != null) - { - _capture.WriteLine(str); - } + _capture.WriteLine(str); + } - if (_writeLine != null) - { - _writeLine(str); - } + if (_writeLine != null) + { + _writeLine(str); } + } - private void ThrowIfNull(object obj) + private void ThrowIfNull(object obj) + { + if (obj == null) { - if (obj == null) - { - throw new ArgumentNullException(nameof(obj)); - } + throw new ArgumentNullException(nameof(obj)); } + } - private void ThrowIfForwarderSet() + private void ThrowIfForwarderSet() + { + if (_writeLine != null) { - if (_writeLine != null) - { - throw new InvalidOperationException(LocalizableStrings.WriteLineForwarderSetPreviously); - } + throw new InvalidOperationException(LocalizableStrings.WriteLineForwarderSetPreviously); } + } - private void ThrowIfCaptureSet() + private void ThrowIfCaptureSet() + { + if (_capture != null) { - if (_capture != null) - { - throw new InvalidOperationException(LocalizableStrings.AlreadyCapturingStream); - } + throw new InvalidOperationException(LocalizableStrings.AlreadyCapturingStream); } } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs index 0bf9f11a4845..337c8c0e5bb0 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs @@ -3,119 +3,118 @@ using System.Diagnostics; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public static class TelemetryEventEntry { - public static class TelemetryEventEntry + public static event EventHandler? EntryPosted; + public static ITelemetryFilter TelemetryFilter { get; set; } = new BlockFilter(); + + public static void TrackEvent( + string? eventName = null, + IDictionary? properties = null, + IDictionary? measurements = null) { - public static event EventHandler? EntryPosted; - public static ITelemetryFilter TelemetryFilter { get; set; } = new BlockFilter(); + EntryPosted?.Invoke(typeof(TelemetryEventEntry), + new InstrumentationEventArgs(eventName, properties, measurements)); + } - public static void TrackEvent( - string? eventName = null, - IDictionary? properties = null, - IDictionary? measurements = null) + public static void SendFiltered(object? o = null) + { + if (o == null) { - EntryPosted?.Invoke(typeof(TelemetryEventEntry), - new InstrumentationEventArgs(eventName, properties, measurements)); + return; } - public static void SendFiltered(object? o = null) + foreach (ApplicationInsightsEntryFormat entry in TelemetryFilter.Filter(o)) { - if (o == null) - { - return; - } - - foreach (ApplicationInsightsEntryFormat entry in TelemetryFilter.Filter(o)) - { - TrackEvent(entry.EventName, entry.Properties, entry.Measurements); - } + TrackEvent(entry.EventName, entry.Properties, entry.Measurements); } + } - public static void Subscribe(Action?, IDictionary?> subscriber) + public static void Subscribe(Action?, IDictionary?> subscriber) + { + void Handler(object? sender, InstrumentationEventArgs eventArgs) { - void Handler(object? sender, InstrumentationEventArgs eventArgs) - { - subscriber(eventArgs.EventName, eventArgs.Properties, eventArgs.Measurements); - } - - EntryPosted += Handler; + subscriber(eventArgs.EventName, eventArgs.Properties, eventArgs.Measurements); } + + EntryPosted += Handler; } +} - public sealed class PerformanceMeasurement : IDisposable - { - private readonly Stopwatch? _timer; - private readonly Dictionary? _data; - private readonly string? _name; +public sealed class PerformanceMeasurement : IDisposable +{ + private readonly Stopwatch? _timer; + private readonly Dictionary? _data; + private readonly string? _name; - public PerformanceMeasurement(Dictionary? data, string name) + public PerformanceMeasurement(Dictionary? data, string name) + { + // Measurement is a no-op if we don't have a dictionary to store the entry. + if (data == null) { - // Measurement is a no-op if we don't have a dictionary to store the entry. - if (data == null) - { - return; - } - - _data = data; - _name = name; - _timer = Stopwatch.StartNew(); + return; } - public void Dispose() - { - if (_name is not null && _timer is not null) - { - _data?.Add(_name, _timer.Elapsed.TotalMilliseconds); - } - } + _data = data; + _name = name; + _timer = Stopwatch.StartNew(); } - public class BlockFilter : ITelemetryFilter + public void Dispose() { - public IEnumerable Filter(object o) + if (_name is not null && _timer is not null) { - return new List(); + _data?.Add(_name, _timer.Elapsed.TotalMilliseconds); } } +} - public class InstrumentationEventArgs : EventArgs +public class BlockFilter : ITelemetryFilter +{ + public IEnumerable Filter(object o) { - internal InstrumentationEventArgs( - string? eventName, - IDictionary? properties, - IDictionary? measurements) - { - EventName = eventName; - Properties = properties; - Measurements = measurements; - } + return new List(); + } +} - public string? EventName { get; } - public IDictionary? Properties { get; } - public IDictionary? Measurements { get; } +public class InstrumentationEventArgs : EventArgs +{ + internal InstrumentationEventArgs( + string? eventName, + IDictionary? properties, + IDictionary? measurements) + { + EventName = eventName; + Properties = properties; + Measurements = measurements; } - public class ApplicationInsightsEntryFormat + public string? EventName { get; } + public IDictionary? Properties { get; } + public IDictionary? Measurements { get; } +} + +public class ApplicationInsightsEntryFormat +{ + public ApplicationInsightsEntryFormat( + string? eventName = null, + IDictionary? properties = null, + IDictionary? measurements = null) { - public ApplicationInsightsEntryFormat( - string? eventName = null, - IDictionary? properties = null, - IDictionary? measurements = null) - { - EventName = eventName; - Properties = properties; - Measurements = measurements; - } + EventName = eventName; + Properties = properties; + Measurements = measurements; + } - public string? EventName { get; } - public IDictionary? Properties { get; } - public IDictionary? Measurements { get; } + public string? EventName { get; } + public IDictionary? Properties { get; } + public IDictionary? Measurements { get; } - public ApplicationInsightsEntryFormat WithAppliedToPropertiesValue(Func func) - { - var appliedProperties = Properties?.ToDictionary(p => p.Key, p => (string?)func(p.Value)); - return new ApplicationInsightsEntryFormat(EventName, appliedProperties, Measurements); - } + public ApplicationInsightsEntryFormat WithAppliedToPropertiesValue(Func func) + { + var appliedProperties = Properties?.ToDictionary(p => p.Key, p => (string?)func(p.Value)); + return new ApplicationInsightsEntryFormat(EventName, appliedProperties, Measurements); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/ToolCommandName.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/ToolCommandName.cs index aa2d04a81e4c..2f05ba6f4a5b 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/ToolCommandName.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/ToolCommandName.cs @@ -1,63 +1,62 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public readonly struct ToolCommandName : IEquatable { - public struct ToolCommandName : IEquatable + public ToolCommandName(string name) { - public ToolCommandName(string name) + if (name == null) { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - if (name == string.Empty) - { - throw new ArgumentException(message: "cannot be empty", paramName: nameof(name)); - } + throw new ArgumentNullException(nameof(name)); + } - Value = name; + if (name == string.Empty) + { + throw new ArgumentException(message: "cannot be empty", paramName: nameof(name)); } - public string Value { get; } + Value = name; + } - public override string ToString() => Value; + public string Value { get; } - public bool Equals(ToolCommandName other) - { - return string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); - } + public override string ToString() => Value; - public override bool Equals(object? obj) - { - return obj is ToolCommandName name && Equals(name); - } + public bool Equals(ToolCommandName other) + { + return string.Equals(Value, other.Value, StringComparison.OrdinalIgnoreCase); + } - public override int GetHashCode() - { - return StringComparer.OrdinalIgnoreCase.GetHashCode(Value); - } + public override bool Equals(object? obj) + { + return obj is ToolCommandName name && Equals(name); + } - public static bool operator ==(ToolCommandName name1, ToolCommandName name2) - { - return name1.Equals(name2); - } + public override int GetHashCode() + { + return StringComparer.OrdinalIgnoreCase.GetHashCode(Value); + } - public static bool operator !=(ToolCommandName name1, ToolCommandName name2) - { - return !name1.Equals(name2); - } + public static bool operator ==(ToolCommandName name1, ToolCommandName name2) + { + return name1.Equals(name2); + } - public static ToolCommandName[] Convert(string[] toolCommandNameStringArray) - { - var toolCommandNames = new ToolCommandName[toolCommandNameStringArray.Length]; - for (int i = 0; i < toolCommandNameStringArray.Length; i++) - { - toolCommandNames[i] = new ToolCommandName(toolCommandNameStringArray[i]); - } + public static bool operator !=(ToolCommandName name1, ToolCommandName name2) + { + return !name1.Equals(name2); + } - return toolCommandNames; + public static ToolCommandName[] Convert(string[] toolCommandNameStringArray) + { + var toolCommandNames = new ToolCommandName[toolCommandNameStringArray.Length]; + for (int i = 0; i < toolCommandNameStringArray.Length; i++) + { + toolCommandNames[i] = new ToolCommandName(toolCommandNameStringArray[i]); } + + return toolCommandNames; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/TypoCorrection.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/TypoCorrection.cs index d5964d48235d..159caca6db49 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/TypoCorrection.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/TypoCorrection.cs @@ -1,192 +1,185 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public static class TypoCorrection { - public static class TypoCorrection + /// + /// Gets the list of tokens similar to + /// based on priority search: + /// 1. Starts with + /// 2. Contains - the call is restricted with length check, minLength:3 and + /// 3. Levenshtein algorithm with restriction + /// max number of suggestion is restricted to 10 entries + /// + /// List of tokens to select from. + /// The token that is being compared. + /// the difference between two strings, default: 3. + /// The enumerator to tokens similar to . + public static IEnumerable GetSimilarTokens(IEnumerable possibleTokens, string currentToken, int maxLevenshteinDistance = 3) { - /// - /// Gets the list of tokens similar to - /// based on priority search: - /// 1. Starts with - /// 2. Contains - the call is restricted with length check, minLength:3 and - /// 3. Levenshtein algorithm with restriction - /// max number of suggestion is restricted to 10 entries - /// - /// List of tokens to select from. - /// The token that is being compared. - /// the difference between two strings, default: 3. - /// The enumerator to tokens similar to . - public static IEnumerable GetSimilarTokens(IEnumerable possibleTokens, string currentToken, int maxLevenshteinDistance = 3) + var minCurrentTokenLength = 3; + var maxNumberOfSuggestions = 10; + + var numberOfSuggestions = 0; + var currentTokenLength = currentToken.Length; + var possibleSuggestions = possibleTokens.Select((string possibleMatch) => new Suggestion(possibleMatch, possibleMatch.Length)).ToArray(); + + var matchByStartsWith = possibleSuggestions + .Where(s => s.PossibleMatch.StartsWith(currentToken)) + .Select(SetSelection) + .Take(maxNumberOfSuggestions) + .OrderBy(s => s.Distance) + .ToList(); + + numberOfSuggestions += matchByStartsWith.Count; + if (numberOfSuggestions >= maxNumberOfSuggestions) { - var minCurrentTokenLength = 3; - var maxNumberOfSuggestions = 10; - - var numberOfSuggestions = 0; - var currentTokenLength = currentToken.Length; - var possibleSuggestions = possibleTokens.Select((string possibleMatch) => new Suggestion(possibleMatch, possibleMatch.Length)).ToArray(); + return matchByStartsWith.Select(s => s.PossibleMatch); + } - var matchByStartsWith = possibleSuggestions - .Where(s => s.PossibleMatch.StartsWith(currentToken)) - .Select(SetSelection) - .Take(maxNumberOfSuggestions) - .OrderBy(s => s.Distance) - .ToList(); + var matchByContains = new List(); + if (currentToken.Length >= minCurrentTokenLength) + { + matchByContains = possibleSuggestions + .Where(s => + !s.IsSelected + && s.PossibleMatch.Contains(currentToken) + && s.Distance - currentTokenLength <= maxLevenshteinDistance) + .OrderBy(s => s.Distance) + .Take(maxNumberOfSuggestions - numberOfSuggestions) + .Select(SetSelection) + .ToList(); - numberOfSuggestions += matchByStartsWith.Count; + numberOfSuggestions += matchByContains.Count; if (numberOfSuggestions >= maxNumberOfSuggestions) { - return matchByStartsWith.Select(s => s.PossibleMatch); + return matchByStartsWith + .Concat(matchByContains) + .Select(s => s.PossibleMatch); } + } - var matchByContains = new List(); - if (currentToken.Length >= minCurrentTokenLength) - { - matchByContains = possibleSuggestions - .Where(s => - !s.IsSelected - && s.PossibleMatch.Contains(currentToken) - && s.Distance - currentTokenLength <= maxLevenshteinDistance) - .OrderBy(s => s.Distance) - .Take(maxNumberOfSuggestions - numberOfSuggestions) - .Select(SetSelection) - .ToList(); - - numberOfSuggestions += matchByContains.Count; - if (numberOfSuggestions >= maxNumberOfSuggestions) - { - return matchByStartsWith - .Concat(matchByContains) - .Select(s => s.PossibleMatch); - } - } + var matchByLevenshteinDistance = possibleSuggestions + .Where(s => !s.IsSelected) + .Select(s => new Suggestion(s.PossibleMatch, GetDistance(s.PossibleMatch, currentToken))) + .Where(s => s.Distance <= maxLevenshteinDistance) + .OrderBy(s => s.Distance) + .ThenByDescending(s => GetStartsWithDistance(currentToken, s.PossibleMatch)) + .FilterByShortestDistance() + .Take(maxNumberOfSuggestions - numberOfSuggestions); + + return matchByStartsWith + .Concat(matchByContains + .Concat(matchByLevenshteinDistance)) + .Select(s => s.PossibleMatch); + } - var matchByLevenshteinDistance = possibleSuggestions - .Where(s => !s.IsSelected) - .Select(s => new Suggestion(s.PossibleMatch, GetDistance(s.PossibleMatch, currentToken))) - .Where(s => s.Distance <= maxLevenshteinDistance) - .OrderBy(s => s.Distance) - .ThenByDescending(s => GetStartsWithDistance(currentToken, s.PossibleMatch)) - .FilterByShortestDistance() - .Take(maxNumberOfSuggestions - numberOfSuggestions); - - return matchByStartsWith - .Concat(matchByContains - .Concat(matchByLevenshteinDistance)) - .Select(s => s.PossibleMatch); - } + // The method takes the matches with the shortest distance + // e.g. (razor, 2), (pazor, 2), (pazors, 3) => (razor, 2), (pazor, 2) + private static IEnumerable FilterByShortestDistance(this IEnumerable possibleMatches) + { + int? bestDistance = null; - // The method takes the matches with the shortest distance - // e.g. (razor, 2), (pazor, 2), (pazors, 3) => (razor, 2), (pazor, 2) - private static IEnumerable FilterByShortestDistance(this IEnumerable possibleMatches) + return possibleMatches.TakeWhile(s => { - int? bestDistance = null; + int distance = s.Distance; + bestDistance ??= distance; + return distance == bestDistance; + }); + } - return possibleMatches.TakeWhile(s => - { - int distance = s.Distance; - bestDistance ??= distance; - return distance == bestDistance; - }); - } + // The method finds the distance to the first mismatch between two strings + // e.g. (cat, cap) => 2 + private static int GetStartsWithDistance(string first, string second) + { + int i; + for (i = 0; i < first.Length && i < second.Length && first[i] == second[i]; i++) ; - // The method finds the distance to the first mismatch between two strings - // e.g. (cat, cap) => 2 - private static int GetStartsWithDistance(string first, string second) - { - int i; - for (i = 0; i < first.Length && i < second.Length && first[i] == second[i]; i++) ; + return i; + } - return i; + //Based on https://blogs.msdn.microsoft.com/toub/2006/05/05/generic-levenshtein-edit-distance-with-c/ + private static int GetDistance(string first, string second) + { + // Validate parameters + if (first is null) + { + throw new ArgumentNullException(nameof(first)); } - //Based on https://blogs.msdn.microsoft.com/toub/2006/05/05/generic-levenshtein-edit-distance-with-c/ - private static int GetDistance(string first, string second) + if (second is null) { - // Validate parameters - if (first is null) - { - throw new ArgumentNullException(nameof(first)); - } + throw new ArgumentNullException(nameof(second)); + } - if (second is null) - { - throw new ArgumentNullException(nameof(second)); - } + // Get the length of both. If either is 0, return + // the length of the other, since that number of insertions + // would be required. - // Get the length of both. If either is 0, return - // the length of the other, since that number of insertions - // would be required. + int n = first.Length, m = second.Length; + if (n == 0) return m; + if (m == 0) return n; - int n = first.Length, m = second.Length; - if (n == 0) return m; - if (m == 0) return n; + // Rather than maintain an entire matrix (which would require O(n*m) space), + // just store the current row and the next row, each of which has a length m+1, + // so just O(m) space. Initialize the current row. - // Rather than maintain an entire matrix (which would require O(n*m) space), - // just store the current row and the next row, each of which has a length m+1, - // so just O(m) space. Initialize the current row. + int curRow = 0, nextRow = 1; + int[][] rows = { new int[m + 1], new int[m + 1] }; - int curRow = 0, nextRow = 1; - int[][] rows = { new int[m + 1], new int[m + 1] }; + for (int j = 0; j <= m; ++j) + { + rows[curRow][j] = j; + } - for (int j = 0; j <= m; ++j) + // For each virtual row (since we only have physical storage for two) + for (int i = 1; i <= n; ++i) + { + // Fill in the values in the row + rows[nextRow][0] = i; + for (int j = 1; j <= m; ++j) { - rows[curRow][j] = j; + int dist1 = rows[curRow][j] + 1; + int dist2 = rows[nextRow][j - 1] + 1; + int dist3 = rows[curRow][j - 1] + (first[i - 1].Equals(second[j - 1]) ? 0 : 1); + + rows[nextRow][j] = Math.Min(dist1, Math.Min(dist2, dist3)); } - // For each virtual row (since we only have physical storage for two) - for (int i = 1; i <= n; ++i) + // Swap the current and next rows + if (curRow == 0) { - // Fill in the values in the row - rows[nextRow][0] = i; - for (int j = 1; j <= m; ++j) - { - int dist1 = rows[curRow][j] + 1; - int dist2 = rows[nextRow][j - 1] + 1; - int dist3 = rows[curRow][j - 1] + (first[i - 1].Equals(second[j - 1]) ? 0 : 1); - - rows[nextRow][j] = Math.Min(dist1, Math.Min(dist2, dist3)); - } - - // Swap the current and next rows - if (curRow == 0) - { - curRow = 1; - nextRow = 0; - } - else - { - curRow = 0; - nextRow = 1; - } + curRow = 1; + nextRow = 0; + } + else + { + curRow = 0; + nextRow = 1; } - - // Return the computed edit distance - return rows[curRow][m]; } - private static Suggestion SetSelection(Suggestion s) - { - s.IsSelected = true; + // Return the computed edit distance + return rows[curRow][m]; + } - return s; - } + private static Suggestion SetSelection(Suggestion s) + { + s.IsSelected = true; - // The class describes properties of a possible match token - // and based on these the decision for the token selection is made - internal sealed class Suggestion - { - public Suggestion(string possibleMatch, int distance) - { - PossibleMatch = possibleMatch; - Distance = distance; - } + return s; + } - public bool IsSelected { get; set; } + // The class describes properties of a possible match token + // and based on these the decision for the token selection is made + internal sealed class Suggestion(string possibleMatch, int distance) + { + public bool IsSelected { get; set; } - public string PossibleMatch { get; } + public string PossibleMatch { get; } = possibleMatch; - public int Distance { get; set; } - } + public int Distance { get; set; } = distance; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/UILanguageOverride.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/UILanguageOverride.cs index 21937c2a9b74..d76b1ba6d8e8 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/UILanguageOverride.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/UILanguageOverride.cs @@ -6,142 +6,143 @@ using System.Security; using Microsoft.Win32; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +internal static class UILanguageOverride { - internal static class UILanguageOverride - { - internal const string DOTNET_CLI_UI_LANGUAGE = nameof(DOTNET_CLI_UI_LANGUAGE); - private const string VSLANG = nameof(VSLANG); - private const string PreferredUILang = nameof(PreferredUILang); - private static Encoding DefaultMultilingualEncoding = Encoding.UTF8; // We choose UTF8 as the default encoding as opposed to specific language encodings because it supports emojis & other chars in .NET. + internal const string DOTNET_CLI_UI_LANGUAGE = nameof(DOTNET_CLI_UI_LANGUAGE); + private const string VSLANG = nameof(VSLANG); + private const string PreferredUILang = nameof(PreferredUILang); + // We choose UTF8 as the default encoding as opposed to specific language encodings because it supports emojis & other chars in .NET. + private static readonly Encoding s_defaultMultilingualEncoding = Encoding.UTF8; - public static void Setup() + public static void Setup() + { + CultureInfo? language = GetOverriddenUILanguage(); + if (language != null) { - CultureInfo? language = GetOverriddenUILanguage(); - if (language != null) - { - ApplyOverrideToCurrentProcess(language); - FlowOverrideToChildProcesses(language); - } + ApplyOverrideToCurrentProcess(language); + FlowOverrideToChildProcesses(language); + } - if ( - !CultureInfo.CurrentUICulture.TwoLetterISOLanguageName.Equals("en", StringComparison.InvariantCultureIgnoreCase) && + if ( + !CultureInfo.CurrentUICulture.TwoLetterISOLanguageName.Equals("en", StringComparison.InvariantCultureIgnoreCase) && #if NET - OperatingSystemSupportsUtf8() + OperatingSystemSupportsUtf8() #else - CurrentPlatformIsWindowsAndOfficiallySupportsUTF8Encoding() + CurrentPlatformIsWindowsAndOfficiallySupportsUTF8Encoding() #endif - ) - { - Console.OutputEncoding = DefaultMultilingualEncoding; - Console.InputEncoding = DefaultMultilingualEncoding; // Setting both encodings causes a change in the CHCP, making it so we dont need to P-Invoke ourselves. - // If the InputEncoding is not set, the encoding will work in CMD but not in Powershell, as the raw CHCP page won't be changed. - } + ) + { + // Setting both encodings causes a change in the CHCP, making it so we don't need to P-Invoke ourselves. + Console.OutputEncoding = s_defaultMultilingualEncoding; + Console.InputEncoding = s_defaultMultilingualEncoding; + // If the InputEncoding is not set, the encoding will work in CMD but not in Powershell, as the raw CHCP page won't be changed. } + } #if NET - public static bool OperatingSystemSupportsUtf8() - { - return !OperatingSystem.IsIOS() && - !OperatingSystem.IsAndroid() && - !OperatingSystem.IsTvOS() && - !OperatingSystem.IsBrowser() && - (!OperatingSystem.IsWindows() || OperatingSystem.IsWindowsVersionAtLeast(10, 0, 18363)); - } + public static bool OperatingSystemSupportsUtf8() + { + return !OperatingSystem.IsIOS() && + !OperatingSystem.IsAndroid() && + !OperatingSystem.IsTvOS() && + !OperatingSystem.IsBrowser() && + (!OperatingSystem.IsWindows() || OperatingSystem.IsWindowsVersionAtLeast(10, 0, 18363)); + } #endif - private static void ApplyOverrideToCurrentProcess(CultureInfo language) - { - CultureInfo.DefaultThreadCurrentUICulture = language; - // We don't need to change CurrentUICulture, as it will be changed by DefaultThreadCurrentUICulture on NET Core (but not Framework) apps. - } + private static void ApplyOverrideToCurrentProcess(CultureInfo language) + { + CultureInfo.DefaultThreadCurrentUICulture = language; + // We don't need to change CurrentUICulture, as it will be changed by DefaultThreadCurrentUICulture on NET Core (but not Framework) apps. + } - private static void FlowOverrideToChildProcesses(CultureInfo language) - { - // Do not override any environment variables that are already set as we do not want to clobber a more granular setting with our global setting. - SetIfNotAlreadySet(DOTNET_CLI_UI_LANGUAGE, language.Name); - SetIfNotAlreadySet(VSLANG, language.LCID); // for tools following VS guidelines to just work in CLI - SetIfNotAlreadySet(PreferredUILang, language.Name); // for C#/VB targets that pass $(PreferredUILang) to compiler - } + private static void FlowOverrideToChildProcesses(CultureInfo language) + { + // Do not override any environment variables that are already set as we do not want to clobber a more granular setting with our global setting. + SetIfNotAlreadySet(DOTNET_CLI_UI_LANGUAGE, language.Name); + SetIfNotAlreadySet(VSLANG, language.LCID); // for tools following VS guidelines to just work in CLI + SetIfNotAlreadySet(PreferredUILang, language.Name); // for C#/VB targets that pass $(PreferredUILang) to compiler + } - /// - /// Look first at UI Language Overrides. (DOTNET_CLI_UI_LANGUAGE and VSLANG). Does NOT check System Locale or OS Display Language. - /// - /// The custom language that was set by the user. - /// DOTNET_CLI_UI_LANGUAGE > VSLANG. Returns null if none are set. - public static CultureInfo? GetOverriddenUILanguage() + /// + /// Look first at UI Language Overrides. (DOTNET_CLI_UI_LANGUAGE and VSLANG). Does NOT check System Locale or OS Display Language. + /// + /// The custom language that was set by the user. + /// DOTNET_CLI_UI_LANGUAGE > VSLANG. Returns null if none are set. + public static CultureInfo? GetOverriddenUILanguage() + { + // DOTNET_CLI_UI_LANGUAGE= is the main way for users to customize the CLI's UI language. + string? dotnetCliLanguage = Environment.GetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE); + if (dotnetCliLanguage != null) { - // DOTNET_CLI_UI_LANGUAGE= is the main way for users to customize the CLI's UI language. - string? dotnetCliLanguage = Environment.GetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE); - if (dotnetCliLanguage != null) - { - try - { - return new CultureInfo(dotnetCliLanguage); - } - catch (CultureNotFoundException) { } - } - - // VSLANG= is set by VS and we respect that as well so that we will respect the VS - // language preference if we're invoked by VS. - string? vsLang = Environment.GetEnvironmentVariable(VSLANG); - if (vsLang != null && int.TryParse(vsLang, out int vsLcid)) + try { - try - { - return new CultureInfo(vsLcid); - } - catch (ArgumentOutOfRangeException) { } - catch (CultureNotFoundException) { } + return new CultureInfo(dotnetCliLanguage); } - - return null; + catch (CultureNotFoundException) { } } - private static void SetIfNotAlreadySet(string environmentVariableName, string value) + // VSLANG= is set by VS and we respect that as well so that we will respect the VS + // language preference if we're invoked by VS. + string? vsLang = Environment.GetEnvironmentVariable(VSLANG); + if (vsLang != null && int.TryParse(vsLang, out int vsLcid)) { - string? currentValue = Environment.GetEnvironmentVariable(environmentVariableName); - if (currentValue == null) + try { - Environment.SetEnvironmentVariable(environmentVariableName, value); + return new CultureInfo(vsLcid); } + catch (ArgumentOutOfRangeException) { } + catch (CultureNotFoundException) { } } - private static void SetIfNotAlreadySet(string environmentVariableName, int value) + return null; + } + + private static void SetIfNotAlreadySet(string environmentVariableName, string value) + { + string? currentValue = Environment.GetEnvironmentVariable(environmentVariableName); + if (currentValue == null) { - SetIfNotAlreadySet(environmentVariableName, value.ToString()); + Environment.SetEnvironmentVariable(environmentVariableName, value); } + } + + private static void SetIfNotAlreadySet(string environmentVariableName, int value) + { + SetIfNotAlreadySet(environmentVariableName, value.ToString()); + } - private static bool CurrentPlatformIsWindowsAndOfficiallySupportsUTF8Encoding() + private static bool CurrentPlatformIsWindowsAndOfficiallySupportsUTF8Encoding() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Major >= 10) // UTF-8 is only officially supported on 10+. { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Major >= 10) // UTF-8 is only officially supported on 10+. - { - return CurrentPlatformOfficiallySupportsUTF8Encoding(); - } - return false; + return CurrentPlatformOfficiallySupportsUTF8Encoding(); } + return false; + } - private static bool CurrentPlatformOfficiallySupportsUTF8Encoding() + private static bool CurrentPlatformOfficiallySupportsUTF8Encoding() + { + Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + try { - Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); - try - { - using RegistryKey? windowsVersionRegistry = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); - var buildNumber = windowsVersionRegistry?.GetValue("CurrentBuildNumber")?.ToString() ?? string.Empty; - const int buildNumberThatOfficiallySupportsUTF8 = 18363; - return int.Parse(buildNumber) >= buildNumberThatOfficiallySupportsUTF8 || ForceUniversalEncodingOptInEnabled(); - } - catch (Exception ex) when (ex is SecurityException || ex is ObjectDisposedException) - { - // We don't want to break those in VS on older versions of Windows with a non-en language. - // Allow those without registry permissions to force the encoding, however. - return ForceUniversalEncodingOptInEnabled(); - } + using RegistryKey? windowsVersionRegistry = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion"); + var buildNumber = windowsVersionRegistry?.GetValue("CurrentBuildNumber")?.ToString() ?? string.Empty; + const int buildNumberThatOfficiallySupportsUTF8 = 18363; + return int.Parse(buildNumber) >= buildNumberThatOfficiallySupportsUTF8 || ForceUniversalEncodingOptInEnabled(); } - - private static bool ForceUniversalEncodingOptInEnabled() + catch (Exception ex) when (ex is SecurityException || ex is ObjectDisposedException) { - return string.Equals(Environment.GetEnvironmentVariable("DOTNET_CLI_FORCE_UTF8_ENCODING"), "true", StringComparison.OrdinalIgnoreCase); + // We don't want to break those in VS on older versions of Windows with a non-en language. + // Allow those without registry permissions to force the encoding, however. + return ForceUniversalEncodingOptInEnabled(); } } + + private static bool ForceUniversalEncodingOptInEnabled() + { + return string.Equals(Environment.GetEnvironmentVariable("DOTNET_CLI_FORCE_UTF8_ENCODING"), "true", StringComparison.OrdinalIgnoreCase); + } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Uuid.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Uuid.cs index 5c89249a0cde..2cc3e85aaa55 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Uuid.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Uuid.cs @@ -4,60 +4,59 @@ using System.Diagnostics; using System.IO.Hashing; -namespace Microsoft.DotNet.Cli.Utils +namespace Microsoft.DotNet.Cli.Utils; + +public class Uuid { - public class Uuid + /// + /// Generate a Version 8 (XxHash3 Name Based) Guid from a name. + /// + /// The name to use for generating the GUID. + /// A generated . + public static Guid Create(string name) { - /// - /// Generate a Version 8 (XxHash3 Name Based) Guid from a name. - /// - /// The name to use for generating the GUID. - /// A generated . - public static Guid Create(string name) - { - // Any fixed GUID will do for a namespace. - Guid namespaceId = new("28F1468D-672B-489A-8E0C-7C5B3030630C"); + // Any fixed GUID will do for a namespace. + Guid namespaceId = new("28F1468D-672B-489A-8E0C-7C5B3030630C"); - var nameBytes = Encoding.UTF8.GetBytes(name ?? string.Empty); - var namespaceBytes = namespaceId.ToByteArray(); + var nameBytes = Encoding.UTF8.GetBytes(name ?? string.Empty); + var namespaceBytes = namespaceId.ToByteArray(); - SwapGuidByteOrder(namespaceBytes); + SwapGuidByteOrder(namespaceBytes); - var streamToHash = new byte[namespaceBytes.Length + nameBytes.Length]; + var streamToHash = new byte[namespaceBytes.Length + nameBytes.Length]; - Array.Copy(namespaceBytes, streamToHash, namespaceBytes.Length); - Array.Copy(nameBytes, 0, streamToHash, namespaceBytes.Length, nameBytes.Length); + Array.Copy(namespaceBytes, streamToHash, namespaceBytes.Length); + Array.Copy(nameBytes, 0, streamToHash, namespaceBytes.Length, nameBytes.Length); - var hashResult = XxHash128.Hash(streamToHash); // This is just used for generating a named pipe so we don't need a cryptographic hash - Debug.Assert(hashResult.Length >= 16); + var hashResult = XxHash128.Hash(streamToHash); // This is just used for generating a named pipe so we don't need a cryptographic hash + Debug.Assert(hashResult.Length >= 16); - var res = new byte[16]; + var res = new byte[16]; - Array.Copy(hashResult, res, res.Length); + Array.Copy(hashResult, res, res.Length); - unchecked { res[6] = (byte)(0x80 | (res[6] & 0x0F)); } - unchecked { res[8] = (byte)(0x40 | (res[8] & 0x3F)); } + unchecked { res[6] = (byte)(0x80 | (res[6] & 0x0F)); } + unchecked { res[8] = (byte)(0x40 | (res[8] & 0x3F)); } - SwapGuidByteOrder(res); + SwapGuidByteOrder(res); - return new Guid(res); - } + return new Guid(res); + } - // Do a byte order swap, .NET GUIDs store multi byte components in little - // endian. - private static void SwapGuidByteOrder(byte[] b) - { - Swap(b, 0, 3); - Swap(b, 1, 2); - Swap(b, 5, 6); - Swap(b, 7, 8); - } + // Do a byte order swap, .NET GUIDs store multi byte components in little + // endian. + private static void SwapGuidByteOrder(byte[] b) + { + Swap(b, 0, 3); + Swap(b, 1, 2); + Swap(b, 5, 6); + Swap(b, 7, 8); + } - private static void Swap(byte[] b, int x, int y) - { - byte t = b[x]; - b[x] = b[y]; - b[y] = t; - } + private static void Swap(byte[] b, int x, int y) + { + byte t = b[x]; + b[x] = b[y]; + b[y] = t; } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Windows.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Windows.cs index 49f43fe2ed64..04f264867deb 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Windows.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Windows.cs @@ -1,7 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if NET using System.Runtime.Versioning; +#endif + using System.Security.Principal; namespace Microsoft.DotNet.Cli.Utils @@ -18,12 +21,8 @@ public static class Windows /// Determines whether the current user has the Administrator role. /// /// if the user has the Administrator role. - public static bool IsAdministrator() - { - WindowsPrincipal principal = new(WindowsIdentity.GetCurrent()); - - return principal.IsInRole(WindowsBuiltInRole.Administrator); - } + public static bool IsAdministrator() => + new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); /// /// Determine if an install is running by trying to open the global _MSIExecute mutex. The mutex is @@ -31,18 +30,14 @@ public static bool IsAdministrator() /// /// if another install is already running; otherwise. /// See the _MSIMutex documentation. - public static bool InstallRunning() - { - return !Mutex.TryOpenExisting(@"Global\_MSIExecute", out _); - } + public static bool InstallRunning() => + !Mutex.TryOpenExisting(@"Global\_MSIExecute", out _); /// /// Returns the commandline of the currently executing process. /// /// The commandline of the current process. - public static string? GetProcessCommandLine() - { - return Marshal.PtrToStringAuto(NativeMethods.Windows.GetCommandLine()); - } + public static string? GetProcessCommandLine() => + Marshal.PtrToStringAuto(NativeMethods.Windows.GetCommandLine()); } } diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/WindowsRegistryEnvironmentPathEditor.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/WindowsRegistryEnvironmentPathEditor.cs index 86516ef63441..32fffb96e4ae 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/WindowsRegistryEnvironmentPathEditor.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/WindowsRegistryEnvironmentPathEditor.cs @@ -3,68 +3,67 @@ using Microsoft.Win32; -namespace Microsoft.DotNet.Cli.Utils -{ +namespace Microsoft.DotNet.Cli.Utils; + #pragma warning disable CA1416 - internal class WindowsRegistryEnvironmentPathEditor : IWindowsRegistryEnvironmentPathEditor +internal class WindowsRegistryEnvironmentPathEditor : IWindowsRegistryEnvironmentPathEditor +{ + private static string Path = "PATH"; + public string? Get(SdkEnvironmentVariableTarget currentUserBeforeEvaluation) { - private static string Path = "PATH"; - public string? Get(SdkEnvironmentVariableTarget currentUserBeforeEvaluation) + using (RegistryKey? environmentKey = OpenEnvironmentKeyIfExists(writable: false, sdkEnvironmentVariableTarget: currentUserBeforeEvaluation)) { - using (RegistryKey? environmentKey = OpenEnvironmentKeyIfExists(writable: false, sdkEnvironmentVariableTarget: currentUserBeforeEvaluation)) - { - return environmentKey?.GetValue(Path, "", RegistryValueOptions.DoNotExpandEnvironmentNames) as string; - } + return environmentKey?.GetValue(Path, "", RegistryValueOptions.DoNotExpandEnvironmentNames) as string; } + } - public void Set(string value, SdkEnvironmentVariableTarget sdkEnvironmentVariableTarget) + public void Set(string value, SdkEnvironmentVariableTarget sdkEnvironmentVariableTarget) + { + using (RegistryKey? environmentKey = OpenEnvironmentKeyIfExists(writable: true, sdkEnvironmentVariableTarget: sdkEnvironmentVariableTarget)) { - using (RegistryKey? environmentKey = OpenEnvironmentKeyIfExists(writable: true, sdkEnvironmentVariableTarget: sdkEnvironmentVariableTarget)) - { - environmentKey?.SetValue(Path, value, RegistryValueKind.ExpandString); - } - - Task.Factory.StartNew(() => - { - unsafe - { - // send a WM_SETTINGCHANGE message to all windows - fixed (char* lParam = "Environment") - { - IntPtr r = SendMessageTimeout(new IntPtr(HWND_BROADCAST), WM_SETTINGCHANGE, IntPtr.Zero, (IntPtr)lParam, 0, 1000, out IntPtr _); - } - } - }); + environmentKey?.SetValue(Path, value, RegistryValueKind.ExpandString); } - private static RegistryKey? OpenEnvironmentKeyIfExists(bool writable, SdkEnvironmentVariableTarget sdkEnvironmentVariableTarget) + Task.Factory.StartNew(() => { - RegistryKey baseKey; - string keyName; - - if (sdkEnvironmentVariableTarget == SdkEnvironmentVariableTarget.CurrentUser) - { - baseKey = Registry.CurrentUser; - keyName = "Environment"; - } - else if (sdkEnvironmentVariableTarget == SdkEnvironmentVariableTarget.DotDefault) + unsafe { - baseKey = Registry.Users; - keyName = ".DEFAULT\\Environment"; - } - else - { - throw new ArgumentException(nameof(sdkEnvironmentVariableTarget) + " cannot be matched, the value is: " + sdkEnvironmentVariableTarget.ToString()); + // send a WM_SETTINGCHANGE message to all windows + fixed (char* lParam = "Environment") + { + IntPtr r = SendMessageTimeout(new IntPtr(HWND_BROADCAST), WM_SETTINGCHANGE, IntPtr.Zero, (IntPtr)lParam, 0, 1000, out IntPtr _); + } } + }); + } - return baseKey.OpenSubKey(keyName, writable: writable); - } + private static RegistryKey? OpenEnvironmentKeyIfExists(bool writable, SdkEnvironmentVariableTarget sdkEnvironmentVariableTarget) + { + RegistryKey baseKey; + string keyName; - [DllImport("user32.dll", EntryPoint = "SendMessageTimeoutW")] - private static extern IntPtr SendMessageTimeout(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, int flags, int timeout, out IntPtr pdwResult); + if (sdkEnvironmentVariableTarget == SdkEnvironmentVariableTarget.CurrentUser) + { + baseKey = Registry.CurrentUser; + keyName = "Environment"; + } + else if (sdkEnvironmentVariableTarget == SdkEnvironmentVariableTarget.DotDefault) + { + baseKey = Registry.Users; + keyName = ".DEFAULT\\Environment"; + } + else + { + throw new ArgumentException(nameof(sdkEnvironmentVariableTarget) + " cannot be matched, the value is: " + sdkEnvironmentVariableTarget.ToString()); + } - private const int HWND_BROADCAST = 0xffff; - private const int WM_SETTINGCHANGE = 0x001A; + return baseKey.OpenSubKey(keyName, writable: writable); } -#pragma warning restore CA1416 + + [DllImport("user32.dll", EntryPoint = "SendMessageTimeoutW")] + private static extern IntPtr SendMessageTimeout(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, int flags, int timeout, out IntPtr pdwResult); + + private const int HWND_BROADCAST = 0xffff; + private const int WM_SETTINGCHANGE = 0x001A; } +#pragma warning restore CA1416 diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/NuGet/NugetApiManager.cs b/src/Cli/Microsoft.TemplateEngine.Cli/NuGet/NugetApiManager.cs index 969b769a1924..4ac3a4bfabc7 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/NuGet/NugetApiManager.cs +++ b/src/Cli/Microsoft.TemplateEngine.Cli/NuGet/NugetApiManager.cs @@ -34,7 +34,7 @@ internal NugetApiManager() PackageSource? sourceFeed = null, CancellationToken cancellationToken = default) { - if (sourceFeed == null && Microsoft.DotNet.Tools.Common.PathUtility.CheckForNuGetInNuGetConfig()) + if (sourceFeed == null && DotNet.Cli.Utils.PathUtility.CheckForNuGetInNuGetConfig()) { sourceFeed = _nugetOrgSource; } diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/TemplatePackageCoordinator.cs b/src/Cli/Microsoft.TemplateEngine.Cli/TemplatePackageCoordinator.cs index ec8e545b4163..ed20a0ee107d 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/TemplatePackageCoordinator.cs +++ b/src/Cli/Microsoft.TemplateEngine.Cli/TemplatePackageCoordinator.cs @@ -4,7 +4,6 @@ using System.CommandLine; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils.Extensions; -using Microsoft.DotNet.Tools.Common; using Microsoft.TemplateEngine.Abstractions; using Microsoft.TemplateEngine.Abstractions.Constraints; using Microsoft.TemplateEngine.Abstractions.Installer; diff --git a/src/Cli/dotnet/CommandFactory/CommandResolution/PackagedCommandSpecFactory.cs b/src/Cli/dotnet/CommandFactory/CommandResolution/PackagedCommandSpecFactory.cs index f944042e77da..d80d10e06637 100644 --- a/src/Cli/dotnet/CommandFactory/CommandResolution/PackagedCommandSpecFactory.cs +++ b/src/Cli/dotnet/CommandFactory/CommandResolution/PackagedCommandSpecFactory.cs @@ -3,7 +3,6 @@ using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils.Extensions; -using Microsoft.DotNet.Tools.Common; using NuGet.ProjectModel; namespace Microsoft.DotNet.CommandFactory diff --git a/src/Cli/dotnet/CommonOptions.cs b/src/Cli/dotnet/CommonOptions.cs index 3757c056a350..cdc47a41aa5c 100644 --- a/src/Cli/dotnet/CommonOptions.cs +++ b/src/Cli/dotnet/CommonOptions.cs @@ -8,7 +8,6 @@ using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; -using Microsoft.DotNet.Tools.Common; namespace Microsoft.DotNet.Cli { diff --git a/src/Cli/dotnet/ToolPackage/ToolPackageInstance.cs b/src/Cli/dotnet/ToolPackage/ToolPackageInstance.cs index 8a9873c002e4..cf9ea457ef22 100644 --- a/src/Cli/dotnet/ToolPackage/ToolPackageInstance.cs +++ b/src/Cli/dotnet/ToolPackage/ToolPackageInstance.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; using Microsoft.Extensions.EnvironmentAbstractions; diff --git a/src/Cli/dotnet/commands/dotnet-new/BuiltInTemplatePackageProvider.cs b/src/Cli/dotnet/commands/dotnet-new/BuiltInTemplatePackageProvider.cs index 625bc56346be..55627ff17719 100644 --- a/src/Cli/dotnet/commands/dotnet-new/BuiltInTemplatePackageProvider.cs +++ b/src/Cli/dotnet/commands/dotnet-new/BuiltInTemplatePackageProvider.cs @@ -48,7 +48,7 @@ private static IEnumerable GetTemplateFolders(IEngineEnvironmentSettings { var templateFoldersToInstall = new List(); - var sdkDirectory = Path.GetDirectoryName(typeof(Microsoft.DotNet.Cli.DotnetFiles).Assembly.Location); + var sdkDirectory = Path.GetDirectoryName(typeof(Cli.Utils.DotnetFiles).Assembly.Location); var dotnetRootPath = Path.GetDirectoryName(Path.GetDirectoryName(sdkDirectory)); // First grab templates from dotnet\templates\M.m folders, in ascending order, up to our version diff --git a/src/Cli/dotnet/commands/dotnet-new/DotnetCommandCallbacks.cs b/src/Cli/dotnet/commands/dotnet-new/DotnetCommandCallbacks.cs index 7d0ea1c3a7c5..68343f175a80 100644 --- a/src/Cli/dotnet/commands/dotnet-new/DotnetCommandCallbacks.cs +++ b/src/Cli/dotnet/commands/dotnet-new/DotnetCommandCallbacks.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.DotNet.Cli; -using Microsoft.DotNet.Tools.Common; +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools.Package.Add; using Microsoft.DotNet.Tools.Reference.Add; using Microsoft.DotNet.Tools.Restore; diff --git a/src/Cli/dotnet/commands/dotnet-new/OptionalWorkloadProvider.cs b/src/Cli/dotnet/commands/dotnet-new/OptionalWorkloadProvider.cs index 4cc35641ef68..20e9cd7a5d62 100644 --- a/src/Cli/dotnet/commands/dotnet-new/OptionalWorkloadProvider.cs +++ b/src/Cli/dotnet/commands/dotnet-new/OptionalWorkloadProvider.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Configurer; using Microsoft.TemplateEngine.Abstractions; using Microsoft.TemplateEngine.Abstractions.TemplatePackage; diff --git a/src/Cli/dotnet/commands/dotnet-reference/dotnet-reference-add/Program.cs b/src/Cli/dotnet/commands/dotnet-reference/dotnet-reference-add/Program.cs index 8e31c38952cb..2f42f43d3058 100644 --- a/src/Cli/dotnet/commands/dotnet-reference/dotnet-reference-add/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-reference/dotnet-reference-add/Program.cs @@ -6,7 +6,6 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.Utils; -using Microsoft.DotNet.Tools.Common; using NuGet.Frameworks; namespace Microsoft.DotNet.Tools.Reference.Add diff --git a/src/Cli/dotnet/commands/dotnet-sln/SlnArgumentValidator.cs b/src/Cli/dotnet/commands/dotnet-sln/SlnArgumentValidator.cs index e6f3ad37dba5..aa5083eabc3d 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/SlnArgumentValidator.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/SlnArgumentValidator.cs @@ -3,7 +3,6 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Utils; -using Microsoft.DotNet.Tools.Common; namespace Microsoft.DotNet.Tools.Sln { diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs index b4d8e7547ff9..baffdef07eff 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs @@ -3,7 +3,7 @@ using System.Collections.Concurrent; using System.Diagnostics; -using Microsoft.DotNet.Tools.Common; +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools.Test; using Microsoft.Testing.Platform.OutputDevice; using Microsoft.Testing.Platform.OutputDevice.Terminal; @@ -55,7 +55,7 @@ public bool RunMSBuild(BuildOptions buildOptions) if (msBuildExitCode != ExitCode.Success) { - _output.WriteMessage(string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorDescription, msBuildExitCode)); + _output.WriteMessage(string.Format(Microsoft.DotNet.Tools.Test.LocalizableStrings.CmdMSBuildProjectsPropertiesErrorDescription, msBuildExitCode)); return false; } @@ -99,7 +99,7 @@ private void InitializeTestApplications(IEnumerable modules) _output.WriteMessage( string.Format( - LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription, + Microsoft.DotNet.Tools.Test.LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription, string.Join(Environment.NewLine, vsTestTestProjects.Select(module => Path.GetFileName(module.ProjectFullPath)))), new SystemConsoleColor { ConsoleColor = ConsoleColor.Red }); diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildUtility.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildUtility.cs index f55338d284b2..6420e35bfe1c 100644 --- a/src/Cli/dotnet/commands/dotnet-test/MSBuildUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildUtility.cs @@ -6,6 +6,7 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.DotNet.Cli.Extensions; +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; using Microsoft.DotNet.Tools.Common; using Microsoft.VisualStudio.SolutionPersistence.Model; diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs index 50f0ffde0cd7..6eeeb1db0912 100644 --- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs +++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.Evaluation; +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; -using Microsoft.DotNet.Tools.Common; using NuGet.Packaging; using LocalizableStrings = Microsoft.DotNet.Tools.Test.LocalizableStrings; diff --git a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs index fdb19928bd63..3cdb97946a62 100644 --- a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs +++ b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs @@ -4,7 +4,7 @@ using System.Collections.Concurrent; using System.Diagnostics; using System.IO.Pipes; -using Microsoft.DotNet.Tools.Common; +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools.Test; namespace Microsoft.DotNet.Cli @@ -196,7 +196,7 @@ private Task OnRequest(IRequest request) default: // If it doesn't match any of the above, throw an exception - throw new NotSupportedException(string.Format(LocalizableStrings.CmdUnsupportedMessageRequestTypeException, request.GetType())); + throw new NotSupportedException(string.Format(Microsoft.DotNet.Tools.Test.LocalizableStrings.CmdUnsupportedMessageRequestTypeException, request.GetType())); } } catch (Exception ex) diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/ToolSearchCommand.cs b/src/Cli/dotnet/commands/dotnet-tool/search/ToolSearchCommand.cs index 29d9c4a75d27..70653a1af66a 100644 --- a/src/Cli/dotnet/commands/dotnet-tool/search/ToolSearchCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-tool/search/ToolSearchCommand.cs @@ -5,7 +5,6 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.NugetSearch; -using Microsoft.DotNet.Tools.Common; using NuGet.Configuration; namespace Microsoft.DotNet.Tools.Tool.Search diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs index cd7a0c252a0d..d8a51d9bc009 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs @@ -18,7 +18,7 @@ using NuGet.Common; using NuGet.Versioning; using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadResolver; -using PathUtility = Microsoft.DotNet.Tools.Common.PathUtility; +using PathUtility = Microsoft.DotNet.Cli.Utils.PathUtility; namespace Microsoft.DotNet.Workloads.Workload.Install { diff --git a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/WorkloadSdkResolver.cs b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/WorkloadSdkResolver.cs index dcf94d39b2e7..c2b483303823 100644 --- a/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/WorkloadSdkResolver.cs +++ b/src/Resolvers/Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver/WorkloadSdkResolver.cs @@ -6,9 +6,8 @@ using Microsoft.DotNet.Configurer; using static Microsoft.NET.Sdk.WorkloadMSBuildSdkResolver.CachingWorkloadResolver; - #if NET -using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.Utils; #else using Microsoft.DotNet.DotNetSdkResolver; #endif diff --git a/test/Microsoft.DotNet.Cli.Utils.Tests/PathUtilityTests.cs b/test/Microsoft.DotNet.Cli.Utils.Tests/PathUtilityTests.cs index 0d7f0cb96a4f..182f14232ec1 100644 --- a/test/Microsoft.DotNet.Cli.Utils.Tests/PathUtilityTests.cs +++ b/test/Microsoft.DotNet.Cli.Utils.Tests/PathUtilityTests.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Tools.Common; - namespace Microsoft.DotNet.Cli.Utils { public class PathUtilityTests diff --git a/test/Microsoft.NET.TestFramework/Utilities/RegexPatternHelper.cs b/test/Microsoft.NET.TestFramework/Utilities/RegexPatternHelper.cs index 289443b79e3a..56115dadee5d 100644 --- a/test/Microsoft.NET.TestFramework/Utilities/RegexPatternHelper.cs +++ b/test/Microsoft.NET.TestFramework/Utilities/RegexPatternHelper.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Tools.Common; +using Microsoft.DotNet.Cli.Utils; namespace Microsoft.NET.TestFramework.Utilities { diff --git a/test/dotnet-list-reference.Tests/GivenDotnetListReference.cs b/test/dotnet-list-reference.Tests/GivenDotnetListReference.cs index 2733a7bcb903..264bb1156299 100644 --- a/test/dotnet-list-reference.Tests/GivenDotnetListReference.cs +++ b/test/dotnet-list-reference.Tests/GivenDotnetListReference.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.DotNet.Cli.CommandLineValidation; +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; -using Microsoft.DotNet.Tools.Common; using Msbuild.Tests.Utilities; namespace Microsoft.DotNet.Cli.List.Reference.Tests @@ -72,8 +72,8 @@ public void WhenTooManyArgumentsArePassedItPrintsError() var cmd = new DotnetCommand(Log, "list one two three reference".Split()) .Execute("proj.csproj"); cmd.ExitCode.Should().NotBe(0); - cmd.StdErr.Should().BeVisuallyEquivalentTo($@"{string.Format(LocalizableStrings.UnrecognizedCommandOrArgument, "two")} -{string.Format(LocalizableStrings.UnrecognizedCommandOrArgument, "three")}"); + cmd.StdErr.Should().BeVisuallyEquivalentTo($@"{string.Format(Microsoft.DotNet.Cli.CommandLineValidation.LocalizableStrings.UnrecognizedCommandOrArgument, "two")} +{string.Format(Microsoft.DotNet.Cli.CommandLineValidation.LocalizableStrings.UnrecognizedCommandOrArgument, "three")}"); } [Theory] diff --git a/test/dotnet-remove-package.Tests/GivenDotnetRemovePackage.cs b/test/dotnet-remove-package.Tests/GivenDotnetRemovePackage.cs index 09c3ee8a60ee..a75722c75d7b 100644 --- a/test/dotnet-remove-package.Tests/GivenDotnetRemovePackage.cs +++ b/test/dotnet-remove-package.Tests/GivenDotnetRemovePackage.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; -using Microsoft.DotNet.Tools.Common; namespace Microsoft.DotNet.Cli.Remove.Package.Tests { diff --git a/test/dotnet-remove-reference.Tests/GivenDotnetRemoveP2P.cs b/test/dotnet-remove-reference.Tests/GivenDotnetRemoveP2P.cs index 0ed49a7d14e5..21dc9483e3f5 100644 --- a/test/dotnet-remove-reference.Tests/GivenDotnetRemoveP2P.cs +++ b/test/dotnet-remove-reference.Tests/GivenDotnetRemoveP2P.cs @@ -4,8 +4,8 @@ #nullable disable using Microsoft.DotNet.Cli.CommandLineValidation; +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; -using Microsoft.DotNet.Tools.Common; using Msbuild.Tests.Utilities; namespace Microsoft.DotNet.Cli.Remove.Reference.Tests @@ -163,8 +163,8 @@ public void WhenTooManyArgumentsArePassedItPrintsError() var cmd = new DotnetCommand(Log, "add", "one", "two", "three", "reference", "proj.csproj") .Execute(); cmd.ExitCode.Should().NotBe(0); - cmd.StdErr.Should().BeVisuallyEquivalentTo($@"{string.Format(LocalizableStrings.UnrecognizedCommandOrArgument, "two")} -{string.Format(LocalizableStrings.UnrecognizedCommandOrArgument, "three")}"); + cmd.StdErr.Should().BeVisuallyEquivalentTo($@"{string.Format(Microsoft.DotNet.Cli.CommandLineValidation.LocalizableStrings.UnrecognizedCommandOrArgument, "two")} +{string.Format(Microsoft.DotNet.Cli.CommandLineValidation.LocalizableStrings.UnrecognizedCommandOrArgument, "three")}"); } [Theory] diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs b/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs index 799a0de7a26d..e62d9e553614 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnAdd.cs @@ -5,7 +5,6 @@ using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; -using Microsoft.DotNet.Tools.Common; using Microsoft.VisualStudio.SolutionPersistence.Serializer; using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnList.cs b/test/dotnet-sln.Tests/GivenDotnetSlnList.cs index 17a1bbfeaadc..15d9d7345c59 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnList.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnList.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; -using Microsoft.DotNet.Tools.Common; using CommandLocalizableStrings = Microsoft.DotNet.Tools.Sln.LocalizableStrings; namespace Microsoft.DotNet.Cli.Sln.List.Tests diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnMigrate.cs b/test/dotnet-sln.Tests/GivenDotnetSlnMigrate.cs index f18ac2d7fba5..e848398d3a82 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnMigrate.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnMigrate.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.DotNet.Tools; -using Microsoft.DotNet.Tools.Common; using CommandLocalizableStrings = Microsoft.DotNet.Tools.Sln.LocalizableStrings; namespace Microsoft.DotNet.Cli.Sln.Migrate.Tests diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs index a398fc4b70cc..25f6b667ecd7 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnRemove.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; -using Microsoft.DotNet.Tools.Common; using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; using Microsoft.VisualStudio.SolutionPersistence.Serializer; diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsHelp.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsHelp.cs index 63987efc0acc..4fd3207c1e23 100644 --- a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsHelp.cs +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsHelp.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Tools.Common; +using Microsoft.DotNet.Cli.Utils; using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; namespace Microsoft.DotNet.Cli.Test.Tests diff --git a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithArtifacts.cs b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithArtifacts.cs index 4d5038d84c45..6fd0b96ce710 100644 --- a/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithArtifacts.cs +++ b/test/dotnet-test.Tests/GivenDotnetTestBuildsAndRunsTestsWithArtifacts.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.DotNet.Tools.Common; +using Microsoft.DotNet.Cli.Utils; using CommandResult = Microsoft.DotNet.Cli.Utils.CommandResult; namespace Microsoft.DotNet.Cli.Test.Tests diff --git a/test/dotnet.Tests/ParserTests/AddReferenceParserTests.cs b/test/dotnet.Tests/ParserTests/AddReferenceParserTests.cs index d123d2ab2bf2..00532b090f26 100644 --- a/test/dotnet.Tests/ParserTests/AddReferenceParserTests.cs +++ b/test/dotnet.Tests/ParserTests/AddReferenceParserTests.cs @@ -3,7 +3,7 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.CommandLineValidation; -using Microsoft.DotNet.Tools.Common; +using Microsoft.DotNet.Cli.Utils; using Parser = Microsoft.DotNet.Cli.Parser; namespace Microsoft.DotNet.Tests.ParserTests @@ -55,7 +55,7 @@ public void AddReferenceWithoutArgumentResultsInAnError() .Errors .Select(e => e.Message) .Should() - .BeEquivalentTo(string.Format(LocalizableStrings.RequiredArgumentMissingForCommand, "'reference'.")); + .BeEquivalentTo(string.Format(Microsoft.DotNet.Cli.CommandLineValidation.LocalizableStrings.RequiredArgumentMissingForCommand, "'reference'.")); } } }