diff --git a/documentation/manpages/sdk/dotnet-nuget-locals.1 b/documentation/manpages/sdk/dotnet-nuget-locals.1 index f2d61f91cd5d..7425d09f2bfb 100644 --- a/documentation/manpages/sdk/dotnet-nuget-locals.1 +++ b/documentation/manpages/sdk/dotnet-nuget-locals.1 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-nuget-locals" "1" "2025-08-15" "" ".NET Documentation" +.TH "dotnet-nuget-locals" "1" "2025-08-29" "" ".NET Documentation" .hy .SH dotnet nuget locals .PP diff --git a/documentation/manpages/sdk/dotnet-nuget-push.1 b/documentation/manpages/sdk/dotnet-nuget-push.1 index cf0dfc6d9e96..e25c575ef1ef 100644 --- a/documentation/manpages/sdk/dotnet-nuget-push.1 +++ b/documentation/manpages/sdk/dotnet-nuget-push.1 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-nuget-push" "1" "2025-08-15" "" ".NET Documentation" +.TH "dotnet-nuget-push" "1" "2025-08-29" "" ".NET Documentation" .hy .SH dotnet nuget push .PP diff --git a/documentation/manpages/sdk/dotnet-nuget-sign.1 b/documentation/manpages/sdk/dotnet-nuget-sign.1 index fbf80a84cd27..46020d0781b5 100644 --- a/documentation/manpages/sdk/dotnet-nuget-sign.1 +++ b/documentation/manpages/sdk/dotnet-nuget-sign.1 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-nuget-sign" "1" "2025-08-15" "" ".NET Documentation" +.TH "dotnet-nuget-sign" "1" "2025-08-29" "" ".NET Documentation" .hy .SH dotnet nuget sign .PP diff --git a/documentation/manpages/sdk/dotnet-publish.1 b/documentation/manpages/sdk/dotnet-publish.1 index b494f7417f22..64610ed45272 100644 --- a/documentation/manpages/sdk/dotnet-publish.1 +++ b/documentation/manpages/sdk/dotnet-publish.1 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-publish" "1" "2025-06-13" "" ".NET Documentation" +.TH "dotnet-publish" "1" "2025-08-29" "" ".NET Documentation" .hy .SH dotnet publish .PP @@ -57,7 +57,7 @@ The application\[cq]s dependencies, which are copied from the NuGet cache into t The \f[V]dotnet publish\f[R] command\[cq]s output is ready for deployment to a hosting system (for example, a server, PC, Mac, laptop) for execution. It\[cq]s the only officially supported way to prepare the application for deployment. Depending on the type of deployment that the project specifies, the hosting system may or may not have the .NET shared runtime installed on it. -For more information, see Publish .NET apps with the .NET CLI. +For more information, see .NET application publishing overview. .SS Implicit restore .PP You don\[cq]t have to run \f[V]dotnet restore\f[R] because it\[cq]s run implicitly by all commands that require a restore to occur, such as \f[V]dotnet new\f[R], \f[V]dotnet build\f[R], \f[V]dotnet run\f[R], \f[V]dotnet test\f[R], \f[V]dotnet publish\f[R], and \f[V]dotnet pack\f[R]. @@ -346,7 +346,7 @@ Available since .NET 6. .PP Publishes the .NET runtime with your application so the runtime doesn\[cq]t need to be installed on the target machine. Default is \f[V]true\f[R] if a runtime identifier is specified and the project is an executable project (not a library project). -For more information, see .NET application publishing and Publish .NET apps with the .NET CLI. +For more information, see Self-contained deployment. .PP If this option is used without specifying \f[V]true\f[R] or \f[V]false\f[R], the default is \f[V]true\f[R]. In that case, don\[cq]t put the solution or project argument immediately after \f[V]--self-contained\f[R], because \f[V]true\f[R] or \f[V]false\f[R] is expected in that position. @@ -369,7 +369,7 @@ The URI of the NuGet package source to use during the restore operation. .PP Publishes the application for a given runtime. For a list of Runtime Identifiers (RIDs), see the RID catalog. -For more information, see .NET application publishing and Publish .NET apps with the .NET CLI. +For more information, see .NET application publishing overview. If you use this option, use \f[V]--self-contained\f[R] or \f[V]--no-self-contained\f[R] also. .RE .IP \[bu] 2 @@ -495,8 +495,6 @@ dotnet publish --no-dependencies .IP \[bu] 2 \&.NET application publishing overview .IP \[bu] 2 -Publish .NET apps with the .NET CLI -.IP \[bu] 2 Target frameworks .IP \[bu] 2 Runtime Identifier (RID) catalog diff --git a/documentation/manpages/sdk/dotnet-reference-remove.1 b/documentation/manpages/sdk/dotnet-reference-remove.1 index 8db63f49bbf9..4117a2412d71 100644 --- a/documentation/manpages/sdk/dotnet-reference-remove.1 +++ b/documentation/manpages/sdk/dotnet-reference-remove.1 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-reference-remove" "1" "2025-08-15" "" ".NET Documentation" +.TH "dotnet-reference-remove" "1" "2025-08-29" "" ".NET Documentation" .hy .SH dotnet reference remove .PP diff --git a/documentation/manpages/sdk/dotnet-sln.1 b/documentation/manpages/sdk/dotnet-sln.1 index 221f2d995795..6cae1db32abe 100644 --- a/documentation/manpages/sdk/dotnet-sln.1 +++ b/documentation/manpages/sdk/dotnet-sln.1 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-sln" "1" "2025-06-13" "" ".NET Documentation" +.TH "dotnet-sln" "1" "2025-08-29" "" ".NET Documentation" .hy .SH dotnet sln .PP @@ -39,7 +39,7 @@ The \f[V]dotnet sln\f[R] command provides a convenient way to list and modify pr To use the \f[V]dotnet sln\f[R] command, the solution file must already exist. If you need to create one, use the dotnet new command with the \f[V]sln\f[R] template name. .PP -The following example creates a \f[I].sln\f[R] file in the current folder, with the same name as the folder: +The following example creates an \f[I].slnx\f[R] file in the current folder, with the same name as the folder: .IP .nf \f[C] @@ -47,7 +47,7 @@ dotnet new sln \f[R] .fi .PP -The following example creates a \f[I].sln\f[R] file in the current folder, with the specified file name: +The following example creates an \f[I].slnx\f[R] file in the current folder, with the specified file name: .IP .nf \f[C] @@ -55,13 +55,17 @@ dotnet new sln --name MySolution \f[R] .fi .PP -The following example creates a \f[I].sln\f[R] file in the specified folder, with the same name as the folder: +The following example creates an \f[I].slnx\f[R] file in the specified folder, with the same name as the folder: .IP .nf \f[C] dotnet new sln --output MySolution \f[R] .fi +.RS +.PP +In .NET 9 and earlier versions, \f[V]dotnet new sln\f[R] creates an \f[I].sln\f[R] file instead of an \f[I].slnx\f[R] file. +.RE .SH ARGUMENTS .IP \[bu] 2 \f[B]\f[VB]SOLUTION_FILE\f[B]\f[R] diff --git a/documentation/manpages/sdk/dotnet-test.1 b/documentation/manpages/sdk/dotnet-test.1 index f85d10f37ec7..f10f0f74b472 100644 --- a/documentation/manpages/sdk/dotnet-test.1 +++ b/documentation/manpages/sdk/dotnet-test.1 @@ -15,7 +15,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-test" "1" "2025-06-13" "" ".NET Documentation" +.TH "dotnet-test" "1" "2025-08-29" "" ".NET Documentation" .hy .SH dotnet test .SH NAME @@ -736,7 +736,6 @@ Passing runsettings arguments through commandline (https://github.com/microsoft/ dotnet test [--project ] [--solution ] - [--directory ] [--test-modules ] [--root-directory ] [--max-parallel-test-modules ] @@ -780,7 +779,7 @@ For information about how to manage NuGet feeds, see the \f[V]dotnet restore\f[R .SH OPTIONS .RS .PP -You can use only one of the following options at a time: \f[V]--project\f[R], \f[V]--solution\f[R], \f[V]--directory\f[R], or \f[V]--test-modules\f[R]. +You can use only one of the following options at a time: \f[V]--project\f[R], \f[V]--solution\f[R], or \f[V]--test-modules\f[R]. These options can\[cq]t be combined. In addition, when using \f[V]--test-modules\f[R], you can\[cq]t specify \f[V]--arch\f[R], \f[V]--configuration\f[R], \f[V]--framework\f[R], \f[V]--os\f[R], or \f[V]--runtime\f[R]. These options are not relevant for an already-built module. @@ -789,19 +788,15 @@ These options are not relevant for an already-built module. \f[B]\f[VB]--project \f[B]\f[R] .RS 2 .PP -Specifies the path to the test project. +Specifies the path of the project file to run (folder name or full path). +If not specified, it defaults to the current directory. .RE .IP \[bu] 2 \f[B]\f[VB]--solution \f[B]\f[R] .RS 2 .PP -Specifies the path to the solution. -.RE -.IP \[bu] 2 -\f[B]\f[VB]--directory \f[B]\f[R] -.RS 2 -.PP -Specifies the path to a directory that contains a project or a solution. +Specifies the path of the solution file to run (folder name or full path). +If not specified, it defaults to the current directory. .RE .IP \[bu] 2 \f[B]\f[VB]--test-modules \f[B]\f[R] @@ -993,16 +988,6 @@ dotnet test --solution ./TestProjects/TestProjects.sln .fi .RE .IP \[bu] 2 -Run the tests in a solution or project that can be found in the \f[V]TestProjects\f[R] directory: -.RS 2 -.IP -.nf -\f[C] -dotnet test --directory ./TestProjects -\f[R] -.fi -.RE -.IP \[bu] 2 Run the tests using \f[V]TestProject.dll\f[R] assembly: .RS 2 .IP diff --git a/documentation/manpages/sdk/dotnet-tool-install.1 b/documentation/manpages/sdk/dotnet-tool-install.1 index 665199a9fa36..bf6966b12e75 100644 --- a/documentation/manpages/sdk/dotnet-tool-install.1 +++ b/documentation/manpages/sdk/dotnet-tool-install.1 @@ -15,7 +15,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-tool-install" "1" "2025-08-15" "" ".NET Documentation" +.TH "dotnet-tool-install" "1" "2025-08-29" "" ".NET Documentation" .hy .SH dotnet tool install .PP diff --git a/eng/pipelines/templates/jobs/sdk-build.yml b/eng/pipelines/templates/jobs/sdk-build.yml index 67d02032d6b3..c1ec3d7dfcd1 100644 --- a/eng/pipelines/templates/jobs/sdk-build.yml +++ b/eng/pipelines/templates/jobs/sdk-build.yml @@ -131,6 +131,7 @@ jobs: displayName: 🟣 Build env: BuildConfig: $(buildConfiguration) + OPENSSL_ENABLE_SHA1_SIGNATURES: 1 ############### TESTING ############### - ${{ if eq(parameters.runTests, true) }}: diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs index 859d02baddb1..12226f42f0d6 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs @@ -12,11 +12,24 @@ namespace Microsoft.DotNet.Cli.Utils; /// public sealed class MSBuildArgs { - private MSBuildArgs(ReadOnlyDictionary? properties, ReadOnlyDictionary? restoreProperties, string[]? targets, VerbosityOptions? verbosity, string[]? otherMSBuildArgs) + private MSBuildArgs( + ReadOnlyDictionary? properties, + ReadOnlyDictionary? restoreProperties, + string[]? targets, + string[]? getProperty, + string[]? getItem, + string[]? getTargetResult, + string[]? getResultOutputFile, + VerbosityOptions? verbosity, + string[]? otherMSBuildArgs) { GlobalProperties = properties; RestoreGlobalProperties = restoreProperties; RequestedTargets = targets; + GetProperty = getProperty; + GetItem = getItem; + GetTargetResult = getTargetResult; + GetResultOutputFile = getResultOutputFile; Verbosity = verbosity; OtherMSBuildArgs = otherMSBuildArgs is not null ? [.. otherMSBuildArgs] @@ -37,6 +50,15 @@ private MSBuildArgs(ReadOnlyDictionary? properties, ReadOnlyDict /// The ordered list of targets that should be passed to MSBuild. /// public string[]? RequestedTargets { get; } + + public string[]? GetProperty { get; } + + public string[]? GetItem { get; } + + public string[]? GetTargetResult { get; } + + public string[]? GetResultOutputFile { get; } + public VerbosityOptions? Verbosity { get; } /// @@ -70,6 +92,10 @@ public static MSBuildArgs AnalyzeMSBuildArguments(IEnumerable forwardedA var globalProperties = parseResult.GetResult("--property") is OptionResult propResult ? propResult.GetValueOrDefault?>() : null; var restoreProperties = parseResult.GetResult("--restoreProperty") is OptionResult restoreResult ? restoreResult.GetValueOrDefault?>() : null; var requestedTargets = parseResult.GetResult("--target") is OptionResult targetResult ? targetResult.GetValueOrDefault() : null; + var getProperty = TryGetValue("--getProperty"); + var getItem = TryGetValue("--getItem"); + var getTargetResult = TryGetValue("--getTargetResult"); + var getResultOutputFile = TryGetValue("--getResultOutputFile"); var verbosity = parseResult.GetResult("--verbosity") is OptionResult verbosityResult ? verbosityResult.GetValueOrDefault() : null; @@ -78,26 +104,35 @@ public static MSBuildArgs AnalyzeMSBuildArguments(IEnumerable forwardedA properties: globalProperties, restoreProperties: restoreProperties, targets: requestedTargets, + getProperty: getProperty, + getItem: getItem, + getTargetResult: getTargetResult, + getResultOutputFile: getResultOutputFile, otherMSBuildArgs: otherMSBuildArgs, verbosity: verbosity); + + T? TryGetValue(string name) + { + return options.Any(o => o.Name == name) ? parseResult.GetValue(name) : default; + } } public static MSBuildArgs FromProperties(ReadOnlyDictionary? properties) { - return new MSBuildArgs(properties, null, null, null, null); + return new MSBuildArgs(properties, null, null, null, null, null, null, null, null); } public static MSBuildArgs FromOtherArgs(params ReadOnlySpan args) { - return new MSBuildArgs(null, null, null, null, args.ToArray()); + return new MSBuildArgs(null, null, null, null, null, null, null, null, args.ToArray()); } public static MSBuildArgs FromVerbosity(VerbosityOptions verbosity) { - return new MSBuildArgs(null, null, null, verbosity, null); + return new MSBuildArgs(null, null, null, null, null, null, null, verbosity, null); } - public static readonly MSBuildArgs ForHelp = new(null, null, null, null, ["--help"]); + public static readonly MSBuildArgs ForHelp = new(null, null, null, null, null, null, null, null, ["--help"]); /// /// Completely replaces the MSBuild arguments with the provided . @@ -108,6 +143,10 @@ public MSBuildArgs CloneWithExplicitArgs(string[] newArgs) properties: GlobalProperties, restoreProperties: RestoreGlobalProperties, targets: RequestedTargets, + getProperty: GetProperty, + getItem: GetItem, + getTargetResult: GetTargetResult, + getResultOutputFile: GetResultOutputFile, otherMSBuildArgs: newArgs, verbosity: Verbosity); } @@ -120,10 +159,28 @@ public MSBuildArgs CloneWithAdditionalArgs(params string[] additionalArgs) if (additionalArgs is null || additionalArgs.Length == 0) { // If there are no additional args, we can just return the current instance. - return new MSBuildArgs(GlobalProperties, RestoreGlobalProperties, RequestedTargets, Verbosity, OtherMSBuildArgs.ToArray()); + return new MSBuildArgs( + GlobalProperties, + RestoreGlobalProperties, + RequestedTargets, + GetProperty, + GetItem, + GetTargetResult, + GetResultOutputFile, + Verbosity, + OtherMSBuildArgs.ToArray()); } - return new MSBuildArgs(GlobalProperties, RestoreGlobalProperties, RequestedTargets, Verbosity, [.. OtherMSBuildArgs, .. additionalArgs]); + return new MSBuildArgs( + GlobalProperties, + RestoreGlobalProperties, + RequestedTargets, + GetProperty, + GetItem, + GetTargetResult, + GetResultOutputFile, + Verbosity, + [.. OtherMSBuildArgs, .. additionalArgs]); } public MSBuildArgs CloneWithAdditionalRestoreProperties(ReadOnlyDictionary? additionalRestoreProperties) @@ -131,11 +188,29 @@ public MSBuildArgs CloneWithAdditionalRestoreProperties(ReadOnlyDictionary(RestoreGlobalProperties, StringComparer.OrdinalIgnoreCase); @@ -143,7 +218,16 @@ public MSBuildArgs CloneWithAdditionalRestoreProperties(ReadOnlyDictionary? additionalProperties) @@ -151,11 +235,29 @@ public MSBuildArgs CloneWithAdditionalProperties(ReadOnlyDictionary(GlobalProperties, StringComparer.OrdinalIgnoreCase); @@ -163,7 +265,16 @@ public MSBuildArgs CloneWithAdditionalProperties(ReadOnlyDictionary additionalTargets) @@ -171,12 +282,30 @@ public MSBuildArgs CloneWithAdditionalTargets(params ReadOnlySpan additi string[] newTargets = RequestedTargets is not null ? [.. RequestedTargets, .. additionalTargets] : [.. additionalTargets]; - return new MSBuildArgs(GlobalProperties, RestoreGlobalProperties, newTargets, Verbosity, OtherMSBuildArgs.ToArray()); + return new MSBuildArgs( + GlobalProperties, + RestoreGlobalProperties, + newTargets, + GetProperty, + GetItem, + GetTargetResult, + GetResultOutputFile, + Verbosity, + OtherMSBuildArgs.ToArray()); } public MSBuildArgs CloneWithVerbosity(VerbosityOptions newVerbosity) { - return new MSBuildArgs(GlobalProperties, RestoreGlobalProperties, RequestedTargets, newVerbosity, OtherMSBuildArgs.ToArray()); + return new MSBuildArgs( + GlobalProperties, + RestoreGlobalProperties, + RequestedTargets, + GetProperty, + GetItem, + GetTargetResult, + GetResultOutputFile, + newVerbosity, + OtherMSBuildArgs.ToArray()); } /// diff --git a/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs b/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs index 12bea5e09309..5b87474d7548 100644 --- a/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs +++ b/src/Cli/dotnet/Commands/Build/BuildCommandParser.cs @@ -92,6 +92,10 @@ private static Command ConstructCommand() command.Options.Add(CommonOptions.OperatingSystemOption); command.Options.Add(CommonOptions.DisableBuildServersOption); command.Options.Add(TargetOption); + command.Options.Add(CommonOptions.GetPropertyOption); + command.Options.Add(CommonOptions.GetItemOption); + command.Options.Add(CommonOptions.GetTargetResultOption); + command.Options.Add(CommonOptions.GetResultOutputFileOption); command.SetAction(BuildCommand.Run); diff --git a/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs b/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs index aff818886fe8..19b1854a1e16 100644 --- a/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs +++ b/src/Cli/dotnet/Commands/Clean/CleanCommandParser.cs @@ -60,6 +60,10 @@ private static Command ConstructCommand() command.Options.Add(NoLogoOption); command.Options.Add(CommonOptions.DisableBuildServersOption); command.Options.Add(TargetOption); + command.Options.Add(CommonOptions.GetPropertyOption); + command.Options.Add(CommonOptions.GetItemOption); + command.Options.Add(CommonOptions.GetTargetResultOption); + command.Options.Add(CommonOptions.GetResultOutputFileOption); command.Subcommands.Add(CleanFileBasedAppArtifactsCommandParser.Command); command.SetAction(CleanCommand.Run); diff --git a/src/Cli/dotnet/Commands/CommandFactory.cs b/src/Cli/dotnet/Commands/CommandFactory.cs index 016436103228..317770bf1f10 100644 --- a/src/Cli/dotnet/Commands/CommandFactory.cs +++ b/src/Cli/dotnet/Commands/CommandFactory.cs @@ -24,7 +24,14 @@ internal static CommandBase CreateVirtualOrPhysicalCommand( var forwardedArgs = parseResult.OptionValuesToBeForwarded(command); if (nonBinLogArgs is [{ } arg] && VirtualProjectBuildingCommand.IsValidEntryPointPath(arg)) { - var msbuildArgs = MSBuildArgs.AnalyzeMSBuildArguments([.. forwardedArgs, .. binLogArgs,], [.. optionsToUseWhenParsingMSBuildFlags]); + var msbuildArgs = MSBuildArgs.AnalyzeMSBuildArguments([.. forwardedArgs, .. binLogArgs], + [ + .. optionsToUseWhenParsingMSBuildFlags, + CommonOptions.GetPropertyOption, + CommonOptions.GetItemOption, + CommonOptions.GetTargetResultOption, + CommonOptions.GetResultOutputFileOption, + ]); return configureVirtualCommand(msbuildArgs, Path.GetFullPath(arg)); } else diff --git a/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs b/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs index eae738584119..c0ee6b1a8b41 100644 --- a/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs +++ b/src/Cli/dotnet/Commands/Pack/PackCommandParser.cs @@ -108,6 +108,10 @@ private static Command ConstructCommand() command.Options.Add(ConfigurationOption); command.Options.Add(CommonOptions.DisableBuildServersOption); command.Options.Add(TargetOption); + command.Options.Add(CommonOptions.GetPropertyOption); + command.Options.Add(CommonOptions.GetItemOption); + command.Options.Add(CommonOptions.GetTargetResultOption); + command.Options.Add(CommonOptions.GetResultOutputFileOption); // Don't include runtime option because we want to include it specifically and allow the short version ("-r") to be used RestoreCommandParser.AddImplicitRestoreOptions(command, includeRuntimeOption: false, includeNoDependenciesOption: true); diff --git a/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs b/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs index 8de1ebee7792..7be9784f5a15 100644 --- a/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs +++ b/src/Cli/dotnet/Commands/Publish/PublishCommandParser.cs @@ -90,6 +90,10 @@ private static Command ConstructCommand() command.Options.Add(CommonOptions.OperatingSystemOption); command.Options.Add(CommonOptions.DisableBuildServersOption); command.Options.Add(TargetOption); + command.Options.Add(CommonOptions.GetPropertyOption); + command.Options.Add(CommonOptions.GetItemOption); + command.Options.Add(CommonOptions.GetTargetResultOption); + command.Options.Add(CommonOptions.GetResultOutputFileOption); command.SetAction(PublishCommand.Run); diff --git a/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs b/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs index e986e495e2d3..5477584f5734 100644 --- a/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs +++ b/src/Cli/dotnet/Commands/Restore/RestoreCommandParser.cs @@ -74,6 +74,10 @@ private static Command ConstructCommand() command.Options.Add(CommonOptions.ArchitectureOption); command.Options.Add(CommonOptions.OperatingSystemOption); + command.Options.Add(CommonOptions.GetPropertyOption); + command.Options.Add(CommonOptions.GetItemOption); + command.Options.Add(CommonOptions.GetTargetResultOption); + command.Options.Add(CommonOptions.GetResultOutputFileOption); command.SetAction(RestoreCommand.Run); return command; diff --git a/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs b/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs index 974449429ecf..51a5d13d0603 100644 --- a/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs +++ b/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs @@ -16,6 +16,7 @@ using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Logging; +using Microsoft.Build.Logging.SimpleErrorLogger; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -112,10 +113,26 @@ public VirtualProjectBuildingCommand( { "RestoreUseSkipNonexistentTargets", bool.FalseString }, } .AsReadOnly()); + + if (MSBuildArgs.RequestedTargets is null or []) + { + RequestedTargets = MSBuildArgs.GetTargetResult; + } + else if (MSBuildArgs.GetTargetResult is null or []) + { + RequestedTargets = MSBuildArgs.RequestedTargets; + } + else + { + RequestedTargets = MSBuildArgs.RequestedTargets + .Union(MSBuildArgs.GetTargetResult, StringComparer.OrdinalIgnoreCase) + .ToArray(); + } } public string EntryPointFileFullPath { get; } public MSBuildArgs MSBuildArgs { get; } + private string[]? RequestedTargets { get; } public string? CustomArtifactsPath { get; init; } public string ArtifactsPath => field ??= CustomArtifactsPath ?? GetArtifactsPath(EntryPointFileFullPath); public bool NoRestore { get; init; } @@ -160,13 +177,37 @@ public ImmutableArray Directives public override int Execute() { + bool msbuildGet = MSBuildArgs.GetProperty is [_, ..] || MSBuildArgs.GetItem is [_, ..] || MSBuildArgs.GetTargetResult is [_, ..]; + bool evalOnly = msbuildGet && RequestedTargets is null or []; + bool minimizeStdOut = msbuildGet && MSBuildArgs.GetResultOutputFile is null or []; + var verbosity = MSBuildArgs.Verbosity ?? MSBuildForwardingAppWithoutLogging.DefaultVerbosity; - var consoleLogger = TerminalLogger.CreateTerminalOrConsoleLogger([$"--verbosity:{verbosity}", .. MSBuildArgs.OtherMSBuildArgs]); + var consoleLogger = minimizeStdOut + ? new SimpleErrorLogger() + : TerminalLogger.CreateTerminalOrConsoleLogger([$"--verbosity:{verbosity}", .. MSBuildArgs.OtherMSBuildArgs]); var binaryLogger = GetBinaryLogger(MSBuildArgs.OtherMSBuildArgs); CacheInfo? cache = null; - if (!NoBuild) + if (msbuildGet) + { + LastBuild = (BuildLevel.None, Cache: null); + } + else if (NoBuild) + { + // This is reached only during `restore`, not `run --no-build` + // (in the latter case, this virtual building command is not executed at all). + Debug.Assert(!NoRestore); + + LastBuild = (BuildLevel.None, Cache: null); + + if (!NoWriteBuildMarkers) + { + CreateTempSubdirectory(ArtifactsPath); + MarkArtifactsFolderUsed(); + } + } + else { if (NoCache) { @@ -230,22 +271,8 @@ public override int Execute() MarkBuildStart(); } - else // if (NoBuild) - { - // This is reached only during `restore`, not `run --no-build` - // (in the latter case, this virtual building command is not executed at all). - Debug.Assert(!NoRestore); - - LastBuild = (BuildLevel.None, Cache: null); - if (!NoWriteBuildMarkers) - { - CreateTempSubdirectory(ArtifactsPath); - MarkArtifactsFolderUsed(); - } - } - - if (!NoWriteBuildMarkers) + if (!NoWriteBuildMarkers && !msbuildGet) { CleanFileBasedAppArtifactsCommand.StartAutomaticCleanupIfNeeded(); } @@ -275,10 +302,14 @@ public override int Execute() BuildManager.DefaultBuildManager.BeginBuild(parameters); + int exitCode = 0; + ProjectInstance? projectInstance = null; + BuildResult? buildOrRestoreResult = null; + // Do a restore first (equivalent to MSBuild's "implicit restore", i.e., `/restore`). // See https://github.com/dotnet/msbuild/blob/a1c2e7402ef0abe36bf493e395b04dd2cb1b3540/src/MSBuild/XMake.cs#L1838 // and https://github.com/dotnet/msbuild/issues/11519. - if (!NoRestore) + if (!NoRestore && !evalOnly) { var restoreRequest = new BuildRequestData( CreateProjectInstance(projectCollection, addGlobalProperties: AddRestoreGlobalProperties(MSBuildArgs.RestoreGlobalProperties)), @@ -289,36 +320,52 @@ public override int Execute() var restoreResult = BuildManager.DefaultBuildManager.BuildRequest(restoreRequest); if (restoreResult.OverallResult != BuildResultCode.Success) { - return 1; + exitCode = 1; } + + projectInstance = restoreRequest.ProjectInstance; + buildOrRestoreResult = restoreResult; } // Then do a build. - if (!NoBuild) + if (exitCode == 0 && !NoBuild && !evalOnly) { var buildRequest = new BuildRequestData( CreateProjectInstance(projectCollection), - targetsToBuild: MSBuildArgs.RequestedTargets ?? ["Build"]); + targetsToBuild: RequestedTargets ?? []); var buildResult = BuildManager.DefaultBuildManager.BuildRequest(buildRequest); if (buildResult.OverallResult != BuildResultCode.Success) { - return 1; + exitCode = 1; } - Debug.Assert(cache != null); - Debug.Assert(buildRequest.ProjectInstance != null); + if (exitCode == 0 && !msbuildGet) + { + Debug.Assert(cache != null); + Debug.Assert(buildRequest.ProjectInstance != null); + + // Cache run info (to avoid re-evaluating the project instance). + cache.CurrentEntry.Run = RunProperties.FromProject(buildRequest.ProjectInstance); + + MarkBuildSuccess(cache); + } - // Cache run info (to avoid re-evaluating the project instance). - cache.CurrentEntry.Run = RunProperties.FromProject(buildRequest.ProjectInstance); + projectInstance = buildRequest.ProjectInstance; + buildOrRestoreResult = buildResult; + } - MarkBuildSuccess(cache); + // Print build information. + if (msbuildGet) + { + projectInstance ??= CreateProjectInstance(projectCollection); + PrintBuildInformation(projectCollection, projectInstance, buildOrRestoreResult); } BuildManager.DefaultBuildManager.EndBuild(); consoleLogger = null; // avoid double disposal which would throw - return 0; + return exitCode; } catch (Exception e) { @@ -385,6 +432,126 @@ static Action> AddRestoreGlobalProperties(ReadOnlyDi return null; } + + void PrintBuildInformation(ProjectCollection projectCollection, ProjectInstance projectInstance, BuildResult? buildOrRestoreResult) + { + var resultOutputFile = MSBuildArgs.GetResultOutputFile is [{ } file, ..] ? file : null; + + // If a single property is requested, don't print as JSON. + if (MSBuildArgs is { GetProperty: [{ } singlePropertyName], GetItem: null or [], GetTargetResult: null or [] }) + { + var result = projectInstance.GetPropertyValue(singlePropertyName); + if (resultOutputFile == null) + { + Console.WriteLine(result); + } + else + { + File.WriteAllText(path: resultOutputFile, contents: result + Environment.NewLine); + } + } + else + { + using var stream = resultOutputFile == null + ? Console.OpenStandardOutput() + : new FileStream(resultOutputFile, FileMode.Create, FileAccess.Write, FileShare.Read); + using var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }); + writer.WriteStartObject(); + + if (MSBuildArgs.GetProperty is [_, ..]) + { + writer.WritePropertyName("Properties"); + writer.WriteStartObject(); + + foreach (var propertyName in MSBuildArgs.GetProperty) + { + writer.WriteString(propertyName, projectInstance.GetPropertyValue(propertyName)); + } + + writer.WriteEndObject(); + } + + if (MSBuildArgs.GetItem is [_, ..]) + { + writer.WritePropertyName("Items"); + writer.WriteStartObject(); + + foreach (var itemName in MSBuildArgs.GetItem) + { + writer.WritePropertyName(itemName); + writer.WriteStartArray(); + + foreach (var item in projectInstance.GetItems(itemName)) + { + writer.WriteStartObject(); + writer.WriteString("Identity", item.GetMetadataValue("Identity")); + + foreach (var metadatumName in item.MetadataNames) + { + if (metadatumName.Equals("Identity", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + writer.WriteString(metadatumName, item.GetMetadataValue(metadatumName)); + } + + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + } + + writer.WriteEndObject(); + } + + if (MSBuildArgs.GetTargetResult is [_, ..]) + { + Debug.Assert(buildOrRestoreResult != null); + + writer.WritePropertyName("TargetResults"); + writer.WriteStartObject(); + + foreach (var targetName in MSBuildArgs.GetTargetResult) + { + var targetResult = buildOrRestoreResult.ResultsByTarget[targetName]; + + writer.WritePropertyName(targetName); + writer.WriteStartObject(); + writer.WriteString("Result", targetResult.TargetResultCodeToString()); + writer.WritePropertyName("Items"); + writer.WriteStartArray(); + + foreach (var item in targetResult.Items) + { + writer.WriteStartObject(); + writer.WriteString("Identity", item.GetMetadata("Identity")); + + foreach (string metadatumName in item.MetadataNames) + { + if (metadatumName.Equals("Identity", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + writer.WriteString(metadatumName, item.GetMetadata(metadatumName)); + } + + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + writer.WriteEndObject(); + } + + writer.WriteEndObject(); + } + + writer.WriteEndObject(); + writer.Flush(); + stream.Write(Encoding.UTF8.GetBytes(Environment.NewLine)); + } + } } /// @@ -753,7 +920,7 @@ ProjectRootElement CreateProjectRootElement(ProjectCollection projectCollection) isVirtualProject: true, targetFilePath: EntryPointFileFullPath, artifactsPath: ArtifactsPath, - includeRuntimeConfigInformation: MSBuildArgs.RequestedTargets?.ContainsAny("Publish", "Pack") != true); + includeRuntimeConfigInformation: RequestedTargets?.ContainsAny("Publish", "Pack") != true); var projectFileText = projectFileWriter.ToString(); using var reader = new StringReader(projectFileText); diff --git a/src/Cli/dotnet/Commands/Test/MTP/IPC/NamedPipeServer.cs b/src/Cli/dotnet/Commands/Test/MTP/IPC/NamedPipeServer.cs index 1a15a1401bf2..b06261dde858 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/IPC/NamedPipeServer.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/IPC/NamedPipeServer.cs @@ -12,6 +12,8 @@ namespace Microsoft.DotNet.Cli.Commands.Test.IPC; internal sealed class NamedPipeServer : NamedPipeBase { + private static bool IsUnix => Path.DirectorySeparatorChar == '/'; + private readonly Func> _callback; private readonly NamedPipeServerStream _namedPipeServerStream; private readonly CancellationToken _cancellationToken; @@ -24,20 +26,18 @@ internal sealed class NamedPipeServer : NamedPipeBase private bool _disposed; public NamedPipeServer( - PipeNameDescription pipeNameDescription, + string pipeName, Func> callback, int maxNumberOfServerInstances, CancellationToken cancellationToken, bool skipUnknownMessages) { - _namedPipeServerStream = new((PipeName = pipeNameDescription).Name, PipeDirection.InOut, maxNumberOfServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly); + _namedPipeServerStream = new(pipeName, PipeDirection.InOut, maxNumberOfServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly); _callback = callback; _cancellationToken = cancellationToken; _skipUnknownMessages = skipUnknownMessages; } - public PipeNameDescription PipeName { get; private set; } - public bool WasConnected { get; private set; } public async Task WaitConnectionAsync(CancellationToken cancellationToken) @@ -183,22 +183,15 @@ private async Task InternalLoopAsync(CancellationToken cancellationToken) } } - public static PipeNameDescription GetPipeName(string name) + public static string GetPipeName(string name) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (!IsUnix) { - return new PipeNameDescription($"testingplatform.pipe.{name.Replace('\\', '.')}", false); + return $"testingplatform.pipe.{name.Replace('\\', '.')}"; } - string directoryId = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), name); - Directory.CreateDirectory(directoryId); - return new PipeNameDescription( - !Directory.Exists(directoryId) - ? throw new DirectoryNotFoundException(string.Format( - CultureInfo.InvariantCulture, - $"Directory: {directoryId} doesn't exist.", - directoryId)) - : Path.Combine(directoryId, ".p"), true); + // Similar to https://github.com/dotnet/roslyn/blob/99bf83c7bc52fa1ff27cf792db38755d5767c004/src/Compilers/Shared/NamedPipeUtil.cs#L26-L42 + return Path.Combine("/tmp", name); } public void Dispose() @@ -227,8 +220,6 @@ public void Dispose() // Ensure we are still disposing the resouces correctly, even if _loopTask completes with // an exception, or if the task doesn't complete within the 90 seconds limit. _namedPipeServerStream.Dispose(); - PipeName.Dispose(); - _disposed = true; } } diff --git a/src/Cli/dotnet/Commands/Test/MTP/IPC/PipeNameDescription.cs b/src/Cli/dotnet/Commands/Test/MTP/IPC/PipeNameDescription.cs deleted file mode 100644 index f4de989a6dd6..000000000000 --- a/src/Cli/dotnet/Commands/Test/MTP/IPC/PipeNameDescription.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#nullable disable - -namespace Microsoft.DotNet.Cli.Commands.Test.IPC; - -internal sealed class PipeNameDescription(string name, bool isDirectory) : IDisposable -{ - private readonly bool _isDirectory = isDirectory; - private bool _disposed; - - public string Name { get; } = name; - - public void Dispose() - { - if (_disposed) - { - return; - } - - if (_isDirectory) - { - try - { - Directory.Delete(Path.GetDirectoryName(Name)!, true); - } - catch (IOException) - { - // This folder is created inside the temp directory and will be cleaned up eventually by the OS - } - } - - _disposed = true; - } -} diff --git a/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs b/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs index 7b088ab8fb03..f830f3896f2c 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs @@ -25,7 +25,7 @@ internal sealed class TestApplication( private readonly List _outputData = []; private readonly List _errorData = []; - private readonly PipeNameDescription _pipeNameDescription = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); + private readonly string _pipeName = NamedPipeServer.GetPipeName(Guid.NewGuid().ToString("N")); private readonly CancellationTokenSource _cancellationToken = new(); private Task _testAppPipeConnectionLoop; @@ -140,7 +140,7 @@ private string GetArguments() builder.Append($" {ArgumentEscaper.EscapeSingleArg(arg)}"); } - builder.Append($" {CliConstants.ServerOptionKey} {CliConstants.ServerOptionValue} {CliConstants.DotNetTestPipeOptionKey} {ArgumentEscaper.EscapeSingleArg(_pipeNameDescription.Name)}"); + builder.Append($" {CliConstants.ServerOptionKey} {CliConstants.ServerOptionValue} {CliConstants.DotNetTestPipeOptionKey} {ArgumentEscaper.EscapeSingleArg(_pipeName)}"); return builder.ToString(); } @@ -157,7 +157,7 @@ private async Task WaitConnectionAsync(CancellationToken token) { while (!token.IsCancellationRequested) { - var pipeConnection = new NamedPipeServer(_pipeNameDescription, OnRequest, NamedPipeServerStream.MaxAllowedServerInstances, token, skipUnknownMessages: true); + var pipeConnection = new NamedPipeServer(_pipeName, OnRequest, NamedPipeServerStream.MaxAllowedServerInstances, token, skipUnknownMessages: true); pipeConnection.RegisterAllSerializers(); await pipeConnection.WaitConnectionAsync(token); diff --git a/src/Cli/dotnet/CommonOptions.cs b/src/Cli/dotnet/CommonOptions.cs index df09c2853540..3823c25d1250 100644 --- a/src/Cli/dotnet/CommonOptions.cs +++ b/src/Cli/dotnet/CommonOptions.cs @@ -72,7 +72,7 @@ internal static class CommonOptions Description = "Build these targets in this project. Use a semicolon or a comma to separate multiple targets, or specify each target separately.", HelpName = "TARGET", DefaultValueFactory = _ => defaultTargetName is not null ? [defaultTargetName] : null, - CustomParser = r => SplitMSBuildTargets(defaultTargetName, r), + CustomParser = r => SplitMSBuildValues(defaultTargetName, r), Hidden = true, Arity = ArgumentArity.ZeroOrMore } @@ -85,7 +85,7 @@ public static Option RequiredMSBuildTargetOption(string defaultTargetN Description = "Build these targets in this project. Use a semicolon or a comma to separate multiple targets, or specify each target separately.", HelpName = "TARGET", DefaultValueFactory = _ => [defaultTargetName], - CustomParser = r => SplitMSBuildTargets(defaultTargetName, r), + CustomParser = r => SplitMSBuildValues(defaultTargetName, r), Hidden = true, Arity = ArgumentArity.ZeroOrMore } @@ -107,18 +107,36 @@ public static IEnumerable ForwardTargetsAndAdditionalProperties(string[] return argsToReturn; } - public static string[] SplitMSBuildTargets(string? defaultTargetName, ArgumentResult argumentResult) + public static readonly Option GetPropertyOption = MSBuildMultiOption("getProperty"); + + public static readonly Option GetItemOption = MSBuildMultiOption("getItem"); + + public static readonly Option GetTargetResultOption = MSBuildMultiOption("getTargetResult"); + + public static readonly Option GetResultOutputFileOption = MSBuildMultiOption("getResultOutputFile"); + + private static Option MSBuildMultiOption(string name) + => new ForwardedOption($"--{name}", $"-{name}", $"/{name}") + { + Hidden = true, + Arity = ArgumentArity.OneOrMore, + CustomParser = static r => SplitMSBuildValues(null, r), + } + .ForwardAsMany(xs => (xs ?? []).Select(x => $"--{name}:{x}")) + .AllowSingleArgPerToken(); + + public static string[] SplitMSBuildValues(string? defaultValue, ArgumentResult argumentResult) { if (argumentResult.Tokens.Count == 0) { - return defaultTargetName is not null ? [defaultTargetName] : []; + return defaultValue is not null ? [defaultValue] : []; } - var userTargets = + var userValues = argumentResult.Tokens.Select(t => t.Value) .SelectMany(t => t.Split([';', ','], StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) .Where(t => !string.IsNullOrEmpty(t)); - var allTargets = defaultTargetName is null ? userTargets : [defaultTargetName, .. userTargets]; - return allTargets.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); + var allValues = defaultValue is null ? userValues : [defaultValue, .. userValues]; + return allValues.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); } public static Option VerbosityOption(VerbosityOptions defaultVerbosity) => diff --git a/src/Layout/pkg/windows/bundles/sdk/bundle.thm b/src/Layout/pkg/windows/bundles/sdk/bundle.thm index b5856671a140..0b8a551e1168 100644 --- a/src/Layout/pkg/windows/bundles/sdk/bundle.thm +++ b/src/Layout/pkg/windows/bundles/sdk/bundle.thm @@ -95,11 +95,11 @@ - #(loc.FirstTimeWelcomeMessage) - #(loc.NetDocumentationLinkText) - #(loc.SdkDocumentationLinkText) - #(loc.ReleaseNotesLinkText) - #(loc.TutorialsLinkText) + #(loc.FirstTimeWelcomeMessage) + #(loc.NetDocumentationLinkText) + #(loc.SdkDocumentationLinkText) + #(loc.ReleaseNotesLinkText) + #(loc.TutorialsLinkText)