diff --git a/.github/policies/resourceManagement.yml b/.github/policies/resourceManagement.yml index e740ad8c75f5..806e13e53edc 100644 --- a/.github/policies/resourceManagement.yml +++ b/.github/policies/resourceManagement.yml @@ -1,10 +1,10 @@ -id: +id: name: GitOps.PullRequestIssueManagement description: GitOps.PullRequestIssueManagement primitive -owner: +owner: resource: repository disabled: false -where: +where: configuration: resourceManagementConfiguration: scheduledSearches: @@ -90,7 +90,7 @@ configuration: label: needs-breaking-change-doc-created - addReply: reply: >- - Added `needs-breaking-change-doc-created` label because this PR has the `breaking-change` label. + Added `needs-breaking-change-doc-created` label because this PR has the `breaking-change` label. When you commit this breaking change: @@ -103,5 +103,23 @@ configuration: You can refer to the [.NET SDK breaking change guidelines](https://github.com/dotnet/sdk/blob/main/documentation/project-docs/breaking-change-guidelines.md) description: Add breaking change instructions to PR. -onFailure: -onSuccess: + - description: Remind Telemetry PR authors of the telemetry guidelines + if: + - payloadType: Pull_Request + - labelAdded: + label: Area-Telemetry + then: + - addReply: + reply: >- + This PR has been labeled with `Area-Telemetry`. Please ensure that any telemetry changes in this PR + + * comply with the [published guidance](https://learn.microsoft.com/dotnet/core/tools/telemetry#data-points) + * are added to the [repo-local telemetry documentation](./documentation/project-docs/telemetry.md) + * get a matching .NET Docs issue raised to document the telemetry changes + * Go to https://learn.microsoft.com/dotnet/core/tools/telemetry + * Scroll down to the bottom and click the 'Open a documentation issue' link to create an issue with pre-filled details + - requestReview: + reviewer: dsplaisted + +onFailure: +onSuccess: diff --git a/.vsts-ci.yml b/.vsts-ci.yml index 429eaf993572..f42c5e490e2e 100644 --- a/.vsts-ci.yml +++ b/.vsts-ci.yml @@ -102,6 +102,7 @@ extends: oneESCompat: templateFolderName: templates-official publishTaskPrefix: 1ES. + populateInternalRuntimeVariables: true runtimeSourceProperties: /p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) locBranch: release/10.0.1xx # WORKAROUND: BinSkim requires the folder exist prior to scanning. @@ -141,6 +142,14 @@ extends: _SignType: real dependsOn: Official_windows_x64 downloadManifestMsiPackages: true + ### TestTemplatesCG ### + # Note: This job is only used to allow the test templates to be built locally on the agent as opposed to Helix. + # The tests acquire the templates' PackageReferences from NuGet, which allows them to be scanned by CG (component governance). + # CG is only ran internally, so this job makes sense to only run alongside of the official jobs. + - categoryName: TestTemplatesCG + testProjects: $(Build.SourcesDirectory)/test/dotnet-new.IntegrationTests/dotnet-new.IntegrationTests.csproj + testRunnerAdditionalArguments: -class Microsoft.DotNet.Cli.New.IntegrationTests.DotnetNewTestTemplatesTests + publishXunitResults: true ############### LINUX ############### - template: /eng/pipelines/templates/jobs/sdk-job-matrix.yml@self @@ -153,6 +162,7 @@ extends: oneESCompat: templateFolderName: templates-official publishTaskPrefix: 1ES. + populateInternalRuntimeVariables: true runtimeSourceProperties: /p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) ${{ if and(eq(parameters.runTestBuild, false), ne(variables['Build.Reason'], 'PullRequest')) }}: timeoutInMinutes: 90 @@ -235,6 +245,7 @@ extends: oneESCompat: templateFolderName: templates-official publishTaskPrefix: 1ES. + populateInternalRuntimeVariables: true runtimeSourceProperties: /p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) ${{ if and(eq(parameters.runTestBuild, false), ne(variables['Build.Reason'], 'PullRequest')) }}: timeoutInMinutes: 90 @@ -260,6 +271,8 @@ extends: vmImage: macOS-latest os: macOS helixTargetQueue: osx.13.arm64 + populateInternalRuntimeVariables: true + runtimeSourceProperties: /p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) macOSJobParameterSets: - categoryName: TestBuild targetArchitecture: arm64 @@ -270,7 +283,9 @@ extends: - template: /eng/dotnet-format/dotnet-format-integration.yml@self parameters: oneESCompat: + templateFolderName: templates-official publishTaskPrefix: 1ES. + populateInternalRuntimeVariables: true runtimeSourceProperties: /p:DotNetRuntimeSourceFeed=https://ci.dot.net/internal /p:DotNetRuntimeSourceFeedKey=$(dotnetbuilds-internal-container-read-token-base64) ############### PUBLISH STAGE ############### diff --git a/CODEOWNERS b/CODEOWNERS index a7e47c14ebd5..4591eb1ff8d7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -31,8 +31,8 @@ /src/WasmSdk @lewing @akoeplinger @pavelsavara @maraf # Area-Format -/src/Cli/dotnet/commands/dotnet-format @arunchndr -/test/dotnet-format.UnitTests @arunchndr +/src/Cli/dotnet/commands/dotnet-format @phil-allen-msft +/test/dotnet-format.UnitTests @phil-allen-msft # Area-NuGet /src/Cli/dotnet/Commands/NuGet @dotnet/nuget-team @@ -77,10 +77,10 @@ /src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.ClickOnce.targets @sujitnayak # Area-Watch -/test/TestAssets/TestProjects/Watch*/ @tmat @arunchndr @dotnet/roslyn-ide -/test/dotnet-watch.Tests/ @tmat @arunchndr @dotnet/roslyn-ide +/test/TestAssets/TestProjects/Watch*/ @tmat @dotnet/roslyn-ide +/test/dotnet-watch.Tests/ @tmat @dotnet/roslyn-ide /test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/ @dotnet/aspnet-blazor-eng -/src/BuiltInTools/* @tmat @arunchndr @dotnet/roslyn-ide +/src/BuiltInTools/* @tmat @dotnet/roslyn-ide /src/BuiltInTools/BrowserRefresh @dotnet/aspnet-blazor-eng /src/BuiltInTools/AspireService @dotnet/aspnet-blazor-eng diff --git a/Directory.Build.targets b/Directory.Build.targets index 2d510c42fc18..2b77ecc8829b 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -72,7 +72,7 @@ $(MicrosoftAspNetCoreAppRefPackageVersion) - ${SupportedRuntimeIdentifiers} + $(SupportedRuntimeIdentifiers) $(MicrosoftAspNetCoreAppRefPackageVersion) $(MicrosoftAspNetCoreAppRefPackageVersion) diff --git a/Directory.Packages.props b/Directory.Packages.props index 788923bf48d6..ecc47ba3f164 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,10 +5,11 @@ $(NoWarn);NU1507 - + + @@ -93,7 +94,7 @@ - + @@ -101,6 +102,7 @@ + diff --git a/build/RunTestTemplateTests.ps1 b/build/RunTestTemplateTests.ps1 deleted file mode 100644 index 5fe903b77f34..000000000000 --- a/build/RunTestTemplateTests.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -<# -.SYNOPSIS - Runs Microsoft.TestTemplates.Acceptance.Tests.dll in the dogfood environment. -.DESCRIPTION - This script enters the dogfood environment and runs the RunTestTemplateTests tests. -#> -[CmdletBinding(PositionalBinding=$false)] -Param( - [string] $configuration = "Release" -) - -function Run-TestTemplateTests { - $ErrorActionPreference = 'Stop' - $RepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..') - $classNameFilter = "--filter" - $filterValue = "FullyQualifiedName~Microsoft.DotNet.Cli.New.IntegrationTests.DotnetNewTestTemplatesTests" - $TestDll = Join-Path $RepoRoot "artifacts\bin\dotnet-new.IntegrationTests\$configuration\dotnet-new.IntegrationTests.dll" - - # Check if the test DLL exists - if (-not (Test-Path $TestDll)) { - Write-Error "Test DLL not found at: $TestDll" - return 1 - } - - Write-Host "Running tests for test templates in the dogfood environment..." -ForegroundColor Cyan - - # Call dogfood.ps1 directly instead of through dogfood.cmd to avoid the -NoExit parameter - $dogfoodPs1 = Join-Path $RepoRoot "eng\dogfood.ps1" - - Write-Host "Executing: dotnet test $TestDll via dogfood environment" -ForegroundColor Gray - # Pass the command directly to the dogfood.ps1 script - & $dogfoodPs1 -configuration $configuration -command @("dotnet", "test", $TestDll, $classNameFilter, $filterValue) - - $exitCode = $LASTEXITCODE - if ($exitCode -ne 0) { - Write-Error "Tests failed with exit code: $exitCode" - } else { - Write-Host "Tests completed successfully!" -ForegroundColor Green - } - - return $exitCode -} - -# Execute the function using Invoke-Command -$exitCode = Invoke-Command -ScriptBlock ${function:Run-TestTemplateTests} -exit $exitCode diff --git a/documentation/general/analyzer-redirecting.md b/documentation/general/analyzer-redirecting.md index 208c28734787..e999b98d76fe 100644 --- a/documentation/general/analyzer-redirecting.md +++ b/documentation/general/analyzer-redirecting.md @@ -28,21 +28,36 @@ Targeting an SDK (and hence also loading analyzers) with newer major version in - Note that when `IAnalyzerAssemblyRedirector` is involved, Roslyn is free to not use shadow copy loading and instead load the DLLs directly. +- It is possible to opt out of analyzer redirecting by setting environment variable `DOTNET_ANALYZER_REDIRECTING=0`. + That is an unsupported scenario though and compiler version mismatch errors will likely occur. + ## Details The VSIX contains some analyzers, for example: ``` -AspNetCoreAnalyzers\9.0.0-preview.5.24306.11\analyzers\dotnet\cs\Microsoft.AspNetCore.App.Analyzers.dll -NetCoreAnalyzers\9.0.0-preview.5.24306.7\analyzers\dotnet\cs\System.Text.RegularExpressions.Generator.dll -WindowsDesktopAnalyzers\9.0.0-preview.5.24306.8\analyzers\dotnet\System.Windows.Forms.Analyzers.dll -SDKAnalyzers\9.0.100-dev\Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll -WebSDKAnalyzers\9.0.100-dev\Sdks\Microsoft.NET.Sdk.Web\analyzers\cs\Microsoft.AspNetCore.Analyzers.dll +AspNetCoreAnalyzers\analyzers\dotnet\cs\Microsoft.AspNetCore.App.Analyzers.dll +NetCoreAnalyzers\analyzers\dotnet\cs\System.Text.RegularExpressions.Generator.dll +WindowsDesktopAnalyzers\analyzers\dotnet\System.Windows.Forms.Analyzers.dll +SDKAnalyzers\Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll +WebSDKAnalyzers\Sdks\Microsoft.NET.Sdk.Web\analyzers\cs\Microsoft.AspNetCore.Analyzers.dll +``` + +And metadata at `metadata.json`: + +```json +{ + "AspNetCoreAnalyzers": "9.0.0-preview.5.24306.11", + "NetCoreAnalyzers": "9.0.0-preview.5.24306.7", + "WindowsDesktopAnalyzers": "9.0.0-preview.5.24306.8", + "SDKAnalyzers": "9.0.100-dev", + "WebSDKAnalyzers": "9.0.100-dev", +} ``` Given an analyzer assembly load going through our `IAnalyzerAssemblyRedirector`, we will redirect it if the original path of the assembly being loaded matches the path of a VSIX-deployed analyzer - -only segments of these paths starting after the version segment are compared, +only relevant segments (see example below) of these paths are compared, plus the major and minor component of the versions must match. For example, the analyzer @@ -54,10 +69,10 @@ C:\Program Files\dotnet\sdk\9.0.100-preview.5.24307.3\Sdks\Microsoft.NET.Sdk\ana will be redirected to ``` -{VSIX}\SDKAnalyzers\9.0.100-dev\Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll +{VSIX}\SDKAnalyzers\Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll ``` -because +where `metadata.json` has `"SDKAnalyzers": "9.0.100-dev"`, because 1. the suffix `Sdks\Microsoft.NET.Sdk\analyzers\Microsoft.CodeAnalysis.NetAnalyzers.dll` matches, and 2. the version `9.0.100-preview.5.24307.3` has the same major and minor component (`9.0`) as the version `9.0.100-dev` (both versions are read from the paths, not DLL metadata). @@ -65,4 +80,9 @@ because Analyzers that cannot be matched will continue to be loaded from the SDK (and will fail to load if they reference Roslyn that is newer than is in VS). +### Implementation + +Analyzer DLLs are contained in transport package `VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers`. +The redirecting logic lives in "system" VS extension `Microsoft.Net.Sdk.AnalyzerRedirecting`. + [torn-sdk]: https://github.com/dotnet/sdk/issues/42087 diff --git a/documentation/general/dotnet-run-file.md b/documentation/general/dotnet-run-file.md index 8111a435800f..41bb96b5f6a2 100644 --- a/documentation/general/dotnet-run-file.md +++ b/documentation/general/dotnet-run-file.md @@ -32,6 +32,8 @@ Additionally, the implicit project file has the following customizations: - `PublishAot` is set to `true`, see [`dotnet publish file.cs`](#other-commands) for more details. +- `UserSecretsId` is set to a hash of the entry point file path. + - [File-level directives](#directives-for-project-metadata) are applied. - The following are virtual only, i.e., not preserved after [converting to a project](#grow-up): @@ -287,7 +289,8 @@ The build is performed using MSBuild APIs on in-memory project files. If an up-to-date check detects that inputs didn't change in subsequent `dotnet run file.cs` invocations, building is skipped (as if `--no-build` option has been passed). The up-to-date check is not 100% precise (e.g., files imported through an implicit build file are not considered). -It is possible to enforce a full build using `--no-cache` flag or `dotnet build file.cs`. +It is possible to enforce a full build using `--no-cache` flag or `dotnet build file.cs` +(for a more permanent opt-out, there is MSBuild property `FileBasedProgramCanSkipMSBuild=false`). Environment variable [`DOTNET_CLI_CONTEXT_VERBOSE=true`][verbose-env] can be used to get more details about caching decisions made by `dotnet run file.cs`. There are multiple optimization levels - skipping build altogether, running just the C# compiler, or running full MSBuild. @@ -295,8 +298,7 @@ We always need to re-run MSBuild if implicit build files like `Directory.Build.p from `.cs` files, the only relevant MSBuild inputs are the `#:` directives, hence we can first check the `.cs` file timestamps and for those that have changed, compare the sets of `#:` directives. If only `.cs` files change, it is enough to invoke `csc.exe` (directly or via a build server) -re-using command-line arguments that the last MSBuild invocation passed to the compiler -(you can opt out of this via an MSBuild property `FileBasedProgramCanSkipMSBuild=false`). +re-using command-line arguments that the last MSBuild invocation passed to the compiler. If no inputs change, it is enough to start the target executable without invoking the build at all. ## Alternatives and future work diff --git a/documentation/manpages/sdk/dotnet-build.1 b/documentation/manpages/sdk/dotnet-build.1 index 99fe1e657990..fb7ce5950153 100644 --- a/documentation/manpages/sdk/dotnet-build.1 +++ b/documentation/manpages/sdk/dotnet-build.1 @@ -14,19 +14,19 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-build" "1" "2025-06-13" "" ".NET Documentation" +.TH "dotnet-build" "1" "2025-09-30" "" ".NET Documentation" .hy .SH dotnet build .PP -\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET Core 3.1 SDK and later versions +\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET 6 and later versions .SH NAME .PP -dotnet-build - Builds a project and all of its dependencies. +dotnet-build - Builds a project, solution, or file-based app and all of its dependencies. .SH SYNOPSIS .IP .nf \f[C] -dotnet build [|] [-a|--arch ] +dotnet build [||] [-a|--arch ] [--artifacts-path ] [-c|--configuration ] [-f|--framework ] [--disable-build-servers] @@ -35,7 +35,7 @@ dotnet build [|] [-a|--arch ] [-o|--output ] [-p|--property:=] [-r|--runtime ] - [--self-contained [true|false]] [--source ] + [-sc|--self-contained [true|false]] [--source ] [--tl:[auto|on|off]] [--use-current-runtime, --ucr [true|false]] [-v|--verbosity ] [--version-suffix ] @@ -44,11 +44,11 @@ dotnet build -h|--help .fi .SH DESCRIPTION .PP -The \f[V]dotnet build\f[R] command builds the project and its dependencies into a set of binaries. +The \f[V]dotnet build\f[R] command builds the project, solution, or file-based app and its dependencies into a set of binaries. The binaries include the project\[cq]s code in Intermediate Language (IL) files with a \f[I].dll\f[R] extension. Depending on the project type and settings, other files may be included, such as: .IP \[bu] 2 -An executable that can be used to run the application, if the project type is an executable targeting .NET Core 3.0 or later. +An executable that can be used to run the application. .IP \[bu] 2 Symbol files used for debugging with a \f[I].pdb\f[R] extension. .IP \[bu] 2 @@ -58,12 +58,6 @@ A \f[I].runtimeconfig.json\f[R] file, which specifies the shared runtime and its .IP \[bu] 2 Other libraries that the project depends on (via project references or NuGet package references). .PP -For executable projects targeting versions earlier than .NET Core 3.0, library dependencies from NuGet are typically NOT copied to the output folder. -They\[cq]re resolved from the NuGet global packages folder at run time. -With that in mind, the product of \f[V]dotnet build\f[R] isn\[cq]t ready to be transferred to another machine to run. -To create a version of the application that can be deployed, you need to publish it (for example, with the dotnet publish command). -For more information, see .NET Application Deployment. -.PP For executable projects targeting .NET Core 3.0 and later, library dependencies are copied to the output folder. This means that if there isn\[cq]t any other publish-specific logic (such as Web projects have), the build output should be deployable. .SS Implicit restore @@ -98,7 +92,8 @@ To produce a library, omit the \f[V]\f[R] property or change its val The IL DLL for a library doesn\[cq]t contain entry points and can\[cq]t be executed. .SS MSBuild .PP -\f[V]dotnet build\f[R] uses MSBuild to build the project, so it supports both parallel and incremental builds. +\f[V]dotnet build\f[R] uses MSBuild to build the project, solution, or file-based app. +It supports both parallel and incremental builds. For more information, see Incremental Builds. .PP In addition to its options, the \f[V]dotnet build\f[R] command accepts MSBuild options, such as \f[V]-p\f[R] for setting properties or \f[V]-l\f[R] to define a logger. @@ -117,10 +112,19 @@ If the download is still running when this command finishes, the download is sto For more information, see Advertising manifests. .SH ARGUMENTS .PP -\f[V]PROJECT | SOLUTION\f[R] +\f[V]PROJECT | SOLUTION | FILE\f[R] .PP -The project or solution file to build. -If a project or solution file isn\[cq]t specified, MSBuild searches the current working directory for a file that has a file extension that ends in either \f[I]proj\f[R] or \f[I]sln\f[R] and uses that file. +The project or solution or C# (file-based app) file to operate on. +If a file isn\[cq]t specified, MSBuild searches the current directory for a project or solution. +.IP \[bu] 2 +\f[V]PROJECT\f[R] is the path and filename of a C#, F#, or Visual Basic project file, or the path to a directory that contains a C#, F#, or Visual Basic project file. +.IP \[bu] 2 +\f[V]SOLUTION\f[R] is the path and filename of a solution file (\f[I].sln\f[R] or \f[I].slnx\f[R] extension), or the path to a directory that contains a solution file. +.IP \[bu] 2 +\f[V]FILE\f[R] is an argument added in .NET 10. +The path and filename of a file-based app. +File-based apps are contained within a single file that is built and run without a corresponding project (\f[I].csproj\f[R]) file. +For more information, see Build file-based C# apps. .SH OPTIONS .IP \[bu] 2 \f[B]\f[VB]-a|--arch \f[B]\f[R] @@ -285,13 +289,13 @@ The URI of the NuGet package source to use during the restore operation. \f[B]\f[VB]--tl:[auto|on|off]\f[B]\f[R] .RS 2 .PP -Specifies whether the \f[I]terminal logger\f[R] should be used for the build output. +Specifies whether \f[I]Terminal Logger\f[R] should be used for the build output. The default is \f[V]auto\f[R], which first verifies the environment before enabling terminal logging. The environment check verifies that the terminal is capable of using modern output features and isn\[cq]t using a redirected standard output before enabling the new logger. \f[V]on\f[R] skips the environment check and enables terminal logging. \f[V]off\f[R] skips the environment check and uses the default console logger. .PP -The terminal logger shows you the restore phase followed by the build phase. +Terminal Logger shows you the restore phase followed by the build phase. During each phase, the currently building projects appear at the bottom of the terminal. Each project that\[cq]s building outputs both the MSBuild target currently being built and the amount of time spent on that target. You can search this information to learn more about the build. @@ -348,6 +352,18 @@ dotnet build .fi .RE .IP \[bu] 2 +Build a file-based app: +.RS 2 +.IP +.nf +\f[C] +dotnet build MyProject.cs +\f[R] +.fi +.PP +File-based app support was added in .NET SDK 10.0.100. +.RE +.IP \[bu] 2 Build a project and its dependencies using Release configuration: .RS 2 .IP diff --git a/documentation/manpages/sdk/dotnet-clean.1 b/documentation/manpages/sdk/dotnet-clean.1 index a259f0fe2df5..d59683069976 100644 --- a/documentation/manpages/sdk/dotnet-clean.1 +++ b/documentation/manpages/sdk/dotnet-clean.1 @@ -14,11 +14,11 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-clean" "1" "2025-06-13" "" ".NET Documentation" +.TH "dotnet-clean" "1" "2025-09-30" "" ".NET Documentation" .hy .SH dotnet clean .PP -\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET Core 3.1 SDK and later versions +\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET 6 and later versions .SH NAME .PP dotnet-clean - Cleans the output of a project. @@ -26,7 +26,7 @@ dotnet-clean - Cleans the output of a project. .IP .nf \f[C] -dotnet clean [|] [--artifacts-path ] +dotnet clean [||] [--artifacts-path ] [-c|--configuration ] [-f|--framework ] [--interactive] [--nologo] [-o|--output ] @@ -44,10 +44,19 @@ Only the outputs created during the build are cleaned. Both intermediate (\f[I]obj\f[R]) and final output (\f[I]bin\f[R]) folders are cleaned. .SH ARGUMENTS .PP -\f[V]PROJECT | SOLUTION\f[R] +\f[V]PROJECT | SOLUTION | FILE\f[R] .PP -The MSBuild project or solution to clean. -If a project or solution file is not specified, MSBuild searches the current working directory for a file that has a file extension that ends in \f[I]proj\f[R] or \f[I]sln\f[R], and uses that file. +The project or solution or C# (file-based app) file to operate on. +If a file isn\[cq]t specified, MSBuild searches the current directory for a project or solution. +.IP \[bu] 2 +\f[V]PROJECT\f[R] is the path and filename of a C#, F#, or Visual Basic project file, or the path to a directory that contains a C#, F#, or Visual Basic project file. +.IP \[bu] 2 +\f[V]SOLUTION\f[R] is the path and filename of a solution file (\f[I].sln\f[R] or \f[I].slnx\f[R] extension), or the path to a directory that contains a solution file. +.IP \[bu] 2 +\f[V]FILE\f[R] is an argument added in .NET 10. +The path and filename of a file-based app. +File-based apps are contained within a single file that is built and run without a corresponding project (\f[I].csproj\f[R]) file. +For more information, see Build file-based C# apps. .SH OPTIONS .IP \[bu] 2 \f[B]\f[VB]--artifacts-path \f[B]\f[R] @@ -119,13 +128,13 @@ This is used when a self-contained deployment was created. \f[B]\f[VB]--tl:[auto|on|off]\f[B]\f[R] .RS 2 .PP -Specifies whether the \f[I]terminal logger\f[R] should be used for the build output. +Specifies whether \f[I]Terminal Logger\f[R] should be used for the build output. The default is \f[V]auto\f[R], which first verifies the environment before enabling terminal logging. The environment check verifies that the terminal is capable of using modern output features and isn\[cq]t using a redirected standard output before enabling the new logger. \f[V]on\f[R] skips the environment check and enables terminal logging. \f[V]off\f[R] skips the environment check and uses the default console logger. .PP -The terminal logger shows you the restore phase followed by the build phase. +Terminal Logger shows you the restore phase followed by the build phase. During each phase, the currently building projects appear at the bottom of the terminal. Each project that\[cq]s building outputs both the MSBuild target currently being built and the amount of time spent on that target. You can search this information to learn more about the build. @@ -164,6 +173,18 @@ dotnet clean .fi .RE .IP \[bu] 2 +Clean a file-based program: +.RS 2 +.IP +.nf +\f[C] +dotnet clean Program.cs. +\f[R] +.fi +.PP +File-based app support was added in .NET SDK 10.0.100. +.RE +.IP \[bu] 2 Clean a project built using the Release configuration: .RS 2 .IP diff --git a/documentation/manpages/sdk/dotnet-pack.1 b/documentation/manpages/sdk/dotnet-pack.1 index 729fa6810b4c..58e02e3e5683 100644 --- a/documentation/manpages/sdk/dotnet-pack.1 +++ b/documentation/manpages/sdk/dotnet-pack.1 @@ -15,7 +15,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-pack" "1" "2025-06-13" "" ".NET Documentation" +.TH "dotnet-pack" "1" "2025-09-30" "" ".NET Documentation" .hy .SH dotnet pack .PP @@ -202,13 +202,13 @@ For more information, see .NET Blog: .NET Framework 4.5.1 Supports Microsoft Sec \f[B]\f[VB]--tl:[auto|on|off]\f[B]\f[R] .RS 2 .PP -Specifies whether the \f[I]terminal logger\f[R] should be used for the build output. +Specifies whether \f[I]Terminal Logger\f[R] should be used for the build output. The default is \f[V]auto\f[R], which first verifies the environment before enabling terminal logging. The environment check verifies that the terminal is capable of using modern output features and isn\[cq]t using a redirected standard output before enabling the new logger. \f[V]on\f[R] skips the environment check and enables terminal logging. \f[V]off\f[R] skips the environment check and uses the default console logger. .PP -The terminal logger shows you the restore phase followed by the build phase. +Terminal Logger shows you the restore phase followed by the build phase. During each phase, the currently building projects appear at the bottom of the terminal. Each project that\[cq]s building outputs both the MSBuild target currently being built and the amount of time spent on that target. You can search this information to learn more about the build. diff --git a/documentation/manpages/sdk/dotnet-publish.1 b/documentation/manpages/sdk/dotnet-publish.1 index 64610ed45272..77da5ae6f4be 100644 --- a/documentation/manpages/sdk/dotnet-publish.1 +++ b/documentation/manpages/sdk/dotnet-publish.1 @@ -14,11 +14,11 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-publish" "1" "2025-08-29" "" ".NET Documentation" +.TH "dotnet-publish" "1" "2025-09-30" "" ".NET Documentation" .hy .SH dotnet publish .PP -\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET Core 3.1 SDK and later versions +\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET 6 and later versions .SH NAME .PP dotnet-publish - Publishes the application and its dependencies to a folder for deployment to a hosting system. @@ -26,7 +26,7 @@ dotnet-publish - Publishes the application and its dependencies to a folder for .IP .nf \f[C] -dotnet publish [|] [-a|--arch ] +dotnet publish [||] [-a|--arch ] [--artifacts-path ] [-c|--configuration ] [--disable-build-servers] [-f|--framework ] [--force] [--interactive] @@ -172,18 +172,20 @@ When you run this command, it initiates an asynchronous background download of a If the download is still running when this command finishes, the download is stopped. For more information, see Advertising manifests. .SH ARGUMENTS -.IP \[bu] 2 -\f[B]\f[VB]PROJECT|SOLUTION\f[B]\f[R] -.RS 2 .PP -The project or solution to publish. +\f[V]PROJECT | SOLUTION | FILE\f[R] +.PP +The project or solution or C# (file-based app) file to operate on. +If a file isn\[cq]t specified, MSBuild searches the current directory for a project or solution. .IP \[bu] 2 \f[V]PROJECT\f[R] is the path and filename of a C#, F#, or Visual Basic project file, or the path to a directory that contains a C#, F#, or Visual Basic project file. -If the directory is not specified, it defaults to the current directory. .IP \[bu] 2 \f[V]SOLUTION\f[R] is the path and filename of a solution file (\f[I].sln\f[R] or \f[I].slnx\f[R] extension), or the path to a directory that contains a solution file. -If the directory is not specified, it defaults to the current directory. -.RE +.IP \[bu] 2 +\f[V]FILE\f[R] is an argument added in .NET 10. +The path and filename of a file-based app. +File-based apps are contained within a single file that is built and run without a corresponding project (\f[I].csproj\f[R]) file. +For more information, see Build file-based C# apps. .SH OPTIONS .IP \[bu] 2 \f[B]\f[VB]-a|--arch \f[B]\f[R] @@ -320,15 +322,6 @@ If you specify a relative path when publishing a solution, all output for all pr To make publish output go to separate folders for each project, specify a relative path by using the msbuild \f[V]PublishDir\f[R] property instead of the \f[V]--output\f[R] option. For example, \f[V]dotnet publish -p:PublishDir=.\[rs]publish\f[R] sends publish output for each project to a \f[V]publish\f[R] folder under the folder that contains the project file. .RE -.IP \[bu] 2 -\&.NET Core 2.x SDK -.RS 2 -.PP -If you specify a relative path when publishing a project, the generated output directory is relative to the project file location, not to the current working directory. -.PP -If you specify a relative path when publishing a solution, each project\[cq]s output goes into a separate folder relative to the project file location. -If you specify an absolute path when publishing a solution, all publish output for all projects goes into the specified folder. -.RE .RE .IP \[bu] 2 \f[B]\f[VB]--os \f[B]\f[R] @@ -376,13 +369,13 @@ If you use this option, use \f[V]--self-contained\f[R] or \f[V]--no-self-contain \f[B]\f[VB]--tl:[auto|on|off]\f[B]\f[R] .RS 2 .PP -Specifies whether the \f[I]terminal logger\f[R] should be used for the build output. +Specifies whether \f[I]Terminal Logger\f[R] should be used for the build output. The default is \f[V]auto\f[R], which first verifies the environment before enabling terminal logging. The environment check verifies that the terminal is capable of using modern output features and isn\[cq]t using a redirected standard output before enabling the new logger. \f[V]on\f[R] skips the environment check and enables terminal logging. \f[V]off\f[R] skips the environment check and uses the default console logger. .PP -The terminal logger shows you the restore phase followed by the build phase. +Terminal Logger shows you the restore phase followed by the build phase. During each phase, the currently building projects appear at the bottom of the terminal. Each project that\[cq]s building outputs both the MSBuild target currently being built and the amount of time spent on that target. You can search this information to learn more about the build. @@ -491,6 +484,18 @@ dotnet publish --no-dependencies \f[R] .fi .RE +.IP \[bu] 2 +Publish the file-based C# program \f[I]app.cs\f[R] in the current directory: +.RS 2 +.IP +.nf +\f[C] +dotnet publish app.cs +\f[R] +.fi +.PP +File-based program support was added in .NET SDK 10.0.100. +.RE .SH SEE ALSO .IP \[bu] 2 \&.NET application publishing overview diff --git a/documentation/manpages/sdk/dotnet-restore.1 b/documentation/manpages/sdk/dotnet-restore.1 index 498f0b210e84..334fba4e4b17 100644 --- a/documentation/manpages/sdk/dotnet-restore.1 +++ b/documentation/manpages/sdk/dotnet-restore.1 @@ -14,11 +14,11 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-restore" "1" "2025-06-30" "" ".NET Documentation" +.TH "dotnet-restore" "1" "2025-09-30" "" ".NET Documentation" .hy .SH dotnet restore .PP -\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET Core 3.1 SDK and later versions +\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET 6 and later versions .SH NAME .PP dotnet-restore - Restores the dependencies and tools of a project. @@ -26,7 +26,7 @@ dotnet-restore - Restores the dependencies and tools of a project. .IP .nf \f[C] -dotnet restore [] [--configfile ] [--disable-build-servers] +dotnet restore [||] [--configfile ] [--disable-build-servers] [--disable-parallel] [-f|--force] [--force-evaluate] [--ignore-failed-sources] [--interactive] [--lock-file-path ] [--locked-mode] @@ -126,12 +126,20 @@ When you run this command, it initiates an asynchronous background download of a If the download is still running when this command finishes, the download is stopped. For more information, see Advertising manifests. .SH ARGUMENTS -.IP \[bu] 2 -\f[B]\f[VB]ROOT\f[B]\f[R] -.RS 2 .PP -Optional path to the project file to restore. -.RE +\f[V]PROJECT | SOLUTION | FILE\f[R] +.PP +The project or solution or C# (file-based app) file to operate on. +If a file isn\[cq]t specified, MSBuild searches the current directory for a project or solution. +.IP \[bu] 2 +\f[V]PROJECT\f[R] is the path and filename of a C#, F#, or Visual Basic project file, or the path to a directory that contains a C#, F#, or Visual Basic project file. +.IP \[bu] 2 +\f[V]SOLUTION\f[R] is the path and filename of a solution file (\f[I].sln\f[R] or \f[I].slnx\f[R] extension), or the path to a directory that contains a solution file. +.IP \[bu] 2 +\f[V]FILE\f[R] is an argument added in .NET 10. +The path and filename of a file-based app. +File-based apps are contained within a single file that is built and run without a corresponding project (\f[I].csproj\f[R]) file. +For more information, see Build file-based C# apps. .SH OPTIONS .IP \[bu] 2 \f[B]\f[VB]-a|--arch \f[B]\f[R] @@ -250,13 +258,13 @@ Multiple sources can be provided by specifying this option multiple times. \f[B]\f[VB]--tl:[auto|on|off]\f[B]\f[R] .RS 2 .PP -Specifies whether the \f[I]terminal logger\f[R] should be used for the build output. +Specifies whether \f[I]Terminal Logger\f[R] should be used for the build output. The default is \f[V]auto\f[R], which first verifies the environment before enabling terminal logging. The environment check verifies that the terminal is capable of using modern output features and isn\[cq]t using a redirected standard output before enabling the new logger. \f[V]on\f[R] skips the environment check and enables terminal logging. \f[V]off\f[R] skips the environment check and uses the default console logger. .PP -The terminal logger shows you the restore phase followed by the build phase. +Terminal Logger shows you the restore phase followed by the build phase. During each phase, the currently building projects appear at the bottom of the terminal. Each project that\[cq]s building outputs both the MSBuild target currently being built and the amount of time spent on that target. You can search this information to learn more about the build. diff --git a/documentation/manpages/sdk/dotnet-run.1 b/documentation/manpages/sdk/dotnet-run.1 index 65cf2e8d10a8..c37a294d9f89 100644 --- a/documentation/manpages/sdk/dotnet-run.1 +++ b/documentation/manpages/sdk/dotnet-run.1 @@ -14,11 +14,11 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-run" "1" "2025-06-13" "" ".NET Documentation" +.TH "dotnet-run" "1" "2025-09-30" "" ".NET Documentation" .hy .SH dotnet run .PP -\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET Core 3.1 SDK and later versions +\f[B]This article applies to:\f[R] \[u2714]\[uFE0F] .NET 6 and later versions .SH NAME .PP dotnet-run - Runs source code without any explicit compile or launch commands. @@ -26,8 +26,8 @@ dotnet-run - Runs source code without any explicit compile or launch commands. .IP .nf \f[C] -dotnet run [-a|--arch ] [-c|--configuration ] - [-e|--environment ] +dotnet run [] [-a|--arch ] [-c|--configuration ] + [-e|--environment ] [--file ] [-f|--framework ] [--force] [--interactive] [--launch-profile ] [--no-build] [--no-dependencies] [--no-launch-profile] [--no-restore] @@ -87,6 +87,14 @@ Short form options, such as \f[V]-s\f[R], are not supported. When you run this command, it initiates an asynchronous background download of advertising manifests for workloads. If the download is still running when this command finishes, the download is stopped. For more information, see Advertising manifests. +.SH ARGUMENTS +.PP +\f[V]\f[R] +.PP +Arguments passed to the application that is being run. +.PP +Any arguments that aren\[cq]t recognized by \f[V]dotnet run\f[R] are passed to the application. +To separate arguments for \f[V]dotnet run\f[R] from arguments for the application, use the \f[V]--\f[R] option. .SH OPTIONS .IP \[bu] 2 \f[B]\f[VB]--\f[B]\f[R] @@ -132,6 +140,34 @@ Builds and runs the app using the specified framework. The framework must be specified in the project file. .RE .IP \[bu] 2 +\f[B]\f[VB]--file \f[B]\f[R] +.RS 2 +.PP +The path to the file-based app to run. +If a path isn\[cq]t specified, the current directory is used to find and run the file. +For more information on file-based apps, see Build file-based C# apps. +.PP +On Unix, you can run file-based apps directly, using the source file name on the command line instead of \f[V]dotnet run\f[R]. +First, ensure the file has execute permissions. +Then, add a shebang line \f[V]#!\f[R] as the first line of the file, for example: +.IP +.nf +\f[C] +#!/usr/bin/env dotnet run +\f[R] +.fi +.PP +Then you can run the file directly from the command line: +.IP +.nf +\f[C] +\&./ConsoleApp.cs +\f[R] +.fi +.PP +Introduced in .NET SDK 10.0.100. +.RE +.IP \[bu] 2 \f[B]\f[VB]--force\f[B]\f[R] .RS 2 .PP @@ -245,13 +281,13 @@ For a list of Runtime Identifiers (RIDs), see the RID catalog. \f[B]\f[VB]--tl:[auto|on|off]\f[B]\f[R] .RS 2 .PP -Specifies whether the \f[I]terminal logger\f[R] should be used for the build output. +Specifies whether \f[I]Terminal Logger\f[R] should be used for the build output. The default is \f[V]auto\f[R], which first verifies the environment before enabling terminal logging. The environment check verifies that the terminal is capable of using modern output features and isn\[cq]t using a redirected standard output before enabling the new logger. \f[V]on\f[R] skips the environment check and enables terminal logging. \f[V]off\f[R] skips the environment check and uses the default console logger. .PP -The terminal logger shows you the restore phase followed by the build phase. +Terminal Logger shows you the restore phase followed by the build phase. During each phase, the currently building projects appear at the bottom of the terminal. Each project that\[cq]s building outputs both the MSBuild target currently being built and the amount of time spent on that target. You can search this information to learn more about the build. @@ -306,6 +342,18 @@ dotnet run .fi .RE .IP \[bu] 2 +Run the specified file-based app in the current directory: +.RS 2 +.IP +.nf +\f[C] +dotnet run --file ConsoleApp.cs +\f[R] +.fi +.PP +File-based app support was added in .NET SDK 10.0.100. +.RE +.IP \[bu] 2 Run the specified project: .RS 2 .IP @@ -345,3 +393,22 @@ dotnet run --verbosity m \f[R] .fi .RE +.IP \[bu] 2 +Run the project in the current directory using the specified framework and pass arguments to the application: +.RS 2 +.IP +.nf +\f[C] +dotnet run -f net6.0 -- arg1 arg2 +\f[R] +.fi +.PP +In the following example, three arguments are passed to the application. +One argument is passed using \f[V]-\f[R], and two arguments are passed after \f[V]--\f[R]: +.IP +.nf +\f[C] +dotnet run -f net6.0 -arg1 -- arg2 arg3 +\f[R] +.fi +.RE diff --git a/documentation/manpages/sdk/dotnet-watch.1 b/documentation/manpages/sdk/dotnet-watch.1 index 6a02e55593a9..538a5acd0632 100644 --- a/documentation/manpages/sdk/dotnet-watch.1 +++ b/documentation/manpages/sdk/dotnet-watch.1 @@ -14,7 +14,7 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "dotnet-watch" "1" "2025-06-13" "" ".NET Documentation" +.TH "dotnet-watch" "1" "2025-09-30" "" ".NET Documentation" .hy .SH dotnet watch .PP @@ -177,7 +177,7 @@ The class uses \f \f[B]\f[VB]DOTNET_WATCH_AUTO_RELOAD_WS_HOSTNAME\f[B]\f[R] .RS 2 .PP -As part of \f[V]dotnet watch\f[R], the browser refresh server mechanism reads this value to determine the WebSocket host environment's hostname. +As part of \f[V]dotnet watch\f[R], the browser refresh server mechanism reads this value to determine the WebSocket host environment. The value \f[V]127.0.0.1\f[R] is replaced by \f[V]localhost\f[R], and the \f[V]http://\f[R] and \f[V]https://\f[R] schemes are replaced with \f[V]ws://\f[R] and \f[V]wss://\f[R] respectively. .RE .IP \[bu] 2 diff --git a/documentation/project-docs/telemetry.md b/documentation/project-docs/telemetry.md new file mode 100644 index 000000000000..e559f60bb25b --- /dev/null +++ b/documentation/project-docs/telemetry.md @@ -0,0 +1,318 @@ +# .NET SDK Telemetry Documentation + +## Table of Contents + +- [.NET SDK Telemetry Documentation](#net-sdk-telemetry-documentation) + - [Table of Contents](#table-of-contents) + - [How to Control Telemetry](#how-to-control-telemetry) + - [Common Properties Collected](#common-properties-collected) + - [Telemetry Events](#telemetry-events) + - [Core CLI Events](#core-cli-events) + - [Template Engine Events](#template-engine-events) + - [SDK-Collected Build Events](#sdk-collected-build-events) + - [MSBuild Engine Telemetry](#msbuild-engine-telemetry) + - [Container Events](#container-events) + - [`dotnet new` Command MSBuild Evaluation](#dotnet-new-command-msbuild-evaluation) + - [How to update this document](#how-to-update-this-document) + +## How to Control Telemetry + +### Disabling Telemetry + +The .NET SDK telemetry can be disabled using the following environment variable: + +- **`DOTNET_CLI_TELEMETRY_OPTOUT`**: Set to `1`, `true`, or `yes` to opt-out of telemetry collection + - Example: `export DOTNET_CLI_TELEMETRY_OPTOUT=1` (Linux/macOS) + - Example: `set DOTNET_CLI_TELEMETRY_OPTOUT=1` (Windows) + - Note: this value is defaulted to `true` on non-Microsoft-provided builds of the .NET SDK (e.g. those provided by our Linux Distro partners, through Source Build, etc.) + +### Related Environment Variables + +- **`DOTNET_NOLOGO`**: Set to `true` to hide .NET welcome and telemetry messages on first run + - Values: `true`, `1`, or `yes` to mute messages + - Values: `false`, `0`, or `no` to allow messages + - Default: `false` (messages are displayed) + - Note: This flag does not affect telemetry collection itself + +### Telemetry Configuration + +- **Default Behavior**: + - For Microsoft official builds: Telemetry is **enabled by default** (opt-out model) + - For non-Microsoft builds: Telemetry is **disabled by default** (controlled by `MICROSOFT_ENABLE_TELEMETRY` compile flag) + +- **Connection String**: Telemetry data is sent to Application Insights with instrumentation key: `74cc1c9e-3e6e-4d05-b3fc-dde9101d0254` + +- **First Time Use**: Telemetry is only collected after the first-time-use notice has been shown and accepted (tracked via sentinel file) + +- **Event Namespace**: All telemetry events are automatically prefixed with `dotnet/cli/` + +## Common Properties Collected + +Every telemetry event automatically includes these common properties: + +| Property | Description | Example Value | +|----------|-------------|---------------| +| **OS Version** | Operating system version | `Microsoft Windows 10.0.14393` | +| **OS Platform** | Operating system platform | `Windows`, `Linux`, `OSX` | +| **OS Architecture** | Operating system architecture | `X64`, `X86`, `Arm64` | +| **Output Redirected** | Whether console output is redirected | `True` or `False` | +| **Runtime Id** | .NET runtime identifier | `win10-x64`, `linux-x64` | +| **Product Version** | .NET SDK version | `8.0.100` | +| **Telemetry Profile** | Custom telemetry profile (if set via env var) | Custom value or null | +| **Docker Container** | Whether running in Docker container | `True` or `False` | +| **CI** | Whether running in CI environment | `True` or `False` | +| **LLM** | Detected LLM/assistant environment identifiers (comma-separated) | `claude`, `cursor`, `gemini`, `copilot`, `generic_agent` | +| **Current Path Hash** | SHA256 hash of current directory path | Hashed value | +| **Machine ID** | SHA256 hash of machine MAC address (or GUID if unavailable) | Hashed value | +| **Machine ID Old** | Legacy machine ID for compatibility | Hashed value | +| **Device ID** | Device identifier | Device-specific ID | +| **Kernel Version** | OS kernel version | `Darwin 17.4.0`, `Linux 4.9.0` | +| **Installation Type** | How the SDK was installed | Installation method | +| **Product Type** | Type of .NET product | Product identifier | +| **Libc Release** | Libc release information | Libc release version | +| **Libc Version** | Libc version information | Libc version number | +| **SessionId** | Unique session identifier | GUID | + +## Telemetry Events + +### Core CLI Events + +#### `command/finish` + +**When fired**: At the end of every dotnet CLI command execution + +**Properties**: + +- `exitCode`: The exit code of the command + +**Description**: Tracks the completion status of any dotnet command + +--- + +#### `schema` + +**When fired**: When CLI schema is generated or accessed + +**Properties**: + +- `command`: The command hierarchy as a string + +**Description**: Tracks schema generation for the CLI + +--- + +#### `toplevelparser/command` + +**When fired**: For every top-level dotnet command + +**Properties**: + +- `verb`: Top-level command name (build, restore, publish, etc.) + +**Measurements**: Performance data (startup time, parse time, etc.) + +**Description**: Tracks usage patterns of main dotnet commands + +--- + +#### `sublevelparser/command` + +**When fired**: For subcommands and specific command options + +**Properties**: Various depending on command (verbosity, configuration, framework, etc.) + +**Description**: Tracks detailed command option usage + +--- + +#### `commandresolution/commandresolved` + +**When fired**: When a command is resolved through the command factory + +**Properties**: + +- `commandName`: SHA256 hash of command name +- `commandResolver`: Type of command resolver used + +**Description**: Tracks how commands are resolved in the CLI + +--- + +#### `install/reportsuccess` + +**When fired**: When installation success is reported + +**Properties**: + +- `exeName`: Name of the install method that was used - one of `debianpackage`, a specific macOS `.pkg` file name, or a specific Windows exe installer file name. + +**Description**: Tracks successful tool installations + +--- + +#### `mainCatchException/exception` + +**When fired**: When unhandled exceptions occur in the main CLI + +**Properties**: + +- `exceptionType`: Type of exception +- `detail`: Exception details (sensitive message removed) + +**Description**: Tracks unhandled exceptions for diagnostics + +### Template Engine Events + +#### `template/new-install` + +**When fired**: During template package installation + +**Properties**: + +- `CountOfThingsToInstall`: Number of template packages being installed + +**Description**: Tracks template package installation operations + +--- + +#### `template/new-create-template` + +**When fired**: When creating a new project from template + +**Properties**: + +- `language`: Template language (C#, F#, VB, etc) +- `argument-error`: Whether there were argument errors ("True"/"False") +- `framework`: Framework choice (only sent for Microsoft-authored templates) +- `template-name`: SHA256 hash of template identity +- `template-short-name`: SHA256 hash of template short names +- `package-name`: SHA256 hash of package name +- `package-version`: SHA256 hash of package version +- `is-template-3rd-party`: Whether template is third-party +- `create-success`: Whether creation succeeded +- `auth`: Authentication choice (only sent for Microsoft-authored templates) + +**Description**: Tracks template usage and creation success + +### SDK-Collected Build Events + +#### `msbuild/targetframeworkeval` + +**When fired**: When target framework is evaluated + +**Properties**: + +- `TargetFrameworkVersion`: Target framework version +- `RuntimeIdentifier`: Runtime identifier +- `SelfContained`: Whether self-contained +- `UseApphost`: Whether using app host +- `OutputType`: Output type (Exe, Library, etc.) +- `UseArtifactsOutput`: Whether using artifacts output +- `ArtifactsPathLocationType`: Artifacts path location type - one of `Explicitly Specified`, `DirectoryBuildPropsFolder`, or `ProjectFolder`. + +**Description**: Tracks target framework configurations + +--- + +#### `taskBaseCatchException` + +**When fired**: When exceptions occur in SDK-provided MSBuild tasks + +**Properties**: + +- `exceptionType`: Exception type +- `detail`: Exception details (message removed) + +**Description**: Tracks exceptions in MSBuild task execution + +--- + +#### `PublishProperties` + +**When fired**: During the Publish Target + +**Properties**: Gathers the values of the following MSBuild Properties if they are set in the project file or via command line: + +- PublishReadyToRun +- PublishSingleFile +- PublishTrimmed +- PublishAot +- PublishProtocol + +**Description**: Tracks publish-related properties + +--- + +#### `WorkloadPublishProperties` + +**When fired**: During workload publish operations + +**Properties**: Gathers the values of the following MSBuild Properties if they are set in the project file or via command line: + +- TargetPlatformIdentifier: TargetPlatformIdentifier +- RuntimeIdentifier: RuntimeIdentifier +- BlazorWasm: _WorkloadUsesBlazorWasm +- WasmSDK: _WorkloadUsesWasmSDK +- UsesMaui: UseMaui +- UsesMobileSDKOnly: _WorkloadUsesMobileSDKOnly +- UsesOtherMobileSDK: _WorkloadUsesOther +- MonoAOT: _WorkloadUsesMonoAOT +- NativeAOT: _WorkloadUsesNativeAOT +- Interp: _WorkloadUsesInterpreter +- LibraryMode: _WorkloadUsesLibraryMode +- ResolvedRuntimePack: _MonoWorkloadRuntimePackPackageVersion +- StripILAfterAOT: _WorkloadUsesStripILAfterAOT + +**Description**: Tracks workload-specific publish properties + +--- + +#### `ReadyToRun` + +**When fired**: During ReadyToRun compilation + +**Properties**: Gathers the values of the following MSBuild Properties and Items if they are set in the project file or via command line: + +- PublishReadyToRunUseCrossgen2: PublishReadyToRunUseCrossgen2 +- Crossgen2PackVersion: ResolvedCrossgen2Pack.NuGetPackageVersion +- CompileListCount: _ReadyToRunCompileList->Count() +- FailedCount: _ReadyToRunCompilationFailures->Count() + +**Description**: Tracks ReadyToRun compilation usage + +--- + +### MSBuild Engine Telemetry + +See [MSBuild Telemetry Documentation](https://github.com/dotnet/msbuild/blob/main/documentation/wiki/CollectedTelemetry.md) for details on these events. + +### Container Events + +See [Container Telemetry Documentation](https://github.com/dotnet/sdk-container-builds/blob/main/docs/Telemetry.md) for details. + +### `dotnet new` Command MSBuild Evaluation + +#### `new/msbuild-eval` + +**When fired**: When MSBuild project evaluation occurs during `dotnet new` + +**Properties** (hashed): + +- `ProjectPath`: Project path +- `SdkStyleProject`: Whether it's SDK-style project +- `Status`: Evaluation status +- `TargetFrameworks`: Target frameworks + +**Measurements**: + +- `EvaluationTime`: Total evaluation time in milliseconds +- `InnerEvaluationTime`: Inner evaluation time in milliseconds + +**Description**: Tracks MSBuild evaluation performance for new projects + +## How to update this document + +- Ensure that all telemetry events and properties are accurately documented +- Review and update common properties as needed +- Add new events or properties as they are introduced in the .NET SDK +- Follow the pre-existing format diff --git a/eng/Signing.props b/eng/Signing.props index 484697efecbc..872602d12f5c 100644 --- a/eng/Signing.props +++ b/eng/Signing.props @@ -84,6 +84,9 @@ + + + diff --git a/eng/common/core-templates/job/publish-build-assets.yml b/eng/common/core-templates/job/publish-build-assets.yml index 37dff559fc1b..15e236f39cab 100644 --- a/eng/common/core-templates/job/publish-build-assets.yml +++ b/eng/common/core-templates/job/publish-build-assets.yml @@ -40,8 +40,6 @@ parameters: repositoryAlias: self - officialBuildId: '' - jobs: - job: Asset_Registry_Publish @@ -64,11 +62,6 @@ jobs: value: false # unconditional - needed for logs publishing (redactor tool version) - template: /eng/common/core-templates/post-build/common-variables.yml - - name: OfficialBuildId - ${{ if ne(parameters.officialBuildId, '') }}: - value: ${{ parameters.officialBuildId }} - ${{ else }}: - value: $(Build.BuildNumber) pool: # We don't use the collection uri here because it might vary (.visualstudio.com vs. dev.azure.com) diff --git a/eng/common/core-templates/jobs/jobs.yml b/eng/common/core-templates/jobs/jobs.yml index 01ada7476651..b637cb6e9480 100644 --- a/eng/common/core-templates/jobs/jobs.yml +++ b/eng/common/core-templates/jobs/jobs.yml @@ -44,7 +44,6 @@ parameters: artifacts: {} is1ESPipeline: '' repositoryAlias: self - officialBuildId: '' # Internal resources (telemetry, microbuild) can only be accessed from non-public projects, # and some (Microbuild) should only be applied to non-PR cases for internal builds. @@ -117,4 +116,3 @@ jobs: artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} signingValidationAdditionalParameters: ${{ parameters.signingValidationAdditionalParameters }} repositoryAlias: ${{ parameters.repositoryAlias }} - officialBuildId: ${{ parameters.officialBuildId }} diff --git a/eng/dotnet-format/dotnet-format-integration.yml b/eng/dotnet-format/dotnet-format-integration.yml index 1fa8bbb8ad9f..25c17948bf59 100644 --- a/eng/dotnet-format/dotnet-format-integration.yml +++ b/eng/dotnet-format/dotnet-format-integration.yml @@ -2,6 +2,7 @@ parameters: - name: oneESCompat type: object default: + templateFolderName: templates publishTaskPrefix: '' - name: TestArguments @@ -63,6 +64,9 @@ parameters: - name: runtimeSourceProperties type: string default: '' +- name: populateInternalRuntimeVariables + type: boolean + default: false jobs: - job: Formatting_Check @@ -77,8 +81,11 @@ jobs: os: windows timeoutInMinutes: ${{ parameters.timeoutInMinutes }} steps: - - template: /eng/common/templates/steps/enable-internal-runtimes.yml - - template: /eng/common/templates/steps/enable-internal-sources.yml + - ${{ if eq(parameters.populateInternalRuntimeVariables, true) }}: + - template: /eng/common/${{ parameters.oneESCompat.templateFolderName }}/steps/enable-internal-sources.yml + parameters: + legacyCredential: $(dn-bot-dnceng-artifact-feeds-rw) + - template: /eng/common/${{ parameters.oneESCompat.templateFolderName }}/steps/enable-internal-runtimes.yml - script: .\restore.cmd ${{ parameters.runtimeSourceProperties }} displayName: 🟣 Restore dependencies - script: | @@ -106,8 +113,11 @@ jobs: os: windows timeoutInMinutes: ${{ parameters.timeoutInMinutes }} steps: - - template: /eng/common/templates/steps/enable-internal-runtimes.yml - - template: /eng/common/templates/steps/enable-internal-sources.yml + - ${{ if eq(parameters.populateInternalRuntimeVariables, true) }}: + - template: /eng/common/${{ parameters.oneESCompat.templateFolderName }}/steps/enable-internal-sources.yml + parameters: + legacyCredential: $(dn-bot-dnceng-artifact-feeds-rw) + - template: /eng/common/${{ parameters.oneESCompat.templateFolderName }}/steps/enable-internal-runtimes.yml - script: eng\dotnet-format\integration-test.cmd -repo '${{ testArgs._repo }}' -branchName '${{ testArgs._branchName }}' -sha '${{ testArgs._sha }}' -targetSolution '${{ testArgs._targetSolution }}' -useParentSdk ${{ testArgs._useParentSdk }} -testPath '$(Agent.TempDirectory)\temp' -stage 'prepare' -runtimeSourceProperties '${{ parameters.runtimeSourceProperties }}' displayName: 🟣 Prepare ${{ testArgs._repoName }} for formatting diff --git a/eng/pipelines/templates/jobs/sdk-build.yml b/eng/pipelines/templates/jobs/sdk-build.yml index c1ec3d7dfcd1..a9fc3537bfcb 100644 --- a/eng/pipelines/templates/jobs/sdk-build.yml +++ b/eng/pipelines/templates/jobs/sdk-build.yml @@ -12,7 +12,9 @@ parameters: testProjects: $(Build.SourcesDirectory)/test/UnitTests.proj publishRetryConfig: false publishXunitResults: false + testRunnerAdditionalArguments: '' enableSbom: true + populateInternalRuntimeVariables: false timeoutInMinutes: 150 ### ENV VARS ### testFullMSBuild: false @@ -71,11 +73,11 @@ jobs: targetPath: $(Build.SourcesDirectory)/eng/BuildConfiguration artifactName: BuildConfiguration - # Populate internal runtime variables. - - template: /eng/common/templates/steps/enable-internal-sources.yml - parameters: - legacyCredential: $(dn-bot-dnceng-artifact-feeds-rw) - - template: /eng/common/templates/steps/enable-internal-runtimes.yml + - ${{ if eq(parameters.populateInternalRuntimeVariables, true) }}: + - template: /eng/common/${{ parameters.oneESCompat.templateFolderName }}/steps/enable-internal-sources.yml + parameters: + legacyCredential: $(dn-bot-dnceng-artifact-feeds-rw) + - template: /eng/common/${{ parameters.oneESCompat.templateFolderName }}/steps/enable-internal-runtimes.yml - ${{ if eq(parameters.downloadManifestMsiPackages, true) }}: - task: DownloadBuildArtifacts@1 @@ -84,7 +86,7 @@ jobs: downloadPath: $(Build.SourcesDirectory)/artifacts/downloaded-manifest-msi-packages itemPattern: '**/*Manifest-*.Msi.*.nupkg' checkDownloadedFiles: true - displayName: Download Manifest msi packages + displayName: 🟣 Download Manifest MSI Packages ############### BUILDING ############### - ${{ if eq(parameters.pool.os, 'windows') }}: @@ -105,9 +107,6 @@ jobs: BuildConfig: $(buildConfiguration) TestFullMSBuild: ${{ parameters.testFullMSBuild }} - - powershell: build/RunTestTemplateTests.ps1 - displayName: 🟣 Run Test Templates Tests - - ${{ else }}: - script: | source $(Build.SourcesDirectory)/eng/common/native/init-os-and-arch.sh @@ -140,12 +139,14 @@ jobs: - script: $(Build.SourcesDirectory)/artifacts/bin/redist/$(buildConfiguration)/dotnet/dotnet workload install wasm-tools --skip-manifest-update workingDirectory: $(Build.SourcesDirectory)/artifacts/bin displayName: 🟣 Install wasm-tools Workload + # For the /p:Projects syntax for PowerShell, see: https://github.com/dotnet/msbuild/issues/471#issuecomment-1146466335 - ${{ if eq(parameters.pool.os, 'windows') }}: - powershell: eng/common/build.ps1 -restore -test -ci -prepareMachine -nativeToolsOnMachine -configuration $(buildConfiguration) /p:Projects=\`"${{ replace(parameters.testProjects, ';', '`;') }}\`" + /p:TestRunnerAdditionalArguments="${{ parameters.testRunnerAdditionalArguments }}" /p:TargetArchitecture=${{ parameters.targetArchitecture }} ${{ parameters.runtimeSourceProperties }} /p:CustomHelixTargetQueue=${{ parameters.helixTargetQueue }} @@ -158,6 +159,7 @@ jobs: HelixAccessToken: $(HelixApiAccessToken) RunAoTTests: ${{ parameters.runAoTTests }} TestFullMSBuild: ${{ parameters.testFullMSBuild }} + - ${{ else }}: # For the /p:Projects syntax for Bash, see: https://github.com/dotnet/msbuild/issues/471#issuecomment-1690189034 # The /p:CustomHelixTargetQueue syntax is: @ @@ -166,6 +168,7 @@ jobs: -restore -test -ci -prepareMachine -configuration $(buildConfiguration) '/p:Projects="${{ parameters.testProjects }}"' + /p:TestRunnerAdditionalArguments="${{ parameters.testRunnerAdditionalArguments }}" /p:TargetArchitecture=${{ parameters.targetArchitecture }} /p:TargetRid=${{ parameters.runtimeIdentifier }} ${{ parameters.osProperties }} diff --git a/eng/pipelines/templates/jobs/sdk-job-matrix.yml b/eng/pipelines/templates/jobs/sdk-job-matrix.yml index 8e9000f3c6cc..870f20d8f88b 100644 --- a/eng/pipelines/templates/jobs/sdk-job-matrix.yml +++ b/eng/pipelines/templates/jobs/sdk-job-matrix.yml @@ -31,7 +31,6 @@ parameters: container: azureLinux30Amd64 helixTargetContainer: $(helixTargetContainerPrefix)ubuntu-24.04-helix-amd64 osProperties: /p:OSName=linux /p:BuildSdkDeb=true - runTests: true # Helix is hanging on this job using the container. See: https://github.com/dotnet/dnceng/issues/6000 disableJob: true - categoryName: TemplateEngine diff --git a/global.json b/global.json index 03a1a2cd11e8..8479a0866f25 100644 --- a/global.json +++ b/global.json @@ -7,7 +7,7 @@ "errorMessage": "The .NET SDK is not installed or is not configured correctly. Please run ./build to install the correct SDK version locally." }, "tools": { - "dotnet": "10.0.100-rc.1.25420.111", + "dotnet": "10.0.100-rc.1.25451.107", "runtimes": { "dotnet": [ "$(MicrosoftNETCorePlatformsPackageVersion)" diff --git a/sdk.slnx b/sdk.slnx index 0246a040f447..dc0633c42d02 100644 --- a/sdk.slnx +++ b/sdk.slnx @@ -312,6 +312,7 @@ + diff --git a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets b/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets index 3fdb67030110..a90625e732e6 100644 --- a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets +++ b/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets @@ -88,11 +88,9 @@ Copyright (c) .NET Foundation. All rights reserved. $(ResolvePublishRelatedStaticWebAssetsDependsOn); - _ReplaceFingerprintedBlazorJsForPublish $(ResolveCompressedFilesForPublishDependsOn); - _ReplaceFingerprintedBlazorJsForPublish @@ -159,65 +157,6 @@ Copyright (c) .NET Foundation. All rights reserved. - - - <_BlazorJSFileNames>;@(_BlazorJSFile->'%(FileName)'); - - - <_BlazorJSJSStaticWebAsset Include="@(StaticWebAsset)" Condition="$(_BlazorJSFileNames.Contains(';%(FileName);')) and '%(Extension)' == '.js'" /> - <_BlazorJSPublishCandidate Include="%(_BlazorJSJSStaticWebAsset.RelativeDir)%(_BlazorJSJSStaticWebAsset.FileName).%(_BlazorJSJSStaticWebAsset.Fingerprint)%(_BlazorJSJSStaticWebAsset.Extension)" /> - <_BlazorJSPublishCandidate Remove="@(_BlazorJSPublishCandidate)" Condition="'%(Extension)' == '.map'" /> - <_BlazorJSPublishCandidate> - _framework/$([System.IO.Path]::GetFileNameWithoutExtension('%(Filename)'))%(Extension) - - - - - - - - - - - <_BlazorJSJSStaticWebAssetFullPath>@(_BlazorJSJSStaticWebAsset->'%(FullPath)') - - - <_BlazorJSJSStaticWebAsset Include="@(StaticWebAsset)" Condition="'%(AssetTraitName)' == 'Content-Encoding' and '%(RelatedAsset)' == '$(_BlazorJSJSStaticWebAssetFullPath)'" /> - - - - - - - - - - - - diff --git a/src/BuiltInTools/AspireService/AspireServerService.cs b/src/BuiltInTools/AspireService/AspireServerService.cs index bf2f8f341b81..064de0ee7a50 100644 --- a/src/BuiltInTools/AspireService/AspireServerService.cs +++ b/src/BuiltInTools/AspireService/AspireServerService.cs @@ -123,6 +123,7 @@ public List> GetServerConnectionEnvironment() new(DebugSessionServerCertEnvVar, _certificateEncodedBytes), ]; + /// public ValueTask NotifySessionEndedAsync(string dcpId, string sessionId, int processId, int? exitCode, CancellationToken cancelationToken) => SendNotificationAsync( new SessionTerminatedNotification() @@ -136,6 +137,7 @@ public ValueTask NotifySessionEndedAsync(string dcpId, string sessionId, int pro sessionId, cancelationToken); + /// public ValueTask NotifySessionStartedAsync(string dcpId, string sessionId, int processId, CancellationToken cancelationToken) => SendNotificationAsync( new ProcessRestartedNotification() @@ -148,6 +150,7 @@ public ValueTask NotifySessionStartedAsync(string dcpId, string sessionId, int p sessionId, cancelationToken); + /// public ValueTask NotifyLogMessageAsync(string dcpId, string sessionId, bool isStdErr, string data, CancellationToken cancelationToken) => SendNotificationAsync( new ServiceLogsNotification() @@ -161,23 +164,28 @@ public ValueTask NotifyLogMessageAsync(string dcpId, string sessionId, bool isSt sessionId, cancelationToken); - private async ValueTask SendNotificationAsync(TNotification notification, string dcpId, string sessionId, CancellationToken cancelationToken) + /// + private async ValueTask SendNotificationAsync(TNotification notification, string dcpId, string sessionId, CancellationToken cancellationToken) where TNotification : SessionNotification { try { - Log($"[#{sessionId}] Sending '{notification.NotificationType}'"); + Log($"[#{sessionId}] Sending '{notification.NotificationType}': {notification}"); var jsonSerialized = JsonSerializer.SerializeToUtf8Bytes(notification, JsonSerializerOptions); - await SendMessageAsync(dcpId, jsonSerialized, cancelationToken); - } - catch (Exception e) when (e is not OperationCanceledException && LogAndPropagate(e)) - { - } + var success = await SendMessageAsync(dcpId, jsonSerialized, cancellationToken); - bool LogAndPropagate(Exception e) + if (!success) + { + cancellationToken.ThrowIfCancellationRequested(); + Log($"[#{sessionId}] Failed to send message: Connection not found (dcpId='{dcpId}')."); + } + } + catch (Exception e) when (e is not OperationCanceledException) { - Log($"[#{sessionId}] Sending '{notification.NotificationType}' failed: {e.Message}"); - return false; + if (!cancellationToken.IsCancellationRequested) + { + Log($"[#{sessionId}] Failed to send message: {e.Message}"); + } } } @@ -373,15 +381,13 @@ private async Task WriteResponseTextAsync(HttpResponse response, Exception ex, b } } - private async Task SendMessageAsync(string dcpId, byte[] messageBytes, CancellationToken cancellationToken) + private async ValueTask SendMessageAsync(string dcpId, byte[] messageBytes, CancellationToken cancellationToken) { // Find the connection for the passed in dcpId WebSocketConnection? connection = _socketConnectionManager.GetSocketConnection(dcpId); if (connection is null) { - // Most likely the connection has already gone away - Log($"Send message failure: Connection with the following dcpId was not found {dcpId}"); - return; + return false; } var success = false; @@ -405,6 +411,8 @@ private async Task SendMessageAsync(string dcpId, byte[] messageBytes, Cancellat _webSocketAccess.Release(); } + + return success; } private async ValueTask HandleStopSessionRequestAsync(HttpContext context, string sessionId) diff --git a/src/BuiltInTools/AspireService/Microsoft.WebTools.AspireService.Package.csproj b/src/BuiltInTools/AspireService/Microsoft.WebTools.AspireService.Package.csproj index 952651e23062..2c32befe16b6 100644 --- a/src/BuiltInTools/AspireService/Microsoft.WebTools.AspireService.Package.csproj +++ b/src/BuiltInTools/AspireService/Microsoft.WebTools.AspireService.Package.csproj @@ -9,6 +9,7 @@ true + true true Aspire.Tools.Service false diff --git a/src/BuiltInTools/AspireService/Models/SessionChangeNotification.cs b/src/BuiltInTools/AspireService/Models/SessionChangeNotification.cs index 579fe6b4a88f..d4213593cf5a 100644 --- a/src/BuiltInTools/AspireService/Models/SessionChangeNotification.cs +++ b/src/BuiltInTools/AspireService/Models/SessionChangeNotification.cs @@ -56,6 +56,9 @@ internal sealed class SessionTerminatedNotification : SessionNotification [Required] [JsonPropertyName("exit_code")] public required int? ExitCode { get; init; } + + public override string ToString() + => $"pid={Pid}, exit_code={ExitCode}"; } /// @@ -70,6 +73,9 @@ internal sealed class ProcessRestartedNotification : SessionNotification [Required] [JsonPropertyName("pid")] public required int PID { get; init; } + + public override string ToString() + => $"pid={PID}"; } /// @@ -91,4 +97,7 @@ internal sealed class ServiceLogsNotification : SessionNotification [Required] [JsonPropertyName("log_message")] public required string LogMessage { get; init; } + + public override string ToString() + => $"log_message='{LogMessage}', is_std_err={IsStdErr}"; } diff --git a/src/BuiltInTools/BrowserRefresh/Microsoft.AspNetCore.Watch.BrowserRefresh.csproj b/src/BuiltInTools/BrowserRefresh/Microsoft.AspNetCore.Watch.BrowserRefresh.csproj index b56559840a89..84852c4df9a9 100644 --- a/src/BuiltInTools/BrowserRefresh/Microsoft.AspNetCore.Watch.BrowserRefresh.csproj +++ b/src/BuiltInTools/BrowserRefresh/Microsoft.AspNetCore.Watch.BrowserRefresh.csproj @@ -7,7 +7,10 @@ net6.0 MicrosoftAspNetCore + true + true + true Microsoft.DotNet.HotReload.Web.Middleware Package containing Hot Reload middleware. true diff --git a/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj b/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj index d5c6f3848416..5172480c5f98 100644 --- a/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj +++ b/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj @@ -16,6 +16,8 @@ true + true + true Microsoft.DotNet.HotReload.Agent.Host Package containing Hot Reload agent host. true diff --git a/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs b/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs index f9ac8e3463f9..28890852482f 100644 --- a/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs +++ b/src/BuiltInTools/DotNetDeltaApplier/StartupHook.cs @@ -89,7 +89,7 @@ private static void Log(string message) if (!string.IsNullOrEmpty(prefix)) { Console.ForegroundColor = ConsoleColor.DarkGray; - Console.WriteLine($"{prefix} {message}"); + Console.Error.WriteLine($"{prefix} {message}"); Console.ResetColor(); } } diff --git a/src/BuiltInTools/HotReloadAgent.Data/Microsoft.DotNet.HotReload.Agent.Data.Package.csproj b/src/BuiltInTools/HotReloadAgent.Data/Microsoft.DotNet.HotReload.Agent.Data.Package.csproj index 285d0de46e69..4dde206b752d 100644 --- a/src/BuiltInTools/HotReloadAgent.Data/Microsoft.DotNet.HotReload.Agent.Data.Package.csproj +++ b/src/BuiltInTools/HotReloadAgent.Data/Microsoft.DotNet.HotReload.Agent.Data.Package.csproj @@ -11,6 +11,7 @@ true + true true Microsoft.DotNet.HotReload.Agent.Data false diff --git a/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.Package.csproj b/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.Package.csproj index a6a3b3ab0ad1..def624accfd0 100644 --- a/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.Package.csproj +++ b/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.Package.csproj @@ -11,6 +11,7 @@ true + true true Microsoft.DotNet.HotReload.Agent.PipeRpc false diff --git a/src/BuiltInTools/HotReloadAgent/Microsoft.DotNet.HotReload.Agent.Package.csproj b/src/BuiltInTools/HotReloadAgent/Microsoft.DotNet.HotReload.Agent.Package.csproj index 67e670e57107..d44f61dd98ca 100644 --- a/src/BuiltInTools/HotReloadAgent/Microsoft.DotNet.HotReload.Agent.Package.csproj +++ b/src/BuiltInTools/HotReloadAgent/Microsoft.DotNet.HotReload.Agent.Package.csproj @@ -12,6 +12,7 @@ true + true true Microsoft.DotNet.HotReload.Agent false diff --git a/src/BuiltInTools/HotReloadClient/DefaultHotReloadClient.cs b/src/BuiltInTools/HotReloadClient/DefaultHotReloadClient.cs index 7587b7623117..1063ba1635f7 100644 --- a/src/BuiltInTools/HotReloadClient/DefaultHotReloadClient.cs +++ b/src/BuiltInTools/HotReloadClient/DefaultHotReloadClient.cs @@ -35,9 +35,14 @@ public override void Dispose() private void DisposePipe() { - Logger.LogDebug("Disposing agent communication pipe"); - _pipe?.Dispose(); - _pipe = null; + if (_pipe != null) + { + Logger.LogDebug("Disposing agent communication pipe"); + + // Dispose the pipe but do not set it to null, so that any in-progress + // operations throw the appropriate exception type. + _pipe.Dispose(); + } } // for testing @@ -101,8 +106,7 @@ private void RequireReadyForUpdates() // should only be called after connection has been created: _ = GetCapabilitiesTask(); - if (_pipe == null) - throw new InvalidOperationException("Pipe has been disposed."); + Debug.Assert(_pipe != null); } public override void ConfigureLaunchEnvironment(IDictionary environmentBuilder) @@ -152,7 +156,13 @@ public override async Task ApplyManagedCodeUpdatesAsync(ImmutableAr { if (!success) { - Logger.LogWarning("Further changes won't be applied to this process."); + // Don't report a warning when cancelled. The process has terminated or the host is shutting down in that case. + // Best effort: There is an inherent race condition due to time between the process exiting and the cancellation token triggering. + if (!cancellationToken.IsCancellationRequested) + { + Logger.LogWarning("Further changes won't be applied to this process."); + } + _managedCodeUpdateFailedOrCancelled = true; DisposePipe(); } @@ -216,7 +226,7 @@ public async override Task ApplyStaticAssetUpdatesAsync(ImmutableAr private ValueTask SendAndReceiveUpdateAsync(TRequest request, bool isProcessSuspended, CancellationToken cancellationToken) where TRequest : IUpdateRequest { - // Should not be disposed: + // Should not initialized: Debug.Assert(_pipe != null); return SendAndReceiveUpdateAsync( @@ -241,8 +251,10 @@ async ValueTask SendAndReceiveAsync(int batchId, CancellationToken cancell Logger.LogDebug("Update batch #{UpdateId} failed.", batchId); } - catch (Exception e) when (e is not OperationCanceledException || isProcessSuspended) + catch (Exception e) { + // Don't report an error when cancelled. The process has terminated or the host is shutting down in that case. + // Best effort: There is an inherent race condition due to time between the process exiting and the cancellation token triggering. if (cancellationToken.IsCancellationRequested) { Logger.LogDebug("Update batch #{UpdateId} canceled.", batchId); @@ -267,7 +279,7 @@ async ValueTask WriteRequestAsync(CancellationToken cancellationToken) private async ValueTask ReceiveUpdateResponseAsync(CancellationToken cancellationToken) { - // Should not be disposed: + // Should be initialized: Debug.Assert(_pipe != null); var (success, log) = await UpdateResponse.ReadAsync(_pipe, cancellationToken); @@ -296,10 +308,12 @@ public override async Task InitialUpdatesAppliedAsync(CancellationToken cancella } catch (Exception e) when (e is not OperationCanceledException) { - // pipe might throw another exception when forcibly closed on process termination: + // Pipe might throw another exception when forcibly closed on process termination. + // Don't report an error when cancelled. The process has terminated or the host is shutting down in that case. + // Best effort: There is an inherent race condition due to time between the process exiting and the cancellation token triggering. if (!cancellationToken.IsCancellationRequested) { - Logger.LogError("Failed to send InitialUpdatesCompleted: {Message}", e.Message); + Logger.LogError("Failed to send {RequestType}: {Message}", nameof(RequestType.InitialUpdatesCompleted), e.Message); } } } diff --git a/src/BuiltInTools/HotReloadClient/HotReloadClient.cs b/src/BuiltInTools/HotReloadClient/HotReloadClient.cs index fe14176a8b6e..e5efa4db2a7b 100644 --- a/src/BuiltInTools/HotReloadClient/HotReloadClient.cs +++ b/src/BuiltInTools/HotReloadClient/HotReloadClient.cs @@ -41,23 +41,42 @@ internal Task PendingUpdates /// /// Initiates connection with the agent in the target process. /// + /// Cancellation token. The cancellation should trigger on process terminatation. public abstract void InitiateConnection(CancellationToken cancellationToken); /// /// Waits until the connection with the agent is established. /// + /// Cancellation token. The cancellation should trigger on process terminatation. public abstract Task WaitForConnectionEstablishedAsync(CancellationToken cancellationToken); + /// + /// Returns update capabilities of the target process. + /// + /// Cancellation token. The cancellation should trigger on process terminatation. public abstract Task> GetUpdateCapabilitiesAsync(CancellationToken cancellationToken); + /// + /// Applies managed code updates to the target process. + /// + /// Cancellation token. The cancellation should trigger on process terminatation. public abstract Task ApplyManagedCodeUpdatesAsync(ImmutableArray updates, bool isProcessSuspended, CancellationToken cancellationToken); + + /// + /// Applies static asset updates to the target process. + /// + /// Cancellation token. The cancellation should trigger on process terminatation. public abstract Task ApplyStaticAssetUpdatesAsync(ImmutableArray updates, bool isProcessSuspended, CancellationToken cancellationToken); /// /// Notifies the agent that the initial set of updates has been applied and the user code in the process can start executing. /// + /// Cancellation token. The cancellation should trigger on process terminatation. public abstract Task InitialUpdatesAppliedAsync(CancellationToken cancellationToken); + /// + /// Disposes the client. Can occur unexpectedly whenever the process exits. + /// public abstract void Dispose(); public static void ReportLogEntry(ILogger logger, string message, AgentMessageSeverity severity) @@ -72,7 +91,7 @@ public static void ReportLogEntry(ILogger logger, string message, AgentMessageSe logger.Log(level, message); } - public async Task> FilterApplicableUpdatesAsync(ImmutableArray updates, CancellationToken cancellationToken) + protected async Task> FilterApplicableUpdatesAsync(ImmutableArray updates, CancellationToken cancellationToken) { var availableCapabilities = await GetUpdateCapabilitiesAsync(cancellationToken); var applicableUpdates = new List(); diff --git a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadClients.cs b/src/BuiltInTools/HotReloadClient/HotReloadClients.cs similarity index 79% rename from src/BuiltInTools/dotnet-watch/HotReload/HotReloadClients.cs rename to src/BuiltInTools/HotReloadClient/HotReloadClients.cs index b06fcc838485..58676fdcf5d7 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadClients.cs +++ b/src/BuiltInTools/HotReloadClient/HotReloadClients.cs @@ -1,20 +1,31 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable + +using System; +using System.Collections.Generic; using System.Collections.Immutable; -using Microsoft.Build.Graph; -using Microsoft.DotNet.Watch; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace Microsoft.DotNet.HotReload; -internal sealed class HotReloadClients(ImmutableArray<(HotReloadClient client, string name)> clients, BrowserRefreshServer? browserRefreshServer) : IDisposable +internal sealed class HotReloadClients(ImmutableArray<(HotReloadClient client, string name)> clients, AbstractBrowserRefreshServer? browserRefreshServer) : IDisposable { - public HotReloadClients(HotReloadClient client, BrowserRefreshServer? browserRefreshServer) + public HotReloadClients(HotReloadClient client, AbstractBrowserRefreshServer? browserRefreshServer) : this([(client, "")], browserRefreshServer) { } + /// + /// Disposes all clients. Can occur unexpectedly whenever the process exits. + /// public void Dispose() { foreach (var (client, _) in clients) @@ -23,7 +34,7 @@ public void Dispose() } } - public BrowserRefreshServer? BrowserRefreshServer + public AbstractBrowserRefreshServer? BrowserRefreshServer => browserRefreshServer; /// @@ -48,6 +59,7 @@ internal void ConfigureLaunchEnvironment(IDictionary environment browserRefreshServer?.ConfigureLaunchEnvironment(environmentBuilder, enableHotReload: true); } + /// Cancellation token. The cancellation should trigger on process terminatation. internal void InitiateConnection(CancellationToken cancellationToken) { foreach (var (client, _) in clients) @@ -56,11 +68,13 @@ internal void InitiateConnection(CancellationToken cancellationToken) } } + /// Cancellation token. The cancellation should trigger on process terminatation. internal async ValueTask WaitForConnectionEstablishedAsync(CancellationToken cancellationToken) { await Task.WhenAll(clients.Select(c => c.client.WaitForConnectionEstablishedAsync(cancellationToken))); } + /// Cancellation token. The cancellation should trigger on process terminatation. public async ValueTask> GetUpdateCapabilitiesAsync(CancellationToken cancellationToken) { if (clients is [var (singleClient, _)]) @@ -75,6 +89,7 @@ public async ValueTask> GetUpdateCapabilitiesAsync(Cancel return [.. results.SelectMany(r => r).Distinct(StringComparer.Ordinal).OrderBy(c => c)]; } + /// Cancellation token. The cancellation should trigger on process terminatation. public async ValueTask ApplyManagedCodeUpdatesAsync(ImmutableArray updates, bool isProcessSuspended, CancellationToken cancellationToken) { var anyFailure = false; @@ -131,6 +146,7 @@ public async ValueTask ApplyManagedCodeUpdatesAsync(ImmutableArrayCancellation token. The cancellation should trigger on process terminatation. public async ValueTask InitialUpdatesAppliedAsync(CancellationToken cancellationToken) { if (clients is [var (singleClient, _)]) @@ -143,6 +159,7 @@ public async ValueTask InitialUpdatesAppliedAsync(CancellationToken cancellation } } + /// Cancellation token. The cancellation should trigger on process terminatation. public async Task ApplyStaticAssetUpdatesAsync(IEnumerable<(string filePath, string relativeUrl, string assemblyName, bool isApplicationProject)> assets, CancellationToken cancellationToken) { if (browserRefreshServer != null) @@ -158,9 +175,14 @@ public async Task ApplyStaticAssetUpdatesAsync(IEnumerable<(string filePath, str ImmutableArray content; try { - content = ImmutableCollectionsMarshal.AsImmutableArray(await File.ReadAllBytesAsync(filePath, cancellationToken)); +#if NET + var blob = await File.ReadAllBytesAsync(filePath, cancellationToken); +#else + var blob = File.ReadAllBytes(filePath); +#endif + content = ImmutableCollectionsMarshal.AsImmutableArray(blob); } - catch (Exception e) + catch (Exception e) when (e is not OperationCanceledException) { ClientLogger.LogError("Failed to read file {FilePath}: {Message}", filePath, e.Message); continue; @@ -177,6 +199,7 @@ public async Task ApplyStaticAssetUpdatesAsync(IEnumerable<(string filePath, str } } + /// Cancellation token. The cancellation should trigger on process terminatation. public async ValueTask ApplyStaticAssetUpdatesAsync(ImmutableArray updates, bool isProcessSuspended, CancellationToken cancellationToken) { if (clients is [var (singleClient, _)]) @@ -189,6 +212,7 @@ public async ValueTask ApplyStaticAssetUpdatesAsync(ImmutableArrayCancellation token. The cancellation should trigger on process terminatation. public ValueTask ReportCompilationErrorsInApplicationAsync(ImmutableArray compilationErrors, CancellationToken cancellationToken) => browserRefreshServer?.ReportCompilationErrorsInBrowserAsync(compilationErrors, cancellationToken) ?? ValueTask.CompletedTask; } diff --git a/src/BuiltInTools/HotReloadClient/Logging/LogEvents.cs b/src/BuiltInTools/HotReloadClient/Logging/LogEvents.cs index a74e5097a6d5..f1672112866f 100644 --- a/src/BuiltInTools/HotReloadClient/Logging/LogEvents.cs +++ b/src/BuiltInTools/HotReloadClient/Logging/LogEvents.cs @@ -30,4 +30,5 @@ public static void Log(this ILogger logger, LogEvent logEvent, params object[] a public static readonly LogEvent UpdatingDiagnostics = Create(LogLevel.Debug, "Updating diagnostics."); public static readonly LogEvent SendingStaticAssetUpdateRequest = Create(LogLevel.Debug, "Sending static asset update request to connected browsers: '{0}'."); public static readonly LogEvent RefreshServerRunningAt = Create(LogLevel.Debug, "Refresh server running at {0}."); + public static readonly LogEvent ConnectedToRefreshServer = Create(LogLevel.Debug, "Connected to refresh server."); } diff --git a/src/BuiltInTools/HotReloadClient/Microsoft.DotNet.HotReload.Client.Package.csproj b/src/BuiltInTools/HotReloadClient/Microsoft.DotNet.HotReload.Client.Package.csproj index 8dd5b87ac79b..7996da1c7dbe 100644 --- a/src/BuiltInTools/HotReloadClient/Microsoft.DotNet.HotReload.Client.Package.csproj +++ b/src/BuiltInTools/HotReloadClient/Microsoft.DotNet.HotReload.Client.Package.csproj @@ -13,6 +13,7 @@ true + true true Microsoft.DotNet.HotReload.Client false diff --git a/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs b/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs index eda15d200f96..9c313bb02b50 100644 --- a/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs +++ b/src/BuiltInTools/HotReloadClient/Web/AbstractBrowserRefreshServer.cs @@ -163,7 +163,7 @@ public async Task WaitForClientConnectionAsync(CancellationToken cancellationTok }, progressCancellationSource.Token); // Work around lack of Task.WaitAsync(cancellationToken) on .NET Framework: - cancellationToken.Register(() => _browserConnected.SetCanceled()); + cancellationToken.Register(() => _browserConnected.TrySetCanceled()); try { diff --git a/src/BuiltInTools/HotReloadClient/Web/BrowserConnection.cs b/src/BuiltInTools/HotReloadClient/Web/BrowserConnection.cs index 507595eb89d1..498c26110089 100644 --- a/src/BuiltInTools/HotReloadClient/Web/BrowserConnection.cs +++ b/src/BuiltInTools/HotReloadClient/Web/BrowserConnection.cs @@ -37,7 +37,7 @@ public BrowserConnection(WebSocket clientSocket, string? sharedSecret, ILoggerFa ServerLogger = loggerFactory.CreateLogger(ServerLogComponentName, displayName); AgentLogger = loggerFactory.CreateLogger(AgentLogComponentName, displayName); - ServerLogger.LogDebug("Connected to referesh server."); + ServerLogger.Log(LogEvents.ConnectedToRefreshServer); } public void Dispose() diff --git a/src/BuiltInTools/dotnet-watch.slnf b/src/BuiltInTools/dotnet-watch.slnf index 09d529c61e9c..b7454eb8869a 100644 --- a/src/BuiltInTools/dotnet-watch.slnf +++ b/src/BuiltInTools/dotnet-watch.slnf @@ -18,10 +18,11 @@ "src\\BuiltInTools\\HotReloadClient\\Microsoft.DotNet.HotReload.Client.shproj", "src\\BuiltInTools\\dotnet-watch\\dotnet-watch.csproj", "test\\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests\\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests.csproj", + "test\\Microsoft.DotNet.HotReload.Client.Tests\\Microsoft.DotNet.HotReload.Client.Tests.csproj", "test\\Microsoft.Extensions.DotNetDeltaApplier.Tests\\Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj", "test\\Microsoft.NET.TestFramework\\Microsoft.NET.TestFramework.csproj", "test\\Microsoft.WebTools.AspireService.Tests\\Microsoft.WebTools.AspireService.Tests.csproj", - "test\\Microsoft.DotNet.HotReload.Client.Tests\\Microsoft.DotNet.HotReload.Client.Tests.csproj", + "test\\dotnet-watch-test-browser\\dotnet-watch-test-browser.csproj", "test\\dotnet-watch.Tests\\dotnet-watch.Tests.csproj" ] } diff --git a/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs b/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs index 1e84fb05a408..3942cb4e2ea7 100644 --- a/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs +++ b/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs @@ -43,6 +43,7 @@ private readonly struct Session(string dcpId, string sessionId, RunningProject r private readonly Dictionary _sessions = []; private int _sessionIdDispenser; + private volatile bool _isDisposed; public SessionManager(ProjectLauncher projectLauncher, ProjectOptions hostProjectOptions) @@ -82,10 +83,7 @@ public async ValueTask TerminateLaunchedProcessesAsync(CancellationToken cancell _sessions.Clear(); } - foreach (var session in sessions) - { - await TerminateSessionAsync(session, cancellationToken); - } + await Task.WhenAll(sessions.Select(TerminateSessionAsync)).WaitAsync(cancellationToken); } public IEnumerable<(string name, string value)> GetEnvironmentVariables() @@ -113,7 +111,9 @@ public async ValueTask StartProjectAsync(string dcpId, string se var processTerminationSource = new CancellationTokenSource(); var outputChannel = Channel.CreateUnbounded(s_outputChannelOptions); - var runningProject = await _projectLauncher.TryLaunchProcessAsync( + RunningProject? runningProject = null; + + runningProject = await _projectLauncher.TryLaunchProcessAsync( projectOptions, processTerminationSource, onOutput: line => @@ -121,6 +121,21 @@ public async ValueTask StartProjectAsync(string dcpId, string se var writeResult = outputChannel.Writer.TryWrite(line); Debug.Assert(writeResult); }, + onExit: async (processId, exitCode) => + { + // Project can be null if the process exists while it's being initialized. + if (runningProject?.IsRestarting == false) + { + try + { + await _service.NotifySessionEndedAsync(dcpId, sessionId, processId, exitCode, cancellationToken); + } + catch (OperationCanceledException) + { + // canceled on shutdown, ignore + } + } + }, restartOperation: cancellationToken => StartProjectAsync(dcpId, sessionId, projectOptions, isRestart: true, cancellationToken), cancellationToken); @@ -134,7 +149,7 @@ public async ValueTask StartProjectAsync(string dcpId, string se await _service.NotifySessionStartedAsync(dcpId, sessionId, runningProject.ProcessId, cancellationToken); // cancel reading output when the process terminates: - var outputReader = StartChannelReader(processTerminationSource.Token); + var outputReader = StartChannelReader(runningProject.ProcessExitedCancellationToken); lock (_guard) { @@ -159,7 +174,7 @@ async Task StartChannelReader(CancellationToken cancellationToken) } catch (Exception e) { - if (e is not OperationCanceledException) + if (!cancellationToken.IsCancellationRequested) { _logger.LogError("Unexpected error reading output of session '{SessionId}': {Exception}", sessionId, e); } @@ -185,18 +200,15 @@ async ValueTask IAspireServerEvents.StopSessionAsync(string dcpId, string _sessions.Remove(sessionId); } - await TerminateSessionAsync(session, cancellationToken); + await TerminateSessionAsync(session); return true; } - private async ValueTask TerminateSessionAsync(Session session, CancellationToken cancellationToken) + private async Task TerminateSessionAsync(Session session) { _logger.LogDebug("Stop session #{SessionId}", session.Id); - var exitCode = await _projectLauncher.TerminateProcessAsync(session.RunningProject, cancellationToken); - - // Wait until the started notification has been sent so that we don't send out of order notifications: - await _service.NotifySessionEndedAsync(session.DcpId, session.Id, session.RunningProject.ProcessId, exitCode, cancellationToken); + await session.RunningProject.TerminateAsync(isRestarting: false); // process termination should cancel output reader task: await session.OutputReader; diff --git a/src/BuiltInTools/dotnet-watch/Browser/BrowserLauncher.cs b/src/BuiltInTools/dotnet-watch/Browser/BrowserLauncher.cs index 422cb1b8a774..c841191e0208 100644 --- a/src/BuiltInTools/dotnet-watch/Browser/BrowserLauncher.cs +++ b/src/BuiltInTools/dotnet-watch/Browser/BrowserLauncher.cs @@ -4,13 +4,14 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using Microsoft.Build.Graph; using Microsoft.DotNet.HotReload; using Microsoft.Extensions.Logging; namespace Microsoft.DotNet.Watch; -internal sealed class BrowserLauncher(ILogger logger, EnvironmentOptions environmentOptions) +internal sealed class BrowserLauncher(ILogger logger, IProcessOutputReporter processOutputReporter, EnvironmentOptions environmentOptions) { // interlocked private ImmutableHashSet _browserLaunchAttempted = []; @@ -22,7 +23,7 @@ public void InstallBrowserLaunchTrigger( ProcessSpec processSpec, ProjectGraphNode projectNode, ProjectOptions projectOptions, - BrowserRefreshServer? server, + AbstractBrowserRefreshServer? server, CancellationToken cancellationToken) { if (!CanLaunchBrowser(projectOptions, out var launchProfile)) @@ -59,20 +60,15 @@ public static string GetLaunchUrl(string? profileLaunchUrl, string outputLaunchU Uri.TryCreate(outputLaunchUrl, UriKind.Absolute, out var launchUri) ? new Uri(launchUri, profileLaunchUrl).ToString() : outputLaunchUrl; - private void LaunchBrowser(string launchUrl, BrowserRefreshServer? server) + private void LaunchBrowser(string launchUrl, AbstractBrowserRefreshServer? server) { - var fileName = launchUrl; + var (fileName, arg, useShellExecute) = environmentOptions.BrowserPath is { } browserPath + ? (browserPath, launchUrl, false) + : (launchUrl, null, true); - var args = string.Empty; - if (environmentOptions.BrowserPath is { } browserPath) - { - args = fileName; - fileName = browserPath; - } + logger.Log(MessageDescriptor.LaunchingBrowser, fileName, arg); - logger.LogDebug("Launching browser: {FileName} {Args}", fileName, args); - - if (environmentOptions.TestFlags != TestFlags.None) + if (environmentOptions.TestFlags != TestFlags.None && environmentOptions.BrowserPath == null) { if (environmentOptions.TestFlags.HasFlag(TestFlags.MockBrowser)) { @@ -83,29 +79,23 @@ private void LaunchBrowser(string launchUrl, BrowserRefreshServer? server) return; } - var info = new ProcessStartInfo + // dotnet-watch, by default, relies on URL file association to launch browsers. On Windows and MacOS, this works fairly well + // where URLs are associated with the default browser. On Linux, this is a bit murky. + // From emperical observation, it's noted that failing to launch a browser results in either Process.Start returning a null-value + // or for the process to have immediately exited. + // We can use this to provide a helpful message. + var processSpec = new ProcessSpec() { - FileName = fileName, - Arguments = args, - UseShellExecute = true, + Executable = fileName, + Arguments = arg != null ? [arg] : [], + UseShellExecute = useShellExecute, + OnOutput = environmentOptions.TestFlags.HasFlag(TestFlags.RedirectBrowserOutput) ? processOutputReporter.ReportOutput : null, }; - try - { - using var browserProcess = Process.Start(info); - if (browserProcess is null or { HasExited: true }) - { - // dotnet-watch, by default, relies on URL file association to launch browsers. On Windows and MacOS, this works fairly well - // where URLs are associated with the default browser. On Linux, this is a bit murky. - // From emperical observation, it's noted that failing to launch a browser results in either Process.Start returning a null-value - // or for the process to have immediately exited. - // We can use this to provide a helpful message. - logger.LogInformation("Unable to launch the browser. Url '{Url}'.", launchUrl); - } - } - catch (Exception e) + using var browserProcess = ProcessRunner.TryStartProcess(processSpec, logger); + if (browserProcess is null or { HasExited: true }) { - logger.LogDebug("Failed to launch a browser: {Message}", e.Message); + logger.LogWarning("Unable to launch the browser. Url '{Url}'.", launchUrl); } } diff --git a/src/BuiltInTools/dotnet-watch/Build/ProjectGraphUtilities.cs b/src/BuiltInTools/dotnet-watch/Build/ProjectGraphUtilities.cs index ca8f616ebcce..56bcba3427e6 100644 --- a/src/BuiltInTools/dotnet-watch/Build/ProjectGraphUtilities.cs +++ b/src/BuiltInTools/dotnet-watch/Build/ProjectGraphUtilities.cs @@ -43,6 +43,10 @@ internal static class ProjectGraphUtilities } catch (Exception e) when (e is not OperationCanceledException) { + // ProejctGraph aggregates OperationCanceledException exception, + // throw here to propagate the cancellation. + cancellationToken.ThrowIfCancellationRequested(); + logger.LogDebug("Failed to load project graph."); if (e is AggregateException { InnerExceptions: var innerExceptions }) diff --git a/src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentOptions.cs b/src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentOptions.cs index f2fbfa7cabd3..72f787332533 100644 --- a/src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentOptions.cs +++ b/src/BuiltInTools/dotnet-watch/CommandLine/EnvironmentOptions.cs @@ -22,6 +22,11 @@ internal enum TestFlags /// This allows tests to trigger key based events. /// ReadKeyFromStdin = 1 << 3, + + /// + /// Redirects the output of the launched browser process to watch output. + /// + RedirectBrowserOutput = 1 << 4, } internal sealed record EnvironmentOptions( diff --git a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs index cedccb2351f1..55aea79f0d10 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs @@ -15,7 +15,6 @@ namespace Microsoft.DotNet.Watch internal sealed class CompilationHandler : IDisposable { public readonly IncrementalMSBuildWorkspace Workspace; - private readonly ILoggerFactory _loggerFactory; private readonly ILogger _logger; private readonly WatchHotReloadService _hotReloadService; private readonly ProcessRunner _processRunner; @@ -40,9 +39,8 @@ internal sealed class CompilationHandler : IDisposable private bool _isDisposed; - public CompilationHandler(ILoggerFactory loggerFactory, ILogger logger, ProcessRunner processRunner) + public CompilationHandler(ILogger logger, ProcessRunner processRunner) { - _loggerFactory = loggerFactory; _logger = logger; _processRunner = processRunner; Workspace = new IncrementalMSBuildWorkspace(logger); @@ -57,15 +55,8 @@ public void Dispose() public async ValueTask TerminateNonRootProcessesAndDispose(CancellationToken cancellationToken) { - _logger.LogDebug("Disposing remaining child processes."); - - var projectsToDispose = await TerminateNonRootProcessesAsync(projectPaths: null, cancellationToken); - - foreach (var project in projectsToDispose) - { - project.Dispose(); - } - + _logger.LogDebug("Terminating remaining child processes."); + await TerminateNonRootProcessesAsync(projectPaths: null, cancellationToken); Dispose(); } @@ -101,19 +92,35 @@ public async ValueTask StartSessionAsync(CancellationToken cancellationToken) CancellationToken cancellationToken) { var processExitedSource = new CancellationTokenSource(); - var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(processExitedSource.Token, cancellationToken); + + // Cancel process communication as soon as process termination is requested, shutdown is requested, or the process exits (whichever comes first). + // If we only cancel after we process exit event handler is triggered the pipe might have already been closed and may fail unexpectedly. + using var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(processTerminationSource.Token, processExitedSource.Token, cancellationToken); + var processCommunicationCancellationToken = processCommunicationCancellationSource.Token; // Dispose these objects on failure: - using var disposables = new Disposables([clients, processExitedSource, processCommunicationCancellationSource]); + using var disposables = new Disposables([clients, processExitedSource]); // It is important to first create the named pipe connection (Hot Reload client is the named pipe server) // and then start the process (named pipe client). Otherwise, the connection would fail. - clients.InitiateConnection(processCommunicationCancellationSource.Token); + clients.InitiateConnection(processCommunicationCancellationToken); - processSpec.OnExit += (_, _) => + RunningProject? publishedRunningProject = null; + + var previousOnExit = processSpec.OnExit; + processSpec.OnExit = async (processId, exitCode) => { - processExitedSource.Cancel(); - return ValueTask.CompletedTask; + // Await the previous action so that we only clean up after all requested "on exit" actions have been completed. + if (previousOnExit != null) + { + await previousOnExit(processId, exitCode); + } + + // Remove the running project if it has been published to _runningProjects (if it hasn't exited during initialization): + if (publishedRunningProject != null && RemoveRunningProject(publishedRunningProject)) + { + publishedRunningProject.Dispose(); + } }; var launchResult = new ProcessLaunchResult(); @@ -124,79 +131,88 @@ public async ValueTask StartSessionAsync(CancellationToken cancellationToken) return null; } - // Wait for agent to create the name pipe and send capabilities over. - // the agent blocks the app execution until initial updates are applied (if any). - var capabilities = await clients.GetUpdateCapabilitiesAsync(processCommunicationCancellationSource.Token); - - var runningProject = new RunningProject( - projectNode, - projectOptions, - clients, - runningProcess, - launchResult.ProcessId.Value, - processExitedSource: processExitedSource, - processTerminationSource: processTerminationSource, - restartOperation: restartOperation, - disposables: [processCommunicationCancellationSource], - capabilities); - var projectPath = projectNode.ProjectInstance.FullPath; - // ownership transferred to running project: - disposables.Items.Clear(); - disposables.Items.Add(runningProject); - - var appliedUpdateCount = 0; - while (true) + try { - // Observe updates that need to be applied to the new process - // and apply them before adding it to running processes. - // Do not block on udpates being made to other processes to avoid delaying the new process being up-to-date. - var updatesToApply = _previousUpdates.Skip(appliedUpdateCount).ToImmutableArray(); - if (updatesToApply.Any()) + // Wait for agent to create the name pipe and send capabilities over. + // the agent blocks the app execution until initial updates are applied (if any). + var capabilities = await clients.GetUpdateCapabilitiesAsync(processCommunicationCancellationToken); + + var runningProject = new RunningProject( + projectNode, + projectOptions, + clients, + runningProcess, + launchResult.ProcessId.Value, + processExitedSource: processExitedSource, + processTerminationSource: processTerminationSource, + restartOperation: restartOperation, + capabilities); + + // ownership transferred to running project: + disposables.Items.Clear(); + disposables.Items.Add(runningProject); + + var appliedUpdateCount = 0; + while (true) { - await clients.ApplyManagedCodeUpdatesAsync(ToManagedCodeUpdates(updatesToApply), isProcessSuspended: false, processCommunicationCancellationSource.Token); - } - - appliedUpdateCount += updatesToApply.Length; - - lock (_runningProjectsAndUpdatesGuard) - { - ObjectDisposedException.ThrowIf(_isDisposed, this); - - // More updates might have come in while we have been applying updates. - // If so, continue updating. - if (_previousUpdates.Count > appliedUpdateCount) + // Observe updates that need to be applied to the new process + // and apply them before adding it to running processes. + // Do not block on udpates being made to other processes to avoid delaying the new process being up-to-date. + var updatesToApply = _previousUpdates.Skip(appliedUpdateCount).ToImmutableArray(); + if (updatesToApply.Any()) { - continue; + await clients.ApplyManagedCodeUpdatesAsync(ToManagedCodeUpdates(updatesToApply), isProcessSuspended: false, processCommunicationCancellationToken); } - // Only add the running process after it has been up-to-date. - // This will prevent new updates being applied before we have applied all the previous updates. - if (!_runningProjects.TryGetValue(projectPath, out var projectInstances)) + appliedUpdateCount += updatesToApply.Length; + + lock (_runningProjectsAndUpdatesGuard) { - projectInstances = []; - } + ObjectDisposedException.ThrowIf(_isDisposed, this); - _runningProjects = _runningProjects.SetItem(projectPath, projectInstances.Add(runningProject)); + // More updates might have come in while we have been applying updates. + // If so, continue updating. + if (_previousUpdates.Count > appliedUpdateCount) + { + continue; + } - // ownership transferred to _runningProjects - disposables.Items.Clear(); - break; + // Only add the running process after it has been up-to-date. + // This will prevent new updates being applied before we have applied all the previous updates. + if (!_runningProjects.TryGetValue(projectPath, out var projectInstances)) + { + projectInstances = []; + } + + _runningProjects = _runningProjects.SetItem(projectPath, projectInstances.Add(runningProject)); + + // ownership transferred to _runningProjects + publishedRunningProject = runningProject; + disposables.Items.Clear(); + break; + } } - } - // Notifies the agent that it can unblock the execution of the process: - await clients.InitialUpdatesAppliedAsync(cancellationToken); + // Notifies the agent that it can unblock the execution of the process: + await clients.InitialUpdatesAppliedAsync(processCommunicationCancellationToken); - // If non-empty solution is loaded into the workspace (a Hot Reload session is active): - if (Workspace.CurrentSolution is { ProjectIds: not [] } currentSolution) + // If non-empty solution is loaded into the workspace (a Hot Reload session is active): + if (Workspace.CurrentSolution is { ProjectIds: not [] } currentSolution) + { + // Preparing the compilation is a perf optimization. We can skip it if the session hasn't been started yet. + PrepareCompilations(currentSolution, projectPath, cancellationToken); + } + + return runningProject; + } + catch (OperationCanceledException) when (processExitedSource.IsCancellationRequested) { - // Preparing the compilation is a perf optimization. We can skip it if the session hasn't been started yet. - PrepareCompilations(currentSolution, projectPath, cancellationToken); + // Process exited during initialization. This should not happen since we control the process during this time. + _logger.LogError("Failed to launch '{ProjectPath}'. Process {PID} exited during initialization.", projectPath, launchResult.ProcessId); + return null; } - - return runningProject; } private ImmutableArray GetAggregateCapabilities() @@ -229,7 +245,7 @@ private static void PrepareCompilations(Solution solution, string projectPath, C ImmutableArray projectUpdates, ImmutableArray projectsToRebuild, ImmutableArray projectsToRedeploy, - ImmutableArray terminatedProjects)> HandleManagedCodeChangesAsync( + ImmutableArray projectsToRestart)> HandleManagedCodeChangesAsync( bool autoRestart, Func, CancellationToken, Task> restartPrompt, CancellationToken cancellationToken) @@ -284,11 +300,11 @@ private static void PrepareCompilations(Solution solution, string projectPath, C // Terminate all tracked processes that need to be restarted, // except for the root process, which will terminate later on. - var terminatedProjects = updates.ProjectsToRestart.IsEmpty + var projectsToRestart = updates.ProjectsToRestart.IsEmpty ? [] : await TerminateNonRootProcessesAsync(updates.ProjectsToRestart.Select(e => currentSolution.GetProject(e.Key)!.FilePath!), cancellationToken); - return (updates.ProjectUpdates, projectsToRebuild, projectsToRedeploy, terminatedProjects); + return (updates.ProjectUpdates, projectsToRebuild, projectsToRedeploy, projectsToRestart); } public async ValueTask ApplyUpdatesAsync(ImmutableArray updates, CancellationToken cancellationToken) @@ -312,10 +328,10 @@ await ForEachProjectAsync(projectsToUpdate, async (runningProject, cancellationT { try { - using var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(runningProject.ProcessExitedSource.Token, cancellationToken); + using var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(runningProject.ProcessExitedCancellationToken, cancellationToken); await runningProject.Clients.ApplyManagedCodeUpdatesAsync(ToManagedCodeUpdates(updates), isProcessSuspended: false, processCommunicationCancellationSource.Token); } - catch (OperationCanceledException) when (runningProject.ProcessExitedSource.Token.IsCancellationRequested && !cancellationToken.IsCancellationRequested) + catch (OperationCanceledException) when (runningProject.ProcessExitedCancellationToken.IsCancellationRequested && !cancellationToken.IsCancellationRequested) { runningProject.Clients.ClientLogger.Log(MessageDescriptor.HotReloadCanceledProcessExited); } @@ -363,7 +379,7 @@ private async ValueTask DisplayResultsAsync(WatchHotReloadService.Updates2 updat _logger.Log(MessageDescriptor.RestartNeededToApplyChanges); } - var diagnosticsToDisplayInApp = new List(); + var errorsToDisplayInApp = new List(); // Display errors first, then warnings: ReportCompilationDiagnostics(DiagnosticSeverity.Error); @@ -373,7 +389,7 @@ private async ValueTask DisplayResultsAsync(WatchHotReloadService.Updates2 updat // report or clear diagnostics in the browser UI await ForEachProjectAsync( _runningProjects, - (project, cancellationToken) => project.Clients.ReportCompilationErrorsInApplicationAsync([.. diagnosticsToDisplayInApp], cancellationToken).AsTask() ?? Task.CompletedTask, + (project, cancellationToken) => project.Clients.ReportCompilationErrorsInApplicationAsync([.. errorsToDisplayInApp], cancellationToken).AsTask() ?? Task.CompletedTask, cancellationToken); void ReportCompilationDiagnostics(DiagnosticSeverity severity) @@ -437,16 +453,20 @@ void ReportRudeEdits() bool IsAutoRestartEnabled(ProjectId id) => runningProjectInfos.TryGetValue(id, out var info) && info.RestartWhenChangesHaveNoEffect; - void ReportDiagnostic(Diagnostic diagnostic, MessageDescriptor descriptor, string prefix = "") + void ReportDiagnostic(Diagnostic diagnostic, MessageDescriptor descriptor, string autoPrefix = "") { var display = CSharpDiagnosticFormatter.Instance.Format(diagnostic); - var args = new[] { prefix, display }; + var args = new[] { autoPrefix, display }; _logger.Log(descriptor, args); - if (descriptor.Severity != MessageSeverity.None) + if (autoPrefix != "") + { + errorsToDisplayInApp.Add(MessageDescriptor.RestartingApplicationToApplyChanges.GetMessage()); + } + else if (descriptor.Severity != MessageSeverity.None) { - diagnosticsToDisplayInApp.Add(descriptor.GetMessage(args)); + errorsToDisplayInApp.Add(descriptor.GetMessage(args)); } } @@ -528,7 +548,8 @@ public async ValueTask HandleStaticAssetChangesAsync(IReadOnlyList { var (runningProject, assets) = entry; - await runningProject.Clients.ApplyStaticAssetUpdatesAsync(assets, cancellationToken); + using var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(runningProject.ProcessExitedCancellationToken, cancellationToken); + await runningProject.Clients.ApplyStaticAssetUpdatesAsync(assets, processCommunicationCancellationSource.Token); }); await Task.WhenAll(tasks).WaitAsync(cancellationToken); @@ -539,76 +560,64 @@ public async ValueTask HandleStaticAssetChangesAsync(IReadOnlyList - /// Terminates all processes launched for projects with , + /// Terminates all processes launched for non-root projects with , /// or all running non-root project processes if is null. /// /// Removes corresponding entries from . /// /// Does not terminate the root project. /// + /// All processes (including root) to be restarted. internal async ValueTask> TerminateNonRootProcessesAsync( IEnumerable? projectPaths, CancellationToken cancellationToken) { ImmutableArray projectsToRestart = []; - UpdateRunningProjects(runningProjectsByPath => + lock (_runningProjectsAndUpdatesGuard) { - if (projectPaths == null) - { - projectsToRestart = _runningProjects.SelectMany(entry => entry.Value).Where(p => !p.Options.IsRootProject).ToImmutableArray(); - return _runningProjects.Clear(); - } - - projectsToRestart = projectPaths.SelectMany(path => _runningProjects.TryGetValue(path, out var array) ? array : []).ToImmutableArray(); - return runningProjectsByPath.RemoveRange(projectPaths); - }); + projectsToRestart = projectPaths == null + ? [.. _runningProjects.SelectMany(entry => entry.Value)] + : [.. projectPaths.SelectMany(path => _runningProjects.TryGetValue(path, out var array) ? array : [])]; + } // Do not terminate root process at this time - it would signal the cancellation token we are currently using. // The process will be restarted later on. - var projectsToTerminate = projectsToRestart.Where(p => !p.Options.IsRootProject); - - // wait for all processes to exit to release their resources, so we can rebuild: - _ = await TerminateRunningProjects(projectsToTerminate, cancellationToken); + // Wait for all processes to exit to release their resources, so we can rebuild. + await Task.WhenAll(projectsToRestart.Where(p => !p.Options.IsRootProject).Select(p => p.TerminateAsync(isRestarting: true))).WaitAsync(cancellationToken); return projectsToRestart; } - /// - /// Terminates process of the given . - /// Removes corresponding entries from . - /// - /// Should not be called with the root project. - /// - /// Exit code of the terminated process. - internal async ValueTask TerminateNonRootProcessAsync(RunningProject project, CancellationToken cancellationToken) + private bool RemoveRunningProject(RunningProject project) { - Debug.Assert(!project.Options.IsRootProject); - var projectPath = project.ProjectNode.ProjectInstance.FullPath; - UpdateRunningProjects(runningProjectsByPath => + return UpdateRunningProjects(runningProjectsByPath => { - if (!runningProjectsByPath.TryGetValue(projectPath, out var runningProjects) || - runningProjects.Remove(project) is var updatedRunningProjects && runningProjects == updatedRunningProjects) + if (!runningProjectsByPath.TryGetValue(projectPath, out var runningInstances)) { - _logger.LogDebug("Ignoring an attempt to terminate process {ProcessId} of project '{Path}' that has no associated running processes.", project.ProcessId, projectPath); return runningProjectsByPath; } + var updatedRunningProjects = runningInstances.Remove(project); return updatedRunningProjects is [] ? runningProjectsByPath.Remove(projectPath) : runningProjectsByPath.SetItem(projectPath, updatedRunningProjects); }); - - // wait for all processes to exit to release their resources: - return (await TerminateRunningProjects([project], cancellationToken)).Single(); } - private void UpdateRunningProjects(Func>, ImmutableDictionary>> updater) + private bool UpdateRunningProjects(Func>, ImmutableDictionary>> updater) { lock (_runningProjectsAndUpdatesGuard) { - _runningProjects = updater(_runningProjects); + var newRunningProjects = updater(_runningProjects); + if (newRunningProjects != _runningProjects) + { + _runningProjects = newRunningProjects; + return true; + } + + return false; } } @@ -620,12 +629,6 @@ public bool TryGetRunningProject(string projectPath, out ImmutableArray> TerminateRunningProjects(IEnumerable projects, CancellationToken cancellationToken) - { - // wait for all tasks to complete: - return await Task.WhenAll(projects.Select(p => p.TerminateAsync().AsTask())).WaitAsync(cancellationToken); - } - private static Task ForEachProjectAsync(ImmutableDictionary> projects, Func action, CancellationToken cancellationToken) => Task.WhenAll(projects.SelectMany(entry => entry.Value).Select(project => action(project, cancellationToken))).WaitAsync(cancellationToken); diff --git a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs index 764a371ab309..6f4b8803ed91 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/HotReloadDotNetWatcher.cs @@ -108,7 +108,7 @@ public async Task WatchAsync(CancellationToken shutdownCancellationToken) } var projectMap = new ProjectNodeMap(evaluationResult.ProjectGraph, _context.Logger); - compilationHandler = new CompilationHandler(_context.LoggerFactory, _context.Logger, _context.ProcessRunner); + compilationHandler = new CompilationHandler(_context.Logger, _context.ProcessRunner); var scopedCssFileHandler = new ScopedCssFileHandler(_context.Logger, _context.BuildLogger, projectMap, _context.BrowserRefreshServerFactory, _context.Options, _context.EnvironmentOptions); var projectLauncher = new ProjectLauncher(_context, projectMap, compilationHandler, iteration); evaluationResult.ItemExclusions.Report(_context.Logger); @@ -127,6 +127,7 @@ public async Task WatchAsync(CancellationToken shutdownCancellationToken) rootProjectOptions, rootProcessTerminationSource, onOutput: null, + onExit: null, restartOperation: new RestartOperation(_ => throw new InvalidOperationException("Root project shouldn't be restarted")), iterationCancellationToken); @@ -138,7 +139,7 @@ public async Task WatchAsync(CancellationToken shutdownCancellationToken) } // Cancel iteration as soon as the root process exits, so that we don't spent time loading solution, etc. when the process is already dead. - rootRunningProject.ProcessExitedSource.Token.Register(() => iterationCancellationSource.Cancel()); + rootRunningProject.ProcessExitedCancellationToken.Register(() => iterationCancellationSource.Cancel()); if (shutdownCancellationToken.IsCancellationRequested) { @@ -146,11 +147,7 @@ public async Task WatchAsync(CancellationToken shutdownCancellationToken) return; } - try - { - await rootRunningProject.WaitForProcessRunningAsync(iterationCancellationToken); - } - catch (OperationCanceledException) when (rootRunningProject.ProcessExitedSource.Token.IsCancellationRequested) + if (!await rootRunningProject.WaitForProcessRunningAsync(iterationCancellationToken)) { // Process might have exited while we were trying to communicate with it. // Cancel the iteration, but wait for a file change before starting a new one. @@ -384,19 +381,7 @@ await Task.WhenAll( projectsToRestart.Select(async runningProject => { var newRunningProject = await runningProject.RestartOperation(shutdownCancellationToken); - - try - { - await newRunningProject.WaitForProcessRunningAsync(shutdownCancellationToken); - } - catch (OperationCanceledException) when (!shutdownCancellationToken.IsCancellationRequested) - { - // Process might have exited while we were trying to communicate with it. - } - finally - { - runningProject.Dispose(); - } + _ = await newRunningProject.WaitForProcessRunningAsync(shutdownCancellationToken); })) .WaitAsync(shutdownCancellationToken); @@ -546,7 +531,7 @@ async Task> CaptureChangedFilesSnapshot(ImmutableArra if (rootRunningProject != null) { - await rootRunningProject.TerminateAsync(); + await rootRunningProject.TerminateAsync(isRestarting: false); } if (runtimeProcessLauncher != null) @@ -554,8 +539,6 @@ async Task> CaptureChangedFilesSnapshot(ImmutableArra await runtimeProcessLauncher.DisposeAsync(); } - rootRunningProject?.Dispose(); - if (waitForFileChangeBeforeRestarting && !shutdownCancellationToken.IsCancellationRequested && !forceRestartCancellationSource.IsCancellationRequested) diff --git a/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs b/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs index 3583dc07b8d8..54ae7d130229 100644 --- a/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs +++ b/src/BuiltInTools/dotnet-watch/Process/ProcessRunner.cs @@ -9,12 +9,22 @@ namespace Microsoft.DotNet.Watch { internal sealed class ProcessRunner(TimeSpan processCleanupTimeout) { - private sealed class ProcessState + private sealed class ProcessState(Process process) : IDisposable { + public Process Process { get; } = process; + public int ProcessId; public bool HasExited; + + // True if Ctrl+C was sent to the process on Windows. + public bool SentWindowsCtrlC; + + public void Dispose() + => Process.Dispose(); } + private const int CtlrCExitCode = unchecked((int)0xC000013A); + // For testing purposes only, lock on access. private static readonly HashSet s_runningApplicationProcesses = []; @@ -31,67 +41,32 @@ public static IReadOnlyCollection GetRunningApplicationProcesses() /// public async Task RunAsync(ProcessSpec processSpec, ILogger logger, ProcessLaunchResult? launchResult, CancellationToken processTerminationToken) { - var state = new ProcessState(); var stopwatch = new Stopwatch(); - - var onOutput = processSpec.OnOutput; - - using var process = CreateProcess(processSpec, onOutput, state, logger); - stopwatch.Start(); - Exception? launchException = null; - try - { - if (!process.Start()) - { - throw new InvalidOperationException("Process can't be started."); - } - - state.ProcessId = process.Id; - - if (processSpec.IsUserApplication) - { - lock (s_runningApplicationProcesses) - { - s_runningApplicationProcesses.Add(state.ProcessId); - } - } - - if (onOutput != null) - { - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - } - } - catch (Exception e) - { - launchException = e; - } - - var argsDisplay = processSpec.GetArgumentsDisplay(); - if (launchException == null) + using var state = TryStartProcessImpl(processSpec, logger); + if (state == null) { - logger.Log(MessageDescriptor.LaunchedProcess, processSpec.Executable, argsDisplay, state.ProcessId); - } - else - { - logger.Log(MessageDescriptor.FailedToLaunchProcess, processSpec.Executable, argsDisplay, launchException.Message); return int.MinValue; } - if (launchResult != null) + if (processSpec.IsUserApplication) { - launchResult.ProcessId = process.Id; + lock (s_runningApplicationProcesses) + { + s_runningApplicationProcesses.Add(state.ProcessId); + } } + launchResult?.ProcessId = state.ProcessId; + int? exitCode = null; try { try { - await process.WaitForExitAsync(processTerminationToken); + await state.Process.WaitForExitAsync(processTerminationToken); } catch (OperationCanceledException) { @@ -99,7 +74,7 @@ public async Task RunAsync(ProcessSpec processSpec, ILogger logger, Process // Either Ctrl+C was pressed or the process is being restarted. // Non-cancellable to not leave orphaned processes around blocking resources: - await TerminateProcessAsync(process, processSpec, state, logger, CancellationToken.None); + await TerminateProcessAsync(state.Process, processSpec, state, logger, CancellationToken.None); } } catch (Exception e) @@ -125,18 +100,18 @@ public async Task RunAsync(ProcessSpec processSpec, ILogger logger, Process try { - exitCode = process.ExitCode; + exitCode = state.Process.ExitCode; } catch { exitCode = null; } - logger.Log(MessageDescriptor.ProcessRunAndExited, process.Id, stopwatch.ElapsedMilliseconds, exitCode); + logger.Log(MessageDescriptor.ProcessRunAndExited, state.ProcessId, stopwatch.ElapsedMilliseconds, exitCode); if (processSpec.IsUserApplication) { - if (exitCode == 0) + if (exitCode == 0 || state.SentWindowsCtrlC && exitCode == CtlrCExitCode) { logger.Log(MessageDescriptor.Exited); } @@ -159,21 +134,28 @@ public async Task RunAsync(ProcessSpec processSpec, ILogger logger, Process return exitCode ?? int.MinValue; } - private static Process CreateProcess(ProcessSpec processSpec, Action? onOutput, ProcessState state, ILogger logger) + internal static Process? TryStartProcess(ProcessSpec processSpec, ILogger logger) + => TryStartProcessImpl(processSpec, logger)?.Process; + + private static ProcessState? TryStartProcessImpl(ProcessSpec processSpec, ILogger logger) { + var onOutput = processSpec.OnOutput; + var process = new Process { EnableRaisingEvents = true, StartInfo = { FileName = processSpec.Executable, - UseShellExecute = false, + UseShellExecute = processSpec.UseShellExecute, WorkingDirectory = processSpec.WorkingDirectory, RedirectStandardOutput = onOutput != null, RedirectStandardError = onOutput != null, } }; + var state = new ProcessState(process); + if (processSpec.IsUserApplication && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { process.StartInfo.CreateNewProcessGroup = true; @@ -229,14 +211,38 @@ private static Process CreateProcess(ProcessSpec processSpec, Action }; } - return process; + var argsDisplay = processSpec.GetArgumentsDisplay(); + + try + { + if (!process.Start()) + { + throw new InvalidOperationException("Process can't be started."); + } + state.ProcessId = process.Id; + + if (onOutput != null) + { + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + } + + logger.Log(MessageDescriptor.LaunchedProcess, processSpec.Executable, argsDisplay, state.ProcessId); + return state; + } + catch (Exception e) + { + logger.Log(MessageDescriptor.FailedToLaunchProcess, processSpec.Executable, argsDisplay, e.Message); + + state.Dispose(); + return null; + } } private async ValueTask TerminateProcessAsync(Process process, ProcessSpec processSpec, ProcessState state, ILogger logger, CancellationToken cancellationToken) { var forceOnly = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && !processSpec.IsUserApplication; - // Ctrl+C hasn't been sent. TerminateProcess(process, state, logger, forceOnly); if (forceOnly) @@ -356,7 +362,11 @@ private static void TerminateWindowsProcess(Process process, ProcessState state, else { var error = ProcessUtilities.SendWindowsCtrlCEvent(state.ProcessId); - if (error != null) + if (error == null) + { + state.SentWindowsCtrlC = true; + } + else { logger.Log(MessageDescriptor.FailedToSendSignalToProcess, signalName, state.ProcessId, error); } diff --git a/src/BuiltInTools/dotnet-watch/Process/ProcessSpec.cs b/src/BuiltInTools/dotnet-watch/Process/ProcessSpec.cs index b780f2770029..b3e1eaa1a6a6 100644 --- a/src/BuiltInTools/dotnet-watch/Process/ProcessSpec.cs +++ b/src/BuiltInTools/dotnet-watch/Process/ProcessSpec.cs @@ -8,12 +8,13 @@ internal sealed class ProcessSpec { public string? Executable { get; set; } public string? WorkingDirectory { get; set; } - public Dictionary EnvironmentVariables { get; } = new(); + public Dictionary EnvironmentVariables { get; } = []; public IReadOnlyList? Arguments { get; set; } public string? EscapedArguments { get; set; } public Action? OnOutput { get; set; } public ProcessExitAction? OnExit { get; set; } public CancellationToken CancelOutputCapture { get; set; } + public bool UseShellExecute { get; set; } = false; /// /// True if the process is a user application, false if it is a helper process (e.g. dotnet build). diff --git a/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs b/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs index 3299710fd18f..475f7e2fe1d5 100644 --- a/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs +++ b/src/BuiltInTools/dotnet-watch/Process/ProjectLauncher.cs @@ -30,6 +30,7 @@ public EnvironmentOptions EnvironmentOptions ProjectOptions projectOptions, CancellationTokenSource processTerminationSource, Action? onOutput, + ProcessExitAction? onExit, RestartOperation restartOperation, CancellationToken cancellationToken) { @@ -66,12 +67,14 @@ public EnvironmentOptions EnvironmentOptions IsUserApplication = true, WorkingDirectory = projectOptions.WorkingDirectory, OnOutput = onOutput, + OnExit = onExit, }; // Stream output lines to the process output reporter. // The reporter synchronizes the output of the process with the logger output, // so that the printed lines don't interleave. - processSpec.OnOutput += line => + // Only send the output to the reporter if no custom output handler was provided (e.g. for Aspire child processes). + processSpec.OnOutput ??= line => { context.ProcessOutputReporter.ReportOutput(context.ProcessOutputReporter.PrefixProcessOutput ? line with { Content = $"[{projectDisplayName}] {line.Content}" } : line); }; @@ -129,7 +132,4 @@ private static IReadOnlyList GetProcessArguments(ProjectOptions projectO arguments.AddRange(projectOptions.CommandArguments); return arguments; } - - public ValueTask TerminateProcessAsync(RunningProject project, CancellationToken cancellationToken) - => compilationHandler.TerminateNonRootProcessAsync(project, cancellationToken); } diff --git a/src/BuiltInTools/dotnet-watch/Process/RunningProject.cs b/src/BuiltInTools/dotnet-watch/Process/RunningProject.cs index 46ecd2c629a1..c2662ceec1f9 100644 --- a/src/BuiltInTools/dotnet-watch/Process/RunningProject.cs +++ b/src/BuiltInTools/dotnet-watch/Process/RunningProject.cs @@ -3,8 +3,10 @@ using System.Collections.Immutable; +using System.Diagnostics; using Microsoft.Build.Graph; using Microsoft.DotNet.HotReload; +using Microsoft.Extensions.Logging; namespace Microsoft.DotNet.Watch { @@ -19,7 +21,6 @@ internal sealed class RunningProject( CancellationTokenSource processExitedSource, CancellationTokenSource processTerminationSource, RestartOperation restartOperation, - IReadOnlyList disposables, ImmutableArray capabilities) : IDisposable { public readonly ProjectGraphNode ProjectNode = projectNode; @@ -31,44 +32,64 @@ internal sealed class RunningProject( public readonly RestartOperation RestartOperation = restartOperation; /// - /// Cancellation source triggered when the process exits. + /// Cancellation token triggered when the process exits. + /// Stores the token to allow callers to use the token even after the source has been disposed. /// - public readonly CancellationTokenSource ProcessExitedSource = processExitedSource; + public CancellationToken ProcessExitedCancellationToken = processExitedSource.Token; /// - /// Cancellation source to use to terminate the process. + /// Set to true when the process termination is being requested so that it can be restarted within + /// the Hot Reload session (i.e. without restarting the root project). /// - public readonly CancellationTokenSource ProcessTerminationSource = processTerminationSource; + public bool IsRestarting { get; private set; } + + private volatile bool _isDisposed; /// - /// Misc disposable object to dispose when the object is disposed. + /// Disposes the project. Can occur unexpectedly whenever the process exits. + /// Must only be called once per project. /// - private readonly IReadOnlyList _disposables = disposables; - public void Dispose() { - Clients.Dispose(); - ProcessTerminationSource.Dispose(); - ProcessExitedSource.Dispose(); + ObjectDisposedException.ThrowIf(_isDisposed, this); - foreach (var disposable in _disposables) - { - disposable.Dispose(); - } + _isDisposed = true; + processExitedSource.Cancel(); + + Clients.Dispose(); + processTerminationSource.Dispose(); + processExitedSource.Dispose(); } /// /// Waits for the application process to start. /// Ensures that the build has been complete and the build outputs are available. + /// Returns false if the process has exited before the connection was established. /// - public async ValueTask WaitForProcessRunningAsync(CancellationToken cancellationToken) + public async ValueTask WaitForProcessRunningAsync(CancellationToken cancellationToken) { - await Clients.WaitForConnectionEstablishedAsync(cancellationToken); + using var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, ProcessExitedCancellationToken); + + try + { + await Clients.WaitForConnectionEstablishedAsync(processCommunicationCancellationSource.Token); + return true; + } + catch (OperationCanceledException) when (ProcessExitedCancellationToken.IsCancellationRequested) + { + return false; + } } - public async ValueTask TerminateAsync() + public async Task TerminateAsync(bool isRestarting) { - ProcessTerminationSource.Cancel(); + IsRestarting = isRestarting; + + if (!_isDisposed) + { + processTerminationSource.Cancel(); + } + return await RunningProcess; } } diff --git a/src/BuiltInTools/dotnet-watch/Program.cs b/src/BuiltInTools/dotnet-watch/Program.cs index b2b9bd03bbd9..f67a360a3a32 100644 --- a/src/BuiltInTools/dotnet-watch/Program.cs +++ b/src/BuiltInTools/dotnet-watch/Program.cs @@ -240,7 +240,7 @@ internal DotNetWatchContext CreateContext(ProcessRunner processRunner) EnvironmentOptions = environmentOptions, RootProjectOptions = rootProjectOptions, BrowserRefreshServerFactory = new BrowserRefreshServerFactory(), - BrowserLauncher = new BrowserLauncher(logger, environmentOptions), + BrowserLauncher = new BrowserLauncher(logger, processOutputReporter, environmentOptions), }; } diff --git a/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json b/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json index b6981fd0ae9d..9e28729eb807 100644 --- a/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json +++ b/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json @@ -3,7 +3,7 @@ "dotnet-watch": { "commandName": "Project", "commandLineArgs": "--verbose -bl", - "workingDirectory": "$(RepoRoot)src\\Assets\\TestProjects\\BlazorWasmWithLibrary\\blazorwasm", + "workingDirectory": "C:\\bugs\\9756\\aspire-watch-start-issue\\Aspire.AppHost", "environmentVariables": { "DOTNET_WATCH_DEBUG_SDK_DIRECTORY": "$(RepoRoot)artifacts\\bin\\redist\\$(Configuration)\\dotnet\\sdk\\$(Version)", "DCP_IDE_REQUEST_TIMEOUT_SECONDS": "100000", diff --git a/src/BuiltInTools/dotnet-watch/UI/ConsoleReporter.cs b/src/BuiltInTools/dotnet-watch/UI/ConsoleReporter.cs index 84cc357ad35a..198e518590bb 100644 --- a/src/BuiltInTools/dotnet-watch/UI/ConsoleReporter.cs +++ b/src/BuiltInTools/dotnet-watch/UI/ConsoleReporter.cs @@ -56,24 +56,24 @@ public void Report(EventId id, Emoji emoji, MessageSeverity severity, string mes { case MessageSeverity.Error: // Use stdout for error messages to preserve ordering with respect to other output. - WriteLine(console.Out, message, ConsoleColor.Red, emoji); + WriteLine(console.Error, message, ConsoleColor.Red, emoji); break; case MessageSeverity.Warning: - WriteLine(console.Out, message, ConsoleColor.Yellow, emoji); + WriteLine(console.Error, message, ConsoleColor.Yellow, emoji); break; case MessageSeverity.Output: if (!IsQuiet) { - WriteLine(console.Out, message, color: null, emoji); + WriteLine(console.Error, message, color: null, emoji); } break; case MessageSeverity.Verbose: if (IsVerbose) { - WriteLine(console.Out, message, ConsoleColor.DarkGray, emoji); + WriteLine(console.Error, message, ConsoleColor.DarkGray, emoji); } break; } diff --git a/src/BuiltInTools/dotnet-watch/UI/IReporter.cs b/src/BuiltInTools/dotnet-watch/UI/IReporter.cs index c6f64394da88..3d60e288a977 100644 --- a/src/BuiltInTools/dotnet-watch/UI/IReporter.cs +++ b/src/BuiltInTools/dotnet-watch/UI/IReporter.cs @@ -209,9 +209,12 @@ public MessageDescriptor WithSeverityWhen(MessageSeverity severity, bool conditi public static readonly MessageDescriptor UpdatingDiagnostics = Create(LogEvents.UpdatingDiagnostics, Emoji.Default); public static readonly MessageDescriptor FailedToReceiveResponseFromConnectedBrowser = Create(LogEvents.FailedToReceiveResponseFromConnectedBrowser, Emoji.Default); public static readonly MessageDescriptor NoBrowserConnected = Create(LogEvents.NoBrowserConnected, Emoji.Default); + public static readonly MessageDescriptor LaunchingBrowser = Create("Launching browser: {0} {1}", Emoji.Default, MessageSeverity.Verbose); public static readonly MessageDescriptor RefreshingBrowser = Create(LogEvents.RefreshingBrowser, Emoji.Default); public static readonly MessageDescriptor ReloadingBrowser = Create(LogEvents.ReloadingBrowser, Emoji.Default); public static readonly MessageDescriptor RefreshServerRunningAt = Create(LogEvents.RefreshServerRunningAt, Emoji.Default); + public static readonly MessageDescriptor ConnectedToRefreshServer = Create(LogEvents.ConnectedToRefreshServer, Emoji.Default); + public static readonly MessageDescriptor RestartingApplicationToApplyChanges = Create("Restarting application to apply changes ...", Emoji.Default, MessageSeverity.Output); public static readonly MessageDescriptor IgnoringChangeInHiddenDirectory = Create("Ignoring change in hidden directory '{0}': {1} '{2}'", Emoji.Watch, MessageSeverity.Verbose); public static readonly MessageDescriptor IgnoringChangeInOutputDirectory = Create("Ignoring change in output directory: {0} '{1}'", Emoji.Watch, MessageSeverity.Verbose); public static readonly MessageDescriptor IgnoringChangeInExcludedFile = Create("Ignoring change in excluded file '{0}': {1}. Path matches {2} glob '{3}' set in '{4}'.", Emoji.Watch, MessageSeverity.Verbose); diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs index 69b532afb0bb..0c5aef161367 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Constants.cs @@ -35,6 +35,14 @@ public static class Constants public const string Identity = nameof(Identity); public const string FullPath = nameof(FullPath); + // MSBuild CLI flags + + /// + /// Disables the live-updating node display in the terminal logger, which is useful for LLM/agentic environments. + /// + public const string TerminalLogger_DisableNodeDisplay = "-tlp:DISABLENODEDISPLAY"; + + public static readonly string ProjectArgumentName = ""; public static readonly string SolutionArgumentName = ""; public static readonly string ToolPackageArgumentName = ""; diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs index 97e7abd086c3..e0dca709ac3c 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildForwardingAppWithoutLogging.cs @@ -56,7 +56,6 @@ public MSBuildForwardingAppWithoutLogging(MSBuildArgs msbuildArgs, string? msbui msbuildArgs.OtherMSBuildArgs.Add("-nologo"); } string? tlpDefault = TerminalLoggerDefault; - // new for .NET 9 - default TL to auto (aka enable in non-CI scenarios) if (string.IsNullOrWhiteSpace(tlpDefault)) { tlpDefault = "auto"; diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/LocalizableStrings.Designer.cs b/src/Cli/Microsoft.TemplateEngine.Cli/LocalizableStrings.Designer.cs index ecf9c58147f5..0b1407d3d11b 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/LocalizableStrings.Designer.cs +++ b/src/Cli/Microsoft.TemplateEngine.Cli/LocalizableStrings.Designer.cs @@ -1439,6 +1439,17 @@ internal static string PostAction_ModifyJson_Error_NoJsonFile { } } + /// + /// Looks up a localized string similar to The result of parsing the following JSON was 'null': + /// + ///{0}. + /// + internal static string PostAction_ModifyJson_Error_NullJson { + get { + return ResourceManager.GetString("PostAction_ModifyJson_Error_NullJson", resourceCulture); + } + } + /// /// Looks up a localized string similar to Parent property path '{0}' could not be traversed.. /// diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/LocalizableStrings.resx b/src/Cli/Microsoft.TemplateEngine.Cli/LocalizableStrings.resx index 01b59dfe8833..d08a680ebae1 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/LocalizableStrings.resx +++ b/src/Cli/Microsoft.TemplateEngine.Cli/LocalizableStrings.resx @@ -953,4 +953,10 @@ The header is followed by the list of parameters and their errors (might be seve Attempting to find json file '{0}' in '{1}' - + + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + + \ No newline at end of file diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/PostActionProcessors/AddJsonPropertyPostActionProcessor.cs b/src/Cli/Microsoft.TemplateEngine.Cli/PostActionProcessors/AddJsonPropertyPostActionProcessor.cs index 80ca23402914..f517a6b363ba 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/PostActionProcessors/AddJsonPropertyPostActionProcessor.cs +++ b/src/Cli/Microsoft.TemplateEngine.Cli/PostActionProcessors/AddJsonPropertyPostActionProcessor.cs @@ -19,7 +19,9 @@ internal class AddJsonPropertyPostActionProcessor : PostActionProcessorBase private const string ParentPropertyPathArgument = "parentPropertyPath"; private const string NewJsonPropertyNameArgument = "newJsonPropertyName"; private const string NewJsonPropertyValueArgument = "newJsonPropertyValue"; - private const string DetectRepoRootForFileCreation = "detectRepositoryRootForFileCreation"; + private const string DetectRepoRoot = "detectRepositoryRoot"; + private const string IncludeAllDirectoriesInSearch = "includeAllDirectoriesInSearch"; + private const string IncludeAllParentDirectoriesInSearch = "includeAllParentDirectoriesInSearch"; private static readonly JsonSerializerOptions SerializerOptions = new() { @@ -87,7 +89,33 @@ protected override bool ProcessInternal( return false; } - IReadOnlyList jsonFiles = FindFilesInCurrentFolderOrParentFolder(environment.Host.FileSystem, outputBasePath, jsonFileName); + if (!bool.TryParse(action.Args.GetValueOrDefault(DetectRepoRoot, "false"), out bool detectRepoRoot)) + { + Reporter.Error.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Error_ArgumentNotBoolean, DetectRepoRoot)); + return false; + } + + if (!bool.TryParse(action.Args.GetValueOrDefault(IncludeAllDirectoriesInSearch, "true"), out bool includeAllDirectories)) + { + Reporter.Error.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Error_ArgumentNotBoolean, IncludeAllDirectoriesInSearch)); + return false; + } + + string? repoRoot = detectRepoRoot ? GetRootDirectory(environment.Host.FileSystem, outputBasePath) : null; + + if (!bool.TryParse(action.Args.GetValueOrDefault(IncludeAllParentDirectoriesInSearch, "false"), out bool includeAllParentDirectories)) + { + Reporter.Error.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Error_ArgumentNotBoolean, IncludeAllParentDirectoriesInSearch)); + return false; + } + + IReadOnlyList jsonFiles = FindFilesInCurrentFolderOrParentFolder( + environment.Host.FileSystem, + outputBasePath, + jsonFileName, + includeAllDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly, + includeAllParentDirectories ? int.MaxValue : 1, + repoRoot); if (jsonFiles.Count == 0) { @@ -103,13 +131,7 @@ protected override bool ProcessInternal( return false; } - if (!bool.TryParse(action.Args.GetValueOrDefault(DetectRepoRootForFileCreation, "false"), out bool detectRepoRoot)) - { - Reporter.Error.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Error_ArgumentNotBoolean, DetectRepoRootForFileCreation)); - return false; - } - - string newJsonFilePath = Path.Combine(detectRepoRoot ? GetRootDirectory(environment.Host.FileSystem, outputBasePath) : outputBasePath, jsonFileName); + string newJsonFilePath = Path.Combine(repoRoot ?? outputBasePath, jsonFileName); environment.Host.FileSystem.WriteAllText(newJsonFilePath, "{}"); jsonFiles = new List { newJsonFilePath }; } @@ -150,17 +172,19 @@ protected override bool ProcessInternal( private static JsonNode? AddElementToJson(IPhysicalFileSystem fileSystem, string targetJsonFile, string? propertyPath, string propertyPathSeparator, string newJsonPropertyName, string newJsonPropertyValue, IPostAction action) { - JsonNode? jsonContent = JsonNode.Parse(fileSystem.ReadAllText(targetJsonFile), nodeOptions: null, documentOptions: DeserializerOptions); + var fileContent = fileSystem.ReadAllText(targetJsonFile); + JsonNode? jsonContent = JsonNode.Parse(fileContent, nodeOptions: null, documentOptions: DeserializerOptions); if (jsonContent == null) { + Reporter.Error.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Error_NullJson, fileContent)); return null; } if (!bool.TryParse(action.Args.GetValueOrDefault(AllowPathCreationArgument, "false"), out bool createPath)) { Reporter.Error.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Error_ArgumentNotBoolean, AllowPathCreationArgument)); - return false; + return null; } JsonNode? parentProperty = FindJsonNode(jsonContent, propertyPath, propertyPathSeparator, createPath); @@ -216,7 +240,10 @@ protected override bool ProcessInternal( private static string[] FindFilesInCurrentFolderOrParentFolder( IPhysicalFileSystem fileSystem, string startPath, - string matchPattern) + string matchPattern, + SearchOption searchOption, + int maxUpLevels, + string? repoRoot) { string? directory = fileSystem.DirectoryExists(startPath) ? startPath : Path.GetDirectoryName(startPath); @@ -230,17 +257,24 @@ private static string[] FindFilesInCurrentFolderOrParentFolder( do { Reporter.Verbose.WriteLine(string.Format(LocalizableStrings.PostAction_ModifyJson_Verbose_AttemptingToFindJsonFile, matchPattern, directory)); - string[] filesInDir = fileSystem.EnumerateFileSystemEntries(directory, matchPattern, SearchOption.AllDirectories).ToArray(); + string[] filesInDir = fileSystem.EnumerateFileSystemEntries(directory, matchPattern, searchOption).ToArray(); if (filesInDir.Length > 0) { return filesInDir; } + if (repoRoot is not null && directory == repoRoot) + { + // The post action wants to detect the "repo root". + // We have already processed up to the repo root and didn't find any matching files, so we shouldn't go up any further. + return Array.Empty(); + } + directory = Path.GetPathRoot(directory) != directory ? Directory.GetParent(directory)?.FullName : null; numberOfUpLevels++; } - while (directory != null && numberOfUpLevels <= 1); + while (directory != null && numberOfUpLevels <= maxUpLevels); return Array.Empty(); } diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.cs.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.cs.xlf index 45f6d58a1ba7..ecb19bae6585 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.cs.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve V řešení se nepovedlo najít soubor JSON. + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. Cestu k nadřazené vlastnosti „{0}“ nelze projít. diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.de.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.de.xlf index eb67ab8bf9bf..0b82cc14850d 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.de.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve Die JSON-Datei wurde in der Projektmappe nicht gefunden. + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. Der übergeordnete Eigenschaftenpfad "{0}" konnte nicht durchlaufen werden. diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.es.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.es.xlf index 748e66f30a00..c76d94288839 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.es.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve No se encuentra el archivo JSON en la solución + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. No se pudo atravesar la ruta de acceso de la propiedad primaria '{0}'. diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.fr.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.fr.xlf index d86722e36e60..3dd088fe92f2 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.fr.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve Impossible de trouver le fichier json dans la solution + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. Le chemin de la propriété parente '{0}' n'a pas pu être traversé. diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.it.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.it.xlf index 45d8e4f02e4e..5022ad18a8c4 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.it.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve Non è possibile trovare il file JSON nella soluzione + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. Impossibile attraversare il percorso della proprietà padre '{0}'. diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ja.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ja.xlf index 2f9a5c77a259..e27c0eea775e 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ja.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve ソリューションで JSON ファイルが見つかりません + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. 親プロパティのパス '{0}' を走査できませんでした。 diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ko.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ko.xlf index 11699454506d..28e0b7ef6ea3 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ko.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve 솔루션에서 json 파일을 찾을 수 없습니다. + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. '{0}' 부모 속성 경로를 트래버스할 수 없습니다. diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pl.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pl.xlf index 6e5167124b31..edc3a399520b 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pl.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve Nie można odnaleźć pliku JSON w rozwiązaniu + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. Nie można przejść przez ścieżkę właściwości nadrzędnej „{0}”. diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pt-BR.xlf index 24f89179cb85..22b848dd30ec 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.pt-BR.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve Não é possível encontrar o arquivo json na solução + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. O caminho da propriedade pai '{0}' não pôde ser percorrido. diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ru.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ru.xlf index 6578a4d5bf14..b41a1a5ac241 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.ru.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve Не удалось найти файл JSON в решении + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. Не удалось просмотреть путь к родительскому свойству "{0}". diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.tr.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.tr.xlf index a0d71cff2b96..158582a214b2 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.tr.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve Çözümde json dosyası bulunamıyor + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. Üst özellik '{0}' yolu geçirilemedi. diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hans.xlf index f75d8ad68dae..2df9444a65e5 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hans.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve 在解决方案中找不到 json 文件 + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. 无法遍历父属性路径“{0}”。 diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hant.xlf index 56c64a9d9b91..1c17c1adc32a 100644 --- a/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/Microsoft.TemplateEngine.Cli/xlf/LocalizableStrings.zh-Hant.xlf @@ -850,6 +850,15 @@ The header is followed by the list of parameters and their errors (might be seve 在解決方案中找不到 JSON 檔案 + + The result of parsing the following JSON was 'null': + +{0} + The result of parsing the following JSON was 'null': + +{0} + {0} is the JSON that is being parsed. + Parent property path '{0}' could not be traversed. 無法周遊父屬性路徑 '{0}'。 diff --git a/src/Cli/dotnet/CliStrings.resx b/src/Cli/dotnet/CliStrings.resx index ea37a0b0333e..7852c04414a0 100644 --- a/src/Cli/dotnet/CliStrings.resx +++ b/src/Cli/dotnet/CliStrings.resx @@ -841,4 +841,7 @@ The default is 'false.' However, when targeting .NET 7 or lower, the default is Only one .nuspec file can be packed at a time + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + \ No newline at end of file diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx index 6ca03e2f5fe6..ad8799d93b33 100644 --- a/src/Cli/dotnet/Commands/CliCommandStrings.resx +++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx @@ -804,8 +804,9 @@ See https://aka.ms/dotnet-test/mtp for more information. Discovering tests from - - .NET Test Command + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Downloading pack {0} version {1} to offline cache {2}... @@ -1995,8 +1996,9 @@ Your project targets multiple frameworks. Specify which framework to run using ' Specify a temporary directory for this command to download and extract NuGet packages (must be secure). - - .NET Test Driver + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Run test(s), without displaying Microsoft Testplatform banner @@ -2702,4 +2704,25 @@ Proceed? Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. {Locked="ExecutionId"} - \ No newline at end of file + + error: + + + total: + + + retried + + + failed: + + + succeeded: + + + skipped: + + + duration: + + diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingApp.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingApp.cs index 67c2a47f1051..3055de0883f5 100644 --- a/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingApp.cs +++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingApp.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Reflection; +using Microsoft.DotNet.Cli.Commands.Run; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Cli.Utils.Extensions; @@ -14,6 +15,9 @@ public class MSBuildForwardingApp : CommandBase private readonly MSBuildForwardingAppWithoutLogging _forwardingAppWithoutLogging; + /// + /// Adds the CLI's telemetry logger to the MSBuild arguments if telemetry is enabled. + /// private static MSBuildArgs ConcatTelemetryLogger(MSBuildArgs msbuildArgs) { if (Telemetry.Telemetry.CurrentSessionId != null) @@ -45,8 +49,9 @@ public MSBuildForwardingApp(IEnumerable rawMSBuildArgs, string? msbuildP public MSBuildForwardingApp(MSBuildArgs msBuildArgs, string? msbuildPath = null, bool includeLogo = false) { + var modifiedMSBuildArgs = CommonRunHelpers.AdjustMSBuildForLLMs(ConcatTelemetryLogger(msBuildArgs)); _forwardingAppWithoutLogging = new MSBuildForwardingAppWithoutLogging( - ConcatTelemetryLogger(msBuildArgs), + modifiedMSBuildArgs, msbuildPath: msbuildPath, includeLogo: includeLogo); diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingLogger.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingLogger.cs index fc7c6488b910..fef3e251bedc 100644 --- a/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingLogger.cs +++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildForwardingLogger.cs @@ -26,11 +26,14 @@ public void Initialize(IEventSource eventSource) eventSource4.IncludeEvaluationPropertiesAndItems(); } - // Only forward telemetry events + // Forward telemetry events if (eventSource is IEventSource2 eventSource2) { eventSource2.TelemetryLogged += (sender, args) => BuildEventRedirector.ForwardEvent(args); } + + // Forward build finished events. Is used for logging the aggregated build events. + eventSource.BuildFinished += (sender, args) => BuildEventRedirector.ForwardEvent(args); } public void Initialize(IEventSource eventSource, int nodeCount) diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs index 1937f117cad2..9a0ded1a180c 100644 --- a/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs +++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs @@ -1,8 +1,6 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System.Globalization; using Microsoft.Build.Framework; using Microsoft.DotNet.Cli.Telemetry; @@ -15,7 +13,7 @@ public sealed class MSBuildLogger : INodeLogger { private readonly IFirstTimeUseNoticeSentinel _sentinel = new FirstTimeUseNoticeSentinel(); - private readonly ITelemetry _telemetry; + private readonly ITelemetry? _telemetry; internal const string TargetFrameworkTelemetryEventName = "targetframeworkeval"; internal const string BuildTelemetryEventName = "build"; @@ -24,6 +22,10 @@ public sealed class MSBuildLogger : INodeLogger internal const string BuildcheckRunEventName = "buildcheck/run"; internal const string BuildcheckRuleStatsEventName = "buildcheck/rule"; + // These two events are aggregated and sent at the end of the build. + internal const string TaskFactoryTelemetryAggregatedEventName = "build/tasks/taskfactory"; + internal const string TasksTelemetryAggregatedEventName = "build/tasks"; + internal const string SdkTaskBaseCatchExceptionTelemetryEventName = "taskBaseCatchException"; internal const string PublishPropertiesTelemetryEventName = "PublishProperties"; internal const string WorkloadPublishPropertiesTelemetryEventName = "WorkloadPublishProperties"; @@ -50,11 +52,20 @@ public sealed class MSBuildLogger : INodeLogger /// internal const string SdkContainerPublishErrorEventName = "sdk/container/publish/error"; + /// + /// Stores aggregated telemetry data by event name and property name. + /// + /// + /// Key: event name, Value: property name to aggregated count. + /// Aggregation is very basic. Only integer properties are aggregated by summing values. Non-integer properties are ignored. + /// + private Dictionary> _aggregatedEvents = new(); + public MSBuildLogger() { try { - string sessionId = + string? sessionId = Environment.GetEnvironmentVariable(MSBuildForwardingApp.TelemetrySessionIdEnvironmentVariableName); if (sessionId != null) @@ -75,6 +86,14 @@ public MSBuildLogger() } } + /// + /// Constructor for testing purposes. + /// + internal MSBuildLogger(ITelemetry telemetry) + { + _telemetry = telemetry; + } + public void Initialize(IEventSource eventSource, int nodeCount) { Initialize(eventSource); @@ -97,6 +116,8 @@ public void Initialize(IEventSource eventSource) { eventSource2.TelemetryLogged += OnTelemetryLogged; } + + eventSource.BuildFinished += OnBuildFinished; } } catch (Exception) @@ -105,58 +126,103 @@ public void Initialize(IEventSource eventSource) } } - internal static void FormatAndSend(ITelemetry telemetry, TelemetryEventArgs args) + private void OnBuildFinished(object sender, BuildFinishedEventArgs e) { - switch (args.EventName) + SendAggregatedEventsOnBuildFinished(_telemetry); + } + + internal void SendAggregatedEventsOnBuildFinished(ITelemetry? telemetry) + { + if (_aggregatedEvents.TryGetValue(TaskFactoryTelemetryAggregatedEventName, out var taskFactoryData)) { - case TargetFrameworkTelemetryEventName: - { - var newEventName = $"msbuild/{TargetFrameworkTelemetryEventName}"; - Dictionary maskedProperties = []; - - foreach (var key in new[] { - TargetFrameworkVersionTelemetryPropertyKey, - RuntimeIdentifierTelemetryPropertyKey, - SelfContainedTelemetryPropertyKey, - UseApphostTelemetryPropertyKey, - OutputTypeTelemetryPropertyKey, - UseArtifactsOutputTelemetryPropertyKey, - ArtifactsPathLocationTypeTelemetryPropertyKey - }) - { - if (args.Properties.TryGetValue(key, out string value)) - { - maskedProperties.Add(key, Sha256Hasher.HashWithNormalizedCasing(value)); - } - } + var taskFactoryProperties = ConvertToStringDictionary(taskFactoryData); + + TrackEvent(telemetry, $"msbuild/{TaskFactoryTelemetryAggregatedEventName}", taskFactoryProperties, toBeHashed: [], toBeMeasured: []); + _aggregatedEvents.Remove(TaskFactoryTelemetryAggregatedEventName); + } + + if (_aggregatedEvents.TryGetValue(TasksTelemetryAggregatedEventName, out var tasksData)) + { + var tasksProperties = ConvertToStringDictionary(tasksData); + + TrackEvent(telemetry, $"msbuild/{TasksTelemetryAggregatedEventName}", tasksProperties, toBeHashed: [], toBeMeasured: []); + _aggregatedEvents.Remove(TasksTelemetryAggregatedEventName); + } + } + + private static Dictionary ConvertToStringDictionary(Dictionary properties) + { + Dictionary stringProperties = new(); + foreach (var kvp in properties) + { + stringProperties[kvp.Key] = kvp.Value.ToString(CultureInfo.InvariantCulture); + } + + return stringProperties; + } - telemetry.TrackEvent(newEventName, maskedProperties, measurements: null); - break; + internal void AggregateEvent(TelemetryEventArgs args) + { + if (args.EventName is null) + { + return; + } + + if (!_aggregatedEvents.TryGetValue(args.EventName, out var eventData)) + { + eventData = []; + _aggregatedEvents[args.EventName] = eventData; + } + + foreach (var kvp in args.Properties) + { + if (int.TryParse(kvp.Value, CultureInfo.InvariantCulture, out int count)) + { + if (!eventData.ContainsKey(kvp.Key)) + { + eventData[kvp.Key] = count; + } + else + { + eventData[kvp.Key] += count; } + } + } + } + + internal static void FormatAndSend(ITelemetry? telemetry, TelemetryEventArgs args) + { + switch (args.EventName) + { + case TargetFrameworkTelemetryEventName: + TrackEvent(telemetry, $"msbuild/{TargetFrameworkTelemetryEventName}", args.Properties); + break; case BuildTelemetryEventName: TrackEvent(telemetry, $"msbuild/{BuildTelemetryEventName}", args.Properties, toBeHashed: ["ProjectPath", "BuildTarget"], - toBeMeasured: ["BuildDurationInMilliseconds", "InnerBuildDurationInMilliseconds"]); + toBeMeasured: ["BuildDurationInMilliseconds", "InnerBuildDurationInMilliseconds"] + ); break; case LoggingConfigurationTelemetryEventName: TrackEvent(telemetry, $"msbuild/{LoggingConfigurationTelemetryEventName}", args.Properties, - toBeHashed: [], - toBeMeasured: []); + toBeMeasured: [] + ); break; case BuildcheckAcquisitionFailureEventName: TrackEvent(telemetry, $"msbuild/{BuildcheckAcquisitionFailureEventName}", args.Properties, - toBeHashed: ["AssemblyName", "ExceptionType", "ExceptionMessage"], - toBeMeasured: []); + toBeHashed: ["AssemblyName", "ExceptionType", "ExceptionMessage"] + ); break; case BuildcheckRunEventName: TrackEvent(telemetry, $"msbuild/{BuildcheckRunEventName}", args.Properties, - toBeHashed: [], - toBeMeasured: ["TotalRuntimeInMilliseconds"]); + toBeMeasured: ["TotalRuntimeInMilliseconds"] + ); break; case BuildcheckRuleStatsEventName: TrackEvent(telemetry, $"msbuild/{BuildcheckRuleStatsEventName}", args.Properties, toBeHashed: ["RuleId", "CheckFriendlyName"], - toBeMeasured: ["TotalRuntimeInMilliseconds"]); + toBeMeasured: ["TotalRuntimeInMilliseconds"] + ); break; // Pass through events that don't need special handling case SdkTaskBaseCatchExceptionTelemetryEventName: @@ -166,7 +232,7 @@ internal static void FormatAndSend(ITelemetry telemetry, TelemetryEventArgs args case SdkContainerPublishBaseImageInferenceEventName: case SdkContainerPublishSuccessEventName: case SdkContainerPublishErrorEventName: - TrackEvent(telemetry, args.EventName, args.Properties, [], []); + TrackEvent(telemetry, args.EventName, args.Properties); break; default: // Ignore unknown events @@ -174,33 +240,44 @@ internal static void FormatAndSend(ITelemetry telemetry, TelemetryEventArgs args } } - private static void TrackEvent(ITelemetry telemetry, string eventName, IDictionary eventProperties, string[] toBeHashed, string[] toBeMeasured) + private static void TrackEvent(ITelemetry? telemetry, string eventName, IDictionary eventProperties, string[]? toBeHashed = null, string[]? toBeMeasured = null) { - Dictionary properties = null; - Dictionary measurements = null; + if (telemetry == null || !telemetry.Enabled) + { + return; + } - foreach (var propertyToBeHashed in toBeHashed) + Dictionary? properties = null; + Dictionary? measurements = null; + + if (toBeHashed is not null) { - if (eventProperties.TryGetValue(propertyToBeHashed, out string value)) + foreach (var propertyToBeHashed in toBeHashed) { - // Lets lazy allocate in case there is tons of telemetry - properties ??= new Dictionary(eventProperties); - properties[propertyToBeHashed] = Sha256Hasher.HashWithNormalizedCasing(value); + if (eventProperties.TryGetValue(propertyToBeHashed, out var value)) + { + // Lets lazy allocate in case there is tons of telemetry + properties ??= new(eventProperties); + properties[propertyToBeHashed] = Sha256Hasher.HashWithNormalizedCasing(value!); + } } } - foreach (var propertyToBeMeasured in toBeMeasured) + if (toBeMeasured is not null) { - if (eventProperties.TryGetValue(propertyToBeMeasured, out string value)) + foreach (var propertyToBeMeasured in toBeMeasured) { - // Lets lazy allocate in case there is tons of telemetry - properties ??= new Dictionary(eventProperties); - properties.Remove(propertyToBeMeasured); - if (double.TryParse(value, CultureInfo.InvariantCulture, out double realValue)) + if (eventProperties.TryGetValue(propertyToBeMeasured, out var value)) { // Lets lazy allocate in case there is tons of telemetry - measurements ??= []; - measurements[propertyToBeMeasured] = realValue; + properties ??= new(eventProperties); + properties.Remove(propertyToBeMeasured); + if (double.TryParse(value, CultureInfo.InvariantCulture, out double realValue)) + { + // Lets lazy allocate in case there is tons of telemetry + measurements ??= []; + measurements[propertyToBeMeasured] = realValue; + } } } } @@ -210,7 +287,14 @@ private static void TrackEvent(ITelemetry telemetry, string eventName, IDictiona private void OnTelemetryLogged(object sender, TelemetryEventArgs args) { - FormatAndSend(_telemetry, args); + if (args.EventName == TaskFactoryTelemetryAggregatedEventName || args.EventName == TasksTelemetryAggregatedEventName) + { + AggregateEvent(args); + } + else + { + FormatAndSend(_telemetry, args); + } } public void Shutdown() @@ -227,5 +311,5 @@ public void Shutdown() public LoggerVerbosity Verbosity { get; set; } - public string Parameters { get; set; } + public string? Parameters { get; set; } } diff --git a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs index d1c73fb5bb1e..dc94f892abd1 100644 --- a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs +++ b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs @@ -163,10 +163,12 @@ private int ExecuteForFileBasedApp(string path) } } - bool hasVersion = _packageId.HasVersion; + string? specifiedVersion = _packageId.HasVersion + ? _packageId.VersionRange?.OriginalString ?? string.Empty + : _parseResult.GetValue(PackageAddCommandParser.VersionOption); bool prerelease = _parseResult.GetValue(PackageAddCommandParser.PrereleaseOption); - if (hasVersion && prerelease) + if (specifiedVersion != null && prerelease) { throw new GracefulException(CliCommandStrings.PrereleaseAndVersionAreNotSupportedAtTheSameTime); } @@ -193,11 +195,7 @@ private int ExecuteForFileBasedApp(string path) // Set initial version to Directory.Packages.props and/or C# file // (we always need to add the package reference to the C# file but when CPM is enabled, it's added without a version). - string version = hasVersion - ? _packageId.VersionRange?.OriginalString ?? string.Empty - : (prerelease - ? "*-*" - : "*"); + string version = specifiedVersion ?? (prerelease ? "*-*" : "*"); bool skipUpdate = false; var central = SetCentralVersion(version); var local = SetLocalVersion(central != null ? null : version); @@ -214,7 +212,7 @@ private int ExecuteForFileBasedApp(string path) } // If no version was specified by the user, save the actually restored version. - if (!hasVersion && !skipUpdate) + if (specifiedVersion == null && !skipUpdate) { var projectAssetsFile = projectInstance.GetProperty("ProjectAssetsFile")?.EvaluatedValue; if (!File.Exists(projectAssetsFile)) @@ -306,7 +304,7 @@ void Update(string value) // If user didn't specify a version and a version is already specified in Directory.Packages.props, // don't update the Directory.Packages.props (that's how the project-based equivalent behaves as well). - if (!hasVersion) + if (specifiedVersion == null) { skipUpdate = true; return (Revert: NoOp, Update: Unreachable, Save: Revert); diff --git a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs index c166f61769fe..f1bc3ab7d655 100644 --- a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs +++ b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs @@ -1,6 +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 System.Collections.Immutable; using System.CommandLine; using System.Diagnostics.CodeAnalysis; using Microsoft.Build.Evaluation; @@ -31,6 +32,16 @@ public override int Execute() var sourceFile = SourceFile.Load(file); var directives = VirtualProjectBuildingCommand.FindDirectives(sourceFile, reportAllErrors: !_force, DiagnosticBag.ThrowOnFirst()); + // Create a project instance for evaluation. + var projectCollection = new ProjectCollection(); + var command = new VirtualProjectBuildingCommand( + entryPointFileFullPath: file, + msbuildArgs: MSBuildArgs.FromOtherArgs([])) + { + Directives = directives, + }; + var projectInstance = command.CreateProjectInstance(projectCollection); + // Find other items to copy over, e.g., default Content items like JSON files in Web apps. var includeItems = FindIncludedItems().ToList(); @@ -61,7 +72,9 @@ public override int Execute() { using var stream = File.Open(projectFile, FileMode.Create, FileAccess.Write); using var writer = new StreamWriter(stream, Encoding.UTF8); - VirtualProjectBuildingCommand.WriteProjectFile(writer, directives, isVirtualProject: false); + VirtualProjectBuildingCommand.WriteProjectFile(writer, UpdateDirectives(directives), isVirtualProject: false, + userSecretsId: DetermineUserSecretsId(), + excludeDefaultProperties: FindDefaultPropertiesToExclude()); } // Copy or move over included items. @@ -112,14 +125,6 @@ void CopyFile(string source, string target) IEnumerable<(string FullPath, string RelativePath)> FindIncludedItems() { string entryPointFileDirectory = PathUtility.EnsureTrailingSlash(Path.GetDirectoryName(file)!); - var projectCollection = new ProjectCollection(); - var command = new VirtualProjectBuildingCommand( - entryPointFileFullPath: file, - msbuildArgs: MSBuildArgs.FromOtherArgs([])) - { - Directives = directives, - }; - var projectInstance = command.CreateProjectInstance(projectCollection); // Include only items we know are files. string[] itemTypes = ["Content", "None", "Compile", "EmbeddedResource"]; @@ -151,6 +156,47 @@ void CopyFile(string source, string target) yield return (FullPath: itemFullPath, RelativePath: itemRelativePath); } } + + string? DetermineUserSecretsId() + { + var implicitValue = projectInstance.GetPropertyValue("_ImplicitFileBasedProgramUserSecretsId"); + var actualValue = projectInstance.GetPropertyValue("UserSecretsId"); + return implicitValue == actualValue ? actualValue : null; + } + + ImmutableArray UpdateDirectives(ImmutableArray directives) + { + var result = ImmutableArray.CreateBuilder(directives.Length); + + foreach (var directive in directives) + { + // Fixup relative project reference paths (they need to be relative to the output directory instead of the source directory). + if (directive is CSharpDirective.Project project && + !Path.IsPathFullyQualified(project.Name)) + { + var modified = project.WithName(Path.GetRelativePath(relativeTo: targetDirectory, path: project.Name)); + result.Add(modified); + } + else + { + result.Add(directive); + } + } + + return result.DrainToImmutable(); + } + + IEnumerable FindDefaultPropertiesToExclude() + { + foreach (var (name, defaultValue) in VirtualProjectBuildingCommand.DefaultProperties) + { + string projectValue = projectInstance.GetPropertyValue(name); + if (!string.Equals(projectValue, defaultValue, StringComparison.OrdinalIgnoreCase)) + { + yield return name; + } + } + } } private string DetermineOutputDirectory(string file) diff --git a/src/Cli/dotnet/Commands/Run/CommonRunHelpers.cs b/src/Cli/dotnet/Commands/Run/CommonRunHelpers.cs index dffbbc0bfab3..64d2b8a1075a 100644 --- a/src/Cli/dotnet/Commands/Run/CommonRunHelpers.cs +++ b/src/Cli/dotnet/Commands/Run/CommonRunHelpers.cs @@ -17,7 +17,6 @@ internal static class CommonRunHelpers public static Dictionary GetGlobalPropertiesFromArgs(MSBuildArgs msbuildArgs) { var globalProperties = msbuildArgs.GlobalProperties?.ToDictionary() ?? new Dictionary(StringComparer.OrdinalIgnoreCase); - globalProperties[Constants.EnableDefaultItems] = "false"; // Disable default item globbing to improve performance globalProperties[Constants.MSBuildExtensionsPath] = AppContext.BaseDirectory; return globalProperties; } @@ -27,4 +26,29 @@ public static string GetPropertiesLaunchSettingsPath(string directoryPath, strin public static string GetFlatLaunchSettingsPath(string directoryPath, string projectNameWithoutExtension) => Path.Join(directoryPath, $"{projectNameWithoutExtension}.run.json"); + + + /// + /// Applies adjustments to MSBuild arguments to better suit LLM/agentic environments, if such an environment is detected. + /// + public static MSBuildArgs AdjustMSBuildForLLMs(MSBuildArgs msbuildArgs) + { + if (new Telemetry.LLMEnvironmentDetectorForTelemetry().IsLLMEnvironment()) + { + // disable the live-update display of the TerminalLogger, which wastes tokens + return msbuildArgs.CloneWithAdditionalArgs(Constants.TerminalLogger_DisableNodeDisplay); + } + else + { + return msbuildArgs; + } + } + + /// + /// Creates a TerminalLogger or ConsoleLogger based on the provided MSBuild arguments. + /// If the environment is detected to be an LLM environment, the logger is adjusted to + /// better suit that environment. + /// + public static Microsoft.Build.Framework.ILogger GetConsoleLogger(MSBuildArgs args) => + Microsoft.Build.Logging.TerminalLogger.CreateTerminalOrConsoleLogger([.. AdjustMSBuildForLLMs(args).OtherMSBuildArgs]); } diff --git a/src/Cli/dotnet/Commands/Run/FileBasedAppSourceEditor.cs b/src/Cli/dotnet/Commands/Run/FileBasedAppSourceEditor.cs index 402da626784e..45fa0380aeea 100644 --- a/src/Cli/dotnet/Commands/Run/FileBasedAppSourceEditor.cs +++ b/src/Cli/dotnet/Commands/Run/FileBasedAppSourceEditor.cs @@ -86,7 +86,8 @@ private TextChange DetermineAddChange(CSharpDirective directive) { // Find one that has the same kind and name. // If found, we will replace it with the new directive. - if (directive is CSharpDirective.Named named && + var named = directive as CSharpDirective.Named; + if (named != null && Directives.OfType().FirstOrDefault(d => NamedDirectiveComparer.Instance.Equals(d, named)) is { } toReplace) { return new TextChange(toReplace.Info.Span, newText: directive.ToString() + NewLine); @@ -99,6 +100,14 @@ private TextChange DetermineAddChange(CSharpDirective directive) { if (existingDirective.GetType() == directive.GetType()) { + // Add named directives in sorted order. + if (named != null && + existingDirective is CSharpDirective.Named existingNamed && + string.CompareOrdinal(existingNamed.Name, named.Name) > 0) + { + break; + } + addAfter = existingDirective; } else if (addAfter != null) @@ -120,7 +129,7 @@ private TextChange DetermineAddChange(CSharpDirective directive) var result = tokenizer.ParseNextToken(); var leadingTrivia = result.Token.LeadingTrivia; - // If there is a comment at the top of the file, we add the directive after it + // If there is a comment or #! at the top of the file, we add the directive after it // (the comment might be a license which should always stay at the top). int insertAfterIndex = -1; int trailingNewLines = 0; @@ -161,6 +170,11 @@ private TextChange DetermineAddChange(CSharpDirective directive) } break; + case SyntaxKind.ShebangDirectiveTrivia: + trailingNewLines = 1; // shebang trivia has one newline embedded in its structure + insertAfterIndex = i; + break; + case SyntaxKind.EndOfLineTrivia: if (insertAfterIndex >= 0) { diff --git a/src/Cli/dotnet/Commands/Run/RunCommand.cs b/src/Cli/dotnet/Commands/Run/RunCommand.cs index 33ec7b5e3219..635566bf07b4 100644 --- a/src/Cli/dotnet/Commands/Run/RunCommand.cs +++ b/src/Cli/dotnet/Commands/Run/RunCommand.cs @@ -397,7 +397,7 @@ internal ICommand GetTargetCommand(Func? pro // So we can skip project evaluation to continue the optimized path. Debug.Assert(EntryPointFileFullPath is not null); Reporter.Verbose.WriteLine("Getting target command: for csc-built program."); - return CreateCommandForCscBuiltProgram(EntryPointFileFullPath); + return CreateCommandForCscBuiltProgram(EntryPointFileFullPath, ApplicationArgs); } Reporter.Verbose.WriteLine("Getting target command: evaluating project."); @@ -463,11 +463,11 @@ static void SetRootVariableName(ICommand command, string runtimeIdentifier, stri } } - static ICommand CreateCommandForCscBuiltProgram(string entryPointFileFullPath) + static ICommand CreateCommandForCscBuiltProgram(string entryPointFileFullPath, string[] args) { var artifactsPath = VirtualProjectBuildingCommand.GetArtifactsPath(entryPointFileFullPath); var exePath = Path.Join(artifactsPath, "bin", "debug", Path.GetFileNameWithoutExtension(entryPointFileFullPath) + FileNameSuffixes.CurrentPlatform.Exe); - var commandSpec = new CommandSpec(path: exePath, args: null); + var commandSpec = new CommandSpec(path: exePath, args: ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(args)); var command = CommandFactoryUsingResolver.Create(commandSpec) .WorkingDirectory(Path.GetDirectoryName(entryPointFileFullPath)); @@ -483,7 +483,9 @@ static ICommand CreateCommandForCscBuiltProgram(string entryPointFileFullPath) static void InvokeRunArgumentsTarget(ProjectInstance project, bool noBuild, FacadeLogger? binaryLogger, MSBuildArgs buildArgs) { List loggersForBuild = [ - TerminalLogger.CreateTerminalOrConsoleLogger([$"--verbosity:{LoggerVerbosity.Quiet.ToString().ToLowerInvariant()}", ..buildArgs.OtherMSBuildArgs]) + CommonRunHelpers.GetConsoleLogger( + buildArgs.CloneWithExplicitArgs([$"--verbosity:{LoggerVerbosity.Quiet.ToString().ToLowerInvariant()}", ..buildArgs.OtherMSBuildArgs]) + ) ]; if (binaryLogger is not null) { @@ -824,11 +826,11 @@ private void SendProjectBasedTelemetry(ProjectLaunchSettingsModel? launchSetting { Debug.Assert(ProjectFileFullPath != null); var projectIdentifier = RunTelemetry.GetProjectBasedIdentifier(ProjectFileFullPath, GetRepositoryRoot(), Sha256Hasher.Hash); - + // Get package and project reference counts for project-based apps int packageReferenceCount = 0; int projectReferenceCount = 0; - + // Try to get project information for telemetry if we built the project if (ShouldBuild) { @@ -837,10 +839,10 @@ private void SendProjectBasedTelemetry(ProjectLaunchSettingsModel? launchSetting var globalProperties = MSBuildArgs.GlobalProperties?.ToDictionary() ?? new Dictionary(StringComparer.OrdinalIgnoreCase); globalProperties[Constants.EnableDefaultItems] = "false"; globalProperties[Constants.MSBuildExtensionsPath] = AppContext.BaseDirectory; - + using var collection = new ProjectCollection(globalProperties: globalProperties); var project = collection.LoadProject(ProjectFileFullPath).CreateProjectInstance(); - + packageReferenceCount = RunTelemetry.CountPackageReferences(project); projectReferenceCount = RunTelemetry.CountProjectReferences(project); } @@ -869,10 +871,10 @@ private void SendProjectBasedTelemetry(ProjectLaunchSettingsModel? launchSetting { try { - var currentDir = ProjectFileFullPath != null + var currentDir = ProjectFileFullPath != null ? Path.GetDirectoryName(ProjectFileFullPath) : Directory.GetCurrentDirectory(); - + while (currentDir != null) { if (Directory.Exists(Path.Combine(currentDir, ".git"))) @@ -886,7 +888,7 @@ private void SendProjectBasedTelemetry(ProjectLaunchSettingsModel? launchSetting { // Ignore errors when trying to find repo root } - + return null; } } diff --git a/src/Cli/dotnet/Commands/Run/RunTelemetry.cs b/src/Cli/dotnet/Commands/Run/RunTelemetry.cs index 35e13b2d3fd2..1e58bdd27c05 100644 --- a/src/Cli/dotnet/Commands/Run/RunTelemetry.cs +++ b/src/Cli/dotnet/Commands/Run/RunTelemetry.cs @@ -47,9 +47,13 @@ public static void TrackRunEvent( { ["app_type"] = isFileBased ? "file_based" : "project_based", ["project_id"] = projectIdentifier, - ["sdk_count"] = sdkCount.ToString(), - ["package_reference_count"] = packageReferenceCount.ToString(), - ["project_reference_count"] = projectReferenceCount.ToString(), + }; + + var measurements = new Dictionary + { + ["sdk_count"] = sdkCount, + ["package_reference_count"] = packageReferenceCount, + ["project_reference_count"] = projectReferenceCount, }; // Launch profile telemetry @@ -75,7 +79,7 @@ public static void TrackRunEvent( // File-based app specific telemetry if (isFileBased) { - properties["additional_properties_count"] = additionalPropertiesCount.ToString(); + measurements["additional_properties_count"] = additionalPropertiesCount; if (usedMSBuild.HasValue) { properties["used_msbuild"] = usedMSBuild.Value ? "true" : "false"; @@ -86,7 +90,7 @@ public static void TrackRunEvent( } } - TelemetryEventEntry.TrackEvent(RunEventName, properties, measurements: null); + TelemetryEventEntry.TrackEvent(RunEventName, properties, measurements); } /// @@ -234,4 +238,4 @@ private static bool IsDefaultProfile(string? profileName) // The default profile name at this point is "(Default)" return profileName.Equals("(Default)", StringComparison.OrdinalIgnoreCase); } -} \ No newline at end of file +} diff --git a/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs b/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs index 6997b8052a0c..794b7f77974e 100644 --- a/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs +++ b/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs @@ -75,13 +75,14 @@ internal sealed class VirtualProjectBuildingCommand : CommandBase /// /// Kept in sync with the default dotnet new console project file (enforced by DotnetProjectAddTests.SameAsTemplate). /// - private static readonly FrozenDictionary s_defaultProperties = FrozenDictionary.Create(StringComparer.OrdinalIgnoreCase, + public static readonly FrozenDictionary DefaultProperties = FrozenDictionary.Create(StringComparer.OrdinalIgnoreCase, [ new("OutputType", "Exe"), new("TargetFramework", $"net{TargetFrameworkVersion}"), new("ImplicitUsings", "enable"), new("Nullable", "enable"), new("PublishAot", "true"), + new("PackAsTool", "true"), ]); /// @@ -189,7 +190,7 @@ public override int Execute() var verbosity = MSBuildArgs.Verbosity ?? MSBuildForwardingAppWithoutLogging.DefaultVerbosity; var consoleLogger = minimizeStdOut ? new SimpleErrorLogger() - : TerminalLogger.CreateTerminalOrConsoleLogger([$"--verbosity:{verbosity}", .. MSBuildArgs.OtherMSBuildArgs]); + : CommonRunHelpers.GetConsoleLogger(MSBuildArgs.CloneWithExplicitArgs([$"--verbosity:{verbosity}", .. MSBuildArgs.OtherMSBuildArgs])); var binaryLogger = GetBinaryLogger(MSBuildArgs.OtherMSBuildArgs); CacheInfo? cache = null; @@ -355,9 +356,16 @@ public override int Execute() // Cache run info (to avoid re-evaluating the project instance). cache.CurrentEntry.Run = RunProperties.FromProject(buildRequest.ProjectInstance); - TryCacheCscArguments(cache, buildResult, buildRequest.ProjectInstance); + if (!MSBuildUtilities.ConvertStringToBool(buildRequest.ProjectInstance.GetPropertyValue(FileBasedProgramCanSkipMSBuild), defaultValue: true)) + { + Reporter.Verbose.WriteLine($"Not saving cache because there is an opt-out via MSBuild property {FileBasedProgramCanSkipMSBuild}."); + } + else + { + CacheCscArguments(cache, buildResult); - MarkBuildSuccess(cache); + MarkBuildSuccess(cache); + } } projectInstance = buildRequest.ProjectInstance; @@ -444,14 +452,8 @@ static Action> AddRestoreGlobalProperties(ReadOnlyDi return null; } - void TryCacheCscArguments(CacheInfo cache, BuildResult result, ProjectInstance projectInstance) + void CacheCscArguments(CacheInfo cache, BuildResult result) { - if (!MSBuildUtilities.ConvertStringToBool(projectInstance.GetPropertyValue(FileBasedProgramCanSkipMSBuild), defaultValue: true)) - { - Reporter.Verbose.WriteLine($"Not saving CSC arguments because there is an opt-out via MSBuild property {FileBasedProgramCanSkipMSBuild}."); - return; - } - // We cannot reuse CSC arguments from previous run and skip MSBuild if there are project references // because we cannot easily detect whether any referenced projects have changed. if (Directives.Any(static d => d is CSharpDirective.Project)) @@ -1138,8 +1140,13 @@ public static void WriteProjectFile( bool isVirtualProject, string? targetFilePath = null, string? artifactsPath = null, - bool includeRuntimeConfigInformation = true) + bool includeRuntimeConfigInformation = true, + string? userSecretsId = null, + IEnumerable? excludeDefaultProperties = null) { + Debug.Assert(userSecretsId == null || !isVirtualProject); + Debug.Assert(excludeDefaultProperties == null || !isVirtualProject); + int processedDirectives = 0; var sdkDirectives = directives.OfType(); @@ -1178,6 +1185,20 @@ public static void WriteProjectFile( artifacts/$(MSBuildProjectName) artifacts/$(MSBuildProjectName) true + false + true + """); + + // Write default properties before importing SDKs so they can be overridden by SDKs + // (and implicit build files which are imported by the default .NET SDK). + foreach (var (name, value) in DefaultProperties) + { + writer.WriteLine($""" + <{name}>{EscapeValue(value)} + """); + } + + writer.WriteLine($""" @@ -1244,27 +1265,30 @@ public static void WriteProjectFile( """); // First write the default properties except those specified by the user. - var customPropertyNames = propertyDirectives.Select(d => d.Name).ToHashSet(StringComparer.OrdinalIgnoreCase); - foreach (var (name, value) in s_defaultProperties) + if (!isVirtualProject) { - if (!customPropertyNames.Contains(name)) + var customPropertyNames = propertyDirectives + .Select(static d => d.Name) + .Concat(excludeDefaultProperties ?? []) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + foreach (var (name, value) in DefaultProperties) + { + if (!customPropertyNames.Contains(name)) + { + writer.WriteLine($""" + <{name}>{EscapeValue(value)} + """); + } + } + + if (userSecretsId != null && !customPropertyNames.Contains("UserSecretsId")) { writer.WriteLine($""" - <{name}>{EscapeValue(value)} + {EscapeValue(userSecretsId)} """); } } - // Write virtual-only properties. - if (isVirtualProject) - { - writer.WriteLine(""" - false - true - false - """); - } - // Write custom properties. foreach (var property in propertyDirectives) { @@ -1279,6 +1303,7 @@ public static void WriteProjectFile( if (isVirtualProject) { writer.WriteLine(""" + false $(Features);FileBasedProgram """); } @@ -1877,7 +1902,11 @@ public sealed class Project(in ParseInfo info) : Named(info) if (Directory.Exists(resolvedProjectPath)) { var fullFilePath = MsbuildProject.GetProjectFileFromDirectory(resolvedProjectPath).FullName; - directiveText = Path.GetRelativePath(relativeTo: sourceDirectory, fullFilePath); + + // Keep a relative path only if the original directive was a relative path. + directiveText = Path.IsPathFullyQualified(directiveText) + ? fullFilePath + : Path.GetRelativePath(relativeTo: sourceDirectory, fullFilePath); } else if (!File.Exists(resolvedProjectPath)) { @@ -1895,6 +1924,11 @@ public sealed class Project(in ParseInfo info) : Named(info) }; } + public Project WithName(string name) + { + return new Project(Info) { Name = name }; + } + public override string ToString() => $"#:project {Name}"; } } diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/AnsiTerminalTestProgressFrame.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/AnsiTerminalTestProgressFrame.cs index 6a6974bd97f1..822b0c4e9791 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/AnsiTerminalTestProgressFrame.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/AnsiTerminalTestProgressFrame.cs @@ -26,60 +26,78 @@ public void AppendTestWorkerProgress(TestProgressState progress, RenderedProgres int nonReservedWidth = Width - (durationString.Length + 2); + int discovered = progress.DiscoveredTests; int passed = progress.PassedTests; int failed = progress.FailedTests; int skipped = progress.SkippedTests; int retried = progress.RetriedFailedTests; int charsTaken = 0; - terminal.Append('['); - charsTaken++; - terminal.SetColor(TerminalColor.DarkGreen); - terminal.Append('✓'); - charsTaken++; - string passedText = passed.ToString(CultureInfo.CurrentCulture); - terminal.Append(passedText); - charsTaken += passedText.Length; - terminal.ResetColor(); + if (!progress.IsDiscovery) + { + terminal.Append('['); + charsTaken++; + terminal.SetColor(TerminalColor.DarkGreen); + terminal.Append('✓'); + charsTaken++; + string passedText = passed.ToString(CultureInfo.CurrentCulture); + terminal.Append(passedText); + charsTaken += passedText.Length; + terminal.ResetColor(); - terminal.Append('/'); - charsTaken++; + terminal.Append('/'); + charsTaken++; - terminal.SetColor(TerminalColor.DarkRed); - terminal.Append('x'); - charsTaken++; - string failedText = failed.ToString(CultureInfo.CurrentCulture); - terminal.Append(failedText); - charsTaken += failedText.Length; - terminal.ResetColor(); + terminal.SetColor(TerminalColor.DarkRed); + terminal.Append('x'); + charsTaken++; + string failedText = failed.ToString(CultureInfo.CurrentCulture); + terminal.Append(failedText); + charsTaken += failedText.Length; + terminal.ResetColor(); - terminal.Append('/'); - charsTaken++; + terminal.Append('/'); + charsTaken++; - terminal.SetColor(TerminalColor.DarkYellow); - terminal.Append('↓'); - charsTaken++; - string skippedText = skipped.ToString(CultureInfo.CurrentCulture); - terminal.Append(skippedText); - charsTaken += skippedText.Length; - terminal.ResetColor(); + terminal.SetColor(TerminalColor.DarkYellow); + terminal.Append('↓'); + charsTaken++; + string skippedText = skipped.ToString(CultureInfo.CurrentCulture); + terminal.Append(skippedText); + charsTaken += skippedText.Length; + terminal.ResetColor(); + + if (retried > 0) + { + terminal.Append('/'); + charsTaken++; + terminal.SetColor(TerminalColor.Gray); + terminal.Append('r'); + charsTaken++; + string retriedText = retried.ToString(CultureInfo.CurrentCulture); + terminal.Append(retriedText); + charsTaken += retriedText.Length; + terminal.ResetColor(); + } - if (retried > 0) + terminal.Append(']'); + charsTaken++; + } + else { - terminal.Append('/'); + string discoveredText = progress.DiscoveredTests.ToString(CultureInfo.CurrentCulture); + terminal.Append('['); charsTaken++; - terminal.SetColor(TerminalColor.Gray); - terminal.Append('r'); + terminal.SetColor(TerminalColor.DarkMagenta); + terminal.Append('+'); charsTaken++; - string retriedText = retried.ToString(CultureInfo.CurrentCulture); - terminal.Append(retriedText); - charsTaken += retriedText.Length; + terminal.Append(discoveredText); + charsTaken += discoveredText.Length; terminal.ResetColor(); + terminal.Append(']'); + charsTaken++; } - terminal.Append(']'); - charsTaken++; - terminal.Append(' '); charsTaken++; AppendToWidth(terminal, progress.AssemblyName, nonReservedWidth, ref charsTaken); diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/HumanReadableDurationFormatter.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/HumanReadableDurationFormatter.cs index 559af6b77847..304166341323 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/HumanReadableDurationFormatter.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/HumanReadableDurationFormatter.cs @@ -16,6 +16,7 @@ public static void Append(ITerminal terminal, TimeSpan duration, bool wrapInPare terminal.Append('('); } + // TODO: Do these abbrevations (d for days, h for hours, etc) need to be localized? if (duration.Days > 0) { terminal.Append($"{duration.Days}d"); diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/SimpleTerminalBase.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/SimpleTerminalBase.cs index 310b3edd271e..760aff97d746 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/SimpleTerminalBase.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/SimpleTerminalBase.cs @@ -67,32 +67,49 @@ public void RenderProgress(TestProgressState?[] progress) string durationString = HumanReadableDurationFormatter.Render(p.Stopwatch.Elapsed); + int discovered = p.DiscoveredTests; int passed = p.PassedTests; int failed = p.FailedTests; int skipped = p.SkippedTests; - // Use just ascii here, so we don't put too many restrictions on fonts needing to - // properly show unicode, or logs being saved in particular encoding. - Append('['); - SetColor(TerminalColor.DarkGreen); - Append('+'); - Append(passed.ToString(CultureInfo.CurrentCulture)); - ResetColor(); + if (!p.IsDiscovery) + { + // Use just ascii here, so we don't put too many restrictions on fonts needing to + // properly show unicode, or logs being saved in particular encoding. + Append('['); + SetColor(TerminalColor.DarkGreen); + Append('+'); + Append(passed.ToString(CultureInfo.CurrentCulture)); + ResetColor(); + + Append('/'); - Append('/'); + SetColor(TerminalColor.DarkRed); + Append('x'); + Append(failed.ToString(CultureInfo.CurrentCulture)); + ResetColor(); - SetColor(TerminalColor.DarkRed); - Append('x'); - Append(failed.ToString(CultureInfo.CurrentCulture)); - ResetColor(); + Append('/'); - Append('/'); + SetColor(TerminalColor.DarkYellow); + Append('?'); + Append(skipped.ToString(CultureInfo.CurrentCulture)); + ResetColor(); - SetColor(TerminalColor.DarkYellow); - Append('?'); - Append(skipped.ToString(CultureInfo.CurrentCulture)); - ResetColor(); - Append(']'); + Append(']'); + } + else + { + // Use just ascii here, so we don't put too many restrictions on fonts needing to + // properly show unicode, or logs being saved in particular encoding. + Append('['); + SetColor(TerminalColor.DarkMagenta); + Append('+'); + Append(discovered.ToString(CultureInfo.CurrentCulture)); + ResetColor(); + + Append(']'); + } Append(' '); Append(p.AssemblyName); diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs index 4494ea61535c..22929d8dfb7f 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TerminalTestReporter.cs @@ -139,7 +139,7 @@ private TestProgressState GetOrAddAssemblyRun(string assembly, string? targetFra return _assemblies.GetOrAdd(executionId, _ => { IStopwatch sw = CreateStopwatch(); - var assemblyRun = new TestProgressState(Interlocked.Increment(ref _counter), assembly, targetFramework, architecture, sw); + var assemblyRun = new TestProgressState(Interlocked.Increment(ref _counter), assembly, targetFramework, architecture, sw, _isDiscovery); int slotIndex = _terminalWithProgress.AddWorker(assemblyRun); assemblyRun.SlotIndex = slotIndex; @@ -280,13 +280,13 @@ private void AppendTestRunSummary(ITerminal terminal, int? exitCode) bool colorizePassed = passed > 0 && _buildErrorsCount == 0 && failed == 0 && error == 0; bool colorizeSkipped = skipped > 0 && skipped == total && _buildErrorsCount == 0 && failed == 0 && error == 0; - string errorText = $"{SingleIndentation}error: {error}"; - string totalText = $"{SingleIndentation}total: {total}"; - string retriedText = $" (+{retried} retried)"; - string failedText = $"{SingleIndentation}failed: {failed}"; - string passedText = $"{SingleIndentation}succeeded: {passed}"; - string skippedText = $"{SingleIndentation}skipped: {skipped}"; - string durationText = $"{SingleIndentation}duration: "; + string errorText = $"{SingleIndentation}{CliCommandStrings.ErrorColon} {error}"; + string totalText = $"{SingleIndentation}{CliCommandStrings.TotalColon} {total}"; + string retriedText = $" (+{retried} {CliCommandStrings.Retried})"; + string failedText = $"{SingleIndentation}{CliCommandStrings.FailedColon} {failed}"; + string passedText = $"{SingleIndentation}{CliCommandStrings.SucceededColon} {passed}"; + string skippedText = $"{SingleIndentation}{CliCommandStrings.SkippedColon} {skipped}"; + string durationText = $"{SingleIndentation}{CliCommandStrings.DurationColon} "; if (error > 0) { @@ -801,7 +801,7 @@ void AppendOutputWhenPresent(string description, string? output) private static void AppendAssemblySummary(TestProgressState assemblyRun, ITerminal terminal) { terminal.ResetColor(); - + AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, assemblyRun.Assembly, assemblyRun.TargetFramework, assemblyRun.Architecture); terminal.Append(' '); AppendAssemblyResult(terminal, assemblyRun); @@ -914,15 +914,15 @@ public void AppendTestDiscoverySummary(ITerminal terminal, int? exitCode) var assemblies = _assemblies.Select(asm => asm.Value).OrderBy(a => a.Assembly).Where(a => a is not null).ToList(); int totalTests = _assemblies.Values.Sum(a => a.TotalTests); - bool runFailed = _wasCancelled; + bool runFailed = _wasCancelled || totalTests < 1; foreach (TestProgressState assembly in assemblies) { - terminal.Append(string.Format(CultureInfo.CurrentCulture, CliCommandStrings.DiscoveredTestsInAssembly, assembly.DiscoveredTests.Count)); + terminal.Append(string.Format(CultureInfo.CurrentCulture, CliCommandStrings.DiscoveredTestsInAssembly, assembly.DiscoveredTestNames.Count)); terminal.Append(" - "); AppendAssemblyLinkTargetFrameworkAndArchitecture(terminal, assembly.Assembly, assembly.TargetFramework, assembly.Architecture); terminal.AppendLine(); - foreach ((string? displayName, string? uid) in assembly.DiscoveredTests) + foreach ((string? displayName, string? uid) in assembly.DiscoveredTestNames) { if (displayName is not null) { diff --git a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TestProgressState.cs b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TestProgressState.cs index a39d0a0fbe48..fd801f6a5a6c 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/Terminal/TestProgressState.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/Terminal/TestProgressState.cs @@ -6,7 +6,7 @@ namespace Microsoft.DotNet.Cli.Commands.Test.Terminal; -internal sealed class TestProgressState(long id, string assembly, string? targetFramework, string? architecture, IStopwatch stopwatch) +internal sealed class TestProgressState(long id, string assembly, string? targetFramework, string? architecture, IStopwatch stopwatch, bool isDiscovery) { private readonly Dictionary _testUidToResults = new(); @@ -24,13 +24,15 @@ internal sealed class TestProgressState(long id, string assembly, string? target public IStopwatch Stopwatch { get; } = stopwatch; + public int DiscoveredTests { get; private set; } + public int FailedTests { get; private set; } public int PassedTests { get; private set; } public int SkippedTests { get; private set; } - public int TotalTests => PassedTests + SkippedTests + FailedTests; + public int TotalTests => IsDiscovery ? DiscoveredTests : PassedTests + SkippedTests + FailedTests; public int RetriedFailedTests { get; private set; } @@ -42,10 +44,12 @@ internal sealed class TestProgressState(long id, string assembly, string? target public long Version { get; internal set; } - public List<(string? DisplayName, string? UID)> DiscoveredTests { get; internal set; } = []; + public List<(string? DisplayName, string? UID)> DiscoveredTestNames { get; internal set; } = []; public bool Success { get; internal set; } + public bool IsDiscovery = isDiscovery; + public int TryCount { get; private set; } private void ReportGenericTestResult( @@ -122,8 +126,8 @@ public void ReportFailedTest(string testNodeUid, string instanceId) public void DiscoverTest(string? displayName, string? uid) { - PassedTests++; - DiscoveredTests.Add(new(displayName, uid)); + DiscoveredTests++; + DiscoveredTestNames.Add(new(displayName, uid)); } internal void NotifyHandshake(string instanceId) diff --git a/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs b/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs index fc181b155db4..16ca1bb218e7 100644 --- a/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs +++ b/src/Cli/dotnet/Commands/Test/MTP/TestApplication.cs @@ -20,6 +20,7 @@ internal sealed class TestApplication( TerminalTestReporter output, Action onHelpRequested) : IDisposable { + private readonly Lock _requestLock = new(); private readonly BuildOptions _buildOptions = buildOptions; private readonly Action _onHelpRequested = onHelpRequested; private readonly TestApplicationHandler _handler = new(output, module, testOptions); @@ -199,63 +200,69 @@ private async Task WaitConnectionAsync(CancellationToken token) private Task OnRequest(NamedPipeServer server, IRequest request) { - try + // We need to lock as we might be called concurrently when test app child processes all communicate with us. + // For example, in a case of a sharding extension, we could get test result messages concurrently. + // To be the most safe, we lock the whole OnRequest. + lock (_requestLock) { - switch (request) + try { - case HandshakeMessage handshakeMessage: - _handshakes.Add(server, handshakeMessage); - string negotiatedVersion = GetSupportedProtocolVersion(handshakeMessage); - OnHandshakeMessage(handshakeMessage, negotiatedVersion.Length > 0); - return Task.FromResult((IResponse)CreateHandshakeMessage(negotiatedVersion)); - - case CommandLineOptionMessages commandLineOptionMessages: - OnCommandLineOptionMessages(commandLineOptionMessages); - break; - - case DiscoveredTestMessages discoveredTestMessages: - OnDiscoveredTestMessages(discoveredTestMessages); - break; - - case TestResultMessages testResultMessages: - OnTestResultMessages(testResultMessages); - break; - - case FileArtifactMessages fileArtifactMessages: - OnFileArtifactMessages(fileArtifactMessages); - break; - - case TestSessionEvent sessionEvent: - OnSessionEvent(sessionEvent); - break; - - // If we don't recognize the message, log and skip it - case UnknownMessage unknownMessage: - Logger.LogTrace($"Request '{request.GetType()}' with Serializer ID = {unknownMessage.SerializerId} is unsupported."); - return Task.FromResult((IResponse)VoidResponse.CachedInstance); - - default: - // If it doesn't match any of the above, throw an exception - throw new NotSupportedException(string.Format(CliCommandStrings.CmdUnsupportedMessageRequestTypeException, request.GetType())); + switch (request) + { + case HandshakeMessage handshakeMessage: + _handshakes.Add(server, handshakeMessage); + string negotiatedVersion = GetSupportedProtocolVersion(handshakeMessage); + OnHandshakeMessage(handshakeMessage, negotiatedVersion.Length > 0); + return Task.FromResult((IResponse)CreateHandshakeMessage(negotiatedVersion)); + + case CommandLineOptionMessages commandLineOptionMessages: + OnCommandLineOptionMessages(commandLineOptionMessages); + break; + + case DiscoveredTestMessages discoveredTestMessages: + OnDiscoveredTestMessages(discoveredTestMessages); + break; + + case TestResultMessages testResultMessages: + OnTestResultMessages(testResultMessages); + break; + + case FileArtifactMessages fileArtifactMessages: + OnFileArtifactMessages(fileArtifactMessages); + break; + + case TestSessionEvent sessionEvent: + OnSessionEvent(sessionEvent); + break; + + // If we don't recognize the message, log and skip it + case UnknownMessage unknownMessage: + Logger.LogTrace($"Request '{request.GetType()}' with Serializer ID = {unknownMessage.SerializerId} is unsupported."); + return Task.FromResult((IResponse)VoidResponse.CachedInstance); + + default: + // If it doesn't match any of the above, throw an exception + throw new NotSupportedException(string.Format(CliCommandStrings.CmdUnsupportedMessageRequestTypeException, request.GetType())); + } + } + catch (Exception ex) + { + // BE CAREFUL: + // When handling some of the messages, we may throw an exception in unexpected state. + // (e.g, OnSessionEvent may throw if we receive TestSessionEnd without TestSessionStart). + // (or if we receive help-related messages when not in help mode) + // In that case, we FailFast. + // The lack of FailFast *might* have unintended consequences, such as breaking the internal loop of pipe server. + // In that case, maybe MTP app will continue waiting for response, but we don't send the response and are waiting for + // MTP app process exit (which doesn't happen). + // So, we explicitly FailFast here. + string exAsString = ex.ToString(); + Logger.LogTrace(exAsString); + Environment.FailFast(exAsString); } - } - catch (Exception ex) - { - // BE CAREFUL: - // When handling some of the messages, we may throw an exception in unexpected state. - // (e.g, OnSessionEvent may throw if we receive TestSessionEnd without TestSessionStart). - // (or if we receive help-related messages when not in help mode) - // In that case, we FailFast. - // The lack of FailFast *might* have unintended consequences, such as breaking the internal loop of pipe server. - // In that case, maybe MTP app will continue waiting for response, but we don't send the response and are waiting for - // MTP app process exit (which doesn't happen). - // So, we explicitly FailFast here. - string exAsString = ex.ToString(); - Logger.LogTrace(exAsString); - Environment.FailFast(exAsString); - } - return Task.FromResult((IResponse)VoidResponse.CachedInstance); + return Task.FromResult((IResponse)VoidResponse.CachedInstance); + } } private static string GetSupportedProtocolVersion(HandshakeMessage handshakeMessage) diff --git a/src/Cli/dotnet/Commands/Test/TestCommandParser.cs b/src/Cli/dotnet/Commands/Test/TestCommandParser.cs index 0fb00c0de94d..db52e8975ed3 100644 --- a/src/Cli/dotnet/Commands/Test/TestCommandParser.cs +++ b/src/Cli/dotnet/Commands/Test/TestCommandParser.cs @@ -5,7 +5,6 @@ using System.Text.Json; using System.Text.Json.Serialization; using Microsoft.DotNet.Cli.Extensions; -using Microsoft.DotNet.Cli.Utils; using Command = System.CommandLine.Command; namespace Microsoft.DotNet.Cli.Commands.Test; @@ -234,7 +233,7 @@ private static Command ConstructCommand() private static Command GetTestingPlatformCliCommand() { - var command = new MicrosoftTestingPlatformTestCommand("test", CliCommandStrings.DotnetTestCommand); + var command = new MicrosoftTestingPlatformTestCommand("test", CliCommandStrings.DotnetTestCommandMTPDescription); command.SetAction(parseResult => command.Run(parseResult)); command.Options.Add(MicrosoftTestingPlatformOptions.ProjectOption); command.Options.Add(MicrosoftTestingPlatformOptions.SolutionOption); @@ -268,7 +267,7 @@ private static Command GetTestingPlatformCliCommand() private static Command GetVSTestCliCommand() { - DocumentedCommand command = new("test", DocsLink, CliCommandStrings.TestAppFullName) + DocumentedCommand command = new("test", DocsLink, CliCommandStrings.DotnetTestCommandVSTestDescription) { TreatUnmatchedTokensAsErrors = false }; diff --git a/src/Cli/dotnet/Commands/Test/VSTest/TestCommand.cs b/src/Cli/dotnet/Commands/Test/VSTest/TestCommand.cs index cf082e5e052e..a17cc0031e13 100644 --- a/src/Cli/dotnet/Commands/Test/VSTest/TestCommand.cs +++ b/src/Cli/dotnet/Commands/Test/VSTest/TestCommand.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 System.Collections.Frozen; using System.CommandLine; using System.Diagnostics.CodeAnalysis; using System.Runtime.Versioning; @@ -298,11 +297,16 @@ internal static int RunArtifactPostProcessingIfNeeded(string testSessionCorrelat private static bool ContainsBuiltTestSources(string[] args) { - foreach (string arg in args) + for (int i = 0; i < args.Length; i++) { - if (!arg.StartsWith("-") && - (arg.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || arg.EndsWith(".exe", StringComparison.OrdinalIgnoreCase))) + string arg = args[i]; + if (arg.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || arg.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) { + var previousArg = i > 0 ? args[i - 1] : null; + if (previousArg != null && CommonOptions.PropertiesOption.Aliases.Contains(previousArg)) + { + return false; + } return true; } } diff --git a/src/Cli/dotnet/Commands/Tool/Install/ToolInstallGlobalOrToolPathCommand.cs b/src/Cli/dotnet/Commands/Tool/Install/ToolInstallGlobalOrToolPathCommand.cs index 59a47e49159f..3e35a31ad9a9 100644 --- a/src/Cli/dotnet/Commands/Tool/Install/ToolInstallGlobalOrToolPathCommand.cs +++ b/src/Cli/dotnet/Commands/Tool/Install/ToolInstallGlobalOrToolPathCommand.cs @@ -92,7 +92,22 @@ public ToolInstallGlobalOrToolPathCommand( NoCache: parseResult.GetValue(ToolCommandRestorePassThroughOptions.NoCacheOption) || parseResult.GetValue(ToolCommandRestorePassThroughOptions.NoHttpCacheOption), IgnoreFailedSources: parseResult.GetValue(ToolCommandRestorePassThroughOptions.IgnoreFailedSourcesOption), Interactive: parseResult.GetValue(ToolCommandRestorePassThroughOptions.InteractiveRestoreOption)); - nugetPackageDownloader ??= new NuGetPackageDownloader.NuGetPackageDownloader(tempDir, verboseLogger: new NullLogger(), restoreActionConfig: _restoreActionConfig, verbosityOptions: _verbosity, verifySignatures: verifySignatures ?? true, shouldUsePackageSourceMapping: true); + nugetPackageDownloader ??= new NuGetPackageDownloader.NuGetPackageDownloader(tempDir, verboseLogger: new NullLogger(), restoreActionConfig: _restoreActionConfig, verbosityOptions: _verbosity, verifySignatures: verifySignatures ?? true, shouldUsePackageSourceMapping: true, currentWorkingDirectory: _currentWorkingDirectory); + + // Perform HTTP source validation early to ensure compatibility with .NET 9 requirements + if (_packageId != null) + { + var packageSourceLocationForValidation = new PackageSourceLocation( + nugetConfig: GetConfigFile(), + additionalSourceFeeds: _addSource, + basePath: _currentWorkingDirectory); + + if (nugetPackageDownloader is NuGetPackageDownloader.NuGetPackageDownloader concreteDownloader) + { + concreteDownloader.LoadNuGetSources((PackageId)_packageId, packageSourceLocationForValidation); + } + } + _shellShimTemplateFinder = new ShellShimTemplateFinder(nugetPackageDownloader, tempDir, packageSourceLocation); _store = store; diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf index d1d8709624e6..c4153bae2da7 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf @@ -186,7 +186,7 @@ Prohledané cesty: „{1}“, „{2}“. Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + Některé direktivy nelze převést. Spuštěním souboru zobrazíte všechny chyby kompilace. Zadejte „--force“, pokud chcete přesto provést převod. {Locked="--force"} @@ -585,7 +585,7 @@ Jedná se o ekvivalent odstranění project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Určuje minimální počet testů, jejichž spuštění se očekává. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + Soubor global.json definuje, že spouštěč testů bude Microsoft.Testing.Platform. Tento spouštěč testů musí používat všechny projekty. +Následující testovací projekty používají spouštěč testů VSTest: {0} -See https://aka.ms/dotnet-test/mtp for more information. +Další informace najdete na https://aka.ms/dotnet-test/mtp. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + chyba Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. Zjišťování testů z - - .NET Test Command - Testovací příkaz .NET - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Podporované verze protokolů odesílané Microsoft.Testing.Platform jsou {0}. Sada SDK podporuje {1}, což není kompatibilní. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + Byla přijata hodnota ExecutionId {0} pro zprávu {1}, zatímco ExecutionId přijaté zprávy metodou handshake bylo {2}. {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + Chyba při rušení serveru NamedPipeServer odpovídajícího zprávě metodou handshake: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + Při rušení serveru NamedPipeServer došlo k chybě a nebyla nalezena žádná zpráva metodou handshake. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + Příkaz dotnet neočekávaně obdržel z pojmenovaného kanálu dotnet test méně než 4 bajty. 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + Příkaz dotnet neočekávaně obdržel překrývající se zprávy z pojmenovaného kanálu dotnet test. @@ -1228,14 +1233,24 @@ Nastavte odlišné názvy profilů. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + Duplicitní direktivy nejsou podporovány: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + Při spuštění testovacího modulu s příkazem RunCommand {0} a argumenty RunArguments {1} došlo k následující výjimce: {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Nastavte odlišné názvy profilů. Nepovedlo se aktualizovat manifest reklamy {0}: {1}. + + failed: + failed: + + failed selhalo @@ -1568,12 +1588,12 @@ Nastavte odlišné názvy profilů. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + Metoda {0} nebyla úspěšně ukončena. The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + Direktiva by měla obsahovat název bez speciálních znaků a volitelnou hodnotu oddělenou znakem {1}, například #:{0} Název{1}Hodnota. {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Nastavte odlišné názvy profilů. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + Direktiva #:project je neplatná: {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Nástroj {1} (verze {2}) se úspěšně nainstaloval. Do souboru manifestu {3} s A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + Byla přijata nová zpráva metodou handshake {0} pro testovací aplikaci, který neodpovídá předchozí zprávě metodou handshake {1}. Missing name of '{0}'. - Missing name of '{0}'. + Chybí název pro: {0}. {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + Byla přijata počáteční událost testovací relace bez odpovídajícího konce testovací relace. @@ -2502,12 +2522,12 @@ Nástroj {1} (verze {2}) se úspěšně nainstaloval. Do souboru manifestu {3} s Invalid property name: {0} - Invalid property name: {0} + Neplatný název vlastnosti: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + Direktiva property musí mít dvě části oddělené znakem =, například #:property PropertyName=PropertyValue. {Locked="#:property"} @@ -2672,6 +2692,11 @@ Ve výchozím nastavení je publikována aplikace závislá na architektuře.Nástroj {0} (verze {1}) se obnovil. Dostupné příkazy: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). Posune se na vyšší verzi architektury (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). @@ -2951,6 +2976,11 @@ Cílem projektu je více architektur. Pomocí parametru {0} určete, která arch Vynechá vytvoření souborů symbolů, které lze použít k profilaci optimalizovaných sestavení. + + skipped: + skipped: + + skipped vynecháno @@ -3048,7 +3078,7 @@ Cílem projektu je více architektur. Pomocí parametru {0} určete, která arch Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + Statické obnovení grafu se pro souborové aplikace nepodporuje. Odeberte #:property. {Locked="#:property"} @@ -3086,6 +3116,11 @@ Cílem projektu je více architektur. Pomocí parametru {0} určete, která arch Cílový modul runtime pro uložení balíčků + + succeeded: + succeeded: + + Summary Souhrn @@ -3101,11 +3136,6 @@ Cílem projektu je více architektur. Pomocí parametru {0} určete, která arch Zadejte dočasný adresář pro tento příkaz, který se má stáhnout a extrahujte balíčky NuGet (musí být zabezpečené). - - .NET Test Driver - Testovací ovladač .NET - - + + total: + total: + + try {0} {0} pokus @@ -3564,22 +3599,22 @@ příkazu „dotnet tool list“. An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + Byla obdržena neočekávaná zpráva týkající se nápovědy, když nebyl použit parametr --help. A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + V režimu nápovědy byla přijata zpráva typu {0}, což není očekáváno. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + Zpráva typu {0} byla přijata předtím, než jsme obdrželi jakékoli zprávy metodou handshake, což je neočekávané. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + Byla přijata koncová událost testovací relace bez odpovídajícího začátku testovací relace. @@ -3599,7 +3634,7 @@ příkazu „dotnet tool list“. Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + Nerozpoznaná direktiva {0}. {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf index 2a39befb9d3c..a80d1aa8f4a6 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf @@ -186,7 +186,7 @@ Durchsuchte Pfade: "{1}", "{2}". Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + Einige Anweisungen können nicht konvertiert werden. Führen Sie die Datei aus, um alle Kompilierungsfehler anzuzeigen. Geben Sie „--force“ an, um das Umwandeln trotzdem auszuführen. {Locked="--force"} @@ -585,7 +585,7 @@ Dies entspricht dem Löschen von "project.assets.json". Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Gibt die Mindestanzahl von Tests an, die ausgeführt werden sollen. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json definiert den Testausführer als Microsoft.Testing.Platform. Alle Projekte müssen diesen Testausführer verwenden. +Die folgenden Testprojekte verwenden den VSTest-Testausführer: {0} -See https://aka.ms/dotnet-test/mtp for more information. +Weitere Informationen finden Sie unter https://aka.ms/dotnet-test/mtp. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + Fehler Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. Tests ermitteln aus - - .NET Test Command - Testbefehl .NET - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Unterstützte Protokollversionen, die von Microsoft.Testing.Platform gesendet werden, sind „{0}“. Das SDK unterstützt „{1}“, was inkompatibel ist. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + „ExecutionId“ des Werts „{0}“ für die Nachricht „{1}“ wurde empfangen, während die „ExecutionId“ der Handshakenachricht „{2}“ war. {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + Fehler beim Löschen von „NamedPipeServer“, der dem Handshake entspricht: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + Fehler beim Freigeben von „NamedPipeServer“, und es wurde kein Handshake gefunden. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + „dotnet“ hat unerwartet weniger als 4 Bytes von der Named Pipe „dotnet test“ empfangen. 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + „dotnet“ hat unerwartet überlappende Nachrichten von der Named Pipe „dotnet test“ empfangen. @@ -1228,14 +1233,24 @@ Erstellen Sie eindeutige Profilnamen. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + Doppelte Anweisungen werden nicht unterstützt: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + Beim Ausführen des Testmoduls mit RunCommand „{0}“ und RunArguments „{1}“ ist folgende Ausnahme aufgetreten: {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Erstellen Sie eindeutige Profilnamen. Fehler beim Aktualisieren des Ankündigungsmanifests "{0}": {1}. + + failed: + failed: + + failed fehlerhaft @@ -1568,12 +1588,12 @@ Erstellen Sie eindeutige Profilnamen. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + Die Methode "{0}" wurde nicht erfolgreich beendet The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + Die Anweisung sollte einen Namen ohne Sonderzeichen und einen optionalen Wert enthalten, die durch „{1}“ getrennt sind, wie „#:{0} Name{1}Wert“. {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Erstellen Sie eindeutige Profilnamen. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + Die Anweisung „#:p roject“ ist ungültig: {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Das Tool "{1}" (Version {2}) wurde erfolgreich installiert. Der Eintrag wird der A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + Für die Testanwendung wurde ein neuer Handshake „{0}“ empfangen, der nicht mit dem vorherigen Handshake „{1}“ übereinstimmt. Missing name of '{0}'. - Missing name of '{0}'. + Fehlender Name der Anweisung „{0}“. {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + Ein Testsitzungs-Startereignis wurde ohne entsprechendes Testsitzungs-Ende empfangen. @@ -2502,12 +2522,12 @@ Das Tool "{1}" (Version {2}) wurde erfolgreich installiert. Der Eintrag wird der Invalid property name: {0} - Invalid property name: {0} + Ungültiger Eigenschaftenname: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + Die Eigenschaftsanweisung muss zwei durch „=“ getrennte Teile aufweisen, z. B. „#:property PropertyName=PropertyValue“. {Locked="#:property"} @@ -2672,6 +2692,11 @@ Standardmäßig wird eine Framework-abhängige Anwendung veröffentlicht.Das Tool "{0}" (Version {1}) wurde wiederhergestellt. Verfügbare Befehle: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). Rollforward zu Frameworkversion (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). @@ -2951,6 +2976,11 @@ Ihr Projekt verwendet mehrere Zielframeworks. Geben Sie über "{0}" an, welches Hiermit wird die Erstellung von Symboldateien übersprungen, die für die Profilerstellung der optimierten Assemblys verwendet werden können. + + skipped: + skipped: + + skipped übersprungen @@ -3048,7 +3078,7 @@ Ihr Projekt verwendet mehrere Zielframeworks. Geben Sie über "{0}" an, welches Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + Die Statische Graphwiederherstellung wird für dateibasierte Apps nicht unterstützt. Entfernen Sie '#:property'. {Locked="#:property"} @@ -3086,6 +3116,11 @@ Ihr Projekt verwendet mehrere Zielframeworks. Geben Sie über "{0}" an, welches Die Zielruntime zum Speichern von Paketen. + + succeeded: + succeeded: + + Summary Zusammenfassung @@ -3101,11 +3136,6 @@ Ihr Projekt verwendet mehrere Zielframeworks. Geben Sie über "{0}" an, welches Geben Sie ein temporäres Verzeichnis für diesen Befehl zum Herunterladen und Extrahieren von NuGet-Paketen an (muss sicher sein). - - .NET Test Driver - .NET-Testtreiber - - + + total: + total: + + try {0} {0} testen @@ -3564,22 +3599,22 @@ und die zugehörigen Paket-IDs für installierte Tools über den Befehl An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + Eine unerwartete hilfebezogene Nachricht wurde empfangen, obwohl „--help“ nicht verwendet wurde. A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + Eine Nachricht vom Typ „{0}“ wurde im Hilfemodus empfangen, was nicht erwartet wird. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + Eine Nachricht vom Typ „{0}“ wurde empfangen, bevor ein Handshake erfolgte, was unerwartet ist. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + Ein Testsitzungs-Endereignis wurde ohne einen entsprechenden Testsitzungsstart empfangen. @@ -3599,7 +3634,7 @@ und die zugehörigen Paket-IDs für installierte Tools über den Befehl Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + Unbekannte Anweisung „{0}“. {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf index 84f4aabb9069..f3db42e561bc 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf @@ -186,7 +186,7 @@ Rutas de acceso buscadas: "{1}", "{2}". Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + Algunas directivas no se pueden convertir. Ejecute el archivo para ver todos los errores de compilación. Especifique "--force" para convertir de todos modos. {Locked="--force"} @@ -585,7 +585,7 @@ Esta acción es equivalente a eliminar project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Especifica el número mínimo de pruebas que se espera que se ejecuten. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json define el ejecutor de pruebas para que sea Microsoft.Testing.Platform. Todos los proyectos deben usar ese ejecutor de pruebas. +Los siguientes proyectos de prueba usan el ejecutor de pruebas de VSTest: {0} -See https://aka.ms/dotnet-test/mtp for more information. +Consulte https://aka.ms/dotnet-test/mtp para obtener más información. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + error Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. Detección de pruebas de - - .NET Test Command - Comando de prueba de .NET - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Las versiones de protocolo admitidas enviadas por Microsoft.Testing.Platform son "{0}". El SDK admite "{1}", que es incompatible. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + Se recibió un "ExecutionId" con valor "{0}" para el mensaje "{1}", mientras que el "ExecutionId" recibido en el mensaje de protocolo de enlace era "{2}". {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + Error al liberar "NamedPipeServer" correspondiente al protocolo de enlace: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + Error al liberar "NamedPipeServer" y no se encontró ningún protocolo de enlace. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + "dotnet" recibió inesperadamente menos de 4 bytes de la canalización con nombre "dotnet test". 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + "dotnet" recibió inesperadamente mensajes superpuestos desde la canalización con nombre "dotnet test". @@ -1228,14 +1233,24 @@ Defina nombres de perfiles distintos. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + No se admiten directivas duplicadas: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + Se produjo la siguiente excepción al ejecutar el módulo de prueba con RunCommand "{0}" y RunArguments "{1}": {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Defina nombres de perfiles distintos. No se pudo actualizar el manifiesto publicitario {0}: {1}. + + failed: + failed: + + failed con errores @@ -1568,12 +1588,12 @@ Defina nombres de perfiles distintos. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + El método '{0}' no se cerró correctamente The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + La directiva debe contener un nombre sin caracteres especiales y un valor opcional separado por "{1}" como "#:{0} Nombre{1}Valor". {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Defina nombres de perfiles distintos. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + La directiva "#:project" no es válida: {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ La herramienta "{1}" (versión "{2}") se instaló correctamente. Se ha agregado A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + Se recibió un nuevo protocolo de enlace "{0}" para la aplicación de prueba que no coincide con el protocolo de enlace anterior "{1}". Missing name of '{0}'. - Missing name of '{0}'. + Falta el nombre de "{0}". {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + Se recibió un evento de inicio de sesión de prueba sin un evento de finalización de sesión de prueba correspondiente. @@ -2502,12 +2522,12 @@ La herramienta "{1}" (versión "{2}") se instaló correctamente. Se ha agregado Invalid property name: {0} - Invalid property name: {0} + Nombre de propiedad no válido {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + La directiva de propiedad debe tener dos partes separadas por "=", como "#:property PropertyName=PropertyValue". {Locked="#:property"} @@ -2672,6 +2692,11 @@ El valor predeterminado es publicar una aplicación dependiente del marco.Se restauró la herramienta "{0}" (versión "{1}"). Comandos disponibles: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). Reenviar a la versión del marco (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). @@ -2951,6 +2976,11 @@ Su proyecto tiene como destino varias plataformas. Especifique la que quiere usa Omite la creación de archivos de símbolos que se pueden usar para generar perfiles para los ensamblados optimizados. + + skipped: + skipped: + + skipped omitido @@ -3048,7 +3078,7 @@ Su proyecto tiene como destino varias plataformas. Especifique la que quiere usa Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + No se admite la restauración de gráficos estáticos para aplicaciones basadas en archivos. Elimine "#:property". {Locked="#:property"} @@ -3086,6 +3116,11 @@ Su proyecto tiene como destino varias plataformas. Especifique la que quiere usa El entorno tiempo de ejecución de destino para el que se almacenan los paquetes. + + succeeded: + succeeded: + + Summary Resumen @@ -3101,11 +3136,6 @@ Su proyecto tiene como destino varias plataformas. Especifique la que quiere usa Especifique un directorio temporal para que este comando descargue y extraiga paquetes NuGet (debe ser seguro). - - .NET Test Driver - Controlador de pruebas de .NET - - + + total: + total: + + try {0} intento {0} @@ -3564,22 +3599,22 @@ y los identificadores de los paquetes correspondientes a las herramientas instal An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + Se recibió un mensaje inesperado relacionado con la ayuda cuando no se usó "--help". A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + Se recibió un mensaje de tipo "{0}" en modo de ayuda, lo cual no se esperaba. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + Se recibió un mensaje de tipo "{0}" antes de recibir cualquier protocolo de enlace, lo cual es inesperado. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + Se recibió un evento de finalización de sesión de prueba sin un evento de inicio de sesión de prueba correspondiente. @@ -3599,7 +3634,7 @@ y los identificadores de los paquetes correspondientes a las herramientas instal Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + Directiva no reconocida "{0}". {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf index 6a637323a60e..25b8735db3dd 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf @@ -186,7 +186,7 @@ Les chemins d’accès ont recherché : « {1} », « {2} ». Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + Vous ne pouvez pas convertir certaines directives. Exécutez le fichier pour voir toutes les erreurs de compilation. Spécifiez « --force » pour convertir quand même. {Locked="--force"} @@ -585,7 +585,7 @@ Cela équivaut à supprimer project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Spécifie le nombre minimal de tests censés s’exécuter. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json définit le test runner en tant que Microsoft.Testing.Platform. Tous les projets doivent utiliser ce test runner. +Les projets de test suivants utilisent le test runner VSTest : {0} -See https://aka.ms/dotnet-test/mtp for more information. +Pour découvrir plus d’informations, consultez https://aka.ms/dotnet-test/mtp. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + erreur Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. Découverte des tests à partir de - - .NET Test Command - Commande de test .NET - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Les versions de protocole prises en charge envoyées par Microsoft.Testing.Platform sont « {0} ». Le Kit de développement logiciel (SDK) prend en charge « {1} », ce qui est incompatible. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + Réception d’un « ExecutionId » de valeur « {0} » pour le message « {1} », alors que « l’ExecutionId » reçu dans le message d’établissement d’une liaison était « {2} ». {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + Erreur lors de la suppression de « NamedPipeServer » correspondant à l’établissement d’une liaison : Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + Erreur lors de la suppression de « NamedPipeServer » et aucun établissement d’une liaison n’a été trouvé. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + « dotnet » a reçu de manière inattendue moins de 4 octets du canal nommé « dotnet test ». 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + « dotnet » a reçu de manière inattendue des messages qui se chevauchent du canal nommé « dotnet test ». @@ -1228,14 +1233,24 @@ Faites en sorte que les noms de profil soient distincts. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + Les directives dupliquées ne sont pas prises en charge : {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + L’exception suivante s’est produite lors de l’exécution du module de test avec la commande RunCommand « {0} » et les arguments RunArguments « {1} » : {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Faites en sorte que les noms de profil soient distincts. Échec de la mise à jour du manifeste de publicité {0} : {1}. + + failed: + failed: + + failed échec @@ -1568,12 +1588,12 @@ Faites en sorte que les noms de profil soient distincts. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + La méthode « {0} » ne s’est pas arrêtée correctement The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + La directive dans doit contenir un nom sans caractères spéciaux et une valeur facultative séparée par « {1} » comme « # :{0} Nom{1}Valeur ». {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Faites en sorte que les noms de profil soient distincts. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + La directive « #:project » n’est pas valide : {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ L'outil '{1}' (version '{2}') a été correctement installé. L'entrée est ajou A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + Une nouvelle négociation « {0} » a été reçue pour l’application de test, qui ne correspond pas à l’établissement d’une liaison précédente « {1} ». Missing name of '{0}'. - Missing name of '{0}'. + Nom manquant pour « {0} ». {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + Un événement de début de session de test a été reçu sans fin d’une session de test correspondante. @@ -2502,12 +2522,12 @@ L'outil '{1}' (version '{2}') a été correctement installé. L'entrée est ajou Invalid property name: {0} - Invalid property name: {0} + Nom de propriété non valide : {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + La directive de propriété doit avoir deux parties séparées par '=' comme '#:property PropertyName=PropertyValue'. {Locked="#:property"} @@ -2672,6 +2692,11 @@ La valeur par défaut est de publier une application dépendante du framework.L'outil '{0}' (version '{1}') a été restauré. Commandes disponibles : {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). Restaurer par progression la version du framework (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). @@ -2951,6 +2976,11 @@ Votre projet cible plusieurs frameworks. Spécifiez le framework à exécuter à Ignorez la création de fichiers de symboles pour le profilage des assemblys optimisés. + + skipped: + skipped: + + skipped ignoré @@ -3048,7 +3078,7 @@ Votre projet cible plusieurs frameworks. Spécifiez le framework à exécuter à Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + La restauration de graphique statique n’est pas prise en charge pour les applications basées sur des fichiers. Supprimer la « #:property ». {Locked="#:property"} @@ -3086,6 +3116,11 @@ Votre projet cible plusieurs frameworks. Spécifiez le framework à exécuter à Runtime cible pour lequel le stockage des packages est effectué. + + succeeded: + succeeded: + + Summary Récapitulatif @@ -3101,11 +3136,6 @@ Votre projet cible plusieurs frameworks. Spécifiez le framework à exécuter à Spécifiez un répertoire temporaire pour que cette commande télécharge et extrait les packages NuGet (doit être sécurisé). - - .NET Test Driver - Pilote de test .NET - - + + total: + total: + + try {0} essayer {0} @@ -3564,22 +3599,22 @@ et les ID de package correspondants aux outils installés, utilisez la commande An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + Un message d’aide inattendu a été reçu alors que `--help` n’a pas été utilisé. A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + Un message de type « {0} » a été reçu en mode d’aide, ce qui n’est pas prévu. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + Un message de type « {0} » a été reçu avant tout établissement d’une liaison, ce qui est inattendu. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + Un événement de fin de session de test a été reçu sans démarrage d’une session de test correspondante. @@ -3599,7 +3634,7 @@ et les ID de package correspondants aux outils installés, utilisez la commande Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + Directive « {0} » non reconnue. {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf index 1f255f43c794..208f99b28efc 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf @@ -186,7 +186,7 @@ Percorsi cercati: '{1}', '{2}'. Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + Non è possibile convertire alcune direttive. Eseguire il file per visualizzare tutti gli errori di compilazione. Specificare '--force' per eseguire comunque la conversione. {Locked="--force"} @@ -585,7 +585,7 @@ Equivale a eliminare project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Specifica il numero minimo dei test che si prevede saranno eseguiti. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json definisce il Test runner come Microsoft.Testing.Platform. Tutti i progetti devono usare tale Test Runner. +I progetti di test seguenti usano il Test Runner VSTest: {0} -See https://aka.ms/dotnet-test/mtp for more information. +Per altre informazioni, vedere https://aka.ms/dotnet-test/mtp. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + errore Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. Individuazione di test da - - .NET Test Command - Comando di test .NET - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Le versioni di protocollo supportate inviate da Microsoft.Testing.Platform sono '{0}'. L'SDK supporta '{1}', che non è compatibile. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + È stato ricevuto l''ExecutionId' del valore '{0}' per il messaggio '{1}' mentre l''ExecutionId' ricevuto nel messaggio di handshake era '{2}'. {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + Errore durante l'eliminazione del 'NamePipeServer' corrispondente all'handshake: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + Errore durante l'eliminazione di 'NamedPipeServer' e non è stato trovato alcun handshake. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + 'dotnet' ha ricevuto inaspettatamente meno di 4 byte dal named pipe 'dotnet test'. 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + 'dotnet' ha ricevuto inaspettatamente messaggi sovrapposti dal named pipe 'dotnet test'. @@ -1228,14 +1233,24 @@ Rendi distinti i nomi profilo. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + Le direttive duplicate non supportate: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + Si è verificata l'eccezione seguente durante l'esecuzione del modulo di test con RunCommand '{0}' e RunArguments '{1}': {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Rendi distinti i nomi profilo. Non è stato possibile scaricare il manifesto di pubblicità {0}: {1}. + + failed: + failed: + + failed operazione non riuscita @@ -1568,12 +1588,12 @@ Rendi distinti i nomi profilo. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + Il metodo '{0}' non è stato chiuso correttamente The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + La direttiva deve contenere un nome senza caratteri speciali e un valore facoltativo delimitato da '{1}' come '#:{0}Nome {1}Valore'. {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Rendi distinti i nomi profilo. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + La direttiva '#:project' non è valida: {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Lo strumento '{1}' versione '{2}' è stato installato. La voce è stata aggiunta A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + È stato ricevuto un nuovo handshake '{0}' per l'applicazione di test che non corrisponde all'handshake precedente '{1}'. Missing name of '{0}'. - Missing name of '{0}'. + Manca il nome di '{0}'. {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + È stato ricevuto un evento di inizio sessione di test senza un evento di fine sessione corrispondente. @@ -2502,12 +2522,12 @@ Lo strumento '{1}' versione '{2}' è stato installato. La voce è stata aggiunta Invalid property name: {0} - Invalid property name: {0} + Nome proprietà non valido: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + La direttiva di proprietà deve avere due parti delimitate da '=', come '#:property PropertyName=PropertyValue'. {Locked="#:property"} @@ -2672,6 +2692,11 @@ Per impostazione predefinita, viene generato un pacchetto dipendente dal framewo Lo strumento '{0}' (versione '{1}') è stato ripristinato. Comandi disponibili: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). Esegue il roll forward alla versione del framework (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). @@ -2951,6 +2976,11 @@ Il progetto è destinato a più framework. Specificare il framework da eseguire Non crea i file di simboli che è possibile usare per la profilatura degli assembly ottimizzati. + + skipped: + skipped: + + skipped ignorato @@ -3048,7 +3078,7 @@ Il progetto è destinato a più framework. Specificare il framework da eseguire Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + Il ripristino statico del grafo non è supportato per le app basate su file. Rimuovere '#:property'. {Locked="#:property"} @@ -3086,6 +3116,11 @@ Il progetto è destinato a più framework. Specificare il framework da eseguire Runtime di destinazione per cui archiviare i pacchetti. + + succeeded: + succeeded: + + Summary Riepilogo @@ -3101,11 +3136,6 @@ Il progetto è destinato a più framework. Specificare il framework da eseguire Specificare una directory temporanea per questo comando per scaricare ed estrarre i pacchetti NuGet (deve essere protetta). - - .NET Test Driver - Driver di test .NET - - + + total: + total: + + try {0} prova {0} @@ -3564,22 +3599,22 @@ e gli ID pacchetto corrispondenti per gli strumenti installati usando il comando An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + È stato ricevuto un messaggio imprevisto relativo alla Guida quando non è stato usato '--help'. A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + È stato ricevuto un messaggio di tipo '{0}' in modalità Guida, cosa non prevista. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + È stato ricevuto un messaggio di tipo '{0}' prima di ricevere handshake, cosa imprevista. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + È stato ricevuto un evento di fine sessione di test senza un evento di inizio sessione corrispondente. @@ -3599,7 +3634,7 @@ e gli ID pacchetto corrispondenti per gli strumenti installati usando il comando Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + Direttiva non riconosciuta '{0}'. {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf index 7aa8108b6954..4fdc18894bad 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf @@ -186,7 +186,7 @@ Paths searched: '{1}', '{2}'. Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + 一部のディレクティブは変換できません。ファイルを実行して、すべてのコンパイル エラーを表示します。それでも変換する場合は '--force' を指定してください。 {Locked="--force"} @@ -585,7 +585,7 @@ This is equivalent to deleting project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + 実行する必要があるテストの最小数を指定します。 @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json では、テスト ランナーを Microsoft.Testing.Platform と定義します。すべてのプロジェクトで、そのテスト ランナーを使用する必要があります。 +次のテスト プロジェクトでは、VSTest テスト ランナーを使用しています: {0} -See https://aka.ms/dotnet-test/mtp for more information. +詳細については、https://aka.ms/dotnet-test/mtp を参照してください。 {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + エラー Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. からテストを検出しています - - .NET Test Command - .NET Test コマンド - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Microsoft.Testing.Platform から送信されたサポートされているプロトコル バージョンは '{0}' です。SDK は互換性のない '{1}' をサポートしています。 Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + ハンドシェイク メッセージで受信した 'ExecutionId' が '{0}' のときに、メッセージ '{1}' の 'ExecutionId' 値 '{2}' を受信しました。 {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + ハンドシェイクに対応する 'NamedPipeServer' の破棄中にエラーが発生しました: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + 'NamedPipeServer' の破棄中にエラーが発生し、ハンドシェイクが見つかりませんでした。 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + 'dotnet' は 'dotnet test' 名前付きパイプから予期せず 4 バイト未満を受信しました。 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + 'dotnet' は 'dotnet test' 名前付きパイプから予期せず重複するメッセージを受信しました。 @@ -1228,14 +1233,24 @@ Make the profile names distinct. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + 重複するディレクティブはサポートされていません: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + RunCommand '{0}' および RunArguments '{1}' でテスト モジュールを実行中に、次の例外が発生しました: {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Make the profile names distinct. 広告マニフェスト {0} を更新できませんでした: {1}。 + + failed: + failed: + + failed 失敗 @@ -1568,12 +1588,12 @@ Make the profile names distinct. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + メソッド '{0}' が正常に終了しませんでした The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + ディレクティブには、特殊文字を含まない名前と、'#:{0} Name{1}Value' などの '{1}' で区切られた省略可能な値を含める必要があります。 {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Make the profile names distinct. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + '#:p roject' ディレクティブが無効です: {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + テスト アプリケーションに対して、前回のハンドシェイク '{0}' と一致しない新しいハンドシェイク '{1}' を受信しました。 Missing name of '{0}'. - Missing name of '{0}'. + '{0}' の名前がありません。 {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + 対応するテスト セッションの終了がないまま、テスト セッション開始イベントを受信しました。 @@ -2502,12 +2522,12 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man Invalid property name: {0} - Invalid property name: {0} + 無効なプロパティ名: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + プロパティ ディレクティブには、'#:property PropertyName=PropertyValue' のように '=' で区切られた 2 つの部分が必要です。 {Locked="#:property"} @@ -2672,6 +2692,11 @@ The default is to publish a framework-dependent application. ツール '{0}' (バージョン '{1}') は復元されました。使用できるコマンド: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). フレームワーク バージョン (LatestPatch、Minor、LatestMinor、Major、LatestMajor、Disable) にロールフォワードします。 @@ -2951,6 +2976,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' 最適化されたアセンブリのプロファイルに使用できるシンボル ファイルの作成をスキップします。 + + skipped: + skipped: + + skipped スキップされました @@ -3048,7 +3078,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + 静的グラフの復元はファイルベースのアプリではサポートされていません。'#:property' を削除します。 {Locked="#:property"} @@ -3086,6 +3116,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' パッケージを格納するターゲット ランタイム。 + + succeeded: + succeeded: + + Summary 概要 @@ -3101,11 +3136,6 @@ Your project targets multiple frameworks. Specify which framework to run using ' このコマンドに NuGet パッケージをダウンロードして抽出するための一時ディレクトリを指定します (セキュリティで保護する必要があります)。 - - .NET Test Driver - .NET Test Driver - - + + total: + total: + + try {0} {0} を試す @@ -3564,22 +3599,22 @@ and the corresponding package Ids for installed tools using the command An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + '--help' が使用されていないときに、予期しないヘルプ関連のメッセージを受信しました。 A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + 種類 '{0}' のメッセージをヘルプ モードで受信しましたが、これは想定されていません。 A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + ハンドシェイクを受信する前に、種類 '{0}' のメッセージを受信しましたが、これは想定されていません。 A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + 対応するテスト セッションの開始がないまま、テスト セッション終了イベントを受信しました。 @@ -3599,7 +3634,7 @@ and the corresponding package Ids for installed tools using the command Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + 認識されないディレクティブ '{0}' です。 {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf index 6544ed0d7051..51da07712783 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf @@ -186,7 +186,7 @@ Paths searched: '{1}', '{2}'. Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + 일부 지시문을 변환할 수 없습니다. 파일을 실행하여 모든 컴파일 오류를 확인하세요. 변환을 강제로 진행하려면 '--force'를 지정하세요. {Locked="--force"} @@ -585,7 +585,7 @@ project.assets.json을 삭제하는 것과 동일합니다. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + 실행될 것으로 예상되는 최소 테스트 수를 지정합니다. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json은 Test Runner를 Microsoft.Testing.Platform으로 정의합니다. 모든 프로젝트는 해당 Test Runner를 사용해야 합니다. +다음 테스트 프로젝트에서 VSTest Test Runner를 사용하고 있습니다. {0} -See https://aka.ms/dotnet-test/mtp for more information. +자세한 내용은 https://aka.ms/dotnet-test/mtp를 참조하세요. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + 오류 Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. 다음에서 테스트 검색하는 중 - - .NET Test Command - .NET 테스트 명령 - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Microsoft.Testing.Platform에서 보낸 지원되는 프로토콜 버전은 '{0}'입니다. SDK는 호환되지 않는 '{1}'을(를) 지원합니다. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + 핸드셰이크 메시지에서 받은 'ExecutionId'가 '{2}'인 동안 메시지 '{1}'에 대해 값 '{0}'의 'ExecutionId'가 수신되었습니다. {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + 핸드셰이크에 해당하는 'NamedPipeServer'를 삭제하는 동안 오류가 발생했습니다. Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + 'NamedPipeServer'를 삭제하는 동안 오류가 발생하여 핸드셰이크를 찾을 수 없습니다. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + 'dotnet'이 'dotnet test'로 명명된 파이프에서 예기치 않게 4바이트 미만의 데이터를 받았습니다. 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + 'dotnet'이 'dotnet test'로 명명된 파이프에서 예기치 않게 겹치는 메시지를 받았습니다. @@ -1228,14 +1233,24 @@ Make the profile names distinct. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + 중복 지시문은 지원되지 않습니다. {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + RunCommand '{0}' 및 RunArguments '{1}'(으)로 테스트 모듈을 실행하는 동안 다음 예외가 발생했습니다. {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Make the profile names distinct. 광고 매니페스트 {0}: {1}을(를) 업데이트하지 못했습니다. + + failed: + failed: + + failed 실패 @@ -1568,12 +1588,12 @@ Make the profile names distinct. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + '{0}' 메서드가 성공적으로 종료되지 않았습니다. The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + 지시문에는 특수 문자가 없는 이름과 '#:{0} 이름{1}값'과 같이 '{1}'(으)로 구분된 선택적 값이 포함되어야 합니다. {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Make the profile names distinct. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + '#:p roject' 지시문이 잘못되었습니다. {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + 이전 핸드셰이크 '{1}'과(와) 일치하지 않는 테스트 애플리케이션에 대해 새 핸드셰이크 '{0}'이(가) 수신되었습니다. Missing name of '{0}'. - Missing name of '{0}'. + '{0}' 이름이 없습니다. {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + 해당 테스트 세션 종료 없이 테스트 세션 시작 이벤트를 받았습니다. @@ -2502,12 +2522,12 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man Invalid property name: {0} - Invalid property name: {0} + 잘못된 속성 이름: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + property 지시문에는 '#:property PropertyName=PropertyValue'와 같이 '='로 구분된 두 부분이 있어야 합니다. {Locked="#:property"} @@ -2672,6 +2692,11 @@ The default is to publish a framework-dependent application. '{0}' 도구(버전 '{1}')가 복원되었습니다. 사용 가능한 명령: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). 프레임워크 버전(LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable)으로 롤포워드합니다. @@ -2951,6 +2976,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' 최적화된 어셈블리 프로파일링에 대해 사용할 수 있는 기호 파일 만들기를 건너뜁니다. + + skipped: + skipped: + + skipped 건너뜀 @@ -3048,7 +3078,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + 정적 그래프 복원은 파일 기반 앱에서 지원되지 않습니다. '#:property'를 제거합니다. {Locked="#:property"} @@ -3086,6 +3116,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' 패키지를 저장할 대상 런타임입니다. + + succeeded: + succeeded: + + Summary 요약 @@ -3101,11 +3136,6 @@ Your project targets multiple frameworks. Specify which framework to run using ' NuGet 패키지를 다운로드하고 추출하려면 이 명령의 임시 디렉터리를 지정합니다(보안이 있어야 합니다). - - .NET Test Driver - .NET 테스트 드라이버 - - + + total: + total: + + try {0} {0} 시도 @@ -3564,22 +3599,22 @@ and the corresponding package Ids for installed tools using the command An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + '--help'를 사용하지 않았는데 예기치 않은 도움말 관련 메시지를 받았습니다. A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + 도움말 모드에서 예상치 못하게 '{0}' 형식의 메시지를 받았습니다. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + 핸드셰이크를 받기 전에 예기치 않게 '{0}' 형식의 메시지가 수신되었습니다. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + 해당 테스트 세션을 시작하지 않고 테스트 세션 종료 이벤트를 받았습니다. @@ -3599,7 +3634,7 @@ and the corresponding package Ids for installed tools using the command Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + 인식할 수 없는 지시문 '{0}'입니다. {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf index 6e7de9021244..7891dbad5c4c 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf @@ -186,7 +186,7 @@ Przeszukane ścieżki: „{1}”, „{2}”. Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + Nie można przekonwertować niektórych dyrektyw. Uruchom plik, aby wyświetlić wszystkie błędy kompilacji. Określ element „--force”, aby mimo to przekonwertować. {Locked="--force"} @@ -585,7 +585,7 @@ Jest to równoważne usunięciu pliku project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Określa minimalną liczbę testów, które mają zostać uruchomione. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json definiuje moduł uruchamiający testy jako Microsoft.Testing.Platform. Wszystkie projekty muszą używać tego modułu uruchamiającego testy. +Następujące projekty testowe używają modułu uruchamiającego testy VSTest: {0} -See https://aka.ms/dotnet-test/mtp for more information. +Aby uzyskać więcej informacji, zobacz https://aka.ms/dotnet-test/mtp. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + błąd Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. Odnajdywanie testów w - - .NET Test Command - Polecenie testowe platformy .NET - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Obsługiwane wersje protokołów wysyłane przez element Microsoft.Testing.Platform to „{0}”. Zestaw SDK obsługuje element „{1}”, co jest niezgodne. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + Odebrano wartość „ExecutionId” dla{0}komunikatu „{1}”, a element „ExecutionId” odebrany z komunikatu uzgadniania to „{2}”. {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + Błąd usuwania elementu „NamedPipeServer” odpowiadającego uzgadnianiu: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + Wystąpił błąd podczas usuwania elementu „NamedPipeServer” i nie znaleziono uzgadniania. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + Element „dotnet” nieoczekiwanie odebrał mniej niż 4 bajty z nazwanego potoku "dotnet test". 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + Element "dotnet" nieoczekiwanie odebrał nakładające się komunikaty z nazwanego potoku „dotnet test”. @@ -1228,14 +1233,24 @@ Rozróżnij nazwy profilów. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + Zduplikowane dyrektywy nie są obsługiwane: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + Wystąpił następujący wyjątek podczas uruchamiania modułu testowego z elementami RunCommand „{0}” i RunArguments „{1}”: {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Rozróżnij nazwy profilów. Nie można zaktualizować manifestu anonsowania {0}: {1}. + + failed: + failed: + + failed zakończone niepowodzeniem @@ -1568,12 +1588,12 @@ Rozróżnij nazwy profilów. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + Metoda „{0}” nie zakończyła się pomyślnie The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + Dyrektywa powinna zawierać nazwę bez znaków specjalnych i opcjonalną wartość rozdzieloną znakiem "{1}#:{0} Name{1}Value". {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Rozróżnij nazwy profilów. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + Dyrektywa „#:project” jest nieprawidłowa: {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Narzędzie „{1}” (wersja „{2}”) zostało pomyślnie zainstalowane. Wpis A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + Odebrano nowe uzgadnianie „{0}” dla aplikacji testowej, która nie pasuje do poprzedniego uzgadniania „{1}”. Missing name of '{0}'. - Missing name of '{0}'. + Brak nazwy „{0}”. {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + Odebrano zdarzenie rozpoczęcia sesji testowej bez odpowiedniego zakończenia sesji testowej. @@ -2502,12 +2522,12 @@ Narzędzie „{1}” (wersja „{2}”) zostało pomyślnie zainstalowane. Wpis Invalid property name: {0} - Invalid property name: {0} + Nieprawidłowa nazwa właściwości: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + Dyrektywa właściwości musi mieć dwie części oddzielone znakiem „=”, na przykład „#:property PropertyName=PropertyValue”. {Locked="#:property"} @@ -2672,6 +2692,11 @@ Domyślnie publikowana jest aplikacja zależna od struktury. Narzędzie „{0}” (wersja „{1}”) zostało przywrócone. Dostępne polecenia: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). Przewiń do wersji platformy (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). @@ -2951,6 +2976,11 @@ Projekt ma wiele platform docelowych. Określ platformę do uruchomienia przy u Pomiń tworzenie plików symboli, za pomocą których można profilować zoptymalizowane zestawy. + + skipped: + skipped: + + skipped pominięte @@ -3048,7 +3078,7 @@ Projekt ma wiele platform docelowych. Określ platformę do uruchomienia przy u Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + Przywracanie statycznego grafu nie jest obsługiwane w przypadku aplikacji opartych na plikach. Usuń element „#:property”. {Locked="#:property"} @@ -3086,6 +3116,11 @@ Projekt ma wiele platform docelowych. Określ platformę do uruchomienia przy u Docelowe środowisko uruchomieniowe, dla którego mają być przechowywane pakiety. + + succeeded: + succeeded: + + Summary Podsumowanie @@ -3101,11 +3136,6 @@ Projekt ma wiele platform docelowych. Określ platformę do uruchomienia przy u Określ katalog tymczasowy dla tego polecenia, aby pobrać i wyodrębnić pakiety NuGet (musi być bezpieczny). - - .NET Test Driver - Sterownik testów platformy .NET - - + + total: + total: + + try {0} wypróbuj {0} @@ -3564,22 +3599,22 @@ i odpowiednie identyfikatory pakietów zainstalowanych narzędzi można znaleź An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + Odebrano nieoczekiwany komunikat związany z pomocą, gdy nie użyto elementu „--help”. A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + Komunikat typu „{0}” został odebrany w trybie pomocy, co nie jest oczekiwane. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + Komunikat typu „{0}” został odebrany przed otrzymaniem jakichkolwiek uzgadniań, co jest nieoczekiwane. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + Odebrano zdarzenie zakończenia sesji testowej bez odpowiedniego uruchomienia sesji testowej. @@ -3599,7 +3634,7 @@ i odpowiednie identyfikatory pakietów zainstalowanych narzędzi można znaleź Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + Nierozpoznana dyrektywa „{0}”. {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf index 032d2caf149f..1365db95b6fc 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf @@ -186,7 +186,7 @@ Caminhos pesquisados: "{1}", "{2}". Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + Algumas diretivas não podem ser convertidas. Execute o arquivo para ver todos os erros de compilação. Especifique '--force' para converter mesmo assim. {Locked="--force"} @@ -585,7 +585,7 @@ Isso equivale a excluir o project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Especifica o número mínimo de testes que devem ser executados. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + O global.json define o executor de teste como Microsoft.Testing.Platform. Todos os projetos devem usar esse executor de teste. +Os seguintes projetos de teste estão usando o executor de teste do VSTest: {0} -See https://aka.ms/dotnet-test/mtp for more information. +Consulte https://aka.ms/dotnet-test/mtp para obter mais informações. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + erro Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. Descobrindo testes de - - .NET Test Command - Comando de Teste do .NET - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + As versões de protocolo suportadas enviadas pelo Microsoft.Testing.Platform são '{0}'. O SDK oferece suporte a '{1}', que é incompatível. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + Recebido 'ExecutionId' com valor '{0}' para a mensagem '{1}', enquanto o 'ExecutionId' recebido na mensagem de handshake era '{2}'. {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + Erro ao descartar 'NamedPipeServer' correspondente ao handshake: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + Erro ao descartar 'NamedPipeServer', e nenhum handshake foi encontrado. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + O 'dotnet' recebeu inesperadamente menos de 4 bytes do pipe nomeado 'dotnet test'. 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + O 'dotnet' recebeu inesperadamente mensagens sobrepostas do pipe nomeado 'dotnet test'. @@ -1228,14 +1233,24 @@ Diferencie os nomes dos perfis. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + Diretivas duplicadas não são suportadas:{0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + A seguinte exceção ocorreu ao executar o módulo de teste com RunCommand '{0}' e RunArguments '{1}': {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Diferencie os nomes dos perfis. Falha ao atualizar o manifesto de publicidade {0}: {1}. + + failed: + failed: + + failed com falha @@ -1568,12 +1588,12 @@ Diferencie os nomes dos perfis. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + O método '{0}' não foi concluído com sucesso The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + A diretiva deve conter um nome sem caracteres especiais e um valor opcional separado por '{1}' como '#:{0} Nome{1}Valor'. {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Diferencie os nomes dos perfis. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + A diretiva '#:project' é inválida:{0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ A ferramenta '{1}' (versão '{2}') foi instalada com êxito. A entrada foi adici A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + Um novo handshake '{0}' foi recebido para o aplicativo de teste que não corresponde ao handshake anterior '{1}'. Missing name of '{0}'. - Missing name of '{0}'. + Nome de '{0}' ausente. {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + Um evento de início de sessão de teste foi recebido sem um término de sessão de teste correspondente. @@ -2502,12 +2522,12 @@ A ferramenta '{1}' (versão '{2}') foi instalada com êxito. A entrada foi adici Invalid property name: {0} - Invalid property name: {0} + Nome de propriedade inválido: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + A diretiva de propriedade precisa ter duas partes separadas por '=' como '#:property PropertyName=PropertyValue'. {Locked="#:property"} @@ -2672,6 +2692,11 @@ O padrão é publicar uma aplicação dependente de framework. A ferramenta '{0}' (versão '{1}') foi restaurada. Comandos disponíveis: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). Role para frente para a versão de estrutura (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). @@ -2951,6 +2976,11 @@ Ele tem diversas estruturas como destino. Especifique que estrutura executar usa Ignorar a criação de arquivos de símbolo que podem ser usados para criar o perfil de assemblies otimizados. + + skipped: + skipped: + + skipped ignorado @@ -3048,7 +3078,7 @@ Ele tem diversas estruturas como destino. Especifique que estrutura executar usa Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + A restauração de grafo estático não é suportada para aplicativos baseados em arquivos. Remova '#:property'. {Locked="#:property"} @@ -3086,6 +3116,11 @@ Ele tem diversas estruturas como destino. Especifique que estrutura executar usa O runtime de destino no qual os pacotes serão armazenados. + + succeeded: + succeeded: + + Summary Resumo @@ -3101,11 +3136,6 @@ Ele tem diversas estruturas como destino. Especifique que estrutura executar usa Especifique um diretório temporário para este comando baixar e extrair pacotes NuGet (deve ser seguro). - - .NET Test Driver - Driver de Teste do .NET - - + + total: + total: + + try {0} experimente {0} @@ -3564,22 +3599,22 @@ e as Ids de pacote correspondentes para as ferramentas instaladas usando o coman An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + Uma mensagem inesperada relacionada à ajuda foi recebida quando `--help` não foi usado. A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + Uma mensagem do tipo '{0}' foi recebida no modo de ajuda, o que não era esperado. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + Uma mensagem do tipo '{0}' foi recebida antes de qualquer handshake, o que é inesperado. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + Um evento de término de sessão de teste foi recebido sem um início de sessão de teste correspondente. @@ -3599,7 +3634,7 @@ e as Ids de pacote correspondentes para as ferramentas instaladas usando o coman Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + Diretiva não reconhecida '{0}'. {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf index 05edfdd693ef..d0d812cd6a2a 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf @@ -186,7 +186,7 @@ Paths searched: '{1}', '{2}'. Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + Некоторые директивы невозможно преобразовать. Запустите файл, чтобы увидеть все ошибки компиляции. Укажите параметр "--force", чтобы выполнить преобразование, невзирая на ошибки. {Locked="--force"} @@ -585,7 +585,7 @@ This is equivalent to deleting project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Указывает минимальное число тестов, которые должны быть запущены. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + В global.json в качестве средства выполнения тестов указано Microsoft.Testing.Platform. Все проекты должны использовать это средство выполнения тестов. +Следующие тестовые проекты используют средство выполнения тестов VSTest: {0} -See https://aka.ms/dotnet-test/mtp for more information. +Дополнительные сведения см. на странице https://aka.ms/dotnet-test/mtp. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + ошибка Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. Обнаружение тестов из - - .NET Test Command - Команда .NET Test - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Поддерживаемые версии протокола, отправленные при помощи Microsoft.Testing.Platform: "{0}". SDK поддерживает "{1}", что несовместимо. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + Получено "ExecutionId" со значением "{0}" для сообщения "{1}", тогда как "ExecutionId" в сообщении подтверждения был "{2}". {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + Ошибка при удалении "NamedPipeServer", соответствующего подтверждению: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + Ошибка при удалении "NamedPipeServer", подтверждение не найдено. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + "dotnet" неожиданно получил менее 4 байт из именованного канала "dotnet test". 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + "dotnet" неожиданно получил перекрывающиеся сообщения из именованного канала "dotnet test". @@ -1228,14 +1233,24 @@ Make the profile names distinct. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + Повторяющиеся директивы не поддерживаются: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + При выполнении тестового модуля с RunCommand "{0}" и RunArguments "{1}" возникло следующее исключение: {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Make the profile names distinct. Не удалось обновить манифест рекламы {0}: {1}. + + failed: + failed: + + failed сбой @@ -1568,12 +1588,12 @@ Make the profile names distinct. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + Сбой выхода метода "{0}" The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + Директива должна содержать имя без специальных символов и необязательное значение, разделенные символом-разделителем "{1}", например "#:{0} Имя{1}Значение". {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Make the profile names distinct. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + Недопустимая директива "#:project": {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + Получено новое подтверждение "{0}" для тестового приложения, которое не совпадает с предыдущим подтверждением "{1}". Missing name of '{0}'. - Missing name of '{0}'. + Отсутствует имя "{0}". {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + Получено событие запуска тестового сеанса без соответствующего завершения тестового сеанса. @@ -2502,12 +2522,12 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man Invalid property name: {0} - Invalid property name: {0} + Недопустимое имя свойства: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + Директива свойства должна иметь две части, разделенные символом "=", например "#:property PropertyName=PropertyValue". {Locked="#:property"} @@ -2672,6 +2692,11 @@ The default is to publish a framework-dependent application. Средство "{0}" (версия "{1}") было восстановлено. Доступные команды: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). Накат до версии платформы (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). @@ -2951,6 +2976,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' Пропуск создания файлов символов, которые можно использовать для профилирования оптимизированных сборок. + + skipped: + skipped: + + skipped пропущено @@ -3048,7 +3078,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + Восстановление статического графа не поддерживается для приложений на основе файлов. Удалите "#:property". {Locked="#:property"} @@ -3086,6 +3116,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' Целевая среда выполнения, для которой хранятся пакеты. + + succeeded: + succeeded: + + Summary Сводка @@ -3101,11 +3136,6 @@ Your project targets multiple frameworks. Specify which framework to run using ' Укажите временный каталог для этой команды, чтобы скачать и извлечь пакеты NuGet (должны быть защищены). - - .NET Test Driver - Драйвер тестов .NET - - + + total: + total: + + try {0} попробуйте {0} @@ -3565,22 +3600,22 @@ and the corresponding package Ids for installed tools using the command An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + Получено неожиданное сообщение, связанное со справкой, без использования параметра "--help". A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + В режиме справки получено сообщение типа "{0}", что является неожиданным. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + Сообщение типа "{0}" получено до получения каких-либо подтверждений, что является неожиданным. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + Получено событие завершения тестового сеанса без соответствующего запуска тестового сеанса. @@ -3600,7 +3635,7 @@ and the corresponding package Ids for installed tools using the command Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + Нераспознанная директива "{0}". {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf index 228d3a11bd02..ad0adf441453 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf @@ -186,7 +186,7 @@ Aranan yollar: '{1}', '{2}'. Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + Bazı yönergeler dönüştürülemez. Tüm derleme hatalarını görmek için dosyayı çalıştırın. Yine de dönüştürmek için '--force' belirtin. {Locked="--force"} @@ -585,7 +585,7 @@ project.assets.json öğesini silmeyle eşdeğerdir. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + Çalıştırılması beklenen en düşük test sayısını belirtir. @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json, test çalıştırıcısını Microsoft.Testing.Platform olarak tanımlar. Tüm projelerin bu test çalıştırıcısını kullanması gereklidir. +Aşağıdaki test projeleri VSTest test çalıştırıcısını kullanıyor: {0} -See https://aka.ms/dotnet-test/mtp for more information. +Daha fazla bilgi için https://aka.ms/dotnet-test/mtp adresine bakın. {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + hata Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. Testler şuradan bulunuyor: - - .NET Test Command - .NET Test Komutu - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Microsoft.Testing.Platform tarafından gönderilen desteklenen protokol sürümleri: '{0}'. SDK, uyumsuz olan '{1}' öğesini desteklemektedir. Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + El sıkışma mesajının aldığı ‘ExecutionId’ değeri '{2}' iken, '{1}' mesajı için '{0}' değerinde ‘ExecutionId’ alındı. {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + El sıkışma ile ilgili 'NamedPipeServer'ın atımında hata oluştu: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + 'NamedPipeServer'ın atımında hata oluştu ve el sıkışma bulunamadı. 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + ‘dotnet’, ‘dotnet test’ adlandırılmış kanaldan beklenmedik bir şekilde 4 bayttan az veri aldı. 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + ‘dotnet’, ‘dotnet test’ adlandırılmış kanaldan beklenmedik bir şekilde çakışan mesajlar aldı. @@ -1228,14 +1233,24 @@ Lütfen profil adlarını değiştirin. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + Yinelenen yönergeler desteklenmez: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + RunCommand '{0}' ve RunArguments '{1}' ile test modülünü çalıştırırken aşağıdaki istisna oluştu: {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Lütfen profil adlarını değiştirin. Reklam bildirimi {0} güncelleştirilemedi: {1}. + + failed: + failed: + + failed başarısız @@ -1568,12 +1588,12 @@ Lütfen profil adlarını değiştirin. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + '{0}' yöntemi başarıyla çıkmadı The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + Yönerge, özel karakterler içermeyen bir ad ve ‘#:{0} Ad{1}Değer’ gibi '{1}' ile ayrılmış isteğe bağlı bir değer içermelidir. {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Lütfen profil adlarını değiştirin. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + ‘#:project’ yönergesi geçersizdir: {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + Test uygulaması için önceki el sıkışma '{1}' ile eşleşmeyen yeni bir el sıkışma '{0}' alındı. Missing name of '{0}'. - Missing name of '{0}'. + '{0}' adı eksik. {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + Karşılık gelen test oturumu sonu olmadan bir test oturumu başlangıç olayı alındı. @@ -2502,12 +2522,12 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man Invalid property name: {0} - Invalid property name: {0} + Geçersiz özellik adı: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + Özellik yönergesi, ‘#:property PropertyName=PropertyValue’ gibi ‘=’ ile ayrılmış iki bölümden oluşmalıdır. {Locked="#:property"} @@ -2672,6 +2692,11 @@ Varsayılan durum, çerçeveye bağımlı bir uygulama yayımlamaktır. '{0}' aracı (sürüm '{1}') geri yüklendi. Kullanılabilen komutlar: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). Şu framework sürümüne ileri sarın: (LatestPatch, İkincil, LatestMinor, Ana, LatestMajor, Devre dışı). @@ -2951,6 +2976,11 @@ Projeniz birden fazla Framework'ü hedefliyor. '{0}' kullanarak hangi Framework' İyileştirilen bütünleştirilmiş kodların profilini oluşturmak için kullanılabilen sembol dosyalarını oluşturma işlemini atlar. + + skipped: + skipped: + + skipped atlandı @@ -3048,7 +3078,7 @@ Projeniz birden fazla Framework'ü hedefliyor. '{0}' kullanarak hangi Framework' Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + Dosya tabanlı uygulamalar için statik grafik geri yükleme desteklenmemektedir. ‘#:property’i kaldırın. {Locked="#:property"} @@ -3086,6 +3116,11 @@ Projeniz birden fazla Framework'ü hedefliyor. '{0}' kullanarak hangi Framework' Paketlerin geri yükleneceği hedef çalışma zamanı. + + succeeded: + succeeded: + + Summary Özet @@ -3101,11 +3136,6 @@ Projeniz birden fazla Framework'ü hedefliyor. '{0}' kullanarak hangi Framework' Bu komut için NuGet paketlerini indirmek ve ayıklamak üzere geçici bir dizin belirtin (güvenli olmalıdır). - - .NET Test Driver - .NET Test Sürücüsü - - + + total: + total: + + try {0} Şunu deneyin: {0} @@ -3564,22 +3599,22 @@ karşılık gelen paket kimliklerini bulmak için An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + `--help` kullanılmadığında beklenmedik bir yardım mesajı alındı. A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + Yardım modunda '{0}' türünde bir mesaj alındı, bu beklenmeyen bir durumdur. A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + Herhangi bir el sıkışma almadan önce '{0}' türünde bir mesaj alındı, bu beklenmedik bir durumdur. A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + Karşılık gelen test oturumu başlangıcı olmadan bir test oturumu bitiş olayı alındı. @@ -3599,7 +3634,7 @@ karşılık gelen paket kimliklerini bulmak için Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + Tanınmayan yönerge '{0}'. {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf index 8dce87da752d..64238e4c2f31 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf @@ -186,7 +186,7 @@ Paths searched: '{1}', '{2}'. Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + 一些指令无法转换。运行该文件以查看所有编译错误。请指定 '--force' 以进行转换。 {Locked="--force"} @@ -585,7 +585,7 @@ This is equivalent to deleting project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + 指定预期运行的最小测试数。 @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json 定义将测试运行器定义为 Microsoft.Testing.Platform。所有项目都必须使用该测试运行器。 +以下测试项目正在使用 VSTest 测试运行器: {0} -See https://aka.ms/dotnet-test/mtp for more information. +有关详细信息,请参阅 https://aka.ms/dotnet-test/mtp。 {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + 错误 Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. 正在发现以下位置中的测试 - - .NET Test Command - .NET 测试命令 - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Microsoft.Testing.Platform 发送的受支持的协议版本为 ‘{0}’。SDK 支持 ‘{1}’,这是不兼容的。 Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + 收到了消息 ‘{1}’ 的值为 ‘{0}’ 的 ‘ExecutionId’,而 ‘ExecutionId’ 收到的握手消息为 ‘{2}’。 {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + 释放与握手对应的 'NamedPipeServer' 时出错: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + 释放 'NamedPipeServer' 时出错,未找到握手。 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + 'dotnet' 意外从 'dotnet test' 命名管道接收了不到 4 个字节。 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + 'dotnet' 意外从 'dotnet test' 命名管道接收了重叠的消息。 @@ -1228,14 +1233,24 @@ Make the profile names distinct. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + 不支持重复指令: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + 使用 RunCommand ‘{0}’ 和 RunArguments ‘{1}’ 运行测试模块时发生以下异常: {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Make the profile names distinct. 未能更新广告清单 {0}: {1}。 + + failed: + failed: + + failed 失败 @@ -1568,12 +1588,12 @@ Make the profile names distinct. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + 方法“{0}”未成功退出 The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + 该指令应包含一个不带特殊字符的名称,以及一个以 '#:{0} Name{1}Value' 等 ‘{1}’ 分隔的可选值。 {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Make the profile names distinct. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + '#:project' 指令无效: {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + 测试应用程序收到了新的握手 ‘{0}’,它与以前的握手 ‘{1}’ 不匹配。 Missing name of '{0}'. - Missing name of '{0}'. + 缺少 '{0}' 的名称。 {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + 在没有相应的测试会话结束的情况下收到了测试会话开始事件。 @@ -2502,12 +2522,12 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man Invalid property name: {0} - Invalid property name: {0} + 属性名无效: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + 属性指令需要包含两个由 ‘=’ 分隔的部件,例如 '#:property PropertyName=PropertyValue'。 {Locked="#:property"} @@ -2672,6 +2692,11 @@ The default is to publish a framework-dependent application. 工具“{0}”(版本“{1}”)已还原。可用的命令: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). 前滚至框架版本(LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable)。 @@ -2951,6 +2976,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' 跳过符号文件的创建操作,这些文件可用于分析已优化的程序集。 + + skipped: + skipped: + + skipped 已跳过 @@ -3048,7 +3078,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + 基于文件的应用不支持静态图形还原。移除 '#:property'。 {Locked="#:property"} @@ -3086,6 +3116,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' 要存储包的目标运行时。 + + succeeded: + succeeded: + + Summary 摘要 @@ -3101,11 +3136,6 @@ Your project targets multiple frameworks. Specify which framework to run using ' 为此命令指定一个临时目录,以下载并提取(必须安全)的 NuGet 包。 - - .NET Test Driver - .NET 测试驱动程序 - - + + total: + total: + + try {0} 尝试 {0} @@ -3564,22 +3599,22 @@ and the corresponding package Ids for installed tools using the command An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + 未使用 `--help` 时收到了与帮助相关的意外消息。 A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + 在帮助模式下收到类型为 ‘{0}’ 的消息,这是预期的事件。 A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + 在我们收到任何握手之前已收到类型为 ‘{0}’ 的消息,这是意外的行为。 A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + 在没有相应的测试会话启动的情况下收到了测试会话结束事件。 @@ -3599,7 +3634,7 @@ and the corresponding package Ids for installed tools using the command Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + 无法识别的指令 ‘{0}’。 {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf index 4402fa7bc229..4578feba8eff 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf @@ -186,7 +186,7 @@ Paths searched: '{1}', '{2}'. Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. - Some directives cannot be converted. Run the file to see all compilation errors. Specify '--force' to convert anyway. + 無法轉換某些指示詞。執行檔案以查看所有編譯錯誤。指定 '--force' 以繼續轉換。 {Locked="--force"} @@ -585,7 +585,7 @@ This is equivalent to deleting project.assets.json. Specifies the minimum number of tests that are expected to run. - Specifies the minimum number of tests that are expected to run. + 指定預期執行的測試數目下限。 @@ -883,11 +883,11 @@ The following test projects are using VSTest test runner: {0} See https://aka.ms/dotnet-test/mtp for more information. - global.json defines test runner to be Microsoft.Testing.Platform. All projects must use that test runner. -The following test projects are using VSTest test runner: + global.json 定義測試執行器為 Microsoft.Testing.Platform。所有專案必須使用該測試執行器。 +以下測試專案正在使用 VSTest 測試執行器: {0} -See https://aka.ms/dotnet-test/mtp for more information. +如需詳細資訊,請參閱 https://aka.ms/dotnet-test/mtp。 {Locked="global.json"}{Locked="Microsoft.Testing.Platform"} {0} is one or more project names. @@ -1128,7 +1128,7 @@ See https://aka.ms/dotnet-test/mtp for more information. error - error + 錯誤 Used when reporting directive errors like "file(location): error: message". @@ -1162,39 +1162,44 @@ See https://aka.ms/dotnet-test/mtp for more information. 正在以下位置找測試 - - .NET Test Command - .NET 測試命令 - + + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for Microsoft.Testing.Platform (opted-in via 'global.json' file). This only supports Microsoft.Testing.Platform and doesn't support VSTest. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} + + + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + .NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test. + {Locked="global.json"}{Locked="Microsoft.Testing.Platform"}{Locked="VSTest"} Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. - Supported protocol versions sent by Microsoft.Testing.Platform are '{0}'. The SDK supports '{1}', which is incompatible. + Microsoft.Testing.Platform 所支援的通訊協定版本為 '{0}'。此 SDK 支援 '{1}',但兩者不相容。 Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. - Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'. + 收到訊息 '{1}' 的 'ExecutionId' 值為 '{0}',但交握訊息收到的 'ExecutionId' 為 '{2}'。 {Locked="ExecutionId"} Error disposing 'NamedPipeServer' corresponding to handshake: - Error disposing 'NamedPipeServer' corresponding to handshake: + 處理與交握對應的 'NamedPipeServer' 時發生錯誤: Error disposing 'NamedPipeServer', and no handshake was found. - Error disposing 'NamedPipeServer', and no handshake was found. + 處理 'NamedPipeServer' 時發生錯誤,且找不到交握。 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received less than 4 bytes from the 'dotnet test' named pipe. + 'dotnet' 從 'dotnet test' 具名管道意外接收到的資料少於 4 個位元組。 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. - 'dotnet' unexpectedly received overlapping messages from the 'dotnet test' named pipe. + 'dotnet' 從 'dotnet test' 具名管道意外接收到重疊訊息。 @@ -1228,14 +1233,24 @@ Make the profile names distinct. Duplicate directives are not supported: {0} - Duplicate directives are not supported: {0} + 不支援重複的指示詞: {0} {0} is the directive type and name. + + duration: + duration: + + + + error: + error: + + The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': {2} - The following exception occurred when running the test module with RunCommand '{0}' and RunArguments '{1}': + 使用 RunCommand '{0}' 和 RunArguments '{1}' 執行測試模組時,發生以下例外狀況: {2} {Locked="RunCommand"}{Locked="RunArguments"} @@ -1281,6 +1296,11 @@ Make the profile names distinct. 無法更新廣告資訊清單 {0}: {1}。 + + failed: + failed: + + failed 已失敗 @@ -1568,12 +1588,12 @@ Make the profile names distinct. Method '{0}' did not exit successfully - Method '{0}' did not exit successfully + 方法 '{0}' 未成功結束 The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. - The directive should contain a name without special characters and an optional value separated by '{1}' like '#:{0} Name{1}Value'. + 指示詞應包含不含特殊字元的名稱,以及 '{1}' 分隔的選用值,例如 '#:{0} Name{1}Value'。 {0} is the directive type like 'package' or 'sdk'. {1} is the expected separator like '@' or '='. @@ -1593,7 +1613,7 @@ Make the profile names distinct. The '#:project' directive is invalid: {0} - The '#:project' directive is invalid: {0} + '#:project' 指示詞無效: {0} {0} is the inner error message. @@ -1730,17 +1750,17 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. - A new handshake '{0}' was received for the test application that doesn't match the previous handshake '{1}'. + 測試應用程式收到新的交握 '{0}',與先前的交握 '{1}' 不相符。 Missing name of '{0}'. - Missing name of '{0}'. + 缺少 '{0}' 的名稱。 {0} is the directive name like 'package' or 'sdk'. A test session start event was received without a corresponding test session end. - A test session start event was received without a corresponding test session end. + 收到測試工作階段開始事件,但沒有對應的測試工作階段結束。 @@ -2502,12 +2522,12 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man Invalid property name: {0} - Invalid property name: {0} + 屬性名稱無效: {0} {0} is an inner exception message. The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. - The property directive needs to have two parts separated by '=' like '#:property PropertyName=PropertyValue'. + 屬性指示詞必須有兩個部分,其以 '=' 分隔,例如 '#:property PropertyName=PropertyValue'。 {Locked="#:property"} @@ -2672,6 +2692,11 @@ The default is to publish a framework-dependent application. 已還原工具 '{0}' (版本 '{1}')。可用的命令: {2} + + retried + retried + + Roll forward to framework version (LatestPatch, Minor, LatestMinor, Major, LatestMajor, Disable). 復原為架構版本 (LatestPatch、Minor、LatestMinor、Major、LatestMajor、Disable)。 @@ -2951,6 +2976,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' 跳過建立符號檔,該檔案可用於分析最佳化組件。 + + skipped: + skipped: + + skipped 已跳過 @@ -3048,7 +3078,7 @@ Your project targets multiple frameworks. Specify which framework to run using ' Static graph restore is not supported for file-based apps. Remove the '#:property'. - Static graph restore is not supported for file-based apps. Remove the '#:property'. + 檔案型應用程式不支援靜態圖表還原。移除 ''#:property'。 {Locked="#:property"} @@ -3086,6 +3116,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' 要對其儲存套件的目標執行階段。 + + succeeded: + succeeded: + + Summary 摘要 @@ -3101,11 +3136,6 @@ Your project targets multiple frameworks. Specify which framework to run using ' 指定此命令的暫存目錄,以下載並解壓縮 NuGet 套件 (必須為安全)。 - - .NET Test Driver - .NET 測試驅動程式 - - + + total: + total: + + try {0} 嘗試 {0} @@ -3564,22 +3599,22 @@ and the corresponding package Ids for installed tools using the command An unexpected help-related message was received when `--help` wasn't used. - An unexpected help-related message was received when `--help` wasn't used. + 未使用 `--help` 時,收到未預期的說明相關訊息。 A message of type '{0}' was received in help mode, which is not expected. - A message of type '{0}' was received in help mode, which is not expected. + 在說明模式中收到類型為 '{0}' 的訊息,這是不預期的。 A message of type '{0}' was received before we got any handshakes, which is unexpected. - A message of type '{0}' was received before we got any handshakes, which is unexpected. + 在收到任何交握之前,我們就接收到類型為 '{0}' 的訊息,這是不預期的情況。 A test session end event was received without a corresponding test session start. - A test session end event was received without a corresponding test session start. + 收到測試工作階段結束事件,但沒有對應的測試工作階段開始。 @@ -3599,7 +3634,7 @@ and the corresponding package Ids for installed tools using the command Unrecognized directive '{0}'. - Unrecognized directive '{0}'. + 無法識別的指示詞 '{0}'。 {0} is the directive name like 'package' or 'sdk'. diff --git a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs index 8ac89af40212..a311e88c646d 100644 --- a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs +++ b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs @@ -4,9 +4,11 @@ #nullable disable using System.Collections.Concurrent; +using Microsoft.DotNet.Cli.Extensions; using Microsoft.DotNet.Cli.NugetPackageDownloader; using Microsoft.DotNet.Cli.ToolPackage; using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Cli.Utils.Extensions; using Microsoft.Extensions.EnvironmentAbstractions; using NuGet.Common; using NuGet.Configuration; @@ -450,9 +452,21 @@ public IEnumerable LoadNuGetSources(PackageId packageId, PackageS throw new NuGetPackageInstallerException("No NuGet sources are defined or enabled"); } + CheckHttpSources(sources); return sources; } + private void CheckHttpSources(IEnumerable packageSources) + { + foreach (var packageSource in packageSources) + { + if (packageSource.IsHttp && !packageSource.IsHttps && !packageSource.AllowInsecureConnections) + { + throw new NuGetPackageInstallerException(string.Format(CliStrings.Error_NU1302_HttpSourceUsed, packageSource.Source)); + } + } + } + private async Task<(PackageSource, IPackageSearchMetadata)> GetMatchingVersionInternalAsync( string packageIdentifier, IEnumerable packageSources, VersionRange versionRange, CancellationToken cancellationToken) diff --git a/src/Cli/dotnet/Program.cs b/src/Cli/dotnet/Program.cs index cd82a15330f4..e57fe3a5635f 100644 --- a/src/Cli/dotnet/Program.cs +++ b/src/Cli/dotnet/Program.cs @@ -6,6 +6,7 @@ using System.CommandLine; using System.CommandLine.Parsing; using System.Diagnostics; +using System.Runtime.InteropServices; using Microsoft.DotNet.Cli.CommandFactory; using Microsoft.DotNet.Cli.CommandFactory.CommandResolution; using Microsoft.DotNet.Cli.Commands.Run; @@ -29,6 +30,10 @@ public class Program public static ITelemetry TelemetryClient; public static int Main(string[] args) { + // Register a handler for SIGTERM to allow graceful shutdown of the application on Unix. + // See https://github.com/dotnet/docs/issues/46226. + using var termSignalRegistration = PosixSignalRegistration.Create(PosixSignal.SIGTERM, _ => Environment.Exit(0)); + using AutomaticEncodingRestorer _ = new(); // Setting output encoding is not available on those platforms @@ -235,10 +240,7 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime) if (TelemetryClient.Enabled) { // Get the global.json state to report in telemetry along with this command invocation. - // We don't care about the actual SDK resolution, just the global.json information, - // so just pass empty string as executable directory for resolution. - NativeWrapper.SdkResolutionResult result = NativeWrapper.NETCoreSdkResolverNativeWrapper.ResolveSdk(string.Empty, Environment.CurrentDirectory); - globalJsonState = result.GlobalJsonState; + globalJsonState = NativeWrapper.NETCoreSdkResolverNativeWrapper.GetGlobalJsonState(Environment.CurrentDirectory); } TelemetryEventEntry.SendFiltered(Tuple.Create(parseResult, performanceData, globalJsonState)); diff --git a/src/Cli/dotnet/Telemetry/EnvironmentDetectionRule.cs b/src/Cli/dotnet/Telemetry/EnvironmentDetectionRule.cs index 5cd73f53abb8..ebdf6321ddd7 100644 --- a/src/Cli/dotnet/Telemetry/EnvironmentDetectionRule.cs +++ b/src/Cli/dotnet/Telemetry/EnvironmentDetectionRule.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli.Telemetry; @@ -33,8 +34,7 @@ public BooleanEnvironmentRule(params string[] variables) public override bool IsMatch() { - return _variables.Any(variable => - bool.TryParse(Environment.GetEnvironmentVariable(variable), out bool value) && value); + return _variables.Any(variable => Env.GetEnvironmentVariableAsBool(variable)); } } @@ -81,12 +81,12 @@ public override bool IsMatch() /// The type of the result value. internal class EnvironmentDetectionRuleWithResult where T : class { - private readonly string[] _variables; + private readonly EnvironmentDetectionRule _rule; private readonly T _result; - public EnvironmentDetectionRuleWithResult(T result, params string[] variables) + public EnvironmentDetectionRuleWithResult(T result, EnvironmentDetectionRule rule) { - _variables = variables ?? throw new ArgumentNullException(nameof(variables)); + _rule = rule ?? throw new ArgumentNullException(nameof(rule)); _result = result ?? throw new ArgumentNullException(nameof(result)); } @@ -96,8 +96,8 @@ public EnvironmentDetectionRuleWithResult(T result, params string[] variables) /// The result value if the rule matches; otherwise, null. public T? GetResult() { - return _variables.Any(variable => !string.IsNullOrEmpty(Environment.GetEnvironmentVariable(variable))) - ? _result + return _rule.IsMatch() + ? _result : null; } -} \ No newline at end of file +} diff --git a/src/Cli/dotnet/Telemetry/ILLMEnvironmentDetector.cs b/src/Cli/dotnet/Telemetry/ILLMEnvironmentDetector.cs index fe599569aa6c..e2ee21591567 100644 --- a/src/Cli/dotnet/Telemetry/ILLMEnvironmentDetector.cs +++ b/src/Cli/dotnet/Telemetry/ILLMEnvironmentDetector.cs @@ -5,5 +5,13 @@ namespace Microsoft.DotNet.Cli.Telemetry; internal interface ILLMEnvironmentDetector { + /// + /// Checks the current environment for known indicators of LLM usage and returns a string identifying the LLM environment if detected. + /// string? GetLLMEnvironment(); -} \ No newline at end of file + + /// + /// Returns true if the current environment is detected to be an LLM/agentic environment, false otherwise. + /// + bool IsLLMEnvironment(); +} diff --git a/src/Cli/dotnet/Telemetry/LLMEnvironmentDetectorForTelemetry.cs b/src/Cli/dotnet/Telemetry/LLMEnvironmentDetectorForTelemetry.cs index 16d13a6879e7..b37f9b5d0830 100644 --- a/src/Cli/dotnet/Telemetry/LLMEnvironmentDetectorForTelemetry.cs +++ b/src/Cli/dotnet/Telemetry/LLMEnvironmentDetectorForTelemetry.cs @@ -1,23 +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 System; -using System.Linq; - namespace Microsoft.DotNet.Cli.Telemetry; internal class LLMEnvironmentDetectorForTelemetry : ILLMEnvironmentDetector { private static readonly EnvironmentDetectionRuleWithResult[] _detectionRules = [ // Claude Code - new EnvironmentDetectionRuleWithResult("claude", "CLAUDECODE"), + new EnvironmentDetectionRuleWithResult("claude", new AnyPresentEnvironmentRule("CLAUDECODE")), // Cursor AI - new EnvironmentDetectionRuleWithResult("cursor", "CURSOR_EDITOR") + new EnvironmentDetectionRuleWithResult("cursor", new AnyPresentEnvironmentRule("CURSOR_EDITOR")), + // Gemini + new EnvironmentDetectionRuleWithResult("gemini", new BooleanEnvironmentRule("GEMINI_CLI")), + // GitHub Copilot + new EnvironmentDetectionRuleWithResult("copilot", new BooleanEnvironmentRule("GITHUB_COPILOT_CLI_MODE")), + // (proposed) generic flag for Agentic usage + new EnvironmentDetectionRuleWithResult("generic_agent", new BooleanEnvironmentRule("AGENT_CLI")), ]; + /// public string? GetLLMEnvironment() { var results = _detectionRules.Select(r => r.GetResult()).Where(r => r != null).ToArray(); return results.Length > 0 ? string.Join(", ", results) : null; } -} \ No newline at end of file + + /// + public bool IsLLMEnvironment() => !string.IsNullOrEmpty(GetLLMEnvironment()); +} diff --git a/src/Cli/dotnet/xlf/CliStrings.cs.xlf b/src/Cli/dotnet/xlf/CliStrings.cs.xlf index bc8118692866..0524396d0b25 100644 --- a/src/Cli/dotnet/xlf/CliStrings.cs.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.cs.xlf @@ -100,16 +100,15 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Nastaví hodnotu proměnné prostředí. -Pokud proměnná neexistuje, vytvoří ji, a pokud existuje, přepíše ji. -Tímto se vynutí spuštění testů v izolovaném procesu. + Nastavuje hodnotu proměnné prostředí. +Pokud proměnná neexistuje, vytvoří ji, a pokud existuje, přepíše ji. Tento argument je možné zadat vícekrát a určit tak více proměnných. -Příklady: --e PROMĚNNÁ=abc --e PROMĚNNÁ="hodnota s mezerami" --e PROMĚNNÁ="hodnota;oddělená;pomocí;středníků" --e PROM1=abc -e PROM2=def -e PROM3=ghi +Examples: +-e VARIABLE=abc +-e VARIABLE="hodnota s mezerami" +-e VARIABLE="hodnota;oddělená;pomocí;středníků" +-e VAR1=abc -e VAR2=def -e VAR3=ghi @@ -130,16 +129,16 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + Nastaví hodnotu proměnné prostředí. +Pokud proměnná neexistuje, vytvoří ji, a pokud existuje, přepíše ji. +Tímto se vynutí spuštění testů v izolovaném procesu. +Tento argument je možné zadat vícekrát a určit tak více proměnných. -Examples: --e VARIABLE=abc --e VARIABLE="value with spaces" --e VARIABLE="value;seperated with;semicolons" --e VAR1=abc -e VAR2=def -e VAR3=ghi +Příklady: +-e PROMĚNNÁ=abc +-e PROMĚNNÁ="hodnota s mezerami" +-e PROMĚNNÁ="hodnota;oddělená;pomocí;středníků" +-e PROM1=abc -e PROM2=def -e PROM3=ghi @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" Chyba + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + Spouštíte operaci instalace nástroje se zdrojem HTTP: {0}. NuGet vyžaduje zdroje HTTPS. Pokud chcete použít zdroj HTTP, musíte v souboru NuGet.Config explicitně nastavit možnost allowInsecureConnections na true. Další informace najdete na https://aka.ms/nuget-https-everywhere. + + {0}: expect deps.json at: {1} {0}: Soubor deps.json se očekává v: {1} diff --git a/src/Cli/dotnet/xlf/CliStrings.de.xlf b/src/Cli/dotnet/xlf/CliStrings.de.xlf index 6157c0b591d4..5a9f502f87b8 100644 --- a/src/Cli/dotnet/xlf/CliStrings.de.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.de.xlf @@ -100,9 +100,8 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Legt den Wert einer Umgebungsvariablen fest. + Legt den Wert einer Umgebungsvariablen fest. Erstellt die Variable, wenn Sie nicht vorhanden ist, und setzt sie andernfalls außer Kraft. -Dadurch wird die Ausführung der Tests in einem isolierten Prozess erzwungen. Dieses Argument kann mehrmals angegeben werden, um mehrere Variablen bereitzustellen. Beispiele: @@ -130,15 +129,15 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + Legt den Wert einer Umgebungsvariablen fest. +Erstellt die Variable, wenn Sie nicht vorhanden ist, und setzt sie andernfalls außer Kraft. +Dadurch wird die Ausführung der Tests in einem isolierten Prozess erzwungen. +Dieses Argument kann mehrmals angegeben werden, um mehrere Variablen bereitzustellen. -Examples: +Beispiele: -e VARIABLE=abc --e VARIABLE="value with spaces" --e VARIABLE="value;seperated with;semicolons" +-e VARIABLE="wert mit leerzeichen" +-e VARIABLE="wert;getrennt durch;semikolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" Fehler + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + Sie führen den Toolinstallationsvorgang mit einer HTTP-Quelle aus: {0}. NuGet erfordert HTTPS-Quellen. Um eine HTTP-Quelle zu verwenden, müssen Sie „allowInsecureConnections“ in Ihrer NuGet.Config-Datei explizit auf TRUE festlegen. Weitere Informationen finden Sie unter https://aka.ms/nuget-https-everywhere. + + {0}: expect deps.json at: {1} {0}: "deps.json" vermutet unter: {1} diff --git a/src/Cli/dotnet/xlf/CliStrings.es.xlf b/src/Cli/dotnet/xlf/CliStrings.es.xlf index 932f14aad0a5..f3b9b7979528 100644 --- a/src/Cli/dotnet/xlf/CliStrings.es.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.es.xlf @@ -100,9 +100,8 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Establece el valor de una variable de entorno. + Establece el valor de una variable de entorno. Crea la variable si no existe o la reemplaza en caso de que exista. -Esto forzará la ejecución de las pruebas en un proceso aislado. Este argumento se puede especificar varias veces para proporcionar múltiples variables. Ejemplos: @@ -130,12 +129,12 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + Establece el valor de una variable de entorno. +Crea la variable si no existe o la reemplaza en caso de que exista. +Esto forzará la ejecución de las pruebas en un proceso aislado. +Este argumento se puede especificar varias veces para proporcionar múltiples variables. -Examples: +Ejemplos: -e VARIABLE=abc -e VARIABLE="value with spaces" -e VARIABLE="value;seperated with;semicolons" @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" Error + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + Está ejecutando la operación "tool install" con un origen "HTTP", {0}. NuGet requiere orígenes HTTPS. Para usar un origen HTTP, es necesario establecer explícitamente "allowInsecureConnections" en true en el archivo NuGet.Config. Consulte https://aka.ms/nuget-https-everywhere para obtener más información. + + {0}: expect deps.json at: {1} {0}: se espera deps.json en: {1} diff --git a/src/Cli/dotnet/xlf/CliStrings.fr.xlf b/src/Cli/dotnet/xlf/CliStrings.fr.xlf index c52c236d5ae4..29b5989cff4d 100644 --- a/src/Cli/dotnet/xlf/CliStrings.fr.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.fr.xlf @@ -100,15 +100,14 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Définit la valeur d'une variable d'environnement. -Crée la variable si elle n'existe pas, et la remplace si elle existe. -Cela entraîne l'exécution forcée des tests dans un processus isolé. + Définit la valeur d’une variable d’environnement. +Crée la variable si elle n’existe pas, et la remplace si elle existe. Vous pouvez spécifier cet argument plusieurs fois pour fournir plusieurs variables. Exemples : -e VARIABLE=abc --e VARIABLE="valeur avec des espaces" --e VARIABLE="valeur;séparée;par;des;points;virgules" +-e VARIABLE="value with spaces" +-e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi @@ -130,15 +129,15 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + Définit la valeur d'une variable d'environnement. +Crée la variable si elle n'existe pas, et la remplace si elle existe. +Cela entraîne l'exécution forcée des tests dans un processus isolé. +Vous pouvez spécifier cet argument plusieurs fois pour fournir plusieurs variables. -Examples: +Exemples : -e VARIABLE=abc --e VARIABLE="value with spaces" --e VARIABLE="value;seperated with;semicolons" +-e VARIABLE="valeur avec des espaces" +-e VARIABLE="valeur;séparée;par;des;points;virgules" -e VAR1=abc -e VAR2=def -e VAR3=ghi @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" Erreur + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + Vous exécutez l'opération « installation d’outils » avec une source « HTTP » : {0}. NuGet nécessite des sources HTTPS. Pour utiliser une source HTTP, vous devez définir explicitement « allowInsecureConnections » sur true dans votre fichier NuGet.Config. Reportez-vous à https://aka.ms/nuget-https-everywhere pour plus d’informations. + + {0}: expect deps.json at: {1} {0} : deps.json attendu sur {1} diff --git a/src/Cli/dotnet/xlf/CliStrings.it.xlf b/src/Cli/dotnet/xlf/CliStrings.it.xlf index 7e1e970856f4..5a96c8c38059 100644 --- a/src/Cli/dotnet/xlf/CliStrings.it.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.it.xlf @@ -100,10 +100,9 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Imposta il valore di una variabile di ambiente. + Imposta il valore di una variabile di ambiente. Crea la variabile se non esiste e la sostituisce se esiste. -In questo modo forza l'esecuzione dei test in un processo isolato. -È possibile specificare più volte questo argomento per fornire più variabili. +È possibile specificare più volte l'argomento per fornire più variabili. Esempi: -e VARIABLE=abc @@ -130,15 +129,15 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + Imposta il valore di una variabile di ambiente. +Crea la variabile se non esiste e la sostituisce se esiste. +In questo modo forza l'esecuzione dei test in un processo isolato. +È possibile specificare più volte questo argomento per fornire più variabili. -Examples: +Esempi: -e VARIABLE=abc --e VARIABLE="value with spaces" --e VARIABLE="value;seperated with;semicolons" +-e VARIABLE="valore con spazi" +-e VARIABLE="valore;delimitato da;punti e virgola" -e VAR1=abc -e VAR2=def -e VAR3=ghi @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" Errore + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + L'operazione 'tool install' è in esecuzione con un'origine 'HTTP': {0}. NuGet richiede origini HTTPS. Per usare un’origine HTTP, è necessario impostare in modo esplicito ‘allowInsecureConnections’ su true nel file NuGet.Config. Vedere https://aka.ms/nuget-https-everywhere per altre informazioni. + + {0}: expect deps.json at: {1} {0}: è previsto deps.json in: {1} diff --git a/src/Cli/dotnet/xlf/CliStrings.ja.xlf b/src/Cli/dotnet/xlf/CliStrings.ja.xlf index 3c65a24afdd1..43d77ade7965 100644 --- a/src/Cli/dotnet/xlf/CliStrings.ja.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.ja.xlf @@ -100,9 +100,8 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - 環境変数の値を設定します。 + 環境変数の値を設定します。 変数が存在しない場合は作成され、存在する場合はオーバーライドされます。 -これにより、テストは強制的に分離プロセスで実行されます。 この引数は、複数の変数を指定するために複数回指定できます。 例: @@ -130,12 +129,12 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + 環境変数の値を設定します。 +変数が存在しない場合は作成され、存在する場合はオーバーライドされます。 +これにより、テストは強制的に分離プロセスで実行されます。 +この引数は、複数の変数を指定するために複数回指定できます。 -Examples: +例: -e VARIABLE=abc -e VARIABLE="value with spaces" -e VARIABLE="value;seperated with;semicolons" @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" エラー + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + 'tool install' 操作を、HTTP ソース {0} を使用して実行しています。NuGet には HTTPS ソースが必要です。HTTP ソースを使用するには、NuGet.Config ファイルで 'allowInsecureConnections' を true に明示的に設定する必要があります。詳しくは、https://aka.ms/nuget-https-everywhere を参照してください。 + + {0}: expect deps.json at: {1} {0}: {1} で deps.json が必要です diff --git a/src/Cli/dotnet/xlf/CliStrings.ko.xlf b/src/Cli/dotnet/xlf/CliStrings.ko.xlf index 5e9908f47783..235a6c1757ff 100644 --- a/src/Cli/dotnet/xlf/CliStrings.ko.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.ko.xlf @@ -100,9 +100,8 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - 환경 변수의 값을 설정합니다. + 환경 변수의 값을 설정합니다. 변수가 없는 경우 변수를 만들고, 변수가 있으면 재정의합니다. -이는 테스트가 격리된 프로세스에서 강제로 실행되도록 합니다. 이 인수를 여러 번 지정하여 여러 변수를 제공할 수 있습니다. 예: @@ -130,12 +129,12 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + 환경 변수의 값을 설정합니다. +변수가 없는 경우 변수를 만들고, 변수가 있으면 재정의합니다. +이는 테스트가 격리된 프로세스에서 강제로 실행되도록 합니다. +이 인수를 여러 번 지정하여 여러 변수를 제공할 수 있습니다. -Examples: +예: -e VARIABLE=abc -e VARIABLE="value with spaces" -e VARIABLE="value;seperated with;semicolons" @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" 오류 + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + 여러분은 'tool install' 작업을 'HTTP' 원본 {0}(으)로 실행하고 있습니다. NuGet에는 HTTPS 원본이 필요합니다. HTTP 원본을 사용하려면 NuGet.Config 파일에서 'allowInsecureConnections'를 명시적으로 true로 설정해야 합니다. https://aka.ms/nuget-https-everywhere에서 자세한 내용을 참조하세요. + + {0}: expect deps.json at: {1} {0}: {1}에서 deps.json 필요 diff --git a/src/Cli/dotnet/xlf/CliStrings.pl.xlf b/src/Cli/dotnet/xlf/CliStrings.pl.xlf index a0827e6cca19..227655baf402 100644 --- a/src/Cli/dotnet/xlf/CliStrings.pl.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.pl.xlf @@ -100,15 +100,14 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Ustawia wartość zmiennej środowiskowej. + Ustawia wartość zmiennej środowiskowej. Jeśli zmienna nie istnieje, tworzy ją, a jeśli istnieje, przesłania. -Wymusi to uruchamianie testów w izolowanym procesie. Ten argument można określić wiele razy w celu podania wielu wartości. Przykłady: -e ZMIENNA=abc --e ZMIENNA="wartość ze spacjami" --e ZMIENNA="wartości;rozdzielone;średnikami" +-e ZMIENNA=”wartość ze spacjami” +-e ZMIENNA=”wartości;rozdzielone;średnikami” -e ZM1=abc -e ZM2=def -e ZM3=ghi @@ -130,16 +129,16 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + Ustawia wartość zmiennej środowiskowej. +Jeśli zmienna nie istnieje, tworzy ją, a jeśli istnieje, przesłania. +Wymusi to uruchamianie testów w izolowanym procesie. +Ten argument można określić wiele razy w celu podania wielu wartości. -Examples: --e VARIABLE=abc --e VARIABLE="value with spaces" --e VARIABLE="value;seperated with;semicolons" --e VAR1=abc -e VAR2=def -e VAR3=ghi +Przykłady: +-e ZMIENNA=abc +-e ZMIENNA="wartość ze spacjami" +-e ZMIENNA="wartości;rozdzielone;średnikami" +-e ZM1=abc -e ZM2=def -e ZM3=ghi @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" Błąd + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + Operację „instalacji narzędzia” wykonujesz ze źródłem „HTTP”: {0}. Menedżer NuGet wymaga źródeł HTTPS. Aby użyć źródła HTTP, musisz wyraźnie ustawić właściwość „allowInsecureConnections” na wartość true w pliku NuGet.Config. Aby uzyskać więcej informacji, sprawdź witrynę https://aka.ms/nuget-https-everywhere. + + {0}: expect deps.json at: {1} {0}: Oczekiwano pliku deps.json w lokalizacji: {1} diff --git a/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf b/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf index 1427fb9cae3c..15f46bef1c46 100644 --- a/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.pt-BR.xlf @@ -100,10 +100,9 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Define o valor de uma variável de ambiente. -Criará a variável quando ela não existir e a substituirá quando existir. -Isso forçará a execução dos testes em um processo isolado. -Esse argumento pode ser especificado várias vezes para fornecer várias variáveis. + Define o valor de uma variável de ambiente. +Cria a variável se ela não existir, substitui se existir. +Este argumento pode ser especificado várias vezes para fornecer múltiplas variáveis. Exemplos: -e VARIABLE=abc @@ -130,15 +129,15 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + Define o valor de uma variável de ambiente. +Criará a variável quando ela não existir e a substituirá quando existir. +Isso forçará a execução dos testes em um processo isolado. +Esse argumento pode ser especificado várias vezes para fornecer várias variáveis. -Examples: +Exemplos: -e VARIABLE=abc --e VARIABLE="value with spaces" --e VARIABLE="value;seperated with;semicolons" +-e VARIABLE="valor com espaços" +-e VARIABLE="valor;separado com;ponto e vírgula" -e VAR1=abc -e VAR2=def -e VAR3=ghi @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" Erro + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + Você está executando a operação 'tool install' com uma fonte 'HTTP': {0}. O NuGet requer fontes HTTPS. Para usar uma fonte HTTP, você deve definir explicitamente 'allowInsecureConnections' como true no arquivo NuGet.Config. Consulte https://aka.ms/nuget-https-everywhere para mais informações. + + {0}: expect deps.json at: {1} {0}: espera de deps.json em: {1} diff --git a/src/Cli/dotnet/xlf/CliStrings.ru.xlf b/src/Cli/dotnet/xlf/CliStrings.ru.xlf index 16d9ce2ed2a1..70c0242e4e90 100644 --- a/src/Cli/dotnet/xlf/CliStrings.ru.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.ru.xlf @@ -100,10 +100,9 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Устанавливает значение переменной среды. -Если переменной среды не существует, она создается. Если переменная среды существует, она переопределяется. -Этот аргумент подразумевает принудительное выполнение тестов в изолированном процессе. -Этот аргумент может быть указан несколько раз для нескольких переменных среды. + Устанавливает значение переменной среды. +Если переменной среды не существует, она создается, если существует — переопределяется. +Этот аргумент может быть указан несколько раз для указания нескольких переменных. Примеры: -e VARIABLE=abc @@ -130,15 +129,15 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + Устанавливает значение переменной среды. +Если переменной среды не существует, она создается. Если переменная среды существует, она переопределяется. +Этот аргумент подразумевает принудительное выполнение тестов в изолированном процессе. +Этот аргумент может быть указан несколько раз для нескольких переменных среды. -Examples: +Примеры: -e VARIABLE=abc --e VARIABLE="value with spaces" --e VARIABLE="value;seperated with;semicolons" +-e VARIABLE="значение с пробелами" +-e VARIABLE="значение;разделенное;точками;с;запятыми" -e VAR1=abc -e VAR2=def -e VAR3=ghi @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" Ошибка + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + Вы выполняете операцию "установка средства" с источником "HTTP": {0}. Для NuGet требуются источники HTTPS. Чтобы использовать источник HTTP, необходимо явно задать для параметра "allowInsecureConnections" значение true в файле NuGet.Config. Дополнительные сведения см. на странице https://aka.ms/nuget-https-everywhere. + + {0}: expect deps.json at: {1} {0}: ожидается deps.json в: {1}. diff --git a/src/Cli/dotnet/xlf/CliStrings.tr.xlf b/src/Cli/dotnet/xlf/CliStrings.tr.xlf index 4718e0657a61..f7d1a020e295 100644 --- a/src/Cli/dotnet/xlf/CliStrings.tr.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.tr.xlf @@ -100,9 +100,8 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Bir ortam değişkeninin değerini ayarlar. + Bir ortam değişkeninin değerini ayarlar. Değişken yoksa oluşturur, varsa değişkeni geçersiz kılar. -Bu, testleri yalıtılmış bir işlemde çalıştırılmaya zorlar. Bu bağımsız değişken, birden çok değişken sağlamak için birden çok kez belirtilebilir. Örnek: @@ -130,16 +129,16 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + Bir ortam değişkeninin değerini ayarlar. +Değişken yoksa oluşturur, varsa değişkeni geçersiz kılar. +Bu, testleri yalıtılmış bir işlemde çalıştırılmaya zorlar. +Bu bağımsız değişken, birden çok değişken sağlamak için birden çok kez belirtilebilir. -Examples: --e VARIABLE=abc --e VARIABLE="value with spaces" --e VARIABLE="value;seperated with;semicolons" --e VAR1=abc -e VAR2=def -e VAR3=ghi +Örnek: +-e DEĞİŞKEN=abc +-e DEĞİŞKEN="boşluk içeren değerler" +-e DEĞİŞKEN="noktalı virgülle;ayrılmış;değerler" +-e DEĞ1=abc -e DEĞ2=def -e DEĞ3=ghi @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" Hata + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + 'tool install' işlemini {0} 'HTTP' kaynağıyla çalıştırıyorsunuz. NuGet için HTTPS kaynakları gereklidir. Bir HTTP kaynağı kullanmak için NuGet.Config dosyanızda 'allowInsecureConnections' ayarını açıkça true olarak ayarlamanız gerekir. Daha fazla bilgi için şuraya bakın: https://aka.ms/nuget-https-everywhere. + + {0}: expect deps.json at: {1} {0}: şu konumda deps.json bekleniyor: {1} diff --git a/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf b/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf index 1276bb9fea39..9a30c19bbe9a 100644 --- a/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.zh-Hans.xlf @@ -100,9 +100,8 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - 设置环境变量的值。 -如果该变量不存在,则创建它;如果它已存在,则替代它。 -这将在隔离的进程中强制运行测试。 + 设置环境变量的值。 +如果该变量不存在,则创建它; 如果它已存在,则替代它。 可多次指定此参数来提供多个变量。 示例: @@ -130,12 +129,12 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + 设置环境变量的值。 +如果该变量不存在,则创建它;如果它已存在,则替代它。 +这将在隔离的进程中强制运行测试。 +可多次指定此参数来提供多个变量。 -Examples: +示例: -e VARIABLE=abc -e VARIABLE="value with spaces" -e VARIABLE="value;seperated with;semicolons" @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" 错误 + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + 正在通过 "HTTP" 源运行“工具安装”操作: {0}。NuGet 需要 HTTPS 源。要使用 HTTP 源,必须在 NuGet.Config 文件中将 "allowInsecureConnections" 显式设置为 true。有关详细信息,请参阅 https://aka.ms/nuget-https-everywhere。 + + {0}: expect deps.json at: {1} {0}: 需要 deps.json: {1} diff --git a/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf b/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf index bd73674ba27c..61f30b5e7080 100644 --- a/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/xlf/CliStrings.zh-Hant.xlf @@ -100,9 +100,8 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - 設定環境變數的值。 + 設定環境變數的值。 若變數不存在,則加以建立; 若有,則予以覆寫。 -這會強制在隔離流程中執行測試。 此引數可多次指定,以提供多項變數。 範例: @@ -130,12 +129,12 @@ Examples: -e VARIABLE="value;seperated with;semicolons" -e VAR1=abc -e VAR2=def -e VAR3=ghi - Sets the value of an environment variable. -Creates the variable if it does not exist, overrides if it does. -This will force the tests to be run in an isolated process. -This argument can be specified multiple times to provide multiple variables. + 設定環境變數的值。 +若變數不存在,則加以建立; 若有,則予以覆寫。 +這會強制在隔離流程中執行測試。 +此引數可多次指定,以提供多項變數。 -Examples: +範例: -e VARIABLE=abc -e VARIABLE="value with spaces" -e VARIABLE="value;seperated with;semicolons" @@ -373,6 +372,11 @@ setx PATH "%PATH%;{0}" 錯誤 + + You are running the 'tool install' operation with an 'HTTP' source: {0}. NuGet requires HTTPS sources. To use an HTTP source, you must explicitly set 'allowInsecureConnections' to true in your NuGet.Config file. Refer to https://aka.ms/nuget-https-everywhere for more information. + 您正使用 'HTTP' 來源執行 'tool install' 作業: {0}。NuGet 需要 HTTPS 來源。若要使用 HTTP 來源,您必須在 NuGet.Config 檔案中將 'allowInsecureConnections' 明確設定為 true。如需詳細資訊,請參閱 https://aka.ms/nuget-https-everywhere。 + + {0}: expect deps.json at: {1} {0}: 於 {1} 需要 deps.json diff --git a/src/Layout/Directory.Build.props b/src/Layout/Directory.Build.props index 479a7c0a421e..c8bc9ffafc2a 100644 --- a/src/Layout/Directory.Build.props +++ b/src/Layout/Directory.Build.props @@ -78,4 +78,10 @@ $(MSBuildThisFileDirectory)pkg\ + + <_RoslynAppHost Include="$(OutputPath)Roslyn\bincore\csc.dll" /> + <_RoslynAppHost Include="$(OutputPath)Roslyn\bincore\vbc.dll" /> + <_RoslynAppHost Include="$(OutputPath)Roslyn\bincore\VBCSCompiler.dll" /> + + diff --git a/src/Layout/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers.proj b/src/Layout/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers.proj index bb4234adee1d..d0a01f20c9cb 100644 --- a/src/Layout/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers.proj +++ b/src/Layout/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers/VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers.proj @@ -1,4 +1,4 @@ - + net472 @@ -21,6 +21,7 @@ $(ArtifactsNonShippingPackagesDir)VS.Redist.Common.Net.Core.SDK.RuntimeAnalyzers.swr + $(OutputPath)\metadata.json @@ -35,21 +36,51 @@ + - + + %(RecursiveDir) + + + %(RecursiveDir) + + + + + + + + + + + + <_VsixNamespace> + + + + + + - + + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1028/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1028/bundle.wxl index f96c267482d0..96ae1777e649 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1028/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1028/bundle.wxl @@ -75,7 +75,7 @@ - + @@ -87,7 +87,7 @@ - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1029/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1029/bundle.wxl index cd55c114d38d..8996b6dba4d6 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1029/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1029/bundle.wxl @@ -75,7 +75,7 @@ Tento produkt shromažďuje data o využití - + @@ -87,7 +87,7 @@ Tento produkt shromažďuje data o využití - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1031/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1031/bundle.wxl index 05740b73dca1..33bf3763a241 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1031/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1031/bundle.wxl @@ -75,7 +75,7 @@ Dieses Produkt sammelt Verbrauchsdaten - + @@ -87,7 +87,7 @@ Dieses Produkt sammelt Verbrauchsdaten - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1033/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1033/bundle.wxl index 1f4adddecf7c..b3a6c66e37ca 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1033/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1033/bundle.wxl @@ -83,4 +83,4 @@ This product collects usage data - + \ No newline at end of file diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1036/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1036/bundle.wxl index 9db45ab9c73d..ca3e7b265f71 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1036/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1036/bundle.wxl @@ -75,7 +75,7 @@ Ce produit collecte des données d’utilisation - + @@ -87,7 +87,7 @@ Ce produit collecte des données d’utilisation - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1040/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1040/bundle.wxl index 69a763f2b418..aab082f8ac25 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1040/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1040/bundle.wxl @@ -75,7 +75,7 @@ Questo prodotto raccoglie i dati di utilizzo - + @@ -87,7 +87,7 @@ Questo prodotto raccoglie i dati di utilizzo - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1041/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1041/bundle.wxl index 666caa439253..cda559ded662 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1041/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1041/bundle.wxl @@ -75,7 +75,7 @@ - + @@ -87,7 +87,7 @@ - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1042/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1042/bundle.wxl index 4e66c6cfff36..754a45fe3699 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1042/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1042/bundle.wxl @@ -75,7 +75,7 @@ - + @@ -87,7 +87,7 @@ - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1045/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1045/bundle.wxl index 5b4721e4be71..9ab2050a67ea 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1045/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1045/bundle.wxl @@ -75,7 +75,7 @@ Ten produkt zbiera dane użycia - + @@ -87,7 +87,7 @@ Ten produkt zbiera dane użycia - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1046/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1046/bundle.wxl index d16c00be7ad4..f8f517f43958 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1046/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1046/bundle.wxl @@ -75,7 +75,7 @@ Esse produto coleta dados de uso - + @@ -87,7 +87,7 @@ Esse produto coleta dados de uso - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1049/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1049/bundle.wxl index 3e509961ce68..59c961fbdd84 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1049/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1049/bundle.wxl @@ -75,7 +75,7 @@ - + @@ -87,7 +87,7 @@ - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/1055/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/1055/bundle.wxl index f03b83263210..0c25970fd44f 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/1055/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/1055/bundle.wxl @@ -75,7 +75,7 @@ Bu ürün, kullanım verilerini toplar - + @@ -87,7 +87,7 @@ Bu ürün, kullanım verilerini toplar - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/2052/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/2052/bundle.wxl index d0d5105a14dc..4b5b8b92809e 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/2052/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/2052/bundle.wxl @@ -75,7 +75,7 @@ - + @@ -87,7 +87,7 @@ - + diff --git a/src/Layout/pkg/windows/bundles/sdk/LCID/3082/bundle.wxl b/src/Layout/pkg/windows/bundles/sdk/LCID/3082/bundle.wxl index ec87cc1580f2..084aa9dbf0ae 100644 --- a/src/Layout/pkg/windows/bundles/sdk/LCID/3082/bundle.wxl +++ b/src/Layout/pkg/windows/bundles/sdk/LCID/3082/bundle.wxl @@ -75,7 +75,7 @@ Este producto recopila datos de uso - + @@ -87,7 +87,7 @@ Este producto recopila datos de uso - + diff --git a/src/Layout/redist/targets/Crossgen.targets b/src/Layout/redist/targets/Crossgen.targets index e9a22ddb4f8f..3e07f481af11 100644 --- a/src/Layout/redist/targets/Crossgen.targets +++ b/src/Layout/redist/targets/Crossgen.targets @@ -35,7 +35,6 @@ $(InstallerOutputDirectory)Sdks\Microsoft.NET.Sdk\analyzers\ $(InstallerOutputDirectory)Sdks\Microsoft.NET.Sdk\tools\$(DefaultToolTfm)\ $(InstallerOutputDirectory)Sdks\Microsoft.NET.Sdk.BlazorWebAssembly\tools\$(DefaultToolTfm)\ - $(InstallerOutputDirectory)Sdks\NuGet.Build.Tasks.Pack\CoreCLR\ $(InstallerOutputDirectory)Sdks\Microsoft.NET.Sdk.Razor\source-generators\ $(InstallerOutputDirectory)Sdks\Microsoft.NET.Sdk.Razor\tasks\$(DefaultToolTfm)\ $(InstallerOutputDirectory)Sdks\Microsoft.NET.Sdk.WindowsDesktop\tools\$(DefaultToolTfm)\ @@ -74,7 +73,6 @@ - @@ -141,7 +139,6 @@ - @@ -200,6 +197,7 @@ + diff --git a/src/Layout/redist/targets/GenerateLayout.targets b/src/Layout/redist/targets/GenerateLayout.targets index 65d4aff197c6..0b5bcb6fa182 100644 --- a/src/Layout/redist/targets/GenerateLayout.targets +++ b/src/Layout/redist/targets/GenerateLayout.targets @@ -59,6 +59,12 @@ + + @@ -495,6 +501,7 @@ + diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.CSharp.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/CSharpDetectPreviewFeatureAnalyzer.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.CSharp.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/CSharpDetectPreviewFeatureAnalyzer.cs index 849b35e3b4a4..2ab9ff663012 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.CSharp.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/CSharpDetectPreviewFeatureAnalyzer.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.CSharp.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/CSharpDetectPreviewFeatureAnalyzer.cs @@ -2,9 +2,12 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using Analyzer.Utilities.Lightup; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; using Microsoft.NetCore.Analyzers.Runtime; namespace Microsoft.NetCore.CSharp.Analyzers.Runtime @@ -12,6 +15,17 @@ namespace Microsoft.NetCore.CSharp.Analyzers.Runtime [DiagnosticAnalyzer(LanguageNames.CSharp)] public class CSharpDetectPreviewFeatureAnalyzer : DetectPreviewFeatureAnalyzer { + protected override ISymbol? SymbolFromAwaitOperation(IAwaitOperation operation) + { + if (operation.Syntax is not AwaitExpressionSyntax awaitSyntax) + { + return null; + } + + var awaitableInfo = operation.SemanticModel.GetAwaitExpressionInfo(awaitSyntax); + return awaitableInfo.RuntimeAwaitMethod; + } + protected override SyntaxNode? GetPreviewSyntaxNodeForFieldsOrEvents(ISymbol fieldOrEventSymbol, ISymbol previewSymbol) { ImmutableArray fieldOrEventReferences = fieldOrEventSymbol.DeclaringSyntaxReferences; diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/DetectPreviewFeatureAnalyzer.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/DetectPreviewFeatureAnalyzer.cs index 52f86e756313..1df63a6f732d 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/DetectPreviewFeatureAnalyzer.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/DetectPreviewFeatureAnalyzer.cs @@ -269,7 +269,8 @@ public override void Initialize(AnalysisContext context) OperationKind.ArrayCreation, OperationKind.CatchClause, OperationKind.TypeOf, - OperationKind.EventAssignment + OperationKind.EventAssignment, + OperationKind.Await ); // Handle preview symbol definitions @@ -809,7 +810,7 @@ private bool OperationUsesPreviewFeatures(OperationAnalysisContext context, return false; } - private static ISymbol? GetOperationSymbol(IOperation operation) + private ISymbol? GetOperationSymbol(IOperation operation) => operation switch { IInvocationOperation iOperation => iOperation.TargetMethod, @@ -824,6 +825,7 @@ private bool OperationUsesPreviewFeatures(OperationAnalysisContext context, ICatchClauseOperation catchClauseOperation => catchClauseOperation.ExceptionType, ITypeOfOperation typeOfOperation => typeOfOperation.TypeOperand, IEventAssignmentOperation eventAssignment => GetOperationSymbol(eventAssignment.EventReference), + IAwaitOperation awaitOperation => SymbolFromAwaitOperation(awaitOperation), _ => null, }; @@ -838,6 +840,8 @@ private bool OperationUsesPreviewFeatures(OperationAnalysisContext context, return ret; } + protected abstract ISymbol? SymbolFromAwaitOperation(IAwaitOperation operation); + private bool TypeParametersHavePreviewAttribute(ISymbol namedTypeSymbolOrMethodSymbol, ImmutableArray typeParameters, ConcurrentDictionary requiresPreviewFeaturesSymbols, diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.VisualBasic.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/BasicDetectPreviewFeatureAnalyzer.vb b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.VisualBasic.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/BasicDetectPreviewFeatureAnalyzer.vb index e357049f7d52..be0214120c69 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.VisualBasic.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/BasicDetectPreviewFeatureAnalyzer.vb +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Microsoft.CodeAnalysis.VisualBasic.NetAnalyzers/Microsoft.NetCore.Analyzers/Runtime/BasicDetectPreviewFeatureAnalyzer.vb @@ -2,6 +2,7 @@ Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Operations Imports Microsoft.NetCore.Analyzers.Runtime Imports Microsoft.CodeAnalysis.VisualBasic.Syntax @@ -16,6 +17,10 @@ Namespace Microsoft.NetCore.VisualBasic.Analyzers.Runtime Return identifier.ValueText.Equals(previewInterfaceSymbol.Name, StringComparison.OrdinalIgnoreCase) End Function + Protected Overrides Function SymbolFromAwaitOperation(operation As IAwaitOperation) As ISymbol + Return Nothing + End Function + Private Shared Function GetElementTypeForNullableAndArrayTypeNodes(parameterType As TypeSyntax) As TypeSyntax Dim ret As TypeSyntax = parameterType Dim loopVariable = TryCast(parameterType, NullableTypeSyntax) @@ -334,4 +339,4 @@ Namespace Microsoft.NetCore.VisualBasic.Analyzers.Runtime End Function End Class -End Namespace \ No newline at end of file +End Namespace diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.projitems b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.projitems index c6f9820f49f9..cad6a604c501 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.projitems +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler.CSharp/Analyzer.CSharp.Utilities.projitems @@ -10,6 +10,7 @@ + - \ No newline at end of file + diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler.CSharp/Lightup/AwaitExpressionInfoWrapper.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler.CSharp/Lightup/AwaitExpressionInfoWrapper.cs new file mode 100644 index 000000000000..719a13858a87 --- /dev/null +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler.CSharp/Lightup/AwaitExpressionInfoWrapper.cs @@ -0,0 +1,33 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace Analyzer.Utilities.Lightup +{ + internal static class AwaitExpressionInfoWrapper + { + private static Func? s_RuntimeAwaitMethodAccessor; + + extension(AwaitExpressionInfo info) + { + public IMethodSymbol? RuntimeAwaitMethod + { + get + { + LazyInitializer.EnsureInitialized(ref s_RuntimeAwaitMethodAccessor, () => + { + return LightupHelpers.CreatePropertyAccessor( + typeof(AwaitExpressionInfo), + "info", + "RuntimeAwaitMethod", + fallbackResult: null); + }); + + RoslynDebug.Assert(s_RuntimeAwaitMethodAccessor is not null); + return s_RuntimeAwaitMethodAccessor(info); + } + } + } + } +} diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Lightup/LightupHelpers.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Lightup/LightupHelpers.cs index 39b63ec4ad03..8c714a20a376 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Lightup/LightupHelpers.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/src/Utilities/Compiler/Lightup/LightupHelpers.cs @@ -53,7 +53,7 @@ internal static Func CreateSymbolPropertyAccessor CreatePropertyAccessor(type, "symbol", propertyName, fallbackResult); - private static Func CreatePropertyAccessor(Type? type, string parameterName, string propertyName, TProperty fallbackResult) + internal static Func CreatePropertyAccessor(Type? type, string parameterName, string propertyName, TProperty fallbackResult) { if (!TryGetProperty(type, propertyName, out var property)) { diff --git a/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.NetCore.Analyzers/Runtime/DetectPreviewFeatureUnitTests.Misc.cs b/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.NetCore.Analyzers/Runtime/DetectPreviewFeatureUnitTests.Misc.cs index a0fe473ba9ab..b3b84fdf3516 100644 --- a/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.NetCore.Analyzers/Runtime/DetectPreviewFeatureUnitTests.Misc.cs +++ b/src/Microsoft.CodeAnalysis.NetAnalyzers/tests/Microsoft.CodeAnalysis.NetAnalyzers.UnitTests/Microsoft.NetCore.Analyzers/Runtime/DetectPreviewFeatureUnitTests.Misc.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using Xunit; using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< @@ -95,7 +96,7 @@ private static VerifyCS.Test TestCSPreview(string csInput) [Fact] public async Task TestCatchPreviewException() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -131,7 +132,7 @@ static void Main(string[] args) [Fact] public async Task TestCustomMessageCustomURL() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -160,7 +161,7 @@ public class Lib [Fact] public async Task TestCustomMessageDefaultURL() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -187,7 +188,7 @@ public class Lib [Fact] public async Task TestDefaultMessageCustomURL() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -214,7 +215,7 @@ public class Lib [Fact] public async Task TestArrayOfPreviewTypes() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -243,7 +244,7 @@ public class Lib [Fact] public async Task TestArrayOfArraysOfPreviewTypes() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -270,7 +271,7 @@ public class Lib [Fact] public async Task TestPreviewLanguageFeaturesHeirarchy() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -304,7 +305,7 @@ public interface IProgram [Fact] public async Task TestPreviewLanguageFeatures() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -339,7 +340,7 @@ public interface IProgram [Fact] public async Task TestInterfaceMethodInvocation() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -401,7 +402,7 @@ public void Foo() [Fact] public async Task TestDelegate() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -426,7 +427,7 @@ static void Main(string[] args) [Fact] public async Task TestTypeOf() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -438,7 +439,7 @@ static void Main(string[] args) Console.WriteLine({|#0:typeof(IFoo)|}); } } - + [RequiresPreviewFeatures] interface IFoo { } }"; @@ -451,7 +452,7 @@ interface IFoo { } [Fact] public async Task TestSimpleCustomAttributeOnPreviewClass() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -485,7 +486,7 @@ class MyAttribute : Attribute [Fact] public async Task TestSimpleCustomAttribute() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -518,7 +519,7 @@ class MyAttribute : Attribute [Fact(Skip = "https://github.com/dotnet/roslyn-analyzers/issues/6134")] public async Task TestCustomAttribute() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -593,7 +594,7 @@ public MyAttribute(bool foo) {} [Fact] public async Task TestDeepNesting() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -638,7 +639,7 @@ public class NestedClass3 [Fact] public async Task TestNestedInvocation() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -666,7 +667,7 @@ class A [Fact] public async Task TestNestedClass() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -694,7 +695,7 @@ static void Main(string[] args) [Fact] public async Task TestCallback() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch {" + @@ -740,7 +741,7 @@ public class Foo [Fact] public async Task TestVbCaseInsensitiveCsharpSensitive() { - var csInput = @" + var csInput = @" using System.Runtime.Versioning; using System; namespace Preview_Feature_Scratch { @@ -775,7 +776,7 @@ public void UnmarkedMethodInUnMarkedInterface() { } test.ExpectedDiagnostics.Add(VerifyCS.Diagnostic(DetectPreviewFeatureAnalyzer.ImplementsPreviewInterfaceRule).WithLocation(1).WithArguments("Program", "IProgram", DetectPreviewFeatureAnalyzer.DefaultURL)); await test.RunAsync(); - var vbInput = @" + var vbInput = @" Imports System Imports System.Runtime.Versioning Module Preview_Feature_Scratch @@ -802,5 +803,93 @@ End Module testVb.ExpectedDiagnostics.Add(VerifyVB.Diagnostic(DetectPreviewFeatureAnalyzer.ImplementsPreviewInterfaceRule).WithLocation(1).WithArguments("Program", "Iprogram", DetectPreviewFeatureAnalyzer.DefaultURL)); await testVb.RunAsync(); } + + [Fact] + public async Task VerifyRuntimeAsyncReportsDiagnostic() + { + var csInput = """ + using System.Threading.Tasks; + class C + { + async Task M() + { + await Task.CompletedTask; + } + } + """; + + var test = new RuntimeAsyncFixVerifier + { + TestState = + { + Sources = + { + csInput + } + }, + ExpectedDiagnostics = + { + // /0/Test0.cs(6,9): error CA2252: Using 'Await' requires opting into preview features. See https://aka.ms/dotnet-warnings/preview-features for more information. + VerifyCS.Diagnostic(DetectPreviewFeatureAnalyzer.GeneralPreviewFeatureAttributeRule).WithSpan(6, 9, 6, 33).WithArguments("Await", DetectPreviewFeatureAnalyzer.DefaultURL) + } + }; + + await test.RunAsync(); + } + + [Fact] + public async Task VerifyRuntimeAsyncReportsDiagnostic_CustomAwaiter() + { + var csInput = """ + using System.Threading.Tasks; + class C + { + async Task M() + { + await Task.Yield(); + } + } + """; + + var test = new RuntimeAsyncFixVerifier + { + TestState = + { + Sources = + { + csInput + } + }, + ExpectedDiagnostics = + { + // /0/Test0.cs(6,9): error CA2252: Using 'UnsafeAwaitAwaiter' requires opting into preview features. See https://aka.ms/dotnet-warnings/preview-features for more information. + VerifyCS.Diagnostic(DetectPreviewFeatureAnalyzer.GeneralPreviewFeatureAttributeRule).WithSpan(6, 9, 6, 27).WithArguments("UnsafeAwaitAwaiter", DetectPreviewFeatureAnalyzer.DefaultURL) + } + }; + await test.RunAsync(); + } + + private class RuntimeAsyncFixVerifier : VerifyCS.Test + { + public static readonly ReferenceAssemblies Net100 = new("net10.0", new PackageIdentity("Microsoft.NETCore.App.Ref", "10.0.0-rc.1.25451.107"), Path.Combine("ref", "net10.0")); + + public RuntimeAsyncFixVerifier() + { + ReferenceAssemblies = Net100; + LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10; + } + + protected override ParseOptions CreateParseOptions() + { + var options = base.CreateParseOptions(); + return options.WithFeatures([new("runtime-async", "on")]); + } + + protected override CompilationOptions CreateCompilationOptions() + { + var options = base.CreateCompilationOptions(); + return options.WithSpecificDiagnosticOptions([new("SYSLIB5007", ReportDiagnostic.Suppress)]); + } + } } } diff --git a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/Microsoft.Net.Sdk.AnalyzerRedirecting.csproj b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/Microsoft.Net.Sdk.AnalyzerRedirecting.csproj index de5612b1c470..20d248f368aa 100644 --- a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/Microsoft.Net.Sdk.AnalyzerRedirecting.csproj +++ b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/Microsoft.Net.Sdk.AnalyzerRedirecting.csproj @@ -24,6 +24,7 @@ + diff --git a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/SdkAnalyzerAssemblyRedirector.cs b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/SdkAnalyzerAssemblyRedirector.cs index c81a829990c2..23736deeb43e 100644 --- a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/SdkAnalyzerAssemblyRedirector.cs +++ b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/SdkAnalyzerAssemblyRedirector.cs @@ -3,7 +3,10 @@ using System.Collections.Immutable; using System.ComponentModel.Composition; +using System.Text.Json; using Microsoft.CodeAnalysis.Workspaces.AnalyzerRedirecting; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; // Example: // FullPath: "C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\8.0.8\analyzers\dotnet\System.Windows.Forms.Analyzers.dll" @@ -13,9 +16,16 @@ namespace Microsoft.Net.Sdk.AnalyzerRedirecting; +/// +/// See documentation/general/analyzer-redirecting.md. +/// [Export(typeof(IAnalyzerAssemblyRedirector))] public sealed class SdkAnalyzerAssemblyRedirector : IAnalyzerAssemblyRedirector { + private readonly IVsActivityLog? _log; + + private readonly bool _enabled; + private readonly string? _insertedAnalyzersDirectory; /// @@ -24,62 +34,91 @@ public sealed class SdkAnalyzerAssemblyRedirector : IAnalyzerAssemblyRedirector private readonly ImmutableDictionary> _analyzerMap; [ImportingConstructor] - public SdkAnalyzerAssemblyRedirector() - : this(Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\DotNetRuntimeAnalyzers"))) { } + public SdkAnalyzerAssemblyRedirector(SVsServiceProvider serviceProvider) : this( + Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"CommonExtensions\Microsoft\DotNet")), + serviceProvider.GetService()) + { + } // Internal for testing. - internal SdkAnalyzerAssemblyRedirector(string? insertedAnalyzersDirectory) + internal SdkAnalyzerAssemblyRedirector(string? insertedAnalyzersDirectory, IVsActivityLog? log = null) { + _log = log; + var enable = Environment.GetEnvironmentVariable("DOTNET_ANALYZER_REDIRECTING"); + _enabled = !"0".Equals(enable, StringComparison.OrdinalIgnoreCase) && !"false".Equals(enable, StringComparison.OrdinalIgnoreCase); _insertedAnalyzersDirectory = insertedAnalyzersDirectory; _analyzerMap = CreateAnalyzerMap(); } private ImmutableDictionary> CreateAnalyzerMap() { + if (!_enabled) + { + Log("Analyzer redirecting is disabled."); + return ImmutableDictionary>.Empty; + } + + var metadataFilePath = Path.Combine(_insertedAnalyzersDirectory, "metadata.json"); + if (!File.Exists(metadataFilePath)) + { + Log($"File does not exist: {metadataFilePath}"); + return ImmutableDictionary>.Empty; + } + + var versions = JsonSerializer.Deserialize>(File.ReadAllText(metadataFilePath)); + if (versions is null || versions.Count == 0) + { + Log($"Versions are empty: {metadataFilePath}"); + return ImmutableDictionary>.Empty; + } + var builder = ImmutableDictionary.CreateBuilder>(StringComparer.OrdinalIgnoreCase); // Expects layout like: - // VsInstallDir\SDK\RuntimeAnalyzers\WindowsDesktopAnalyzers\8.0.8\analyzers\dotnet\System.Windows.Forms.Analyzers.dll - // ~~~~~~~~~~~~~~~~~~~~~~~ = topLevelDirectory - // ~~~~~ = versionDirectory - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ = analyzerPath + // VsInstallDir\DotNetRuntimeAnalyzers\WindowsDesktopAnalyzers\analyzers\dotnet\System.Windows.Forms.Analyzers.dll + // ~~~~~~~~~~~~~~~~~~~~~~~ = topLevelDirectory + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ = analyzerPath foreach (string topLevelDirectory in Directory.EnumerateDirectories(_insertedAnalyzersDirectory)) { - foreach (string versionDirectory in Directory.EnumerateDirectories(topLevelDirectory)) + foreach (string analyzerPath in Directory.EnumerateFiles(topLevelDirectory, "*.dll", SearchOption.AllDirectories)) { - foreach (string analyzerPath in Directory.EnumerateFiles(versionDirectory, "*.dll", SearchOption.AllDirectories)) + if (!analyzerPath.StartsWith(topLevelDirectory, StringComparison.OrdinalIgnoreCase)) { - if (!analyzerPath.StartsWith(versionDirectory, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - string version = Path.GetFileName(versionDirectory); - string analyzerName = Path.GetFileNameWithoutExtension(analyzerPath); - string pathSuffix = analyzerPath.Substring(versionDirectory.Length + 1 /* slash */); - pathSuffix = Path.GetDirectoryName(pathSuffix); - - AnalyzerInfo analyzer = new() { FullPath = analyzerPath, ProductVersion = version, PathSuffix = pathSuffix }; - - if (builder.TryGetValue(analyzerName, out var existing)) - { - existing.Add(analyzer); - } - else - { - builder.Add(analyzerName, [analyzer]); - } + continue; + } + + string subsetName = Path.GetFileName(topLevelDirectory); + if (!versions.TryGetValue(subsetName, out string version)) + { + continue; + } + + string analyzerName = Path.GetFileNameWithoutExtension(analyzerPath); + string pathSuffix = analyzerPath.Substring(topLevelDirectory.Length + 1 /* slash */); + pathSuffix = Path.GetDirectoryName(pathSuffix); + + AnalyzerInfo analyzer = new() { FullPath = analyzerPath, ProductVersion = version, PathSuffix = pathSuffix }; + + if (builder.TryGetValue(analyzerName, out var existing)) + { + existing.Add(analyzer); + } + else + { + builder.Add(analyzerName, [analyzer]); } } } + Log($"Loaded analyzer map ({builder.Count}): {_insertedAnalyzersDirectory}"); + return builder.ToImmutable(); } public string? RedirectPath(string fullPath) { - if (_analyzerMap.TryGetValue(Path.GetFileNameWithoutExtension(fullPath), out var analyzers)) + if (_enabled && _analyzerMap.TryGetValue(Path.GetFileNameWithoutExtension(fullPath), out var analyzers)) { foreach (AnalyzerInfo analyzer in analyzers) { @@ -134,4 +173,12 @@ static bool areVersionMajorMinorPartEqual(string version1, string version2) return 0 == string.Compare(version1, 0, version2, 0, secondDotIndex, StringComparison.OrdinalIgnoreCase); } } + + private void Log(string message) + { + _log?.LogEntry( + (uint)__ACTIVITYLOG_ENTRYTYPE.ALE_INFORMATION, + nameof(SdkAnalyzerAssemblyRedirector), + message); + } } diff --git a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/source.extension.vsixmanifest b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/source.extension.vsixmanifest index 3c3150875aab..cfa767fb3ca0 100644 --- a/src/Microsoft.Net.Sdk.AnalyzerRedirecting/source.extension.vsixmanifest +++ b/src/Microsoft.Net.Sdk.AnalyzerRedirecting/source.extension.vsixmanifest @@ -13,10 +13,10 @@ - + - - + + amd64 diff --git a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs index 6b891c586a23..ae38c8e2f61c 100644 --- a/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs +++ b/src/Resolvers/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs @@ -32,7 +32,7 @@ public sealed class DotNetMSBuildSdkResolver : SdkResolver private readonly Func _getMsbuildRuntime; private readonly NETCoreSdkResolver _netCoreSdkResolver; - private const string DOTNET_HOST = nameof(DOTNET_HOST); + private const string DOTNET_HOST_PATH = nameof(DOTNET_HOST_PATH); private const string DotnetHostExperimentalKey = "DOTNET_EXPERIMENTAL_HOST_PATH"; private const string MSBuildTaskHostRuntimeVersion = "SdkResolverMSBuildTaskHostRuntimeVersion"; private const string SdkResolverHonoredGlobalJson = "SdkResolverHonoredGlobalJson"; @@ -245,12 +245,12 @@ private sealed class CachedState // this is the future-facing implementation. environmentVariablesToAdd ??= new Dictionary(1) { - [DOTNET_HOST] = fullPathToMuxer + [DOTNET_HOST_PATH] = fullPathToMuxer }; } else { - logger?.LogMessage($"Could not set '{DOTNET_HOST}' environment variable because dotnet executable '{fullPathToMuxer}' does not exist."); + logger?.LogMessage($"Could not set '{DOTNET_HOST_PATH}' environment variable because dotnet executable '{fullPathToMuxer}' does not exist."); } string? runtimeVersion = dotnetRoot != null ? diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs index 678577e9053f..a6d9f6d808ae 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs @@ -28,7 +28,7 @@ static Interop() } // MSBuild SDK resolvers are required to be AnyCPU, but we have a native dependency and .NETFramework does not - // have a built-in facility for dynamically loading user native dlls for the appropriate platform. We therefore + // have a built-in facility for dynamically loading user native dlls for the appropriate platform. We therefore // preload the version with the correct architecture (from a corresponding sub-folder relative to us) on static // construction so that subsequent P/Invokes can find it. private static void PreloadWindowsLibrary(string dllFileName) @@ -124,6 +124,12 @@ internal static extern int hostfxr_get_dotnet_environment_info( hostfxr_get_dotnet_environment_info_result_fn result, IntPtr result_context); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void hostfxr_error_writer_fn(IntPtr message); + + [DllImport(Constants.HostFxr, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr hostfxr_set_error_writer(IntPtr error_writer); + public static class Windows { private const CharSet UTF16 = CharSet.Unicode; diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/NETCoreSdkResolverNativeWrapper.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/NETCoreSdkResolverNativeWrapper.cs index 160346fbccfc..b0571d439d09 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/NETCoreSdkResolverNativeWrapper.cs +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/NETCoreSdkResolverNativeWrapper.cs @@ -23,6 +23,26 @@ public static SdkResolutionResult ResolveSdk( return result; } + public static string? GetGlobalJsonState(string globalJsonStartDirectory) + { + // We don't care about the actual SDK resolution, just the global.json information, + // so just pass empty string as executable directory for resolution. This means that + // we expect the call to fail to resolve an SDK. Set up the error writer to avoid + // output going to stderr. We reset it after the call. + var swallowErrors = new Interop.hostfxr_error_writer_fn(message => { }); + IntPtr errorWriter = Marshal.GetFunctionPointerForDelegate(swallowErrors); + IntPtr previousErrorWriter = Interop.hostfxr_set_error_writer(errorWriter); + try + { + SdkResolutionResult result = ResolveSdk(string.Empty, globalJsonStartDirectory); + return result.GlobalJsonState; + } + finally + { + Interop.hostfxr_set_error_writer(previousErrorWriter); + } + } + private sealed class SdkList { public string[]? Entries; diff --git a/src/StaticWebAssetsSdk/Tasks/Data/StaticWebAssetPathPattern.cs b/src/StaticWebAssetsSdk/Tasks/Data/StaticWebAssetPathPattern.cs index 23a0f96f6607..5c9b48c486c2 100644 --- a/src/StaticWebAssetsSdk/Tasks/Data/StaticWebAssetPathPattern.cs +++ b/src/StaticWebAssetsSdk/Tasks/Data/StaticWebAssetPathPattern.cs @@ -505,4 +505,71 @@ public override int GetHashCode() private static bool IsLiteralSegment(StaticWebAssetPathSegment segment) => segment.Parts.Count == 1 && segment.Parts[0].IsLiteral; internal static string PathWithoutTokens(string path) => Parse(path).ComputePatternLabel(); + + internal static string ExpandIdentityFileNameForFingerprint(string fileNamePattern, string fingerprint) + { + var pattern = Parse(fileNamePattern); + var sb = new StringBuilder(); + foreach (var segment in pattern.Segments) + { + var isLiteral = segment.Parts.Count == 1 && segment.Parts[0].IsLiteral; + if (isLiteral) + { + sb.Append(segment.Parts[0].Name); + continue; + } + + if (segment.IsOptional && !segment.IsPreferred) + { + continue; // skip non-preferred optional segments + } + + bool missingRequired = false; + foreach (var part in segment.Parts) + { + if (!part.IsLiteral && part.Value.IsEmpty) + { + var tokenName = part.Name.ToString(); + if (string.Equals(tokenName, "fingerprint", StringComparison.OrdinalIgnoreCase) && string.IsNullOrEmpty(fingerprint)) + { + missingRequired = true; + break; + } + } + } + if (missingRequired) + { + if (!segment.IsOptional) + { + throw new InvalidOperationException($"Token 'fingerprint' not provided for '{fileNamePattern}'."); + } + continue; + } + + foreach (var part in segment.Parts) + { + if (part.IsLiteral) + { + sb.Append(part.Name); + } + else if (!part.Value.IsEmpty) + { + sb.Append(part.Value); + } + else + { + var tokenName = part.Name.ToString(); + if (string.Equals(tokenName, "fingerprint", StringComparison.OrdinalIgnoreCase)) + { + sb.Append(fingerprint); + } + else + { + throw new InvalidOperationException($"Unsupported token '{tokenName}' in '{fileNamePattern}'."); + } + } + } + } + return sb.ToString(); + } } diff --git a/src/StaticWebAssetsSdk/Tasks/DefineStaticWebAssets.cs b/src/StaticWebAssetsSdk/Tasks/DefineStaticWebAssets.cs index 4d24ce9ed429..9f43c63845d1 100644 --- a/src/StaticWebAssetsSdk/Tasks/DefineStaticWebAssets.cs +++ b/src/StaticWebAssetsSdk/Tasks/DefineStaticWebAssets.cs @@ -238,6 +238,14 @@ public override bool Execute() break; } + // IMPORTANT: Apply fingerprint pattern (which can change the file name) BEFORE computing identity + // for non-Discovered assets so that a synthesized identity incorporates the fingerprint pattern. + if (FingerprintCandidates) + { + matchContext.SetPathAndReinitialize(relativePathCandidate); + relativePathCandidate = StaticWebAsset.Normalize(fingerprintPatternMatcher.AppendFingerprintPattern(matchContext, identity)); + } + if (!string.Equals(SourceType, StaticWebAsset.SourceTypes.Discovered, StringComparison.OrdinalIgnoreCase)) { // We ignore the content root for publish only assets since it doesn't matter. @@ -246,16 +254,21 @@ public override bool Execute() if (computed) { + // If we synthesized identity and there is a fingerprint placeholder pattern in the file name + // expand it to the concrete fingerprinted file name while keeping RelativePath pattern form. + if (FingerprintCandidates && !string.IsNullOrEmpty(fingerprint)) + { + var fileNamePattern = Path.GetFileName(identity); + if (fileNamePattern.Contains("#[")) + { + var expanded = StaticWebAssetPathPattern.ExpandIdentityFileNameForFingerprint(fileNamePattern, fingerprint); + identity = Path.Combine(Path.GetDirectoryName(identity) ?? string.Empty, expanded); + } + } assetsCache.AppendCopyCandidate(hash, candidate.ItemSpec, identity); } } - if (FingerprintCandidates) - { - matchContext.SetPathAndReinitialize(relativePathCandidate); - relativePathCandidate = StaticWebAsset.Normalize(fingerprintPatternMatcher.AppendFingerprintPattern(matchContext, identity)); - } - var asset = StaticWebAsset.FromProperties( identity, sourceId, @@ -357,7 +370,13 @@ public override bool Execute() // Alternatively, we could be explicit here and support ContentRootSubPath to indicate where it needs to go. var identitySubPath = Path.GetDirectoryName(relativePath); var itemSpecFileName = Path.GetFileName(candidateFullPath); - var finalIdentity = Path.Combine(normalizedContentRoot, identitySubPath, itemSpecFileName); + var relativeFileName = Path.GetFileName(relativePath); + // If the relative path filename has been modified (e.g. fingerprint pattern appended) use it when synthesizing identity. + if (!string.IsNullOrEmpty(relativeFileName) && !string.Equals(relativeFileName, itemSpecFileName, StringComparison.OrdinalIgnoreCase)) + { + itemSpecFileName = relativeFileName; + } + var finalIdentity = Path.Combine(normalizedContentRoot, identitySubPath ?? string.Empty, itemSpecFileName); Log.LogMessage(MessageImportance.Low, "Identity for candidate '{0}' is '{1}' because it did not start with the content root '{2}'", candidate.ItemSpec, finalIdentity, normalizedContentRoot); return (finalIdentity, true); } @@ -493,7 +512,7 @@ private void UpdateAssetKindIfNecessary( { case (StaticWebAsset.AssetCopyOptions.Never, StaticWebAsset.AssetCopyOptions.Never): case (not StaticWebAsset.AssetCopyOptions.Never, not StaticWebAsset.AssetCopyOptions.Never): - var errorMessage = "Two assets found targeting the same path with incompatible asset kinds: " + Environment.NewLine + + var errorMessage = "Two assets found targeting the same path with incompatible asset kinds:" + Environment.NewLine + "'{0}' with kind '{1}'" + Environment.NewLine + "'{2}' with kind '{3}'" + Environment.NewLine + "for path '{4}'"; diff --git a/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs b/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs index 0387bb131057..f996a75b7168 100644 --- a/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs +++ b/src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs @@ -116,6 +116,8 @@ public override bool Execute() endpoint.AssetFile = asset.ResolvedAsset.ComputeTargetPath("", '/', StaticWebAssetTokenResolver.Instance); endpoint.Route = route; + EncodeLinkHeadersIfNeeded(endpoint); + Log.LogMessage(MessageImportance.Low, "Including endpoint '{0}' for asset '{1}' with final location '{2}'", endpoint.Route, endpoint.AssetFile, asset.TargetPath); } @@ -137,6 +139,48 @@ public override bool Execute() return !Log.HasLoggedErrors; } + private static void EncodeLinkHeadersIfNeeded(StaticWebAssetEndpoint endpoint) + { + for (var i = 0; i < endpoint.ResponseHeaders.Length; i++) + { + ref var header = ref endpoint.ResponseHeaders[i]; + if (!string.Equals(header.Name, "Link", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + var headerValues = header.Value.Split([','], StringSplitOptions.RemoveEmptyEntries); + for (var j = 0; j < headerValues.Length; j++) + { + ref var value = ref headerValues[j]; + value = EncodeHeaderValue(value); + } + header.Value = string.Join(",", headerValues); + } + } + + private static string EncodeHeaderValue(string header) + { + var index = header.IndexOf('<'); + if (index == -1) + { + return header; + } + index++; + var endIndex = header.IndexOf('>', index); + if (endIndex == -1) + { + return header; + } + var link = header.AsSpan(index, endIndex - index).ToString(); + var segments = link.Split('/'); + for (var j = 0; j < segments.Length; j++) + { + segments[j] = System.Net.WebUtility.UrlEncode(segments[j]); + } + var encoded = string.Join("/", segments); + return $"{header.Substring(0, index)}{encoded}{header.Substring(endIndex)}"; + } + private static (string, string[]) ParseAndSortPatterns(string patterns) { if (string.IsNullOrEmpty(patterns)) diff --git a/src/StaticWebAssetsSdk/Tasks/Utils/Globbing/StaticWebAssetGlobMatcher.cs b/src/StaticWebAssetsSdk/Tasks/Utils/Globbing/StaticWebAssetGlobMatcher.cs index 3f2910cdd1da..07b44a10440b 100644 --- a/src/StaticWebAssetsSdk/Tasks/Utils/Globbing/StaticWebAssetGlobMatcher.cs +++ b/src/StaticWebAssetsSdk/Tasks/Utils/Globbing/StaticWebAssetGlobMatcher.cs @@ -479,7 +479,10 @@ private static MatchStage GetInitialStage(GlobNode node) return MatchStage.Done; } - internal readonly MatchState NextExtension(int extensionIndex) => new(Node, MatchStage.Extension, SegmentIndex, extensionIndex, ComplexSegmentIndex); + internal readonly MatchState NextExtension(int extensionIndex) => new(Node, MatchStage.Extension, SegmentIndex, extensionIndex, ComplexSegmentIndex) + { + StemStartIndex = StemStartIndex + }; internal readonly MatchState NextComplex() => new(Node, MatchStage.Complex, SegmentIndex, ExtensionSegmentIndex, ComplexSegmentIndex + 1); diff --git a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf index bd159c631b08..d8efa3b3e0c0 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.cs.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.cs.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: Vlastnosti {0} a {1} nelze zadat současně. Odeberte jednu nebo druhou. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: Soubor {0} neexistuje. Ujistěte se prosím, že cesta ke grafu modulu runtime existuje. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: Projekt(y) několika řešení obsahuj(e/í) konfliktní hodnoty „{0}“; ujistěte se, že se hodnoty shodují. Zvažte použití souboru Directory.build.props k nastavení vlastnosti pro všechny projekty. Konfliktní projekty: + NETSDK1197: Projekt(y) několika řešení obsahuj(e/í) konfliktní hodnoty „{0}“; ujistěte se, že se hodnoty shodují. Zvažte použití souboru Directory.build.props k nastavení vlastnosti pro všechny projekty. Konfliktní projekty: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: Nelze najít odpovídající identifikátor RID pro {0} z [{1}] v grafu {2}. {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: Cílení na verzi Windows SDK s hodnotou 1, protože číslo revize bude odkazovat na CsWinRT 3.0, což je aktuálně ve verzi Preview. Aktuální projekt cílí na verzi Windows SDK {0}. Pokud to není záměrné, změňte číslo revize na 0, aby se místo toho používalo CsWinRT 2.x. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.de.xlf b/src/Tasks/Common/Resources/xlf/Strings.de.xlf index 531c0faa0052..81a06bb12710 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.de.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.de.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: Die Eigenschaften{0} und {1} können nicht beide angegeben werden. Entfernen Sie eine der beiden. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: Die Datei „{0}“ existiert nicht. Stellen Sie sicher, dass der Pfad zum Runtimegraph vorhanden ist. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: Mehrere Projektmappenprojekte enthalten widersprüchliche Werte für "{0}". Stellen Sie sicher, dass die Werte übereinstimmen. Erwägen Sie die Verwendung einer Directory.build.props-Datei, um die Eigenschaft für alle Projekte festzulegen. In Konflikt stehende Projekte: + NETSDK1197: Mehrere Projektmappenprojekte enthalten widersprüchliche Werte für "{0}". Stellen Sie sicher, dass die Werte übereinstimmen. Erwägen Sie die Verwendung einer Directory.build.props-Datei, um die Eigenschaft für alle Projekte festzulegen. In Konflikt stehende Projekte: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: Es konnte keine passende RID für „{0}“ aus [{1}] im Graph „{2}“ gefunden werden. {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: Das Anvisieren einer Windows-SDK-Version mit der Revisionsnummer „1“ verweist auf CsWinRT 3.0, das sich derzeit in der Vorschau befindet. Das aktuelle Projekt verwendet die Windows-SDK-Version „{0}“. Wenn dies nicht beabsichtigt ist, ändern Sie die Revisionsnummer auf „0“, um stattdessen CsWinRT 2.x zu verwenden. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.es.xlf b/src/Tasks/Common/Resources/xlf/Strings.es.xlf index 511a72b3c3a6..c9928b5aa3eb 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.es.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.es.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: no se pueden especificar las propiedades {0} y {1} a la vez. Quite uno u otra. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: el archivo "{0}" no existe. Asegúrese de que la ruta de acceso del grafo en tiempo de ejecución existe. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: Varios proyectos de solución que contienen valores '{0}' en conflicto; asegúrese de que los valores coinciden. Considere la posibilidad de usar un archivo Directory.build.props para establecer la propiedad para todos los proyectos. Proyectos en conflicto: + NETSDK1197: Varios proyectos de solución que contienen valores '{0}' en conflicto; asegúrese de que los valores coinciden. Considere la posibilidad de usar un archivo Directory.build.props para establecer la propiedad para todos los proyectos. Proyectos en conflicto: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: no se puede encontrar un RID coincidente para "{0}" entre [{1}] en el grafo "{2}". {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: si se selecciona una versión del SDK de Windows con "1" como número de revisión, se hará referencia a CsWinRT 3.0, que está actualmente en versión preliminar. El proyecto actual tiene como destino la versión de Windows SDK "{0}". Si esto no está previsto, cambie el número de revisión a "0" para usar CsWinRT 2.x en su lugar. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf index 9c62ece01900..4ff758501a74 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.fr.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.fr.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: les propriétés {0} et {1} ne peuvent pas être spécifiées simultanément. Supprimez l’une ou l’autre. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: le fichier « {0} » n’existe pas. Veuillez vérifier que le chemin d’accès du graphique d’exécution existe. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: plusieurs projets de solution contiennent des valeurs '{0}' en conflit ; assurez-vous que les valeurs correspondent. Envisagez d'utiliser un fichier Directory.build.props pour définir la propriété pour tous les projets. Projets conflictuels : + NETSDK1197: plusieurs projets de solution contiennent des valeurs '{0}' en conflit ; assurez-vous que les valeurs correspondent. Envisagez d'utiliser un fichier Directory.build.props pour définir la propriété pour tous les projets. Projets conflictuels : {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: nous ne pouvons pas trouver un RID correspondant à « {0} » parmi [{1}] dans le graphique « {2} ». {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: ciblez une version du Kit de développement logiciel Windows (Kit SDK Windows) avec « 1 » comme numéro de révision fait référence à CsWinRT 3.0 qui est actuellement en préversion. Le projet actuel cible la version du Kit de développement logiciel Windows (Kit SDK Windows) « {0} ». Si ce n’est pas votre intention, remplacez le numéro de révision par « 0 » pour utiliser CsWinRT 2.x à la place. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.it.xlf b/src/Tasks/Common/Resources/xlf/Strings.it.xlf index de06e93873ab..ab607e4f9965 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.it.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: non è possibile specificare contemporaneamente le proprietà {0} e {1}. Rimuoverne una delle due. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: il file '{0}' non esiste. Verificare che il percorso del grafo del runtime esista. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: più progetti della soluzione contengono valori '{0}' in conflitto. Verificare che i valori corrispondano. Provare a usare un file Directory.build.props per impostare la proprietà di tutti i progetti. Progetti in conflitto: + NETSDK1197: più progetti della soluzione contengono valori '{0}' in conflitto. Verificare che i valori corrispondano. Provare a usare un file Directory.build.props per impostare la proprietà di tutti i progetti. Progetti in conflitto: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: non è possibile trovare un RID corrispondente a '{0}' tra [{1}] nel grafo '{2}'. {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: l'impostazione di una versione di Windows SDK con '1' come numero di revisione come destinazione farà riferimento a CsWinRT 3.0, che è attualmente in anteprima. La destinazione del progetto corrente è impostata sulla versione di Windows SDK '{0}'. Se non è intenzionale, modificare il numero di revisione in '0' per usare invece CsWinRT 2.x. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf index babd059b78b5..b0201395826e 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ja.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ja.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: {0} プロパティと {1} プロパティの両方を指定することはできません。どちらか一方を削除してください。 {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: ファイル '{0}' は存在しません。ランタイム グラフ パスが存在することを確認してください。 {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: 複数のソリューションのプロジェクトに、競合する '{0}' 値が含まれています。値が一致するようにしてください。Directory.build.props ファイルを使用して、すべてのプロジェクトのプロパティを設定することを検討してください。競合するプロジェクト: + NETSDK1197: 複数のソリューションのプロジェクトに、競合する '{0}' 値が含まれています。値が一致するようにしてください。Directory.build.props ファイルを使用して、すべてのプロジェクトのプロパティを設定することを検討してください。競合するプロジェクト: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: グラフ '{2}' の [{1}] の中から'{0}' に一致する RID が見つかりません。 {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: リビジョン番号に '1' を指定して Windows SDK バージョンをターゲットにすると、現在プレビュー中の CsWinRT 3.0 が参照されます。現在のプロジェクトは Windows SDK バージョン '{0}' をターゲットにしています。これが意図されていない場合は、リビジョン番号を '0' に変更して CsWinRT 2.x を代わりに使用してください。 {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf index 36b7099e3bdf..20518f8a3d6e 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ko.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ko.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: {0} 및 {1} 속성을 둘 다 지정할 수 없습니다. 둘 중 하나를 제거하세요. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: '{0}' 파일이 없습니다. 런타임 그래프 경로가 있는지 확인하세요. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: 여러 솔루션 프로젝트에 충돌하는 '{0}' 값이 있습니다. 값이 일치하는지 확인하세요. Directory.build.props 파일을 사용하여 모든 프로젝트에 대한 속성을 설정하는 것이 좋습니다. 충돌하는 프로젝트: + NETSDK1197: 여러 솔루션 프로젝트에 충돌하는 '{0}' 값이 있습니다. 값이 일치하는지 확인하세요. Directory.build.props 파일을 사용하여 모든 프로젝트에 대한 속성을 설정하는 것이 좋습니다. 충돌하는 프로젝트: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: 그래프 '{2}'의 [{1}] 중에서 '{0}'에 대해 일치하는 RID를 찾을 수 없습니다. {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: 수정 번호가 '1'인 Windows SDK 버전을 대상으로 지정하면 현재 미리 보기 상태인 CsWinRT 3.0을 참조합니다. 현재 프로젝트가 Windows SDK 버전 '{0}'을(를) 대상으로 합니다. 의도하지 않은 경우 수정 번호를 '0'으로 변경하여 CsWinRT 2.x를 대신 사용합니다. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf index dc4aba1f2e5e..459bfc4b7afb 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.pl.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.pl.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: nie można jednocześnie określić właściwości {0} i {1}. Usuń jedną lub drugą. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: plik „{0}” nie istnieje. Upewnij się, że ścieżka grafu środowiska uruchomieniowego istnieje. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: Wiele projektów rozwiązań zawiera wartości „{0}” powodujące konflikt; upewnij się, że wartości są zgodne. Rozważ użycie pliku Directory.build.props na potrzeby ustawienia właściwości dla wszystkich projektów. Projekty powodujące konflikt: + NETSDK1197: Wiele projektów rozwiązań zawiera wartości „{0}” powodujące konflikt; upewnij się, że wartości są zgodne. Rozważ użycie pliku Directory.build.props na potrzeby ustawienia właściwości dla wszystkich projektów. Projekty powodujące konflikt: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: Nie można odnaleźć zgodnego identyfikatora RID dla elementu „{0}” spośród [{1}] na wykresie „{2}”. {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: określanie wersji zestawu Windows SDK z numerem poprawki „1” będzie odwoływać się do wersji CsWinRT 3.0, która jest obecnie dostępna w wersji zapoznawczej. Bieżący projekt jest przeznaczony dla zestawu Windows SDK w wersji „{0}”. Jeśli nie jest to zamierzone, zmień numer poprawki na „0”, aby zamiast tego użyć csWinRT 2.x. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf index 906080a716bd..cd0a8868c26b 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.pt-BR.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: As propriedades {0} e {1} não podem ser especificadas ao mesmo tempo. Remova um ou outro. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: O arquivo '{0}' não existe. Certifique-se de que o caminho do grafo de runtime exista. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: Vários projetos de solução contêm valores '{0}' conflitantes; garantir que os valores correspondam. Considere usar um arquivo Directory.build.props para configurar a propriedade para todos os projetos. Projetos conflitantes: + NETSDK1197: Vários projetos de solução contêm valores '{0}' conflitantes; garantir que os valores correspondam. Considere usar um arquivo Directory.build.props para configurar a propriedade para todos os projetos. Projetos conflitantes: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: Não foi possível encontrar um RID correspondente para '{0}' entre [{1}] no grafo '{2}'. {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: Apontar para uma versão do Windows SDK com '1' como número de revisão fará referência ao CsWinRT 3.0, que está atualmente em pré-visualização. O projeto atual está apontando para a versão '{0}' do Windows SDK. Se isso não for intencional, altere o número de revisão para '0' para usar o CsWinRT 2.x em vez disso. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf index 135d04e004a0..ca045eac8c2e 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.ru.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.ru.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: свойства {0} и {1} не могут быть указаны одновременно. Удалите одно из них. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: файл "{0}" не существует. Убедитесь, что путь к графу среды выполнения существует. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: несколько проектов решения содержат конфликтующие значения "{0}". Убедитесь, что значения совпадают. Рассмотрите возможность использования файла Directory.build.props для настройки свойства для всех проектов. Конфликтующие проекты: + NETSDK1197: несколько проектов решения содержат конфликтующие значения "{0}". Убедитесь, что значения совпадают. Рассмотрите возможность использования файла Directory.build.props для настройки свойства для всех проектов. Конфликтующие проекты: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: не удалось найти соответствующий RID для "{0}" среди [{1}] в графе "{2}". {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: нацеливание на версию Windows SDK с номером редакции "1" будет ссылаться на CsWinRT 3.0, которая сейчас находится в предварительной версии. Текущий проект нацелен на Windows SDK версии "{0}". Если это не соответствует намерениям, измените номер редакции на "0", чтобы использовать CsWinRT 2.x. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf index ddf897db38c3..92fde9043a27 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.tr.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.tr.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: {0} ve {1} özellikleri birlikte belirtilemez. Birini kaldırın. {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: '{0}' dosyası mevcut değil. Lütfen çalışma zamanı grafiği yolunun var olduğundan emin olun. {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: Birden çok çözüm projesi çakışan '{0}' değerleri içeriyor; değerlerin eşleştiğinden emin olun. Özelliği tüm projeler için ayarlamak için Directory.build.props dosyası kullanabilirsiniz. Çakışan projeler: + NETSDK1197: Birden çok çözüm projesi çakışan '{0}' değerleri içeriyor; değerlerin eşleştiğinden emin olun. Özelliği tüm projeler için ayarlamak için Directory.build.props dosyası kullanabilirsiniz. Çakışan projeler: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: '{2}' grafiğindeki [{1}] arasından '{0}' için eşleşen bir RID bulunamadı. {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: Düzeltme numarası olarak '1' kullanılarak bir Windows SDK sürümünü hedeflemek, şu anda önizlemede olan CsWinRT 3.0’ı referans alacaktır. Geçerli proje Windows SDK sürüm '{0}' sürümünü hedefliyor. Eğer bu amaçlanmadıysa, CsWinRT 2.x kullanmak için düzeltme numarasını '0' olarak değiştirin. {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf index 3ab48490f99b..6b177d50d08f 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hans.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: 不能同时指定 {0} 和 {1} 属性。移除其中一个。 {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: 文件 "{0}" 不存在。请确保运行时图形路径存在。 {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: 多个解决方案项目包含存在冲突的“{0}”值;请确保值能够匹配。请考虑使用 Directory.build.props 文件设置所有项目的属性。存在冲突的项目: + NETSDK1197: 多个解决方案项目包含存在冲突的“{0}”值;请确保值能够匹配。请考虑使用 Directory.build.props 文件设置所有项目的属性。存在冲突的项目: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: 无法在图 "{2}" 的 [{1}] 中找到与 "{0}" 匹配的 RID。 {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: 将修订号为 "1" 的 Windows SDK 版本作为目标时,将引用 CsWinRT 3.0,它目前为预览版。当前项目面向 Windows SDK 版本 "{0}"。如果这不是预期内的,请将修订号更改为 "0" 以改用 CsWinRT 2.x。 {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf index 10ec0f90c63c..31e3d5a6c83c 100644 --- a/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Tasks/Common/Resources/xlf/Strings.zh-Hant.xlf @@ -214,7 +214,7 @@ NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. - NETSDK1230: The {0} and {1} properties cannot both be specified. Remove one or the other. + NETSDK1230: {0} 和 {1} 屬性無法同時指定。請移除其中一者。 {StrBegins="NETSDK1230: "} @@ -863,7 +863,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. - NETSDK1231: File '{0}' does not exist. Please ensure the runtime graph path exists. + NETSDK1231: 檔案 '{0}' 不存在。請確認執行階段圖形路徑是否存在。 {StrBegins="NETSDK1231: "}{Locked="{0}"} @@ -914,7 +914,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1197: Multiple solution project(s) contain conflicting '{0}' values; ensure the values match. Consider using a Directory.build.props file to set the property for all projects. Conflicting projects: {1} - NETSDK1197: 多個解決方案專案包含衝突的 '{0}' 值; 請確保值相符。考慮使用 Directory.build.props 檔案來設定所有專案的屬性。衝突的專案: + NETSDK1197: 多個解決方案專案包含衝突的 '{0}' 值; 請確保值相符。考慮使用 Directory.build.props 檔案來設定所有專案的屬性。衝突的專案: {1} {StrBegins="NETSDK1197: "} @@ -965,7 +965,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. - NETSDK1232: Unable to find a matching RID for '{0}' from among [{1}] in graph '{2}'. + NETSDK1232: 無法在圖表 '{2}' 中的 '{1}' 項目中找到與 '{0}' 相符的 RID。 {StrBegins="NETSDK1232: "}{Locked="{0}"}{Locked="{1}"}{Locked="{2}"} @@ -1060,7 +1060,7 @@ The following are names of parameters or literal values and should not be transl NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. - NETSDK1229: Targeting a Windows SDK version with '1' as the revision number will reference CsWinRT 3.0, which is currently in preview. The current project is targeting the Windows SDK version '{0}'. If this is not intended, change the revision number to '0' to use CsWinRT 2.x instead. + NETSDK1229: 若將修訂編號設為「1」來指定 Windows SDK 版本,將參考目前處於預覽階段的 CsWinRT 3.0。目前專案的目標 Windows SDK 版本為 '{0}'。若非此意圖,請將修訂編號改為「0」以改用 CsWinRT 2.x。 {StrBegins="NETSDK1229: "} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj index 157fde746f9d..d32804d3ee51 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.NET.Build.Tasks/Microsoft.NET.Build.Tasks.csproj @@ -56,7 +56,8 @@ - + + @@ -131,17 +132,12 @@ - <_NugetBuildTasksPackPath>$(NuGetPackageRoot)nuget.build.tasks.pack\$(NuGetBuildTasksPackageVersion) <_Stage0SdksFolder>$(DOTNET_INSTALL_DIR)\sdk\$(NETCoreSdkVersion)\Sdks %(None.PackagePath)\%(None.RecursiveDir)%(None.Filename)%(None.Extension) - - - ..\NuGet.Build.Tasks.Pack\%(PackFile.RecursiveDir)%(PackFile.Filename)%(PackFile.Extension) - diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs index 191dbdaeee48..a119f005fe80 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs @@ -817,20 +817,36 @@ private ToolPackSupport AddToolPack( { var packNamePattern = knownPack.GetMetadata(packName + "PackNamePattern"); var packSupportedRuntimeIdentifiers = knownPack.GetMetadata(packName + "RuntimeIdentifiers").Split(';'); - // When publishing for the non-portable RID that matches NETCoreSdkRuntimeIdentifier, prefer NETCoreSdkRuntimeIdentifier for the host. + var packSupportedPortableRuntimeIdentifiers = knownPack.GetMetadata(packName + "PortableRuntimeIdentifiers").Split(';'); + + // When publishing for a non-portable RID, prefer NETCoreSdkRuntimeIdentifier for the host. // Otherwise prefer the NETCoreSdkPortableRuntimeIdentifier. - // This makes non-portable SDKs behave the same as portable SDKs except for the specific case of targetting the non-portable RID. - // It also enables the non-portable ILCompiler to be packaged separately from the SDK and - // only required when publishing for the non-portable SDK RID. - string portableSdkRid = !string.IsNullOrEmpty(NETCoreSdkPortableRuntimeIdentifier) ? NETCoreSdkPortableRuntimeIdentifier : NETCoreSdkRuntimeIdentifier; - bool targetsNonPortableSdkRid = EffectiveRuntimeIdentifier == NETCoreSdkRuntimeIdentifier && NETCoreSdkRuntimeIdentifier != portableSdkRid; - string? hostRuntimeIdentifier = targetsNonPortableSdkRid ? NETCoreSdkRuntimeIdentifier : portableSdkRid; - Log.LogMessage(MessageImportance.Low, $"Determining best RID for '{knownPack.ItemSpec}@{packVersion}' for '{hostRuntimeIdentifier}' from among '{knownPack.GetMetadata(packName + "RuntimeIdentifiers")}'"); - // Get the best RID for the host machine, which will be used to validate that we can run crossgen for the target platform and architecture + // This makes non-portable SDKs behave the same as portable SDKs except for the specific case of targetting a non-portable RID. + // This ensures that targeting portable RIDs doesn't require any non-portable assets that aren't packaged in the SDK. + // Due to size concerns, the non-portable ILCompiler and Crossgen2 aren't included by default in non-portable SDK distributions. var runtimeGraph = new RuntimeGraphCache(this).GetRuntimeGraph(RuntimeGraphPath); - hostRuntimeIdentifier = NuGetUtils.GetBestMatchingRid(runtimeGraph, hostRuntimeIdentifier, packSupportedRuntimeIdentifiers, out bool wasInGraph); + + // Prefer portable when the "supported RID" for the tool pack is the same RID as the "supported portable RID". + // This makes non-portable SDKs behave the same as portable SDKs except for the specific cases added to "supported", such as targeting the non-portable RID. + // This also ensures that targeting common RIDs doesn't require any non-portable assets that aren't packaged in the SDK by default. + // Due to size concerns, the non-portable ILCompiler and Crossgen2 aren't included by default in non-portable SDK distributions. + var runtimeIdentifier = RuntimeIdentifier ?? "any"; + string? supportedTargetRid = NuGetUtils.GetBestMatchingRid(runtimeGraph, runtimeIdentifier, packSupportedRuntimeIdentifiers, out _); + string? supportedPortableTargetRid = NuGetUtils.GetBestMatchingRid(runtimeGraph, runtimeIdentifier, packSupportedPortableRuntimeIdentifiers, out _); + + bool usePortable = !string.IsNullOrEmpty(NETCoreSdkPortableRuntimeIdentifier) + && supportedTargetRid is not null && supportedPortableTargetRid is not null + && supportedTargetRid == supportedPortableTargetRid; + + // Get the best RID for the host machine, which will be used to validate that we can run crossgen for the target platform and architecture + Log.LogMessage(MessageImportance.Low, $"Determining best RID for '{knownPack.ItemSpec}@{packVersion}' from among '{knownPack.GetMetadata(packName + "RuntimeIdentifiers")}'"); + string? hostRuntimeIdentifier = usePortable + ? NuGetUtils.GetBestMatchingRid(runtimeGraph, NETCoreSdkPortableRuntimeIdentifier!, packSupportedPortableRuntimeIdentifiers, out _) + : NuGetUtils.GetBestMatchingRid(runtimeGraph, NETCoreSdkRuntimeIdentifier!, packSupportedRuntimeIdentifiers, out _); + if (hostRuntimeIdentifier == null) { + Log.LogMessage(MessageImportance.Low, $"No matching RID was found'"); return ToolPackSupport.UnsupportedForHostRuntimeIdentifier; } Log.LogMessage(MessageImportance.Low, $"Best RID for '{knownPack.ItemSpec}@{packVersion}' is '{hostRuntimeIdentifier}'"); diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.targets b/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.targets index 841a3654e9ea..3c8ce290c016 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/sdk/Sdk.targets @@ -46,8 +46,7 @@ Copyright (c) .NET Foundation. All rights reserved. - $(MSBuildThisFileDirectory)..\..\NuGet.Build.Tasks.Pack\buildCrossTargeting\NuGet.Build.Tasks.Pack.targets - $(MSBuildThisFileDirectory)..\..\NuGet.Build.Tasks.Pack\build\NuGet.Build.Tasks.Pack.targets + $(MSBuildThisFileDirectory)..\..\..\NuGet.Build.Tasks.Pack.targets true diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.PackTool.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.PackTool.targets index 6566bebb20ef..0ac75dd8f8ae 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.PackTool.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.PackTool.targets @@ -38,9 +38,14 @@ NOTE: This file is imported from the following contexts, so be aware when writin AssemblyFile="$(MicrosoftNETBuildTasksAssembly)" /> - + false + + false + + + + <_ImplicitFileBasedProgramUserSecretsId Condition="'$(FileBasedProgram)' == 'true'">$(MSBuildProjectName)-$([MSBuild]::StableStringHash($(MSBuildProjectFullPath.ToLowerInvariant()), 'Sha256')) + $(_ImplicitFileBasedProgramUserSecretsId) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.TargetFrameworkInference.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.TargetFrameworkInference.targets index 2eb7b93b4e92..7e902d066b81 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.TargetFrameworkInference.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.TargetFrameworkInference.targets @@ -54,7 +54,7 @@ Copyright (c) .NET Foundation. All rights reserved. $([MSBuild]::GetTargetFrameworkIdentifier('$(TargetFramework)')) v$([MSBuild]::GetTargetFrameworkVersion('$(TargetFramework)', 2)) - + <_TargetFrameworkVersionWithoutV>$(TargetFrameworkVersion.TrimStart('vV')) @@ -154,8 +154,8 @@ Copyright (c) .NET Foundation. All rights reserved. - - + + diff --git a/src/Tasks/sdk-tasks/GenerateRuntimeAnalyzersSWR.cs b/src/Tasks/sdk-tasks/GenerateRuntimeAnalyzersSWR.cs index f899342e578e..fd5d0e6370ba 100644 --- a/src/Tasks/sdk-tasks/GenerateRuntimeAnalyzersSWR.cs +++ b/src/Tasks/sdk-tasks/GenerateRuntimeAnalyzersSWR.cs @@ -19,11 +19,19 @@ public override bool Execute() // NOTE: Keep in sync with SdkAnalyzerAssemblyRedirector. // This is intentionally short to avoid long path problems. - const string installDir = @"DotNetRuntimeAnalyzers"; + const string installDir = @"Common7\IDE\CommonExtensions\Microsoft\DotNet"; AddFolder(sb, - @"AnalyzerRedirecting", - @"Common7\IDE\CommonExtensions\Microsoft\AnalyzerRedirecting", + "", + installDir, + filesToInclude: + [ + "metadata.json", + ]); + + AddFolder(sb, + "AnalyzerRedirecting", + @$"{installDir}\AnalyzerRedirecting", filesToInclude: [ "Microsoft.Net.Sdk.AnalyzerRedirecting.dll", @@ -32,23 +40,23 @@ public override bool Execute() ]); AddFolder(sb, - @"AspNetCoreAnalyzers", + "AspNetCoreAnalyzers", @$"{installDir}\AspNetCoreAnalyzers"); AddFolder(sb, - @"NetCoreAnalyzers", + "NetCoreAnalyzers", @$"{installDir}\NetCoreAnalyzers"); AddFolder(sb, - @"WindowsDesktopAnalyzers", + "WindowsDesktopAnalyzers", @$"{installDir}\WindowsDesktopAnalyzers"); AddFolder(sb, - @"SDKAnalyzers", + "SDKAnalyzers", @$"{installDir}\SDKAnalyzers"); AddFolder(sb, - @"WebSDKAnalyzers", + "WebSDKAnalyzers", @$"{installDir}\WebSDKAnalyzers"); File.WriteAllText(OutputFile, sb.ToString()); diff --git a/src/Tasks/sdk-tasks/ProcessRuntimeAnalyzerVersions.cs b/src/Tasks/sdk-tasks/ProcessRuntimeAnalyzerVersions.cs new file mode 100644 index 000000000000..e1f7df9dd671 --- /dev/null +++ b/src/Tasks/sdk-tasks/ProcessRuntimeAnalyzerVersions.cs @@ -0,0 +1,58 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json; + +namespace Microsoft.DotNet.Build.Tasks; + +/// +/// Extracts version numbers and saves them into a metadata file. +/// +public sealed class ProcessRuntimeAnalyzerVersions : Task +{ + [Required] + public ITaskItem[]? Inputs { get; set; } + + [Required] + public string? MetadataFilePath { get; set; } + + [Output] + public ITaskItem[]? Outputs { get; set; } + + public override bool Execute() + { + var metadata = new Dictionary(); + + // Extract version from a path like: + // ...\packs\Microsoft.NetCore.App.Ref\\analyzers\**\*.* + // The version segment is always the first segment in %(RecursiveDir). + foreach (var input in Inputs ?? []) + { + var deploymentSubpath = input.GetMetadata("DeploymentSubpath"); + var recursiveDir = input.GetMetadata("CustomRecursiveDir"); + + var slashIndex = recursiveDir.IndexOfAny('/', '\\'); + var version = recursiveDir.Substring(0, slashIndex); + var rest = recursiveDir.Substring(slashIndex + 1); + + input.SetMetadata("CustomRecursiveDir", rest); + + if (!metadata.TryGetValue(deploymentSubpath, out var existingVersion)) + { + metadata.Add(deploymentSubpath, version); + } + else if (existingVersion != version) + { + Log.LogError($"Version mismatch for '{deploymentSubpath}': '{existingVersion}' != '{version}'. " + + $"Expected only one version of '{input.GetMetadata("Identity")}'."); + return false; + } + } + + Directory.CreateDirectory(Path.GetDirectoryName(MetadataFilePath)!); + File.WriteAllText(path: MetadataFilePath!, JsonSerializer.Serialize(metadata)); + + Outputs = Inputs; + return true; + } +} diff --git a/src/Tasks/sdk-tasks/sdk-tasks.InTree.targets b/src/Tasks/sdk-tasks/sdk-tasks.InTree.targets index 0ab2824f1c38..547f8ccd7595 100644 --- a/src/Tasks/sdk-tasks/sdk-tasks.InTree.targets +++ b/src/Tasks/sdk-tasks/sdk-tasks.InTree.targets @@ -27,6 +27,7 @@ + diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.cs.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.cs.json index 78c8287035f0..055a31846996 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.cs.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.cs.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "Nejnovější hlavní verze", "symbols/RollForward/choices/disable/description": "Nedá se posunout vpřed. Vyžaduje se přesná shoda.", "symbols/RollForward/choices/disable/displayName": "Zakázat posunutí vpřed", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "Spouštěč testů, který se má použít.", + "symbols/TestRunner/displayName": "Test Runner", + "symbols/TestRunner/choices/VSTest/description": "Použít VSTest jako spouštěč testů", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Použít Microsoft.Testing.Platform jako spouštěč testů", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "Otevře global.json v editoru." } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.de.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.de.json index 3b9e4c415693..6157073e542e 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.de.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.de.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "Neueste Haupt-", "symbols/RollForward/choices/disable/description": "Kein Rollforward. Exakte Übereinstimmung erforderlich.", "symbols/RollForward/choices/disable/displayName": "Rollforward deaktivieren", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "Der zu verwendende Testausführer.", + "symbols/TestRunner/displayName": "Test Runner", + "symbols/TestRunner/choices/VSTest/description": "VSTest als Testausführer verwenden", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Microsoft.Testing.Platform als Testausführer verwenden", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "Öffnet „global.json“ im Editor." } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.es.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.es.json index 91b84990cc56..289585b96176 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.es.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.es.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "Última versión principal", "symbols/RollForward/choices/disable/description": "No se hace un reenvío. Se requiere una coincidencia exacta.", "symbols/RollForward/choices/disable/displayName": "Deshabilitar el reenvío", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "Ejecutor de pruebas que se va a usar.", + "symbols/TestRunner/displayName": "Ejecutor de pruebas", + "symbols/TestRunner/choices/VSTest/description": "Usar VSTest como ejecutor de pruebas", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Usar Microsoft.Testing.Platform como ejecutor de pruebas", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "Abre global.json el editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.fr.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.fr.json index 6a0d8074ddec..28f116ff68b3 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.fr.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.fr.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "Dernière version majeure", "symbols/RollForward/choices/disable/description": "Ne pas restaurer par progression vers l’avant. Correspondance exacte requise.", "symbols/RollForward/choices/disable/displayName": "Désactiver la restauration par progression", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "Le moteur de test à utiliser.", + "symbols/TestRunner/displayName": "Test Runner", + "symbols/TestRunner/choices/VSTest/description": "Utilisez VSTest comme moteur de test", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Utiliser Microsoft.Testing.Platform comme moteur de test", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "Ouvre global.json dans l’éditeur" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.it.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.it.json index 1955f5986f6b..2a62d6bc5c55 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.it.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.it.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "Valore principale più recente", "symbols/RollForward/choices/disable/description": "Non esegue il roll-forward. Corrispondenza esatta richiesta.", "symbols/RollForward/choices/disable/displayName": "Disabilita roll-forward", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "Test runner da usare.", + "symbols/TestRunner/displayName": "Test Runner", + "symbols/TestRunner/choices/VSTest/description": "Usa VSTest come test runner", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Usa Microsoft.Testing.Platform come test runner", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "Apre global.json nell'editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ja.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ja.json index dd3c5bfae900..35c290f8b8fd 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ja.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ja.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "最新のメジャー", "symbols/RollForward/choices/disable/description": "ロールフォワードしません。完全に一致する必要があります。", "symbols/RollForward/choices/disable/displayName": "ロールフォワードの無効化", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "使用するテスト ランナーです。", + "symbols/TestRunner/displayName": "Test Runner", + "symbols/TestRunner/choices/VSTest/description": "VSTest をテスト ランナーとして使用する", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Microsoft.Testing.Platform をテスト ランナーとして使用する", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "エディターで global.json を開く" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ko.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ko.json index 63603d9e76c0..79c43a896860 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ko.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ko.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "최신 주", "symbols/RollForward/choices/disable/description": "롤포워드하지 않습니다. 정확히 일치해야 합니다.", "symbols/RollForward/choices/disable/displayName": "롤포워드 비활성화", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "사용할 Test Runner입니다.", + "symbols/TestRunner/displayName": "Test Runner", + "symbols/TestRunner/choices/VSTest/description": "VSTest를 Test Runner로 사용", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Microsoft.Testing.Platform을 Test Runner로 사용", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "편집기에서 global.json을 엽니다" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.pl.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.pl.json index 52731300fa71..a7239081f6e8 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.pl.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.pl.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "Najnowszy przedział główny", "symbols/RollForward/choices/disable/description": "Nie przechodzi. Wymagane jest dokładne dopasowanie.", "symbols/RollForward/choices/disable/displayName": "Wyłącz przechodzenie", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "Moduł uruchamiający testy do użycia.", + "symbols/TestRunner/displayName": "Moduł uruchamiający", + "symbols/TestRunner/choices/VSTest/description": "Używa narzędzia VSTest jako modułu uruchamiającego testy", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Użyj elementu Microsoft.Testing.Platform jako modułu uruchamiającego testy", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "Otwiera plik global.json w edytorze" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.pt-BR.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.pt-BR.json index 5ac94d837da2..55a2663ecced 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.pt-BR.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.pt-BR.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "Principal mais recente", "symbols/RollForward/choices/disable/description": "Não rola para frente. Correspondência exata necessária.", "symbols/RollForward/choices/disable/displayName": "Desabilitar rolar para frente", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "O executor de teste a ser usado.", + "symbols/TestRunner/displayName": "Executor de teste", + "symbols/TestRunner/choices/VSTest/description": "Usar VSTest como executor de teste", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Usar Microsoft.Testing.Platform como executor de teste", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "Abrir global.json no editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ru.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ru.json index fea09bc51165..a07f42658c92 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ru.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.ru.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "Последняя основная версия", "symbols/RollForward/choices/disable/description": "Накат не выполняется. Требуется точное совпадение.", "symbols/RollForward/choices/disable/displayName": "Отключить накат", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "Средство выполнения тестов для использования.", + "symbols/TestRunner/displayName": "Средство выполнения тестов", + "symbols/TestRunner/choices/VSTest/description": "Использовать VSTest в качестве средства выполнения тестов", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Использовать Microsoft.Testing.Platform в качестве средства выполнения тестов", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "Открывает файл global.json в редакторе" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.tr.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.tr.json index 4ccff9c7d878..74048234a66c 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.tr.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.tr.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "Son birincil sürüm", "symbols/RollForward/choices/disable/description": "İleri sarılmıyor. Tam eşleşme gerekiyor.", "symbols/RollForward/choices/disable/displayName": "İleri sarma devre dışı bırakıldı", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "Kullanılacak test çalıştırıcısı.", + "symbols/TestRunner/displayName": "Test çalıştırıcı", + "symbols/TestRunner/choices/VSTest/description": "Test çalıştırıcısı olarak VSTest'i kullanın", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Test çalıştırıcı olarak Microsoft.Testing.Platform'u kullanın", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "Düzenleyicide global.json dosyasını açar" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.zh-Hans.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.zh-Hans.json index cd4c9cb8e84f..5030c3598283 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.zh-Hans.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.zh-Hans.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "最新主要版本", "symbols/RollForward/choices/disable/description": "不要前滚。需要完全匹配。", "symbols/RollForward/choices/disable/displayName": "禁用前滚", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "要使用的测试运行程序。", + "symbols/TestRunner/displayName": "Test Runner", + "symbols/TestRunner/choices/VSTest/description": "使用 VSTest 作为测试运行程序", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "使用 Microsoft.Testing.Platform 作为测试运行程序", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "在编辑器中打开 global.json" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.zh-Hant.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.zh-Hant.json index 169fa5fd8f29..a459d69f8807 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.zh-Hant.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/GlobalJson/.template.config/localize/templatestrings.zh-Hant.json @@ -25,11 +25,11 @@ "symbols/RollForward/choices/latestMajor/displayName": "最新主要", "symbols/RollForward/choices/disable/description": "不向前復原。需要完全相符。", "symbols/RollForward/choices/disable/displayName": "停用向前復原", - "symbols/TestRunner/description": "The test runner to use.", - "symbols/TestRunner/displayName": "Test runner", - "symbols/TestRunner/choices/VSTest/description": "Use VSTest as test runner", + "symbols/TestRunner/description": "要使用的測試執行器。", + "symbols/TestRunner/displayName": "測試執行器", + "symbols/TestRunner/choices/VSTest/description": "使用 VSTest 作為測試執行器", "symbols/TestRunner/choices/VSTest/displayName": "VSTest", - "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "Use Microsoft.Testing.Platform as test runner", + "symbols/TestRunner/choices/Microsoft.Testing.Platform/description": "使用 Microsoft.Testing.Platform 測試執行器", "symbols/TestRunner/choices/Microsoft.Testing.Platform/displayName": "Microsoft.Testing.Platform", "postActions/open-file/description": "在編輯器中開啟 global.json" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.cs.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.cs.json index a84a213c397a..dbdc72f5048d 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.cs.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.cs.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Metoda testovacího přípravku TestCleanup", "postActions/restoreNugetPackages/description": "Obnoví balíčky NuGet vyžadované tímto projektem.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Spustit dotnet restore", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Vytvořte nebo aktualizujte soubor global.json vyžadovaný Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Ručně aktualizujte nebo vytvořte soubor global.json, který nastaví spouštěč testů na Microsoft.Testing.Platform", "postActions/openInEditor/description": "Otevře Test1.cs v editoru." } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.de.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.de.json index 6ce2fa04d6bb..827485d01a68 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.de.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.de.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Fixierungsmethode \"TestCleanup\"", "postActions/restoreNugetPackages/description": "Stellt die NuGet-Pakete wieder her, die für dieses Projekt erforderlich sind.", "postActions/restoreNugetPackages/manualInstructions/default/text": "\"dotnet restore\" ausführen", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Erstellen oder aktualisieren Sie die für die Microsoft.Testing.Platform erforderliche Datei „global.json“.", + "postActions/addJsonProperty/manualInstructions/default/text": "Aktualisieren oder erstellen Sie die Datei „global.json“ manuell, um den Testausführer auf Microsoft.Testing.Platform einzustellen.", "postActions/openInEditor/description": "Öffnet Test1.cs im Editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.es.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.es.json index 79f6e8b7ed5f..c35eccac0856 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.es.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.es.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Método de accesorio TestCleanup", "postActions/restoreNugetPackages/description": "Restaure los paquetes NuGet necesarios para este proyecto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Ejecutar \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Cree o actualice el archivo \"global.json\" requerido por Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Actualizar o crear manualmente el archivo \"global.json\" estableciendo el ejecutor de pruebas en Microsoft.Testing.Platform", "postActions/openInEditor/description": "Abrir Test1.cs en el editor." } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.fr.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.fr.json index 53fe20ff3650..33a3f59420f6 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.fr.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.fr.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Méthode de fixture TestCleanup", "postActions/restoreNugetPackages/description": "Restaurez les packages NuGet requis par ce projet.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Exécutez « dotnet restore »", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Créez ou mettez à jour le fichier « global.json » requis par Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Mettez à jour ou créez manuellement le fichier « global.json » en définissant l’exécuteur de tests sur Microsoft.Testing.Platform", "postActions/openInEditor/description": "Ouvre Test1.cs dans l’éditeur" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.it.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.it.json index f0bbe81b6081..385d5e56fdb7 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.it.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.it.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Metodo fixture TestCleanup", "postActions/restoreNugetPackages/description": "Ripristina i pacchetti NuGet richiesti da questo progetto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Esegui 'dotnet restore'", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Creare o aggiornare il file 'global.json' richiesto da Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Aggiornare o creare manualmente il file 'global.json' impostando il test runner su Microsoft.Testing.Platform", "postActions/openInEditor/description": "Apre Test1.cs nell'editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ja.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ja.json index 8abec526fba5..5e9ca4b3ba89 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ja.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ja.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup フィクスチャ メソッド", "postActions/restoreNugetPackages/description": "このプロジェクトに必要な NuGet パッケージを復元します。", "postActions/restoreNugetPackages/manualInstructions/default/text": "'dotnet restore' を実行する", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform に必要な 'global.json' ファイルを作成または更新します。", + "postActions/addJsonProperty/manualInstructions/default/text": "テスト ランナーを Microsoft.Testing.Platform に設定する 'global.json' ファイルを手動で更新または作成する", "postActions/openInEditor/description": "エディターで Test1.cs を開きます" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ko.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ko.json index 7b5233ccbc18..a0c1a01a2d67 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ko.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ko.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup fixture 메서드", "postActions/restoreNugetPackages/description": "이 프로젝트에 필요한 NuGet 패키지를 복원합니다.", "postActions/restoreNugetPackages/manualInstructions/default/text": "'dotnet restore' 실행", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform에 필요한 'global.json' 파일을 만들거나 업데이트합니다.", + "postActions/addJsonProperty/manualInstructions/default/text": "테스트 실행기를 Microsoft.Testing.Platform으로 설정하는 'global.json' 파일을 수동으로 만들거나 업데이트합니다.", "postActions/openInEditor/description": "편집기에서 Test1.cs 열기" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.pl.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.pl.json index bca7305e53d9..5bda1965559b 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.pl.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.pl.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup — metoda początkowa", "postActions/restoreNugetPackages/description": "Przywróć pakiety NuGet wymagane przez ten projekt.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Uruchom polecenie „dotnet restore”", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Utwórz lub zaktualizuj plik „global.json” wymagany przez Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Ręcznie zaktualizuj lub utwórz plik „global.json” ustawiając moduł uruchamiający testy na Microsoft.Testing.Platform", "postActions/openInEditor/description": "Otwiera plik Test1.cs w edytorze" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.pt-BR.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.pt-BR.json index e97e6205db55..a14db3f6cfe2 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.pt-BR.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.pt-BR.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Método de acessório TestCleanup", "postActions/restoreNugetPackages/description": "Restaura os pacotes do NuGet exigidos por este projeto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Executa \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Criar ou atualizar o arquivo 'global.json' necessário para o Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Atualize ou crie manualmente o arquivo 'global.json', definindo o test runner como Microsoft.Testing.Platform", "postActions/openInEditor/description": "Abre o Test1.cs no editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ru.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ru.json index b3bd988269a7..e677a9bfed40 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ru.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.ru.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Метод работы со средствами TestCleanup", "postActions/restoreNugetPackages/description": "Восстановление пакетов NuGet, необходимых для этого проекта.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Выполнить команду \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Создайте или обновите файл global.json, необходимый для Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Вручную обновите или создайте файл global.json, настраивающий Microsoft.Testing.Platform в качестве средства выполнения тестов", "postActions/openInEditor/description": "Открывает Test1.cs редакторе" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.tr.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.tr.json index e09e1d1eb151..c2710ec296de 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.tr.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.tr.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup düzen yöntemi", "postActions/restoreNugetPackages/description": "Bu projenin gerektirdiği NuGet paketlerini geri yükleyin.", "postActions/restoreNugetPackages/manualInstructions/default/text": "\"dotnet restore\" çalıştır", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform tarafından gerekli olan ‘global.json’ dosyasını oluşturun veya güncelleştirin.", + "postActions/addJsonProperty/manualInstructions/default/text": "Test çalıştırıcısını Microsoft.Testing.Platform olarak ayarlayarak ‘global.json’ dosyasını manuel olarak güncelleştirin veya oluşturun.", "postActions/openInEditor/description": "Test1.cs'yi düzenleyicide açar" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.zh-Hans.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.zh-Hans.json index a7fda34f2294..8a8bbfb8339c 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.zh-Hans.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.zh-Hans.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup 固定例程方法", "postActions/restoreNugetPackages/description": "还原此项目所需的 NuGet 包。", "postActions/restoreNugetPackages/manualInstructions/default/text": "运行 \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "创建或更新 Microsoft.Testing.Platform 所需的 \"global.json\" 文件。", + "postActions/addJsonProperty/manualInstructions/default/text": "手动更新或创建 \"global.json\" 文件,将测试运行程序设置为 Microsoft.Testing.Platform", "postActions/openInEditor/description": "在编辑器中打开 Test1.cs" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.zh-Hant.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.zh-Hant.json index 51fc6105a344..e677ca36d9a4 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.zh-Hant.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/localize/templatestrings.zh-Hant.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup 固件方法", "postActions/restoreNugetPackages/description": "還原此專案所需的 NuGet 套件。", "postActions/restoreNugetPackages/manualInstructions/default/text": "執行 'dotnet restore'", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "建立或更新 Microsoft.Testing.Platform 所需的 'global.json' 檔案。", + "postActions/addJsonProperty/manualInstructions/default/text": "手動更新或建立 'global.json' 檔案,將測試執行器設定為 Microsoft.Testing.Platform", "postActions/openInEditor/description": "在編輯器中開啟 Test1.cs" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/template.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/template.json index 01977ec20d99..642d2f3b5a19 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/template.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/.template.config/template.json @@ -240,7 +240,9 @@ "parentPropertyPath": "test", "newJsonPropertyName": "runner", "newJsonPropertyValue": "Microsoft.Testing.Platform", - "detectRepositoryRootForFileCreation": true + "detectRepositoryRoot": true, + "includeAllDirectoriesInSearch": false, + "includeAllParentDirectoriesInSearch": true }, "continueOnError": true }, diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/Company.TestProject1.csproj b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/Company.TestProject1.csproj index a8c889eafa29..b4490a1d30fe 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/Company.TestProject1.csproj +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-CSharp/Company.TestProject1.csproj @@ -1,5 +1,5 @@  - + net10.0 @@ -43,7 +43,7 @@ - + diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.cs.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.cs.json index b7ff6b32d45f..044ee2de540a 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.cs.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.cs.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Metoda testovacího přípravku TestCleanup", "postActions/restoreNugetPackages/description": "Obnoví balíčky NuGet vyžadované tímto projektem.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Spustit dotnet restore", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Vytvořte nebo aktualizujte soubor global.json vyžadovaný Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Ručně aktualizujte nebo vytvořte soubor global.json, který nastaví spouštěč testů na Microsoft.Testing.Platform", "postActions/openInEditor/description": "Otevře Test1.fs v editoru." } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.de.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.de.json index 83a5669b3e1b..d63f705d430c 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.de.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.de.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Fixierungsmethode \"TestCleanup\"", "postActions/restoreNugetPackages/description": "Stellt die NuGet-Pakete wieder her, die für dieses Projekt erforderlich sind.", "postActions/restoreNugetPackages/manualInstructions/default/text": "\"dotnet restore\" ausführen", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Erstellen oder aktualisieren Sie die für die Microsoft.Testing.Platform erforderliche Datei „global.json“.", + "postActions/addJsonProperty/manualInstructions/default/text": "Aktualisieren oder erstellen Sie die Datei „global.json“ manuell, um den Testausführer auf Microsoft.Testing.Platform einzustellen.", "postActions/openInEditor/description": "Öffnet Test1.fs im Editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.es.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.es.json index 1e9575a5346f..e8453105b8d7 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.es.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.es.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Método de accesorio TestCleanup", "postActions/restoreNugetPackages/description": "Restaure los paquetes NuGet necesarios para este proyecto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Ejecutar \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Cree o actualice el archivo \"global.json\" requerido por Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Actualizar o crear manualmente el archivo \"global.json\" estableciendo el ejecutor de pruebas en Microsoft.Testing.Platform", "postActions/openInEditor/description": "Abrir Test1.fs en el editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.fr.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.fr.json index 8c0eb4a05506..5d164b74452d 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.fr.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.fr.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Méthode de fixture TestCleanup", "postActions/restoreNugetPackages/description": "Restaurez les packages NuGet requis par ce projet.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Exécutez « dotnet restore »", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Créez ou mettez à jour le fichier « global.json » requis par Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Mettez à jour ou créez manuellement le fichier « global.json » en définissant l’exécuteur de tests sur Microsoft.Testing.Platform", "postActions/openInEditor/description": "Ouvre Test1.fs dans l’éditeur" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.it.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.it.json index db8b42b1f617..58a28f206074 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.it.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.it.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Metodo fixture TestCleanup", "postActions/restoreNugetPackages/description": "Ripristina i pacchetti NuGet richiesti da questo progetto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Esegui 'dotnet restore'", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Creare o aggiornare il file 'global.json' richiesto da Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Aggiornare o creare manualmente il file 'global.json' impostando il test runner su Microsoft.Testing.Platform", "postActions/openInEditor/description": "Apre Test1.fs nell'editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ja.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ja.json index 93ecd87fadd3..843eb3d0de87 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ja.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ja.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup フィクスチャ メソッド", "postActions/restoreNugetPackages/description": "このプロジェクトに必要な NuGet パッケージを復元します。", "postActions/restoreNugetPackages/manualInstructions/default/text": "'dotnet restore' を実行する", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform に必要な 'global.json' ファイルを作成または更新します。", + "postActions/addJsonProperty/manualInstructions/default/text": "テスト ランナーを Microsoft.Testing.Platform に設定する 'global.json' ファイルを手動で更新または作成する", "postActions/openInEditor/description": "エディターで Test1.fs を開きます" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ko.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ko.json index 4d43f0445d48..6d0c6975eff3 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ko.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ko.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup fixture 메서드", "postActions/restoreNugetPackages/description": "이 프로젝트에 필요한 NuGet 패키지를 복원합니다.", "postActions/restoreNugetPackages/manualInstructions/default/text": "'dotnet restore' 실행", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform에 필요한 'global.json' 파일을 만들거나 업데이트합니다.", + "postActions/addJsonProperty/manualInstructions/default/text": "테스트 실행기를 Microsoft.Testing.Platform으로 설정하는 'global.json' 파일을 수동으로 만들거나 업데이트합니다.", "postActions/openInEditor/description": "편집기에서 Test1.fs 열기" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.pl.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.pl.json index 0562966c9b78..1d42137802a6 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.pl.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.pl.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup — metoda początkowa", "postActions/restoreNugetPackages/description": "Przywróć pakiety NuGet wymagane przez ten projekt.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Uruchom polecenie „dotnet restore”", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Utwórz lub zaktualizuj plik „global.json” wymagany przez Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Ręcznie zaktualizuj lub utwórz plik „global.json” ustawiając moduł uruchamiający testy na Microsoft.Testing.Platform", "postActions/openInEditor/description": "Otwiera plik Test1.fs w edytorze" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.pt-BR.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.pt-BR.json index 1e0e1f8c92d2..f7d966d58048 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.pt-BR.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.pt-BR.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Método de acessório TestCleanup", "postActions/restoreNugetPackages/description": "Restaura os pacotes do NuGet exigidos por este projeto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Executa \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Criar ou atualizar o arquivo 'global.json' necessário para o Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Atualize ou crie manualmente o arquivo 'global.json', definindo o test runner como Microsoft.Testing.Platform", "postActions/openInEditor/description": "Abre o Test1.fs no editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ru.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ru.json index d82d72c10c99..c1037df6c803 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ru.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.ru.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Метод работы со средствами TestCleanup", "postActions/restoreNugetPackages/description": "Восстановление пакетов NuGet, необходимых для этого проекта.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Выполнить команду \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Создайте или обновите файл global.json, необходимый для Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Вручную обновите или создайте файл global.json, настраивающий Microsoft.Testing.Platform в качестве средства выполнения тестов", "postActions/openInEditor/description": "Открывает Test1.fs в редакторе" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.tr.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.tr.json index 1cd7b63e0a39..f4f8ad417a2e 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.tr.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.tr.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup düzen yöntemi", "postActions/restoreNugetPackages/description": "Bu projenin gerektirdiği NuGet paketlerini geri yükleyin.", "postActions/restoreNugetPackages/manualInstructions/default/text": "\"dotnet restore\" çalıştır", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform tarafından gerekli olan ‘global.json’ dosyasını oluşturun veya güncelleştirin.", + "postActions/addJsonProperty/manualInstructions/default/text": "Test çalıştırıcısını Microsoft.Testing.Platform olarak ayarlayarak ‘global.json’ dosyasını manuel olarak güncelleştirin veya oluşturun.", "postActions/openInEditor/description": "Test1.fs'yi düzenleyicide açar" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.zh-Hans.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.zh-Hans.json index bf7797f48535..64ab47000ce2 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.zh-Hans.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.zh-Hans.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup 固定例程方法", "postActions/restoreNugetPackages/description": "还原此项目所需的 NuGet 包。", "postActions/restoreNugetPackages/manualInstructions/default/text": "运行 \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "创建或更新 Microsoft.Testing.Platform 所需的 \"global.json\" 文件。", + "postActions/addJsonProperty/manualInstructions/default/text": "手动更新或创建 \"global.json\" 文件,将测试运行程序设置为 Microsoft.Testing.Platform", "postActions/openInEditor/description": "在编辑器中打开 Test1.fs" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.zh-Hant.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.zh-Hant.json index 0f55f462362c..838f19ee002d 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.zh-Hant.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/localize/templatestrings.zh-Hant.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup 固件方法", "postActions/restoreNugetPackages/description": "還原此專案所需的 NuGet 套件。", "postActions/restoreNugetPackages/manualInstructions/default/text": "執行 'dotnet restore'", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "建立或更新 Microsoft.Testing.Platform 所需的 'global.json' 檔案。", + "postActions/addJsonProperty/manualInstructions/default/text": "手動更新或建立 'global.json' 檔案,將測試執行器設定為 Microsoft.Testing.Platform", "postActions/openInEditor/description": "在編輯器中開啟 Test1.fs" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/template.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/template.json index 022ffe70b9cf..ed388b0b28da 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/template.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/.template.config/template.json @@ -240,7 +240,9 @@ "parentPropertyPath": "test", "newJsonPropertyName": "runner", "newJsonPropertyValue": "Microsoft.Testing.Platform", - "detectRepositoryRootForFileCreation": true + "detectRepositoryRoot": true, + "includeAllDirectoriesInSearch": false, + "includeAllParentDirectoriesInSearch": true }, "continueOnError": true }, diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/Company.TestProject1.fsproj b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/Company.TestProject1.fsproj index 40e22d8c07c0..a1b85e78555b 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/Company.TestProject1.fsproj +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-FSharp/Company.TestProject1.fsproj @@ -1,5 +1,5 @@ - + net10.0 @@ -48,7 +48,7 @@ - + diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.cs.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.cs.json index d51fe797a198..7fc05eea04bf 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.cs.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.cs.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Metoda testovacího přípravku TestCleanup", "postActions/restoreNugetPackages/description": "Obnoví balíčky NuGet vyžadované tímto projektem.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Spustit dotnet restore", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Vytvořte nebo aktualizujte soubor global.json vyžadovaný Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Ručně aktualizujte nebo vytvořte soubor global.json, který nastaví spouštěč testů na Microsoft.Testing.Platform", "postActions/openInEditor/description": "Otevře Test1.vb v editoru." } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.de.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.de.json index 91296abf8c4b..b75b7acc6f3a 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.de.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.de.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Fixierungsmethode \"TestCleanup\"", "postActions/restoreNugetPackages/description": "Stellt die NuGet-Pakete wieder her, die für dieses Projekt erforderlich sind.", "postActions/restoreNugetPackages/manualInstructions/default/text": "\"dotnet restore\" ausführen", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Erstellen oder aktualisieren Sie die für die Microsoft.Testing.Platform erforderliche Datei „global.json“.", + "postActions/addJsonProperty/manualInstructions/default/text": "Aktualisieren oder erstellen Sie die Datei „global.json“ manuell, um den Testausführer auf Microsoft.Testing.Platform einzustellen.", "postActions/openInEditor/description": "Öffnet Test1.vb im Editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.es.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.es.json index 26c232daac1e..5975b418d535 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.es.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.es.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Método de accesorio TestCleanup", "postActions/restoreNugetPackages/description": "Restaure los paquetes NuGet necesarios para este proyecto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Ejecutar \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Cree o actualice el archivo \"global.json\" requerido por Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Actualizar o crear manualmente el archivo \"global.json\" estableciendo el ejecutor de pruebas en Microsoft.Testing.Platform", "postActions/openInEditor/description": "Abrir Test1.vb en el editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.fr.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.fr.json index 38dd553aa8a8..dc3b33577aa9 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.fr.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.fr.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Méthode de fixture TestCleanup", "postActions/restoreNugetPackages/description": "Restaurez les packages NuGet requis par ce projet.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Exécutez « dotnet restore »", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Créez ou mettez à jour le fichier « global.json » requis par Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Mettez à jour ou créez manuellement le fichier « global.json » en définissant l’exécuteur de tests sur Microsoft.Testing.Platform", "postActions/openInEditor/description": "Ouvre Test1.vb dans l’éditeur" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.it.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.it.json index d90ba815124c..6d460d9c878f 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.it.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.it.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Metodo fixture TestCleanup", "postActions/restoreNugetPackages/description": "Ripristina i pacchetti NuGet richiesti da questo progetto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Esegui 'dotnet restore'", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Creare o aggiornare il file 'global.json' richiesto da Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Aggiornare o creare manualmente il file 'global.json' impostando il test runner su Microsoft.Testing.Platform", "postActions/openInEditor/description": "Apre Test1.vb nell'editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ja.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ja.json index 1e5086d83fa6..b633c77f31e8 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ja.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ja.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup フィクスチャ メソッド", "postActions/restoreNugetPackages/description": "このプロジェクトに必要な NuGet パッケージを復元します。", "postActions/restoreNugetPackages/manualInstructions/default/text": "'dotnet restore' を実行する", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform に必要な 'global.json' ファイルを作成または更新します。", + "postActions/addJsonProperty/manualInstructions/default/text": "テスト ランナーを Microsoft.Testing.Platform に設定する 'global.json' ファイルを手動で更新または作成する", "postActions/openInEditor/description": "エディターで Test1.vb を開きます" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ko.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ko.json index b9023d907f62..ecb7b8cfc7e3 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ko.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ko.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup fixture 메서드", "postActions/restoreNugetPackages/description": "이 프로젝트에 필요한 NuGet 패키지를 복원합니다.", "postActions/restoreNugetPackages/manualInstructions/default/text": "'dotnet restore' 실행", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform에 필요한 'global.json' 파일을 만들거나 업데이트합니다.", + "postActions/addJsonProperty/manualInstructions/default/text": "테스트 실행기를 Microsoft.Testing.Platform으로 설정하는 'global.json' 파일을 수동으로 만들거나 업데이트합니다.", "postActions/openInEditor/description": "편집기에서 Test1.vb 열기" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.pl.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.pl.json index 2df4f1064515..b27d9229cbde 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.pl.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.pl.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup — metoda początkowa", "postActions/restoreNugetPackages/description": "Przywróć pakiety NuGet wymagane przez ten projekt.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Uruchom polecenie „dotnet restore”", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Utwórz lub zaktualizuj plik „global.json” wymagany przez Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Ręcznie zaktualizuj lub utwórz plik „global.json” ustawiając moduł uruchamiający testy na Microsoft.Testing.Platform", "postActions/openInEditor/description": "Otwiera plik Test1.vb w edytorze" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.pt-BR.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.pt-BR.json index b0311cbc2cac..a9207ecaa0fc 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.pt-BR.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.pt-BR.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Método de acessório TestCleanup", "postActions/restoreNugetPackages/description": "Restaura os pacotes do NuGet exigidos por este projeto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Executa \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Criar ou atualizar o arquivo 'global.json' necessário para o Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Atualize ou crie manualmente o arquivo 'global.json', definindo o test runner como Microsoft.Testing.Platform", "postActions/openInEditor/description": "Abre o Test1.vb no editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ru.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ru.json index b5a84d8cd027..a9c905169569 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ru.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.ru.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Метод работы со средствами TestCleanup", "postActions/restoreNugetPackages/description": "Восстановление пакетов NuGet, необходимых для этого проекта.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Выполнить команду \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Создайте или обновите файл global.json, необходимый для Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Вручную обновите или создайте файл global.json, настраивающий Microsoft.Testing.Platform в качестве средства выполнения тестов", "postActions/openInEditor/description": "Открывает Test1.vb в редакторе" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.tr.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.tr.json index 88d9f7cd1761..7a131994c2bc 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.tr.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.tr.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup düzen yöntemi", "postActions/restoreNugetPackages/description": "Bu projenin gerektirdiği NuGet paketlerini geri yükleyin.", "postActions/restoreNugetPackages/manualInstructions/default/text": "\"dotnet restore\" çalıştır", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform tarafından gerekli olan ‘global.json’ dosyasını oluşturun veya güncelleştirin.", + "postActions/addJsonProperty/manualInstructions/default/text": "Test çalıştırıcısını Microsoft.Testing.Platform olarak ayarlayarak ‘global.json’ dosyasını manuel olarak güncelleştirin veya oluşturun.", "postActions/openInEditor/description": "Test1.vb'yi düzenleyicide açar" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.zh-Hans.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.zh-Hans.json index 53b04ad750ca..c0c1696f53c4 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.zh-Hans.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.zh-Hans.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup 固定例程方法", "postActions/restoreNugetPackages/description": "还原此项目所需的 NuGet 包。", "postActions/restoreNugetPackages/manualInstructions/default/text": "运行 \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "创建或更新 Microsoft.Testing.Platform 所需的 \"global.json\" 文件。", + "postActions/addJsonProperty/manualInstructions/default/text": "手动更新或创建 \"global.json\" 文件,将测试运行程序设置为 Microsoft.Testing.Platform", "postActions/openInEditor/description": "在编辑器中打开 Test1.vb" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.zh-Hant.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.zh-Hant.json index 9a74906310ef..3aefbbe58e9f 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.zh-Hant.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/localize/templatestrings.zh-Hant.json @@ -52,7 +52,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup 固件方法", "postActions/restoreNugetPackages/description": "還原此專案所需的 NuGet 套件。", "postActions/restoreNugetPackages/manualInstructions/default/text": "執行 'dotnet restore'", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "建立或更新 Microsoft.Testing.Platform 所需的 'global.json' 檔案。", + "postActions/addJsonProperty/manualInstructions/default/text": "手動更新或建立 'global.json' 檔案,將測試執行器設定為 Microsoft.Testing.Platform", "postActions/openInEditor/description": "在編輯器中開啟 Test1.vb" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/template.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/template.json index cca88e73c3cd..a110969e6973 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/template.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/.template.config/template.json @@ -240,7 +240,9 @@ "parentPropertyPath": "test", "newJsonPropertyName": "runner", "newJsonPropertyValue": "Microsoft.Testing.Platform", - "detectRepositoryRootForFileCreation": true + "detectRepositoryRoot": true, + "includeAllDirectoriesInSearch": false, + "includeAllParentDirectoriesInSearch": true }, "continueOnError": true }, diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/Company.TestProject1.vbproj b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/Company.TestProject1.vbproj index a8c889eafa29..b4490a1d30fe 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/Company.TestProject1.vbproj +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/MSTest-VisualBasic/Company.TestProject1.vbproj @@ -1,5 +1,5 @@  - + net10.0 @@ -43,7 +43,7 @@ - + diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.cs.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.cs.json index 70f943fbeceb..648f92bd3831 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.cs.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.cs.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Metoda testovacího přípravku TestCleanup", "postActions/restoreNugetPackages/description": "Obnoví balíčky NuGet vyžadované tímto projektem.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Spustit dotnet restore", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Vytvořte nebo aktualizujte soubor global.json vyžadovaný Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Ručně aktualizujte nebo vytvořte soubor global.json, který nastaví spouštěč testů na Microsoft.Testing.Platform", "postActions/openInEditor/description": "Otevře Test1.cs v editoru." } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.de.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.de.json index a033b36b85f2..7f9ccea06133 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.de.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.de.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Fixierungsmethode \"TestCleanup\"", "postActions/restoreNugetPackages/description": "Stellt die NuGet-Pakete wieder her, die für dieses Projekt erforderlich sind.", "postActions/restoreNugetPackages/manualInstructions/default/text": "\"dotnet restore\" ausführen", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Erstellen oder aktualisieren Sie die für die Microsoft.Testing.Platform erforderliche Datei „global.json“.", + "postActions/addJsonProperty/manualInstructions/default/text": "Aktualisieren oder erstellen Sie die Datei „global.json“ manuell, um den Testausführer auf Microsoft.Testing.Platform einzustellen.", "postActions/openInEditor/description": "Öffnet Test1.cs im Editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.es.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.es.json index dafa0d5fcefb..64561fd033d4 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.es.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.es.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Método de accesorio TestCleanup", "postActions/restoreNugetPackages/description": "Restaure los paquetes NuGet necesarios para este proyecto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Ejecutar \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Cree o actualice el archivo \"global.json\" requerido por Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Actualizar o crear manualmente el archivo \"global.json\" estableciendo el ejecutor de pruebas en Microsoft.Testing.Platform", "postActions/openInEditor/description": "Abrir Test1.cs en el editor." } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.fr.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.fr.json index a42ccf5c7cde..ea1459b7e9ab 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.fr.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.fr.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Méthode de fixture TestCleanup", "postActions/restoreNugetPackages/description": "Restaurez les packages NuGet requis par ce projet.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Exécutez « dotnet restore »", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Créez ou mettez à jour le fichier « global.json » requis par Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Mettez à jour ou créez manuellement le fichier « global.json » en définissant l’exécuteur de tests sur Microsoft.Testing.Platform", "postActions/openInEditor/description": "Ouvre Test1.cs dans l’éditeur" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.it.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.it.json index 3cfad9d1ecc3..5acc742cb7af 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.it.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.it.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Metodo fixture TestCleanup", "postActions/restoreNugetPackages/description": "Ripristina i pacchetti NuGet richiesti da questo progetto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Esegui 'dotnet restore'", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Creare o aggiornare il file 'global.json' richiesto da Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Aggiornare o creare manualmente il file 'global.json' impostando il test runner su Microsoft.Testing.Platform", "postActions/openInEditor/description": "Apre Test1.cs nell'editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ja.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ja.json index 0cffb118b6ed..00caa4fbecee 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ja.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ja.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup フィクスチャ メソッド", "postActions/restoreNugetPackages/description": "このプロジェクトに必要な NuGet パッケージを復元します。", "postActions/restoreNugetPackages/manualInstructions/default/text": "'dotnet restore' を実行する", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform に必要な 'global.json' ファイルを作成または更新します。", + "postActions/addJsonProperty/manualInstructions/default/text": "テスト ランナーを Microsoft.Testing.Platform に設定する 'global.json' ファイルを手動で更新または作成する", "postActions/openInEditor/description": "エディターで Test1.cs を開きます" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ko.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ko.json index 04e74ff275e0..91eb4cc23954 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ko.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ko.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup fixture 메서드", "postActions/restoreNugetPackages/description": "이 프로젝트에 필요한 NuGet 패키지를 복원합니다.", "postActions/restoreNugetPackages/manualInstructions/default/text": "'dotnet restore' 실행", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform에 필요한 'global.json' 파일을 만들거나 업데이트합니다.", + "postActions/addJsonProperty/manualInstructions/default/text": "테스트 실행기를 Microsoft.Testing.Platform으로 설정하는 'global.json' 파일을 수동으로 만들거나 업데이트합니다.", "postActions/openInEditor/description": "편집기에서 Test1.cs 열기" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.pl.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.pl.json index 7822568a090c..be9450819ab2 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.pl.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.pl.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup — metoda początkowa", "postActions/restoreNugetPackages/description": "Przywróć pakiety NuGet wymagane przez ten projekt.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Uruchom polecenie „dotnet restore”", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Utwórz lub zaktualizuj plik „global.json” wymagany przez Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Ręcznie zaktualizuj lub utwórz plik „global.json” ustawiając moduł uruchamiający testy na Microsoft.Testing.Platform", "postActions/openInEditor/description": "Otwiera plik Test1.cs w edytorze" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.pt-BR.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.pt-BR.json index b16e73aaff84..10f54ae4f26c 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.pt-BR.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.pt-BR.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Método de acessório TestCleanup", "postActions/restoreNugetPackages/description": "Restaura os pacotes do NuGet exigidos por este projeto.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Executa \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Criar ou atualizar o arquivo 'global.json' necessário para o Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Atualize ou crie manualmente o arquivo 'global.json', definindo o test runner como Microsoft.Testing.Platform", "postActions/openInEditor/description": "Abre o Test1.cs no editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ru.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ru.json index 62dd753e1e66..1651693ff9ee 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ru.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.ru.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "Метод работы со средствами TestCleanup", "postActions/restoreNugetPackages/description": "Восстановление пакетов NuGet, необходимых для этого проекта.", "postActions/restoreNugetPackages/manualInstructions/default/text": "Выполнить команду \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Создайте или обновите файл global.json, необходимый для Microsoft.Testing.Platform.", + "postActions/addJsonProperty/manualInstructions/default/text": "Вручную обновите или создайте файл global.json, настраивающий Microsoft.Testing.Platform в качестве средства выполнения тестов", "postActions/openInEditor/description": "Открывает Test1.cs редакторе" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.tr.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.tr.json index a0fe5d119211..500badeba886 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.tr.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.tr.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup düzen yöntemi", "postActions/restoreNugetPackages/description": "Bu projenin gerektirdiği NuGet paketlerini geri yükleyin.", "postActions/restoreNugetPackages/manualInstructions/default/text": "\"dotnet restore\" çalıştır", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "Microsoft.Testing.Platform tarafından gerekli olan ‘global.json’ dosyasını oluşturun veya güncelleştirin.", + "postActions/addJsonProperty/manualInstructions/default/text": "Test çalıştırıcısını Microsoft.Testing.Platform olarak ayarlayarak ‘global.json’ dosyasını manuel olarak güncelleştirin veya oluşturun.", "postActions/openInEditor/description": "Test1.cs'yi düzenleyicide açar" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.zh-Hans.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.zh-Hans.json index d3813429eb2c..ef8e55b747cc 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.zh-Hans.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.zh-Hans.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup 固定例程方法", "postActions/restoreNugetPackages/description": "还原此项目所需的 NuGet 包。", "postActions/restoreNugetPackages/manualInstructions/default/text": "运行 \"dotnet restore\"", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "创建或更新 Microsoft.Testing.Platform 所需的 \"global.json\" 文件。", + "postActions/addJsonProperty/manualInstructions/default/text": "手动更新或创建 \"global.json\" 文件,将测试运行程序设置为 Microsoft.Testing.Platform", "postActions/openInEditor/description": "在编辑器中打开 Test1.cs" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.zh-Hant.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.zh-Hant.json index 65f448819360..0b99b95bc994 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.zh-Hant.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/localize/templatestrings.zh-Hant.json @@ -57,7 +57,7 @@ "symbols/Fixture/choices/TestCleanup/description": "TestCleanup 固件方法", "postActions/restoreNugetPackages/description": "還原此專案所需的 NuGet 套件。", "postActions/restoreNugetPackages/manualInstructions/default/text": "執行 'dotnet restore'", - "postActions/addJsonProperty/description": "Create or update 'global.json' file required by Microsoft.Testing.Platform.", - "postActions/addJsonProperty/manualInstructions/default/text": "Manually update or create 'global.json' file setting the test runner to Microsoft.Testing.Platform", + "postActions/addJsonProperty/description": "建立或更新 Microsoft.Testing.Platform 所需的 'global.json' 檔案。", + "postActions/addJsonProperty/manualInstructions/default/text": "手動更新或建立 'global.json' 檔案,將測試執行器設定為 Microsoft.Testing.Platform", "postActions/openInEditor/description": "在編輯器中開啟 Test1.cs" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/template.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/template.json index b55dff5e27a9..cca0c7262957 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/template.json +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/.template.config/template.json @@ -260,7 +260,9 @@ "parentPropertyPath": "test", "newJsonPropertyName": "runner", "newJsonPropertyValue": "Microsoft.Testing.Platform", - "detectRepositoryRootForFileCreation": true + "detectRepositoryRoot": true, + "includeAllDirectoriesInSearch": false, + "includeAllParentDirectoriesInSearch": true }, "continueOnError": true }, diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/Company.TestProject1.csproj b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/Company.TestProject1.csproj index c2484950c146..9c6ca5d3e253 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/Company.TestProject1.csproj +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/Playwright-MSTest-CSharp/Company.TestProject1.csproj @@ -1,5 +1,5 @@  - + net10.0 @@ -45,7 +45,7 @@ - + diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-CSharp/Company.TestProject1.csproj b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-CSharp/Company.TestProject1.csproj index 3eef41cd1588..bbcb92a579c5 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-CSharp/Company.TestProject1.csproj +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-CSharp/Company.TestProject1.csproj @@ -6,34 +6,19 @@ Company.TestProject1 enable enable - Exe true false - - - - - - - - - - - false + - - - - - - + + - + \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-CSharp/xunit.runner.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-CSharp/xunit.runner.json deleted file mode 100644 index 86c7ea05b16c..000000000000 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-CSharp/xunit.runner.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json" -} diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-FSharp/Company.TestProject1.fsproj b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-FSharp/Company.TestProject1.fsproj index 3f4c2abd455e..bf9c5b0b607e 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-FSharp/Company.TestProject1.fsproj +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-FSharp/Company.TestProject1.fsproj @@ -4,20 +4,8 @@ net10.0 TargetFrameworkOverride Company.TestProject1 - Exe true false - - - - - - - - - - - false @@ -25,13 +13,10 @@ + - - - - - - + + - + \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-FSharp/xunit.runner.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-FSharp/xunit.runner.json deleted file mode 100644 index 86c7ea05b16c..000000000000 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-FSharp/xunit.runner.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json" -} diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-VisualBasic/Company.TestProject1.vbproj b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-VisualBasic/Company.TestProject1.vbproj index 10d29b506258..d88c072952f9 100644 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-VisualBasic/Company.TestProject1.vbproj +++ b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-VisualBasic/Company.TestProject1.vbproj @@ -4,30 +4,15 @@ Company.TestProject1 net10.0 TargetFrameworkOverride - Exe true false - - - - - - - - - - - false + - - - - - - + + - + \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-VisualBasic/xunit.runner.json b/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-VisualBasic/xunit.runner.json deleted file mode 100644 index 86c7ea05b16c..000000000000 --- a/template_feed/Microsoft.DotNet.Common.ProjectTemplates.10.0/content/XUnit-VisualBasic/xunit.runner.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json" -} diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreAppForTelemetry.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreAppForTelemetry.cs index 54bc27a974d3..2dfa4c6fda50 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreAppForTelemetry.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreAppForTelemetry.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Reflection; +using Microsoft.DotNet.Cli.Utils; namespace Microsoft.NET.Build.Tests { @@ -29,7 +30,7 @@ private string CreateTargetFrameworkEvalTelemetryJson( string publishProtocol = "null", string configuration = "Debug") { - return $"{{\"EventName\":\"targetframeworkeval\",\"Properties\":{{\"TargetFrameworkVersion\":\"{targetFrameworkVersion}\",\"RuntimeIdentifier\":\"{runtimeIdentifier}\",\"SelfContained\":\"{selfContained}\",\"UseApphost\":\"{useApphost}\",\"OutputType\":\"{outputType}\",\"UseArtifactsOutput\":\"{useArtifactsOutput}\",\"ArtifactsPathLocationType\":\"{artifactsPathLocationType}\",\"TargetPlatformIdentifier\":\"{targetPlatformIdentifier}\",\"UseMonoRuntime\":\"{useMonoRuntime}\",\"PublishAot\":\"{publishAot}\",\"PublishTrimmed\":\"{publishTrimmed}\",\"PublishSelfContained\":\"{publishSelfContained}\",\"PublishReadyToRun\":\"{publishReadyToRun}\",\"PublishReadyToRunComposite\":\"{publishReadyToRunComposite}\",\"PublishProtocol\":\"{publishProtocol}\",\"Configuration\":\"{configuration}\"}}"; + return $"{{\"EventName\":\"targetframeworkeval\",\"Properties\":{{\"TargetFrameworkVersion\":\"{targetFrameworkVersion}\",\"RuntimeIdentifier\":\"{runtimeIdentifier}\",\"SelfContained\":\"{selfContained}\",\"UseApphost\":\"{useApphost}\",\"OutputType\":\"{outputType}\",\"UseArtifactsOutput\":\"{useArtifactsOutput}\",\"ArtifactsPathLocationType\":\"{artifactsPathLocationType}\",\"TargetPlatformIdentifier\":\"{targetPlatformIdentifier}\",\"UseMonoRuntime\":\"{useMonoRuntime}\",\"PublishAot\":\"{publishAot}\",\"PublishTrimmed\":\"{publishTrimmed}\",\"PublishSelfContained\":\"{publishSelfContained}\",\"PublishReadyToRun\":\"{publishReadyToRun}\",\"PublishReadyToRunComposite\":\"{publishReadyToRunComposite}\",\"PublishProtocol\":\"{publishProtocol}\",\"Configuration\":\"{Sha256Hasher.HashWithNormalizedCasing(configuration)}\"}}"; } [CoreMSBuildOnlyFact] @@ -82,7 +83,7 @@ public void It_collects_multi_TargetFramework_version_and_other_properties() result .StdOut.Should() .Contain(CreateTargetFrameworkEvalTelemetryJson( - ".NETFramework,Version=v4.6", + ".NETFramework,Version=v4.6", targetPlatformIdentifier: "Windows", publishReadyToRunComposite: "null")) .And diff --git a/test/Microsoft.NET.Build.Tests/RoslynBuildTaskTests.cs b/test/Microsoft.NET.Build.Tests/RoslynBuildTaskTests.cs index ba14cd54eebc..2f473b12d72f 100644 --- a/test/Microsoft.NET.Build.Tests/RoslynBuildTaskTests.cs +++ b/test/Microsoft.NET.Build.Tests/RoslynBuildTaskTests.cs @@ -6,6 +6,7 @@ using Basic.CompilerLog.Util; using Microsoft.Build.Logging.StructuredLogger; using Microsoft.CodeAnalysis; +using Microsoft.DotNet.Cli.Utils; namespace Microsoft.NET.Build.Tests; @@ -18,16 +19,16 @@ public sealed class RoslynBuildTaskTests(ITestOutputHelper log) : SdkTest(log) _ => throw new ArgumentOutOfRangeException(paramName: nameof(language)), }; - private static string CoreCompilerFileName(Language language) => CompilerFileNameWithoutExtension(language) + ".dll"; + private static string DotNetExecCompilerFileName(Language language) => CompilerFileNameWithoutExtension(language) + ".dll"; - private static string FxCompilerFileName(Language language) => CompilerFileNameWithoutExtension(language) + ".exe"; + private static string AppHostCompilerFileName(Language language) => CompilerFileNameWithoutExtension(language) + FileNameSuffixes.CurrentPlatform.Exe; [FullMSBuildOnlyTheory, CombinatorialData] public void FullMSBuild_SdkStyle(bool useSharedCompilation, Language language) { var testAsset = CreateProject(useSharedCompilation, language); var buildCommand = BuildAndRunUsingMSBuild(testAsset); - VerifyCompiler(buildCommand, CoreCompilerFileName(language), useSharedCompilation); + VerifyCompiler(buildCommand, AppHostCompilerFileName(language), useSharedCompilation); } [FullMSBuildOnlyTheory, CombinatorialData] @@ -38,7 +39,7 @@ public void FullMSBuild_SdkStyle_OptOut(bool useSharedCompilation, Language lang doc.Root!.Element("PropertyGroup")!.Add(new XElement("RoslynCompilerType", "Framework")); }); var buildCommand = BuildAndRunUsingMSBuild(testAsset); - VerifyCompiler(buildCommand, FxCompilerFileName(language), useSharedCompilation); + VerifyCompiler(buildCommand, AppHostCompilerFileName(language), useSharedCompilation); } [FullMSBuildOnlyTheory, CombinatorialData] @@ -50,7 +51,7 @@ public void FullMSBuild_NonSdkStyle(bool useSharedCompilation, Language language project.TargetFrameworkVersion = "v4.7.2"; }); var buildCommand = BuildAndRunUsingMSBuild(testAsset); - VerifyCompiler(buildCommand, FxCompilerFileName(language), useSharedCompilation); + VerifyCompiler(buildCommand, AppHostCompilerFileName(language), useSharedCompilation); } [FullMSBuildOnlyTheory, CombinatorialData] @@ -58,7 +59,7 @@ public void FullMSBuild_SdkStyle_ToolsetPackage(bool useSharedCompilation, Langu { var testAsset = CreateProject(useSharedCompilation, language, AddCompilersToolsetPackage); var buildCommand = BuildAndRunUsingMSBuild(testAsset); - VerifyCompiler(buildCommand, FxCompilerFileName(language), useSharedCompilation, toolsetPackage: true); + VerifyCompiler(buildCommand, AppHostCompilerFileName(language), useSharedCompilation, toolsetPackage: true); } [Theory, CombinatorialData] @@ -66,7 +67,7 @@ public void DotNet(bool useSharedCompilation, Language language) { var testAsset = CreateProject(useSharedCompilation, language); var buildCommand = BuildAndRunUsingDotNet(testAsset); - VerifyCompiler(buildCommand, CoreCompilerFileName(language), useSharedCompilation); + VerifyCompiler(buildCommand, AppHostCompilerFileName(language), useSharedCompilation); } // https://github.com/dotnet/sdk/issues/49665 @@ -75,7 +76,7 @@ public void DotNet_ToolsetPackage(bool useSharedCompilation, Language language) { var testAsset = CreateProject(useSharedCompilation, language, AddCompilersToolsetPackage); var buildCommand = BuildAndRunUsingDotNet(testAsset); - VerifyCompiler(buildCommand, CoreCompilerFileName(language), useSharedCompilation, toolsetPackage: true); + VerifyCompiler(buildCommand, DotNetExecCompilerFileName(language), useSharedCompilation, toolsetPackage: true); } private TestAsset CreateProject(bool useSharedCompilation, Language language, Action? configure = null, [CallerMemberName] string callingMethod = "") diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishIncrementally.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishIncrementally.cs index c47c60089692..9cd038e21ff5 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishIncrementally.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishIncrementally.cs @@ -134,7 +134,7 @@ public void It_cleans_between_single_file_publishes() CheckPublishOutput(publishDir, expectedSingleExeFiles.Append(testProject.Name + ".dll"), null); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/sdk/issues/50784")] public void It_cleans_before_trimmed_single_file_publish() { var testProject = new TestProject() diff --git a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPublishIntegrationTest.cs b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPublishIntegrationTest.cs index 6b26bd237940..cab0eb56e6e1 100644 --- a/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPublishIntegrationTest.cs +++ b/test/Microsoft.NET.Sdk.BlazorWebAssembly.Tests/WasmPublishIntegrationTest.cs @@ -1612,6 +1612,16 @@ public class TestReference fileInWwwroot.Should().Exist(); } + [RequiresMSBuildVersionTheory("17.12", Reason = "Needs System.Text.Json 8.0.5")] + [InlineData("")] + [InlineData("/p:BlazorFingerprintBlazorJs=false")] + public void Publish_BlazorWasmReferencedByAspNetCoreServer(string publishArg) + { + var testInstance = CreateAspNetSdkTestAsset("BlazorWasmReferencedByAspNetCoreServer"); + var publishCommand = CreatePublishCommand(testInstance, "Server"); + ExecuteCommand(publishCommand, publishArg).Should().Pass(); + } + private void VerifyTypeGranularTrimming(string blazorPublishDirectory) { VerifyAssemblyHasTypes(Path.Combine(blazorPublishDirectory, "_framework", "Microsoft.AspNetCore.Components.wasm"), new[] { diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs index 9668d8656913..6155a3498987 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/ScopedCssIntegrationTests.cs @@ -3,6 +3,7 @@ #nullable disable +using System.Text.Json; using System.Text.RegularExpressions; using Microsoft.AspNetCore.StaticWebAssets.Tasks; @@ -617,5 +618,72 @@ public void RegeneratingScopedCss_ForProjectWithReferences() text.Should().Contain("background-color: orangered"); text.Should().MatchRegex(""".*@import '_content/ClassLibrary/ClassLibrary\.[a-zA-Z0-9]+\.bundle\.scp\.css.*"""); } + + [Fact] + public void Build_GeneratesUrlEncodedLinkHeaderForNonAsciiProjectName() + { + var testAsset = "RazorAppWithPackageAndP2PReference"; + ProjectDirectory = CreateAspNetSdkTestAsset(testAsset); + + // Rename the ClassLibrary project to have non-ASCII characters + var originalLibPath = Path.Combine(ProjectDirectory.Path, "AnotherClassLib"); + var newLibPath = Path.Combine(ProjectDirectory.Path, "项目"); + Directory.Move(originalLibPath, newLibPath); + + // Update the project file to set the assembly name and package ID + var libProjectFile = Path.Combine(newLibPath, "AnotherClassLib.csproj"); + var newLibProjectFile = Path.Combine(newLibPath, "项目.csproj"); + File.Move(libProjectFile, newLibProjectFile); + + // Add assembly name property to ensure consistent naming + var libProjectContent = File.ReadAllText(newLibProjectFile); + // Find the first PropertyGroup closing tag and replace it + var targetPattern = ""; + var replacement = " 项目\n 项目\n "; + var index = libProjectContent.IndexOf(targetPattern); + if (index >= 0) + { + libProjectContent = libProjectContent.Substring(0, index) + replacement + libProjectContent.Substring(index + targetPattern.Length); + } + File.WriteAllText(newLibProjectFile, libProjectContent); + + // Update the main project to reference the renamed library + var mainProjectFile = Path.Combine(ProjectDirectory.Path, "AppWithPackageAndP2PReference", "AppWithPackageAndP2PReference.csproj"); + var mainProjectContent = File.ReadAllText(mainProjectFile); + mainProjectContent = mainProjectContent.Replace(@"..\AnotherClassLib\AnotherClassLib.csproj", @"..\项目\项目.csproj"); + File.WriteAllText(mainProjectFile, mainProjectContent); + + // Ensure library has scoped CSS + var libCssFile = Path.Combine(newLibPath, "Views", "Shared", "Index.cshtml.css"); + if (!File.Exists(libCssFile)) + { + Directory.CreateDirectory(Path.GetDirectoryName(libCssFile)); + File.WriteAllText(libCssFile, ".test { color: red; }"); + } + + EnsureLocalPackagesExists(); + + var restore = CreateRestoreCommand(ProjectDirectory, "AppWithPackageAndP2PReference"); + ExecuteCommand(restore).Should().Pass(); + + var build = CreateBuildCommand(ProjectDirectory, "AppWithPackageAndP2PReference"); + ExecuteCommand(build).Should().Pass(); + + var intermediateOutputPath = build.GetIntermediateDirectory(DefaultTfm, "Debug").ToString(); + + // Check that the staticwebassets.build.endpoints.json file contains URL-encoded characters + var endpointsFile = Path.Combine(intermediateOutputPath, "staticwebassets.build.endpoints.json"); + new FileInfo(endpointsFile).Should().Exist(); + + var endpointsContent = File.ReadAllText(endpointsFile); + var json = JsonSerializer.Deserialize(endpointsContent, new JsonSerializerOptions(JsonSerializerDefaults.Web)); + + var styles = json.Endpoints.Where(e => e.Route.EndsWith("styles.css")); + + foreach (var styleEndpoint in styles) + { + styleEndpoint.ResponseHeaders.Should().Contain(h => h.Name.Equals("Link", StringComparison.OrdinalIgnoreCase) && h.Value.Contains("%E9%A1%B9%E7%9B%AE")); + } + } } } diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs index 5fcd970d8c53..f252022d1338 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/DiscoverStaticWebAssetsTest.cs @@ -217,6 +217,69 @@ public void FingerprintsContentUsingPatternsWhenMoreThanOneExtension(string file asset.GetMetadata(nameof(StaticWebAsset.OriginalItemSpec)).Should().Be(Path.Combine("wwwroot", fileName)); } + [Fact] + [Trait("Category", "FingerprintIdentity")] + public void ComputesIdentity_UsingFingerprintPattern_ForComputedAssets_WhenIdentityNeedsComputation() + { + // Arrange: simulate a packaged asset (outside content root) with a RelativePath inside the app + var errorMessages = new List(); + var buildEngine = new Mock(); + buildEngine.Setup(e => e.LogErrorEvent(It.IsAny())) + .Callback(args => errorMessages.Add(args.Message)); + + // Create a physical file to allow fingerprint computation (tests override ResolveFileDetails returning null file otherwise) + var tempRoot = Path.Combine(Path.GetTempPath(), "swafp_identity_test"); + var nugetPackagePath = Path.Combine(tempRoot, "microsoft.aspnetcore.components.webassembly", "10.0.0-rc.1.25451.107", "build", "net10.0"); + Directory.CreateDirectory(nugetPackagePath); + var assetFileName = "blazor.webassembly.js"; + var assetFullPath = Path.Combine(nugetPackagePath, assetFileName); + File.WriteAllText(assetFullPath, "console.log('test');"); + // Relative path provided by the item (pre-fingerprinting) + var relativePath = Path.Combine("_framework", assetFileName).Replace('\\', '/'); + var contentRoot = Path.Combine("bin", "Release", "net10.0", "wwwroot"); + + var task = new DefineStaticWebAssets + { + BuildEngine = buildEngine.Object, + // Use default file resolution so the file we created is used for hashing. + TestResolveFileDetails = null, + CandidateAssets = + [ + new TaskItem(assetFullPath, new Dictionary + { + ["RelativePath"] = relativePath + }) + ], + // No RelativePathPattern, we trigger the branch that synthesizes identity under content root. + FingerprintPatterns = [ new TaskItem("Js", new Dictionary{{"Pattern","*.js"},{"Expression","#[.{fingerprint}]!"}})], + FingerprintCandidates = true, + SourceType = "Computed", + SourceId = "Client", + ContentRoot = contentRoot, + BasePath = "/", + AssetKind = StaticWebAsset.AssetKinds.All, + AssetTraitName = "WasmResource", + AssetTraitValue = "boot" + }; + + // Act + var result = task.Execute(); + + // Assert + result.Should().BeTrue($"Errors: {Environment.NewLine} {string.Join($"{Environment.NewLine} ", errorMessages)}"); + task.Assets.Length.Should().Be(1); + var asset = task.Assets[0]; + + // RelativePath should still contain the hard fingerprint pattern placeholder (not expanded yet) + asset.GetMetadata(nameof(StaticWebAsset.RelativePath)).Should().Be("_framework/blazor.webassembly#[.{fingerprint}]!.js"); + + // Identity must contain the ACTUAL fingerprint value in the file name (placeholder expanded) + var actualFingerprint = asset.GetMetadata(nameof(StaticWebAsset.Fingerprint)); + actualFingerprint.Should().NotBeNullOrEmpty(); + var expectedIdentity = Path.GetFullPath(Path.Combine(contentRoot, "_framework", $"blazor.webassembly.{actualFingerprint}.js")); + asset.ItemSpec.Should().Be(expectedIdentity); + } + [Fact] public void RespectsItemRelativePathWhenExplicitlySpecified() { @@ -450,7 +513,7 @@ public void FailsDiscoveringAssetsWhenThereIsAConflict( // Assert result.Should().Be(false); errorMessages.Count.Should().Be(1); - errorMessages[0].Should().Be($@"Two assets found targeting the same path with incompatible asset kinds: + errorMessages[0].Should().Be($@"Two assets found targeting the same path with incompatible asset kinds: '{Path.GetFullPath(Path.Combine("wwwroot", "candidate.js"))}' with kind '{firstKind}' '{Path.GetFullPath(Path.Combine("wwwroot", "candidate.publish.js"))}' with kind '{secondKind}' for path 'candidate.js'"); diff --git a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs index f8061c5d6ba1..fb2746fb1ce5 100644 --- a/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs +++ b/test/Microsoft.NET.Sdk.StaticWebAssets.Tests/StaticWebAssets/Globbing/StaticWebAssetGlobMatcherTest.cs @@ -27,6 +27,29 @@ namespace Microsoft.AspNetCore.StaticWebAssets.Tasks.Test; // Recursive wildcard in the middle 'a/**/c' public partial class StaticWebAssetGlobMatcherTest { + [Theory] + [InlineData("**/*.razor.js", "Components/Pages/RegularComponent.razor.js", "Components/Pages/RegularComponent.razor.js")] + [InlineData("**/*.razor.js", "Components/User.Profile.Details.razor.js", "Components/User.Profile.Details.razor.js")] + [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js", "Components/Area/Sub/Feature/User.Profile.Details.razor.js")] + [InlineData("**/*.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js", "Components/Area/Sub/Feature/Deep.Component.Name.With.Many.Parts.razor.js")] + [InlineData("**/*.cshtml.js", "Pages/Shared/_Host.cshtml.js", "Pages/Shared/_Host.cshtml.js")] + [InlineData("**/*.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js", "Areas/Admin/Pages/Dashboard.cshtml.js")] + [InlineData("*.lib.module.js", "Widget.lib.module.js", "Widget.lib.module.js")] + [InlineData("*.razor.css", "Component.razor.css", "Component.razor.css")] + [InlineData("*.cshtml.css", "View.cshtml.css", "View.cshtml.css")] + [InlineData("*.modules.json", "app.modules.json", "app.modules.json")] + [InlineData("*.lib.module.js", "Rcl.Client.Feature.lib.module.js", "Rcl.Client.Feature.lib.module.js")] + public void Can_Match_WellKnownExistingPatterns(string pattern, string path, string expectedStem) + { + var matcher = new StaticWebAssetGlobMatcherBuilder(); + matcher.AddIncludePatterns(pattern); + var globMatcher = matcher.Build(); + + var match = globMatcher.Match(path); + Assert.True(match.IsMatch); + Assert.Equal(pattern, match.Pattern); + Assert.Equal(expectedStem, match.Stem); + } [Fact] public void CanMatchLiterals() { diff --git a/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/SdkAnalyzerAssemblyRedirectorTests.cs b/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/SdkAnalyzerAssemblyRedirectorTests.cs index 62a042d8f419..956161b134a8 100644 --- a/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/SdkAnalyzerAssemblyRedirectorTests.cs +++ b/test/Microsoft.Net.Sdk.AnalyzerRedirecting.Tests/SdkAnalyzerAssemblyRedirectorTests.cs @@ -1,6 +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 System.Text.Json; + namespace Microsoft.Net.Sdk.AnalyzerRedirecting.Tests; public class SdkAnalyzerAssemblyRedirectorTests(ITestOutputHelper log) : SdkTest(log) @@ -16,7 +18,8 @@ public void SameMajorMinorVersion(string a, string b) TestDirectory testDir = _testAssetsManager.CreateTestDirectory(identifier: "RuntimeAnalyzers"); var vsDir = Path.Combine(testDir.Path, "vs"); - var vsAnalyzerPath = FakeDll(vsDir, @$"AspNetCoreAnalyzers\{a}\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); + Metadata(vsDir, new() { { "AspNetCoreAnalyzers", a } }); + var vsAnalyzerPath = FakeDll(vsDir, @$"AspNetCoreAnalyzers\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); var sdkAnalyzerPath = FakeDll(testDir.Path, @$"sdk\packs\Microsoft.AspNetCore.App.Ref\{b}\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); var resolver = new SdkAnalyzerAssemblyRedirector(vsDir); @@ -30,7 +33,8 @@ public void DifferentPathSuffix() TestDirectory testDir = _testAssetsManager.CreateTestDirectory(identifier: "RuntimeAnalyzers"); var vsDir = Path.Combine(testDir.Path, "vs"); - FakeDll(vsDir, @"AspNetCoreAnalyzers\9.0.0-preview.5.24306.11\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); + Metadata(vsDir, new() { { "AspNetCoreAnalyzers", "9.0.0-preview.5.24306.11" } }); + FakeDll(vsDir, @"AspNetCoreAnalyzers\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); var sdkAnalyzerPath = FakeDll(testDir.Path, @"sdk\packs\Microsoft.AspNetCore.App.Ref\9.0.0-preview.7.24406.2\analyzers\dotnet\vb", "Microsoft.AspNetCore.App.Analyzers"); var resolver = new SdkAnalyzerAssemblyRedirector(vsDir); @@ -50,7 +54,8 @@ public void DifferentMajorMinorVersion(string a, string b) TestDirectory testDir = _testAssetsManager.CreateTestDirectory(identifier: "RuntimeAnalyzers"); var vsDir = Path.Combine(testDir.Path, "vs"); - FakeDll(vsDir, @$"AspNetCoreAnalyzers\{a}\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); + Metadata(vsDir, new() { { "AspNetCoreAnalyzers", a } }); + FakeDll(vsDir, @$"AspNetCoreAnalyzers\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); var sdkAnalyzerPath = FakeDll(testDir.Path, @$"sdk\packs\Microsoft.AspNetCore.App.Ref\{b}\analyzers\dotnet\cs", "Microsoft.AspNetCore.App.Analyzers"); var resolver = new SdkAnalyzerAssemblyRedirector(vsDir); @@ -65,4 +70,11 @@ private static string FakeDll(string root, string subdir, string name) File.WriteAllText(dllPath, ""); return dllPath; } + + private static void Metadata(string root, Dictionary versions) + { + var metadataFilePath = Path.Combine(root, "metadata.json"); + Directory.CreateDirectory(Path.GetDirectoryName(metadataFilePath)); + File.WriteAllText(metadataFilePath, JsonSerializer.Serialize(versions)); + } } diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/BlazorWasmReferencedByAspNetCoreServer.slnx b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/BlazorWasmReferencedByAspNetCoreServer.slnx new file mode 100644 index 000000000000..3e2628def731 --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/BlazorWasmReferencedByAspNetCoreServer.slnx @@ -0,0 +1,4 @@ + + + + diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/App.razor b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/App.razor new file mode 100644 index 000000000000..f796217a715d --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/App.razor @@ -0,0 +1,6 @@ + + + + + + diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/Client.csproj b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/Client.csproj new file mode 100644 index 000000000000..88afc4e9be57 --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/Client.csproj @@ -0,0 +1,16 @@ + + + net10.0 + enable + enable + true + service-worker-assets.js + + + + + + + + + diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/Program.cs b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/Program.cs new file mode 100644 index 000000000000..121b10340b73 --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/Program.cs @@ -0,0 +1,11 @@ +using Client; +using Microsoft.AspNetCore.Components.Web; +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); +builder.RootComponents.Add("#app"); +builder.RootComponents.Add("head::after"); + +builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); + +await builder.Build().RunAsync(); diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/_Imports.razor b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/_Imports.razor new file mode 100644 index 000000000000..13999df0b5a3 --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/_Imports.razor @@ -0,0 +1,9 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.JSInterop +@using Client diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/css/app.css b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/css/app.css new file mode 100644 index 000000000000..a87511a380bf --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/css/app.css @@ -0,0 +1,2 @@ +/* minimal css */ +body { font-family: sans-serif; } diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/index.html b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/index.html new file mode 100644 index 000000000000..f68fb3fc8ab3 --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/index.html @@ -0,0 +1,33 @@ + + + + + + Client + + + + + + + + + + + + +
+ + + + +
+
+
+ An unhandled error has occurred. + Reload + 🗙 +
+ + + diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/manifest.webmanifest b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/manifest.webmanifest new file mode 100644 index 000000000000..85aa02b14543 --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/manifest.webmanifest @@ -0,0 +1,8 @@ +{ + "name": "Client", + "short_name": "Client", + "start_url": "/", + "display": "standalone", + "background_color": "#ffffff", + "description": "Test Blazor WASM referenced by ASP.NET Core Server" +} diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/service-worker.js b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/service-worker.js new file mode 100644 index 000000000000..22fcb7e905aa --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/service-worker.js @@ -0,0 +1,2 @@ +self.addEventListener('install', () => self.skipWaiting()); +self.addEventListener('activate', () => clients.claim()); diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/service-worker.published.js b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/service-worker.published.js new file mode 100644 index 000000000000..0074cb787624 --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Client/wwwroot/service-worker.published.js @@ -0,0 +1,3 @@ +// Published service worker placeholder +self.addEventListener('install', () => self.skipWaiting()); +self.addEventListener('activate', () => clients.claim()); diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Server/Program.cs b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Server/Program.cs new file mode 100644 index 000000000000..068ce63ca79a --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Server/Program.cs @@ -0,0 +1,16 @@ +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddOpenApi(); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.UseWebAssemblyDebugging(); + app.MapOpenApi(); +} + +app.UseHttpsRedirection(); +app.MapStaticAssets(); +app.MapFallbackToFile("index.html"); +app.Run(); diff --git a/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Server/Server.csproj b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Server/Server.csproj new file mode 100644 index 000000000000..0dcd90d3c786 --- /dev/null +++ b/test/TestAssets/TestProjects/BlazorWasmReferencedByAspNetCoreServer/Server/Server.csproj @@ -0,0 +1,14 @@ + + + net10.0 + enable + enable + + + + + + + + + diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.ApiService/Program.cs b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.ApiService/Program.cs index 29ac848cd132..95a823165073 100644 --- a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.ApiService/Program.cs +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.ApiService/Program.cs @@ -37,7 +37,7 @@ app.Run(); -internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +internal record WeatherForecast(DateOnly Date, int TemperatureC, string Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/Program.cs b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/Program.cs index 4e0bf9d4cee6..7ef5c7a828ee 100644 --- a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/Program.cs +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/Program.cs @@ -1,8 +1,13 @@ var builder = DistributedApplication.CreateBuilder(args); -var apiService = builder.AddProject("apiservice"); +var migration = builder.AddProject("migrationservice"); + +var apiService = builder + .AddProject("apiservice") + .WaitForCompletion(migration); builder.AddProject("webfrontend") + .WaitForCompletion(migration) .WithExternalHttpEndpoints() .WithReference(apiService) .WaitFor(apiService); diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/WatchAspire.AppHost.csproj b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/WatchAspire.AppHost.csproj index 90483a02df60..3f394f6f83c0 100644 --- a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/WatchAspire.AppHost.csproj +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.AppHost/WatchAspire.AppHost.csproj @@ -9,9 +9,10 @@ - - - + + + + diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/Program.cs b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/Program.cs new file mode 100644 index 000000000000..0abee487007d --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/Program.cs @@ -0,0 +1,10 @@ +using MigrationService; +using Microsoft.Extensions.Hosting; + +var builder = Host.CreateApplicationBuilder(args); + +builder.AddServiceDefaults(); +builder.Services.AddHostedService(); + +var host = builder.Build(); +host.Run(); diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/Properties/launchSettings.json b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/Properties/launchSettings.json new file mode 100644 index 000000000000..a312e8fb30bf --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "MigrationService": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/WatchAspire.MigrationService.csproj b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/WatchAspire.MigrationService.csproj new file mode 100644 index 000000000000..621a330ee1b4 --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/WatchAspire.MigrationService.csproj @@ -0,0 +1,11 @@ + + + + net10.0 + enable + enable + + + + + \ No newline at end of file diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/Worker.cs b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/Worker.cs new file mode 100644 index 000000000000..4792261219db --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/Worker.cs @@ -0,0 +1,22 @@ +using System.Diagnostics; + +namespace MigrationService; + +public class Worker(IHostApplicationLifetime hostApplicationLifetime, ILogger logger) : BackgroundService +{ + private static readonly ActivitySource s_activitySource = new("Migrations"); + + protected override Task ExecuteAsync(CancellationToken cancellationToken) + { + using var activity = s_activitySource.StartActivity( + "Migrating database", + ActivityKind.Client + ); + + logger.LogInformation("Migration complete"); + + hostApplicationLifetime.StopApplication(); + + return Task.CompletedTask; + } +} diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/appsettings.Development.json b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/appsettings.Development.json new file mode 100644 index 000000000000..cd7d0bc9100b --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} \ No newline at end of file diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/appsettings.json b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/appsettings.json new file mode 100644 index 000000000000..0c208ae9181e --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.MigrationService/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/App.razor b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/App.razor index 13f3043f0c49..eba23da9b5ae 100644 --- a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/App.razor +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.Wasm/App.razor @@ -1,4 +1,4 @@ - + diff --git a/test/TestAssets/TestProjects/WatchAspire/WatchAspire.slnx b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.slnx new file mode 100644 index 000000000000..d9a238e555dc --- /dev/null +++ b/test/TestAssets/TestProjects/WatchAspire/WatchAspire.slnx @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/test/TestAssets/TestProjects/WatchHotReloadApp/Program.cs b/test/TestAssets/TestProjects/WatchHotReloadApp/Program.cs index f1d2a3b77bbd..7171e0a19260 100644 --- a/test/TestAssets/TestProjects/WatchHotReloadApp/Program.cs +++ b/test/TestAssets/TestProjects/WatchHotReloadApp/Program.cs @@ -8,6 +8,7 @@ using System.Runtime.Versioning; using System.Threading; using System.Threading.Tasks; +using System.Runtime.InteropServices; // diff --git a/test/TestAssets/TestProjects/WatchRazorWithDeps/RazorApp/Components/Pages/Home.razor b/test/TestAssets/TestProjects/WatchRazorWithDeps/RazorApp/Components/Pages/Home.razor index bb44fc2639e7..2daa1cadc252 100644 --- a/test/TestAssets/TestProjects/WatchRazorWithDeps/RazorApp/Components/Pages/Home.razor +++ b/test/TestAssets/TestProjects/WatchRazorWithDeps/RazorApp/Components/Pages/Home.razor @@ -5,4 +5,11 @@ -Welcome to your new app. \ No newline at end of file +Welcome to your new app. + +@code{ + class C + { + /* member placeholder */ + } +} \ No newline at end of file diff --git a/test/dotnet-watch-test-browser/Program.cs b/test/dotnet-watch-test-browser/Program.cs new file mode 100644 index 000000000000..78f96cb73ccb --- /dev/null +++ b/test/dotnet-watch-test-browser/Program.cs @@ -0,0 +1,146 @@ +using System; +using System.Buffers; +using System.Linq; +using System.Net.Http; +using System.Net.WebSockets; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +if (args is not [var urlArg]) +{ + Console.Error.WriteLine(); + return -1; +} + +Log($"Test browser opened at '{urlArg}'."); + +var url = new Uri(urlArg, UriKind.Absolute); + +var (webSocketUrls, publicKey) = await GetWebSocketUrlsAndPublicKey(url); + +var secret = RandomNumberGenerator.GetBytes(32); +var encryptedSecret = GetEncryptedSecret(publicKey, secret); + +using var webSocket = await OpenWebSocket(webSocketUrls, encryptedSecret); +var buffer = new byte[8 * 1024]; + +while (await TryReceiveMessageAsync(webSocket, message => Log($"Received: {Encoding.UTF8.GetString(message)}"))) +{ +} + +Log("WebSocket closed"); + +return 0; + +static async Task OpenWebSocket(string[] urls, string encryptedSecret) +{ + foreach (var url in urls) + { + try + { + var webSocket = new ClientWebSocket(); + webSocket.Options.AddSubProtocol(Uri.EscapeDataString(encryptedSecret)); + await webSocket.ConnectAsync(new Uri(url), CancellationToken.None); + return webSocket; + } + catch (Exception e) + { + Log($"Error connecting to '{url}': {e.Message}"); + } + } + + throw new InvalidOperationException("Unable to establish a connection."); +} + +static async ValueTask TryReceiveMessageAsync(WebSocket socket, Action> receiver) +{ + var writer = new ArrayBufferWriter(initialCapacity: 1024); + + while (true) + { + ValueWebSocketReceiveResult result; + var data = writer.GetMemory(); + try + { + result = await socket.ReceiveAsync(data, CancellationToken.None); + } + catch (Exception e) when (e is not OperationCanceledException) + { + Log($"Failed to receive response: {e.Message}"); + return false; + } + + if (result.MessageType == WebSocketMessageType.Close) + { + return false; + } + + writer.Advance(result.Count); + if (result.EndOfMessage) + { + break; + } + } + + receiver(writer.WrittenSpan); + return true; +} + +static async Task<(string[] url, string key)> GetWebSocketUrlsAndPublicKey(Uri baseUrl) +{ + var refreshScriptUrl = new Uri(baseUrl, "/_framework/aspnetcore-browser-refresh.js"); + + Log($"Fetching: {refreshScriptUrl}"); + + using var httpClient = new HttpClient(); + var content = await httpClient.GetStringAsync(refreshScriptUrl); + + Log($"Request for '{refreshScriptUrl}' succeeded"); + var webSocketUrl = GetWebSocketUrls(content); + var key = GetSharedSecretKey(content); + + Log($"WebSocket urls are '{string.Join(',', webSocketUrl)}'."); + Log($"Key is '{key}'."); + + return (webSocketUrl, key); +} + +static string[] GetWebSocketUrls(string refreshScript) +{ + var pattern = "const webSocketUrls = '([^']+)'"; + + var match = Regex.Match(refreshScript, pattern); + if (!match.Success) + { + throw new InvalidOperationException($"Can't find web socket URL pattern in the script: {pattern}{Environment.NewLine}{refreshScript}"); + } + + return match.Groups[1].Value.Split(","); +} + +static string GetSharedSecretKey(string refreshScript) +{ + var pattern = @"const sharedSecret = await getSecret\('([^']+)'\)"; + + var match = Regex.Match(refreshScript, pattern); + if (!match.Success) + { + throw new InvalidOperationException($"Can't find web socket shared secret pattern in the script: {pattern}{Environment.NewLine}{refreshScript}"); + } + + return match.Groups[1].Value; +} + +// Equivalent to getSecret function in WebSocketScriptInjection.js: +static string GetEncryptedSecret(string key, byte[] secret) +{ + using var rsa = RSA.Create(); + rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(key), out _); + return Convert.ToBase64String(rsa.Encrypt(secret, RSAEncryptionPadding.OaepSHA256)); +} + +static void Log(string message) + => Console.WriteLine($"🧪 {message}"); diff --git a/test/dotnet-watch-test-browser/dotnet-watch-test-browser.csproj b/test/dotnet-watch-test-browser/dotnet-watch-test-browser.csproj new file mode 100644 index 000000000000..408cb9159c99 --- /dev/null +++ b/test/dotnet-watch-test-browser/dotnet-watch-test-browser.csproj @@ -0,0 +1,14 @@ + + + Exe + $(ToolsetTargetFramework) + MicrosoftAspNetCore + Microsoft.DotNet.Watch.UnitTests + + + + + + + + diff --git a/test/dotnet-watch.Tests/Browser/BrowserLaunchTests.cs b/test/dotnet-watch.Tests/Browser/BrowserLaunchTests.cs deleted file mode 100644 index f5fbf358d819..000000000000 --- a/test/dotnet-watch.Tests/Browser/BrowserLaunchTests.cs +++ /dev/null @@ -1,49 +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.Watch.UnitTests -{ - public class BrowserLaunchTests : DotNetWatchTestBase - { - private const string AppName = "WatchBrowserLaunchApp"; - - public BrowserLaunchTests(ITestOutputHelper logger) - : base(logger) - { - } - - [Fact] - public async Task LaunchesBrowserOnStart() - { - var testAsset = TestAssets.CopyTestAsset(AppName) - .WithSource(); - - App.Start(testAsset, [], testFlags: TestFlags.MockBrowser); - - // check that all app output is printed out: - await App.WaitForOutputLineContaining("Content root path:"); - - Assert.Contains(App.Process.Output, line => line.Contains("Application started. Press Ctrl+C to shut down.")); - Assert.Contains(App.Process.Output, line => line.Contains("Hosting environment: Development")); - - // Verify we launched the browser. - Assert.Contains(App.Process.Output, line => line.Contains("dotnet watch ⌚ Launching browser: https://localhost:5001")); - } - - [Fact] - public async Task UsesBrowserSpecifiedInEnvironment() - { - var testAsset = TestAssets.CopyTestAsset(AppName) - .WithSource(); - - App.EnvironmentVariables.Add("DOTNET_WATCH_BROWSER_PATH", "mycustombrowser.bat"); - - App.Start(testAsset, [], testFlags: TestFlags.MockBrowser); - await App.WaitForOutputLineContaining(MessageDescriptor.ConfiguredToUseBrowserRefresh); - await App.WaitForOutputLineContaining(MessageDescriptor.ConfiguredToLaunchBrowser); - - // Verify we launched the browser. - await App.AssertOutputLineStartsWith("dotnet watch ⌚ Launching browser: mycustombrowser.bat https://localhost:5001"); - } - } -} diff --git a/test/dotnet-watch.Tests/Browser/BrowserTests.cs b/test/dotnet-watch.Tests/Browser/BrowserTests.cs new file mode 100644 index 000000000000..8fa4c00de4ef --- /dev/null +++ b/test/dotnet-watch.Tests/Browser/BrowserTests.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text.Json; + +namespace Microsoft.DotNet.Watch.UnitTests; + +public class BrowserTests(ITestOutputHelper logger) : DotNetWatchTestBase(logger) +{ + [Fact] + public async Task LaunchesBrowserOnStart() + { + var testAsset = TestAssets.CopyTestAsset("WatchBrowserLaunchApp") + .WithSource(); + + App.Start(testAsset, [], testFlags: TestFlags.MockBrowser); + + // check that all app output is printed out: + await App.WaitForOutputLineContaining("Content root path:"); + + Assert.Contains(App.Process.Output, line => line.Contains("Application started. Press Ctrl+C to shut down.")); + Assert.Contains(App.Process.Output, line => line.Contains("Hosting environment: Development")); + + // Verify we launched the browser. + App.AssertOutputContains(MessageDescriptor.LaunchingBrowser.GetMessage("https://localhost:5001", "")); + } + + [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/aspnetcore/issues/63759 + public async Task BrowserDiagnostics() + { + var testAsset = TestAssets.CopyTestAsset("WatchRazorWithDeps") + .WithSource(); + + App.UseTestBrowser(); + + var url = $"http://localhost:{TestOptions.GetTestPort()}"; + var tfm = ToolsetInfo.CurrentTargetFramework; + + App.Start(testAsset, ["--urls", url], relativeProjectDirectory: "RazorApp", testFlags: TestFlags.ReadKeyFromStdin); + + await App.WaitForOutputLineContaining(MessageDescriptor.ConfiguredToUseBrowserRefresh); + await App.WaitForOutputLineContaining(MessageDescriptor.ConfiguredToLaunchBrowser); + await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); + + // Verify the browser has been launched. + await App.WaitUntilOutputContains($"🧪 Test browser opened at '{url}'."); + + // Verify the browser connected to the refresh server. + await App.WaitUntilOutputContains(MessageDescriptor.ConnectedToRefreshServer, "Browser #1"); + + App.Process.ClearOutput(); + + var homePagePath = Path.Combine(testAsset.Path, "RazorApp", "Components", "Pages", "Home.razor"); + + // rude edit: + UpdateSourceFile(homePagePath, src => src.Replace("/* member placeholder */", """ + public virtual int F() => 1; + """)); + + var errorMessage = $"{homePagePath}(13,9): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application."; + var jsonErrorMessage = JsonSerializer.Serialize(errorMessage); + + await App.WaitForOutputLineContaining(errorMessage); + + await App.WaitForOutputLineContaining("Do you want to restart your app?"); + + await App.WaitUntilOutputContains($$""" + 🧪 Received: {"type":"HotReloadDiagnosticsv1","diagnostics":[{{jsonErrorMessage}}]} + """); + + // auto restart next time: + App.SendKey('a'); + + // browser page is reloaded when the app restarts: + await App.WaitForOutputLineContaining(MessageDescriptor.ReloadingBrowser, $"RazorApp ({tfm})"); + + // browser page was reloaded after the app restarted: + await App.WaitUntilOutputContains(""" + 🧪 Received: Reload + """); + + await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); + + // another rude edit: + UpdateSourceFile(homePagePath, src => src.Replace("public virtual int F() => 1;", "/* member placeholder */")); + + errorMessage = $"{homePagePath}(11,5): error ENC0033: Deleting method 'F()' requires restarting the application."; + await App.WaitForOutputLineContaining("[auto-restart] " + errorMessage); + + await App.WaitUntilOutputContains($$""" + 🧪 Received: {"type":"HotReloadDiagnosticsv1","diagnostics":["Restarting application to apply changes ..."]} + """); + + await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); + + // browser page was reloaded after the app restarted: + await App.WaitUntilOutputContains(""" + 🧪 Received: Reload + """); + + // valid edit: + UpdateSourceFile(homePagePath, src => src.Replace("/* member placeholder */", "public int F() => 1;")); + + await App.WaitForOutputLineContaining(MessageDescriptor.HotReloadSucceeded); + + await App.WaitUntilOutputContains($$""" + 🧪 Received: {"type":"AspNetCoreHotReloadApplied"} + """); + } +} diff --git a/test/dotnet-watch.Tests/Build/EvaluationTests.cs b/test/dotnet-watch.Tests/Build/EvaluationTests.cs index 5a6786875334..4c1bcbdbb30a 100644 --- a/test/dotnet-watch.Tests/Build/EvaluationTests.cs +++ b/test/dotnet-watch.Tests/Build/EvaluationTests.cs @@ -236,8 +236,13 @@ public async Task ProjectReferences_OneLevel() var project1 = new TestProject("Project1") { + IsExe = true, TargetFrameworks = $"{ToolsetInfo.CurrentTargetFramework};net462", ReferencedProjects = { project2 }, + SourceFiles = + { + { "Project1.cs", s_emptyProgram }, + }, }; var testAsset = _testAssets.CreateTestProject(project1); @@ -271,8 +276,13 @@ public async Task TransitiveProjectReferences_TwoLevels() var project1 = new TestProject("Project1") { + IsExe = true, TargetFrameworks = $"{ToolsetInfo.CurrentTargetFramework};net462", ReferencedProjects = { project2 }, + SourceFiles = + { + { "Project1.cs", s_emptyProgram }, + }, }; var testAsset = _testAssets.CreateTestProject(project1); @@ -305,8 +315,13 @@ public async Task SingleTargetRoot_MultiTargetedDependency(bool specifyTargetFra var project1 = new TestProject("Project1") { + IsExe = true, TargetFrameworks = ToolsetInfo.CurrentTargetFramework, ReferencedProjects = { project2 }, + SourceFiles = + { + { "Project1.cs", s_emptyProgram }, + }, }; var testAsset = _testAssets.CreateTestProject(project1, identifier: specifyTargetFramework.ToString()); @@ -479,8 +494,13 @@ public async Task MsbuildOutput() var project1 = new TestProject("Project1") { + IsExe = true, TargetFrameworks = "net462", ReferencedProjects = { project2 }, + SourceFiles = + { + { "Program.cs", s_emptyProgram }, + }, }; var testAsset = _testAssets.CreateTestProject(project1); @@ -495,9 +515,9 @@ public async Task MsbuildOutput() Assert.Null(result); // note: msbuild prints errors to stdout, we match the pattern and report as error: - AssertEx.Equal( + Assert.Contains( $"[Error] {project1Path} : error NU1201: Project Project2 is not compatible with net462 (.NETFramework,Version=v4.6.2). Project Project2 supports: netstandard2.1 (.NETStandard,Version=v2.1)", - _logger.GetAndClearMessages().Single(m => m.Contains("error NU1201"))); + _logger.GetAndClearMessages()); } private readonly struct ExpectedFile(string path, string? staticAssetUrl = null, bool targetsOnly = false, bool graphOnly = false) diff --git a/test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs b/test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs index 24ee2076885b..95bbd1d79657 100644 --- a/test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs +++ b/test/dotnet-watch.Tests/CommandLine/LaunchSettingsTests.cs @@ -92,22 +92,15 @@ public async Task RunsWithIterationEnvVariable() App.Start(testAsset, []); - await App.AssertStarted(); + await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForFileChangeBeforeRestarting); - var source = Path.Combine(testAsset.Path, "Program.cs"); - var contents = File.ReadAllText(source); - const string messagePrefix = "DOTNET_WATCH_ITERATION = "; + App.AssertOutputContains("DOTNET_WATCH_ITERATION = 1"); + App.Process.ClearOutput(); - var value = await App.AssertOutputLineStartsWith(messagePrefix); - Assert.Equal(1, int.Parse(value, CultureInfo.InvariantCulture)); + UpdateSourceFile(Path.Combine(testAsset.Path, "Program.cs")); await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForFileChangeBeforeRestarting); - - UpdateSourceFile(source); - await App.AssertStarted(); - - value = await App.AssertOutputLineStartsWith(messagePrefix); - Assert.Equal(2, int.Parse(value, CultureInfo.InvariantCulture)); + App.AssertOutputContains("DOTNET_WATCH_ITERATION = 2"); } [Fact] diff --git a/test/dotnet-watch.Tests/ConsoleReporterTests.cs b/test/dotnet-watch.Tests/ConsoleReporterTests.cs index 7fb1c564451d..8df1601dc0dc 100644 --- a/test/dotnet-watch.Tests/ConsoleReporterTests.cs +++ b/test/dotnet-watch.Tests/ConsoleReporterTests.cs @@ -18,19 +18,19 @@ public void WritesToStandardStreams(bool suppressEmojis) var reporter = new ConsoleReporter(testConsole, verbose: true, quiet: false, suppressEmojis: suppressEmojis); reporter.Report(id: default, Emoji.Watch, MessageSeverity.Verbose, "verbose {0}"); - Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "⌚")} verbose {{0}}" + EOL, testConsole.GetOutput()); + Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "⌚")} verbose {{0}}" + EOL, testConsole.GetError()); testConsole.Clear(); reporter.Report(id: default, Emoji.Watch, MessageSeverity.Output, "out"); - Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "⌚")} out" + EOL, testConsole.GetOutput()); + Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "⌚")} out" + EOL, testConsole.GetError()); testConsole.Clear(); reporter.Report(id: default, Emoji.Warning, MessageSeverity.Warning, "warn"); - Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "⚠")} warn" + EOL, testConsole.GetOutput()); + Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "⚠")} warn" + EOL, testConsole.GetError()); testConsole.Clear(); reporter.Report(id: default, Emoji.Error, MessageSeverity.Error, "error"); - Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "❌")} error" + EOL, testConsole.GetOutput()); + Assert.Equal($"dotnet watch {(suppressEmojis ? ":" : "❌")} error" + EOL, testConsole.GetError()); testConsole.Clear(); } diff --git a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs index a7a1bcda1923..104438d08082 100644 --- a/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs +++ b/test/dotnet-watch.Tests/HotReload/ApplyDeltaTests.cs @@ -429,7 +429,7 @@ public async Task AutoRestartOnRudeEdit(bool nonInteractive) await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); App.AssertOutputContains(MessageDescriptor.RestartNeededToApplyChanges); - App.AssertOutputContains($"⌚ [auto-restart] {programPath}(38,11): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application."); + App.AssertOutputContains($"⌚ [auto-restart] {programPath}(39,11): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application."); App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Exited"); App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Launched"); App.Process.ClearOutput(); @@ -459,7 +459,7 @@ public async Task AutoRestartOnRudeEditAfterRestartPrompt() await App.AssertOutputLineStartsWith(" ❔ Do you want to restart your app? Yes (y) / No (n) / Always (a) / Never (v)", failure: _ => false); App.AssertOutputContains(MessageDescriptor.RestartNeededToApplyChanges); - App.AssertOutputContains($"❌ {programPath}(38,11): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application."); + App.AssertOutputContains($"❌ {programPath}(39,11): error ENC0023: Adding an abstract method or overriding an inherited method requires restarting the application."); App.Process.ClearOutput(); App.SendKey('a'); @@ -476,7 +476,7 @@ public async Task AutoRestartOnRudeEditAfterRestartPrompt() await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); App.AssertOutputContains(MessageDescriptor.RestartNeededToApplyChanges); - App.AssertOutputContains($"⌚ [auto-restart] {programPath}(38,1): error ENC0033: Deleting method 'F()' requires restarting the application."); + App.AssertOutputContains($"⌚ [auto-restart] {programPath}(39,1): error ENC0033: Deleting method 'F()' requires restarting the application."); App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Exited"); App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Launched"); } @@ -514,7 +514,7 @@ public async Task AutoRestartOnNoEffectEdit(bool nonInteractive) await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); App.AssertOutputContains(MessageDescriptor.RestartNeededToApplyChanges); - App.AssertOutputContains($"⌚ [auto-restart] {programPath}(16,19): warning ENC0118: Changing 'top-level code' might not have any effect until the application is restarted."); + App.AssertOutputContains($"⌚ [auto-restart] {programPath}(17,19): warning ENC0118: Changing 'top-level code' might not have any effect until the application is restarted."); App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Exited"); App.AssertOutputContains($"[WatchHotReloadApp ({ToolsetInfo.CurrentTargetFramework})] Launched"); App.AssertOutputContains(""); @@ -721,6 +721,8 @@ class AppUpdateHandler [PlatformSpecificFact(TestPlatforms.Windows)] public async Task GracefulTermination_Windows() { + var tfm = ToolsetInfo.CurrentTargetFramework; + var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp") .WithSource(); @@ -739,7 +741,7 @@ public async Task GracefulTermination_Windows() await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); - await App.WaitUntilOutputContains(new Regex(@"dotnet watch 🕵️ \[.*\] Windows Ctrl\+C handling enabled.")); + await App.WaitUntilOutputContains($"dotnet watch 🕵️ [WatchHotReloadApp ({tfm})] Windows Ctrl+C handling enabled."); await App.WaitUntilOutputContains("Started"); @@ -749,7 +751,38 @@ public async Task GracefulTermination_Windows() await App.WaitUntilOutputContains("exited with exit code 0."); } - [PlatformSpecificTheory(TestPlatforms.Windows, Skip = "https://github.com/dotnet/sdk/issues/49928")] // https://github.com/dotnet/sdk/issues/49307 + [PlatformSpecificFact(TestPlatforms.AnyUnix)] + public async Task GracefulTermination_Unix() + { + var tfm = ToolsetInfo.CurrentTargetFramework; + + var testAsset = TestAssets.CopyTestAsset("WatchHotReloadApp") + .WithSource(); + + var programPath = Path.Combine(testAsset.Path, "Program.cs"); + + UpdateSourceFile(programPath, src => src.Replace("// ", """ + using var termSignalRegistration = PosixSignalRegistration.Create(PosixSignal.SIGTERM, _ => + { + Console.WriteLine("SIGTERM detected! Performing cleanup..."); + }); + """)); + + App.Start(testAsset, [], testFlags: TestFlags.ReadKeyFromStdin); + + await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); + + await App.WaitUntilOutputContains($"dotnet watch 🕵️ [WatchHotReloadApp ({tfm})] Posix signal handlers registered."); + + await App.WaitUntilOutputContains("Started"); + + App.SendControlC(); + + await App.WaitForOutputLineContaining("SIGTERM detected! Performing cleanup..."); + await App.WaitUntilOutputContains("exited with exit code 0."); + } + + [PlatformSpecificTheory(TestPlatforms.Windows, Skip = "https://github.com/dotnet/sdk/issues/49928")] // https://github.com/dotnet/aspnetcore/issues/63759 [CombinatorialData] public async Task BlazorWasm(bool projectSpecifiesCapabilities) { @@ -777,7 +810,7 @@ public async Task BlazorWasm(bool projectSpecifiesCapabilities) App.AssertOutputContains(MessageDescriptor.ConfiguredToLaunchBrowser); // Browser is launched based on blazor-devserver output "Now listening on: ...". - await App.WaitUntilOutputContains($"dotnet watch ⌚ Launching browser: http://localhost:{port}"); + await App.WaitUntilOutputContains(MessageDescriptor.LaunchingBrowser.GetMessage($"http://localhost:{port}", "")); // Middleware should have been loaded to blazor-devserver before the browser is launched: App.AssertOutputContains("dbug: Microsoft.AspNetCore.Watch.BrowserRefresh.BlazorWasmHotReloadMiddleware[0]"); @@ -809,7 +842,7 @@ public async Task BlazorWasm(bool projectSpecifiesCapabilities) } } - [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307") + [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/aspnetcore/issues/63759 public async Task BlazorWasm_MSBuildWarning() { var testAsset = TestAssets @@ -831,7 +864,7 @@ public async Task BlazorWasm_MSBuildWarning() await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); } - [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307") + [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/aspnetcore/issues/63759 public async Task BlazorWasm_Restart() { var testAsset = TestAssets.CopyTestAsset("WatchBlazorWasm") @@ -847,14 +880,14 @@ public async Task BlazorWasm_Restart() App.AssertOutputContains(MessageDescriptor.PressCtrlRToRestart); // Browser is launched based on blazor-devserver output "Now listening on: ...". - await App.WaitUntilOutputContains($"dotnet watch ⌚ Launching browser: http://localhost:{port}"); + await App.WaitUntilOutputContains(MessageDescriptor.LaunchingBrowser.GetMessage($"http://localhost:{port}", "")); App.SendControlR(); await App.WaitUntilOutputContains(MessageDescriptor.ReloadingBrowser); } - [PlatformSpecificFact(TestPlatforms.Windows, Skip = "https://github.com/dotnet/sdk/issues/49928")] // "https://github.com/dotnet/sdk/issues/49307") + [PlatformSpecificFact(TestPlatforms.Windows, Skip = "https://github.com/dotnet/sdk/issues/49928")] // https://github.com/dotnet/aspnetcore/issues/63759 public async Task BlazorWasmHosted() { var testAsset = TestAssets.CopyTestAsset("WatchBlazorWasmHosted") @@ -878,7 +911,7 @@ public async Task BlazorWasmHosted() App.AssertOutputContains($"dotnet watch ⌚ [blazorhosted ({tfm})] Capabilities: 'Baseline AddMethodToExistingType AddStaticFieldToExistingType AddInstanceFieldToExistingType NewTypeDefinition ChangeCustomAttributes UpdateParameters GenericUpdateMethod GenericAddMethodToExistingType GenericAddFieldToExistingType AddFieldRva'"); } - [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307") + [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/aspnetcore/issues/63759 public async Task Razor_Component_ScopedCssAndStaticAssets() { var testAsset = TestAssets.CopyTestAsset("WatchRazorWithDeps") @@ -891,7 +924,7 @@ public async Task Razor_Component_ScopedCssAndStaticAssets() App.AssertOutputContains(MessageDescriptor.ConfiguredToUseBrowserRefresh); App.AssertOutputContains(MessageDescriptor.ConfiguredToLaunchBrowser); - App.AssertOutputContains($"dotnet watch ⌚ Launching browser: http://localhost:{port}"); + App.AssertOutputContains(MessageDescriptor.LaunchingBrowser.GetMessage($"http://localhost:{port}", "")); App.Process.ClearOutput(); var scopedCssPath = Path.Combine(testAsset.Path, "RazorClassLibrary", "Components", "Example.razor.css"); @@ -1114,7 +1147,7 @@ public static void PrintDirectoryName([CallerFilePathAttribute] string filePath await App.AssertOutputLineStartsWith("> NewSubdir", failure: _ => false); } - [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307") + [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/aspnetcore/issues/63759 public async Task Aspire_BuildError_ManualRestart() { var tfm = ToolsetInfo.CurrentTargetFramework; @@ -1135,8 +1168,13 @@ public async Task Aspire_BuildError_ManualRestart() // check that Aspire server output is logged via dotnet-watch reporter: await App.WaitUntilOutputContains("dotnet watch ⭐ Now listening on:"); - // wait until after DCP session started: - await App.WaitUntilOutputContains("dotnet watch ⭐ Session started: #1"); + // wait until after all DCP sessions have started: + await App.WaitUntilOutputContains("dotnet watch ⭐ Session started: #3"); + App.AssertOutputContains("dotnet watch ⭐ Session started: #1"); + App.AssertOutputContains("dotnet watch ⭐ Session started: #2"); + + // MigrationService terminated: + App.AssertOutputContains("dotnet watch ⭐ [#1] Sending 'sessionTerminated'"); // working directory of the service should be it's project directory: await App.WaitUntilOutputContains($"ApiService working directory: '{Path.GetDirectoryName(serviceProjectPath)}'"); @@ -1176,9 +1214,7 @@ public async Task Aspire_BuildError_ManualRestart() App.AssertOutputContains("Application is shutting down..."); - // We don't have means to gracefully terminate process on Windows, see https://github.com/dotnet/runtime/issues/109432 App.AssertOutputContains($"[WatchAspire.ApiService ({tfm})] Exited"); - App.AssertOutputContains(new Regex(@"dotnet watch ⌚ \[WatchAspire.ApiService \(net.*\)\] Process id [0-9]+ ran for [0-9]+ms and exited with exit code 0")); App.AssertOutputContains(MessageDescriptor.Building.GetMessage(serviceProjectPath)); App.AssertOutputContains("error CS0246: The type or namespace name 'WeatherForecast' could not be found"); @@ -1189,11 +1225,12 @@ public async Task Aspire_BuildError_ManualRestart() serviceSourcePath, serviceSource.Replace("WeatherForecast", "WeatherForecast2")); - await App.WaitForOutputLineContaining(MessageDescriptor.Capabilities, $"WatchAspire.ApiService ({tfm})"); + await App.WaitForOutputLineContaining(MessageDescriptor.ProjectsRestarted.GetMessage(1)); App.AssertOutputContains(MessageDescriptor.BuildSucceeded.GetMessage(serviceProjectPath)); App.AssertOutputContains(MessageDescriptor.ProjectsRebuilt); App.AssertOutputContains($"dotnet watch ⭐ Starting project: {serviceProjectPath}"); + App.Process.ClearOutput(); App.SendControlC(); @@ -1201,16 +1238,17 @@ public async Task Aspire_BuildError_ManualRestart() await App.WaitUntilOutputContains($"[WatchAspire.ApiService ({tfm})] Exited"); await App.WaitUntilOutputContains($"[WatchAspire.AppHost ({tfm})] Exited"); - await App.WaitUntilOutputContains(new Regex(@"dotnet watch ⌚ \[WatchAspire.ApiService \(net.*\)\] Process id [0-9]+ ran for [0-9]+ms and exited with exit code 0")); - await App.WaitUntilOutputContains(new Regex(@"dotnet watch ⌚ \[WatchAspire.AppHost \(net.*\)\] Process id [0-9]+ ran for [0-9]+ms and exited with exit code 0")); await App.WaitUntilOutputContains("dotnet watch ⭐ Waiting for server to shutdown ..."); App.AssertOutputContains("dotnet watch ⭐ Stop session #1"); - App.AssertOutputContains("dotnet watch ⭐ [#1] Sending 'sessionTerminated'"); + App.AssertOutputContains("dotnet watch ⭐ Stop session #2"); + App.AssertOutputContains("dotnet watch ⭐ Stop session #3"); + App.AssertOutputContains("dotnet watch ⭐ [#2] Sending 'sessionTerminated'"); + App.AssertOutputContains("dotnet watch ⭐ [#3] Sending 'sessionTerminated'"); } - [PlatformSpecificFact(TestPlatforms.Windows)] // "https://github.com/dotnet/sdk/issues/49307") + [PlatformSpecificFact(TestPlatforms.Windows)] // https://github.com/dotnet/aspnetcore/issues/63759 public async Task Aspire_NoEffect_AutoRestart() { var tfm = ToolsetInfo.CurrentTargetFramework; @@ -1226,18 +1264,32 @@ public async Task Aspire_NoEffect_AutoRestart() await App.WaitForOutputLineContaining(MessageDescriptor.WaitingForChanges); // wait until after DCP sessions have been started for all projects: - await App.WaitUntilOutputContains("dotnet watch ⭐ Session started: #2"); + await App.WaitUntilOutputContains("dotnet watch ⭐ Session started: #3"); + + // other services are waiting for completion of MigrationService: + App.AssertOutputContains("dotnet watch ⭐ Session started: #1"); + App.AssertOutputContains(MessageDescriptor.Exited, $"WatchAspire.MigrationService ({tfm})"); + App.AssertOutputContains("dotnet watch ⭐ [#1] Sending 'sessionTerminated'"); + + // migration service output should not be printed to dotnet-watch output, it hsould be sent via DCP as a notification: + App.AssertOutputContains("dotnet watch ⭐ [#1] Sending 'serviceLogs': log_message=' Migration complete', is_std_err=False"); + App.AssertOutputDoesNotContain(new Regex("^ +Migration complete")); + App.Process.ClearOutput(); // no-effect edit: UpdateSourceFile(webSourcePath, src => src.Replace("/* top-level placeholder */", "builder.Services.AddRazorComponents();")); await App.WaitForOutputLineContaining(MessageDescriptor.HotReloadChangeHandled); - await App.WaitUntilOutputContains("dotnet watch ⭐ Session started: #2"); + await App.WaitUntilOutputContains("dotnet watch ⭐ Session started: #3"); App.AssertOutputContains(MessageDescriptor.ProjectsRestarted.GetMessage(1)); App.AssertOutputDoesNotContain("⚠"); + // The process exited and should not participate in Hot Reload: + App.AssertOutputDoesNotContain($"[WatchAspire.MigrationService ({tfm})]"); + App.AssertOutputDoesNotContain("dotnet watch ⭐ [#1]"); + App.Process.ClearOutput(); // lambda body edit: @@ -1245,9 +1297,13 @@ public async Task Aspire_NoEffect_AutoRestart() await App.WaitForOutputLineContaining(MessageDescriptor.HotReloadChangeHandled); App.AssertOutputContains($"dotnet watch 🕵️ [WatchAspire.Web ({tfm})] Updates applied."); - App.AssertOutputDoesNotContain("Projects rebuilt"); - App.AssertOutputDoesNotContain("Projects restarted"); + App.AssertOutputDoesNotContain(MessageDescriptor.ProjectsRebuilt); + App.AssertOutputDoesNotContain(MessageDescriptor.ProjectsRestarted); App.AssertOutputDoesNotContain("⚠"); + + // The process exited and should not participate in Hot Reload: + App.AssertOutputDoesNotContain($"[WatchAspire.MigrationService ({tfm})]"); + App.AssertOutputDoesNotContain("dotnet watch ⭐ [#1]"); } } } diff --git a/test/dotnet-watch.Tests/HotReload/CompilationHandlerTests.cs b/test/dotnet-watch.Tests/HotReload/CompilationHandlerTests.cs index bedd9d2a7c5c..9ac5030c4eba 100644 --- a/test/dotnet-watch.Tests/HotReload/CompilationHandlerTests.cs +++ b/test/dotnet-watch.Tests/HotReload/CompilationHandlerTests.cs @@ -25,7 +25,7 @@ public async Task ReferenceOutputAssembly_False() var loggerFactory = new LoggerFactory(reporter); var logger = loggerFactory.CreateLogger("Test"); var projectGraph = ProjectGraphUtilities.TryLoadProjectGraph(options.ProjectPath, globalOptions: [], logger, projectGraphRequired: false, CancellationToken.None); - var handler = new CompilationHandler(loggerFactory, logger, processRunner); + var handler = new CompilationHandler(logger, processRunner); await handler.Workspace.UpdateProjectConeAsync(hostProject, CancellationToken.None); diff --git a/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs b/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs index 76993fbe54e3..499c04e68837 100644 --- a/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs +++ b/test/dotnet-watch.Tests/HotReload/RuntimeProcessLauncherTests.cs @@ -78,6 +78,7 @@ private static async Task Launch(string projectPath, TestRuntime projectOptions, new CancellationTokenSource(), onOutput: null, + onExit: null, restartOperation: startOp!, cancellationToken); @@ -525,7 +526,7 @@ public async Task RudeEditInProjectWithoutRunningProcess() // Terminate the process: Log($"Terminating process {runningProject.ProjectNode.GetDisplayName()} ..."); - await w.Service.ProjectLauncher.TerminateProcessAsync(runningProject, CancellationToken.None); + await runningProject.TerminateAsync(isRestarting: false); // rude edit in A (changing assembly level attribute): UpdateSourceFile(serviceSourceA2, """ diff --git a/test/dotnet-watch.Tests/TestUtilities/AssertEx.cs b/test/dotnet-watch.Tests/TestUtilities/AssertEx.cs index 6f2b2fc7e30a..aeaffc43f286 100644 --- a/test/dotnet-watch.Tests/TestUtilities/AssertEx.cs +++ b/test/dotnet-watch.Tests/TestUtilities/AssertEx.cs @@ -117,11 +117,11 @@ public static void Equal(T expected, T actual, IEqualityComparer? comparer if (expected == null) { - Fail("expected was null, but actual wasn't" + Environment.NewLine + message); + Fail("pattern was null, but actual wasn't" + Environment.NewLine + message); } else if (actual == null) { - Fail("actual was null, but expected wasn't" + Environment.NewLine + message); + Fail("actual was null, but pattern wasn't" + Environment.NewLine + message); } else if (!(comparer ?? AssertEqualityComparer.Instance).Equals(expected, actual)) { @@ -235,14 +235,25 @@ public static void EqualFileList(IEnumerable expectedFiles, IEnumerable< } public static void ContainsSubstring(string expected, IEnumerable items) + => AssertSubstringPresence(expected, items, expectedPresent: true); + + public static void DoesNotContainSubstring(string expected, IEnumerable items) + => AssertSubstringPresence(expected, items, expectedPresent: false); + + private static void AssertSubstringPresence(string expected, IEnumerable items, bool expectedPresent) { - if (items.Any(item => item.Contains(expected))) + if (items.Any(item => item.Contains(expected)) == expectedPresent) { return; } var message = new StringBuilder(); - message.AppendLine($"Expected output not found:"); + + + message.AppendLine(expectedPresent + ? "Expected text found in the output:" + : "Text not expected to be found in the output:"); + message.AppendLine(expected); message.AppendLine(); message.AppendLine("Actual output:"); @@ -256,15 +267,25 @@ public static void ContainsSubstring(string expected, IEnumerable items) } public static void ContainsPattern(Regex expected, IEnumerable items) + => AssertPatternPresence(expected, items, expectedPresent: true); + + public static void DoesNotContainPattern(Regex pattern, IEnumerable items) + => AssertPatternPresence(pattern, items, expectedPresent: false); + + private static void AssertPatternPresence(Regex pattern, IEnumerable items, bool expectedPresent) { - if (items.Any(item => expected.IsMatch(item))) + if (items.Any(item => pattern.IsMatch(item)) == expectedPresent) { return; } var message = new StringBuilder(); - message.AppendLine($"Expected pattern not found in the output:"); - message.AppendLine(expected.ToString()); + + message.AppendLine(expectedPresent + ? "Expected pattern found in the output:" + : "Pattern not expected to be found in the output:"); + + message.AppendLine(pattern.ToString()); message.AppendLine(); message.AppendLine("Actual output:"); diff --git a/test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs b/test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs index 8888b5634398..81bfd17aae20 100644 --- a/test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs +++ b/test/dotnet-watch.Tests/TestUtilities/WatchableApp.cs @@ -32,15 +32,21 @@ internal sealed class WatchableApp(DebugTestOutputLogger logger) : IDisposable public void AssertOutputContains(string message) => AssertEx.ContainsSubstring(message, Process.Output); - public void AssertOutputDoesNotContain(string message) - => Assert.DoesNotContain(Process.Output, line => line.Contains(message)); - public void AssertOutputContains(Regex pattern) => AssertEx.ContainsPattern(pattern, Process.Output); public void AssertOutputContains(MessageDescriptor descriptor, string projectDisplay = null) => AssertOutputContains(GetPattern(descriptor, projectDisplay)); + public void AssertOutputDoesNotContain(string message) + => AssertEx.DoesNotContainSubstring(message, Process.Output); + + public void AssertOutputDoesNotContain(Regex pattern) + => AssertEx.DoesNotContainPattern(pattern, Process.Output); + + public void AssertOutputDoesNotContain(MessageDescriptor descriptor, string projectDisplay = null) + => AssertOutputDoesNotContain(GetPattern(descriptor, projectDisplay)); + private static Regex GetPattern(MessageDescriptor descriptor, string projectDisplay = null) => new Regex(Regex.Replace(Regex.Escape((projectDisplay != null ? $"[{projectDisplay}] " : "") + descriptor.Format), @"\\\{[0-9]+\}", ".*")); @@ -203,5 +209,22 @@ public void SendKey(char c) Process.Process.StandardInput.Write(c); Process.Process.StandardInput.Flush(); } + + public void UseTestBrowser() + { + var path = GetTestBrowserPath(); + EnvironmentVariables.Add("DOTNET_WATCH_BROWSER_PATH", path); + + if (!OperatingSystem.IsWindows()) + { + File.SetUnixFileMode(path, UnixFileMode.UserExecute); + } + } + + public static string GetTestBrowserPath() + { + var exeExtension = OperatingSystem.IsWindows() ? ".exe" : string.Empty; + return Path.Combine(Path.GetDirectoryName(typeof(WatchableApp).Assembly.Location!)!, "test-browser", "dotnet-watch-test-browser" + exeExtension); + } } } diff --git a/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs b/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs index 1efccba2eb22..d673b618826c 100644 --- a/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs +++ b/test/dotnet-watch.Tests/Watch/BuildEvaluatorTests.cs @@ -16,9 +16,11 @@ private static DotNetWatchContext CreateContext(bool suppressMSBuildIncrementali SuppressMSBuildIncrementalism = suppressMSBuildIncrementalism }; + var processOutputReporter = new TestProcessOutputReporter(); + return new DotNetWatchContext() { - ProcessOutputReporter = new TestProcessOutputReporter(), + ProcessOutputReporter = processOutputReporter, Logger = NullLogger.Instance, BuildLogger = NullLogger.Instance, LoggerFactory = NullLoggerFactory.Instance, @@ -26,7 +28,7 @@ private static DotNetWatchContext CreateContext(bool suppressMSBuildIncrementali Options = new(), RootProjectOptions = TestOptions.ProjectOptions, EnvironmentOptions = environmentOptions, - BrowserLauncher = new BrowserLauncher(NullLogger.Instance, environmentOptions), + BrowserLauncher = new BrowserLauncher(NullLogger.Instance, processOutputReporter, environmentOptions), BrowserRefreshServerFactory = new BrowserRefreshServerFactory() }; } diff --git a/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs b/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs index a28d91c3423e..895a9d6b779e 100644 --- a/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs +++ b/test/dotnet-watch.Tests/Watch/NoRestoreTests.cs @@ -13,9 +13,11 @@ private static DotNetWatchContext CreateContext(string[] args = null, Environmen { environmentOptions ??= TestOptions.GetEnvironmentOptions(); + var processOutputReporter = new TestProcessOutputReporter(); + return new() { - ProcessOutputReporter = new TestProcessOutputReporter(), + ProcessOutputReporter = processOutputReporter, LoggerFactory = NullLoggerFactory.Instance, Logger = NullLogger.Instance, BuildLogger = NullLogger.Instance, @@ -23,7 +25,7 @@ private static DotNetWatchContext CreateContext(string[] args = null, Environmen Options = new(), RootProjectOptions = TestOptions.GetProjectOptions(args), EnvironmentOptions = environmentOptions, - BrowserLauncher = new BrowserLauncher(NullLogger.Instance, environmentOptions), + BrowserLauncher = new BrowserLauncher(NullLogger.Instance, processOutputReporter, environmentOptions), BrowserRefreshServerFactory = new BrowserRefreshServerFactory() }; } diff --git a/test/dotnet-watch.Tests/dotnet-watch.Tests.csproj b/test/dotnet-watch.Tests/dotnet-watch.Tests.csproj index 6a81d97e1ed3..772d35b80517 100644 --- a/test/dotnet-watch.Tests/dotnet-watch.Tests.csproj +++ b/test/dotnet-watch.Tests/dotnet-watch.Tests.csproj @@ -16,10 +16,23 @@ --> + + + + + + <_Files>@(TestBrowserOutput->'%(RootDir)%(Directory)*.*') + + + <_FileItem Include="$(_Files)" /> + + + + diff --git a/test/dotnet.Tests/CommandTests/MSBuild/FakeTelemetry.cs b/test/dotnet.Tests/CommandTests/MSBuild/FakeTelemetry.cs index f5624bf0a482..bcb6a6dbfde1 100644 --- a/test/dotnet.Tests/CommandTests/MSBuild/FakeTelemetry.cs +++ b/test/dotnet.Tests/CommandTests/MSBuild/FakeTelemetry.cs @@ -9,12 +9,14 @@ namespace Microsoft.DotNet.Cli.MSBuild.Tests { public class FakeTelemetry : ITelemetry { - public bool Enabled { get; set; } + public bool Enabled { get; set; } = true; + + private readonly List _logEntries = new List(); public void TrackEvent(string eventName, IDictionary properties, IDictionary measurements) { - LogEntry = new LogEntry { EventName = eventName, Properties = properties, Measurement = measurements }; - + var entry = new LogEntry { EventName = eventName, Properties = properties, Measurement = measurements }; + _logEntries.Add(entry); } public void Flush() @@ -25,8 +27,8 @@ public void Dispose() { } - public LogEntry LogEntry { get; private set; } + public LogEntry LogEntry => _logEntries.Count > 0 ? _logEntries[_logEntries.Count - 1] : null; + public IReadOnlyList LogEntries => _logEntries.AsReadOnly(); } - } diff --git a/test/dotnet.Tests/CommandTests/MSBuild/GivenDotnetBuildInvocation.cs b/test/dotnet.Tests/CommandTests/MSBuild/GivenDotnetBuildInvocation.cs index 08c5ee7071cd..8b6c7de5b074 100644 --- a/test/dotnet.Tests/CommandTests/MSBuild/GivenDotnetBuildInvocation.cs +++ b/test/dotnet.Tests/CommandTests/MSBuild/GivenDotnetBuildInvocation.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.DotNet.Cli.Commands.Restore; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Tests; using BuildCommand = Microsoft.DotNet.Cli.Commands.Build.BuildCommand; namespace Microsoft.DotNet.Cli.MSBuild.Tests @@ -10,8 +12,8 @@ namespace Microsoft.DotNet.Cli.MSBuild.Tests public class GivenDotnetBuildInvocation : IClassFixture { string[] ExpectedPrefix = ["-maxcpucount", "--verbosity:m", "-tlp:default=auto", "-nologo"]; - public static string[] RestoreExpectedPrefixForImplicitRestore = [..RestoringCommand.RestoreOptimizationProperties.Select(kvp => $"--restoreProperty:{kvp.Key}={kvp.Value}")]; - public static string[] RestoreExpectedPrefixForSeparateRestore = [..RestoringCommand.RestoreOptimizationProperties.Select(kvp => $"--property:{kvp.Key}={kvp.Value}")]; + public static string[] RestoreExpectedPrefixForImplicitRestore = [.. RestoringCommand.RestoreOptimizationProperties.Select(kvp => $"--restoreProperty:{kvp.Key}={kvp.Value}")]; + public static string[] RestoreExpectedPrefixForSeparateRestore = [.. RestoringCommand.RestoreOptimizationProperties.Select(kvp => $"--property:{kvp.Key}={kvp.Value}")]; const string NugetInteractiveProperty = "--property:NuGetInteractive=false"; @@ -118,5 +120,47 @@ public void MsbuildInvocationIsCorrectForSeparateRestore( .BeEquivalentTo([.. ExpectedPrefix, "-consoleloggerparameters:Summary", NugetInteractiveProperty, .. expectedAdditionalArgs]); }); } + + [Theory] + [MemberData(memberName: nameof(TelemetryCommonPropertiesTests.LLMTelemetryTestCases), MemberType =typeof(TelemetryCommonPropertiesTests))] + public void WhenLLMIsDetectedTLLiveUpdateIsDisabled(Dictionary? llmEnvVarsToSet, string? expectedLLMName) + { + CommandDirectoryContext.PerformActionWithBasePath(WorkingDirectory, () => + { + try + { + // Set environment variables to simulate LLM environment + if (llmEnvVarsToSet is not null) + { + foreach (var (key, value) in llmEnvVarsToSet) + { + Environment.SetEnvironmentVariable(key, value); + } + } + + var command = (RestoringCommand)BuildCommand.FromArgs([]); + + if (expectedLLMName is not null) + { + command.GetArgumentTokensToMSBuild().Should().Contain(Constants.TerminalLogger_DisableNodeDisplay); + } + else + { + command.GetArgumentTokensToMSBuild().Should().NotContain(Constants.TerminalLogger_DisableNodeDisplay); + } + } + finally + { + // Clear the environment variables after the test + if (llmEnvVarsToSet is not null) + { + foreach (var (key, value) in llmEnvVarsToSet) + { + Environment.SetEnvironmentVariable(key, null); + } + } + } + }); + } } } diff --git a/test/dotnet.Tests/CommandTests/MSBuild/GivenMSBuildLogger.cs b/test/dotnet.Tests/CommandTests/MSBuild/GivenMSBuildLogger.cs index 75fd2ef787dd..12a75b9493ee 100644 --- a/test/dotnet.Tests/CommandTests/MSBuild/GivenMSBuildLogger.cs +++ b/test/dotnet.Tests/CommandTests/MSBuild/GivenMSBuildLogger.cs @@ -5,6 +5,7 @@ using Microsoft.Build.Framework; using Microsoft.DotNet.Cli.Commands.MSBuild; +using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Cli.MSBuild.Tests { @@ -44,6 +45,7 @@ public void ItDoesNotMasksExceptionTelemetry() MSBuildLogger.FormatAndSend(fakeTelemetry, telemetryEventArgs); + fakeTelemetry.LogEntry.Should().NotBeNull(); fakeTelemetry.LogEntry.EventName.Should().Be(MSBuildLogger.SdkTaskBaseCatchExceptionTelemetryEventName); fakeTelemetry.LogEntry.Properties.Keys.Count.Should().Be(2); fakeTelemetry.LogEntry.Properties["exceptionType"].Should().Be("System.Exception"); @@ -108,20 +110,109 @@ public void ItCanSendProperties() { "RuntimeIdentifier", "null"}, { "SelfContained", "null"}, { "UseApphost", "null"}, - { "OutputType", "Library"}, + { "OutputType", "Library"} } }; MSBuildLogger.FormatAndSend(fakeTelemetry, telemetryEventArgs); - fakeTelemetry.LogEntry.Properties.Should().BeEquivalentTo(new Dictionary + fakeTelemetry.LogEntry.Properties.Should().BeEquivalentTo(telemetryEventArgs.Properties); + } + + [Fact] + public void ItAggregatesEvents() + { + var fakeTelemetry = new FakeTelemetry(); + fakeTelemetry.Enabled = true; + var logger = new MSBuildLogger(fakeTelemetry); + + var event1 = new TelemetryEventArgs + { + EventName = MSBuildLogger.TaskFactoryTelemetryAggregatedEventName, + Properties = new Dictionary + { + { "AssemblyTaskFactoryTasksExecutedCount", "2" }, + { "RoslynCodeTaskFactoryTasksExecutedCount", "1" } + } + }; + + var event2 = new TelemetryEventArgs + { + EventName = MSBuildLogger.TaskFactoryTelemetryAggregatedEventName, + Properties = new Dictionary + { + { "AssemblyTaskFactoryTasksExecutedCount", "3" }, + { "CustomTaskFactoryTasksExecutedCount", "2" } + } + }; + + var event3 = new TelemetryEventArgs + { + EventName = MSBuildLogger.TasksTelemetryAggregatedEventName, + Properties = new Dictionary + { + { "TasksExecutedCount", "3" }, + { "TaskHostTasksExecutedCount", "2" } + } + }; + + var event4 = new TelemetryEventArgs + { + EventName = MSBuildLogger.TasksTelemetryAggregatedEventName, + Properties = new Dictionary { - { "TargetFrameworkVersion", "9a871d7066260764d4cb5047e4b10570271d04bd1da275681a4b12bce0b27496"}, - { "RuntimeIdentifier", "fb329000228cc5a24c264c57139de8bf854fc86fc18bf1c04ab61a2b5cb4b921"}, - { "SelfContained", "fb329000228cc5a24c264c57139de8bf854fc86fc18bf1c04ab61a2b5cb4b921"}, - { "UseApphost", "fb329000228cc5a24c264c57139de8bf854fc86fc18bf1c04ab61a2b5cb4b921"}, - { "OutputType", "d77982267d9699c2a57bcab5bb975a1935f6427002f52fd4569762fd72db3a94"}, - }); + { "TasksExecutedCount", "5" } + } + }; + + logger.AggregateEvent(event1); + logger.AggregateEvent(event2); + logger.AggregateEvent(event3); + logger.AggregateEvent(event4); + + logger.SendAggregatedEventsOnBuildFinished(fakeTelemetry); + + fakeTelemetry.LogEntries.Should().HaveCount(2); + + var taskFactoryEntry = fakeTelemetry.LogEntries.FirstOrDefault(e => e.EventName == $"msbuild/{MSBuildLogger.TaskFactoryTelemetryAggregatedEventName}"); + taskFactoryEntry.Should().NotBeNull(); + taskFactoryEntry.Properties["AssemblyTaskFactoryTasksExecutedCount"].Should().Be("5"); // 2 + 3 + taskFactoryEntry.Properties["RoslynCodeTaskFactoryTasksExecutedCount"].Should().Be("1"); // 1 + 0 + taskFactoryEntry.Properties["CustomTaskFactoryTasksExecutedCount"].Should().Be("2"); // 0 + 2 + + var tasksEntry = fakeTelemetry.LogEntries.FirstOrDefault(e => e.EventName == $"msbuild/{MSBuildLogger.TasksTelemetryAggregatedEventName}"); + tasksEntry.Should().NotBeNull(); + tasksEntry.Properties["TasksExecutedCount"].Should().Be("8"); // 3 + 5 + tasksEntry.Properties["TaskHostTasksExecutedCount"].Should().Be("2"); // 2 + 0 + } + + [Fact] + public void ItIgnoresNonIntegerPropertiesDuringAggregation() + { + var fakeTelemetry = new FakeTelemetry(); + fakeTelemetry.Enabled = true; + var logger = new MSBuildLogger(fakeTelemetry); + + var eventArgs = new TelemetryEventArgs + { + EventName = MSBuildLogger.TaskFactoryTelemetryAggregatedEventName, + Properties = new Dictionary + { + { "AssemblyTaskFactoryTasksExecutedCount", "3" }, + { "InvalidProperty", "not-a-number" }, + { "InvalidProperty2", "1.234" }, + } + }; + + logger.AggregateEvent(eventArgs); + + logger.SendAggregatedEventsOnBuildFinished(fakeTelemetry); + + fakeTelemetry.LogEntry.Should().NotBeNull(); + fakeTelemetry.LogEntry.EventName.Should().Be($"msbuild/{MSBuildLogger.TaskFactoryTelemetryAggregatedEventName}"); + fakeTelemetry.LogEntry.Properties["AssemblyTaskFactoryTasksExecutedCount"].Should().Be("3"); + fakeTelemetry.LogEntry.Properties.Should().NotContainKey("InvalidProperty"); + fakeTelemetry.LogEntry.Properties.Should().NotContainKey("InvalidProperty2"); } } } diff --git a/test/dotnet.Tests/CommandTests/Package/Add/GivenDotnetPackageAdd.cs b/test/dotnet.Tests/CommandTests/Package/Add/GivenDotnetPackageAdd.cs index c924c1ac65bd..4bfa88903553 100644 --- a/test/dotnet.Tests/CommandTests/Package/Add/GivenDotnetPackageAdd.cs +++ b/test/dotnet.Tests/CommandTests/Package/Add/GivenDotnetPackageAdd.cs @@ -290,16 +290,45 @@ public void VersionRange(bool asArgument) cmd.StdErr.Should().BeEmpty(); } - [Fact] - public void FileBasedApp() + private string[]? GetFileBasedAppArgs(bool legacyForm, bool? versionOption, bool fileOption, bool noRestore, string packageName = "Humanizer") { + if (!legacyForm && !fileOption) + { + Log.WriteLine("Skipping invalid combination of parameters"); + return null; + } + + (string, string) commandArgs = legacyForm + ? ("add", "package") + : ("package", "add"); + + return [ + commandArgs.Item1, + .. (ReadOnlySpan)(fileOption ? [] : ["Program.cs"]), + commandArgs.Item2, + .. (ReadOnlySpan)(versionOption switch + { + true => [packageName, "--version", "2.14.1"], + false => [$"{packageName}@2.14.1"], + null => [packageName], + }), + .. (ReadOnlySpan)(fileOption ? ["--file", "Program.cs"] : []), + .. (ReadOnlySpan)(noRestore ? ["--no-restore"] : []), + ]; + } + + [Theory, CombinatorialData] + public void FileBasedApp(bool legacyForm, bool versionOption, bool fileOption, bool noRestore) + { + if (GetFileBasedAppArgs(legacyForm, versionOption, fileOption, noRestore) is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); var file = Path.Join(testInstance.Path, "Program.cs"); File.WriteAllText(file, """ Console.WriteLine(); """); - new DotnetCommand(Log, "package", "add", "Humanizer@2.14.1", "--file", "Program.cs") + new DotnetCommand(Log, args) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Pass(); @@ -311,12 +340,13 @@ public void FileBasedApp() """); } - [Theory] - [InlineData("Humanizer")] - [InlineData("humanizer")] + [Theory, CombinatorialData] public void FileBasedApp_ReplaceExisting( - string sourceFilePackageId) + [CombinatorialValues("Humanizer", "humanizer")] string sourceFilePackageId, + bool legacyForm, bool versionOption, bool fileOption, bool noRestore) { + if (GetFileBasedAppArgs(legacyForm, versionOption, fileOption, noRestore) is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); var file = Path.Join(testInstance.Path, "Program.cs"); File.WriteAllText(file, $""" @@ -324,7 +354,7 @@ public void FileBasedApp_ReplaceExisting( Console.WriteLine(); """); - new DotnetCommand(Log, "package", "add", "Humanizer@2.14.1", "--file", "Program.cs") + new DotnetCommand(Log, args) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Pass(); @@ -400,16 +430,18 @@ public void FileBasedApp_NoVersion_Prerelease(string[] inputVersions, string? _, """); } - [Fact] - public void FileBasedApp_NoVersionAndNoRestore() + [Theory, CombinatorialData] + public void FileBasedApp_NoVersionAndNoRestore(bool legacyForm, bool fileOption) { + if (GetFileBasedAppArgs(legacyForm, versionOption: null, fileOption, noRestore: true) is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); var file = Path.Join(testInstance.Path, "Program.cs"); File.WriteAllText(file, """ Console.WriteLine(); """); - new DotnetCommand(Log, "package", "add", "Humanizer", "--file", "Program.cs", "--no-restore") + new DotnetCommand(Log, args) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Pass(); @@ -421,9 +453,11 @@ public void FileBasedApp_NoVersionAndNoRestore() """); } - [Fact] - public void FileBasedApp_VersionAndPrerelease() + [Theory, CombinatorialData] + public void FileBasedApp_VersionAndPrerelease(bool legacyForm, bool versionOption, bool fileOption, bool noRestore) { + if (GetFileBasedAppArgs(legacyForm, versionOption, fileOption, noRestore) is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); var file = Path.Join(testInstance.Path, "Program.cs"); var source = """ @@ -431,7 +465,7 @@ public void FileBasedApp_VersionAndPrerelease() """; File.WriteAllText(file, source); - new DotnetCommand(Log, "package", "add", "Humanizer@2.14.1", "--file", "Program.cs", "--prerelease") + new DotnetCommand(Log, [.. args, "--prerelease"]) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Fail() @@ -440,9 +474,11 @@ public void FileBasedApp_VersionAndPrerelease() File.ReadAllText(file).Should().Be(source); } - [Fact] - public void FileBasedApp_InvalidPackage() + [Theory, CombinatorialData] + public void FileBasedApp_InvalidPackage(bool legacyForm, bool fileOption) { + if (GetFileBasedAppArgs(legacyForm, versionOption: null, fileOption, noRestore: false, packageName: "Microsoft.ThisPackageDoesNotExist") is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); var file = Path.Join(testInstance.Path, "Program.cs"); var source = """ @@ -450,7 +486,7 @@ public void FileBasedApp_InvalidPackage() """; File.WriteAllText(file, source); - new DotnetCommand(Log, "package", "add", "Microsoft.ThisPackageDoesNotExist", "--file", "Program.cs") + new DotnetCommand(Log, args) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Fail(); @@ -458,16 +494,18 @@ public void FileBasedApp_InvalidPackage() File.ReadAllText(file).Should().Be(source); } - [Fact] - public void FileBasedApp_InvalidPackage_NoRestore() + [Theory, CombinatorialData] + public void FileBasedApp_InvalidPackage_NoRestore(bool legacyForm, bool fileOption) { + if (GetFileBasedAppArgs(legacyForm, versionOption: null, fileOption, noRestore: true, packageName: "Microsoft.ThisPackageDoesNotExist") is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); var file = Path.Join(testInstance.Path, "Program.cs"); File.WriteAllText(file, """ Console.WriteLine(); """); - new DotnetCommand(Log, "package", "add", "Microsoft.ThisPackageDoesNotExist", "--file", "Program.cs", "--no-restore") + new DotnetCommand(Log, args) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Pass(); @@ -479,9 +517,11 @@ public void FileBasedApp_InvalidPackage_NoRestore() """); } - [Fact] - public void FileBasedApp_CentralPackageManagement() + [Theory, CombinatorialData] + public void FileBasedApp_CentralPackageManagement(bool legacyForm, bool versionOption, bool fileOption, bool noRestore) { + if (GetFileBasedAppArgs(legacyForm, versionOption, fileOption, noRestore) is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); var file = Path.Join(testInstance.Path, "Program.cs"); var source = """ @@ -498,7 +538,7 @@ public void FileBasedApp_CentralPackageManagement() """); - new DotnetCommand(Log, "package", "add", "Humanizer@2.14.1", "--file", "Program.cs") + new DotnetCommand(Log, args) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Pass(); @@ -522,8 +562,10 @@ public void FileBasedApp_CentralPackageManagement() } [Theory, CombinatorialData] - public void FileBasedApp_CentralPackageManagement_ReplaceExisting(bool wasInFile) + public void FileBasedApp_CentralPackageManagement_ReplaceExisting(bool wasInFile, bool legacyForm, bool versionOption, bool fileOption, bool noRestore) { + if (GetFileBasedAppArgs(legacyForm, versionOption, fileOption, noRestore) is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); var file = Path.Join(testInstance.Path, "Program.cs"); var source = """ @@ -553,7 +595,7 @@ public void FileBasedApp_CentralPackageManagement_ReplaceExisting(bool wasInFile """); - new DotnetCommand(Log, "package", "add", "Humanizer@2.14.1", "--file", "Program.cs") + new DotnetCommand(Log, args) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Pass(); @@ -576,9 +618,11 @@ public void FileBasedApp_CentralPackageManagement_ReplaceExisting(bool wasInFile """); } - [Fact] - public void FileBasedApp_CentralPackageManagement_NoVersionSpecified() + [Theory, CombinatorialData] + public void FileBasedApp_CentralPackageManagement_NoVersionSpecified(bool legacyForm, bool fileOption) { + if (GetFileBasedAppArgs(legacyForm, versionOption: null, fileOption, noRestore: false, packageName: "A") is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); string[] versions = ["0.0.5", "0.9.0", "1.0.0-preview.3"]; @@ -602,7 +646,7 @@ public void FileBasedApp_CentralPackageManagement_NoVersionSpecified() """); - new DotnetCommand(Log, "package", "add", "A", "--file", "Program.cs") + new DotnetCommand(Log, args) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Pass(); @@ -624,9 +668,11 @@ public void FileBasedApp_CentralPackageManagement_NoVersionSpecified() """); } - [Fact] - public void FileBasedApp_CentralPackageManagement_NoVersionSpecified_KeepExisting() + [Theory, CombinatorialData] + public void FileBasedApp_CentralPackageManagement_NoVersionSpecified_KeepExisting(bool legacyForm, bool fileOption, bool noRestore) { + if (GetFileBasedAppArgs(legacyForm, versionOption: null, fileOption, noRestore) is not { } args) return; + var testInstance = _testAssetsManager.CreateTestDirectory(); var file = Path.Join(testInstance.Path, "Program.cs"); var source = """ @@ -648,7 +694,7 @@ public void FileBasedApp_CentralPackageManagement_NoVersionSpecified_KeepExistin """; File.WriteAllText(directoryPackagesProps, directoryPackagesPropsSource); - new DotnetCommand(Log, "package", "add", "Humanizer", "--file", "Program.cs") + new DotnetCommand(Log, args) .WithWorkingDirectory(testInstance.Path) .Execute() .Should().Pass(); diff --git a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs index 27bf71fa48dc..cb7fd4331429 100644 --- a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs +++ b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs @@ -1,6 +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 System.Security; +using System.Text.RegularExpressions; using Microsoft.CodeAnalysis.Text; using Microsoft.DotNet.Cli.Commands; using Microsoft.DotNet.Cli.Commands.Run; @@ -56,14 +58,139 @@ public void SameAsTemplate() var dotnetProjectConvertProjectText = File.ReadAllText(dotnetProjectConvertProject); var dotnetNewConsoleProjectText = File.ReadAllText(dotnetNewConsoleProject); - // There are some differences: we add PublishAot=true. + // There are some differences: we add PublishAot=true, PackAsTool=true, and UserSecretsId. var patchedDotnetProjectConvertProjectText = dotnetProjectConvertProjectText - .Replace(" true" + Environment.NewLine, string.Empty); + .Replace(""" + true + true + + """, string.Empty); + patchedDotnetProjectConvertProjectText = Regex.Replace(patchedDotnetProjectConvertProjectText, + """ [^<]*<\/UserSecretsId>""" + Environment.NewLine, string.Empty); patchedDotnetProjectConvertProjectText.Should().Be(dotnetNewConsoleProjectText) .And.StartWith(""""""); } + [Theory] // https://github.com/dotnet/sdk/issues/50832 + [InlineData("File", "Lib", "../Lib", "Project", "../Lib/lib.csproj")] + [InlineData(".", "Lib", "./Lib", "Project", "../Lib/lib.csproj")] + [InlineData(".", "Lib", "Lib/../Lib", "Project", "../Lib/lib.csproj")] + [InlineData("File", "Lib", "../Lib", "File/Project", "../../Lib/lib.csproj")] + [InlineData("File", "Lib", "..\\Lib", "File/Project", "../../Lib/lib.csproj")] + public void ProjectReference_RelativePaths(string fileDir, string libraryDir, string reference, string outputDir, string convertedReference) + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + + var libraryDirFullPath = Path.Join(testInstance.Path, libraryDir); + Directory.CreateDirectory(libraryDirFullPath); + File.WriteAllText(Path.Join(libraryDirFullPath, "lib.cs"), """ + public static class C + { + public static void M() + { + System.Console.WriteLine("Hello from library"); + } + } + """); + File.WriteAllText(Path.Join(libraryDirFullPath, "lib.csproj"), $""" + + + {ToolsetInfo.CurrentTargetFramework} + + + """); + + var fileDirFullPath = Path.Join(testInstance.Path, fileDir); + Directory.CreateDirectory(fileDirFullPath); + File.WriteAllText(Path.Join(fileDirFullPath, "app.cs"), $""" + #:project {reference} + C.M(); + """); + + var expectedOutput = "Hello from library"; + + new DotnetCommand(Log, "run", "app.cs") + .WithWorkingDirectory(fileDirFullPath) + .Execute() + .Should().Pass() + .And.HaveStdOut(expectedOutput); + + var outputDirFullPath = Path.Join(testInstance.Path, outputDir); + new DotnetCommand(Log, "project", "convert", "app.cs", "-o", outputDirFullPath) + .WithWorkingDirectory(fileDirFullPath) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(outputDirFullPath) + .Execute() + .Should().Pass() + .And.HaveStdOut(expectedOutput); + + File.ReadAllText(Path.Join(outputDirFullPath, "app.csproj")) + .Should().Contain($""" + + """); + } + + [Fact] // https://github.com/dotnet/sdk/issues/50832 + public void ProjectReference_FullPath() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + + var libraryDirFullPath = Path.Join(testInstance.Path, "Lib"); + Directory.CreateDirectory(libraryDirFullPath); + File.WriteAllText(Path.Join(libraryDirFullPath, "lib.cs"), """ + public static class C + { + public static void M() + { + System.Console.WriteLine("Hello from library"); + } + } + """); + File.WriteAllText(Path.Join(libraryDirFullPath, "lib.csproj"), $""" + + + {ToolsetInfo.CurrentTargetFramework} + + + """); + + var fileDirFullPath = Path.Join(testInstance.Path, "File"); + Directory.CreateDirectory(fileDirFullPath); + File.WriteAllText(Path.Join(fileDirFullPath, "app.cs"), $""" + #:project {libraryDirFullPath} + C.M(); + """); + + var expectedOutput = "Hello from library"; + + new DotnetCommand(Log, "run", "app.cs") + .WithWorkingDirectory(fileDirFullPath) + .Execute() + .Should().Pass() + .And.HaveStdOut(expectedOutput); + + var outputDirFullPath = Path.Join(testInstance.Path, "File/Project"); + new DotnetCommand(Log, "project", "convert", "app.cs", "-o", outputDirFullPath) + .WithWorkingDirectory(fileDirFullPath) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(outputDirFullPath) + .Execute() + .Should().Pass() + .And.HaveStdOut(expectedOutput); + + File.ReadAllText(Path.Join(outputDirFullPath, "app.csproj")) + .Should().Contain($""" + + """); + } + [Fact] public void DirectoryAlreadyExists() { @@ -648,7 +775,7 @@ public void ProcessingSucceeds() .Should().Be("Console.WriteLine();"); File.ReadAllText(Path.Join(testInstance.Path, "Program", "Program.csproj")) - .Should().Be($""" + .Should().Match($""" @@ -657,6 +784,8 @@ public void ProcessingSucceeds() enable enable true + true + Program-* @@ -668,6 +797,135 @@ public void ProcessingSucceeds() """); } + [Theory, CombinatorialData] + public void UserSecretsId_Overridden_ViaDirective(bool hasDirectiveBuildProps) + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ + #:property UserSecretsId=MyIdFromDirective + Console.WriteLine(); + """); + + if (hasDirectiveBuildProps) + { + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + + + MyIdFromDirBuildProps + + + """); + } + + new DotnetCommand(Log, "project", "convert", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + File.ReadAllText(Path.Join(testInstance.Path, "Program", "Program.csproj")) + .Should().Be($""" + + + + Exe + {ToolsetInfo.CurrentTargetFramework} + enable + enable + true + true + MyIdFromDirective + + + + + """); + } + + [Fact] + public void UserSecretsId_Overridden_ViaDirectoryBuildProps() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ + Console.WriteLine(); + """); + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + + + MyIdFromDirBuildProps + + + """); + + new DotnetCommand(Log, "project", "convert", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + File.ReadAllText(Path.Join(testInstance.Path, "Program", "Program.csproj")) + .Should().Be($""" + + + + Exe + {ToolsetInfo.CurrentTargetFramework} + enable + enable + true + true + + + + + """); + } + + [Theory, CombinatorialData] + public void UserSecretsId_Overridden_SameAsImplicit(bool hasDirective, bool hasDirectiveBuildProps) + { + const string implicitValue = "$(MSBuildProjectName)-$([MSBuild]::StableStringHash($(MSBuildProjectFullPath.ToLowerInvariant()), 'Sha256'))"; + + var testInstance = _testAssetsManager.CreateTestDirectory(); + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), $""" + {(hasDirective ? $"#:property UserSecretsId={implicitValue}" : "")} + Console.WriteLine(); + """); + + if (hasDirectiveBuildProps) + { + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), $""" + + + {SecurityElement.Escape(implicitValue)} + + + """); + } + + new DotnetCommand(Log, "project", "convert", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + File.ReadAllText(Path.Join(testInstance.Path, "Program", "Program.csproj")) + .Should().Match($""" + + + + Exe + {ToolsetInfo.CurrentTargetFramework} + enable + enable + true + true + {(hasDirective ? SecurityElement.Escape(implicitValue) : "Program-*")} + + + + + """); + } + [Fact] public void Directives() { @@ -691,6 +949,7 @@ public void Directives() enable enable true + true net472 preview @@ -721,6 +980,7 @@ public void Directives_AllDefaultOverridden() #:property TargetFramework=net472 #:property Nullable=disable #:property PublishAot=false + #:property PackAsTool=false #:property Custom=1 #:property ImplicitUsings=disable Console.WriteLine(); @@ -733,6 +993,7 @@ public void Directives_AllDefaultOverridden() net472 disable false + false 1 disable @@ -762,6 +1023,7 @@ public void Directives_Variable() enable enable true + true MyValue @@ -799,6 +1061,7 @@ public void Directives_DirectoryPath() enable enable true + true @@ -837,6 +1100,7 @@ public void Directives_Separators() enable enable true + true One=a/b Two/a=b @@ -946,6 +1210,7 @@ public void Directives_Escaping() enable enable true + true <test"> @@ -980,6 +1245,7 @@ public void Directives_Whitespace() enable enable true + true Value "My package with spaces" @@ -1006,6 +1272,7 @@ public void Directives_BlankLines() enable enable true + true @@ -1071,6 +1338,7 @@ public void Directives_AfterToken() enable enable true + true 1 2 @@ -1117,6 +1385,7 @@ public void Directives_AfterIf() enable enable true + true 1 2 @@ -1160,6 +1429,7 @@ public void Directives_Comments() enable enable true + true 1 2 @@ -1267,6 +1537,7 @@ public void Directives_VersionedSdkFirst() enable enable true + true diff --git a/test/dotnet.Tests/CommandTests/Run/FileBasedAppSourceEditorTests.cs b/test/dotnet.Tests/CommandTests/Run/FileBasedAppSourceEditorTests.cs index ea2480b032f4..371b29d7f314 100644 --- a/test/dotnet.Tests/CommandTests/Run/FileBasedAppSourceEditorTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/FileBasedAppSourceEditorTests.cs @@ -347,6 +347,45 @@ public void GroupWithoutSpace() """)); } + /// + /// New package directive should be sorted into the correct location in the package group. + /// + [Fact] + public void Sort() + { + Verify( + """ + #:property X=Y + #:package B@C + #:package X@Y + #:project D + #:package E + + Console.WriteLine(); + """, + (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "Test", Version = "1.0.0" }), + """ + #:property X=Y + #:package B@C + #:package Test@1.0.0 + #:package X@Y + #:project D + #:package E + + Console.WriteLine(); + """), + (static editor => editor.Remove(editor.Directives[2]), + """ + #:property X=Y + #:package B@C + #:package X@Y + #:project D + #:package E + + Console.WriteLine(); + """)); + } + [Fact] public void OtherDirectives() { @@ -371,6 +410,33 @@ public void OtherDirectives() """)); } + /// + /// Shebang directive should always stay first. + /// + [Fact] + public void Shebang() + { + Verify( + """ + #!/test + Console.WriteLine(); + """, + (static editor => editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" }), + """ + #!/test + + #:package MyPackage@1.0.0 + + Console.WriteLine(); + """), + (static editor => editor.Remove(editor.Directives[1]), + """ + #!/test + + Console.WriteLine(); + """)); + } + [Fact] public void AfterTokens() { diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index 2c87e8cc7469..541788369fb5 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -332,10 +332,10 @@ public void FilePath_AsProjectArgument() ///
[Theory] // error MSB1003: Specify a project or solution file. The current working directory does not contain a project or solution file. - [InlineData("build", "MSB1003")] + [InlineData("build", "MSB1003", false)] // dotnet watch: Could not find a MSBuild project file in '...'. Specify which project to use with the --project option. - [InlineData("watch", "--project")] - public void Precedence_BuiltInCommand(string cmd, string error) + [InlineData("watch", "--project", true)] + public void Precedence_BuiltInCommand(string cmd, string error, bool errorInStdErr) { var testInstance = _testAssetsManager.CreateTestDirectory(); File.WriteAllText(Path.Join(testInstance.Path, cmd), """ @@ -348,18 +348,26 @@ public void Precedence_BuiltInCommand(string cmd, string error) """); // dotnet build -> built-in command - new DotnetCommand(Log, cmd) + var failure = new DotnetCommand(Log, cmd) .WithWorkingDirectory(testInstance.Path) .Execute() - .Should().Fail() - .And.HaveStdOutContaining(error); + .Should().Fail(); + + if (errorInStdErr) + { + failure.And.HaveStdErrContaining(error); + } + else + { + failure.And.HaveStdOutContaining(error); + } // dotnet ./build -> file-based app new DotnetCommand(Log, $"./{cmd}") - .WithWorkingDirectory(testInstance.Path) - .Execute() - .Should().Pass() - .And.HaveStdOut("hello 1"); + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOut("hello 1"); // dotnet run build -> file-based app new DotnetCommand(Log, "run", cmd) @@ -870,6 +878,128 @@ public void DirectoryBuildProps() .And.HaveStdOut("Hello from TestName"); } + /// + /// Overriding default (implicit) properties of file-based apps via implicit build files. + /// + [Fact] + public void DefaultProps_DirectoryBuildProps() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), """ + Console.WriteLine("Hi"); + """); + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + + + disable + + + """); + + new DotnetCommand(Log, "run", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Fail() + // error CS0103: The name 'Console' does not exist in the current context + .And.HaveStdOutContaining("error CS0103"); + + // Converting to a project should not change the behavior. + + new DotnetCommand(Log, "project", "convert", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run") + .WithWorkingDirectory(Path.Join(testInstance.Path, "Program")) + .Execute() + .Should().Fail() + // error CS0103: The name 'Console' does not exist in the current context + .And.HaveStdOutContaining("error CS0103"); + } + + /// + /// Overriding default (implicit) properties of file-based apps from custom SDKs. + /// + [Fact] + public void DefaultProps_CustomSdk() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + + var sdkDir = Path.Join(testInstance.Path, "MySdk"); + Directory.CreateDirectory(sdkDir); + File.WriteAllText(Path.Join(sdkDir, "Sdk.props"), """ + + + disable + + + """); + File.WriteAllText(Path.Join(sdkDir, "Sdk.targets"), """ + + """); + File.WriteAllText(Path.Join(sdkDir, "MySdk.csproj"), $""" + + + {ToolsetInfo.CurrentTargetFramework} + MSBuildSdk + false + + + + + + """); + + new DotnetCommand(Log, "pack") + .WithWorkingDirectory(sdkDir) + .Execute() + .Should().Pass(); + + var appDir = Path.Join(testInstance.Path, "app"); + Directory.CreateDirectory(appDir); + File.WriteAllText(Path.Join(appDir, "NuGet.config"), $""" + + + + + + + """); + File.WriteAllText(Path.Join(appDir, "Program.cs"), """ + #:sdk Microsoft.NET.Sdk + #:sdk MySdk@1.0.0 + Console.WriteLine("Hi"); + """); + + // Use custom package cache to avoid reuse of the custom SDK packed by previous test runs. + var packagesDir = Path.Join(testInstance.Path, ".packages"); + + new DotnetCommand(Log, "run", "Program.cs") + .WithEnvironmentVariable("NUGET_PACKAGES", packagesDir) + .WithWorkingDirectory(appDir) + .Execute() + .Should().Fail() + // error CS0103: The name 'Console' does not exist in the current context + .And.HaveStdOutContaining("error CS0103"); + + // Converting to a project should not change the behavior. + + new DotnetCommand(Log, "project", "convert", "Program.cs") + .WithEnvironmentVariable("NUGET_PACKAGES", packagesDir) + .WithWorkingDirectory(appDir) + .Execute() + .Should().Pass(); + + new DotnetCommand(Log, "run") + .WithEnvironmentVariable("NUGET_PACKAGES", packagesDir) + .WithWorkingDirectory(Path.Join(appDir, "Program")) + .Execute() + .Should().Fail() + // error CS0103: The name 'Console' does not exist in the current context + .And.HaveStdOutContaining("error CS0103"); + } + [Fact] public void ComputeRunArguments_Success() { @@ -1695,7 +1825,6 @@ public void Pack() var testInstance = _testAssetsManager.CreateTestDirectory(); var programFile = Path.Join(testInstance.Path, "MyFileBasedTool.cs"); File.WriteAllText(programFile, """ - #:property PackAsTool=true Console.WriteLine($"Hello; EntryPointFilePath set? {AppContext.GetData("EntryPointFilePath") is string}"); #if !DEBUG Console.WriteLine("Release config"); @@ -1742,7 +1871,6 @@ public void Pack_CustomPath() var testInstance = _testAssetsManager.CreateTestDirectory(); var programFile = Path.Join(testInstance.Path, "MyFileBasedTool.cs"); File.WriteAllText(programFile, """ - #:property PackAsTool=true #:property PackageOutputPath=custom Console.WriteLine($"Hello; EntryPointFilePath set? {AppContext.GetData("EntryPointFilePath") is string}"); """); @@ -2191,6 +2319,70 @@ public void ProjectReference_Errors() string.Format(CliStrings.MoreThanOneProjectInDirectory, Path.Join(testInstance.Path, "dir/")))); } + [Theory] // https://github.com/dotnet/aspnetcore/issues/63440 + [InlineData(true, null)] + [InlineData(false, null, Skip = "Needs https://github.com/dotnet/aspnetcore/pull/63496")] + [InlineData(true, "test-id")] + [InlineData(false, "test-id", Skip = "Needs https://github.com/dotnet/aspnetcore/pull/63496")] + public void UserSecrets(bool useIdArg, string? userSecretsId) + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + + string code = $""" + #:package Microsoft.Extensions.Configuration.UserSecrets@{CSharpCompilerCommand.RuntimeVersion} + {(userSecretsId is null ? "" : $"#:property UserSecretsId={userSecretsId}")} + + using Microsoft.Extensions.Configuration; + + IConfigurationRoot config = new ConfigurationBuilder() + .AddUserSecrets() + .Build(); + + Console.WriteLine("v1"); + Console.WriteLine(config.GetDebugView()); + """; + + var programPath = Path.Join(testInstance.Path, "Program.cs"); + File.WriteAllText(programPath, code); + + if (useIdArg) + { + if (userSecretsId == null) + { + var result = new DotnetCommand(Log, "build", "-getProperty:UserSecretsId", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute(); + result.Should().Pass(); + userSecretsId = result.StdOut!.Trim(); + } + + new DotnetCommand(Log, "user-secrets", "set", "MySecret", "MyValue", "--id", userSecretsId) + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + } + else + { + new DotnetCommand(Log, "user-secrets", "set", "MySecret", "MyValue", "--file", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + } + + Build(testInstance, BuildLevel.All, expectedOutput: """ + v1 + MySecret=MyValue (JsonConfigurationProvider for 'secrets.json' (Optional)) + """); + + code = code.Replace("v1", "v2"); + File.WriteAllText(programPath, code); + + Build(testInstance, BuildLevel.Csc, expectedOutput: """ + v2 + MySecret=MyValue (JsonConfigurationProvider for 'secrets.json' (Optional)) + """); + } + /// /// Verifies that msbuild-based runs use CSC args equivalent to csc-only runs. /// Can regenerate CSC arguments template in . @@ -2725,24 +2917,97 @@ public void UpToDate_InvalidOptions() /// /// Up-to-date checks and optimizations currently don't support other included files. /// - [Fact] - public void UpToDate_DefaultItems() + [Theory, CombinatorialData] // https://github.com/dotnet/sdk/issues/50912 + public void UpToDate_DefaultItems(bool optOut) { var testInstance = _testAssetsManager.CreateTestDirectory(); - File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), s_programReadingEmbeddedResource); + var code = $""" + {(optOut ? "#:property FileBasedProgramCanSkipMSBuild=false" : "")} + {s_programReadingEmbeddedResource} + """; + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), code); + File.WriteAllText(Path.Join(testInstance.Path, "Resources.resx"), s_resx); + + Build(testInstance, BuildLevel.All, expectedOutput: "[MyString, TestValue]"); + + // Update the RESX file. + File.WriteAllText(Path.Join(testInstance.Path, "Resources.resx"), s_resx.Replace("TestValue", "UpdatedValue")); + + Build(testInstance, optOut ? BuildLevel.All : BuildLevel.None, expectedOutput: optOut ? "[MyString, UpdatedValue]" : "[MyString, TestValue]"); // note: outdated output (build skipped) + + // Update the C# file. + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), "//v2\n" + code); + + Build(testInstance, optOut ? BuildLevel.All : BuildLevel.Csc, expectedOutput: optOut ? "[MyString, UpdatedValue]" : "[MyString, TestValue]"); // note: outdated output (only CSC used) + + Build(testInstance, BuildLevel.All, ["--no-cache"], expectedOutput: "[MyString, UpdatedValue]"); + } + + /// + /// Combination of with optimization. + /// + [Theory, CombinatorialData] // https://github.com/dotnet/sdk/issues/50912 + public void UpToDate_DefaultItems_CscOnly(bool optOut) + { + var testInstance = _testAssetsManager.CreateTestDirectory(baseDirectory: OutOfTreeBaseDirectory); + var code = $""" + {(optOut ? "#:property FileBasedProgramCanSkipMSBuild=false" : "")} + {s_programReadingEmbeddedResource} + """; + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), code); + File.WriteAllText(Path.Join(testInstance.Path, "Resources.resx"), s_resx); + + Build(testInstance, optOut ? BuildLevel.All : BuildLevel.Csc, expectedOutput: optOut ? "[MyString, TestValue]" : "Resource not found"); + + // Update the RESX file. + File.WriteAllText(Path.Join(testInstance.Path, "Resources.resx"), s_resx.Replace("TestValue", "UpdatedValue")); + + Build(testInstance, optOut ? BuildLevel.All : BuildLevel.None, expectedOutput: optOut ? "[MyString, UpdatedValue]" : "Resource not found"); + + // Update the C# file. + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), "//v2\n" + code); + + Build(testInstance, optOut ? BuildLevel.All : BuildLevel.Csc, expectedOutput: optOut ? "[MyString, UpdatedValue]" : "Resource not found"); + + Build(testInstance, BuildLevel.All, ["--no-cache"], expectedOutput: "[MyString, UpdatedValue]"); + + // Update the C# file. + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), "//v3\n" + code); + + Build(testInstance, optOut ? BuildLevel.All : BuildLevel.Csc, expectedOutput: "[MyString, UpdatedValue]"); + } + + /// + /// Combination of with optimization. + /// + [Theory, CombinatorialData] // https://github.com/dotnet/sdk/issues/50912 + public void UpToDate_DefaultItems_CscOnly_AfterMSBuild(bool optOut) + { + var testInstance = _testAssetsManager.CreateTestDirectory(baseDirectory: OutOfTreeBaseDirectory); + var code = $""" + #:property Configuration=Release + {(optOut ? "#:property FileBasedProgramCanSkipMSBuild=false" : "")} + {s_programReadingEmbeddedResource} + """; + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), code); File.WriteAllText(Path.Join(testInstance.Path, "Resources.resx"), s_resx); Build(testInstance, BuildLevel.All, expectedOutput: "[MyString, TestValue]"); + // Update the C# file. + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), "//v2\n" + code); + + Build(testInstance, optOut ? BuildLevel.All : BuildLevel.Csc, expectedOutput: optOut ? "[MyString, TestValue]" : "[MyString, TestValue]"); + // Update the RESX file. File.WriteAllText(Path.Join(testInstance.Path, "Resources.resx"), s_resx.Replace("TestValue", "UpdatedValue")); - Build(testInstance, BuildLevel.None, expectedOutput: "[MyString, TestValue]"); // note: outdated output (build skipped) + Build(testInstance, optOut ? BuildLevel.All : BuildLevel.None, expectedOutput: optOut ? "[MyString, UpdatedValue]" : "[MyString, TestValue]"); // Update the C# file. - File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), "//v2\n" + s_programReadingEmbeddedResource); + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), "//v3\n" + code); - Build(testInstance, BuildLevel.Csc, expectedOutput: "[MyString, TestValue]"); // note: outdated output (only CSC used) + Build(testInstance, optOut ? BuildLevel.All : BuildLevel.Csc, expectedOutput: optOut ? "[MyString, UpdatedValue]" : "[MyString, TestValue]"); Build(testInstance, BuildLevel.All, ["--no-cache"], expectedOutput: "[MyString, UpdatedValue]"); } @@ -2970,6 +3235,27 @@ public void CscOnly_SpacesInPath() Build(testInstance, BuildLevel.Csc, expectedOutput: "v1", programFileName: programFileName); } + [Fact] // https://github.com/dotnet/sdk/issues/50778 + public void CscOnly_Args() + { + var testInstance = _testAssetsManager.CreateTestDirectory(baseDirectory: OutOfTreeBaseDirectory); + var programPath = Path.Join(testInstance.Path, "Program.cs"); + File.WriteAllText(programPath, s_program); + + // Remove artifacts from possible previous runs of this test. + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + Build(testInstance, BuildLevel.Csc, args: ["test", "args"], expectedOutput: """ + echo args:test;args + Hello from Program + """); + } + + /// + /// Tests an optimization which remembers CSC args from prior MSBuild runs and can skip subsequent MSBuild invocations and call CSC directly. + /// This optimization kicks in when the file has some #: directives (then the simpler "hard-coded CSC args" optimization cannot be used). + /// [Fact] public void CscOnly_AfterMSBuild() { @@ -3025,6 +3311,9 @@ public void CscOnly_AfterMSBuild() Build(testInstance, BuildLevel.All, expectedOutput: "v4 "); } + /// + /// See . + /// [Fact] public void CscOnly_AfterMSBuild_SpacesInPath() { @@ -3055,6 +3344,46 @@ public void CscOnly_AfterMSBuild_SpacesInPath() Build(testInstance, BuildLevel.Csc, expectedOutput: "v2 Release", programFileName: programFileName); } + /// + /// See . + /// + [Fact] + public void CscOnly_AfterMSBuild_Args() + { + var testInstance = _testAssetsManager.CreateTestDirectory(baseDirectory: OutOfTreeBaseDirectory); + var programPath = Path.Join(testInstance.Path, "Program.cs"); + + var code = $""" + #:property Configuration=Release + {s_program} + """; + + File.WriteAllText(programPath, code); + + // Remove artifacts from possible previous runs of this test. + var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath); + if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true); + + Build(testInstance, BuildLevel.All, args: ["test", "args"], expectedOutput: """ + echo args:test;args + Hello from Program + Release config + """); + + code = code.Replace("Hello", "Hi"); + File.WriteAllText(programPath, code); + + Build(testInstance, BuildLevel.Csc, ["test", "args"], expectedOutput: """ + echo args:test;args + Hi from Program + Release config + """); + } + + /// + /// See . + /// This optimization currently does not support #:project references and hence is disabled if those are present. + /// [Fact] public void CscOnly_AfterMSBuild_ProjectReferences() { @@ -3111,8 +3440,8 @@ public class LibClass } /// - /// If users have more complex build customizations, they can opt out of the optimization which - /// reuses CSC arguments and skips subsequent MSBuild invocations. + /// See . + /// If users have more complex build customizations, they can opt out of the optimization. /// [Theory, CombinatorialData] public void CscOnly_AfterMSBuild_OptOut(bool canSkipMSBuild, bool inDirectoryBuildProps) @@ -3156,6 +3485,9 @@ public void CscOnly_AfterMSBuild_OptOut(bool canSkipMSBuild, bool inDirectoryBui Build(testInstance, canSkipMSBuild ? BuildLevel.Csc : BuildLevel.All, expectedOutput: "v2 Release"); } + /// + /// See . + /// [Fact] public void CscOnly_AfterMSBuild_AuxiliaryFilesNotReused() { @@ -3231,6 +3563,14 @@ public void Api() artifacts/$(MSBuildProjectName) artifacts/$(MSBuildProjectName) true + false + true + Exe + {ToolsetInfo.CurrentTargetFramework} + enable + enable + true + true @@ -3241,15 +3581,9 @@ public void Api() - Exe - enable - enable - true - false - true - false net11.0 preview + false $(Features);FileBasedProgram @@ -3301,6 +3635,14 @@ public void Api_Diagnostic_01() artifacts/$(MSBuildProjectName) artifacts/$(MSBuildProjectName) true + false + true + Exe + {ToolsetInfo.CurrentTargetFramework} + enable + enable + true + true @@ -3310,13 +3652,6 @@ public void Api_Diagnostic_01() - Exe - {ToolsetInfo.CurrentTargetFramework} - enable - enable - true - false - true false $(Features);FileBasedProgram @@ -3368,6 +3703,14 @@ public void Api_Diagnostic_02() artifacts/$(MSBuildProjectName) artifacts/$(MSBuildProjectName) true + false + true + Exe + {ToolsetInfo.CurrentTargetFramework} + enable + enable + true + true @@ -3377,13 +3720,6 @@ public void Api_Diagnostic_02() - Exe - {ToolsetInfo.CurrentTargetFramework} - enable - enable - true - false - true false $(Features);FileBasedProgram diff --git a/test/dotnet.Tests/CommandTests/Run/RunTelemetryTests.cs b/test/dotnet.Tests/CommandTests/Run/RunTelemetryTests.cs index 8ced55de7342..08d323f2cc28 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunTelemetryTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunTelemetryTests.cs @@ -146,9 +146,9 @@ public void TrackRunEvent_FileBasedApp_SendsCorrectTelemetry() { // Arrange var events = new List<(string? eventName, IDictionary? properties, IDictionary? measurements)>(); - + void handler(object? sender, InstrumentationEventArgs args) => events.Add((args.EventName, args.Properties, args.Measurements)); - + TelemetryEventEntry.EntryPosted += handler; try @@ -172,18 +172,21 @@ public void TrackRunEvent_FileBasedApp_SendsCorrectTelemetry() var eventData = events[0]; eventData.eventName.Should().Be("run"); eventData.properties.Should().NotBeNull(); - + eventData.measurements.Should().NotBeNull(); + var props = eventData.properties!; props["app_type"].Should().Be("file_based"); props["project_id"].Should().Be("test-hash"); - props["sdk_count"].Should().Be("2"); - props["package_reference_count"].Should().Be("3"); - props["project_reference_count"].Should().Be("1"); - props["additional_properties_count"].Should().Be("2"); props["used_msbuild"].Should().Be("true"); props["used_roslyn_compiler"].Should().Be("false"); props["launch_profile_requested"].Should().Be("explicit"); props["launch_profile_is_default"].Should().Be("true"); + + var measurements = eventData.measurements!; + measurements["sdk_count"].Should().Be(2); + measurements["package_reference_count"].Should().Be(3); + measurements["project_reference_count"].Should().Be(1); + measurements["additional_properties_count"].Should().Be(2); } finally { @@ -197,9 +200,9 @@ public void TrackRunEvent_ProjectBasedApp_SendsCorrectTelemetry() { // Arrange var events = new List<(string? eventName, IDictionary? properties, IDictionary? measurements)>(); - + void handler(object? sender, InstrumentationEventArgs args) => events.Add((args.EventName, args.Properties, args.Measurements)); - + TelemetryEventEntry.EntryPosted += handler; try @@ -220,17 +223,20 @@ public void TrackRunEvent_ProjectBasedApp_SendsCorrectTelemetry() var eventData = events[0]; eventData.eventName.Should().Be("run"); eventData.properties.Should().NotBeNull(); - + eventData.measurements.Should().NotBeNull(); + var props = eventData.properties!; props["app_type"].Should().Be("project_based"); props["project_id"].Should().Be("project-hash"); - props["sdk_count"].Should().Be("1"); - props["package_reference_count"].Should().Be("5"); - props["project_reference_count"].Should().Be("2"); props["launch_profile_requested"].Should().Be("none"); - props.Should().NotContainKey("additional_properties_count"); props.Should().NotContainKey("used_msbuild"); props.Should().NotContainKey("used_roslyn_compiler"); + + var measurements = eventData.measurements!; + measurements["sdk_count"].Should().Be(1); + measurements["package_reference_count"].Should().Be(5); + measurements["project_reference_count"].Should().Be(2); + measurements.Should().NotContainKey("additional_properties_count"); } finally { @@ -244,9 +250,9 @@ public void TrackRunEvent_WithDefaultLaunchProfile_MarksTelemetryCorrectly() { // Arrange var events = new List<(string? eventName, IDictionary? properties, IDictionary? measurements)>(); - + void handler(object? sender, InstrumentationEventArgs args) => events.Add((args.EventName, args.Properties, args.Measurements)); - + TelemetryEventEntry.EntryPosted += handler; var launchSettings = new ProjectLaunchSettingsModel diff --git a/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTestfromCsproj.cs b/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTestfromCsproj.cs index 85d6749e07a5..96b0e9edb73b 100644 --- a/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTestfromCsproj.cs +++ b/test/dotnet.Tests/CommandTests/Test/GivenDotnetTestBuildsAndRunsTestfromCsproj.cs @@ -804,6 +804,38 @@ public void ArgumentsEndWithDllOrExeShouldNotFail(string arg) } } + + [Theory] + [InlineData("-p:ABC=C:\\my.dll")] + [InlineData("/p:ABC=C:\\my.dll")] + [InlineData("-property:ABC=C:\\my.dll")] + public void PropertiesEndingWithDotDllShouldNotFail(string property) + { + var testProjectDirectory = CopyAndRestoreVSTestDotNetCoreTestApp([]); + + // Call test + // The test will complain about --property:VsTestUseMSBuildOutput=false but + // it is the .dll parameter that is causing this. It forces the command to offload work + // to vstest.console.exe directly, because it thinks there is some test .dll that we should run + // directly, rather than a project file. + // Vstest.console.exe will then complain just about the first unknown parameter. + CommandResult result = new DotnetTestCommand(Log, disableNewOutput: true) + .WithWorkingDirectory(testProjectDirectory) + .Execute(ConsoleLoggerOutputNormal.Concat([property])); + + // Verify + if (!TestContext.IsLocalized()) + { + result.StdOut.Should().Contain("Total tests: 2"); + result.StdOut.Should().Contain("Passed: 1"); + result.StdOut.Should().Contain("Failed: 1"); + result.StdOut.Should().Contain("Passed VSTestPassTest"); + result.StdOut.Should().Contain("Failed VSTestFailTest"); + } + + result.ExitCode.Should().Be(1); + } + private string CopyAndRestoreVSTestDotNetCoreTestApp(object[] parameters, [CallerMemberName] string callingMethod = "") { // Copy VSTestCore project in output directory of project dotnet-vstest.Tests diff --git a/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs b/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs index 257338656fc3..b55a7f2cdfb4 100644 --- a/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs +++ b/test/dotnet.Tests/CommandTests/Test/TestProgressStateTests.cs @@ -18,7 +18,7 @@ public class TestProgressStateTests public void ReportSkippedTest_MultipleCalls_DifferentInstanceId() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); string testUid = "test1"; string instanceA = "instanceA"; string instanceB = "instanceB"; @@ -48,7 +48,7 @@ public void ReportSkippedTest_MultipleCalls_DifferentInstanceId() public void ReportSkippedTest_RepeatedInstanceAfterRetry_ThrowsInvalidOperationException() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); string testUid = "test1"; string instanceA = "instanceA"; string instanceB = "instanceB"; @@ -75,7 +75,7 @@ public void ReportSkippedTest_RepeatedInstanceAfterRetry_ThrowsInvalidOperationE public void ReportFailedTest_RepeatedCalls_IncrementsFailedTests(int callCount) { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); state.NotifyHandshake("instance1"); for (int i = 0; i < callCount; i++) { @@ -95,7 +95,7 @@ public void ReportFailedTest_RepeatedCalls_IncrementsFailedTests(int callCount) public void ReportFailedTest_DifferentInstanceId_RetriesFailureAndResetsCount() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); state.NotifyHandshake("id1"); state.ReportFailedTest("testUid", "id1"); state.ReportFailedTest("testUid", "id1"); @@ -114,7 +114,7 @@ public void ReportFailedTest_DifferentInstanceId_RetriesFailureAndResetsCount() public void ReportFailedTest_ReusingOldInstanceId_ThrowsInvalidOperationException() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); state.NotifyHandshake("id1"); state.ReportFailedTest("testUid", "id1"); state.NotifyHandshake("id2"); @@ -134,7 +134,7 @@ public void ReportFailedTest_ReusingOldInstanceId_ThrowsInvalidOperationExceptio public void ReportTest_WithNewInstanceId_ClearsOldReports() { var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); state.NotifyHandshake("id1"); state.ReportFailedTest("testUid", "id1"); state.ReportFailedTest("testUid", "id1"); @@ -154,6 +154,7 @@ public void ReportTest_WithNewInstanceId_ClearsOldReports() state.SkippedTests.Should().Be(1); state.RetriedFailedTests.Should().Be(3); } + /// /// Tests that DiscoverTest increments PassedTests and adds the displayName and uid to DiscoveredTests. /// @@ -173,14 +174,15 @@ public void DiscoverTest_DisplayNameAndUid_AddsEntryAndIncrementsPassedTests(str assembly: "assembly.dll", targetFramework: null, architecture: null, - stopwatch: stopwatchMock.Object); + stopwatch: stopwatchMock.Object, + isDiscovery: true); state.DiscoverTest(displayName, uid); - state.PassedTests.Should().Be(1); - state.DiscoveredTests.Count.Should().Be(1); - state.DiscoveredTests[0].DisplayName.Should().Be(displayName); - state.DiscoveredTests[0].UID.Should().Be(uid); + state.DiscoveredTests.Should().Be(1); + state.DiscoveredTestNames.Count.Should().Be(1); + state.DiscoveredTestNames[0].DisplayName.Should().Be(displayName); + state.DiscoveredTestNames[0].UID.Should().Be(uid); } [Fact] @@ -188,7 +190,7 @@ public void FailedTestRetryShouldShouldShowTheSameTotalCountsInEachRetry() { // Tests are retried, total test count stays 3 to give use comparable counts, no matter how many times we retry. var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); // First run state.NotifyHandshake("run1"); @@ -227,7 +229,7 @@ public void FailedTestRetryShouldNotFailTheRunWhenSecondRunProducesLessDynamicTe { // This is special test for dynamic tests where we don't know how many tests will be produced in the second run. var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); // First run state.NotifyHandshake("run1"); @@ -259,7 +261,7 @@ public void FailedTestRetryShouldAccountPassedTestsInRetry() { // This is special test for dynamic tests where we cannot avoid re-running even non-failing tests from dynamic tests. var stopwatchMock = new Mock(); - var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object); + var state = new TestProgressState(1, "assembly.dll", null, null, stopwatchMock.Object, isDiscovery: false); // First run state.NotifyHandshake("run1"); diff --git a/test/dotnet.Tests/CommandTests/Tool/Install/ToolInstallGlobalOrToolPathCommandTests.cs b/test/dotnet.Tests/CommandTests/Tool/Install/ToolInstallGlobalOrToolPathCommandTests.cs index 8b1f84c62cc8..65c0dd06d35c 100644 --- a/test/dotnet.Tests/CommandTests/Tool/Install/ToolInstallGlobalOrToolPathCommandTests.cs +++ b/test/dotnet.Tests/CommandTests/Tool/Install/ToolInstallGlobalOrToolPathCommandTests.cs @@ -197,7 +197,7 @@ public void WhenRunWithSourceItShouldFindOnlyTheProvidedSource() [Fact] public void WhenRunWithPackageIdWithSourceItShouldCreateValidShim() { - const string sourcePath = "http://mysource.com"; + const string sourcePath = "https://mysource.com"; ParseResult result = Parser.Parse($"dotnet tool install -g {PackageId} --add-source {sourcePath}"); var toolInstallGlobalOrToolPathCommand = new ToolInstallGlobalOrToolPathCommand( @@ -666,7 +666,6 @@ public void WhenRunWithoutValidVersionUnlistedToolItShouldThrow() var testDir = _testAssetsManager.CreateTestDirectory().Path; var toolInstallGlobalOrToolPathCommand = new DotnetCommand(Log, "tool", "install", "-g", UnlistedPackageId, "--add-source", nugetSourcePath) - .WithEnvironmentVariable("DOTNET_SKIP_WORKLOAD_INTEGRITY_CHECK", "true") .WithWorkingDirectory(testDir); toolInstallGlobalOrToolPathCommand.Execute().Should().Fail(); @@ -928,6 +927,78 @@ public void WhenRunWithArchOptionItDownloadsAppHostTemplate() nugetPackageDownloader.DownloadCallParams.First().Item1.Should().Be(new PackageId("microsoft.netcore.app.host.win-arm64")); } + [Fact] + public void WhenRunWithHttpSourceViaAddSourceItShouldShowNU1302Error() + { + var testDir = _testAssetsManager.CreateTestDirectory().Path; + + var toolInstallCommand = new DotnetCommand(Log, "tool", "install", "-g", "fake-tool", "--add-source", "http://test.example.com/nuget") + .WithWorkingDirectory(testDir); + + var result = toolInstallCommand.Execute(); + + result.Should().Fail(); + result.StdErr.Should().Contain("You are running the 'tool install' operation with an 'HTTP' source: http://test.example.com/nuget"); + result.StdErr.Should().Contain("NuGet requires HTTPS sources"); + result.StdErr.Should().Contain("allowInsecureConnections"); + } + + [Fact] + public void WhenRunWithHttpSourceInNuGetConfigItShouldShowNU1302Error() + { + var testDir = _testAssetsManager.CreateTestDirectory().Path; + var nugetConfigPath = Path.Combine(testDir, "nuget.config"); + + var nugetConfigContent = @" + + + + + +"; + + File.WriteAllText(nugetConfigPath, nugetConfigContent); + + var toolInstallCommand = new DotnetCommand(Log, "tool", "install", "-g", "fake-tool") + .WithWorkingDirectory(testDir); + + var result = toolInstallCommand.Execute(); + + result.Should().Fail(); + result.StdErr.Should().Contain("You are running the 'tool install' operation with an 'HTTP' source: http://test.example.com/nuget"); + result.StdErr.Should().Contain("NuGet requires HTTPS sources"); + result.StdErr.Should().Contain("allowInsecureConnections"); + } + + [Fact] + public void WhenRunWithHttpSourceAndAllowInsecureConnectionsItShouldSucceed() + { + var testDir = _testAssetsManager.CreateTestDirectory().Path; + var nugetConfigPath = Path.Combine(testDir, "nuget.config"); + + var nugetConfigContent = @" + + + + + +"; + + File.WriteAllText(nugetConfigPath, nugetConfigContent); + + var toolInstallCommand = new DotnetCommand(Log, "tool", "install", "-g", "fake-tool") + .WithWorkingDirectory(testDir); + + var result = toolInstallCommand.Execute(); + + // Should fail for other reasons (unable to load service index) but not due to HTTP source validation + result.Should().Fail(); + result.StdErr.Should().NotContain("You are running the 'tool install' operation with an 'HTTP' source:"); + result.StdErr.Should().NotContain("NuGet requires HTTPS sources"); + // Should fail because the service index can't be loaded, not because of HTTP validation + result.StdErr.Should().Contain("Unable to load the service index"); + } + private string ExpectedCommandPath() { var extension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty; diff --git a/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh b/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh index 0e1338dd8889..c6b13b332377 100644 --- a/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh +++ b/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh @@ -1137,7 +1137,7 @@ _testhost_package_update() { prev="${COMP_WORDS[COMP_CWORD-1]}" COMPREPLY=() - opts="--project --interactive --verbosity --help" + opts="--project --vulnerable --interactive --verbosity --help" if [[ $COMP_CWORD == "$1" ]]; then COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) @@ -1145,6 +1145,10 @@ _testhost_package_update() { fi case $prev in + --vulnerable) + COMPREPLY=( $(compgen -W "False True" -- "$cur") ) + return + ;; --interactive) COMPREPLY=( $(compgen -W "False True" -- "$cur") ) return diff --git a/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 b/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 index b041745522b0..1066fdfe3711 100644 --- a/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 +++ b/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 @@ -48,7 +48,7 @@ Register-ArgumentCompleter -Native -CommandName 'testhost' -ScriptBlock { [CompletionResult]::new('solution', 'solution', [CompletionResultType]::ParameterValue, ".NET modify solution file command") [CompletionResult]::new('solution', 'sln', [CompletionResultType]::ParameterValue, ".NET modify solution file command") [CompletionResult]::new('store', 'store', [CompletionResultType]::ParameterValue, "Stores the specified assemblies for the .NET Platform. By default, these will be optimized for the target runtime and framework.") - [CompletionResult]::new('test', 'test', [CompletionResultType]::ParameterValue, ".NET Test Driver") + [CompletionResult]::new('test', 'test', [CompletionResultType]::ParameterValue, ".NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https://aka.ms/dotnet-test.") [CompletionResult]::new('tool', 'tool', [CompletionResultType]::ParameterValue, "Install or work with tools that extend the .NET experience.") [CompletionResult]::new('vstest', 'vstest', [CompletionResultType]::ParameterValue, "vstest") [CompletionResult]::new('help', 'help', [CompletionResultType]::ParameterValue, ".NET CLI help utility") @@ -671,6 +671,7 @@ Register-ArgumentCompleter -Native -CommandName 'testhost' -ScriptBlock { 'testhost;package;update' { $staticCompletions = @( [CompletionResult]::new('--project', '--project', [CompletionResultType]::ParameterName, "Path to a project or solution file, or a directory.") + [CompletionResult]::new('--vulnerable', '--vulnerable', [CompletionResultType]::ParameterName, "Upgrade packages with known vulnerabilities.") [CompletionResult]::new('--interactive', '--interactive', [CompletionResultType]::ParameterName, "Allows the command to stop and wait for user input or action (for example to complete authentication).") [CompletionResult]::new('--verbosity', '--verbosity', [CompletionResultType]::ParameterName, "Set the verbosity level of the command. Allowed values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic].") [CompletionResult]::new('--verbosity', '-v', [CompletionResultType]::ParameterName, "Set the verbosity level of the command. Allowed values are q[uiet], m[inimal], n[ormal], d[etailed], and diag[nostic].") diff --git a/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh b/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh index a69bd908c3a8..4688c833f62a 100644 --- a/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh +++ b/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh @@ -669,6 +669,7 @@ _testhost() { (update) _arguments "${_arguments_options[@]}" : \ '--project=[Path to a project or solution file, or a directory.]: : ' \ + '--vulnerable=[Upgrade packages with known vulnerabilities.]: :((False\:"False" True\:"True" ))' \ '--interactive=[Allows the command to stop and wait for user input or action (for example to complete authentication).]: :((False\:"False" True\:"True" ))' \ '--verbosity=[Set the verbosity level of the command. Allowed values are q\[uiet\], m\[inimal\], n\[ormal\], d\[etailed\], and diag\[nostic\].]: :((d\:"d" detailed\:"detailed" diag\:"diag" diagnostic\:"diagnostic" m\:"m" minimal\:"minimal" n\:"n" normal\:"normal" q\:"q" quiet\:"quiet" ))' \ '-v=[Set the verbosity level of the command. Allowed values are q\[uiet\], m\[inimal\], n\[ormal\], d\[etailed\], and diag\[nostic\].]: :((d\:"d" detailed\:"detailed" diag\:"diag" diagnostic\:"diagnostic" m\:"m" minimal\:"minimal" n\:"n" normal\:"normal" q\:"q" quiet\:"quiet" ))' \ @@ -1493,7 +1494,7 @@ _testhost_commands() { 'run:.NET Run Command' \ 'solution:.NET modify solution file command' \ 'store:Stores the specified assemblies for the .NET Platform. By default, these will be optimized for the target runtime and framework.' \ - 'test:.NET Test Driver' \ + 'test:.NET Test Command for VSTest. To use Microsoft.Testing.Platform, opt-in to the Microsoft.Testing.Platform-based command via global.json. For more information, see https\://aka.ms/dotnet-test.' \ 'tool:Install or work with tools that extend the .NET experience.' \ 'vstest:' \ 'help:.NET CLI help utility' \ diff --git a/test/dotnet.Tests/TelemetryCommonPropertiesTests.cs b/test/dotnet.Tests/TelemetryCommonPropertiesTests.cs index a77f0bf81765..4e28b92479d7 100644 --- a/test/dotnet.Tests/TelemetryCommonPropertiesTests.cs +++ b/test/dotnet.Tests/TelemetryCommonPropertiesTests.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. -#nullable disable - using Microsoft.DotNet.Cli.Telemetry; using Microsoft.DotNet.Configurer; @@ -193,29 +191,34 @@ public void CanDetectCIStatusForEnvVars(Dictionary envVars, bool [Theory] [MemberData(nameof(LLMTelemetryTestCases))] - public void CanDetectLLMStatusForEnvVars(Dictionary envVars, string expected) + public void CanDetectLLMStatusForEnvVars(Dictionary? envVars, string? expected) { try { - foreach (var (key, value) in envVars) - { - Environment.SetEnvironmentVariable(key, value); + if (envVars is not null){ + foreach (var (key, value) in envVars) + { + Environment.SetEnvironmentVariable(key, value); + } } new LLMEnvironmentDetectorForTelemetry().GetLLMEnvironment().Should().Be(expected); } finally { - foreach (var (key, value) in envVars) + if (envVars is not null) { - Environment.SetEnvironmentVariable(key, null); + foreach (var (key, value) in envVars) + { + Environment.SetEnvironmentVariable(key, null); + } } } } - + [Theory] [InlineData("dummySessionId")] [InlineData(null)] - public void TelemetryCommonPropertiesShouldContainSessionId(string sessionId) + public void TelemetryCommonPropertiesShouldContainSessionId(string? sessionId) { var unitUnderTest = new TelemetryCommonProperties(userLevelCacheWriter: new NothingCache()); var commonProperties = unitUnderTest.GetTelemetryCommonProperties(sessionId); @@ -225,34 +228,42 @@ public void TelemetryCommonPropertiesShouldContainSessionId(string sessionId) } - public static IEnumerable LLMTelemetryTestCases => new List{ - new object[] { new Dictionary { { "CLAUDECODE", "1" } }, "claude" }, - new object[] { new Dictionary { { "CURSOR_EDITOR", "1" } }, "cursor" }, - new object[] { new Dictionary { { "CLAUDECODE", "1" }, { "CURSOR_EDITOR", "1" } }, "claude, cursor" }, - new object[] { new Dictionary(), null }, + public static TheoryData?, string?> LLMTelemetryTestCases => new() + { + { new Dictionary { {"CLAUDECODE", "1" } }, "claude" }, + { new Dictionary { { "CURSOR_EDITOR", "1" } }, "cursor" }, + { new Dictionary { { "GEMINI_CLI", "true" } }, "gemini" }, + { new Dictionary { { "GITHUB_COPILOT_CLI_MODE", "true" } }, "copilot" }, + { new Dictionary { { "AGENT_CLI", "true" } }, "generic_agent" }, + { new Dictionary { { "CLAUDECODE", "1" }, { "CURSOR_EDITOR", "1" } }, "claude, cursor" }, + { new Dictionary { { "GEMINI_CLI", "true" }, { "GITHUB_COPILOT_CLI_MODE", "true" } }, "gemini, copilot" }, + { new Dictionary { { "CLAUDECODE", "1" }, { "GEMINI_CLI", "true" }, { "AGENT_CLI", "true" } }, "claude, gemini, generic_agent" }, + { new Dictionary { { "CLAUDECODE", "1" }, { "CURSOR_EDITOR", "1" }, { "GEMINI_CLI", "true" }, { "GITHUB_COPILOT_CLI_MODE", "true" }, { "AGENT_CLI", "true" } }, "claude, cursor, gemini, copilot, generic_agent" }, + { new Dictionary { { "GEMINI_CLI", "false" } }, null }, + { new Dictionary { { "GITHUB_COPILOT_CLI_MODE", "false" } }, null }, + { new Dictionary { { "AGENT_CLI", "false" } }, null }, + { new Dictionary(), null }, }; - public static IEnumerable CITelemetryTestCases => new List{ - new object[] { new Dictionary { { "TF_BUILD", "true" } }, true }, - new object[] { new Dictionary { { "GITHUB_ACTIONS", "true" } }, true }, - new object[] { new Dictionary { { "APPVEYOR", "true"} }, true }, - new object[] { new Dictionary { { "CI", "true"} }, true }, - new object[] { new Dictionary { { "TRAVIS", "true"} }, true }, - new object[] { new Dictionary { { "CIRCLECI", "true"} }, true }, - - new object[] { new Dictionary { { "CODEBUILD_BUILD_ID", "hi" }, { "AWS_REGION", "hi" } }, true }, - new object[] { new Dictionary { { "CODEBUILD_BUILD_ID", "hi" } }, false }, - new object[] { new Dictionary { { "BUILD_ID", "hi" }, { "BUILD_URL", "hi" } }, true }, - new object[] { new Dictionary { { "BUILD_ID", "hi" } }, false }, - new object[] { new Dictionary { { "BUILD_ID", "hi" }, { "PROJECT_ID", "hi" } }, true }, - new object[] { new Dictionary { { "BUILD_ID", "hi" } }, false }, - - new object[] { new Dictionary { { "TEAMCITY_VERSION", "hi" } }, true }, - new object[] { new Dictionary { { "TEAMCITY_VERSION", "" } }, false }, - new object[] { new Dictionary { { "JB_SPACE_API_URL", "hi" } }, true }, - new object[] { new Dictionary { { "JB_SPACE_API_URL", "" } }, false }, - - new object[] { new Dictionary { { "SomethingElse", "hi" } }, false }, + public static TheoryData, bool> CITelemetryTestCases => new() + { + { new Dictionary { { "TF_BUILD", "true" } }, true }, + { new Dictionary { { "GITHUB_ACTIONS", "true" } }, true }, + { new Dictionary { { "APPVEYOR", "true"} }, true }, + { new Dictionary { { "CI", "true"} }, true }, + { new Dictionary { { "TRAVIS", "true"} }, true }, + { new Dictionary { { "CIRCLECI", "true"} }, true }, +{ new Dictionary { { "CODEBUILD_BUILD_ID", "hi" }, { "AWS_REGION", "hi" } }, true }, + { new Dictionary { { "CODEBUILD_BUILD_ID", "hi" } }, false }, + { new Dictionary { { "BUILD_ID", "hi" }, { "BUILD_URL", "hi" } }, true }, + { new Dictionary { { "BUILD_ID", "hi" } }, false }, + { new Dictionary { { "BUILD_ID", "hi" }, { "PROJECT_ID", "hi" } }, true }, + { new Dictionary { { "BUILD_ID", "hi" } }, false }, +{ new Dictionary { { "TEAMCITY_VERSION", "hi" } }, true }, + { new Dictionary { { "TEAMCITY_VERSION", "" } }, false }, + { new Dictionary { { "JB_SPACE_API_URL", "hi" } }, true }, + { new Dictionary { { "JB_SPACE_API_URL", "" } }, false }, +{ new Dictionary { { "SomethingElse", "hi" } }, false }, }; private class NothingCache : IUserLevelCacheWriter