diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index d20fe7f60048..91e9458e9a3d 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"microsoft.dotnet.darc": {
- "version": "1.1.0-beta.25064.2",
+ "version": "1.1.0-beta.25081.2",
"commands": [
"darc"
]
diff --git a/.gitattributes b/.gitattributes
index 395c74ff4cd5..2f46e347155a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -10,6 +10,12 @@
###############################################################################
*.xlf linguist-generated=true
+###############################################################################
+# Set default behavior to:
+# don't collapse these files in PRs
+###############################################################################
+**/build/** linguist-generated=false
+
###############################################################################
# Set file behavior to:
# treat as text
diff --git a/.vsts-ci.yml b/.vsts-ci.yml
index d5f5f34e84b9..6315d97a9945 100644
--- a/.vsts-ci.yml
+++ b/.vsts-ci.yml
@@ -5,7 +5,7 @@ trigger:
branches:
include:
- main
- - release/9.0.2xx
+ - release/9.0.3xx
- internal/release/*
- exp/*
@@ -22,6 +22,10 @@ parameters:
displayName: Run A Test Build
type: boolean
default: false
+- name: enableArm64Job
+ displayName: Enables the ARM64 job
+ type: boolean
+ default: false
variables:
- template: /eng/pipelines/templates/variables/sdk-defaults.yml
@@ -297,7 +301,7 @@ extends:
officialBuildProperties: $(_officialBuildProperties)
runTests: false
### ARM64 TESTBUILD ###
- - ${{ if or(eq(parameters.runTestBuild, true), eq(variables['Build.Reason'], 'PullRequest')) }}:
+ - ${{ if and(or(eq(parameters.runTestBuild, true), eq(variables['Build.Reason'], 'PullRequest')), eq(parameters.enableArm64Job, true)) }}:
- template: /eng/pipelines/templates/jobs/sdk-job-matrix.yml@self
parameters:
pool:
diff --git a/.vsts-pr.yml b/.vsts-pr.yml
index 68b652751d4d..2e5c086a70e4 100644
--- a/.vsts-pr.yml
+++ b/.vsts-pr.yml
@@ -14,6 +14,12 @@ pr:
- README.md
- CODEOWNERS
+parameters:
+- name: enableArm64Job
+ displayName: Enables the ARM64 job
+ type: boolean
+ default: false
+
variables:
- template: /eng/pipelines/templates/variables/sdk-defaults.yml
# Variables used: DncEngPublicBuildPool
@@ -64,17 +70,18 @@ stages:
os: macOS
helixTargetQueue: osx.13.amd64.open
### ARM64 ###
- - template: /eng/pipelines/templates/jobs/sdk-job-matrix.yml
- parameters:
- pool:
- name: Azure Pipelines
- vmImage: macOS-latest
- os: macOS
- helixTargetQueue: osx.13.arm64.open
- macOSJobParameterSets:
- - categoryName: TestBuild
- buildArchitecture: arm64
- runtimeIdentifier: osx-arm64
+ - ${{ if eq(parameters.enableArm64Job, true) }}:
+ - template: /eng/pipelines/templates/jobs/sdk-job-matrix.yml
+ parameters:
+ pool:
+ name: Azure Pipelines
+ vmImage: macOS-latest
+ os: macOS
+ helixTargetQueue: osx.13.arm64.open
+ macOSJobParameterSets:
+ - categoryName: TestBuild
+ buildArchitecture: arm64
+ runtimeIdentifier: osx-arm64
############### DOTNET-FORMAT ###############
- template: /eng/dotnet-format/dotnet-format-integration.yml
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2e3b4ae1f740..d05494c58666 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -94,7 +94,6 @@
-
diff --git a/documentation/general/ExecutingCustomerCode.md b/documentation/general/ExecutingCustomerCode.md
new file mode 100644
index 000000000000..e087065a5822
--- /dev/null
+++ b/documentation/general/ExecutingCustomerCode.md
@@ -0,0 +1,78 @@
+# Executing Customer Code
+
+## Summary
+
+When customers discover issues with the .NET SDK, we often need more information to see how exactly their scenarios differ from the (presumably working) mainline scenarios. This additional information often takes the form of a 'repro' or set of steps by which we can see the error ourselves and walk through what is happening as their scenario plays out and how it ultimately diverged from our expectations.
+
+Blindly executing, or even just opening in an IDE like Visual Studio without intending to build, unvetted customer code can be a security hazard, however, not just for the machine executing the code but for any machine on the same network and any service accessible using credentials they can access through those machines. In this way, a malicious actor can exfiltrate sensitive Microsoft data, including information about other Microsoft employees, proprietary code, or private customer data. They may even be able to take down a service or introduce further security bugs in shipping products. Indeed, the most common vector hackers use to gain access is through compromising one or more individual users with employee credentials. At Microsoft where security is paramount, we want to prevent such hacks.
+
+This document contains recommended best practices on how to securely test users' code. They are arranged in order of security with the most secure at the top. This should also be the priority you should use to stay secure when executing code.
+
+## Strategies for Staying Secure
+
+### Binary Logs
+
+MSBuild binary logs (binlogs) are structured data covering what happened during a build. Many operations that aren't obviously 'builds' such as publish (`dotnet publish`) or pack (`dotnet pack`) include building as part or all of the process, which makes them very useful for learning more about and potentially diagnosing a variety of potential problems. Furthermore, they are safe in the sense that analyzing a binlog does not expose your system to risks.
+
+To provide information to customers about collecting binlogs, direct them to:
+https://aka.ms/msbuild/binlog
+
+It is important to note, however, that binlogs often collect secrets as part of the information relevant to the build. A binlog of a simplified repro can work as well, but it may be difficult to create a simple repro. Customers are sometimes concerned (with good reason) about sharing binlogs of their real (or simplified) builds. If so, they can attempt to redact secrets from their binlog, either via the [MSBuild Structured Log Viewer](https://msbuildlog.com/#redaction) or with [binlogtool](https://www.nuget.org/packages/binlogtool). If that isn't sufficient, or if the reported issue does not involve MSBuild, another option may be more relevant.
+
+### Windows Sandbox
+
+Windows Sandbox permits safely running any operation in a controlled, isolated environment where it will not affect your machine or Microsoft in any detrimental way. More information can be found [here](https://learn.microsoft.com/windows/security/application-security/application-isolation/windows-sandbox/windows-sandbox-overview). If the steps a customer provides for reproducing their scenario can be executed in Windows Sandbox, it is the safest place to do so. Unfortunately, Sandbox does come with a few notable downsides:
+1. Windows Sandbox only exists for Windows. If a customer's scenario only reproduces on macOS or linux, it will not be able to be reproduced on Windows, and Sandbox is not helpful.
+2. When Sandbox exits for any reason, all data is lost. This means, for instance, that if the computer hosting Sandbox restarts while Sandbox is open, any progress you might have made in putting together the customer's repro scenario is lost.
+3. Sandbox is essentially empty when you first create it. It has internet access as long as your host computer has internet access, but it will be missing various programs and tools you may need to reproduce the customer's problem. Such programs and tools need to be downloaded anew every time you want to use Sandbox.
+
+Conveniently, however, it is relatively easy to copy files from your host computer into Sandbox, and as noted earlier, it is a secure, isolated environment, so it is an excellent choice if it is viable.
+
+Before executing customer code in Windows Sandbox, make sure to [disable networking](https://learn.microsoft.com/windows/security/application-security/application-isolation/windows-sandbox/windows-sandbox-configure-using-wsb-file#networking).
+
+### Create Your Own Solution
+
+Depending on how well you understand the customer's scenario, you may or may not understand ahead of time the most likely problem, but if it's possible to create your own solution that reproduces the issue the customer alluded to in their report, and you understand all the steps it took to create that solution, that is a safe method for obtaining a project to test and debug. Note that this is only a secure method if you understand all steps involved in creating the test project. "Copy project file provided by customer to disk and build it" does not count as a well-understood pair of steps.
+
+### Create a Virtual Machine Without Your Credentials
+
+Creating and using a virtual machine (VM) without your credentials is another secure way to test a customer's code, as it's disconnected from either your credentials or your computer. It is important if using this method to ensure that you do not give your VM your credentials at any point such as, for example, signing into your Microsoft account so you can use Visual Studio Enterprise.
+
+The other disadvantage of this approach is that it can take a lot of time to select appropriate parameters for your VM, start it, connect to it, set it up properly for your scenario, and execute the scenario. Unlike Sandbox, it can retain certain information across restarts, but it may lose active work just as your computer loses active work when you restart it. It's important to keep in mind that many VMs restart automatically at a fixed time every day when not in use to save resources.
+
+That said, this is a secure means for executing a customer's scenario, and it works for any operating system and can even be configured to work for other architectures. Note that using a VM in the cloud costs real money, though a local VM (such as using [Hyper-V](https://learn.microsoft.com/windows-server/virtualization/hyper-v/get-started/create-a-virtual-machine-in-hyper-v)) typically does not.
+
+For Microsoft employees, [DevTestLab](https://ms.portal.azure.com/#browse/Microsoft.Compute%2FVirtualMachines) can help you create VMs.
+
+### Read Code
+
+If none of the above methods work, it may be viable to read all customer code carefully to ensure that no part of it is risky or malicious. Many IDEs such as Visual Studio automatically run design-time builds when code is open. As a result, even just opening a malicious code base in Visual Studio can lead to exploits. Prefer an IDE that does not run design-time builds. Some examples include:
+1. Visual Studio Code (if no addons are installed)
+2. Notepad
+3. Emacs/Vim
+
+Some examples of specific patterns to watch out for that can cause issues include:
+1. PackageReferences. These download code from the internet and permit its execution locally. If you are unfamiliar with any package in a PackageReference, this is insecure.
+2. MSBuild SDKs. At the top of many .NET projects is a line that includes `Sdk=""`. This automatically imports build logic associated with the specified SDK. If you are unfamiliar with the specified SDK, ensure it is secure before building any code including it.
+3. MSBuild tasks. Within a `Target` block are MSBuild Tasks. Many of these, including `Exec` and `DownloadFile` execute logic that pulls code or build logic from the internet and may execute it. If you are unfamiliar with any MSBuild Task in a customer-provided project, ensure you fully understand its semantics before executing it (by building).
+4. Typos. NuGet packages starting with Microsoft. and System. can only be pushed to nuget.org by Microsoft accounts, but Mircosoft and Sysstem are unregulated.
+5. Malicious NuGet.Config files. Even if a PackageReference points to a package you're familiar with, perhaps even a Microsoft. or System. package, a NuGet.Config file can dictate searching in a different package source first that may not be secure and may include a malicious version of the package.
+
+More examples of patterns that can easily be exploited by malicious users can be found [here](https://aka.ms/msbuild-security-documentation)
+
+In addition to those more specific examples, it is important to follow the principle of least privilege. This means, for instance, that when on linux, you should only run a command as root if that's truly necessary to reproduce the customer's scenario. On Windows, you should only use an admin terminal if it's necessary.
+
+## Response to Malicious Repros
+
+If you discover that a customer provided a malicious project or solution, there are several steps. You should do all of these if possible.
+
+1. Most importantly, do not keep any vestige of the repro on your machine. Do not follow their steps.
+2. Add a note to the issue that it was malicious. Include this information in the title if possible.
+3. Delete the contents of the repro steps. (On GitHub, it can still be found by looking at your edit.)
+4. Report the infraction using https://aka.ms/reportitnow (This step is only available to Microsoft employees.)
+5. Report your finding to your manager and your security team.
+6. Depending on the severity of the issue, consider banning the user from GitHub.
+
+If you are external, you may not be able to follow all of these steps. Reach out to someone from our team to help facilitate doing all these.
+
+Last but not least, thank you for helping keep Microsoft secure!
\ No newline at end of file
diff --git a/documentation/package-table.md b/documentation/package-table.md
index cb7f1380d4d3..ab64a6cacf7a 100644
--- a/documentation/package-table.md
+++ b/documentation/package-table.md
@@ -1,20 +1,20 @@
### Table
--------------------------------------------------------------------------------------
-| Platform | main
(10.0.x Runtime) | 9.0.2xx
(9.0 Runtime) | 9.0.1xx
(9.0 Runtime) |
+| Platform | main
(10.0.x Runtime) | 9.0.3xx
(9.0.x Runtime) | 9.0.2xx
(9.0.x Runtime) |
| :--------- | :----------: | :----------: | :----------: |
-| **Windows x64** | [![][win-x64-badge-main]][win-x64-version-main]
[Installer][win-x64-installer-main] - [Checksum][win-x64-installer-checksum-main]
[zip][win-x64-zip-main] - [Checksum][win-x64-zip-checksum-main] | [![][win-x64-badge-9.0.2XX]][win-x64-version-9.0.2XX]
[Installer][win-x64-installer-9.0.2XX] - [Checksum][win-x64-installer-checksum-9.0.2XX]
[zip][win-x64-zip-9.0.2XX] - [Checksum][win-x64-zip-checksum-9.0.2XX] | [![][win-x64-badge-9.0.1XX]][win-x64-version-9.0.1XX]
[Installer][win-x64-installer-9.0.1XX] - [Checksum][win-x64-installer-checksum-9.0.1XX]
[zip][win-x64-zip-9.0.1XX] - [Checksum][win-x64-zip-checksum-9.0.1XX] |
-| **Windows x86** | [![][win-x86-badge-main]][win-x86-version-main]
[Installer][win-x86-installer-main] - [Checksum][win-x86-installer-checksum-main]
[zip][win-x86-zip-main] - [Checksum][win-x86-zip-checksum-main] | [![][win-x86-badge-9.0.2XX]][win-x86-version-9.0.2XX]
[Installer][win-x86-installer-9.0.2XX] - [Checksum][win-x86-installer-checksum-9.0.2XX]
[zip][win-x86-zip-9.0.2XX] - [Checksum][win-x86-zip-checksum-9.0.2XX] | [![][win-x86-badge-9.0.1XX]][win-x86-version-9.0.1XX]
[Installer][win-x86-installer-9.0.1XX] - [Checksum][win-x86-installer-checksum-9.0.1XX]
[zip][win-x86-zip-9.0.1XX] - [Checksum][win-x86-zip-checksum-9.0.1XX] |
+| **Windows x64** | [![][win-x64-badge-main]][win-x64-version-main]
[Installer][win-x64-installer-main] - [Checksum][win-x64-installer-checksum-main]
[zip][win-x64-zip-main] - [Checksum][win-x64-zip-checksum-main] | [![][win-x64-badge-9.0.3XX]][win-x64-version-9.0.3XX]
[Installer][win-x64-installer-9.0.3XX] - [Checksum][win-x64-installer-checksum-9.0.3XX]
[zip][win-x64-zip-9.0.3XX] - [Checksum][win-x64-zip-checksum-9.0.3XX] | [![][win-x64-badge-9.0.2XX]][win-x64-version-9.0.2XX]
[Installer][win-x64-installer-9.0.2XX] - [Checksum][win-x64-installer-checksum-9.0.2XX]
[zip][win-x64-zip-9.0.2XX] - [Checksum][win-x64-zip-checksum-9.0.2XX] |
+| **Windows x86** | [![][win-x86-badge-main]][win-x86-version-main]
[Installer][win-x86-installer-main] - [Checksum][win-x86-installer-checksum-main]
[zip][win-x86-zip-main] - [Checksum][win-x86-zip-checksum-main] | [![][win-x86-badge-9.0.3XX]][win-x86-version-9.0.3XX]
[Installer][win-x86-installer-9.0.3XX] - [Checksum][win-x86-installer-checksum-9.0.3XX]
[zip][win-x86-zip-9.0.3XX] - [Checksum][win-x86-zip-checksum-9.0.3XX] | [![][win-x86-badge-9.0.2XX]][win-x86-version-9.0.2XX]
[Installer][win-x86-installer-9.0.2XX] - [Checksum][win-x86-installer-checksum-9.0.2XX]
[zip][win-x86-zip-9.0.2XX] - [Checksum][win-x86-zip-checksum-9.0.2XX] |
| **Windows arm** | **N/A** | **N/A** | **N/A** |
-| **Windows arm64** | [![][win-arm64-badge-main]][win-arm64-version-main]
[Installer][win-arm64-installer-main] - [Checksum][win-arm64-installer-checksum-main]
[zip][win-arm64-zip-main] | [![][win-arm64-badge-9.0.2XX]][win-arm64-version-9.0.2XX]
[Installer][win-arm64-installer-9.0.2XX] - [Checksum][win-arm64-installer-checksum-9.0.2XX]
[zip][win-arm64-zip-9.0.2XX] | [![][win-arm64-badge-9.0.1XX]][win-arm64-version-9.0.1XX]
[Installer][win-arm64-installer-9.0.1XX] - [Checksum][win-arm64-installer-checksum-9.0.1XX]
[zip][win-arm64-zip-9.0.1XX] |
-| **macOS x64** | [![][osx-x64-badge-main]][osx-x64-version-main]
[Installer][osx-x64-installer-main] - [Checksum][osx-x64-installer-checksum-main]
[tar.gz][osx-x64-targz-main] - [Checksum][osx-x64-targz-checksum-main] | [![][osx-x64-badge-9.0.2XX]][osx-x64-version-9.0.2XX]
[Installer][osx-x64-installer-9.0.2XX] - [Checksum][osx-x64-installer-checksum-9.0.2XX]
[tar.gz][osx-x64-targz-9.0.2XX] - [Checksum][osx-x64-targz-checksum-9.0.2XX] | [![][osx-x64-badge-9.0.1XX]][osx-x64-version-9.0.1XX]
[Installer][osx-x64-installer-9.0.1XX] - [Checksum][osx-x64-installer-checksum-9.0.1XX]
[tar.gz][osx-x64-targz-9.0.1XX] - [Checksum][osx-x64-targz-checksum-9.0.1XX] |
-| **macOS arm64** | [![][osx-arm64-badge-main]][osx-arm64-version-main]
[Installer][osx-arm64-installer-main] - [Checksum][osx-arm64-installer-checksum-main]
[tar.gz][osx-arm64-targz-main] - [Checksum][osx-arm64-targz-checksum-main] | [![][osx-arm64-badge-9.0.2XX]][osx-arm64-version-9.0.2XX]
[Installer][osx-arm64-installer-9.0.2XX] - [Checksum][osx-arm64-installer-checksum-9.0.2XX]
[tar.gz][osx-arm64-targz-9.0.2XX] - [Checksum][osx-arm64-targz-checksum-9.0.2XX] | [![][osx-arm64-badge-9.0.1XX]][osx-arm64-version-9.0.1XX]
[Installer][osx-arm64-installer-9.0.1XX] - [Checksum][osx-arm64-installer-checksum-9.0.1XX]
[tar.gz][osx-arm64-targz-9.0.1XX] - [Checksum][osx-arm64-targz-checksum-9.0.1XX] |
-| **Linux x64** | [![][linux-badge-main]][linux-version-main]
[DEB Installer][linux-DEB-installer-main] - [Checksum][linux-DEB-installer-checksum-main]
[RPM Installer][linux-RPM-installer-main] - [Checksum][linux-RPM-installer-checksum-main]
_see installer note below_1
[tar.gz][linux-targz-main] - [Checksum][linux-targz-checksum-main] | [![][linux-badge-9.0.2XX]][linux-version-9.0.2XX]
[DEB Installer][linux-DEB-installer-9.0.2XX] - [Checksum][linux-DEB-installer-checksum-9.0.2XX]
[RPM Installer][linux-RPM-installer-9.0.2XX] - [Checksum][linux-RPM-installer-checksum-9.0.2XX]
_see installer note below_1
[tar.gz][linux-targz-9.0.2XX] - [Checksum][linux-targz-checksum-9.0.2XX] | [![][linux-badge-9.0.1XX]][linux-version-9.0.1XX]
[DEB Installer][linux-DEB-installer-9.0.1XX] - [Checksum][linux-DEB-installer-checksum-9.0.1XX]
[RPM Installer][linux-RPM-installer-9.0.1XX] - [Checksum][linux-RPM-installer-checksum-9.0.1XX]
_see installer note below_1
[tar.gz][linux-targz-9.0.1XX] - [Checksum][linux-targz-checksum-9.0.1XX] |
-| **Linux arm** | [![][linux-arm-badge-main]][linux-arm-version-main]
[tar.gz][linux-arm-targz-main] - [Checksum][linux-arm-targz-checksum-main] | [![][linux-arm-badge-9.0.2XX]][linux-arm-version-9.0.2XX]
[tar.gz][linux-arm-targz-9.0.2XX] - [Checksum][linux-arm-targz-checksum-9.0.2XX] | [![][linux-arm-badge-9.0.1XX]][linux-arm-version-9.0.1XX]
[tar.gz][linux-arm-targz-9.0.1XX] - [Checksum][linux-arm-targz-checksum-9.0.1XX] |
-| **Linux arm64** | [![][linux-arm64-badge-main]][linux-arm64-version-main]
[tar.gz][linux-arm64-targz-main] - [Checksum][linux-arm64-targz-checksum-main] | [![][linux-arm64-badge-9.0.2XX]][linux-arm64-version-9.0.2XX]
[tar.gz][linux-arm64-targz-9.0.2XX] - [Checksum][linux-arm64-targz-checksum-9.0.2XX] | [![][linux-arm64-badge-9.0.1XX]][linux-arm64-version-9.0.1XX]
[tar.gz][linux-arm64-targz-9.0.1XX] - [Checksum][linux-arm64-targz-checksum-9.0.1XX] |
-| **Linux-musl-x64** | [![][linux-musl-x64-badge-main]][linux-musl-x64-version-main]
[tar.gz][linux-musl-x64-targz-main] - [Checksum][linux-musl-x64-targz-checksum-main] | [![][linux-musl-x64-badge-9.0.2XX]][linux-musl-x64-version-9.0.2XX]
[tar.gz][linux-musl-x64-targz-9.0.2XX] - [Checksum][linux-musl-x64-targz-checksum-9.0.2XX] | [![][linux-musl-x64-badge-9.0.1XX]][linux-musl-x64-version-9.0.1XX]
[tar.gz][linux-musl-x64-targz-9.0.1XX] - [Checksum][linux-musl-x64-targz-checksum-9.0.1XX] |
-| **Linux-musl-arm** | [![][linux-musl-arm-badge-main]][linux-musl-arm-version-main]
[tar.gz][linux-musl-arm-targz-main] - [Checksum][linux-musl-arm-targz-checksum-main] | [![][linux-musl-arm-badge-9.0.2XX]][linux-musl-arm-version-9.0.2XX]
[tar.gz][linux-musl-arm-targz-9.0.2XX] - [Checksum][linux-musl-arm-targz-checksum-9.0.2XX] | [![][linux-musl-arm-badge-9.0.1XX]][linux-musl-arm-version-9.0.1XX]
[tar.gz][linux-musl-arm-targz-9.0.1XX] - [Checksum][linux-musl-arm-targz-checksum-9.0.1XX] |
-| **Linux-musl-arm64** | [![][linux-musl-arm64-badge-main]][linux-musl-arm64-version-main]
[tar.gz][linux-musl-arm64-targz-main] - [Checksum][linux-musl-arm64-targz-checksum-main] | [![][linux-musl-arm64-badge-9.0.2XX]][linux-musl-arm64-version-9.0.2XX]
[tar.gz][linux-musl-arm64-targz-9.0.2XX] - [Checksum][linux-musl-arm64-targz-checksum-9.0.2XX] | [![][linux-musl-arm64-badge-9.0.1XX]][linux-musl-arm64-version-9.0.1XX]
[tar.gz][linux-musl-arm64-targz-9.0.1XX] - [Checksum][linux-musl-arm64-targz-checksum-9.0.1XX] |
+| **Windows arm64** | [![][win-arm64-badge-main]][win-arm64-version-main]
[Installer][win-arm64-installer-main] - [Checksum][win-arm64-installer-checksum-main]
[zip][win-arm64-zip-main] | [![][win-arm64-badge-9.0.3XX]][win-arm64-version-9.0.3XX]
[Installer][win-arm64-installer-9.0.3XX] - [Checksum][win-arm64-installer-checksum-9.0.3XX]
[zip][win-arm64-zip-9.0.3XX] | [![][win-arm64-badge-9.0.2XX]][win-arm64-version-9.0.2XX]
[Installer][win-arm64-installer-9.0.2XX] - [Checksum][win-arm64-installer-checksum-9.0.2XX]
[zip][win-arm64-zip-9.0.2XX] |
+| **macOS x64** | [![][osx-x64-badge-main]][osx-x64-version-main]
[Installer][osx-x64-installer-main] - [Checksum][osx-x64-installer-checksum-main]
[tar.gz][osx-x64-targz-main] - [Checksum][osx-x64-targz-checksum-main] | [![][osx-x64-badge-9.0.3XX]][osx-x64-version-9.0.3XX]
[Installer][osx-x64-installer-9.0.3XX] - [Checksum][osx-x64-installer-checksum-9.0.3XX]
[tar.gz][osx-x64-targz-9.0.3XX] - [Checksum][osx-x64-targz-checksum-9.0.3XX] | [![][osx-x64-badge-9.0.2XX]][osx-x64-version-9.0.2XX]
[Installer][osx-x64-installer-9.0.2XX] - [Checksum][osx-x64-installer-checksum-9.0.2XX]
[tar.gz][osx-x64-targz-9.0.2XX] - [Checksum][osx-x64-targz-checksum-9.0.2XX] |
+| **macOS arm64** | [![][osx-arm64-badge-main]][osx-arm64-version-main]
[Installer][osx-arm64-installer-main] - [Checksum][osx-arm64-installer-checksum-main]
[tar.gz][osx-arm64-targz-main] - [Checksum][osx-arm64-targz-checksum-main] | [![][osx-arm64-badge-9.0.3XX]][osx-arm64-version-9.0.3XX]
[Installer][osx-arm64-installer-9.0.3XX] - [Checksum][osx-arm64-installer-checksum-9.0.3XX]
[tar.gz][osx-arm64-targz-9.0.3XX] - [Checksum][osx-arm64-targz-checksum-9.0.3XX] | [![][osx-arm64-badge-9.0.2XX]][osx-arm64-version-9.0.2XX]
[Installer][osx-arm64-installer-9.0.2XX] - [Checksum][osx-arm64-installer-checksum-9.0.2XX]
[tar.gz][osx-arm64-targz-9.0.2XX] - [Checksum][osx-arm64-targz-checksum-9.0.2XX] |
+| **Linux x64** | [![][linux-badge-main]][linux-version-main]
[DEB Installer][linux-DEB-installer-main] - [Checksum][linux-DEB-installer-checksum-main]
[RPM Installer][linux-RPM-installer-main] - [Checksum][linux-RPM-installer-checksum-main]
_see installer note below_1
[tar.gz][linux-targz-main] - [Checksum][linux-targz-checksum-main] | [![][linux-badge-9.0.3XX]][linux-version-9.0.3XX]
[DEB Installer][linux-DEB-installer-9.0.3XX] - [Checksum][linux-DEB-installer-checksum-9.0.3XX]
[RPM Installer][linux-RPM-installer-9.0.3XX] - [Checksum][linux-RPM-installer-checksum-9.0.3XX]
_see installer note below_1
[tar.gz][linux-targz-9.0.3XX] - [Checksum][linux-targz-checksum-9.0.3XX] | [![][linux-badge-9.0.2XX]][linux-version-9.0.2XX]
[DEB Installer][linux-DEB-installer-9.0.2XX] - [Checksum][linux-DEB-installer-checksum-9.0.2XX]
[RPM Installer][linux-RPM-installer-9.0.2XX] - [Checksum][linux-RPM-installer-checksum-9.0.2XX]
_see installer note below_1
[tar.gz][linux-targz-9.0.2XX] - [Checksum][linux-targz-checksum-9.0.2XX] |
+| **Linux arm** | [![][linux-arm-badge-main]][linux-arm-version-main]
[tar.gz][linux-arm-targz-main] - [Checksum][linux-arm-targz-checksum-main] | [![][linux-arm-badge-9.0.3XX]][linux-arm-version-9.0.3XX]
[tar.gz][linux-arm-targz-9.0.3XX] - [Checksum][linux-arm-targz-checksum-9.0.3XX] | [![][linux-arm-badge-9.0.2XX]][linux-arm-version-9.0.2XX]
[tar.gz][linux-arm-targz-9.0.2XX] - [Checksum][linux-arm-targz-checksum-9.0.2XX] |
+| **Linux arm64** | [![][linux-arm64-badge-main]][linux-arm64-version-main]
[tar.gz][linux-arm64-targz-main] - [Checksum][linux-arm64-targz-checksum-main] | [![][linux-arm64-badge-9.0.3XX]][linux-arm64-version-9.0.3XX]
[tar.gz][linux-arm64-targz-9.0.3XX] - [Checksum][linux-arm64-targz-checksum-9.0.3XX] | [![][linux-arm64-badge-9.0.2XX]][linux-arm64-version-9.0.2XX]
[tar.gz][linux-arm64-targz-9.0.2XX] - [Checksum][linux-arm64-targz-checksum-9.0.2XX] |
+| **Linux-musl-x64** | [![][linux-musl-x64-badge-main]][linux-musl-x64-version-main]
[tar.gz][linux-musl-x64-targz-main] - [Checksum][linux-musl-x64-targz-checksum-main] | [![][linux-musl-x64-badge-9.0.3XX]][linux-musl-x64-version-9.0.3XX]
[tar.gz][linux-musl-x64-targz-9.0.3XX] - [Checksum][linux-musl-x64-targz-checksum-9.0.3XX] | [![][linux-musl-x64-badge-9.0.2XX]][linux-musl-x64-version-9.0.2XX]
[tar.gz][linux-musl-x64-targz-9.0.2XX] - [Checksum][linux-musl-x64-targz-checksum-9.0.2XX] |
+| **Linux-musl-arm** | [![][linux-musl-arm-badge-main]][linux-musl-arm-version-main]
[tar.gz][linux-musl-arm-targz-main] - [Checksum][linux-musl-arm-targz-checksum-main] | [![][linux-musl-arm-badge-9.0.3XX]][linux-musl-arm-version-9.0.3XX]
[tar.gz][linux-musl-arm-targz-9.0.3XX] - [Checksum][linux-musl-arm-targz-checksum-9.0.3XX] | [![][linux-musl-arm-badge-9.0.2XX]][linux-musl-arm-version-9.0.2XX]
[tar.gz][linux-musl-arm-targz-9.0.2XX] - [Checksum][linux-musl-arm-targz-checksum-9.0.2XX] |
+| **Linux-musl-arm64** | [![][linux-musl-arm64-badge-main]][linux-musl-arm64-version-main]
[tar.gz][linux-musl-arm64-targz-main] - [Checksum][linux-musl-arm64-targz-checksum-main] | [![][linux-musl-arm64-badge-9.0.3XX]][linux-musl-arm64-version-9.0.3XX]
[tar.gz][linux-musl-arm64-targz-9.0.3XX] - [Checksum][linux-musl-arm64-targz-checksum-9.0.3XX] | [![][linux-musl-arm64-badge-9.0.2XX]][linux-musl-arm64-version-9.0.2XX]
[tar.gz][linux-musl-arm64-targz-9.0.2XX] - [Checksum][linux-musl-arm64-targz-checksum-9.0.2XX] |
| **RHEL 6** | **N/A** | **N/A** | **N/A** |
Reference notes:
@@ -25,221 +25,236 @@ Reference notes:
[win-x64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/win_x64_Release_version_badge.svg?no-cache
[win-x64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-win-x64.txt
[win-x64-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.exe
-[win-x64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.exe.sha512
+[win-x64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.exe.sha
[win-x64-zip-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.zip
-[win-x64-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.zip.sha512
+[win-x64-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x64.zip.sha
+
+[win-x64-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/win_x64_Release_version_badge.svg?no-cache
+[win-x64-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-win-x64.txt
+[win-x64-installer-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-x64.exe
+[win-x64-installer-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-x64.exe.sha
+[win-x64-zip-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-x64.zip
+[win-x64-zip-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-x64.zip.sha
[win-x64-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/win_x64_Release_version_badge.svg?no-cache
[win-x64-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-win-x64.txt
[win-x64-installer-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x64.exe
-[win-x64-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x64.exe.sha512
+[win-x64-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x64.exe.sha
[win-x64-zip-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x64.zip
-[win-x64-zip-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x64.zip.sha512
-
-[win-x64-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/win_x64_Release_version_badge.svg?no-cache
-[win-x64-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-win-x64.txt
-[win-x64-installer-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-x64.exe
-[win-x64-installer-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-x64.exe.sha512
-[win-x64-zip-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-x64.zip
-[win-x64-zip-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-x64.zip.sha512
+[win-x64-zip-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x64.zip.sha
[win-x86-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/win_x86_Release_version_badge.svg?no-cache
[win-x86-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-win-x86.txt
[win-x86-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.exe
-[win-x86-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.exe.sha512
+[win-x86-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.exe.sha
[win-x86-zip-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.zip
-[win-x86-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.zip.sha512
+[win-x86-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-x86.zip.sha
+
+[win-x86-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/win_x86_Release_version_badge.svg?no-cache
+[win-x86-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-win-x86.txt
+[win-x86-installer-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-x86.exe
+[win-x86-installer-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-x86.exe.sha
+[win-x86-zip-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-x86.zip
+[win-x86-zip-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-x86.zip.sha
[win-x86-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/win_x86_Release_version_badge.svg?no-cache
[win-x86-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-win-x86.txt
[win-x86-installer-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x86.exe
-[win-x86-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x86.exe.sha512
+[win-x86-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x86.exe.sha
[win-x86-zip-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x86.zip
-[win-x86-zip-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x86.zip.sha512
-
-[win-x86-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/win_x86_Release_version_badge.svg?no-cache
-[win-x86-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-win-x86.txt
-[win-x86-installer-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-x86.exe
-[win-x86-installer-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-x86.exe.sha512
-[win-x86-zip-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-x86.zip
-[win-x86-zip-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-x86.zip.sha512
+[win-x86-zip-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-x86.zip.sha
[osx-x64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/osx_x64_Release_version_badge.svg?no-cache
[osx-x64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-osx-x64.txt
[osx-x64-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.pkg
-[osx-x64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.pkg.sha512
+[osx-x64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.pkg.sha
[osx-x64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.tar.gz
-[osx-x64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha512
+[osx-x64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha
+
+[osx-x64-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/osx_x64_Release_version_badge.svg?no-cache
+[osx-x64-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-osx-x64.txt
+[osx-x64-installer-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-osx-x64.pkg
+[osx-x64-installer-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-osx-x64.pkg.sha
+[osx-x64-targz-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-osx-x64.tar.gz
+[osx-x64-targz-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha
[osx-x64-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/osx_x64_Release_version_badge.svg?no-cache
[osx-x64-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-osx-x64.txt
[osx-x64-installer-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-x64.pkg
-[osx-x64-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-x64.pkg.sha512
+[osx-x64-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-x64.pkg.sha
[osx-x64-targz-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-x64.tar.gz
-[osx-x64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha512
-
-[osx-x64-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/osx_x64_Release_version_badge.svg?no-cache
-[osx-x64-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-osx-x64.txt
-[osx-x64-installer-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-osx-x64.pkg
-[osx-x64-installer-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-osx-x64.pkg.sha512
-[osx-x64-targz-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-osx-x64.tar.gz
-[osx-x64-targz-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha512
+[osx-x64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-x64.pkg.tar.gz.sha
[osx-arm64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/osx_arm64_Release_version_badge.svg?no-cache
[osx-arm64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-osx-arm64.txt
[osx-arm64-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.pkg
-[osx-arm64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.sha512
+[osx-arm64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.sha
[osx-arm64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.tar.gz
-[osx-arm64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha512
+[osx-arm64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha
+
+[osx-arm64-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/osx_arm64_Release_version_badge.svg?no-cache
+[osx-arm64-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-osx-arm64.txt
+[osx-arm64-installer-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-osx-arm64.pkg
+[osx-arm64-installer-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-osx-arm64.pkg.sha
+[osx-arm64-targz-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-osx-arm64.tar.gz
+[osx-arm64-targz-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha
[osx-arm64-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/osx_arm64_Release_version_badge.svg?no-cache
[osx-arm64-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-osx-arm64.txt
[osx-arm64-installer-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-arm64.pkg
-[osx-arm64-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-arm64.pkg.sha512
+[osx-arm64-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-arm64.pkg.sha
[osx-arm64-targz-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-arm64.tar.gz
-[osx-arm64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha512
-
-[osx-arm64-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/osx_arm64_Release_version_badge.svg?no-cache
-[osx-arm64-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-osx-arm64.txt
-[osx-arm64-installer-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-osx-arm64.pkg
-[osx-arm64-installer-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.sha512
-[osx-arm64-targz-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-osx-arm64.tar.gz
-[osx-arm64-targz-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha512
+[osx-arm64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-osx-arm64.pkg.tar.gz.sha
[linux-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_x64_Release_version_badge.svg?no-cache
[linux-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-x64.txt
[linux-DEB-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.deb
-[linux-DEB-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.deb.sha512
+[linux-DEB-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.deb.sha
[linux-RPM-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.rpm
-[linux-RPM-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.rpm.sha512
+[linux-RPM-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-x64.rpm.sha
[linux-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-x64.tar.gz
-[linux-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-x64.tar.gz.sha512
+[linux-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-x64.tar.gz.sha
+
+[linux-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/linux_x64_Release_version_badge.svg?no-cache
+[linux-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-linux-x64.txt
+[linux-DEB-installer-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-x64.deb
+[linux-DEB-installer-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-x64.deb.sha
+[linux-RPM-installer-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-x64.rpm
+[linux-RPM-installer-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-x64.rpm.sha
+[linux-targz-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-x64.tar.gz
+[linux-targz-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-x64.tar.gz.sha
[linux-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/linux_x64_Release_version_badge.svg?no-cache
[linux-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-linux-x64.txt
[linux-DEB-installer-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-x64.deb
-[linux-DEB-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-x64.deb.sha512
+[linux-DEB-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-x64.deb.sha
[linux-RPM-installer-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-x64.rpm
-[linux-RPM-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-x64.rpm.sha512
+[linux-RPM-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-x64.rpm.sha
[linux-targz-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-x64.tar.gz
-[linux-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-x64.tar.gz.sha512
-
-[linux-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/linux_x64_Release_version_badge.svg?no-cache
-[linux-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-linux-x64.txt
-[linux-DEB-installer-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-x64.deb
-[linux-DEB-installer-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-x64.deb.sha512
-[linux-RPM-installer-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-x64.rpm
-[linux-RPM-installer-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-x64.rpm.sha512
-[linux-targz-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-x64.tar.gz
-[linux-targz-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-x64.tar.gz.sha512
+[linux-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-x64.tar.gz.sha
[linux-arm-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_arm_Release_version_badge.svg?no-cache
[linux-arm-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-arm.txt
[linux-arm-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm.tar.gz
-[linux-arm-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm.tar.gz.sha512
+[linux-arm-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm.tar.gz.sha
+
+[linux-arm-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/linux_arm_Release_version_badge.svg?no-cache
+[linux-arm-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-linux-arm.txt
+[linux-arm-targz-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-arm.tar.gz
+[linux-arm-targz-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-arm.tar.gz.sha
[linux-arm-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/linux_arm_Release_version_badge.svg?no-cache
[linux-arm-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-linux-arm.txt
[linux-arm-targz-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-arm.tar.gz
-[linux-arm-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-arm.tar.gz.sha512
-
-[linux-arm-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/linux_arm_Release_version_badge.svg?no-cache
-[linux-arm-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-linux-arm.txt
-[linux-arm-targz-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-arm.tar.gz
-[linux-arm-targz-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-arm.tar.gz.sha512
+[linux-arm-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-arm.tar.gz.sha
[linux-arm64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_arm64_Release_version_badge.svg?no-cache
[linux-arm64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-arm64.txt
[linux-arm64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm64.tar.gz
-[linux-arm64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm64.tar.gz.sha512
+[linux-arm64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-arm64.tar.gz.sha
+
+[linux-arm64-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/linux_arm64_Release_version_badge.svg?no-cache
+[linux-arm64-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-linux-arm64.txt
+[linux-arm64-targz-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-arm64.tar.gz
+[linux-arm64-targz-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-arm64.tar.gz.sha
[linux-arm64-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/linux_arm64_Release_version_badge.svg?no-cache
[linux-arm64-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-linux-arm64.txt
[linux-arm64-targz-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-arm64.tar.gz
-[linux-arm64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-arm64.tar.gz.sha512
+[linux-arm64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-arm64.tar.gz.sha
+
+[rhel-6-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/rhel.6_x64_Release_version_badge.svg?no-cache
+[rhel-6-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-rhel.6-x64.txt
+[rhel-6-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-rhel.6-x64.tar.gz
+[rhel-6-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-rhel.6-x64.tar.gz.sha
+
+[rhel-6-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/rhel.6_x64_Release_version_badge.svg?no-cache
+[rhel-6-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-rhel.6-x64.txt
+[rhel-6-targz-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-rhel.6-x64.tar.gz
+[rhel-6-targz-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-rhel.6-x64.tar.gz.sha
-[linux-arm64-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/linux_arm64_Release_version_badge.svg?no-cache
-[linux-arm64-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-linux-arm64.txt
-[linux-arm64-targz-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-arm64.tar.gz
-[linux-arm64-targz-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-arm64.tar.gz.sha512
+[rhel-6-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/rhel.6_x64_Release_version_badge.svg?no-cache
+[rhel-6-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-rhel.6-x64.txt
+[rhel-6-targz-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-rhel.6-x64.tar.gz
+[rhel-6-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-rhel.6-x64.tar.gz.sha
[linux-musl-x64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_musl_x64_Release_version_badge.svg?no-cache
[linux-musl-x64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-musl-x64.txt
[linux-musl-x64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-x64.tar.gz
-[linux-musl-x64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha512
+[linux-musl-x64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha
+
+[linux-musl-x64-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/linux_musl_x64_Release_version_badge.svg?no-cache
+[linux-musl-x64-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-linux-musl-x64.txt
+[linux-musl-x64-targz-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-musl-x64.tar.gz
+[linux-musl-x64-targz-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha
[linux-musl-x64-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/linux_musl_x64_Release_version_badge.svg?no-cache
[linux-musl-x64-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-linux-musl-x64.txt
[linux-musl-x64-targz-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-musl-x64.tar.gz
-[linux-musl-x64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha512
-
-[linux-musl-x64-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/linux_musl_x64_Release_version_badge.svg?no-cache
-[linux-musl-x64-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-linux-musl-x64.txt
-[linux-musl-x64-targz-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-musl-x64.tar.gz
-[linux-musl-x64-targz-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha512
+[linux-musl-x64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-musl-x64.tar.gz.sha
[linux-musl-arm-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_musl_arm_Release_version_badge.svg?no-cache
[linux-musl-arm-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-musl-arm.txt
[linux-musl-arm-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm.tar.gz
-[linux-musl-arm-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha512
+[linux-musl-arm-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha
+
+[linux-musl-arm-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/linux_musl_arm_Release_version_badge.svg?no-cache
+[linux-musl-arm-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-linux-musl-arm.txt
+[linux-musl-arm-targz-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-musl-arm.tar.gz
+[linux-musl-arm-targz-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha
[linux-musl-arm-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/linux_musl_arm_Release_version_badge.svg?no-cache
[linux-musl-arm-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-linux-musl-arm.txt
[linux-musl-arm-targz-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-musl-arm.tar.gz
-[linux-musl-arm-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha512
-
-[linux-musl-arm-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/linux_musl_arm_Release_version_badge.svg?no-cache
-[linux-musl-arm-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-linux-musl-arm.txt
-[linux-musl-arm-targz-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-musl-arm.tar.gz
-[linux-musl-arm-targz-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha512
+[linux-musl-arm-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-musl-arm.tar.gz.sha
[linux-musl-arm64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/linux_musl_arm64_Release_version_badge.svg?no-cache
[linux-musl-arm64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-linux-musl-arm64.txt
[linux-musl-arm64-targz-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz
-[linux-musl-arm64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha512
+[linux-musl-arm64-targz-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha
+
+[linux-musl-arm64-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/linux_musl_arm64_Release_version_badge.svg?no-cache
+[linux-musl-arm64-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-linux-musl-arm64.txt
+[linux-musl-arm64-targz-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz
+[linux-musl-arm64-targz-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha
[linux-musl-arm64-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/linux_musl_arm64_Release_version_badge.svg?no-cache
[linux-musl-arm64-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-linux-musl-arm64.txt
[linux-musl-arm64-targz-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz
-[linux-musl-arm64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha512
-
-[linux-musl-arm64-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/linux_musl_arm64_Release_version_badge.svg?no-cache
-[linux-musl-arm64-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-linux-musl-arm64.txt
-[linux-musl-arm64-targz-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz
-[linux-musl-arm64-targz-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha512
+[linux-musl-arm64-targz-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-linux-musl-arm64.tar.gz.sha
[win-arm-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/win_arm_Release_version_badge.svg?no-cache
[win-arm-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-win-arm.txt
[win-arm-zip-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm.zip
-[win-arm-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm.zip.sha512
+[win-arm-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm.zip.sha
+
+[win-arm-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/win_arm_Release_version_badge.svg?no-cache
+[win-arm-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-win-arm.txt
+[win-arm-zip-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-arm.zip
+[win-arm-zip-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-arm.zip.sha
[win-arm-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/win_arm_Release_version_badge.svg?no-cache
[win-arm-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-win-arm.txt
[win-arm-zip-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-arm.zip
-[win-arm-zip-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-arm.zip.sha512
-
-[win-arm-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/win_arm_Release_version_badge.svg?no-cache
-[win-arm-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-win-arm.txt
-[win-arm-zip-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-arm.zip
-[win-arm-zip-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-arm.zip.sha512
+[win-arm-zip-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-arm.zip.sha
[win-arm64-badge-main]: https://aka.ms/dotnet/10.0.1xx/daily/win_arm64_Release_version_badge.svg?no-cache
[win-arm64-version-main]: https://aka.ms/dotnet/10.0.1xx/daily/productCommit-win-arm64.txt
[win-arm64-installer-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.exe
-[win-arm64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.exe.sha512
+[win-arm64-installer-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.exe.sha
[win-arm64-zip-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.zip
-[win-arm64-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.zip.sha512
+[win-arm64-zip-checksum-main]: https://aka.ms/dotnet/10.0.1xx/daily/dotnet-sdk-win-arm64.zip.sha
+
+[win-arm64-badge-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/win_arm64_Release_version_badge.svg?no-cache
+[win-arm64-version-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/productCommit-win-arm64.txt
+[win-arm64-installer-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-arm64.exe
+[win-arm64-installer-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-arm64.exe.sha
+[win-arm64-zip-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-arm64.zip
+[win-arm64-zip-checksum-9.0.3XX]: https://aka.ms/dotnet/9.0.3xx/daily/dotnet-sdk-win-arm64.zip.sha
[win-arm64-badge-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/win_arm64_Release_version_badge.svg?no-cache
[win-arm64-version-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/productCommit-win-arm64.txt
[win-arm64-installer-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-arm64.exe
-[win-arm64-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-arm64.exe.sha512
+[win-arm64-installer-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-arm64.exe.sha
[win-arm64-zip-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-arm64.zip
-[win-arm64-zip-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-arm64.zip.sha512
-
-[win-arm64-badge-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/win_arm64_Release_version_badge.svg?no-cache
-[win-arm64-version-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/productCommit-win-arm64.txt
-[win-arm64-installer-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-arm64.exe
-[win-arm64-installer-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-arm64.exe.sha512
-[win-arm64-zip-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-arm64.zip
-[win-arm64-zip-checksum-9.0.1XX]: https://aka.ms/dotnet/9.0.1xx/daily/dotnet-sdk-win-arm64.zip.sha512
+[win-arm64-zip-checksum-9.0.2XX]: https://aka.ms/dotnet/9.0.2xx/daily/dotnet-sdk-win-arm64.zip.sha
diff --git a/eng/AfterSigning.targets b/eng/AfterSigning.targets
deleted file mode 100644
index 8e7aa3cf435c..000000000000
--- a/eng/AfterSigning.targets
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
- <_SuppressSdkImports>false
-
-
-
-
-
-
-
-
-
-
-
- %(FullPath).sha512
-
-
-
-
-
-
-
diff --git a/eng/Publishing.props b/eng/Publishing.props
index bc7ff4ee664a..e4a04a4a7351 100644
--- a/eng/Publishing.props
+++ b/eng/Publishing.props
@@ -6,239 +6,24 @@
true
-
- Sdk
- $(Product)
- assets/$(Product)
- -BuildPass$(DotNetBuildPass)
- $(OS)-$(PlatformName)-SdkAssets$(SdkAssetManifestBuildPass).xml
- $(ArtifactsLogDir)AssetManifest\$(SdkAssetManifestFileName)
-
- true
-
- false
- true
-
-
-
-
- $(SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)
-
-
- $(CollectionUri.Split('/')[3])
-
-
- $(CollectionUri.Split('.')[0].Split('/')[2])
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $(PublishDependsOnTargets);_ResolvePublishFSharpNuGetPackages
-
-
-
-
- Shipping
- Release
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- $(AGENT_OS)
- $(OS)
- $(AssetManifestOS)-$(SYSTEM_PHASENAME)
- $(AssetManifestOS)
- $(AssetManifestOS)-$(Architecture)
- $(BaseAssetManifestFileName)-installers$(SdkAssetManifestBuildPass)
-
- $(ArtifactsLogDir)AssetManifest\$(InstallersAssetManifestFileName).xml
+ To achieve this, we find the FSharp compiler package, then the stable or non-stable FSharp.Core and Compiler service
+ package contained within, depending on the stability switch of the SDK. The SDK then treats these packages as its own outputs,
+ which means they get automatically pushed on release day.
+
+ These packages have already been signed by the FSharp build so we don't need to re-include them for signing. -->
+
+ Shipping
+ Release
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
- $(BlobStoragePartialRelativePath)/$(FullNugetVersion)/$([System.String]::Copy('%(Filename)%(Extension)'))
- DotNetReleaseShipping=true
-
-
-
- $(BlobStoragePartialRelativePath)/$(FullNugetVersion)/$([System.String]::Copy('%(Filename)%(Extension)'))
- NonShipping=true
-
-
-
- $(BlobStoragePartialRelativePath)/$(FullNugetVersion)/$([System.String]::Copy('%(Filename)%(Extension)'))
- DotNetReleaseShipping=true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/eng/Signing.props b/eng/Signing.props
index d21cce9aa953..6eb14f2e0ba3 100644
--- a/eng/Signing.props
+++ b/eng/Signing.props
@@ -1,21 +1,14 @@
-
-
-
-
-
-
-
+
+
+ Sdk/$([System.IO.File]::ReadAllText('$(ArtifactsTmpDir)FullNugetVersion.version').Trim())
+ true
+
3PartySHA2
MicrosoftDotNet500
-
-
- true
- true
-
true
@@ -83,8 +76,47 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ %(FullPath).sha512
+
+
+
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index abe69229760d..bbb52c0f6715 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,60 +1,60 @@
-
+
https://github.com/dotnet/templating
- 6a6c089abc661bd597b32a63e41a9bd2a011950e
+ 7137d45fe29466e53caadc374472912811f6522e
-
+
https://github.com/dotnet/templating
- 6a6c089abc661bd597b32a63e41a9bd2a011950e
+ 7137d45fe29466e53caadc374472912811f6522e
-
+
https://github.com/dotnet/templating
- 6a6c089abc661bd597b32a63e41a9bd2a011950e
+ 7137d45fe29466e53caadc374472912811f6522e
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
@@ -73,341 +73,345 @@
0de3165cb0d56323b6caaf8e9916d4d9e72da32d
-
+
https://github.com/dotnet/msbuild
- c81c498d4efee2ac3360bbfb9f161016a5969994
+ 291a8108761ed347562228f2f8f25477996a5a93
-
+
https://github.com/dotnet/msbuild
- c81c498d4efee2ac3360bbfb9f161016a5969994
+ 291a8108761ed347562228f2f8f25477996a5a93
-
+
https://github.com/dotnet/msbuild
- c81c498d4efee2ac3360bbfb9f161016a5969994
+ 291a8108761ed347562228f2f8f25477996a5a93
-
+
https://github.com/dotnet/fsharp
- f4710e30f913b910ab468f0ca19dc67b7e561727
+ 387f04cb6675c984036a64c9bb768ac6f00e06f9
-
+
https://github.com/dotnet/fsharp
- f4710e30f913b910ab468f0ca19dc67b7e561727
+ 387f04cb6675c984036a64c9bb768ac6f00e06f9
-
+
https://github.com/dotnet/roslyn
- 6f7613e4d34d835667c90c6b09fbac1240f77bb1
+ 6908aead3a8b313798c381d5e435e9e6068301a7
-
+
https://github.com/dotnet/roslyn
- 6f7613e4d34d835667c90c6b09fbac1240f77bb1
+ 6908aead3a8b313798c381d5e435e9e6068301a7
-
+
https://github.com/dotnet/roslyn
- 6f7613e4d34d835667c90c6b09fbac1240f77bb1
+ 6908aead3a8b313798c381d5e435e9e6068301a7
-
+
https://github.com/dotnet/roslyn
- 6f7613e4d34d835667c90c6b09fbac1240f77bb1
+ 6908aead3a8b313798c381d5e435e9e6068301a7
-
+
https://github.com/dotnet/roslyn
- 6f7613e4d34d835667c90c6b09fbac1240f77bb1
+ 6908aead3a8b313798c381d5e435e9e6068301a7
-
+
https://github.com/dotnet/roslyn
- 6f7613e4d34d835667c90c6b09fbac1240f77bb1
+ 6908aead3a8b313798c381d5e435e9e6068301a7
-
+
https://github.com/dotnet/roslyn
- 6f7613e4d34d835667c90c6b09fbac1240f77bb1
+ 6908aead3a8b313798c381d5e435e9e6068301a7
-
+
https://github.com/dotnet/roslyn
- 6f7613e4d34d835667c90c6b09fbac1240f77bb1
+ 6908aead3a8b313798c381d5e435e9e6068301a7
-
+
https://github.com/dotnet/roslyn
- 6f7613e4d34d835667c90c6b09fbac1240f77bb1
+ 6908aead3a8b313798c381d5e435e9e6068301a7
-
+
+ https://github.com/dotnet/roslyn
+ 6908aead3a8b313798c381d5e435e9e6068301a7
+
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/nuget/nuget.client
- c4b26195ee5a77e70b2ea5fd50db87d6a9194c24
+ a4a2e3f31ddc2adb52ebd3204ea6f650d89caf3f
-
+
https://github.com/microsoft/vstest
- bcef12d909709f13027afe1557c724f11bc8df05
+ 0c76159090e0921b9cf38b95092dbccd4e59cd24
-
+
https://github.com/microsoft/vstest
- bcef12d909709f13027afe1557c724f11bc8df05
+ 0c76159090e0921b9cf38b95092dbccd4e59cd24
-
+
https://github.com/microsoft/vstest
- bcef12d909709f13027afe1557c724f11bc8df05
+ 0c76159090e0921b9cf38b95092dbccd4e59cd24
-
+
https://github.com/microsoft/vstest
- bcef12d909709f13027afe1557c724f11bc8df05
+ 0c76159090e0921b9cf38b95092dbccd4e59cd24
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/windowsdesktop
- 2077f17807081da3fbc2cc4afc9289d29f43d8e2
+ 4a45754d6da44a8d1f945b67126f44a55bdc32a6
-
+
https://github.com/dotnet/windowsdesktop
- 2077f17807081da3fbc2cc4afc9289d29f43d8e2
+ 4a45754d6da44a8d1f945b67126f44a55bdc32a6
-
+
https://github.com/dotnet/windowsdesktop
- 2077f17807081da3fbc2cc4afc9289d29f43d8e2
+ 4a45754d6da44a8d1f945b67126f44a55bdc32a6
-
+
https://github.com/dotnet/windowsdesktop
- 2077f17807081da3fbc2cc4afc9289d29f43d8e2
+ 4a45754d6da44a8d1f945b67126f44a55bdc32a6
-
+
https://github.com/dotnet/wpf
- 3ce8132a0e8d6c8a9078d52383d6852b93c981c3
+ 6110f47e04816b7064861ad8b4ebae29149aec2b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/razor
- 46efcec83821d7f0322c01bc9549de83e855dcac
+ 25c7bf034222c9621fcc78e6cb503664e4604f0b
-
+
https://github.com/dotnet/razor
- 46efcec83821d7f0322c01bc9549de83e855dcac
+ 25c7bf034222c9621fcc78e6cb503664e4604f0b
-
+
https://github.com/dotnet/razor
- 46efcec83821d7f0322c01bc9549de83e855dcac
+ 25c7bf034222c9621fcc78e6cb503664e4604f0b
-
+
https://github.com/dotnet/razor
- 46efcec83821d7f0322c01bc9549de83e855dcac
+ 25c7bf034222c9621fcc78e6cb503664e4604f0b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/winforms
- f0026e791faecab516dc9bd3d16872cd75cdbdb8
+ f79026644d3f38a7ab9cfb734a011d5448346d66
-
+
https://github.com/dotnet/wpf
- 3ce8132a0e8d6c8a9078d52383d6852b93c981c3
+ 6110f47e04816b7064861ad8b4ebae29149aec2b
https://github.com/dotnet/xdt
@@ -419,228 +423,228 @@
1156b9aac00609107c21cf3458b797c76db6be7a
-
+
https://github.com/dotnet/roslyn-analyzers
- 45caa455b34553e7ef3ff7cb1def454eef78037c
+ 8fe7aeb135c64e095f43292c427453858d937184
-
+
https://github.com/dotnet/roslyn-analyzers
- 45caa455b34553e7ef3ff7cb1def454eef78037c
+ 8fe7aeb135c64e095f43292c427453858d937184
-
+
https://github.com/dotnet/roslyn-analyzers
- 45caa455b34553e7ef3ff7cb1def454eef78037c
+ 8fe7aeb135c64e095f43292c427453858d937184
-
+
https://github.com/dotnet/command-line-api
- feb61c7f328a2401d74f4317b39d02126cfdfe24
+ 3bbb940ceeb3254790899d751a8d418348563d40
-
+
https://github.com/dotnet/command-line-api
- feb61c7f328a2401d74f4317b39d02126cfdfe24
+ 3bbb940ceeb3254790899d751a8d418348563d40
-
+
https://github.com/dotnet/symreader
ff1852196a042a124267a6e599d12da20d7ff65a
-
+
https://github.com/dotnet/command-line-api
- feb61c7f328a2401d74f4317b39d02126cfdfe24
+ 3bbb940ceeb3254790899d751a8d418348563d40
-
+
https://github.com/dotnet/source-build-externals
- c9e4423c80e00371e43e306ee6e2e97fd2196af9
+ 962bb86f95ebece01c2b1de97ed7d4261e00b058
-
+
https://github.com/dotnet/source-build-reference-packages
- be366997dfae0aa6e3c9a78bad10bfb3f79cbde1
+ f18184030957ae8b02c6cb971ff2a58a06d2e7b1
-
+
https://github.com/dotnet/deployment-tools
- c7bcd7d32f7744af89dfd031a9b2085e3004fd9c
+ eddd72e696074a457cabf62df5d889928324e0b7
-
+
https://github.com/dotnet/sourcelink
- 762b0e8b055fe9f203755b651e67c829e759f44d
+ fab841741055c19fc07623f8947cecc9a0f63baa
-
+
https://github.com/dotnet/sourcelink
- 762b0e8b055fe9f203755b651e67c829e759f44d
+ fab841741055c19fc07623f8947cecc9a0f63baa
-
+
https://github.com/dotnet/sourcelink
- 762b0e8b055fe9f203755b651e67c829e759f44d
+ fab841741055c19fc07623f8947cecc9a0f63baa
-
+
https://github.com/dotnet/sourcelink
- 762b0e8b055fe9f203755b651e67c829e759f44d
+ fab841741055c19fc07623f8947cecc9a0f63baa
-
+
https://github.com/dotnet/sourcelink
- 762b0e8b055fe9f203755b651e67c829e759f44d
+ fab841741055c19fc07623f8947cecc9a0f63baa
-
+
https://github.com/dotnet/sourcelink
- 762b0e8b055fe9f203755b651e67c829e759f44d
+ fab841741055c19fc07623f8947cecc9a0f63baa
-
+
https://github.com/dotnet/sourcelink
- 762b0e8b055fe9f203755b651e67c829e759f44d
+ fab841741055c19fc07623f8947cecc9a0f63baa
-
+
https://github.com/dotnet/deployment-tools
- c7bcd7d32f7744af89dfd031a9b2085e3004fd9c
+ eddd72e696074a457cabf62df5d889928324e0b7
-
+
https://github.com/dotnet/symreader
ff1852196a042a124267a6e599d12da20d7ff65a
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/aspnetcore
- 82269fc71f9ebb38457d5a0010a1171430f16db5
+ febd7e8bdf05f17fb4e0e4dd3123e9538fbf7e7b
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/arcade
- 98b4ae348fa01b99dc6fbfc8f601efd9b90090db
+ bbea86c614fcf4380c58c80eacd279a0b8305a79
-
+
https://github.com/dotnet/arcade
- 98b4ae348fa01b99dc6fbfc8f601efd9b90090db
+ bbea86c614fcf4380c58c80eacd279a0b8305a79
-
+
https://github.com/dotnet/arcade
- 98b4ae348fa01b99dc6fbfc8f601efd9b90090db
+ bbea86c614fcf4380c58c80eacd279a0b8305a79
-
+
https://github.com/dotnet/arcade
- 98b4ae348fa01b99dc6fbfc8f601efd9b90090db
+ bbea86c614fcf4380c58c80eacd279a0b8305a79
-
+
https://github.com/dotnet/arcade
- 98b4ae348fa01b99dc6fbfc8f601efd9b90090db
+ bbea86c614fcf4380c58c80eacd279a0b8305a79
-
+
https://github.com/dotnet/arcade
- 98b4ae348fa01b99dc6fbfc8f601efd9b90090db
+ bbea86c614fcf4380c58c80eacd279a0b8305a79
-
+
https://github.com/dotnet/arcade
- 98b4ae348fa01b99dc6fbfc8f601efd9b90090db
+ bbea86c614fcf4380c58c80eacd279a0b8305a79
-
+
https://github.com/dotnet/runtime
- 6c58f7992cfd628a53d9b90f258ac123cb803644
+ e51af404d1ea26be4a3d8e51fe21cf2f09ad34dd
-
+
https://github.com/dotnet/arcade-services
- 6c7cc1d1653051d35db84d40ef77ee6702d91436
+ 0a9f6b177026a7473b9225355269c2c9a90b777f
-
+
https://github.com/dotnet/arcade-services
- 6c7cc1d1653051d35db84d40ef77ee6702d91436
+ 0a9f6b177026a7473b9225355269c2c9a90b777f
https://github.com/dotnet/scenario-tests
@@ -675,13 +679,13 @@
https://github.com/dotnet/runtime
e77011b31a3e5c47d931248a64b47f9b2d47853d
-
+
https://github.com/microsoft/testfx
- edc05edf02f74ff5d32b02c782bf10aa1d774a5b
+ b5c93525849bce7009877e042501e171edc9585a
-
+
https://github.com/microsoft/testfx
- edc05edf02f74ff5d32b02c782bf10aa1d774a5b
+ b5c93525849bce7009877e042501e171edc9585a
diff --git a/eng/Versions.props b/eng/Versions.props
index 7904d6b34c41..ad5f94af7360 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -18,17 +18,18 @@
false
release
- alpha
+ preview
rtm
servicing
- 1
+ 2
true
6.0.1
true
- 1.6.0-preview.25067.5
+
+ 1.6.0-preview.25101.5
30
@@ -36,8 +37,8 @@
17
36
20
- 11
- 0
+ 12
+ 1
<_NET70ILLinkPackVersion>7.0.100-1.23211.1
@@ -57,10 +58,9 @@
4.8.6
1.2.0-beta.435
4.0.5
- 2.0.0-beta4.24528.1
- 0.4.0-alpha.24528.1
- 2.0.0-preview.1.24630.1
- 2.2.0-beta.24622.1
+ 2.0.0-beta4.25071.2
+ 2.0.0-preview.1.25071.2
+ 2.2.0-beta.25076.1
1.1.2-beta1.22216.1
10.3.0
3.2.2146
@@ -75,7 +75,7 @@
- 1.1.0-beta.25064.2
+ 1.1.0-beta.25081.2
@@ -83,83 +83,86 @@
- 10.0.0-alpha.1.25066.5
+ 10.0.0-preview.2.25080.2
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
8.0.0-rc.1.23414.4
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
2.1.0
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
- 10.0.0-alpha.1.25067.10
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
+ 10.0.0-preview.2.25101.4
8.0.0
2.0.0-preview.1.24427.4
+ 4.5.1
8.0.0
+ 4.5.5
8.0.0
8.0.0
8.0.5
+ 4.5.4
8.0.0
- 10.0.0-alpha.1.25067.6
- 10.0.0-alpha.1.25067.6
- 10.0.0-alpha.1.25067.6
+ 10.0.0-preview.2.25081.1
+ 10.0.0-preview.2.25081.1
+ 10.0.0-preview.2.25081.1
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
- 6.13.0-rc.113
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
+ 6.14.0-preview.1.11
- 17.13.0-preview-25060-03
- 17.13.0-preview-25060-03
- 17.13.0-preview-25060-03
+ 17.14.0-preview-25079-01
+ 17.14.0-preview-25079-01
+ 17.14.0-preview-25079-01
@@ -167,8 +170,8 @@
- 10.0.0-preview.25066.1
- 3.12.0-beta1.25066.1
+ 10.0.0-preview.25081.1
+ 3.12.0-beta1.25081.1
@@ -189,65 +192,66 @@
At usage sites, either we use MicrosoftBuildMinimumVersion, or MicrosoftBuildVersion in source-only modes.
Additionally, set the MinimumVSVersion for the installer UI that's required for targeting NetCurrent -->
- 17.14.0-preview-25067-08
- 17.14.0-preview-25067-08
+ 17.14.0-preview-25073-02
+ 17.14.0-preview-25073-02
17.11.4
17.12
- 10.0.100-alpha.1.25066.10
+ 10.0.100-preview.2.25080.1
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
- 10.0.100-alpha.1.25066.10
+ 10.0.100-preview.2.25080.1
$(MicrosoftTemplateEngineMocksPackageVersion)
$(MicrosoftTemplateEngineAbstractionsPackageVersion)
$(MicrosoftTemplateEngineMocksPackageVersion)
- 13.9.300-beta.25067.2
+ 13.9.300-beta.25079.4
- 4.14.0-1.25068.1
- 4.14.0-1.25068.1
- 4.14.0-1.25068.1
- 4.14.0-1.25068.1
- 4.14.0-1.25068.1
- 4.14.0-1.25068.1
- 4.14.0-1.25068.1
- 4.14.0-1.25068.1
+ 4.14.0-1.25070.2
+ 4.14.0-1.25070.2
+ 4.14.0-1.25070.2
+ 4.14.0-1.25070.2
+ 4.14.0-1.25070.2
+ 4.14.0-1.25070.2
+ 4.14.0-1.25070.2
+ 4.14.0-1.25070.2
+ 4.14.0-1.25070.2
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
- 10.0.0-alpha.2.25068.1
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
+ 10.0.0-alpha.2.25073.4
- 9.0.0-preview.25058.6
- 9.0.0-preview.25058.6
- 9.0.0-preview.25058.6
+ 9.0.0-preview.25081.4
+ 9.0.0-preview.25081.4
+ 9.0.0-preview.25081.4
- 10.0.0-alpha.1.25067.3
- 10.0.0-alpha.1.25067.3
+ 10.0.0-preview.2.25081.3
+ 10.0.0-preview.2.25081.3
@@ -266,19 +270,19 @@
- 10.0.0-beta.25061.1
- 10.0.0-beta.25061.1
- 10.0.0-beta.25061.1
- 10.0.0-beta.25061.1
+ 10.0.0-beta.25080.7
+ 10.0.0-beta.25080.7
+ 10.0.0-beta.25080.7
+ 10.0.0-beta.25080.7
- 9.0.0-beta.25066.1
- 9.0.0-beta.25066.1
- 9.0.0-beta.25066.1
- 9.0.0-beta.25066.1
- 9.0.0-beta.25066.1
- 9.0.0-beta.25066.1
+ 10.0.0-beta.25080.2
+ 10.0.0-beta.25080.2
+ 10.0.0-beta.25080.2
+ 10.0.0-beta.25080.2
+ 10.0.0-beta.25080.2
+ 10.0.0-beta.25080.2
@@ -289,7 +293,7 @@
6.12.0
6.1.0
4.18.4
- 3.8.0-preview.25067.5
+ 3.8.0-preview.25101.5
1.3.2
8.0.0-beta.23607.1
diff --git a/eng/common/internal/Tools.csproj b/eng/common/internal/Tools.csproj
index 32f79dfb3402..feaa6d20812d 100644
--- a/eng/common/internal/Tools.csproj
+++ b/eng/common/internal/Tools.csproj
@@ -15,16 +15,6 @@
-
-
-
- https://devdiv.pkgs.visualstudio.com/_packaging/dotnet-core-internal-tooling/nuget/v3/index.json;
-
-
- $(RestoreSources);
- https://devdiv.pkgs.visualstudio.com/_packaging/VS/nuget/v3/index.json;
-
-
diff --git a/eng/common/native/install-dependencies.sh b/eng/common/native/install-dependencies.sh
index 71bde0e4573b..ce661e9e5cd8 100755
--- a/eng/common/native/install-dependencies.sh
+++ b/eng/common/native/install-dependencies.sh
@@ -24,13 +24,13 @@ case "$os" in
apt update
apt install -y build-essential gettext locales cmake llvm clang lld lldb liblldb-dev libunwind8-dev libicu-dev liblttng-ust-dev \
- libssl-dev libkrb5-dev zlib1g-dev pigz cpio
+ libssl-dev libkrb5-dev pigz cpio
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ]; then
- dnf install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel zlib-devel lttng-ust-devel pigz cpio
+ dnf install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio
elif [ "$ID" = "alpine" ]; then
- apk add build-base cmake bash curl clang llvm-dev lld lldb krb5-dev lttng-ust-dev icu-dev zlib-dev openssl-dev pigz cpio
+ apk add build-base cmake bash curl clang llvm-dev lld lldb krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio
else
echo "Unsupported distro. distro: $ID"
exit 1
diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1
index 853c63849626..80f9130b1508 100644
--- a/eng/common/tools.ps1
+++ b/eng/common/tools.ps1
@@ -42,7 +42,7 @@
[bool]$useInstalledDotNetCli = if (Test-Path variable:useInstalledDotNetCli) { $useInstalledDotNetCli } else { $true }
# Enable repos to use a particular version of the on-line dotnet-install scripts.
-# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1
+# default URL: https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1
[string]$dotnetInstallScriptVersion = if (Test-Path variable:dotnetInstallScriptVersion) { $dotnetInstallScriptVersion } else { 'v1' }
# True to use global NuGet cache instead of restoring packages to repository-local directory.
@@ -262,7 +262,7 @@ function GetDotNetInstallScript([string] $dotnetRoot) {
if (!(Test-Path $installScript)) {
Create-Directory $dotnetRoot
$ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit
- $uri = "https://raw.githubusercontent.com/dotnet/install-scripts/4b17227b30fbbad567d4d4fba17c59da51bc817b/src/dotnet-install.ps1"
+ $uri = "https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1"
Retry({
Write-Host "GET $uri"
diff --git a/eng/common/tools.sh b/eng/common/tools.sh
index 84ab5f124acc..df203b517842 100755
--- a/eng/common/tools.sh
+++ b/eng/common/tools.sh
@@ -54,7 +54,7 @@ warn_as_error=${warn_as_error:-true}
use_installed_dotnet_cli=${use_installed_dotnet_cli:-true}
# Enable repos to use a particular version of the on-line dotnet-install scripts.
-# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh
+# default URL: https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh
dotnetInstallScriptVersion=${dotnetInstallScriptVersion:-'v1'}
# True to use global NuGet cache instead of restoring packages to repository-local directory.
@@ -295,7 +295,7 @@ function with_retries {
function GetDotNetInstallScript {
local root=$1
local install_script="$root/dotnet-install.sh"
- local install_script_url="https://raw.githubusercontent.com/dotnet/install-scripts/4b17227b30fbbad567d4d4fba17c59da51bc817b/src/dotnet-install.sh"
+ local install_script_url="https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh"
if [[ ! -a "$install_script" ]]; then
mkdir -p "$root"
diff --git a/eng/install-scancode.sh b/eng/install-scancode.sh
index c82c274ed018..39ad70be318c 100755
--- a/eng/install-scancode.sh
+++ b/eng/install-scancode.sh
@@ -5,7 +5,7 @@ set -euo pipefail
# Install instructions: https://scancode-toolkit.readthedocs.io/en/latest/getting-started/install.html#installation-as-a-library-via-pip
# See latest release at https://github.com/nexB/scancode-toolkit/releases
-SCANCODE_VERSION="32.2.1"
+SCANCODE_VERSION="32.3.2"
pyEnvPath="/tmp/scancode-env"
python3 -m venv $pyEnvPath
diff --git a/eng/pipelines/templates/jobs/vmr-build.yml b/eng/pipelines/templates/jobs/vmr-build.yml
index a85476f6dc5d..9c570993ed5d 100644
--- a/eng/pipelines/templates/jobs/vmr-build.yml
+++ b/eng/pipelines/templates/jobs/vmr-build.yml
@@ -43,13 +43,8 @@ parameters:
type: boolean
default: false
-- name: useDevVersions
- displayName: True when build output uses dev/CI versioning instead of official build versioning
- type: boolean
- default: false
-
- name: sign
- displayName: True when build output should be signed
+ displayName: True when build output should be signed (includes dry runs)
type: boolean
default: true
@@ -142,9 +137,13 @@ jobs:
${{ if ne(parameters.reuseBuildArtifactsFrom, '') }}:
${{ if eq(parameters.buildPass, '') }}:
- # Always attempt to run the bootstrap leg (e.g. even when stage 1 tests fail) in order to get a complete accessment of the build status.
+ # For PR builds, skip the stage 2 build if the stage 1 build fails.
+ # Otherwise, run the stage 2 build even if the stage 1 build fails so that we can get a complete assessment of the build status.
# The build shortcuts when stage 1 build fails and doesn't produce the SDK.
- condition: succeededOrFailed()
+ ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
+ condition: succeeded()
+ ${{ else }}:
+ condition: succeededOrFailed()
${{ else }}:
condition: succeeded()
dependsOn: ${{ parameters.reuseBuildArtifactsFrom }}
@@ -199,25 +198,14 @@ jobs:
- ${{ if eq(parameters.targetOS, 'windows') }}:
## Basic arguments
- name: baseArguments
- value: -ci -cleanWhileBuilding -prepareMachine /p:VerticalName=$(Agent.JobName) /p:ArtifactsStagingDir=$(artifactsStagingDir)
-
- - name: configArguments
- value: -c ${{ parameters.configuration }}
+ value: -ci -cleanWhileBuilding -prepareMachine -c ${{ parameters.configuration }} $(officialBuildParameter) /p:VerticalName=$(Agent.JobName) /p:ArtifactsStagingDir=$(artifactsStagingDir)
- name: targetArguments
value: /p:TargetOS=${{ parameters.targetOS }} /p:TargetArchitecture=${{ parameters.targetArchitecture }}
- ### Dev versions variables
- - ${{ if eq(parameters.useDevVersions, 'True') }}:
- - name: devArguments
- value: -dev
- - ${{ else }}:
- - name: devArguments
- value: ''
-
### Signing variables
- ${{ if eq(parameters.sign, 'True') }}:
- - ${{ if or(eq(parameters.useDevVersions, 'True'), eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest')) }}:
+ - ${{ if or(eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest')) }}:
# The _SignType variable is used by microbuild installation
- name: _SignType
value: ''
@@ -375,9 +363,7 @@ jobs:
- script: build.cmd
$(baseArguments)
- $(configArguments)
$(targetArguments)
- $(devArguments)
$(signArguments)
$(buildPassArguments)
${{ parameters.extraProperties }}
@@ -385,8 +371,14 @@ jobs:
workingDirectory: ${{ variables.sourcesPath }}
- ${{ if eq(parameters.runTests, 'True') }}:
- - script: |
- call build.cmd -ci -prepareMachine -test -excludeCIBinarylog /bl:artifacts/log/Release/Test.binlog -c ${{ parameters.configuration }} /p:TargetOS=${{ parameters.targetOS }} /p:TargetArchitecture=${{ parameters.targetArchitecture }} /p:VerticalName=$(Agent.JobName) /p:ArtifactsStagingDir=$(artifactsStagingDir) ${{ parameters.extraProperties }}
+ - script: build.cmd
+ $(baseArguments)
+ $(targetArguments)
+ $(buildPassArguments)
+ ${{ parameters.extraProperties }}
+ -test
+ -excludeCIBinarylog
+ /bl:artifacts/log/Release/Test.binlog
displayName: Run Tests
workingDirectory: ${{ variables.sourcesPath }}
timeoutInMinutes: ${{ variables.runTestsTimeout }}
@@ -436,7 +428,7 @@ jobs:
customEnvVars=""
customPreBuildArgs=""
- customBuildArgs="--ci --clean-while-building --prepareMachine -c ${{ parameters.configuration }}"
+ customBuildArgs="--ci --clean-while-building --prepareMachine -c ${{ parameters.configuration }} $(officialBuildParameter)"
if [[ '${{ parameters.runOnline }}' == 'True' ]]; then
customBuildArgs="$customBuildArgs --online"
@@ -462,10 +454,6 @@ jobs:
customBuildArgs="$customBuildArgs --use-mono-runtime"
fi
- if [[ '${{ parameters.useDevVersions }}' == 'True' ]]; then
- customBuildArgs="$customBuildArgs --dev"
- fi
-
if [[ '${{ parameters.sign }}' == 'True' ]] && [[ '${{ parameters.buildSourceOnly }}' != 'True' ]]; then
customBuildArgs="$customBuildArgs --sign"
if [[ '$(_SignType)' == 'real' ]] || [[ '$(_SignType)' == 'test' ]]; then
@@ -546,7 +534,7 @@ jobs:
customPreBuildArgs=''
customBuildArgs=''
extraBuildProperties=''
- customBuildArgs="--ci --prepareMachine -c ${{ parameters.configuration }}"
+ customBuildArgs="--ci --prepareMachine -c ${{ parameters.configuration }} $(officialBuildParameter)"
if [[ '${{ parameters.runOnline }}' == 'False' ]]; then
customPreBuildArgs="$customPreBuildArgs sudo"
@@ -560,10 +548,6 @@ jobs:
extraBuildProperties="$extraBuildProperties /p:TargetArchitecture=${{ parameters.targetArchitecture }}"
fi
- if [[ '${{ parameters.useDevVersions }}' == 'True' ]]; then
- customBuildArgs="$customBuildArgs --dev"
- fi
-
if [[ '${{ parameters.buildSourceOnly }}' == 'True' ]]; then
if [[ '${{ parameters.enablePoison }}' == 'True' ]]; then
customBuildArgs="$customBuildArgs --poison"
@@ -600,7 +584,6 @@ jobs:
SourceFolder: '$(sourcesPath)'
Contents: |
artifacts/log/**
- artifacts/scenario-tests/**/*.binlog
artifacts/TestResults/**/*.binlog
artifacts/TestResults/**/*.diff
artifacts/TestResults/**/Updated*.txt
diff --git a/eng/pipelines/templates/stages/vmr-build.yml b/eng/pipelines/templates/stages/vmr-build.yml
index bdede627acf5..59821af97fb5 100644
--- a/eng/pipelines/templates/stages/vmr-build.yml
+++ b/eng/pipelines/templates/stages/vmr-build.yml
@@ -7,6 +7,10 @@ parameters:
type: string
default: $(Build.SourceBranch)
+- name: desiredSigning
+ type: string
+ default: ''
+
# Scope of jobs which are executed
- name: scope
type: string
@@ -369,19 +373,6 @@ stages:
image: ${{ variables.ubuntuContainerImage }}
targetOS: linux
targetArchitecture: x64
- useDevVersions: true # Use dev versions for CI validation of the experience.
- runTests: false # Temporarily do not run tests. The nuget comparison fails for some non-obvious reason and needs further investigation. Mostly, I'm not sure why it ever passed. https://github.com/dotnet/sdk/issues/42920
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: Windows_DevVersions
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Windows }}
- targetOS: windows
- targetArchitecture: x64
- useDevVersions: true # Use dev versions for CI validation of the experience.
- runTests: false # Temporarily do not run tests. The nuget comparison fails for some non-obvious reason and needs further investigation. Mostly, I'm not sure why it ever passed. https://github.com/dotnet/sdk/issues/42920
#### VERTICAL BUILD (Official) ####
- ${{ if not(parameters.isSourceOnlyBuild) }}:
@@ -392,6 +383,7 @@ stages:
- template: ../variables/vmr-build.yml
parameters:
vmrBranch: ${{ parameters.vmrBranch }}
+ desiredSigning: ${{ parameters.desiredSigning }}
jobs:
- template: ../jobs/vmr-build.yml
@@ -399,28 +391,40 @@ stages:
buildName: Windows
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Windows }}
targetOS: windows
targetArchitecture: x64
+ - template: ../jobs/vmr-build.yml
+ parameters:
+ buildName: Windows
+ isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
+ vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
+ pool: ${{ parameters.pool_Windows }}
+ targetOS: windows
+ targetArchitecture: x86
+
- template: ../jobs/vmr-build.yml
parameters:
buildName: Android_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.androidCrossContainerName }}
image: ${{ variables.androidCrossContainerImage }}
targetOS: android
targetArchitecture: arm64
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: Browser_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.browserCrossContainerName }}
@@ -428,17 +432,16 @@ stages:
crossRootFs: '/crossrootfs/x64'
targetOS: browser
targetArchitecture: wasm
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: iOSSimulator_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: iossimulator
targetArchitecture: arm64
- runTests: false
### Additional jobs for full build ###
- ${{ if in(parameters.scope, 'full') }}:
@@ -448,45 +451,46 @@ stages:
buildName: Android_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.androidCrossContainerName }}
image: ${{ variables.androidCrossContainerImage }}
targetOS: android
targetArchitecture: arm
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: Android_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.androidCrossContainerName }}
image: ${{ variables.androidCrossContainerImage }}
targetOS: android
targetArchitecture: x64
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: Android_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.androidCrossContainerName }}
image: ${{ variables.androidCrossContainerImage }}
targetOS: android
targetArchitecture: x86
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: Browser_Multithreaded_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.browserCrossContainerName }}
@@ -495,13 +499,13 @@ stages:
targetOS: browser
targetArchitecture: wasm
extraProperties: /p:DotNetBuildRuntimeWasmEnableThreads=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: LinuxBionic_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.linuxBionicCrossContainerName }}
@@ -509,28 +513,13 @@ stages:
crossRootFs: '/crossrootfs/x64'
targetOS: linux-bionic
targetArchitecture: arm64
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
- buildName: LinuxBionic_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux_Shortstack }}
- container:
- name: ${{ variables.linuxBionicCrossContainerName }}
- image: ${{ variables.linuxBionicCrossContainerImage }}
- crossRootFs: '/crossrootfs/x64'
- targetOS: linux-bionic
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: LinuxBionic_NativeAOT_Shortstack
+ buildName: LinuxBionic_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.linuxBionicCrossContainerName }}
@@ -538,14 +527,13 @@ stages:
crossRootFs: '/crossrootfs/x64'
targetOS: linux-bionic
targetArchitecture: arm
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: LinuxBionic_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.linuxBionicCrossContainerName }}
@@ -553,186 +541,83 @@ stages:
crossRootFs: '/crossrootfs/x64'
targetOS: linux-bionic
targetArchitecture: x64
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: LinuxBionic_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux_Shortstack }}
- container:
- name: ${{ variables.linuxBionicCrossContainerName }}
- image: ${{ variables.linuxBionicCrossContainerImage }}
- crossRootFs: '/crossrootfs/x64'
- targetOS: linux-bionic
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: iOS_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: ios
targetArchitecture: arm64
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: iOS_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: ios
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: iOSSimulator_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: iossimulator
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: iOSSimulator_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: iossimulator
targetArchitecture: x64
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: iOSSimulator_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: iossimulator
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: MacCatalyst_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: maccatalyst
targetArchitecture: arm64
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: MacCatalyst_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: maccatalyst
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: MacCatalyst_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: maccatalyst
targetArchitecture: x64
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: MacCatalyst_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: maccatalyst
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: tvOS_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: tvos
targetArchitecture: arm64
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: tvOS_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: tvos
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: tvOSSimulator_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: tvossimulator
targetArchitecture: arm64
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: tvOSSimulator_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: tvossimulator
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: tvOSSimulator_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: tvossimulator
targetArchitecture: x64
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: tvOSSimulator_NativeAOT_Shortstack
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: tvossimulator
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: Wasi_Shortstack
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.wasiCrossContainerName }}
@@ -740,32 +625,23 @@ stages:
crossRootFs: '/crossrootfs/x64'
targetOS: wasi
targetArchitecture: wasm
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: OSX
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: osx
targetArchitecture: x64
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: OSX_Mono
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- useMonoRuntime: true
- targetOS: osx
- targetArchitecture: x64
-
- template: ../jobs/vmr-build.yml
parameters:
buildName: AzureLinux_x64_Cross
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux }}
container:
name: ${{ variables.azurelinuxX64CrossContainerName }}
@@ -779,37 +655,22 @@ stages:
buildName: AzureLinux_x64_Cross_Pgo
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxX64CrossContainerName }}
- image: ${{ variables.azurelinuxX64CrossContainerImage }}
- crossRootFs: '/crossrootfs/x64'
- targetOS: linux
- targetArchitecture: x64
- extraProperties: /p:PgoInstrument=true
- runTests: false
sign: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_Mono
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
pool: ${{ parameters.pool_Linux }}
container:
name: ${{ variables.azurelinuxX64CrossContainerName }}
image: ${{ variables.azurelinuxX64CrossContainerImage }}
crossRootFs: '/crossrootfs/x64'
- useMonoRuntime: true
targetOS: linux
targetArchitecture: x64
- runTests: false # scenario-tests time out: https://github.com/dotnet/source-build/issues/4775
+ extraProperties: /p:PgoInstrument=true
- template: ../jobs/vmr-build.yml
parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_Mono_LLVMAOT
+ buildName: AzureLinux_x64_Cross_Shortstack_Mono_LLVMAOT
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.azurelinuxX64CrossContainerName }}
@@ -819,13 +680,13 @@ stages:
targetOS: linux
targetArchitecture: x64
extraProperties: /p:DotNetBuildMonoEnableLLVM=true /p:DotNetBuildMonoAOTEnableLLVM=true /p:DotNetBuildMonoBundleLLVMOptimizer=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: AzureLinux_x64_Cross
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux }}
container:
name: ${{ variables.azurelinuxArmCrossContainerName }}
@@ -834,40 +695,12 @@ stages:
targetOS: linux
targetArchitecture: arm
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_Mono
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxArmCrossContainerName }}
- image: ${{ variables.azurelinuxArmCrossContainerImage }}
- crossRootFs: '/crossrootfs/arm'
- useMonoRuntime: true
- targetOS: linux
- targetArchitecture: arm
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxArmCrossContainerName }}
- image: ${{ variables.azurelinuxArmCrossContainerImage }}
- crossRootFs: '/crossrootfs/arm'
- targetOS: linux
- targetArchitecture: arm
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- template: ../jobs/vmr-build.yml
parameters:
buildName: AzureLinux_x64_Cross
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux }}
container:
name: ${{ variables.azurelinuxArm64CrossContainerName }}
@@ -881,56 +714,27 @@ stages:
buildName: AzureLinux_x64_Cross_Pgo
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxArm64CrossContainerName }}
- image: ${{ variables.azurelinuxArm64CrossContainerImage }}
- crossRootFs: '/crossrootfs/arm64'
- targetOS: linux
- targetArchitecture: arm64
- extraProperties: /p:PgoInstrument=true
- runTests: false
sign: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_Mono
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
pool: ${{ parameters.pool_Linux }}
container:
name: ${{ variables.azurelinuxArm64CrossContainerName }}
image: ${{ variables.azurelinuxArm64CrossContainerImage }}
crossRootFs: '/crossrootfs/arm64'
- useMonoRuntime: true
targetOS: linux
targetArchitecture: arm64
+ extraProperties: /p:PgoInstrument=true
- template: ../jobs/vmr-build.yml
parameters:
buildName: AzureLinux_x64_Cross_Alpine
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux }}
container:
- name: ${{ variables.azurelinuxX64AlpineCrossContainerName }}
- image: ${{ variables.azurelinuxX64AlpineCrossContainerImage }}
- crossRootFs: '/crossrootfs/x64'
- targetOS: linux-musl
- targetArchitecture: x64
- targetRid: ${{ variables.linuxMuslX64Rid}}
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_Alpine_Mono
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxX64AlpineCrossContainerName }}
- image: ${{ variables.azurelinuxX64AlpineCrossContainerImage }}
+ name: ${{ variables.azurelinuxX64MuslCrossContainerName }}
+ image: ${{ variables.azurelinuxX64MuslCrossContainerImage }}
crossRootFs: '/crossrootfs/x64'
- useMonoRuntime: true
targetOS: linux-musl
targetArchitecture: x64
targetRid: ${{ variables.linuxMuslX64Rid }}
@@ -940,10 +744,11 @@ stages:
buildName: AzureLinux_x64_Cross_Alpine
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux }}
container:
- name: ${{ variables.azurelinuxArmAlpineCrossContainerName }}
- image: ${{ variables.azurelinuxArmAlpineCrossContainerImage }}
+ name: ${{ variables.azurelinuxArmMuslCrossContainerName }}
+ image: ${{ variables.azurelinuxArmMuslCrossContainerImage }}
crossRootFs: '/crossrootfs/arm'
targetOS: linux-musl
targetArchitecture: arm
@@ -954,10 +759,11 @@ stages:
buildName: AzureLinux_x64_Cross_Alpine
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux }}
container:
- name: ${{ variables.azurelinuxArm64AlpineCrossContainerName }}
- image: ${{ variables.azurelinuxArm64AlpineCrossContainerImage }}
+ name: ${{ variables.azurelinuxArm64MuslCrossContainerName }}
+ image: ${{ variables.azurelinuxArm64MuslCrossContainerImage }}
crossRootFs: '/crossrootfs/arm64'
targetOS: linux-musl
targetArchitecture: arm64
@@ -965,134 +771,10 @@ stages:
- template: ../jobs/vmr-build.yml
parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_Alpine_Mono_CrossAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxX64AlpineCrossContainerName }}
- image: ${{ variables.azurelinuxX64AlpineCrossContainerImage }}
- crossRootFs: '/crossrootfs/x64'
- targetOS: linux-musl
- targetArchitecture: x64
- targetRid: ${{ variables.linuxMuslX64Rid }}
- extraProperties: /p:DotNetBuildMonoCrossAOT=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_Alpine_Mono_CrossAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxArm64AlpineCrossContainerName }}
- image: ${{ variables.azurelinuxArm64AlpineCrossContainerImage }}
- crossRootFs: '/crossrootfs/arm64'
- targetOS: linux-musl
- targetArchitecture: arm64
- targetRid: ${{ variables.linuxMuslArm64Rid }}
- extraProperties: /p:DotNetBuildMonoCrossAOT=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_Alpine_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxX64AlpineCrossContainerName }}
- image: ${{ variables.azurelinuxX64AlpineCrossContainerImage }}
- crossRootFs: '/crossrootfs/x64'
- targetOS: linux-musl
- targetArchitecture: x64
- targetRid: ${{ variables.linuxMuslX64Rid }}
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_Alpine_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxArmAlpineCrossContainerName }}
- image: ${{ variables.azurelinuxArmAlpineCrossContainerImage }}
- crossRootFs: '/crossrootfs/arm'
- targetOS: linux-musl
- targetArchitecture: arm
- targetRid: ${{ variables.linuxMuslArmRid }}
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_Alpine_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux }}
- container:
- name: ${{ variables.azurelinuxArm64AlpineCrossContainerName }}
- image: ${{ variables.azurelinuxArm64AlpineCrossContainerImage }}
- crossRootFs: '/crossrootfs/arm64'
- targetOS: linux-musl
- targetArchitecture: arm64
- targetRid: ${{ variables.linuxMuslArm64Rid }}
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_Mono_CrossAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux_Shortstack }}
- container:
- name: ${{ variables.azurelinuxX64CrossContainerName }}
- image: ${{ variables.azurelinuxX64CrossContainerImage }}
- crossRootFs: '/crossrootfs/x64'
- targetOS: linux
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildMonoCrossAOT=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_ShortStack_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux_Shortstack }}
- container:
- name: ${{ variables.azurelinuxX64CrossContainerName }}
- image: ${{ variables.azurelinuxX64CrossContainerImage }}
- crossRootFs: '/crossrootfs/x64'
- targetOS: linux
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_Mono_CrossAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux_Shortstack }}
- container:
- name: ${{ variables.azurelinuxArm64CrossContainerName }}
- image: ${{ variables.azurelinuxArm64CrossContainerImage }}
- crossRootFs: '/crossrootfs/arm64'
- targetOS: linux
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildMonoCrossAOT=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_Cross_ShortStack_Mono_LLVMAOT
+ buildName: AzureLinux_x64_Cross_Shortstack_Mono_LLVMAOT
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Linux_Shortstack }}
container:
name: ${{ variables.azurelinuxArm64CrossContainerName }}
@@ -1102,214 +784,59 @@ stages:
targetOS: linux
targetArchitecture: arm64
extraProperties: /p:DotNetBuildMonoEnableLLVM=true /p:DotNetBuildMonoAOTEnableLLVM=true /p:DotNetBuildMonoBundleLLVMOptimizer=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: AzureLinux_x64_ShortStack_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Linux_Shortstack }}
- container:
- name: ${{ variables.azurelinuxArm64CrossContainerName }}
- image: ${{ variables.azurelinuxArm64CrossContainerImage }}
- crossRootFs: '/crossrootfs/arm64'
- targetOS: linux
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: OSX
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Mac }}
targetOS: osx
targetArchitecture: arm64
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: OSX_Mono
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- useMonoRuntime: true
- targetOS: osx
- targetArchitecture: arm64
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: OSX_ShortStack_Mono_CrossAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: osx
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildMonoCrossAOT=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: OSX_ShortStack_Mono_CrossAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: osx
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildMonoCrossAOT=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: OSX_ShortStack_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: osx
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: OSX_ShortStack_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Mac }}
- targetOS: osx
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- template: ../jobs/vmr-build.yml
parameters:
buildName: Windows
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Windows }}
targetOS: windows
targetArchitecture: arm64
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: Windows_ShortStack_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Windows }}
- targetOS: windows
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: Windows_Mono
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Windows }}
- useMonoRuntime: true
- targetOS: windows
- targetArchitecture: x64
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: Windows_ShortStack_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Windows }}
- targetOS: windows
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: Windows_ShortStack_Mono_CrossAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Windows }}
- targetOS: windows
- targetArchitecture: x64
- extraProperties: /p:DotNetBuildMonoCrossAOT=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: Windows_ShortStack_Mono_CrossAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Windows }}
- targetOS: windows
- targetArchitecture: arm64
- extraProperties: /p:DotNetBuildMonoCrossAOT=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: Windows
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Windows }}
- targetOS: windows
- targetArchitecture: x86
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: Windows_ShortStack_NativeAOT
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Windows }}
- targetOS: windows
- targetArchitecture: x86
- extraProperties: /p:DotNetBuildRuntimeNativeAOTRuntimePack=true
- runTests: false
-
- - template: ../jobs/vmr-build.yml
- parameters:
- buildName: Windows_Mono
- isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
- vmrBranch: ${{ variables.VmrBranch }}
- pool: ${{ parameters.pool_Windows }}
- useMonoRuntime: true
- targetOS: windows
- targetArchitecture: x86
-
- template: ../jobs/vmr-build.yml
parameters:
buildName: Windows_Pgo
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: false
pool: ${{ parameters.pool_Windows }}
targetOS: windows
targetArchitecture: x64
extraProperties: /p:PgoInstrument=true
- runTests: false
- sign: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: Windows_Pgo
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: false
pool: ${{ parameters.pool_Windows }}
targetOS: windows
targetArchitecture: x86
extraProperties: /p:PgoInstrument=true
- runTests: false
- sign: false
- template: ../jobs/vmr-build.yml
parameters:
buildName: Windows_Pgo
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: false
pool: ${{ parameters.pool_Windows }}
targetOS: windows
targetArchitecture: arm64
extraProperties: /p:PgoInstrument=true
- runTests: false
- sign: false
# Build Pass 2 verticals
@@ -1318,10 +845,10 @@ stages:
buildName: Windows
isBuiltFromVmr: ${{ parameters.isBuiltFromVmr }}
vmrBranch: ${{ variables.VmrBranch }}
+ sign: ${{ variables.signEnabled }}
pool: ${{ parameters.pool_Windows }}
targetOS: windows
targetArchitecture: x64
- runTests: false
buildPass: 2
reuseBuildArtifactsFrom:
- Windows_x64
@@ -1366,3 +893,18 @@ stages:
dotNetBuildPass: final
primaryDependentJob: Windows_x64
outputFolder: $(Build.ArtifactStagingDirectory)/artifacts
+
+### VALIDATION ###
+- ${{ if and(parameters.isBuiltFromVmr, not(parameters.isSourceOnlyBuild), eq(variables['System.TeamProject'], 'internal'), eq(variables.signEnabled, 'true')) }}:
+ - stage: VMR_Validation
+ displayName: VMR Validation
+ dependsOn: VMR_Final_Join
+ jobs:
+ - job: ValidateSigning
+ displayName: Validate Signing
+ pool: ${{ parameters.pool_Windows }}
+ timeoutInMinutes: 240
+ steps:
+ - template: ../steps/vmr-validate-signing.yml
+ parameters:
+ continueOnError: true
diff --git a/eng/pipelines/templates/steps/vmr-validate-signing.yml b/eng/pipelines/templates/steps/vmr-validate-signing.yml
new file mode 100644
index 000000000000..cee51d040259
--- /dev/null
+++ b/eng/pipelines/templates/steps/vmr-validate-signing.yml
@@ -0,0 +1,26 @@
+parameters:
+- name: continueOnError
+ type: boolean
+ default: false
+
+steps:
+- task: DownloadBuildArtifacts@1
+ inputs:
+ artifactName: 'PackageArtifacts'
+ downloadPath: $(Build.ArtifactStagingDirectory)
+ checkDownloadedFiles: true
+ displayName: Download Package Artifacts
+
+# This is necessary whenever we want to publish/restore to an AzDO private feed
+# Since sdk-task.ps1 tries to restore packages we need to do this authentication here
+# otherwise it'll complain about accessing a private feed.
+- task: NuGetAuthenticate@1
+ displayName: 'Authenticate to AzDO Feeds'
+
+- task: PowerShell@2
+ inputs:
+ filePath: eng\common\sdk-task.ps1
+ arguments: -task SigningValidation -restore -msbuildEngine vs
+ /p:PackageBasePath='$(Build.ArtifactStagingDirectory)/PackageArtifacts'
+ displayName: Validate Signing of Packages
+ continueOnError: ${{ parameters.continueOnError }}
diff --git a/eng/pipelines/templates/variables/vmr-build.yml b/eng/pipelines/templates/variables/vmr-build.yml
index 935c01099a5b..f49db6b7e54f 100644
--- a/eng/pipelines/templates/variables/vmr-build.yml
+++ b/eng/pipelines/templates/variables/vmr-build.yml
@@ -4,6 +4,11 @@ parameters:
type: string
default: ''
+- name: desiredSigning
+ displayName: Signed/unsigned/default signing parameters
+ type: string
+ default: ''
+
variables:
- ${{ if ne(parameters.vmrBranch, '') }}:
- name: VmrBranch
@@ -18,6 +23,42 @@ variables:
- name: defaultContainerOptions
value: --privileged
+- name: isOfficialBuild
+ value: ${{ and(eq(variables['System.TeamProject'], 'internal'), notin(variables['Build.Reason'], 'PullRequest')) }}
+
+- name: officialBuildParameter
+ ${{ if eq(variables['isOfficialBuild'], true) }}:
+ value: /p:OfficialBuildId=$(Build.BuildNumber)
+ ${{ else }}:
+ value: ''
+
+- ${{ if eq(parameters.desiredSigning, 'Signed') }}:
+ - name: signEnabled
+ value: true
+- ${{ if eq(parameters.desiredSigning, 'Unsigned') }}:
+ - name: signEnabled
+ value: false
+- ${{ if or(startsWith(parameters.desiredSigning, 'Default'), eq(parameters.desiredSigning, '')) }}:
+ # Always sign on the public project (will be dry-run signing)
+ - ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ - name: signEnabled
+ value: true
+ # Always sign in pull requests (dry run)
+ - ${{ elseif eq(variables['Build.Reason'], 'PullRequest') }}:
+ - name: signEnabled
+ value: true
+ # Pass sign on the internal project if the, branch is a main or release branch
+ - ${{ elseif and(eq(variables['System.TeamProject'], 'internal'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), and(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), contains(variables['Build.SourceBranch'], '-preview')))) }}:
+ - name: signEnabled
+ value: true
+ # Do not sign otherwise.
+ - ${{ else }}:
+ - name: signEnabled
+ value: false
+- ${{ else }}:
+ - name: signEnabled
+ value: false
+
- name: almaLinuxContainerName
value: almaLinuxContainer
- name: almaLinuxContainerImage
@@ -51,52 +92,52 @@ variables:
- name: azurelinuxX64CrossContainerName
value: azurelinuxX64CrossContainer
- name: azurelinuxX64CrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-cross-amd64
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-amd64
- name: azurelinuxArmCrossContainerName
value: azurelinuxArmCrossContainer
- name: azurelinuxArmCrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-cross-arm
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-arm
- name: azurelinuxArm64CrossContainerName
value: azurelinuxArm64CrossContainer
- name: azurelinuxArm64CrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-cross-arm64
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-arm64
-- name: azurelinuxX64AlpineCrossContainerName
- value: azurelinuxX64AlpineCrossContainer
-- name: azurelinuxX64AlpineCrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-cross-amd64-alpine
+- name: azurelinuxX64MuslCrossContainerName
+ value: azurelinuxX64MuslCrossContainer
+- name: azurelinuxX64MuslCrossContainerImage
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-amd64-musl
-- name: azurelinuxArmAlpineCrossContainerName
- value: azurelinuxArmAlpineCrossContainer
-- name: azurelinuxArmAlpineCrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-cross-arm-alpine
+- name: azurelinuxArmMuslCrossContainerName
+ value: azurelinuxArmMuslCrossContainer
+- name: azurelinuxArmMuslCrossContainerImage
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-arm-musl
-- name: azurelinuxArm64AlpineCrossContainerName
- value: azurelinuxArm64AlpineCrossContainer
-- name: azurelinuxArm64AlpineCrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-cross-arm64-alpine
+- name: azurelinuxArm64MuslCrossContainerName
+ value: azurelinuxArm64MuslCrossContainer
+- name: azurelinuxArm64MuslCrossContainerImage
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-arm64-musl
- name: androidCrossContainerName
value: androidCrossContainer
- name: androidCrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-cross-android-amd64
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-android-amd64
- name: linuxBionicCrossContainerName
value: linuxBionicCrossContainer
- name: linuxBionicCrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-android-openssl
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-cross-android-openssl-amd64
- name: browserCrossContainerName
value: browserCrossContainer
- name: browserCrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-webassembly-amd64
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-webassembly-amd64
- name: wasiCrossContainerName
value: wasiCrossContainer
- name: wasiCrossContainerImage
- value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net9.0-webassembly-amd64
+ value: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux-3.0-net10.0-webassembly-amd64
- name: almaLinuxName
value: AlmaLinux8
diff --git a/eng/pipelines/vmr-build-pr.yml b/eng/pipelines/vmr-build-pr.yml
index 20352fae76fa..9abe336f5371 100644
--- a/eng/pipelines/vmr-build-pr.yml
+++ b/eng/pipelines/vmr-build-pr.yml
@@ -39,6 +39,13 @@ parameters:
variables:
- template: /eng/common/templates/variables/pool-providers.yml@self
+- ${{ if eq(variables['System.TeamProject'], 'public') }}:
+ - name: skipComponentGovernanceDetection # we run CG on internal builds only
+ value: true
+
+ - name: Codeql.Enabled # we run CodeQL on internal builds only
+ value: false
+
- ${{ if ne(parameters.vmrBranch, ' ') }}:
- name: VmrBranch
value: ${{ replace(parameters.vmrBranch, ' ', '') }}
diff --git a/github-merge-flow.jsonc b/github-merge-flow.jsonc
index f14a2eb445f1..fe686d5ff56b 100644
--- a/github-merge-flow.jsonc
+++ b/github-merge-flow.jsonc
@@ -21,8 +21,13 @@
"MergeToBranch": "release/9.0.2xx",
"ExtraSwitches": "-QuietComments"
},
- // Automate opening PRs to merge sdk repos from release/9.0.2xx to main
+ // Automate opening PRs to merge sdk repos from release/9.0.2xx to release/9.0.3xx
"release/9.0.2xx":{
+ "MergeToBranch": "release/9.0.3xx",
+ "ExtraSwitches": "-QuietComments"
+ },
+ // Automate opening PRs to merge sdk repos from release/9.0.3xx to main
+ "release/9.0.3xx":{
"MergeToBranch": "main",
"ExtraSwitches": "-QuietComments"
}
diff --git a/global.json b/global.json
index 7d56494ca0cf..069338eb4678 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"tools": {
- "dotnet": "10.0.100-alpha.1.24573.1",
+ "dotnet": "10.0.100-alpha.1.25077.2",
"runtimes": {
"dotnet": [
"$(VSRedistCommonNetCoreSharedFrameworkx64100PackageVersion)"
@@ -17,8 +17,8 @@
"cmake": "latest"
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25061.1",
- "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25061.1",
+ "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.25080.7",
+ "Microsoft.DotNet.Helix.Sdk": "10.0.0-beta.25080.7",
"Microsoft.Build.NoTargets": "3.7.0",
"Microsoft.DotNet.CMake.Sdk": "9.0.0-beta.24217.1"
}
diff --git a/sdk.sln b/sdk.sln
index 2480a90d380b..93cacf6e7274 100644
--- a/sdk.sln
+++ b/sdk.sln
@@ -73,8 +73,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cli", "Cli", "{9CEF03F6-1A6
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Cli.Utils", "src\Cli\Microsoft.DotNet.Cli.Utils\Microsoft.DotNet.Cli.Utils.csproj", "{CC25E192-70AD-4D91-B288-3AD40065DBAC}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Cli.Sln.Internal", "src\Cli\Microsoft.DotNet.Cli.Sln.Internal\Microsoft.DotNet.Cli.Sln.Internal.csproj", "{2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.InternalAbstractions", "src\Cli\Microsoft.DotNet.InternalAbstractions\Microsoft.DotNet.InternalAbstractions.csproj", "{0FA6CEE9-7A97-4036-8D48-39BA4F85118E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Configurer", "src\Cli\Microsoft.DotNet.Configurer\Microsoft.DotNet.Configurer.csproj", "{B22C90C9-BA46-4F07-9613-253DD01E3EFF}"
@@ -109,8 +107,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ArgumentForwarding.Tests",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EndToEnd.Tests", "test\EndToEnd.Tests\EndToEnd.Tests.csproj", "{A0EFB2CB-517F-4746-902E-D0EBEFFBD89B}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Cli.Sln.Internal.Tests", "test\Microsoft.DotNet.Cli.Sln.Internal.Tests\Microsoft.DotNet.Cli.Sln.Internal.Tests.csproj", "{82D910C3-A5E2-41F0-A142-F4F2D770CB59}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.Cli.Utils.Tests", "test\Microsoft.DotNet.Cli.Utils.Tests\Microsoft.DotNet.Cli.Utils.Tests.csproj", "{C34FB893-2320-41A3-9D38-0061C22A6464}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.DotNet.MSBuildSdkResolver.Tests", "test\Microsoft.DotNet.MSBuildSdkResolver.Tests\Microsoft.DotNet.MSBuildSdkResolver.Tests.csproj", "{0AE2AB83-1F0D-4E04-9C8D-5C35CE97FDDC}"
@@ -517,6 +513,10 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.DotNet.HotReload.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.HotReload.Agent.Package", "src\BuiltInTools\HotReloadAgent\Microsoft.DotNet.HotReload.Agent.Package.csproj", "{2FF79F82-60C1-349A-4726-7783D5A6D5DF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.DotNet.HotReload.Agent.PipeRpc.Package", "src\BuiltInTools\HotReloadAgent.PipeRpc\Microsoft.DotNet.HotReload.Agent.PipeRpc.Package.csproj", "{692B71D8-9C31-D1EE-6C1B-570A12B18E39}"
+EndProject
+Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Microsoft.DotNet.HotReload.Agent.PipeRpc", "src\BuiltInTools\HotReloadAgent.PipeRpc\Microsoft.DotNet.HotReload.Agent.PipeRpc.shproj", "{FA3C7F91-42A2-45AD-897C-F646B081016C}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -579,10 +579,6 @@ Global
{CC25E192-70AD-4D91-B288-3AD40065DBAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC25E192-70AD-4D91-B288-3AD40065DBAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC25E192-70AD-4D91-B288-3AD40065DBAC}.Release|Any CPU.Build.0 = Release|Any CPU
- {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B}.Release|Any CPU.Build.0 = Release|Any CPU
{0FA6CEE9-7A97-4036-8D48-39BA4F85118E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0FA6CEE9-7A97-4036-8D48-39BA4F85118E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0FA6CEE9-7A97-4036-8D48-39BA4F85118E}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -647,10 +643,6 @@ Global
{A0EFB2CB-517F-4746-902E-D0EBEFFBD89B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0EFB2CB-517F-4746-902E-D0EBEFFBD89B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0EFB2CB-517F-4746-902E-D0EBEFFBD89B}.Release|Any CPU.Build.0 = Release|Any CPU
- {82D910C3-A5E2-41F0-A142-F4F2D770CB59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {82D910C3-A5E2-41F0-A142-F4F2D770CB59}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {82D910C3-A5E2-41F0-A142-F4F2D770CB59}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {82D910C3-A5E2-41F0-A142-F4F2D770CB59}.Release|Any CPU.Build.0 = Release|Any CPU
{C34FB893-2320-41A3-9D38-0061C22A6464}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C34FB893-2320-41A3-9D38-0061C22A6464}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C34FB893-2320-41A3-9D38-0061C22A6464}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -979,6 +971,10 @@ Global
{2FF79F82-60C1-349A-4726-7783D5A6D5DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2FF79F82-60C1-349A-4726-7783D5A6D5DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2FF79F82-60C1-349A-4726-7783D5A6D5DF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {692B71D8-9C31-D1EE-6C1B-570A12B18E39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {692B71D8-9C31-D1EE-6C1B-570A12B18E39}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {692B71D8-9C31-D1EE-6C1B-570A12B18E39}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {692B71D8-9C31-D1EE-6C1B-570A12B18E39}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1002,7 +998,6 @@ Global
{E740A596-2CAE-476C-8062-49705C3A9CF0} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{9CEF03F6-1A61-431A-8ABD-9095974629AE} = {22AB674F-ED91-4FBC-BFEE-8A1E82F9F05E}
{CC25E192-70AD-4D91-B288-3AD40065DBAC} = {9CEF03F6-1A61-431A-8ABD-9095974629AE}
- {2119E6E5-2B75-402A-AFF3-3E7E7B671B9B} = {9CEF03F6-1A61-431A-8ABD-9095974629AE}
{0FA6CEE9-7A97-4036-8D48-39BA4F85118E} = {9CEF03F6-1A61-431A-8ABD-9095974629AE}
{B22C90C9-BA46-4F07-9613-253DD01E3EFF} = {9CEF03F6-1A61-431A-8ABD-9095974629AE}
{D62591F3-ED86-4014-B765-7CDA47F55EE2} = {9CEF03F6-1A61-431A-8ABD-9095974629AE}
@@ -1020,7 +1015,6 @@ Global
{587A6639-FA2A-498C-8FA2-F77925DB732B} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{2D8AA9EA-3013-47DE-B2DD-E074F67467ED} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{A0EFB2CB-517F-4746-902E-D0EBEFFBD89B} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
- {82D910C3-A5E2-41F0-A142-F4F2D770CB59} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{C34FB893-2320-41A3-9D38-0061C22A6464} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{0AE2AB83-1F0D-4E04-9C8D-5C35CE97FDDC} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{3D20D19D-74FB-4A43-B78C-B0AF90A696A0} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
@@ -1159,6 +1153,8 @@ Global
{8D6A9984-118D-4415-A8FA-AB1F26CF5C44} = {3FA6F1CB-295B-4414-B18F-93845917A8CD}
{418B10BD-CA42-49F3-8F4A-D8CC90C8A17D} = {71A9F549-0EB6-41F9-BC16-4A6C5007FC91}
{2FF79F82-60C1-349A-4726-7783D5A6D5DF} = {71A9F549-0EB6-41F9-BC16-4A6C5007FC91}
+ {692B71D8-9C31-D1EE-6C1B-570A12B18E39} = {71A9F549-0EB6-41F9-BC16-4A6C5007FC91}
+ {FA3C7F91-42A2-45AD-897C-F646B081016C} = {71A9F549-0EB6-41F9-BC16-4A6C5007FC91}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FB8F26CE-4DE6-433F-B32A-79183020BBD6}
@@ -1166,12 +1162,15 @@ Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompat.Shared\Microsoft.DotNet.ApiCompat.Shared.projitems*{03c5a84a-982b-4f38-ac73-ab832c645c4a}*SharedItemsImports = 5
src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompat.Shared\Microsoft.DotNet.ApiCompat.Shared.projitems*{0a3c9afd-f6e6-4a5d-83fb-93bf66732696}*SharedItemsImports = 5
+ src\BuiltInTools\HotReloadAgent.PipeRpc\Microsoft.DotNet.HotReload.Agent.PipeRpc.projitems*{1bbfa19c-03f0-4d27-9d0d-0f8172642107}*SharedItemsImports = 5
src\BuiltInTools\HotReloadAgent\Microsoft.DotNet.HotReload.Agent.projitems*{1bbfa19c-03f0-4d27-9d0d-0f8172642107}*SharedItemsImports = 5
src\BuiltInTools\AspireService\Microsoft.WebTools.AspireService.projitems*{1f0b4b3c-dc88-4740-b04f-1707102e9930}*SharedItemsImports = 5
src\BuiltInTools\HotReloadAgent\Microsoft.DotNet.HotReload.Agent.projitems*{418b10bd-ca42-49f3-8f4a-d8cc90c8a17d}*SharedItemsImports = 13
src\BuiltInTools\AspireService\Microsoft.WebTools.AspireService.projitems*{445efbd5-6730-4f09-943d-278e77501ffd}*SharedItemsImports = 5
+ src\BuiltInTools\HotReloadAgent.PipeRpc\Microsoft.DotNet.HotReload.Agent.PipeRpc.projitems*{445efbd5-6730-4f09-943d-278e77501ffd}*SharedItemsImports = 5
src\BuiltInTools\AspireService\Microsoft.WebTools.AspireService.projitems*{94c8526e-dcc2-442f-9868-3dd0ba2688be}*SharedItemsImports = 13
src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompat.Shared\Microsoft.DotNet.ApiCompat.Shared.projitems*{9d36039f-d0a1-462f-85b4-81763c6b02cb}*SharedItemsImports = 13
src\Compatibility\ApiCompat\Microsoft.DotNet.ApiCompat.Shared\Microsoft.DotNet.ApiCompat.Shared.projitems*{a9103b98-d888-4260-8a05-fa36f640698a}*SharedItemsImports = 5
+ src\BuiltInTools\HotReloadAgent.PipeRpc\Microsoft.DotNet.HotReload.Agent.PipeRpc.projitems*{fa3c7f91-42a2-45ad-897c-f646b081016c}*SharedItemsImports = 13
EndGlobalSection
EndGlobal
diff --git a/source-build.slnf b/source-build.slnf
index 8ee120cc0e10..ff429db4dbae 100644
--- a/source-build.slnf
+++ b/source-build.slnf
@@ -8,7 +8,6 @@
"src\\BuiltInTools\\DotNetDeltaApplier\\Microsoft.Extensions.DotNetDeltaApplier.csproj",
"src\\BuiltInTools\\DotNetWatchTasks\\DotNetWatchTasks.csproj",
"src\\BuiltInTools\\dotnet-watch\\dotnet-watch.csproj",
- "src\\Cli\\Microsoft.DotNet.Cli.Sln.Internal\\Microsoft.DotNet.Cli.Sln.Internal.csproj",
"src\\Cli\\Microsoft.DotNet.Cli.Utils\\Microsoft.DotNet.Cli.Utils.csproj",
"src\\Cli\\Microsoft.DotNet.Configurer\\Microsoft.DotNet.Configurer.csproj",
"src\\Cli\\Microsoft.DotNet.InternalAbstractions\\Microsoft.DotNet.InternalAbstractions.csproj",
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 ffd2cb044182..46e2ef8cd2d1 100644
--- a/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets
+++ b/src/BlazorWasmSdk/Targets/Microsoft.NET.Sdk.BlazorWebAssembly.6_0.targets
@@ -44,6 +44,7 @@ Copyright (c) .NET Foundation. All rights reserved.
false
false
true
+ true
false
false
false
diff --git a/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj b/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj
index 12de0ae0c4fa..a663b6440dc0 100644
--- a/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj
+++ b/src/BuiltInTools/DotNetDeltaApplier/Microsoft.Extensions.DotNetDeltaApplier.csproj
@@ -1,5 +1,6 @@
+
+ netstandard2.0
+ false
+ none
+ false
+ enable
+ preview
+
+
+ true
+ true
+ Microsoft.DotNet.HotReload.Agent.PipeRpc
+ false
+
+ Package containing sources of Hot Reload agent pipe RPC.
+
+
+ $(NoWarn);NU5128
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.projitems b/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.projitems
new file mode 100644
index 000000000000..f1969e528c57
--- /dev/null
+++ b/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.projitems
@@ -0,0 +1,14 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ {FA3C7F91-42A2-45AD-897C-F646B081016C}
+
+
+ Microsoft.DotNet.HotReload
+
+
+
+
+
\ No newline at end of file
diff --git a/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.shproj b/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.shproj
new file mode 100644
index 000000000000..cf55004689ca
--- /dev/null
+++ b/src/BuiltInTools/HotReloadAgent.PipeRpc/Microsoft.DotNet.HotReload.Agent.PipeRpc.shproj
@@ -0,0 +1,13 @@
+
+
+
+ FA3C7F91-42A2-45AD-897C-F646B081016C
+ 14.0
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs b/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs
new file mode 100644
index 000000000000..62686ddc226c
--- /dev/null
+++ b/src/BuiltInTools/HotReloadAgent.PipeRpc/NamedPipeContract.cs
@@ -0,0 +1,190 @@
+// 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.Collections.Generic;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.DotNet.HotReload;
+
+internal interface IRequest
+{
+ RequestType Type { get; }
+ ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken);
+}
+
+internal interface IUpdateRequest : IRequest
+{
+}
+
+internal enum RequestType
+{
+ ManagedCodeUpdate = 1,
+ StaticAssetUpdate = 2,
+ InitialUpdatesCompleted = 3,
+}
+
+internal readonly struct ManagedCodeUpdateRequest(IReadOnlyList deltas, ResponseLoggingLevel responseLoggingLevel) : IUpdateRequest
+{
+ private const byte Version = 4;
+
+ public IReadOnlyList Deltas { get; } = deltas;
+ public ResponseLoggingLevel ResponseLoggingLevel { get; } = responseLoggingLevel;
+ public RequestType Type => RequestType.ManagedCodeUpdate;
+
+ public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ await stream.WriteAsync(Version, cancellationToken);
+ await stream.WriteAsync(Deltas.Count, cancellationToken);
+
+ foreach (var delta in Deltas)
+ {
+ await stream.WriteAsync(delta.ModuleId, cancellationToken);
+ await stream.WriteByteArrayAsync(delta.MetadataDelta, cancellationToken);
+ await stream.WriteByteArrayAsync(delta.ILDelta, cancellationToken);
+ await stream.WriteByteArrayAsync(delta.PdbDelta, cancellationToken);
+ await stream.WriteAsync(delta.UpdatedTypes, cancellationToken);
+ }
+
+ await stream.WriteAsync((byte)ResponseLoggingLevel, cancellationToken);
+ }
+
+ public static async ValueTask ReadAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ var version = await stream.ReadByteAsync(cancellationToken);
+ if (version != Version)
+ {
+ throw new NotSupportedException($"Unsupported version {version}.");
+ }
+
+ var count = await stream.ReadInt32Async(cancellationToken);
+
+ var deltas = new UpdateDelta[count];
+ for (var i = 0; i < count; i++)
+ {
+ var moduleId = await stream.ReadGuidAsync(cancellationToken);
+ var metadataDelta = await stream.ReadByteArrayAsync(cancellationToken);
+ var ilDelta = await stream.ReadByteArrayAsync(cancellationToken);
+ var pdbDelta = await stream.ReadByteArrayAsync(cancellationToken);
+ var updatedTypes = await stream.ReadIntArrayAsync(cancellationToken);
+
+ deltas[i] = new UpdateDelta(moduleId, metadataDelta: metadataDelta, ilDelta: ilDelta, pdbDelta: pdbDelta, updatedTypes);
+ }
+
+ var responseLoggingLevel = (ResponseLoggingLevel)await stream.ReadByteAsync(cancellationToken);
+ return new ManagedCodeUpdateRequest(deltas, responseLoggingLevel: responseLoggingLevel);
+ }
+}
+
+internal readonly struct UpdateResponse(IReadOnlyCollection<(string message, AgentMessageSeverity severity)> log, bool success)
+{
+ public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ await stream.WriteAsync(success, cancellationToken);
+ await stream.WriteAsync(log.Count, cancellationToken);
+
+ foreach (var (message, severity) in log)
+ {
+ await stream.WriteAsync(message, cancellationToken);
+ await stream.WriteAsync((byte)severity, cancellationToken);
+ }
+ }
+
+ public static async ValueTask<(bool success, IAsyncEnumerable<(string message, AgentMessageSeverity severity)>)> ReadAsync(
+ Stream stream, CancellationToken cancellationToken)
+ {
+ var success = await stream.ReadBooleanAsync(cancellationToken);
+ var log = ReadLogAsync(cancellationToken);
+ return (success, log);
+
+ async IAsyncEnumerable<(string message, AgentMessageSeverity severity)> ReadLogAsync([EnumeratorCancellation] CancellationToken cancellationToken)
+ {
+ var entryCount = await stream.ReadInt32Async(cancellationToken);
+
+ for (var i = 0; i < entryCount; i++)
+ {
+ var message = await stream.ReadStringAsync(cancellationToken);
+ var severity = (AgentMessageSeverity)await stream.ReadByteAsync(cancellationToken);
+ yield return (message, severity);
+ }
+ }
+ }
+}
+
+internal readonly struct ClientInitializationResponse(string capabilities)
+{
+ private const byte Version = 0;
+
+ public string Capabilities { get; } = capabilities;
+
+ public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ await stream.WriteAsync(Version, cancellationToken);
+ await stream.WriteAsync(Capabilities, cancellationToken);
+ }
+
+ public static async ValueTask ReadAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ var version = await stream.ReadByteAsync(cancellationToken);
+ if (version != Version)
+ {
+ throw new NotSupportedException($"Unsupported version {version}.");
+ }
+
+ var capabilities = await stream.ReadStringAsync(cancellationToken);
+ return new ClientInitializationResponse(capabilities);
+ }
+}
+
+internal readonly struct StaticAssetUpdateRequest(
+ string assemblyName,
+ string relativePath,
+ byte[] contents,
+ bool isApplicationProject,
+ ResponseLoggingLevel responseLoggingLevel) : IUpdateRequest
+{
+ private const byte Version = 2;
+
+ public string AssemblyName { get; } = assemblyName;
+ public bool IsApplicationProject { get; } = isApplicationProject;
+ public string RelativePath { get; } = relativePath;
+ public byte[] Contents { get; } = contents;
+ public ResponseLoggingLevel ResponseLoggingLevel { get; } = responseLoggingLevel;
+
+ public RequestType Type => RequestType.StaticAssetUpdate;
+
+ public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ await stream.WriteAsync(Version, cancellationToken);
+ await stream.WriteAsync(AssemblyName, cancellationToken);
+ await stream.WriteAsync(IsApplicationProject, cancellationToken);
+ await stream.WriteAsync(RelativePath, cancellationToken);
+ await stream.WriteByteArrayAsync(Contents, cancellationToken);
+ await stream.WriteAsync((byte)ResponseLoggingLevel, cancellationToken);
+ }
+
+ public static async ValueTask ReadAsync(Stream stream, CancellationToken cancellationToken)
+ {
+ var version = await stream.ReadByteAsync(cancellationToken);
+ if (version != Version)
+ {
+ throw new NotSupportedException($"Unsupported version {version}.");
+ }
+
+ var assemblyName = await stream.ReadStringAsync(cancellationToken);
+ var isApplicationProject = await stream.ReadBooleanAsync(cancellationToken);
+ var relativePath = await stream.ReadStringAsync(cancellationToken);
+ var contents = await stream.ReadByteArrayAsync(cancellationToken);
+ var responseLoggingLevel = (ResponseLoggingLevel)await stream.ReadByteAsync(cancellationToken);
+
+ return new StaticAssetUpdateRequest(
+ assemblyName: assemblyName,
+ relativePath: relativePath,
+ contents: contents,
+ isApplicationProject,
+ responseLoggingLevel);
+ }
+}
diff --git a/src/BuiltInTools/HotReloadAgent.PipeRpc/StreamExtensions.cs b/src/BuiltInTools/HotReloadAgent.PipeRpc/StreamExtensions.cs
new file mode 100644
index 000000000000..4169cae79e38
--- /dev/null
+++ b/src/BuiltInTools/HotReloadAgent.PipeRpc/StreamExtensions.cs
@@ -0,0 +1,274 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+
+using System;
+using System.Buffers;
+using System.Buffers.Binary;
+using System.IO;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.DotNet.HotReload;
+
+///
+/// Implements async read/write helpers that provide functionality of and .
+/// See https://github.com/dotnet/runtime/issues/17229
+///
+internal static class StreamExtesions
+{
+ public static ValueTask WriteAsync(this Stream stream, bool value, CancellationToken cancellationToken)
+ => WriteAsync(stream, (byte)(value ? 1 : 0), cancellationToken);
+
+ public static async ValueTask WriteAsync(this Stream stream, byte value, CancellationToken cancellationToken)
+ {
+ var size = sizeof(byte);
+ var buffer = ArrayPool.Shared.Rent(minimumLength: size);
+ try
+ {
+ buffer[0] = value;
+ await stream.WriteAsync(buffer, offset: 0, count: size, cancellationToken);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ }
+
+ public static async ValueTask WriteAsync(this Stream stream, int value, CancellationToken cancellationToken)
+ {
+ var size = sizeof(int);
+ var buffer = ArrayPool.Shared.Rent(minimumLength: size);
+ try
+ {
+ BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
+ await stream.WriteAsync(buffer, offset: 0, count: size, cancellationToken);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ }
+
+ public static ValueTask WriteAsync(this Stream stream, Guid value, CancellationToken cancellationToken)
+ => stream.WriteAsync(value.ToByteArray(), cancellationToken);
+
+ public static async ValueTask WriteByteArrayAsync(this Stream stream, byte[] value, CancellationToken cancellationToken)
+ {
+ await stream.WriteAsync(value.Length, cancellationToken);
+ await stream.WriteAsync(value, cancellationToken);
+ }
+
+ public static async ValueTask WriteAsync(this Stream stream, int[] value, CancellationToken cancellationToken)
+ {
+ var size = sizeof(int) * (value.Length + 1);
+ var buffer = ArrayPool.Shared.Rent(minimumLength: size);
+ try
+ {
+ BinaryPrimitives.WriteInt32LittleEndian(buffer, value.Length);
+ for (int i = 0; i < value.Length; i++)
+ {
+ BinaryPrimitives.WriteInt32LittleEndian(buffer.AsSpan((i + 1) * sizeof(int), sizeof(int)), value[i]);
+ }
+
+ await stream.WriteAsync(buffer, offset: 0, count: size, cancellationToken);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ }
+
+ public static async ValueTask WriteAsync(this Stream stream, string value, CancellationToken cancellationToken)
+ {
+ var bytes = Encoding.UTF8.GetBytes(value);
+ await stream.Write7BitEncodedIntAsync(bytes.Length, cancellationToken);
+ await stream.WriteAsync(bytes, cancellationToken);
+ }
+
+#if !NET
+ public static async ValueTask WriteAsync(this Stream stream, byte[] value, CancellationToken cancellationToken)
+ => await stream.WriteAsync(value, offset: 0, count: value.Length, cancellationToken);
+#endif
+ public static async ValueTask Write7BitEncodedIntAsync(this Stream stream, int value, CancellationToken cancellationToken)
+ {
+ uint uValue = (uint)value;
+
+ while (uValue > 0x7Fu)
+ {
+ await stream.WriteAsync((byte)(uValue | ~0x7Fu), cancellationToken);
+ uValue >>= 7;
+ }
+
+ await stream.WriteAsync((byte)uValue, cancellationToken);
+ }
+
+ public static async ValueTask ReadBooleanAsync(this Stream stream, CancellationToken cancellationToken)
+ => await stream.ReadByteAsync(cancellationToken) != 0;
+
+ public static async ValueTask ReadByteAsync(this Stream stream, CancellationToken cancellationToken)
+ {
+ int size = sizeof(byte);
+ var buffer = ArrayPool.Shared.Rent(minimumLength: size);
+ try
+ {
+ await ReadExactlyAsync(stream, buffer, size, cancellationToken);
+ return buffer[0];
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ }
+
+ public static async ValueTask ReadInt32Async(this Stream stream, CancellationToken cancellationToken)
+ {
+ int size = sizeof(int);
+ var buffer = ArrayPool.Shared.Rent(minimumLength: size);
+ try
+ {
+ await ReadExactlyAsync(stream, buffer, size, cancellationToken);
+ return BinaryPrimitives.ReadInt32LittleEndian(buffer);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ }
+
+ public static async ValueTask ReadGuidAsync(this Stream stream, CancellationToken cancellationToken)
+ {
+ const int size = 16;
+#if NET
+ var buffer = ArrayPool.Shared.Rent(minimumLength: size);
+
+ try
+ {
+ await ReadExactlyAsync(stream, buffer, size, cancellationToken);
+ return new Guid(buffer.AsSpan(0, size));
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+#else
+ var buffer = new byte[size];
+ await ReadExactlyAsync(stream, buffer, size, cancellationToken);
+ return new Guid(buffer);
+#endif
+ }
+
+ public static async ValueTask ReadByteArrayAsync(this Stream stream, CancellationToken cancellationToken)
+ {
+ var count = await stream.ReadInt32Async(cancellationToken);
+ if (count == 0)
+ {
+ return [];
+ }
+
+ var bytes = new byte[count];
+ await ReadExactlyAsync(stream, bytes, count, cancellationToken);
+ return bytes;
+ }
+
+ public static async ValueTask ReadIntArrayAsync(this Stream stream, CancellationToken cancellationToken)
+ {
+ var count = await stream.ReadInt32Async(cancellationToken);
+ if (count == 0)
+ {
+ return [];
+ }
+
+ var result = new int[count];
+ int size = count * sizeof(int);
+ var buffer = ArrayPool.Shared.Rent(minimumLength: size);
+ try
+ {
+ await ReadExactlyAsync(stream, buffer, size, cancellationToken);
+
+ for (var i = 0; i < count; i++)
+ {
+ result[i] = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(i * sizeof(int)));
+ }
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+
+ return result;
+ }
+
+ public static async ValueTask ReadStringAsync(this Stream stream, CancellationToken cancellationToken)
+ {
+ int size = await stream.Read7BitEncodedIntAsync(cancellationToken);
+ if (size < 0)
+ {
+ throw new InvalidDataException();
+ }
+
+ if (size == 0)
+ {
+ return string.Empty;
+ }
+
+ var buffer = ArrayPool.Shared.Rent(minimumLength: size);
+ try
+ {
+ await ReadExactlyAsync(stream, buffer, size, cancellationToken);
+ return Encoding.UTF8.GetString(buffer, 0, size);
+ }
+ finally
+ {
+ ArrayPool.Shared.Return(buffer);
+ }
+ }
+
+ public static async ValueTask Read7BitEncodedIntAsync(this Stream stream, CancellationToken cancellationToken)
+ {
+ const int MaxBytesWithoutOverflow = 4;
+
+ uint result = 0;
+ byte b;
+
+ for (int shift = 0; shift < MaxBytesWithoutOverflow * 7; shift += 7)
+ {
+ b = await stream.ReadByteAsync(cancellationToken);
+ result |= (b & 0x7Fu) << shift;
+
+ if (b <= 0x7Fu)
+ {
+ return (int)result;
+ }
+ }
+
+ // Read the 5th byte. Since we already read 28 bits,
+ // the value of this byte must fit within 4 bits (32 - 28),
+ // and it must not have the high bit set.
+
+ b = await stream.ReadByteAsync(cancellationToken);
+ if (b > 0b_1111u)
+ {
+ throw new InvalidDataException();
+ }
+
+ result |= (uint)b << (MaxBytesWithoutOverflow * 7);
+ return (int)result;
+ }
+
+ private static async ValueTask ReadExactlyAsync(this Stream stream, byte[] buffer, int size, CancellationToken cancellationToken)
+ {
+ int totalRead = 0;
+ while (totalRead < size)
+ {
+ int read = await stream.ReadAsync(buffer, offset: totalRead, count: size - totalRead, cancellationToken).ConfigureAwait(false);
+ if (read == 0)
+ {
+ throw new EndOfStreamException();
+ }
+
+ totalRead += read;
+ }
+
+ return totalRead;
+ }
+}
diff --git a/src/BuiltInTools/HotReloadAgent/AgentEnvironmentVariables.cs b/src/BuiltInTools/HotReloadAgent/AgentEnvironmentVariables.cs
new file mode 100644
index 000000000000..606e5be8f881
--- /dev/null
+++ b/src/BuiltInTools/HotReloadAgent/AgentEnvironmentVariables.cs
@@ -0,0 +1,35 @@
+// 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.HotReload;
+
+internal static class AgentEnvironmentVariables
+{
+ ///
+ /// Intentionally different from the variable name used by the debugger.
+ /// This is to avoid the debugger colliding with dotnet-watch pipe connection when debugging dotnet-watch (or tests).
+ ///
+ public const string DotNetWatchHotReloadNamedPipeName = "DOTNET_WATCH_HOTRELOAD_NAMEDPIPE_NAME";
+
+ ///
+ /// The full path to the process being launched by dotnet run.
+ /// Workaround for https://github.com/dotnet/sdk/issues/40484
+ ///
+ public const string DotNetWatchHotReloadTargetProcessPath = "DOTNET_WATCH_HOTRELOAD_TARGET_PROCESS_PATH";
+
+ ///
+ /// Enables logging from the client delta applier agent.
+ ///
+ public const string HotReloadDeltaClientLogMessages = "HOTRELOAD_DELTA_CLIENT_LOG_MESSAGES";
+
+ ///
+ /// dotnet runtime environment variable.
+ /// https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-environment-variables#dotnet_startup_hooks
+ ///
+ public const string DotNetStartupHooks = "DOTNET_STARTUP_HOOKS";
+
+ ///
+ /// dotnet runtime environment variable.
+ ///
+ public const string DotNetModifiableAssemblies = "DOTNET_MODIFIABLE_ASSEMBLIES";
+}
diff --git a/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs b/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs
index 7e1f026d8faa..f88d44707ce3 100644
--- a/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs
+++ b/src/BuiltInTools/HotReloadAgent/HotReloadAgent.cs
@@ -2,12 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System;
-using System.Linq;
-using System.Collections.Generic;
using System.Collections.Concurrent;
+using System.Collections.Generic;
using System.Diagnostics;
+using System.IO;
+using System.Linq;
using System.Reflection;
-using Microsoft.DotNet.Watch;
namespace Microsoft.DotNet.HotReload;
@@ -127,7 +127,9 @@ public void ApplyDeltas(IEnumerable deltas)
cachedDeltas.Add(delta);
}
- _metadataUpdateHandlerInvoker.Invoke(GetMetadataUpdateTypes(deltas));
+ _metadataUpdateHandlerInvoker.MetadataUpdated(GetMetadataUpdateTypes(deltas));
+
+ Reporter.Report("Deltas applied.", AgentMessageSeverity.Verbose);
}
private Type[] GetMetadataUpdateTypes(IEnumerable deltas)
@@ -196,4 +198,42 @@ private void ApplyDeltas(Assembly assembly, IReadOnlyList deltas)
return default;
}
+
+ ///
+ /// Applies the content update.
+ ///
+ public void ApplyStaticAssetUpdate(StaticAssetUpdate update)
+ {
+ _metadataUpdateHandlerInvoker.ContentUpdated(update);
+ }
+
+ ///
+ /// Clear any hot-reload specific environment variables. This prevents child processes from being
+ /// affected by the current app's hot reload settings. See https://github.com/dotnet/runtime/issues/58000
+ ///
+ public static void ClearHotReloadEnvironmentVariables(Type startupHookType)
+ {
+ Environment.SetEnvironmentVariable(AgentEnvironmentVariables.DotNetStartupHooks,
+ RemoveCurrentAssembly(startupHookType, Environment.GetEnvironmentVariable(AgentEnvironmentVariables.DotNetStartupHooks)!));
+
+ Environment.SetEnvironmentVariable(AgentEnvironmentVariables.DotNetWatchHotReloadNamedPipeName, "");
+ Environment.SetEnvironmentVariable(AgentEnvironmentVariables.HotReloadDeltaClientLogMessages, "");
+ }
+
+ // internal for testing
+ internal static string RemoveCurrentAssembly(Type startupHookType, string environment)
+ {
+ if (environment is "")
+ {
+ return environment;
+ }
+
+ var comparison = Path.DirectorySeparatorChar == '\\' ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
+
+ var assemblyLocation = startupHookType.Assembly.Location;
+ var updatedValues = environment.Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries)
+ .Where(e => !string.Equals(e, assemblyLocation, comparison));
+
+ return string.Join(Path.PathSeparator, updatedValues);
+ }
}
diff --git a/src/BuiltInTools/HotReloadAgent/MetadataUpdateHandlerInvoker.cs b/src/BuiltInTools/HotReloadAgent/MetadataUpdateHandlerInvoker.cs
index fa8b0630e840..5924d9fbc3ad 100644
--- a/src/BuiltInTools/HotReloadAgent/MetadataUpdateHandlerInvoker.cs
+++ b/src/BuiltInTools/HotReloadAgent/MetadataUpdateHandlerInvoker.cs
@@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Threading;
+using System.Runtime.CompilerServices;
namespace Microsoft.DotNet.HotReload;
@@ -18,34 +19,71 @@ namespace Microsoft.DotNet.HotReload;
#endif
internal sealed class MetadataUpdateHandlerInvoker(AgentReporter reporter)
{
- internal sealed class RegisteredActions(IReadOnlyList> clearCache, IReadOnlyList> updateApplication)
+ internal delegate void ContentUpdateAction(StaticAssetUpdate update);
+ internal delegate void MetadataUpdateAction(Type[]? updatedTypes);
+
+ internal readonly struct UpdateHandler(TAction action, MethodInfo method)
+ where TAction : Delegate
+ {
+ public TAction Action { get; } = action;
+ public MethodInfo Method { get; } = method;
+
+ public void ReportInvocation(AgentReporter reporter)
+ => reporter.Report(GetHandlerDisplayString(Method), AgentMessageSeverity.Verbose);
+ }
+
+ internal sealed class RegisteredActions(
+ IReadOnlyList> clearCacheHandlers,
+ IReadOnlyList> updateApplicationHandlers,
+ List> updateContentHandlers)
{
- public void Invoke(Type[] updatedTypes)
+ public void MetadataUpdated(AgentReporter reporter, Type[] updatedTypes)
{
- foreach (var action in clearCache)
+ foreach (var handler in clearCacheHandlers)
{
- action(updatedTypes);
+ handler.ReportInvocation(reporter);
+ handler.Action(updatedTypes);
}
- foreach (var action in updateApplication)
+ foreach (var handler in updateApplicationHandlers)
{
- action(updatedTypes);
+ handler.ReportInvocation(reporter);
+ handler.Action(updatedTypes);
}
}
+ public void UpdateContent(AgentReporter reporter, StaticAssetUpdate update)
+ {
+ foreach (var handler in updateContentHandlers)
+ {
+ handler.ReportInvocation(reporter);
+ handler.Action(update);
+ }
+ }
+
+ ///
+ /// For testing.
+ ///
+ internal IEnumerable> ClearCacheHandlers => clearCacheHandlers;
+
///
/// For testing.
///
- internal IEnumerable> ClearCache => clearCache;
+ internal IEnumerable> UpdateApplicationHandlers => updateApplicationHandlers;
///
/// For testing.
///
- internal IEnumerable> UpdateApplication => updateApplication;
+ internal IEnumerable> UpdateContentHandlers => updateContentHandlers;
}
private const string ClearCacheHandlerName = "ClearCache";
private const string UpdateApplicationHandlerName = "UpdateApplication";
+ private const string UpdateContentHandlerName = "UpdateContent";
+ private const BindingFlags HandlerMethodBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
+
+ private static readonly Type[] s_contentUpdateSignature = [typeof(string), typeof(bool), typeof(string), typeof(byte[])];
+ private static readonly Type[] s_metadataUpdateSignature = [typeof(Type[])];
private RegisteredActions? _actions;
@@ -55,27 +93,47 @@ public void Invoke(Type[] updatedTypes)
internal void Clear()
=> Interlocked.Exchange(ref _actions, null);
+ private RegisteredActions GetActions()
+ {
+ // Defer discovering metadata updata handlers until after hot reload deltas have been applied.
+ // This should give enough opportunity for AppDomain.GetAssemblies() to be sufficiently populated.
+ var actions = _actions;
+ if (actions == null)
+ {
+ Interlocked.CompareExchange(ref _actions, GetUpdateHandlerActions(), null);
+ actions = _actions;
+ }
+
+ return actions;
+ }
+
///
- /// Invokes all registerd handlers.
+ /// Invokes all registered mtadata update handlers.
///
- internal void Invoke(Type[] updatedTypes)
+ internal void MetadataUpdated(Type[] updatedTypes)
{
try
{
- // Defer discovering metadata updata handlers until after hot reload deltas have been applied.
- // This should give enough opportunity for AppDomain.GetAssemblies() to be sufficiently populated.
- var actions = _actions;
- if (actions == null)
- {
- Interlocked.CompareExchange(ref _actions, GetMetadataUpdateHandlerActions(), null);
- actions = _actions;
- }
-
reporter.Report("Invoking metadata update handlers.", AgentMessageSeverity.Verbose);
- actions.Invoke(updatedTypes);
+ GetActions().MetadataUpdated(reporter, updatedTypes);
+ }
+ catch (Exception e)
+ {
+ reporter.Report(e.ToString(), AgentMessageSeverity.Warning);
+ }
+ }
+
+ ///
+ /// Invokes all registered content update handlers.
+ ///
+ internal void ContentUpdated(StaticAssetUpdate update)
+ {
+ try
+ {
+ reporter.Report("Invoking content update handlers.", AgentMessageSeverity.Verbose);
- reporter.Report("Deltas applied.", AgentMessageSeverity.Verbose);
+ GetActions().UpdateContent(reporter, update);
}
catch (Exception e)
{
@@ -116,67 +174,111 @@ private IEnumerable GetHandlerTypes()
}
}
- public RegisteredActions GetMetadataUpdateHandlerActions()
- => GetMetadataUpdateHandlerActions(GetHandlerTypes());
+ public RegisteredActions GetUpdateHandlerActions()
+ => GetUpdateHandlerActions(GetHandlerTypes());
///
/// Internal for testing.
///
- internal RegisteredActions GetMetadataUpdateHandlerActions(IEnumerable handlerTypes)
+ internal RegisteredActions GetUpdateHandlerActions(IEnumerable handlerTypes)
{
- var clearCacheActions = new List>();
- var updateApplicationActions = new List>();
+ var clearCacheHandlers = new List>();
+ var applicationUpdateHandlers = new List>();
+ var contentUpdateHandlers = new List>();
foreach (var handlerType in handlerTypes)
{
bool methodFound = false;
- if (GetUpdateMethod(handlerType, ClearCacheHandlerName) is MethodInfo clearCache)
+ if (GetMetadataUpdateMethod(handlerType, ClearCacheHandlerName) is MethodInfo clearCache)
{
- clearCacheActions.Add(CreateAction(clearCache));
+ clearCacheHandlers.Add(CreateMetadataUpdateAction(clearCache));
methodFound = true;
}
- if (GetUpdateMethod(handlerType, UpdateApplicationHandlerName) is MethodInfo updateApplication)
+ if (GetMetadataUpdateMethod(handlerType, UpdateApplicationHandlerName) is MethodInfo updateApplication)
{
- updateApplicationActions.Add(CreateAction(updateApplication));
+ applicationUpdateHandlers.Add(CreateMetadataUpdateAction(updateApplication));
+ methodFound = true;
+ }
+
+ if (GetContentUpdateMethod(handlerType, UpdateContentHandlerName) is MethodInfo updateContent)
+ {
+ contentUpdateHandlers.Add(CreateContentUpdateAction(updateContent));
methodFound = true;
}
if (!methodFound)
{
reporter.Report(
- $"Expected to find a static method '{ClearCacheHandlerName}' or '{UpdateApplicationHandlerName}' on type '{handlerType.AssemblyQualifiedName}' but neither exists.",
+ $"Expected to find a static method '{ClearCacheHandlerName}', '{UpdateApplicationHandlerName}' or '{UpdateContentHandlerName}' on type '{handlerType.AssemblyQualifiedName}' but neither exists.",
AgentMessageSeverity.Warning);
}
}
- return new RegisteredActions(clearCacheActions, updateApplicationActions);
+ return new RegisteredActions(clearCacheHandlers, applicationUpdateHandlers, contentUpdateHandlers);
- Action CreateAction(MethodInfo update)
+ UpdateHandler CreateMetadataUpdateAction(MethodInfo method)
{
- var action = (Action)update.CreateDelegate(typeof(Action));
- return types =>
+ var action = (MetadataUpdateAction)method.CreateDelegate(typeof(MetadataUpdateAction));
+ return new(types =>
{
try
{
action(types);
}
- catch (Exception ex)
+ catch (Exception e)
{
- reporter.Report($"Exception from '{action}': {ex}", AgentMessageSeverity.Warning);
+ ReportException(e, method);
}
- };
+ }, method);
}
- MethodInfo? GetUpdateMethod(Type handlerType, string name)
+ UpdateHandler CreateContentUpdateAction(MethodInfo method)
+ {
+ var action = (Action)method.CreateDelegate(typeof(Action));
+ return new(update =>
+ {
+ try
+ {
+ action(update.AssemblyName, update.IsApplicationProject, update.RelativePath, update.Contents);
+ }
+ catch (Exception e)
+ {
+ ReportException(e, method);
+ }
+ }, method);
+ }
+
+ void ReportException(Exception e, MethodInfo method)
+ => reporter.Report($"Exception from '{GetHandlerDisplayString(method)}': {e}", AgentMessageSeverity.Warning);
+
+ MethodInfo? GetMetadataUpdateMethod(Type handlerType, string name)
{
- if (handlerType.GetMethod(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, binder: null, [typeof(Type[])], modifiers: null) is MethodInfo updateMethod &&
+ if (handlerType.GetMethod(name, HandlerMethodBindingFlags, binder: null, s_metadataUpdateSignature, modifiers: null) is MethodInfo updateMethod &&
updateMethod.ReturnType == typeof(void))
{
return updateMethod;
}
+ ReportSignatureMismatch(handlerType, name);
+ return null;
+ }
+
+ MethodInfo? GetContentUpdateMethod(Type handlerType, string name)
+ {
+ if (handlerType.GetMethod(name, HandlerMethodBindingFlags, binder: null, s_contentUpdateSignature, modifiers: null) is MethodInfo updateMethod &&
+ updateMethod.ReturnType == typeof(void))
+ {
+ return updateMethod;
+ }
+
+ ReportSignatureMismatch(handlerType, name);
+ return null;
+ }
+
+ void ReportSignatureMismatch(Type handlerType, string name)
+ {
foreach (MethodInfo method in handlerType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (method.Name == name)
@@ -185,11 +287,12 @@ internal RegisteredActions GetMetadataUpdateHandlerActions(IEnumerable han
break;
}
}
-
- return null;
}
}
+ private static string GetHandlerDisplayString(MethodInfo method)
+ => $"{method.DeclaringType!.FullName}.{method.Name}";
+
private IList TryGetCustomAttributesData(Assembly assembly)
{
try
diff --git a/src/BuiltInTools/HotReloadAgent/StaticAssetUpdate.cs b/src/BuiltInTools/HotReloadAgent/StaticAssetUpdate.cs
new file mode 100644
index 000000000000..3601980b93ee
--- /dev/null
+++ b/src/BuiltInTools/HotReloadAgent/StaticAssetUpdate.cs
@@ -0,0 +1,13 @@
+namespace Microsoft.DotNet.HotReload;
+
+internal readonly struct StaticAssetUpdate(
+ string assemblyName,
+ string relativePath,
+ byte[] contents,
+ bool isApplicationProject)
+{
+ public string AssemblyName { get; } = assemblyName;
+ public bool IsApplicationProject { get; } = isApplicationProject;
+ public string RelativePath { get; } = relativePath;
+ public byte[] Contents { get; } = contents;
+}
diff --git a/src/BuiltInTools/HotReloadAgent/UpdateDelta.cs b/src/BuiltInTools/HotReloadAgent/UpdateDelta.cs
index 3277755dea56..757e46c28ee4 100644
--- a/src/BuiltInTools/HotReloadAgent/UpdateDelta.cs
+++ b/src/BuiltInTools/HotReloadAgent/UpdateDelta.cs
@@ -3,7 +3,7 @@
using System;
-namespace Microsoft.DotNet.Watch;
+namespace Microsoft.DotNet.HotReload;
internal readonly struct UpdateDelta(Guid moduleId, byte[] metadataDelta, byte[] ilDelta, byte[] pdbDelta, int[] updatedTypes)
{
diff --git a/src/BuiltInTools/dotnet-format/Commands/FormatAnalyzersCommand.cs b/src/BuiltInTools/dotnet-format/Commands/FormatAnalyzersCommand.cs
index 3a601096addf..1fc765d41cf5 100644
--- a/src/BuiltInTools/dotnet-format/Commands/FormatAnalyzersCommand.cs
+++ b/src/BuiltInTools/dotnet-format/Commands/FormatAnalyzersCommand.cs
@@ -3,7 +3,6 @@
using System.Collections.Immutable;
using System.CommandLine;
using System.CommandLine.Invocation;
-using System.CommandLine.IO;
using Microsoft.Extensions.Logging;
using static Microsoft.CodeAnalysis.Tools.FormatCommandCommon;
@@ -31,7 +30,7 @@ private class FormatAnalyzersHandler : AsynchronousCliAction
public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
var formatOptions = parseResult.ParseVerbosityOption(FormatOptions.Instance);
- var logger = new SystemConsole().SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning);
+ var logger = SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning);
formatOptions = parseResult.ParseCommonOptions(formatOptions, logger);
formatOptions = parseResult.ParseWorkspaceOptions(formatOptions);
diff --git a/src/BuiltInTools/dotnet-format/Commands/FormatCommandCommon.cs b/src/BuiltInTools/dotnet-format/Commands/FormatCommandCommon.cs
index a801de15dfbf..48acb26267d4 100644
--- a/src/BuiltInTools/dotnet-format/Commands/FormatCommandCommon.cs
+++ b/src/BuiltInTools/dotnet-format/Commands/FormatCommandCommon.cs
@@ -137,10 +137,10 @@ public static CliArgument DefaultToCurrentDirectory(this CliArgument SetupLogging(this IConsole console, LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
+ public static ILogger SetupLogging(LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
{
var loggerFactory = new LoggerFactory()
- .AddSimpleConsole(console, minimalLogLevel, minimalErrorLevel);
+ .AddSimpleConsole(minimalLogLevel, minimalErrorLevel);
var logger = loggerFactory.CreateLogger();
return logger;
}
diff --git a/src/BuiltInTools/dotnet-format/Commands/FormatStyleCommand.cs b/src/BuiltInTools/dotnet-format/Commands/FormatStyleCommand.cs
index d7aaa6519774..12d96959c6bc 100644
--- a/src/BuiltInTools/dotnet-format/Commands/FormatStyleCommand.cs
+++ b/src/BuiltInTools/dotnet-format/Commands/FormatStyleCommand.cs
@@ -3,7 +3,6 @@
using System.Collections.Immutable;
using System.CommandLine;
using System.CommandLine.Invocation;
-using System.CommandLine.IO;
using Microsoft.Extensions.Logging;
using static Microsoft.CodeAnalysis.Tools.FormatCommandCommon;
@@ -31,7 +30,7 @@ private class FormatStyleHandler : AsynchronousCliAction
public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
var formatOptions = parseResult.ParseVerbosityOption(FormatOptions.Instance);
- var logger = new SystemConsole().SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning);
+ var logger = SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning);
formatOptions = parseResult.ParseCommonOptions(formatOptions, logger);
formatOptions = parseResult.ParseWorkspaceOptions(formatOptions);
diff --git a/src/BuiltInTools/dotnet-format/Commands/FormatWhitespaceCommand.cs b/src/BuiltInTools/dotnet-format/Commands/FormatWhitespaceCommand.cs
index 3c28fe4c5474..0885f96f284d 100644
--- a/src/BuiltInTools/dotnet-format/Commands/FormatWhitespaceCommand.cs
+++ b/src/BuiltInTools/dotnet-format/Commands/FormatWhitespaceCommand.cs
@@ -2,7 +2,6 @@
using System.CommandLine;
using System.CommandLine.Invocation;
-using System.CommandLine.IO;
using System.CommandLine.Parsing;
using Microsoft.Extensions.Logging;
using static Microsoft.CodeAnalysis.Tools.FormatCommandCommon;
@@ -11,21 +10,6 @@ namespace Microsoft.CodeAnalysis.Tools.Commands
{
internal static class FormatWhitespaceCommand
{
- // This delegate should be kept in Sync with the FormatCommand options and argument names
- // so that values bind correctly.
- internal delegate Task Handler(
- bool folder,
- string? workspace,
- bool noRestore,
- bool check,
- string[] include,
- string[] exclude,
- bool includeGenerated,
- string? verbosity,
- string? binarylog,
- string? report,
- IConsole console);
-
private static readonly FormatWhitespaceHandler s_formattingHandler = new();
internal static CliCommand GetCommand()
@@ -66,7 +50,7 @@ private class FormatWhitespaceHandler : AsynchronousCliAction
public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
var formatOptions = parseResult.ParseVerbosityOption(FormatOptions.Instance);
- var logger = new SystemConsole().SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning);
+ var logger = SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning);
formatOptions = parseResult.ParseCommonOptions(formatOptions, logger);
formatOptions = parseResult.ParseWorkspaceOptions(formatOptions);
diff --git a/src/BuiltInTools/dotnet-format/Commands/RootFormatCommand.cs b/src/BuiltInTools/dotnet-format/Commands/RootFormatCommand.cs
index 55947beed4ad..66d036f85c2b 100644
--- a/src/BuiltInTools/dotnet-format/Commands/RootFormatCommand.cs
+++ b/src/BuiltInTools/dotnet-format/Commands/RootFormatCommand.cs
@@ -3,7 +3,6 @@
using System.Collections.Immutable;
using System.CommandLine;
using System.CommandLine.Invocation;
-using System.CommandLine.IO;
using Microsoft.Extensions.Logging;
using static Microsoft.CodeAnalysis.Tools.FormatCommandCommon;
@@ -34,7 +33,7 @@ private class FormatCommandDefaultHandler : AsynchronousCliAction
public override async Task InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
var formatOptions = parseResult.ParseVerbosityOption(FormatOptions.Instance);
- var logger = new SystemConsole().SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning);
+ var logger = SetupLogging(minimalLogLevel: formatOptions.LogLevel, minimalErrorLevel: LogLevel.Warning);
formatOptions = parseResult.ParseCommonOptions(formatOptions, logger);
formatOptions = parseResult.ParseWorkspaceOptions(formatOptions);
diff --git a/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLogger.cs b/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLogger.cs
index 6c25bfbb85c6..966818d2f16e 100644
--- a/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLogger.cs
+++ b/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLogger.cs
@@ -1,18 +1,18 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
using System.Collections.Immutable;
-using System.CommandLine;
-using System.CommandLine.Rendering;
using Microsoft.Extensions.Logging;
namespace Microsoft.CodeAnalysis.Tools.Logging
{
internal class SimpleConsoleLogger : ILogger
{
- private readonly object _gate = new object();
+ private static readonly bool ColorsAreSupported =
+ !(OperatingSystem.IsBrowser() || OperatingSystem.IsAndroid() || OperatingSystem.IsIOS() || OperatingSystem.IsTvOS())
+ && !Console.IsOutputRedirected;
+
+ private readonly static object _gate = new object();
- private readonly IConsole _console;
- private readonly ITerminal _terminal;
private readonly LogLevel _minimalLogLevel;
private readonly LogLevel _minimalErrorLevel;
@@ -27,10 +27,8 @@ internal class SimpleConsoleLogger : ILogger
[LogLevel.None] = ConsoleColor.White,
}.ToImmutableDictionary();
- public SimpleConsoleLogger(IConsole console, LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
+ public SimpleConsoleLogger(LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
{
- _terminal = console.GetTerminal();
- _console = console;
_minimalLogLevel = minimalLogLevel;
_minimalErrorLevel = minimalErrorLevel;
}
@@ -46,14 +44,8 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except
{
var message = formatter(state, exception);
var logToErrorStream = logLevel >= _minimalErrorLevel;
- if (_terminal is null)
- {
- LogToConsole(_console, message, logToErrorStream);
- }
- else
- {
- LogToTerminal(message, logLevel, logToErrorStream);
- }
+
+ Log(message, logLevel, logToErrorStream);
}
}
@@ -67,25 +59,25 @@ public bool IsEnabled(LogLevel logLevel)
return NullScope.Instance;
}
- private void LogToTerminal(string message, LogLevel logLevel, bool logToErrorStream)
+ private void Log(string message, LogLevel logLevel, bool logToErrorStream)
{
- var messageColor = LogLevelColorMap[logLevel];
- _terminal.ForegroundColor = messageColor;
-
- LogToConsole(_terminal, message, logToErrorStream);
-
- _terminal.ResetColor();
- }
+ if (ColorsAreSupported)
+ {
+ Console.ForegroundColor = LogLevelColorMap[logLevel];
+ }
- private static void LogToConsole(IConsole console, string message, bool logToErrorStream)
- {
if (logToErrorStream)
{
- console.Error.Write($"{message}{Environment.NewLine}");
+ Console.Error.WriteLine(message);
}
else
{
- console.Out.Write($" {message}{Environment.NewLine}");
+ Console.Out.WriteLine(message);
+ }
+
+ if (ColorsAreSupported)
+ {
+ Console.ResetColor();
}
}
}
diff --git a/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLoggerFactoryExtensions.cs b/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLoggerFactoryExtensions.cs
index 5795b9e3bd7d..cc9dd09e69c4 100644
--- a/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLoggerFactoryExtensions.cs
+++ b/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLoggerFactoryExtensions.cs
@@ -1,15 +1,14 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
-using System.CommandLine;
using Microsoft.Extensions.Logging;
namespace Microsoft.CodeAnalysis.Tools.Logging
{
internal static class SimpleConsoleLoggerFactoryExtensions
{
- public static ILoggerFactory AddSimpleConsole(this ILoggerFactory factory, IConsole console, LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
+ public static ILoggerFactory AddSimpleConsole(this ILoggerFactory factory, LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
{
- factory.AddProvider(new SimpleConsoleLoggerProvider(console, minimalLogLevel, minimalErrorLevel));
+ factory.AddProvider(new SimpleConsoleLoggerProvider(minimalLogLevel, minimalErrorLevel));
return factory;
}
}
diff --git a/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLoggerProvider.cs b/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLoggerProvider.cs
index 1b98c212df40..7674c8a5d3a1 100644
--- a/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLoggerProvider.cs
+++ b/src/BuiltInTools/dotnet-format/Logging/SimpleConsoleLoggerProvider.cs
@@ -1,26 +1,23 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
-using System.CommandLine;
using Microsoft.Extensions.Logging;
namespace Microsoft.CodeAnalysis.Tools.Logging
{
internal class SimpleConsoleLoggerProvider : ILoggerProvider
{
- private readonly IConsole _console;
private readonly LogLevel _minimalLogLevel;
private readonly LogLevel _minimalErrorLevel;
- public SimpleConsoleLoggerProvider(IConsole console, LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
+ public SimpleConsoleLoggerProvider(LogLevel minimalLogLevel, LogLevel minimalErrorLevel)
{
- _console = console;
_minimalLogLevel = minimalLogLevel;
_minimalErrorLevel = minimalErrorLevel;
}
public ILogger CreateLogger(string name)
{
- return new SimpleConsoleLogger(_console, _minimalLogLevel, _minimalErrorLevel);
+ return new SimpleConsoleLogger(_minimalLogLevel, _minimalErrorLevel);
}
public void Dispose()
diff --git a/src/BuiltInTools/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs b/src/BuiltInTools/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs
index f654dade542c..4bdc9001568a 100644
--- a/src/BuiltInTools/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs
+++ b/src/BuiltInTools/dotnet-format/Workspaces/MSBuildWorkspaceFinder.cs
@@ -76,8 +76,11 @@ private static (bool isSolution, string workspacePath) FindFile(string workspace
return (isSolution, workspacePath);
}
- private static IEnumerable FindSolutionFiles(string basePath) => Directory.EnumerateFileSystemEntries(basePath, "*.sln", SearchOption.TopDirectoryOnly)
- .Concat(Directory.EnumerateFileSystemEntries(basePath, "*.slnf", SearchOption.TopDirectoryOnly));
+ private static IEnumerable FindSolutionFiles(string basePath) => [
+ ..Directory.EnumerateFileSystemEntries(basePath, "*.sln", SearchOption.TopDirectoryOnly),
+ ..Directory.EnumerateFileSystemEntries(basePath, "*.slnf", SearchOption.TopDirectoryOnly),
+ ..Directory.EnumerateFileSystemEntries(basePath, "*.slnx", SearchOption.TopDirectoryOnly)
+ ];
private static IEnumerable FindProjectFiles(string basePath) => Directory.EnumerateFileSystemEntries(basePath, "*.*proj", SearchOption.TopDirectoryOnly)
.Where(f => !DnxProjectExtension.Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase));
diff --git a/src/BuiltInTools/dotnet-format/dotnet-format.csproj b/src/BuiltInTools/dotnet-format/dotnet-format.csproj
index dda1c67a29db..dd60e3a0f784 100644
--- a/src/BuiltInTools/dotnet-format/dotnet-format.csproj
+++ b/src/BuiltInTools/dotnet-format/dotnet-format.csproj
@@ -49,7 +49,6 @@
-
diff --git a/src/BuiltInTools/dotnet-watch.slnf b/src/BuiltInTools/dotnet-watch.slnf
index 2484e98c2da7..b0e0ee2dcafc 100644
--- a/src/BuiltInTools/dotnet-watch.slnf
+++ b/src/BuiltInTools/dotnet-watch.slnf
@@ -9,6 +9,8 @@
"src\\BuiltInTools\\DotNetWatchTasks\\DotNetWatchTasks.csproj",
"src\\BuiltInTools\\HotReloadAgent\\Microsoft.DotNet.HotReload.Agent.Package.csproj",
"src\\BuiltInTools\\HotReloadAgent\\Microsoft.DotNet.HotReload.Agent.shproj",
+ "src\\BuiltInTools\\HotReloadAgent.PipeRpc\\Microsoft.DotNet.HotReload.Agent.PipeRpc.Package.csproj",
+ "src\\BuiltInTools\\HotReloadAgent.PipeRpc\\Microsoft.DotNet.HotReload.Agent.PipeRpc.shproj",
"src\\BuiltInTools\\dotnet-watch\\dotnet-watch.csproj",
"test\\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests\\Microsoft.AspNetCore.Watch.BrowserRefresh.Tests.csproj",
"test\\Microsoft.Extensions.DotNetDeltaApplier.Tests\\Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj",
diff --git a/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs b/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs
index d806dd11ba45..158a49274bf6 100644
--- a/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs
+++ b/src/BuiltInTools/dotnet-watch/Aspire/AspireServiceFactory.cs
@@ -12,7 +12,7 @@ namespace Microsoft.DotNet.Watch;
internal class AspireServiceFactory : IRuntimeProcessLauncherFactory
{
- private sealed class SessionManager : IAspireServerEvents, IRuntimeProcessLauncher
+ internal sealed class SessionManager : IAspireServerEvents, IRuntimeProcessLauncher
{
private readonly struct Session(string dcpId, string sessionId, RunningProject runningProject, Task outputReader)
{
@@ -30,7 +30,7 @@ private readonly struct Session(string dcpId, string sessionId, RunningProject r
private readonly ProjectLauncher _projectLauncher;
private readonly AspireServerService _service;
- private readonly IReadOnlyList _buildArguments;
+ private readonly ProjectOptions _hostProjectOptions;
///
/// Lock to access:
@@ -43,10 +43,10 @@ private readonly struct Session(string dcpId, string sessionId, RunningProject r
private int _sessionIdDispenser;
private volatile bool _isDisposed;
- public SessionManager(ProjectLauncher projectLauncher, IReadOnlyList buildArguments)
+ public SessionManager(ProjectLauncher projectLauncher, ProjectOptions hostProjectOptions)
{
_projectLauncher = projectLauncher;
- _buildArguments = buildArguments;
+ _hostProjectOptions = hostProjectOptions;
_service = new AspireServerService(
this,
@@ -204,44 +204,64 @@ private async ValueTask TerminateSessionAsync(Session session, CancellationToken
}
private ProjectOptions GetProjectOptions(ProjectLaunchRequest projectLaunchInfo)
+ {
+ var hostLaunchProfile = _hostProjectOptions.NoLaunchProfile ? null : _hostProjectOptions.LaunchProfileName;
+
+ return new()
+ {
+ IsRootProject = false,
+ ProjectPath = projectLaunchInfo.ProjectPath,
+ WorkingDirectory = _projectLauncher.EnvironmentOptions.WorkingDirectory,
+ BuildArguments = _hostProjectOptions.BuildArguments,
+ Command = "run",
+ CommandArguments = GetRunCommandArguments(projectLaunchInfo, hostLaunchProfile),
+ LaunchEnvironmentVariables = projectLaunchInfo.Environment?.Select(e => (e.Key, e.Value))?.ToArray() ?? [],
+ LaunchProfileName = projectLaunchInfo.LaunchProfile,
+ NoLaunchProfile = projectLaunchInfo.DisableLaunchProfile,
+ TargetFramework = _hostProjectOptions.TargetFramework,
+ };
+ }
+
+ // internal for testing
+ internal static IReadOnlyList GetRunCommandArguments(ProjectLaunchRequest projectLaunchInfo, string? hostLaunchProfile)
{
var arguments = new List
{
"--project",
projectLaunchInfo.ProjectPath,
- // TODO: https://github.com/dotnet/sdk/issues/43946
- // Need to suppress launch profile for now, otherwise it would override the port set via env variable.
- "--no-launch-profile",
};
- //if (projectLaunchInfo.DisableLaunchProfile)
- //{
- // arguments.Add("--no-launch-profile");
- //}
- //else if (!string.IsNullOrEmpty(projectLaunchInfo.LaunchProfile))
- //{
- // arguments.Add("--launch-profile");
- // arguments.Add(projectLaunchInfo.LaunchProfile);
- //}
+ // Implements https://github.com/dotnet/aspire/blob/main/docs/specs/IDE-execution.md#launch-profile-processing-project-launch-configuration
- if (projectLaunchInfo.Arguments != null)
+ if (projectLaunchInfo.DisableLaunchProfile)
+ {
+ arguments.Add("--no-launch-profile");
+ }
+ else if (!string.IsNullOrEmpty(projectLaunchInfo.LaunchProfile))
{
- arguments.AddRange(projectLaunchInfo.Arguments);
+ arguments.Add("--launch-profile");
+ arguments.Add(projectLaunchInfo.LaunchProfile);
+ }
+ else if (hostLaunchProfile != null)
+ {
+ arguments.Add("--launch-profile");
+ arguments.Add(hostLaunchProfile);
}
- return new()
+ if (projectLaunchInfo.Arguments != null)
{
- IsRootProject = false,
- ProjectPath = projectLaunchInfo.ProjectPath,
- WorkingDirectory = _projectLauncher.EnvironmentOptions.WorkingDirectory, // TODO: Should DCP protocol specify?
- BuildArguments = _buildArguments, // TODO: Should DCP protocol specify?
- Command = "run",
- CommandArguments = arguments,
- LaunchEnvironmentVariables = projectLaunchInfo.Environment?.Select(kvp => (kvp.Key, kvp.Value)).ToArray() ?? [],
- LaunchProfileName = projectLaunchInfo.LaunchProfile,
- NoLaunchProfile = projectLaunchInfo.DisableLaunchProfile,
- TargetFramework = null, // TODO: Should DCP protocol specify?
- };
+ if (projectLaunchInfo.Arguments.Any())
+ {
+ arguments.AddRange(projectLaunchInfo.Arguments);
+ }
+ else
+ {
+ // indicate that no arguments should be used even if launch profile specifies some:
+ arguments.Add("--no-launch-profile-arguments");
+ }
+ }
+
+ return arguments;
}
}
@@ -250,8 +270,8 @@ private ProjectOptions GetProjectOptions(ProjectLaunchRequest projectLaunchInfo)
public static readonly AspireServiceFactory Instance = new();
public const string AppHostProjectCapability = "Aspire";
- public IRuntimeProcessLauncher? TryCreate(ProjectGraphNode projectNode, ProjectLauncher projectLauncher, IReadOnlyList buildArguments)
+ public IRuntimeProcessLauncher? TryCreate(ProjectGraphNode projectNode, ProjectLauncher projectLauncher, ProjectOptions hostProjectOptions)
=> projectNode.GetCapabilities().Contains(AppHostProjectCapability)
- ? new SessionManager(projectLauncher, buildArguments)
+ ? new SessionManager(projectLauncher, hostProjectOptions)
: null;
}
diff --git a/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs b/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs
index 9126b8ec54b6..733a7abec5c5 100644
--- a/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs
+++ b/src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs
@@ -9,15 +9,19 @@
namespace Microsoft.DotNet.Watch
{
- internal sealed partial class BrowserConnector(DotNetWatchContext context) : IAsyncDisposable
+ internal sealed partial class BrowserConnector(DotNetWatchContext context) : IAsyncDisposable, IStaticAssetChangeApplierProvider
{
// This needs to be in sync with the version BrowserRefreshMiddleware is compiled against.
private static readonly Version s_minimumSupportedVersion = Versions.Version6_0;
- private static readonly Regex s_nowListeningRegex = s_nowListeningOnRegex();
+ private static readonly Regex s_nowListeningRegex = GetNowListeningOnRegex();
+ private static readonly Regex s_aspireDashboardUrlRegex = GetAspireDashboardUrlRegex();
[GeneratedRegex(@"Now listening on: (?.*)\s*$", RegexOptions.Compiled)]
- private static partial Regex s_nowListeningOnRegex();
+ private static partial Regex GetNowListeningOnRegex();
+
+ [GeneratedRegex(@"Login to the dashboard at (?.*)\s*$", RegexOptions.Compiled)]
+ private static partial Regex GetAspireDashboardUrlRegex();
private readonly object _serversGuard = new();
private readonly Dictionary _servers = [];
@@ -44,7 +48,12 @@ await Task.WhenAll(serversToDispose.Select(async server =>
}));
}
- public async ValueTask LaunchOrRefreshBrowserAsync(
+ ///
+ /// A single browser refresh server is created for each project that supports browser launching.
+ /// When the project is rebuilt we reuse the same refresh server and browser instance.
+ /// Reload message is sent to the browser in that case.
+ ///
+ public async ValueTask GetOrCreateBrowserRefreshServerAsync(
ProjectGraphNode projectNode,
ProcessSpec processSpec,
EnvironmentVariablesBuilder environmentBuilder,
@@ -64,12 +73,9 @@ await Task.WhenAll(serversToDispose.Select(async server =>
}
}
- // Attach trigger to the process that launches browser on URL found in the process output.
- // Only do so for root projects, not for child processes.
- if (projectOptions.IsRootProject)
- {
- processSpec.OnOutput += GetBrowserLaunchTrigger(projectNode, projectOptions, server, cancellationToken);
- }
+ // Attach trigger to the process that detects when the web server reports to the output that it's listening.
+ // Launches browser on the URL found in the process output for root projects.
+ processSpec.OnOutput += GetBrowserLaunchTrigger(projectNode, projectOptions, server, cancellationToken);
if (server == null)
{
@@ -81,15 +87,23 @@ await Task.WhenAll(serversToDispose.Select(async server =>
{
// Start the server we just created:
await server.StartAsync(cancellationToken);
- server.SetEnvironmentVariables(environmentBuilder);
}
- else
+
+ server.SetEnvironmentVariables(environmentBuilder);
+
+ return server;
+ }
+
+ bool IStaticAssetChangeApplierProvider.TryGetApplier(ProjectGraphNode projectNode, [NotNullWhen(true)] out IStaticAssetChangeApplier? applier)
+ {
+ if (TryGetRefreshServer(projectNode, out var server))
{
- // Notify the browser of a project rebuild (delta applier notifies of updates):
- await server.SendWaitMessageAsync(cancellationToken);
+ applier = server;
+ return true;
}
- return server;
+ applier = null;
+ return false;
}
public bool TryGetRefreshServer(ProjectGraphNode projectNode, [NotNullWhen(true)] out BrowserRefreshServer? server)
@@ -117,6 +131,10 @@ public bool TryGetRefreshServer(ProjectGraphNode projectNode, [NotNullWhen(true)
bool matchFound = false;
+ // Workaround for Aspire dashboard launching: scan for "Login to the dashboard at " prefix in the output and use the URL.
+ // TODO: Share launch profile processing logic as implemented in VS with dotnet-run and implement browser launching there.
+ var isAspireHost = projectNode.GetCapabilities().Contains(AspireServiceFactory.AppHostProjectCapability);
+
return handler;
void handler(OutputLine line)
@@ -129,7 +147,7 @@ void handler(OutputLine line)
return;
}
- var match = s_nowListeningRegex.Match(line.Content);
+ var match = (isAspireHost ? s_aspireDashboardUrlRegex : s_nowListeningRegex).Match(line.Content);
if (!match.Success)
{
return;
@@ -137,11 +155,12 @@ void handler(OutputLine line)
matchFound = true;
- var projectAddedToAttemptedSet = ImmutableInterlocked.Update(ref _browserLaunchAttempted, static (set, projectNode) => set.Add(projectNode), projectNode);
- if (projectAddedToAttemptedSet)
+ if (projectOptions.IsRootProject &&
+ ImmutableInterlocked.Update(ref _browserLaunchAttempted, static (set, projectNode) => set.Add(projectNode), projectNode))
{
- // first iteration:
- LaunchBrowser(launchProfile, match.Groups["url"].Value, server);
+ // first build iteration of a root project:
+ var launchUrl = GetLaunchUrl(launchProfile.LaunchUrl, match.Groups["url"].Value);
+ LaunchBrowser(launchUrl, server);
}
else if (server != null)
{
@@ -153,10 +172,15 @@ void handler(OutputLine line)
}
}
- private void LaunchBrowser(LaunchSettingsProfile launchProfile, string launchUrl, BrowserRefreshServer? server)
+ public static string GetLaunchUrl(string? profileLaunchUrl, string outputLaunchUrl)
+ => string.IsNullOrWhiteSpace(profileLaunchUrl) ? outputLaunchUrl :
+ Uri.TryCreate(profileLaunchUrl, UriKind.Absolute, out _) ? profileLaunchUrl :
+ Uri.TryCreate(outputLaunchUrl, UriKind.Absolute, out var launchUri) ? new Uri(launchUri, profileLaunchUrl).ToString() :
+ outputLaunchUrl;
+
+ private void LaunchBrowser(string launchUrl, BrowserRefreshServer? server)
{
- var launchPath = launchProfile.LaunchUrl;
- var fileName = Uri.TryCreate(launchPath, UriKind.Absolute, out _) ? launchPath : launchUrl + "/" + launchPath;
+ var fileName = launchUrl;
var args = string.Empty;
if (EnvironmentVariables.BrowserPath is { } browserPath)
diff --git a/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs b/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs
index 0f7c737e5687..ec9e12e07a87 100644
--- a/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs
+++ b/src/BuiltInTools/dotnet-watch/Browser/BrowserRefreshServer.cs
@@ -22,7 +22,7 @@ namespace Microsoft.DotNet.Watch
///
/// Communicates with aspnetcore-browser-refresh.js loaded in the browser.
///
- internal sealed class BrowserRefreshServer : IAsyncDisposable
+ internal sealed class BrowserRefreshServer : IAsyncDisposable, IStaticAssetChangeApplier
{
private static readonly ReadOnlyMemory s_reloadMessage = Encoding.UTF8.GetBytes("Reload");
private static readonly ReadOnlyMemory s_waitMessage = Encoding.UTF8.GetBytes("Wait");
@@ -81,8 +81,8 @@ public void SetEnvironmentVariables(EnvironmentVariablesBuilder environmentBuild
environmentBuilder.SetVariable(EnvironmentVariables.Names.AspNetCoreAutoReloadWSEndPoint, _serverUrls);
environmentBuilder.SetVariable(EnvironmentVariables.Names.AspNetCoreAutoReloadWSKey, GetServerKey());
- environmentBuilder.DotNetStartupHookDirective.Add(Path.Combine(AppContext.BaseDirectory, "middleware", "Microsoft.AspNetCore.Watch.BrowserRefresh.dll"));
- environmentBuilder.AspNetCoreHostingStartupAssembliesVariable.Add("Microsoft.AspNetCore.Watch.BrowserRefresh");
+ environmentBuilder.DotNetStartupHooks.Add(Path.Combine(AppContext.BaseDirectory, "middleware", "Microsoft.AspNetCore.Watch.BrowserRefresh.dll"));
+ environmentBuilder.AspNetCoreHostingStartupAssemblies.Add("Microsoft.AspNetCore.Watch.BrowserRefresh");
if (_reporter.IsVerbose)
{
@@ -288,7 +288,7 @@ public ValueTask SendReloadMessageAsync(CancellationToken cancellationToken)
public ValueTask SendWaitMessageAsync(CancellationToken cancellationToken)
=> SendAsync(s_waitMessage, cancellationToken);
- public ValueTask SendAsync(ReadOnlyMemory messageBytes, CancellationToken cancellationToken)
+ private ValueTask SendAsync(ReadOnlyMemory messageBytes, CancellationToken cancellationToken)
=> SendAndReceiveAsync(request: _ => messageBytes, response: null, cancellationToken);
public async ValueTask SendAndReceiveAsync(
@@ -354,6 +354,17 @@ public ValueTask ReportCompilationErrorsInBrowserAsync(ImmutableArray co
}
}
+ public async ValueTask UpdateStaticAssetsAsync(IEnumerable relativeUrls, CancellationToken cancellationToken)
+ {
+ // Serialize all requests sent to a single server:
+ foreach (var relativeUrl in relativeUrls)
+ {
+ _reporter.Verbose($"Sending static asset update request to browser: '{relativeUrl}'.");
+ var message = JsonSerializer.SerializeToUtf8Bytes(new UpdateStaticFileMessage { Path = relativeUrl }, s_jsonSerializerOptions);
+ await SendAsync(message, cancellationToken);
+ }
+ }
+
private readonly struct AspNetCoreHotReloadApplied
{
public string Type => "AspNetCoreHotReloadApplied";
@@ -365,5 +376,11 @@ private readonly struct HotReloadDiagnostics
public IEnumerable Diagnostics { get; init; }
}
+
+ private readonly struct UpdateStaticFileMessage
+ {
+ public string Type => "UpdateStaticFile";
+ public string Path { get; init; }
+ }
}
}
diff --git a/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs
index 321ad2924813..9fcedd45853c 100644
--- a/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs
+++ b/src/BuiltInTools/dotnet-watch/DotNetWatcher.cs
@@ -62,10 +62,10 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
};
var browserRefreshServer = (projectRootNode != null)
- ? await browserConnector.LaunchOrRefreshBrowserAsync(projectRootNode, processSpec, environmentBuilder, Context.RootProjectOptions, shutdownCancellationToken)
+ ? await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectRootNode, processSpec, environmentBuilder, Context.RootProjectOptions, shutdownCancellationToken)
: null;
- environmentBuilder.ConfigureProcess(processSpec);
+ environmentBuilder.SetProcessEnvironmentVariables(processSpec);
// Reset for next run
buildEvaluator.RequiresRevaluation = false;
diff --git a/src/BuiltInTools/dotnet-watch/EnvironmentOptions.cs b/src/BuiltInTools/dotnet-watch/EnvironmentOptions.cs
index 43d17a2312cb..199c4d864fc2 100644
--- a/src/BuiltInTools/dotnet-watch/EnvironmentOptions.cs
+++ b/src/BuiltInTools/dotnet-watch/EnvironmentOptions.cs
@@ -27,6 +27,7 @@ internal enum TestFlags
internal sealed record EnvironmentOptions(
string WorkingDirectory,
string MuxerPath,
+ TimeSpan ProcessCleanupTimeout,
bool IsPollingEnabled = false,
bool SuppressHandlingStaticContentFiles = false,
bool SuppressMSBuildIncrementalism = false,
@@ -40,6 +41,7 @@ internal sealed record EnvironmentOptions(
(
WorkingDirectory: Directory.GetCurrentDirectory(),
MuxerPath: GetMuxerPathFromEnvironment(),
+ ProcessCleanupTimeout: EnvironmentVariables.ProcessCleanupTimeout,
IsPollingEnabled: EnvironmentVariables.IsPollingEnabled,
SuppressHandlingStaticContentFiles: EnvironmentVariables.SuppressHandlingStaticContentFiles,
SuppressMSBuildIncrementalism: EnvironmentVariables.SuppressMSBuildIncrementalism,
diff --git a/src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs b/src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs
index c31413b1d3f2..6893ab561060 100644
--- a/src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs
+++ b/src/BuiltInTools/dotnet-watch/EnvironmentVariables.cs
@@ -3,9 +3,9 @@
namespace Microsoft.DotNet.Watch;
-internal static partial class EnvironmentVariables
+internal static class EnvironmentVariables
{
- public static partial class Names
+ public static class Names
{
public const string DotnetWatch = "DOTNET_WATCH";
public const string DotnetWatchIteration = "DOTNET_WATCH_ITERATION";
@@ -16,12 +16,19 @@ public static partial class Names
public const string AspNetCoreHostingStartupAssemblies = "ASPNETCORE_HOSTINGSTARTUPASSEMBLIES";
public const string AspNetCoreAutoReloadWSEndPoint = "ASPNETCORE_AUTO_RELOAD_WS_ENDPOINT";
public const string AspNetCoreAutoReloadWSKey = "ASPNETCORE_AUTO_RELOAD_WS_KEY";
+
+ public const string DotNetWatchHotReloadNamedPipeName = HotReload.AgentEnvironmentVariables.DotNetWatchHotReloadNamedPipeName;
+ public const string DotNetWatchHotReloadTargetProcessPath = HotReload.AgentEnvironmentVariables.DotNetWatchHotReloadTargetProcessPath;
+ public const string DotNetStartupHooks = HotReload.AgentEnvironmentVariables.DotNetStartupHooks;
+ public const string DotNetModifiableAssemblies = HotReload.AgentEnvironmentVariables.DotNetModifiableAssemblies;
+ public const string HotReloadDeltaClientLogMessages = HotReload.AgentEnvironmentVariables.HotReloadDeltaClientLogMessages;
}
public static bool VerboseCliOutput => ReadBool("DOTNET_CLI_CONTEXT_VERBOSE");
public static bool IsPollingEnabled => ReadBool("DOTNET_USE_POLLING_FILE_WATCHER");
public static bool SuppressEmojis => ReadBool("DOTNET_WATCH_SUPPRESS_EMOJIS");
public static bool RestartOnRudeEdit => ReadBool("DOTNET_WATCH_RESTART_ON_RUDE_EDIT");
+ public static TimeSpan ProcessCleanupTimeout => ReadTimeSpan("DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS");
public static string SdkRootDirectory =>
#if DEBUG
@@ -43,4 +50,7 @@ public static partial class Names
private static bool ReadBool(string variableName)
=> Environment.GetEnvironmentVariable(variableName) is var value && (value == "1" || bool.TryParse(value, out var boolValue) && boolValue);
+
+ private static TimeSpan ReadTimeSpan(string variableName)
+ => Environment.GetEnvironmentVariable(variableName) is var value && long.TryParse(value, out var intValue) && intValue >= 0 ? TimeSpan.FromMilliseconds(intValue) : TimeSpan.FromSeconds(5);
}
diff --git a/src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs b/src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs
index aec5634a4acf..640389af9175 100644
--- a/src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs
+++ b/src/BuiltInTools/dotnet-watch/EnvironmentVariablesBuilder.cs
@@ -10,89 +10,64 @@ internal sealed class EnvironmentVariablesBuilder
private static readonly char s_startupHooksSeparator = Path.PathSeparator;
private const char AssembliesSeparator = ';';
- public List DotNetStartupHookDirective { get; } = [];
- public List AspNetCoreHostingStartupAssembliesVariable { get; } = [];
+ public List DotNetStartupHooks { get; } = [];
+ public List AspNetCoreHostingStartupAssemblies { get; } = [];
///
/// Environment variables set on the dotnet run process.
///
private readonly Dictionary _variables = [];
- ///
- /// Environment variables passed as directives on command line (dotnet [env:name=value] run).
- /// Currently, the effect is the same as setting due to
- /// https://github.com/dotnet/sdk/issues/40484
- ///
- private readonly Dictionary _directives = [];
-
public static EnvironmentVariablesBuilder FromCurrentEnvironment()
{
var builder = new EnvironmentVariablesBuilder();
- if (Environment.GetEnvironmentVariable(EnvironmentVariables.Names.DotnetStartupHooks) is { } dotnetStartupHooks)
+ if (Environment.GetEnvironmentVariable(EnvironmentVariables.Names.DotNetStartupHooks) is { } dotnetStartupHooks)
{
- builder.DotNetStartupHookDirective.AddRange(dotnetStartupHooks.Split(s_startupHooksSeparator));
+ builder.DotNetStartupHooks.AddRange(dotnetStartupHooks.Split(s_startupHooksSeparator));
}
if (Environment.GetEnvironmentVariable(EnvironmentVariables.Names.AspNetCoreHostingStartupAssemblies) is { } assemblies)
{
- builder.AspNetCoreHostingStartupAssembliesVariable.AddRange(assemblies.Split(AssembliesSeparator));
+ builder.AspNetCoreHostingStartupAssemblies.AddRange(assemblies.Split(AssembliesSeparator));
}
return builder;
}
- public void SetDirective(string name, string value)
- {
- // should use DotNetStartupHookDirective
- Debug.Assert(!name.Equals(EnvironmentVariables.Names.DotnetStartupHooks, StringComparison.OrdinalIgnoreCase));
-
- _directives[name] = value;
- }
-
public void SetVariable(string name, string value)
{
- // should use AspNetCoreHostingStartupAssembliesVariable
+ // should use AspNetCoreHostingStartupAssembliesVariable/DotNetStartupHookDirective
Debug.Assert(!name.Equals(EnvironmentVariables.Names.AspNetCoreHostingStartupAssemblies, StringComparison.OrdinalIgnoreCase));
+ Debug.Assert(!name.Equals(EnvironmentVariables.Names.DotNetStartupHooks, StringComparison.OrdinalIgnoreCase));
_variables[name] = value;
}
- public void ConfigureProcess(ProcessSpec processSpec)
+ public void SetProcessEnvironmentVariables(ProcessSpec processSpec)
{
- processSpec.Arguments = [.. GetCommandLineDirectives(), .. processSpec.Arguments ?? []];
- AddToEnvironment(processSpec.EnvironmentVariables);
+ foreach (var (name, value) in GetEnvironment())
+ {
+ processSpec.EnvironmentVariables.Add(name, value);
+ }
}
- // for testing
- internal void AddToEnvironment(Dictionary variables)
+ public IEnumerable<(string name, string value)> GetEnvironment()
{
foreach (var (name, value) in _variables)
{
- variables.Add(name, value);
+ yield return (name, value);
}
- if (AspNetCoreHostingStartupAssembliesVariable is not [])
+ if (DotNetStartupHooks is not [])
{
- variables.Add(EnvironmentVariables.Names.AspNetCoreHostingStartupAssemblies, string.Join(AssembliesSeparator, AspNetCoreHostingStartupAssembliesVariable));
+ yield return (EnvironmentVariables.Names.DotNetStartupHooks, string.Join(s_startupHooksSeparator, DotNetStartupHooks));
}
- }
- // for testing
- internal IEnumerable GetCommandLineDirectives()
- {
- foreach (var (name, value) in _directives)
+ if (AspNetCoreHostingStartupAssemblies is not [])
{
- yield return MakeDirective(name, value);
+ yield return (EnvironmentVariables.Names.AspNetCoreHostingStartupAssemblies, string.Join(AssembliesSeparator, AspNetCoreHostingStartupAssemblies));
}
-
- if (DotNetStartupHookDirective is not [])
- {
- yield return MakeDirective(EnvironmentVariables.Names.DotnetStartupHooks, string.Join(s_startupHooksSeparator, DotNetStartupHookDirective));
- }
-
- static string MakeDirective(string name, string value)
- => $"[env:{name}={value}]";
}
}
}
diff --git a/src/BuiltInTools/dotnet-watch/EnvironmentVariables_StartupHook.cs b/src/BuiltInTools/dotnet-watch/EnvironmentVariables_StartupHook.cs
deleted file mode 100644
index 6a9191c1dab7..000000000000
--- a/src/BuiltInTools/dotnet-watch/EnvironmentVariables_StartupHook.cs
+++ /dev/null
@@ -1,30 +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;
-
-internal static partial class EnvironmentVariables
-{
- public static partial class Names
- {
- ///
- /// Intentionally different from the variable name used by the debugger.
- /// This is to avoid the debugger colliding with dotnet-watch pipe connection when debugging dotnet-watch (or tests).
- ///
- public const string DotnetWatchHotReloadNamedPipeName = "DOTNET_WATCH_HOTRELOAD_NAMEDPIPE_NAME";
-
- ///
- /// The full path to the process being launched by dotnet run.
- /// Workaround for https://github.com/dotnet/sdk/issues/40484
- ///
- public const string DotnetWatchHotReloadTargetProcessPath = "DOTNET_WATCH_HOTRELOAD_TARGET_PROCESS_PATH";
-
- ///
- /// Enables logging from the client delta applier agent.
- ///
- public const string HotReloadDeltaClientLogMessages = "HOTRELOAD_DELTA_CLIENT_LOG_MESSAGES";
-
- public const string DotnetStartupHooks = "DOTNET_STARTUP_HOOKS";
- public const string DotnetModifiableAssemblies = "DOTNET_MODIFIABLE_ASSEMBLIES";
- }
-}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs
index 35d0828ad56a..617d7d733856 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs
@@ -67,7 +67,7 @@ public override Task> GetApplyUpdateCapabilitiesAsync(Can
return Task.FromResult(capabilities);
}
- public override async Task Apply(ImmutableArray updates, CancellationToken cancellationToken)
+ public override async Task ApplyManagedCodeUpdates(ImmutableArray updates, CancellationToken cancellationToken)
{
var applicableUpdates = await FilterApplicableUpdatesAsync(updates, cancellationToken);
if (applicableUpdates.Count == 0)
@@ -135,6 +135,13 @@ await browserRefreshServer.SendAndReceiveAsync(
return (!anySuccess && anyFailure) ? ApplyStatus.Failed : (applicableUpdates.Count < updates.Length) ? ApplyStatus.SomeChangesApplied : ApplyStatus.AllChangesApplied;
}
+ public override Task ApplyStaticAssetUpdates(ImmutableArray updates, CancellationToken cancellationToken)
+ // static asset updates are handled by browser refresh server:
+ => Task.FromResult(ApplyStatus.NoChangesApplied);
+
+ public override Task InitialUpdatesApplied(CancellationToken cancellationToken)
+ => Task.CompletedTask;
+
private readonly struct JsonApplyHotReloadDeltasRequest
{
public string Type => "BlazorHotReloadDeltav3";
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs
index f2eb00de24bc..2499e85e13c1 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs
@@ -41,7 +41,7 @@ public override async Task> GetApplyUpdateCapabilitiesAsy
return result[0].Union(result[1], StringComparer.OrdinalIgnoreCase).ToImmutableArray();
}
- public override async Task Apply(ImmutableArray updates, CancellationToken cancellationToken)
+ public override async Task ApplyManagedCodeUpdates(ImmutableArray updates, CancellationToken cancellationToken)
{
// Apply to both processes.
// The module the change is for does not need to be loaded in either of the processes, yet we still consider it successful if the application does not fail.
@@ -50,8 +50,8 @@ public override async Task Apply(ImmutableArray ApplyStaticAssetUpdates(ImmutableArray updates, CancellationToken cancellationToken)
+ // static asset updates are handled by browser refresh server:
+ => Task.FromResult(ApplyStatus.NoChangesApplied);
+
+ public override Task InitialUpdatesApplied(CancellationToken cancellationToken)
+ => _hostApplier.InitialUpdatesApplied(cancellationToken);
}
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
index 838c3f97ab70..15c4ea831d9b 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.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.Immutable;
using System.Diagnostics;
using Microsoft.Build.Graph;
@@ -15,6 +14,7 @@ namespace Microsoft.DotNet.Watch
internal sealed class CompilationHandler : IDisposable
{
public readonly IncrementalMSBuildWorkspace Workspace;
+ public readonly EnvironmentOptions EnvironmentOptions;
private readonly IReporter _reporter;
private readonly WatchHotReloadService _hotReloadService;
@@ -37,19 +37,17 @@ internal sealed class CompilationHandler : IDisposable
///
private ImmutableList _previousUpdates = [];
- ///
- /// Set of capabilities aggregated across the current set of .
- /// Default if not calculated yet.
- ///
- private ImmutableArray _currentAggregateCapabilities;
+ private readonly CancellationToken _shutdownCancellationToken;
private bool _isDisposed;
- public CompilationHandler(IReporter reporter)
+ public CompilationHandler(IReporter reporter, EnvironmentOptions environmentOptions, CancellationToken shutdownCancellationToken)
{
_reporter = reporter;
+ EnvironmentOptions = environmentOptions;
Workspace = new IncrementalMSBuildWorkspace(reporter);
- _hotReloadService = new WatchHotReloadService(Workspace.CurrentSolution.Services, GetAggregateCapabilitiesAsync);
+ _hotReloadService = new WatchHotReloadService(Workspace.CurrentSolution.Services, () => ValueTask.FromResult(GetAggregateCapabilities()));
+ _shutdownCancellationToken = shutdownCancellationToken;
}
public void Dispose()
@@ -150,10 +148,14 @@ private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, Project
return null;
}
- var capabilityProvider = deltaApplier.GetApplyUpdateCapabilitiesAsync(processCommunicationCancellationSource.Token);
+ // 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 deltaApplier.GetApplyUpdateCapabilitiesAsync(processCommunicationCancellationSource.Token);
+
var runningProject = new RunningProject(
projectNode,
projectOptions,
+ EnvironmentOptions,
deltaApplier,
processReporter,
browserRefreshServer,
@@ -163,24 +165,22 @@ private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, Project
processTerminationSource: processTerminationSource,
restartOperation: restartOperation,
disposables: [processCommunicationCancellationSource],
- capabilityProvider);
+ capabilities);
// ownership transferred to running project:
disposables.Items.Clear();
disposables.Items.Add(runningProject);
- ImmutableArray observedCapabilities = default;
-
var appliedUpdateCount = 0;
while (true)
{
// Observe updates that need to be applied to the new process
// and apply them before adding it to running processes.
- // Do bot block on udpates being made to other processes to avoid delaying the new process being up-to-date.
+ // 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())
{
- _ = await deltaApplier.Apply(updatesToApply, processCommunicationCancellationSource.Token);
+ _ = await deltaApplier.ApplyManagedCodeUpdates(updatesToApply, processCommunicationCancellationSource.Token);
}
appliedUpdateCount += updatesToApply.Length;
@@ -205,26 +205,18 @@ private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, Project
_runningProjects = _runningProjects.SetItem(projectPath, projectInstances.Add(runningProject));
- // reset capabilities:
- observedCapabilities = _currentAggregateCapabilities;
- _currentAggregateCapabilities = default;
-
// ownership transferred to _runningProjects
disposables.Items.Clear();
break;
}
}
+ // Notifies the agent that it can unblock the execution of the process:
+ await deltaApplier.InitialUpdatesApplied(cancellationToken);
+
// If non-empty solution is loaded into the workspace (a Hot Reload session is active):
if (Workspace.CurrentSolution is { ProjectIds: not [] } currentSolution)
{
- // If capabilities have been observed by an edit session, restart the session. Next time EnC service needs
- // capabilities it calls GetAggregateCapabilitiesAsync which uses the set of projects assigned above to calculate them.
- if (!observedCapabilities.IsDefault)
- {
- _hotReloadService.CapabilitiesChanged();
- }
-
// Preparing the compilation is a perf optimization. We can skip it if the session hasn't been started yet.
PrepareCompilations(currentSolution, projectPath, cancellationToken);
}
@@ -232,33 +224,13 @@ private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, Project
return runningProject;
}
- private async ValueTask> GetAggregateCapabilitiesAsync()
+ private ImmutableArray GetAggregateCapabilities()
{
- var capabilities = _currentAggregateCapabilities;
- if (!capabilities.IsDefault)
- {
- return capabilities;
- }
-
- while (true)
- {
- var runningProjects = _runningProjects;
- var capabilitiesByProvider = await Task.WhenAll(runningProjects.SelectMany(p => p.Value).Select(p => p.CapabilityProvider));
- capabilities = capabilitiesByProvider.SelectMany(c => c).Distinct(StringComparer.Ordinal).ToImmutableArray();
-
- lock (_runningProjectsAndUpdatesGuard)
- {
- if (runningProjects != _runningProjects)
- {
- // Another process has been launched while we were retrieving capabilities, query the providers again.
- // The providers cache the result so we won't be calling into the respective processes again.
- continue;
- }
-
- _currentAggregateCapabilities = capabilities;
- break;
- }
- }
+ var capabilities = _runningProjects
+ .SelectMany(p => p.Value)
+ .SelectMany(p => p.Capabilities)
+ .Distinct(StringComparer.Ordinal)
+ .ToImmutableArray();
_reporter.Verbose($"Hot reload capabilities: {string.Join(" ", capabilities)}.", emoji: "🔥");
return capabilities;
@@ -277,7 +249,7 @@ private static void PrepareCompilations(Solution solution, string projectPath, C
}
}
- public async ValueTask<(ImmutableDictionary projectsToRebuild, ImmutableArray terminatedProjects)> HandleFileChangesAsync(
+ public async ValueTask<(ImmutableDictionary projectsToRebuild, ImmutableArray terminatedProjects)> HandleManagedCodeChangesAsync(
Func, CancellationToken, Task> restartPrompt,
CancellationToken cancellationToken)
{
@@ -337,7 +309,7 @@ await ForEachProjectAsync(projectsToUpdate, async (runningProject, cancellationT
try
{
using var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(runningProject.ProcessExitedSource.Token, cancellationToken);
- var applySucceded = await runningProject.DeltaApplier.Apply(updates.ProjectUpdates, processCommunicationCancellationSource.Token) != ApplyStatus.Failed;
+ var applySucceded = await runningProject.DeltaApplier.ApplyManagedCodeUpdates(updates.ProjectUpdates, processCommunicationCancellationSource.Token) != ApplyStatus.Failed;
if (applySucceded)
{
runningProject.Reporter.Report(MessageDescriptor.HotReloadSucceeded);
@@ -364,7 +336,7 @@ private async ValueTask DisplayResultsAsync(WatchHotReloadService.Updates update
switch (updates.Status)
{
case ModuleUpdateStatus.None:
- _reporter.Report(MessageDescriptor.NoHotReloadChangesToApply);
+ _reporter.Report(MessageDescriptor.NoCSharpChangesToApply);
break;
case ModuleUpdateStatus.Ready:
@@ -465,6 +437,102 @@ await ForEachProjectAsync(
cancellationToken);
}
+ public async ValueTask HandleStaticAssetChangesAsync(IReadOnlyList files, ProjectNodeMap projectMap, CancellationToken cancellationToken)
+ {
+ var allFilesHandled = true;
+
+ var updates = new Dictionary>();
+
+ foreach (var changedFile in files)
+ {
+ var file = changedFile.Item;
+
+ if (file.StaticWebAssetPath is null)
+ {
+ allFilesHandled = false;
+ continue;
+ }
+
+ foreach (var containingProjectPath in file.ContainingProjectPaths)
+ {
+ if (!projectMap.Map.TryGetValue(containingProjectPath, out var containingProjectNodes))
+ {
+ // Shouldn't happen.
+ _reporter.Warn($"Project '{containingProjectPath}' not found in the project graph.");
+ continue;
+ }
+
+ foreach (var containingProjectNode in containingProjectNodes)
+ {
+ foreach (var referencingProjectNode in new[] { containingProjectNode }.GetTransitivelyReferencingProjects())
+ {
+ if (TryGetRunningProject(referencingProjectNode.ProjectInstance.FullPath, out var runningProjects))
+ {
+ foreach (var runningProject in runningProjects)
+ {
+ if (!updates.TryGetValue(runningProject, out var updatesPerRunningProject))
+ {
+ updates.Add(runningProject, updatesPerRunningProject = []);
+ }
+
+ updatesPerRunningProject.Add((file.FilePath, file.StaticWebAssetPath, containingProjectNode));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (updates.Count == 0)
+ {
+ return allFilesHandled;
+ }
+
+ var tasks = updates.Select(async entry =>
+ {
+ var (runningProject, assets) = entry;
+
+ if (runningProject.BrowserRefreshServer != null)
+ {
+ await runningProject.BrowserRefreshServer.UpdateStaticAssetsAsync(assets.Select(a => a.relativeUrl), cancellationToken);
+ }
+ else
+ {
+ var updates = new List();
+
+ foreach (var (filePath, relativeUrl, containingProject) in assets)
+ {
+ byte[] content;
+ try
+ {
+ content = await File.ReadAllBytesAsync(filePath, cancellationToken);
+ }
+ catch (Exception e)
+ {
+ _reporter.Error(e.Message);
+ continue;
+ }
+
+ updates.Add(new StaticAssetUpdate(
+ relativePath: relativeUrl,
+ assemblyName: containingProject.GetAssemblyName(),
+ content: content,
+ isApplicationProject: containingProject == runningProject.ProjectNode));
+
+ _reporter.Verbose($"Sending static file update request for asset '{relativeUrl}'.");
+ }
+
+ await runningProject.DeltaApplier.ApplyStaticAssetUpdates([.. updates], cancellationToken);
+ }
+ });
+
+ await Task.WhenAll(tasks).WaitAsync(cancellationToken);
+
+ _reporter.Output("Hot reload of static files succeeded.", emoji: "🔥");
+
+ return allFilesHandled;
+ }
+
///
/// Terminates all processes launched for projects with ,
/// or all running non-root project processes if is null.
@@ -536,22 +604,21 @@ private void UpdateRunningProjects(Func> TerminateRunningProjects(IEnumerable projects, CancellationToken cancellationToken)
+ public bool TryGetRunningProject(string projectPath, out ImmutableArray projects)
{
- // cancel first, this will cause the process tasks to complete:
- foreach (var project in projects)
+ lock (_runningProjectsAndUpdatesGuard)
{
- project.ProcessTerminationSource.Cancel();
+ return _runningProjects.TryGetValue(projectPath, out projects);
}
+ }
+ private async ValueTask> TerminateRunningProjects(IEnumerable projects, CancellationToken cancellationToken)
+ {
// wait for all tasks to complete:
- return await Task.WhenAll(projects.Select(p => p.RunningProcess)).WaitAsync(cancellationToken);
+ return await Task.WhenAll(projects.Select(p => p.TerminateAsync(_shutdownCancellationToken).AsTask())).WaitAsync(cancellationToken);
}
private static Task ForEachProjectAsync(ImmutableDictionary> projects, Func action, CancellationToken cancellationToken)
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
index a3228802a53e..5d7f507984cc 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/DefaultDeltaApplier.cs
@@ -15,7 +15,7 @@ internal sealed class DefaultDeltaApplier(IReporter reporter) : SingleProcessDel
{
private Task>? _capabilitiesTask;
private NamedPipeServerStream? _pipe;
- private bool _changeApplicationErrorFailed;
+ private bool _managedCodeUpdateFailedOrCancelled;
public override void CreateConnection(string namedPipeName, CancellationToken cancellationToken)
{
@@ -36,7 +36,7 @@ async Task> ConnectAsync()
// When the client connects, the first payload it sends is the initialization payload which includes the apply capabilities.
- var capabilities = (await ClientInitializationPayload.ReadAsync(_pipe, cancellationToken)).Capabilities;
+ var capabilities = (await ClientInitializationResponse.ReadAsync(_pipe, cancellationToken)).Capabilities;
Reporter.Verbose($"Capabilities: '{capabilities}'");
return [.. capabilities.Split(' ')];
}
@@ -66,7 +66,10 @@ public override Task> GetApplyUpdateCapabilitiesAsync(Can
// Should only be called after CreateConnection
=> _capabilitiesTask ?? throw new InvalidOperationException();
- public override async Task Apply(ImmutableArray updates, CancellationToken cancellationToken)
+ private ResponseLoggingLevel ResponseLoggingLevel
+ => Reporter.IsVerbose ? ResponseLoggingLevel.Verbose : ResponseLoggingLevel.WarningsAndErrors;
+
+ public override async Task ApplyManagedCodeUpdates(ImmutableArray updates, CancellationToken cancellationToken)
{
// Should only be called after CreateConnection
Debug.Assert(_capabilitiesTask != null);
@@ -74,7 +77,7 @@ public override async Task Apply(ImmutableArray Apply(ImmutableArray new UpdateDelta(
+ var request = new ManagedCodeUpdateRequest(
+ deltas: [.. applicableUpdates.Select(update => new UpdateDelta(
update.ModuleId,
- metadataDelta: update.MetadataDelta.ToArray(),
- ilDelta: update.ILDelta.ToArray(),
- pdbDelta: update.PdbDelta.ToArray(),
- update.UpdatedTypes.ToArray())).ToArray(),
- responseLoggingLevel: Reporter.IsVerbose ? ResponseLoggingLevel.Verbose : ResponseLoggingLevel.WarningsAndErrors);
+ metadataDelta: [.. update.MetadataDelta],
+ ilDelta: [.. update.ILDelta],
+ pdbDelta: [.. update.PdbDelta],
+ updatedTypes: [.. update.UpdatedTypes]))],
+ responseLoggingLevel: ResponseLoggingLevel);
var success = false;
var canceled = false;
try
{
- await payload.WriteAsync(_pipe, cancellationToken);
- await _pipe.FlushAsync(cancellationToken);
- success = await ReceiveApplyUpdateResult(cancellationToken);
+ success = await SendAndReceiveUpdate(request, cancellationToken);
}
catch (OperationCanceledException) when (!(canceled = true))
{
+ // unreachable
}
catch (Exception e) when (e is not OperationCanceledException)
{
+ success = false;
Reporter.Error($"Change failed to apply (error: '{e.Message}'). Further changes won't be applied to this process.");
Reporter.Verbose($"Exception stack trace: {e.StackTrace}", "❌");
}
@@ -121,7 +124,7 @@ public override async Task Apply(ImmutableArray Apply(ImmutableArray ReceiveApplyUpdateResult(CancellationToken cancellationToken)
+ public async override Task ApplyStaticAssetUpdates(ImmutableArray updates, CancellationToken cancellationToken)
{
- Debug.Assert(_pipe != null);
+ var appliedUpdateCount = 0;
- var status = ArrayPool.Shared.Rent(1);
- try
+ foreach (var update in updates)
{
- var statusBytesRead = await _pipe.ReadAsync(status, offset: 0, count: 1, cancellationToken);
- if (statusBytesRead != 1 || status[0] != UpdatePayload.ApplySuccessValue)
+ var request = new StaticAssetUpdateRequest(
+ update.AssemblyName,
+ update.RelativePath,
+ update.Content,
+ update.IsApplicationProject,
+ ResponseLoggingLevel);
+
+ var success = false;
+ var canceled = false;
+ try
+ {
+ success = await SendAndReceiveUpdate(request, cancellationToken);
+ }
+ catch (OperationCanceledException) when (!(canceled = true))
{
- var message = (statusBytesRead == 0) ? "received no data" : $"received status 0x{status[0]:x2}";
- Reporter.Error($"Change failed to apply ({message}). Further changes won't be applied to this process.");
- return false;
+ // unreachable
+ }
+ catch (Exception e) when (e is not OperationCanceledException)
+ {
+ success = false;
+ Reporter.Error($"Change failed to apply (error: '{e.Message}').");
+ Reporter.Verbose($"Exception stack trace: {e.StackTrace}", "❌");
+ }
+ finally
+ {
+ if (canceled)
+ {
+ Reporter.Verbose("Change application cancelled.", "🔥");
+ }
}
- await foreach (var (message, severity) in UpdatePayload.ReadLogAsync(_pipe, cancellationToken))
+ if (success)
{
- ReportLogEntry(Reporter, message, severity);
+ appliedUpdateCount++;
}
+ }
+
+ Reporter.Report(MessageDescriptor.UpdatesApplied, appliedUpdateCount, updates.Length);
+
+ return
+ (appliedUpdateCount == 0) ? ApplyStatus.Failed :
+ (appliedUpdateCount < updates.Length) ? ApplyStatus.SomeChangesApplied : ApplyStatus.AllChangesApplied;
+ }
- return true;
+ private async ValueTask SendAndReceiveUpdate(TRequest request, CancellationToken cancellationToken)
+ where TRequest : IUpdateRequest
+ {
+ // Should not be disposed:
+ Debug.Assert(_pipe != null);
+
+ await _pipe.WriteAsync((byte)request.Type, cancellationToken);
+ await request.WriteAsync(_pipe, cancellationToken);
+ await _pipe.FlushAsync(cancellationToken);
+
+ var (success, log) = await UpdateResponse.ReadAsync(_pipe, cancellationToken);
+
+ await foreach (var (message, severity) in log)
+ {
+ ReportLogEntry(Reporter, message, severity);
}
- finally
+
+ return success;
+ }
+
+ public override async Task InitialUpdatesApplied(CancellationToken cancellationToken)
+ {
+ // Should only be called after CreateConnection
+ Debug.Assert(_capabilitiesTask != null);
+
+ // Should not be disposed:
+ Debug.Assert(_pipe != null);
+
+ if (_managedCodeUpdateFailedOrCancelled)
{
- ArrayPool.Shared.Return(status);
+ return;
}
+
+ await _pipe.WriteAsync((byte)RequestType.InitialUpdatesCompleted, cancellationToken);
+ await _pipe.FlushAsync(cancellationToken);
}
private void DisposePipe()
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs
index 78f6a02d01a6..852c9295e949 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/DeltaApplier.cs
@@ -24,7 +24,10 @@ internal abstract class DeltaApplier(IReporter reporter) : IDisposable
public abstract Task> GetApplyUpdateCapabilitiesAsync(CancellationToken cancellationToken);
- public abstract Task Apply(ImmutableArray updates, CancellationToken cancellationToken);
+ public abstract Task ApplyManagedCodeUpdates(ImmutableArray updates, CancellationToken cancellationToken);
+ public abstract Task ApplyStaticAssetUpdates(ImmutableArray updates, CancellationToken cancellationToken);
+
+ public abstract Task InitialUpdatesApplied(CancellationToken cancellationToken);
public abstract void Dispose();
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncherFactory.cs b/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncherFactory.cs
index 431e79830ae3..93d69f69db59 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncherFactory.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/IRuntimeProcessLauncherFactory.cs
@@ -12,5 +12,5 @@ namespace Microsoft.DotNet.Watch;
///
internal interface IRuntimeProcessLauncherFactory
{
- public IRuntimeProcessLauncher? TryCreate(ProjectGraphNode projectNode, ProjectLauncher projectLauncher, IReadOnlyList buildArguments);
+ public IRuntimeProcessLauncher? TryCreate(ProjectGraphNode projectNode, ProjectLauncher projectLauncher, ProjectOptions hostProjectOptions);
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/IStaticAssetChangeApplierProvider.cs b/src/BuiltInTools/dotnet-watch/HotReload/IStaticAssetChangeApplierProvider.cs
new file mode 100644
index 000000000000..ad2f9491cc70
--- /dev/null
+++ b/src/BuiltInTools/dotnet-watch/HotReload/IStaticAssetChangeApplierProvider.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Build.Graph;
+
+namespace Microsoft.DotNet.Watch;
+
+internal interface IStaticAssetChangeApplierProvider
+{
+ bool TryGetApplier(ProjectGraphNode projectNode, [NotNullWhen(true)] out IStaticAssetChangeApplier? applier);
+}
+
+internal interface IStaticAssetChangeApplier
+{
+}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/NamedPipeContract.cs b/src/BuiltInTools/dotnet-watch/HotReload/NamedPipeContract.cs
deleted file mode 100644
index d95c3e8e5128..000000000000
--- a/src/BuiltInTools/dotnet-watch/HotReload/NamedPipeContract.cs
+++ /dev/null
@@ -1,431 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Buffers;
-using System.Buffers.Binary;
-using System.Runtime.CompilerServices;
-using Microsoft.DotNet.HotReload;
-
-namespace Microsoft.DotNet.Watch
-{
- internal enum PayloadType
- {
- ManagedCodeUpdate = 1,
- StaticAssetUpdate = 2,
- InitialUpdatesCompleted = 3,
- }
-
- internal readonly struct UpdatePayload(IReadOnlyList deltas, ResponseLoggingLevel responseLoggingLevel)
- {
- public const byte ApplySuccessValue = 0;
-
- private const byte Version = 4;
-
- public IReadOnlyList Deltas { get; } = deltas;
- public ResponseLoggingLevel ResponseLoggingLevel { get; } = responseLoggingLevel;
-
- ///
- /// Called by the dotnet-watch.
- ///
- public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken)
- {
- await stream.WriteAsync(Version, cancellationToken);
- await stream.WriteAsync(Deltas.Count, cancellationToken);
-
- foreach (var delta in Deltas)
- {
- await stream.WriteAsync(delta.ModuleId, cancellationToken);
- await stream.WriteByteArrayAsync(delta.MetadataDelta, cancellationToken);
- await stream.WriteByteArrayAsync(delta.ILDelta, cancellationToken);
- await stream.WriteByteArrayAsync(delta.PdbDelta, cancellationToken);
- await stream.WriteAsync(delta.UpdatedTypes, cancellationToken);
- }
-
- await stream.WriteAsync((byte)ResponseLoggingLevel, cancellationToken);
- }
-
- ///
- /// Called by the dotnet-watch.
- ///
- public static async ValueTask WriteLogAsync(Stream stream, IReadOnlyCollection<(string message, AgentMessageSeverity severity)> log, CancellationToken cancellationToken)
- {
- await stream.WriteAsync(log.Count, cancellationToken);
-
- foreach (var (message, severity) in log)
- {
- await stream.WriteAsync(message, cancellationToken);
- await stream.WriteAsync((byte)severity, cancellationToken);
- }
- }
-
- ///
- /// Called by delta applier.
- ///
- public static async ValueTask ReadAsync(Stream stream, CancellationToken cancellationToken)
- {
- var version = await stream.ReadByteAsync(cancellationToken);
- if (version != Version)
- {
- throw new NotSupportedException($"Unsupported version {version}.");
- }
-
- var count = await stream.ReadInt32Async(cancellationToken);
-
- var deltas = new UpdateDelta[count];
- for (var i = 0; i < count; i++)
- {
- var moduleId = await stream.ReadGuidAsync(cancellationToken);
- var metadataDelta = await stream.ReadByteArrayAsync(cancellationToken);
- var ilDelta = await stream.ReadByteArrayAsync(cancellationToken);
- var pdbDelta = await stream.ReadByteArrayAsync(cancellationToken);
- var updatedTypes = await stream.ReadIntArrayAsync(cancellationToken);
-
- deltas[i] = new UpdateDelta(moduleId, metadataDelta: metadataDelta, ilDelta: ilDelta, pdbDelta: pdbDelta, updatedTypes);
- }
-
- var responseLoggingLevel = (ResponseLoggingLevel)await stream.ReadByteAsync(cancellationToken);
- return new UpdatePayload(deltas, responseLoggingLevel: responseLoggingLevel);
- }
-
- ///
- /// Called by delta applier.
- ///
- public static async IAsyncEnumerable<(string message, AgentMessageSeverity severity)> ReadLogAsync(Stream stream, [EnumeratorCancellation] CancellationToken cancellationToken)
- {
- var entryCount = await stream.ReadInt32Async(cancellationToken);
-
- for (var i = 0; i < entryCount; i++)
- {
- var message = await stream.ReadStringAsync(cancellationToken);
- var severity = (AgentMessageSeverity)await stream.ReadByteAsync(cancellationToken);
- yield return (message, severity);
- }
- }
- }
-
- internal readonly struct ClientInitializationPayload(string capabilities)
- {
- private const byte Version = 0;
-
- public string Capabilities { get; } = capabilities;
-
- ///
- /// Called by delta applier.
- ///
- public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken)
- {
- await stream.WriteAsync(Version, cancellationToken);
- await stream.WriteAsync(Capabilities, cancellationToken);
- }
-
- ///
- /// Called by dotnet-watch.
- ///
- public static async ValueTask ReadAsync(Stream stream, CancellationToken cancellationToken)
- {
- var version = await stream.ReadByteAsync(cancellationToken);
- if (version != Version)
- {
- throw new NotSupportedException($"Unsupported version {version}.");
- }
-
- var capabilities = await stream.ReadStringAsync(cancellationToken);
- return new ClientInitializationPayload(capabilities);
- }
- }
-
- internal readonly struct StaticAssetPayload(
- string assemblyName,
- string relativePath,
- byte[] contents,
- bool isApplicationProject)
- {
- private const byte Version = 1;
-
- public string AssemblyName { get; } = assemblyName;
- public bool IsApplicationProject { get; } = isApplicationProject;
- public string RelativePath { get; } = relativePath;
- public byte[] Contents { get; } = contents;
-
- public async ValueTask WriteAsync(Stream stream, CancellationToken cancellationToken)
- {
- await stream.WriteAsync(Version, cancellationToken);
- await stream.WriteAsync(AssemblyName, cancellationToken);
- await stream.WriteAsync(IsApplicationProject, cancellationToken);
- await stream.WriteAsync(RelativePath, cancellationToken);
- await stream.WriteByteArrayAsync(Contents, cancellationToken);
- }
-
- public static async ValueTask ReadAsync(Stream stream, CancellationToken cancellationToken)
- {
- var version = await stream.ReadByteAsync(cancellationToken);
- if (version != Version)
- {
- throw new NotSupportedException($"Unsupported version {version}.");
- }
-
- var assemblyName = await stream.ReadStringAsync(cancellationToken);
- var isAppProject = await stream.ReadBooleanAsync(cancellationToken);
- var relativePath = await stream.ReadStringAsync(cancellationToken);
- var contents = await stream.ReadByteArrayAsync(cancellationToken);
-
- return new StaticAssetPayload(
- assemblyName: assemblyName,
- relativePath: relativePath,
- contents: contents,
- isApplicationProject: isAppProject);
- }
- }
-
- ///
- /// Implements async read/write helpers that provide functionality of and .
- /// See https://github.com/dotnet/runtime/issues/17229
- ///
- internal static class StreamExtesions
- {
- public static ValueTask WriteAsync(this Stream stream, bool value, CancellationToken cancellationToken)
- => WriteAsync(stream, (byte)(value ? 1 : 0), cancellationToken);
-
- public static async ValueTask WriteAsync(this Stream stream, byte value, CancellationToken cancellationToken)
- {
- var size = sizeof(byte);
- var buffer = ArrayPool.Shared.Rent(minimumLength: size);
- try
- {
- buffer[0] = value;
- await stream.WriteAsync(buffer, offset: 0, count: size, cancellationToken);
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
-
- public static async ValueTask WriteAsync(this Stream stream, int value, CancellationToken cancellationToken)
- {
- var size = sizeof(int);
- var buffer = ArrayPool.Shared.Rent(minimumLength: size);
- try
- {
- BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
- await stream.WriteAsync(buffer, offset: 0, count: size, cancellationToken);
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
-
- public static ValueTask WriteAsync(this Stream stream, Guid value, CancellationToken cancellationToken)
- => stream.WriteAsync(value.ToByteArray(), cancellationToken);
-
- public static async ValueTask WriteByteArrayAsync(this Stream stream, byte[] value, CancellationToken cancellationToken)
- {
- await stream.WriteAsync(value.Length, cancellationToken);
- await stream.WriteAsync(value, cancellationToken);
- }
-
- public static async ValueTask WriteAsync(this Stream stream, int[] value, CancellationToken cancellationToken)
- {
- var size = sizeof(int) * (value.Length + 1);
- var buffer = ArrayPool.Shared.Rent(minimumLength: size);
- try
- {
- BinaryPrimitives.WriteInt32LittleEndian(buffer, value.Length);
- for (int i = 0; i < value.Length; i++)
- {
- BinaryPrimitives.WriteInt32LittleEndian(buffer.AsSpan((i + 1) * sizeof(int), sizeof(int)), value[i]);
- }
-
- await stream.WriteAsync(buffer, offset: 0, count: size, cancellationToken);
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
-
- public static async ValueTask WriteAsync(this Stream stream, string value, CancellationToken cancellationToken)
- {
- var bytes = Encoding.UTF8.GetBytes(value);
- await stream.Write7BitEncodedIntAsync(bytes.Length, cancellationToken);
- await stream.WriteAsync(bytes, cancellationToken);
- }
-
- public static async ValueTask Write7BitEncodedIntAsync(this Stream stream, int value, CancellationToken cancellationToken)
- {
- uint uValue = (uint)value;
-
- while (uValue > 0x7Fu)
- {
- await stream.WriteAsync((byte)(uValue | ~0x7Fu), cancellationToken);
- uValue >>= 7;
- }
-
- await stream.WriteAsync((byte)uValue, cancellationToken);
- }
-
- public static async ValueTask ReadBooleanAsync(this Stream stream, CancellationToken cancellationToken)
- => await stream.ReadByteAsync(cancellationToken) != 0;
-
- public static async ValueTask ReadByteAsync(this Stream stream, CancellationToken cancellationToken)
- {
- int size = sizeof(byte);
- var buffer = ArrayPool.Shared.Rent(minimumLength: size);
- try
- {
- await ReadExactlyAsync(stream, buffer.AsMemory(0, size), cancellationToken);
- return buffer[0];
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
-
- public static async ValueTask ReadInt32Async(this Stream stream, CancellationToken cancellationToken)
- {
- int size = sizeof(int);
- var buffer = ArrayPool.Shared.Rent(minimumLength: size);
- try
- {
- await ReadExactlyAsync(stream, buffer.AsMemory(0, size), cancellationToken);
- return BinaryPrimitives.ReadInt32LittleEndian(buffer);
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
-
- public static async ValueTask ReadGuidAsync(this Stream stream, CancellationToken cancellationToken)
- {
- const int size = 16;
- var buffer = ArrayPool.Shared.Rent(minimumLength: size);
- try
- {
- await ReadExactlyAsync(stream, buffer.AsMemory(0, size), cancellationToken);
- return new Guid(buffer.AsSpan(0, size));
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
-
- public static async ValueTask ReadByteArrayAsync(this Stream stream, CancellationToken cancellationToken)
- {
- var count = await stream.ReadInt32Async(cancellationToken);
- if (count == 0)
- {
- return [];
- }
-
- var bytes = new byte[count];
- await ReadExactlyAsync(stream, bytes, cancellationToken);
- return bytes;
- }
-
- public static async ValueTask ReadIntArrayAsync(this Stream stream, CancellationToken cancellationToken)
- {
- var count = await stream.ReadInt32Async(cancellationToken);
- if (count == 0)
- {
- return [];
- }
-
- var result = new int[count];
- int size = count * sizeof(int);
- var buffer = ArrayPool.Shared.Rent(minimumLength: size);
- try
- {
- await ReadExactlyAsync(stream, buffer.AsMemory(0, size), cancellationToken);
-
- for (var i = 0; i < count; i++)
- {
- result[i] = BinaryPrimitives.ReadInt32LittleEndian(buffer.AsSpan(i * sizeof(int)));
- }
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
-
- return result;
- }
-
- public static async ValueTask ReadStringAsync(this Stream stream, CancellationToken cancellationToken)
- {
- int size = await stream.Read7BitEncodedIntAsync(cancellationToken);
- if (size < 0)
- {
- throw new InvalidDataException();
- }
-
- if (size == 0)
- {
- return string.Empty;
- }
-
- var buffer = ArrayPool.Shared.Rent(minimumLength: size);
- try
- {
- await ReadExactlyAsync(stream, buffer.AsMemory(0, size), cancellationToken);
- return Encoding.UTF8.GetString(buffer.AsSpan(0, size));
- }
- finally
- {
- ArrayPool.Shared.Return(buffer);
- }
- }
-
- public static async ValueTask Read7BitEncodedIntAsync(this Stream stream, CancellationToken cancellationToken)
- {
- const int MaxBytesWithoutOverflow = 4;
-
- uint result = 0;
- byte b;
-
- for (int shift = 0; shift < MaxBytesWithoutOverflow * 7; shift += 7)
- {
- b = await stream.ReadByteAsync(cancellationToken);
- result |= (b & 0x7Fu) << shift;
-
- if (b <= 0x7Fu)
- {
- return (int)result;
- }
- }
-
- // Read the 5th byte. Since we already read 28 bits,
- // the value of this byte must fit within 4 bits (32 - 28),
- // and it must not have the high bit set.
-
- b = await stream.ReadByteAsync(cancellationToken);
- if (b > 0b_1111u)
- {
- throw new InvalidDataException();
- }
-
- result |= (uint)b << (MaxBytesWithoutOverflow * 7);
- return (int)result;
- }
-
- private static async ValueTask ReadExactlyAsync(this Stream stream, Memory buffer, CancellationToken cancellationToken)
- {
- int totalRead = 0;
- while (totalRead < buffer.Length)
- {
- int read = await stream.ReadAsync(buffer.Slice(totalRead), cancellationToken).ConfigureAwait(false);
- if (read == 0)
- {
- throw new EndOfStreamException();
- }
-
- totalRead += read;
- }
-
- return totalRead;
- }
- }
-}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
index 46a33b2b301d..c68073001705 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
@@ -1,8 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Diagnostics;
using System.Globalization;
using Microsoft.Build.Graph;
+using Microsoft.DotNet.HotReload;
namespace Microsoft.DotNet.Watch;
@@ -53,22 +55,17 @@ public EnvironmentOptions EnvironmentOptions
{
Executable = EnvironmentOptions.MuxerPath,
WorkingDirectory = projectOptions.WorkingDirectory,
- OnOutput = onOutput,
- Arguments = [projectOptions.Command, "--no-build", .. projectOptions.CommandArguments]
+ OnOutput = onOutput
};
var environmentBuilder = EnvironmentVariablesBuilder.FromCurrentEnvironment();
var namedPipeName = Guid.NewGuid().ToString();
- // Directives:
-
- // Variables:
-
foreach (var (name, value) in projectOptions.LaunchEnvironmentVariables)
{
// ignore dotnet-watch reserved variables -- these shouldn't be set by the project
if (name.Equals(EnvironmentVariables.Names.AspNetCoreHostingStartupAssemblies, StringComparison.OrdinalIgnoreCase) ||
- name.Equals(EnvironmentVariables.Names.DotnetStartupHooks, StringComparison.OrdinalIgnoreCase))
+ name.Equals(EnvironmentVariables.Names.DotNetStartupHooks, StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -85,30 +82,38 @@ public EnvironmentOptions EnvironmentOptions
// expect DOTNET_MODIFIABLE_ASSEMBLIES to be set in the blazor-devserver process, even though we are not performing Hot Reload in this process.
// The value is converted to DOTNET-MODIFIABLE-ASSEMBLIES header, which is in turn converted back to environment variable in Mono browser runtime loader:
// https://github.com/dotnet/runtime/blob/342936c5a88653f0f622e9d6cb727a0e59279b31/src/mono/browser/runtime/loader/config.ts#L330
- environmentBuilder.SetDirective(EnvironmentVariables.Names.DotnetModifiableAssemblies, "debug");
+ environmentBuilder.SetVariable(EnvironmentVariables.Names.DotNetModifiableAssemblies, "debug");
if (injectDeltaApplier)
{
- environmentBuilder.DotNetStartupHookDirective.Add(DeltaApplier.StartupHookPath);
- environmentBuilder.SetDirective(EnvironmentVariables.Names.DotnetWatchHotReloadNamedPipeName, namedPipeName);
-
- // Do not ask agent to log to stdout until https://github.com/dotnet/sdk/issues/40484 is fixed.
- // For now we need to set the env variable explicitly when we need to diagnose issue with the agent.
- // Build targets might launch a process and read it's stdout. If the agent is loaded into such process and starts logging
- // to stdout it might interfere with the expected output.
- //if (context.Options.Verbose)
- //{
- // environmentBuilder.SetVariable(EnvironmentVariables.Names.HotReloadDeltaClientLogMessages, "1");
- //}
-
- // TODO: workaround for https://github.com/dotnet/sdk/issues/40484
- var targetPath = projectNode.ProjectInstance.GetPropertyValue("RunCommand");
- environmentBuilder.SetVariable(EnvironmentVariables.Names.DotnetWatchHotReloadTargetProcessPath, targetPath);
- Reporter.Verbose($"Target process is '{targetPath}'");
+ // HotReload startup hook should be loaded before any other startup hooks:
+ environmentBuilder.DotNetStartupHooks.Insert(0, DeltaApplier.StartupHookPath);
+
+ environmentBuilder.SetVariable(EnvironmentVariables.Names.DotNetWatchHotReloadNamedPipeName, namedPipeName);
+
+ if (context.Options.Verbose)
+ {
+ environmentBuilder.SetVariable(EnvironmentVariables.Names.HotReloadDeltaClientLogMessages, "1");
+ }
+ }
+
+ var browserRefreshServer = await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectNode, processSpec, environmentBuilder, projectOptions, cancellationToken);
+
+ var arguments = new List()
+ {
+ projectOptions.Command,
+ "--no-build"
+ };
+
+ foreach (var (name, value) in environmentBuilder.GetEnvironment())
+ {
+ arguments.Add("-e");
+ arguments.Add($"{name}={value}");
}
- var browserRefreshServer = await browserConnector.LaunchOrRefreshBrowserAsync(projectNode, processSpec, environmentBuilder, projectOptions, cancellationToken);
- environmentBuilder.ConfigureProcess(processSpec);
+ arguments.AddRange(projectOptions.CommandArguments);
+
+ processSpec.Arguments = arguments;
var processReporter = new ProjectSpecificReporter(projectNode, Reporter);
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs b/src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs
index 49d8268cf0cf..e24717b23d27 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/RunningProject.cs
@@ -12,6 +12,7 @@ namespace Microsoft.DotNet.Watch
internal sealed class RunningProject(
ProjectGraphNode projectNode,
ProjectOptions options,
+ EnvironmentOptions environmentOptions,
DeltaApplier deltaApplier,
IReporter reporter,
BrowserRefreshServer? browserRefreshServer,
@@ -21,13 +22,13 @@ internal sealed class RunningProject(
CancellationTokenSource processTerminationSource,
RestartOperation restartOperation,
IReadOnlyList disposables,
- Task> capabilityProvider) : IDisposable
+ ImmutableArray capabilities) : IDisposable
{
public readonly ProjectGraphNode ProjectNode = projectNode;
public readonly ProjectOptions Options = options;
public readonly BrowserRefreshServer? BrowserRefreshServer = browserRefreshServer;
public readonly DeltaApplier DeltaApplier = deltaApplier;
- public readonly Task> CapabilityProvider = capabilityProvider;
+ public readonly ImmutableArray Capabilities = capabilities;
public readonly IReporter Reporter = reporter;
public readonly Task RunningProcess = runningProcess;
public readonly int ProcessId = processId;
@@ -68,5 +69,24 @@ public async ValueTask WaitForProcessRunningAsync(CancellationToken cancellation
{
await DeltaApplier.WaitForProcessRunningAsync(cancellationToken);
}
+
+ public async ValueTask TerminateAsync(CancellationToken shutdownCancellationToken)
+ {
+ if (shutdownCancellationToken.IsCancellationRequested)
+ {
+ // Ctrl+C sent, wait for the process to exit
+ try
+ {
+ _ = await RunningProcess.WaitAsync(environmentOptions.ProcessCleanupTimeout, CancellationToken.None);
+ }
+ catch (TimeoutException)
+ {
+ // nop
+ }
+ }
+
+ ProcessTerminationSource.Cancel();
+ return await RunningProcess;
+ }
}
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/ScopedCssFileHandler.cs b/src/BuiltInTools/dotnet-watch/HotReload/ScopedCssFileHandler.cs
index d9f241eb0fab..6c30ef91c0d9 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/ScopedCssFileHandler.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/ScopedCssFileHandler.cs
@@ -4,7 +4,6 @@
using Microsoft.Build.Framework;
using Microsoft.Build.Graph;
-using Microsoft.TemplateEngine.Utils;
namespace Microsoft.DotNet.Watch
{
@@ -81,12 +80,13 @@ public async ValueTask HandleFileChangesAsync(IReadOnlyList files,
{
if (browserConnector.TryGetRefreshServer(projectNode, out var browserRefreshServer))
{
- reporter.Verbose($"[{projectNode.GetDisplayName()}] Refreshing browser.");
- await HandleBrowserRefresh(browserRefreshServer, projectNode.ProjectInstance.FullPath, cancellationToken);
- }
- else
- {
- reporter.Verbose($"[{projectNode.GetDisplayName()}] No refresh server.");
+ // We'd like an accurate scoped css path, but this needs a lot of work to wire-up now.
+ // We'll handle this as part of https://github.com/dotnet/aspnetcore/issues/31217.
+ // For now, we'll make it look like some css file which would cause JS to update a
+ // single file if it's from the current project, or all locally hosted css files if it's a file from
+ // referenced project.
+ var relativeUrl = Path.GetFileNameWithoutExtension(projectNode.ProjectInstance.FullPath) + ".css";
+ await browserRefreshServer.UpdateStaticAssetsAsync([relativeUrl], cancellationToken);
}
});
@@ -107,24 +107,5 @@ public async ValueTask HandleFileChangesAsync(IReadOnlyList files,
reporter.Output("Hot reload of scoped css failed.", emoji: "🔥");
}
}
-
- private static async Task HandleBrowserRefresh(BrowserRefreshServer browserRefreshServer, string containingProjectPath, CancellationToken cancellationToken)
- {
- // We'd like an accurate scoped css path, but this needs a lot of work to wire-up now.
- // We'll handle this as part of https://github.com/dotnet/aspnetcore/issues/31217.
- // For now, we'll make it look like some css file which would cause JS to update a
- // single file if it's from the current project, or all locally hosted css files if it's a file from
- // referenced project.
- var cssFilePath = Path.GetFileNameWithoutExtension(containingProjectPath) + ".css";
- var message = new UpdateStaticFileMessage { Path = cssFilePath };
- await browserRefreshServer.SendJsonMessageAsync(message, cancellationToken);
- }
-
- private readonly struct UpdateStaticFileMessage
- {
- public string Type => "UpdateStaticFile";
-
- public string Path { get; init; }
- }
}
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/StaticAssetUpdate.cs b/src/BuiltInTools/dotnet-watch/HotReload/StaticAssetUpdate.cs
new file mode 100644
index 000000000000..8a2438ac1ea1
--- /dev/null
+++ b/src/BuiltInTools/dotnet-watch/HotReload/StaticAssetUpdate.cs
@@ -0,0 +1,12 @@
+// 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;
+
+internal readonly struct StaticAssetUpdate(string relativePath, string assemblyName, byte[] content, bool isApplicationProject)
+{
+ public string RelativePath { get; } = relativePath;
+ public string AssemblyName { get; } = assemblyName;
+ public byte[] Content { get; } = content;
+ public bool IsApplicationProject { get; } = isApplicationProject;
+}
diff --git a/src/BuiltInTools/dotnet-watch/HotReload/StaticFileHandler.cs b/src/BuiltInTools/dotnet-watch/HotReload/StaticFileHandler.cs
index 05fde9ab4001..c89ee64fb86a 100644
--- a/src/BuiltInTools/dotnet-watch/HotReload/StaticFileHandler.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReload/StaticFileHandler.cs
@@ -1,20 +1,12 @@
// 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;
-using System.Text.Json.Serialization;
using Microsoft.Build.Graph;
namespace Microsoft.DotNet.Watch
{
internal sealed class StaticFileHandler(IReporter reporter, ProjectNodeMap projectMap, BrowserConnector browserConnector)
{
- private static readonly JsonSerializerOptions s_jsonSerializerOptions = new(JsonSerializerDefaults.Web)
- {
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
- };
-
public async ValueTask HandleFileChangesAsync(IReadOnlyList files, CancellationToken cancellationToken)
{
var allFilesHandled = true;
@@ -67,20 +59,11 @@ public async ValueTask HandleFileChangesAsync(IReadOnlyList f
return allFilesHandled;
}
- var tasks = refreshRequests.Select(async request =>
- {
- // Serialize all requests sent to a single server:
- foreach (var path in request.Value)
- {
- reporter.Verbose($"Sending static file update request for asset '{path}'.");
- var message = JsonSerializer.SerializeToUtf8Bytes(new UpdateStaticFileMessage { Path = path }, s_jsonSerializerOptions);
- await request.Key.SendAsync(message, cancellationToken);
- }
- });
+ var tasks = refreshRequests.Select(request => request.Key.UpdateStaticAssetsAsync(request.Value, cancellationToken).AsTask());
await Task.WhenAll(tasks).WaitAsync(cancellationToken);
- reporter.Output("Hot Reload of static files succeeded.", emoji: "🔥");
+ reporter.Output("Hot reload of static files succeeded.", emoji: "🔥");
return allFilesHandled;
}
diff --git a/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs b/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
index 4402c1fbf742..228b286bdccc 100644
--- a/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
+++ b/src/BuiltInTools/dotnet-watch/HotReloadDotNetWatcher.cs
@@ -72,7 +72,6 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
var waitForFileChangeBeforeRestarting = true;
EvaluationResult? evaluationResult = null;
RunningProject? rootRunningProject = null;
- Task>? fileWatcherTask = null;
IRuntimeProcessLauncher? runtimeProcessLauncher = null;
CompilationHandler? compilationHandler = null;
Action? fileChangedCallback = null;
@@ -101,8 +100,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
await using var browserConnector = new BrowserConnector(Context);
var projectMap = new ProjectNodeMap(evaluationResult.ProjectGraph, Context.Reporter);
- compilationHandler = new CompilationHandler(Context.Reporter);
- var staticFileHandler = new StaticFileHandler(Context.Reporter, projectMap, browserConnector);
+ compilationHandler = new CompilationHandler(Context.Reporter, Context.EnvironmentOptions, shutdownCancellationToken);
var scopedCssFileHandler = new ScopedCssFileHandler(Context.Reporter, projectMap, browserConnector);
var projectLauncher = new ProjectLauncher(Context, projectMap, browserConnector, compilationHandler, iteration);
var outputDirectories = GetProjectOutputDirectories(evaluationResult.ProjectGraph);
@@ -111,7 +109,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
var rootProjectNode = evaluationResult.ProjectGraph.GraphRoots.Single();
- runtimeProcessLauncher = runtimeProcessLauncherFactory?.TryCreate(rootProjectNode, projectLauncher, rootProjectOptions.BuildArguments);
+ runtimeProcessLauncher = runtimeProcessLauncherFactory?.TryCreate(rootProjectNode, projectLauncher, rootProjectOptions);
if (runtimeProcessLauncher != null)
{
var launcherEnvironment = runtimeProcessLauncher.GetEnvironmentVariables();
@@ -255,7 +253,7 @@ void FileChangedCallback(ChangedPath change)
var stopwatch = Stopwatch.StartNew();
HotReloadEventSource.Log.HotReloadStart(HotReloadEventSource.StartType.StaticHandler);
- await staticFileHandler.HandleFileChangesAsync(changedFiles, iterationCancellationToken);
+ await compilationHandler.HandleStaticAssetChangesAsync(changedFiles, projectMap, iterationCancellationToken);
HotReloadEventSource.Log.HotReloadEnd(HotReloadEventSource.StartType.StaticHandler);
HotReloadEventSource.Log.HotReloadStart(HotReloadEventSource.StartType.ScopedCssHandler);
@@ -264,7 +262,7 @@ void FileChangedCallback(ChangedPath change)
HotReloadEventSource.Log.HotReloadStart(HotReloadEventSource.StartType.CompilationHandler);
- var (projectsToRebuild, projectsToRestart) = await compilationHandler.HandleFileChangesAsync(restartPrompt: async (projectNames, cancellationToken) =>
+ var (projectsToRebuild, projectsToRestart) = await compilationHandler.HandleManagedCodeChangesAsync(restartPrompt: async (projectNames, cancellationToken) =>
{
if (_rudeEditRestartPrompt != null)
{
@@ -517,38 +515,24 @@ async Task> CaptureChangedFilesSnapshot(ImmutableDict
await compilationHandler.TerminateNonRootProcessesAndDispose(CancellationToken.None);
}
- if (!rootProcessTerminationSource.IsCancellationRequested)
+ if (rootRunningProject != null)
{
- rootProcessTerminationSource.Cancel();
+ await rootRunningProject.TerminateAsync(shutdownCancellationToken);
}
- try
- {
- // Wait for the root process to exit.
- await Task.WhenAll(new[] { (Task?)rootRunningProject?.RunningProcess, fileWatcherTask }.Where(t => t != null)!);
- }
- catch (OperationCanceledException) when (!shutdownCancellationToken.IsCancellationRequested)
+ if (runtimeProcessLauncher != null)
{
- // nop
+ await runtimeProcessLauncher.DisposeAsync();
}
- finally
- {
- fileWatcherTask = null;
- if (runtimeProcessLauncher != null)
- {
- await runtimeProcessLauncher.DisposeAsync();
- }
-
- rootRunningProject?.Dispose();
+ rootRunningProject?.Dispose();
- if (waitForFileChangeBeforeRestarting &&
- !shutdownCancellationToken.IsCancellationRequested &&
- !forceRestartCancellationSource.IsCancellationRequested)
- {
- using var shutdownOrForcedRestartSource = CancellationTokenSource.CreateLinkedTokenSource(shutdownCancellationToken, forceRestartCancellationSource.Token);
- await WaitForFileChangeBeforeRestarting(fileWatcher, evaluationResult, shutdownOrForcedRestartSource.Token);
- }
+ if (waitForFileChangeBeforeRestarting &&
+ !shutdownCancellationToken.IsCancellationRequested &&
+ !forceRestartCancellationSource.IsCancellationRequested)
+ {
+ using var shutdownOrForcedRestartSource = CancellationTokenSource.CreateLinkedTokenSource(shutdownCancellationToken, forceRestartCancellationSource.Token);
+ await WaitForFileChangeBeforeRestarting(fileWatcher, evaluationResult, shutdownOrForcedRestartSource.Token);
}
}
}
diff --git a/src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs b/src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs
index d50f49e8b1cc..d539664fe43b 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs
+++ b/src/BuiltInTools/dotnet-watch/Internal/ConsoleReporter.cs
@@ -58,7 +58,8 @@ public void Report(MessageDescriptor descriptor, string prefix, object?[] args)
switch (descriptor.Severity)
{
case MessageSeverity.Error:
- WriteLine(console.Error, message, ConsoleColor.Red, descriptor.Emoji);
+ // Use stdout for error messages to preserve ordering with respect to other output.
+ WriteLine(console.Out, message, ConsoleColor.Red, descriptor.Emoji);
break;
case MessageSeverity.Warning:
diff --git a/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs b/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs
index e216f30af874..e00058d62221 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs
+++ b/src/BuiltInTools/dotnet-watch/Internal/IReporter.cs
@@ -75,7 +75,7 @@ public bool TryGetMessage(string? prefix, object?[] args, [NotNullWhen(true)] ou
public static readonly MessageDescriptor IgnoringChangeInHiddenDirectory = new("Ignoring change in hidden directory '{0}': {1} '{2}'", "⌚", MessageSeverity.Verbose, s_id++);
public static readonly MessageDescriptor IgnoringChangeInOutputDirectory = new("Ignoring change in output directory: {0} '{1}'", "⌚", MessageSeverity.Verbose, s_id++);
public static readonly MessageDescriptor FileAdditionTriggeredReEvaluation = new("File addition triggered re-evaluation.", "⌚", MessageSeverity.Verbose, s_id++);
- public static readonly MessageDescriptor NoHotReloadChangesToApply = new ("No C# changes to apply.", "⌚", MessageSeverity.Output, s_id++);
+ public static readonly MessageDescriptor NoCSharpChangesToApply = new ("No C# changes to apply.", "⌚", MessageSeverity.Output, s_id++);
}
internal interface IReporter
diff --git a/src/BuiltInTools/dotnet-watch/Internal/PhysicalConsole.cs b/src/BuiltInTools/dotnet-watch/Internal/PhysicalConsole.cs
index 3e3e14195dec..4a3bc0c17998 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/PhysicalConsole.cs
+++ b/src/BuiltInTools/dotnet-watch/Internal/PhysicalConsole.cs
@@ -17,27 +17,7 @@ internal sealed class PhysicalConsole : IConsole
public PhysicalConsole(TestFlags testFlags)
{
Console.OutputEncoding = Encoding.UTF8;
-
- bool readFromStdin;
- if (testFlags.HasFlag(TestFlags.ReadKeyFromStdin))
- {
- readFromStdin = true;
- }
- else
- {
- try
- {
- Console.TreatControlCAsInput = true;
- readFromStdin = false;
- }
- catch
- {
- // fails when stdin is redirected
- readFromStdin = true;
- }
- }
-
- _ = readFromStdin ? ListenToStandardInputAsync() : ListenToConsoleKeyPressAsync();
+ _ = testFlags.HasFlag(TestFlags.ReadKeyFromStdin) ? ListenToStandardInputAsync() : ListenToConsoleKeyPressAsync();
}
private async Task ListenToStandardInputAsync()
@@ -73,7 +53,14 @@ private async Task ListenToStandardInputAsync()
}
private Task ListenToConsoleKeyPressAsync()
- => Task.Factory.StartNew(() =>
+ {
+ Console.CancelKeyPress += (s, e) =>
+ {
+ e.Cancel = true;
+ KeyPressed?.Invoke(new ConsoleKeyInfo(CtrlC, ConsoleKey.C, shift: false, alt: false, control: true));
+ };
+
+ return Task.Factory.StartNew(() =>
{
while (true)
{
@@ -81,6 +68,7 @@ private Task ListenToConsoleKeyPressAsync()
KeyPressed?.Invoke(key);
}
}, TaskCreationOptions.LongRunning);
+ }
public TextWriter Error => Console.Error;
public TextWriter Out => Console.Out;
diff --git a/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json b/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json
index de41cecc021a..356ddeb0689a 100644
--- a/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json
+++ b/src/BuiltInTools/dotnet-watch/Properties/launchSettings.json
@@ -8,7 +8,8 @@
"DOTNET_WATCH_DEBUG_SDK_DIRECTORY": "$(RepoRoot)artifacts\\bin\\redist\\$(Configuration)\\dotnet\\sdk\\$(Version)",
"DCP_IDE_REQUEST_TIMEOUT_SECONDS": "100000",
"DCP_IDE_NOTIFICATION_TIMEOUT_SECONDS": "100000",
- "DCP_IDE_NOTIFICATION_KEEPALIVE_SECONDS": "100000"
+ "DCP_IDE_NOTIFICATION_KEEPALIVE_SECONDS": "100000",
+ "DOTNET_WATCH_PROCESS_CLEANUP_TIMEOUT_MS": "0"
}
}
}
diff --git a/src/BuiltInTools/dotnet-watch/Utilities/ProjectGraphNodeExtensions.cs b/src/BuiltInTools/dotnet-watch/Utilities/ProjectGraphNodeExtensions.cs
index d46945d500d9..42ff4703992c 100644
--- a/src/BuiltInTools/dotnet-watch/Utilities/ProjectGraphNodeExtensions.cs
+++ b/src/BuiltInTools/dotnet-watch/Utilities/ProjectGraphNodeExtensions.cs
@@ -36,6 +36,9 @@ public static bool IsNetCoreApp(this ProjectGraphNode projectNode, Version minVe
public static string? GetOutputDirectory(this ProjectGraphNode projectNode)
=> projectNode.ProjectInstance.GetPropertyValue("TargetPath") is { Length: >0 } path ? Path.GetDirectoryName(Path.Combine(projectNode.ProjectInstance.Directory, path)) : null;
+ public static string GetAssemblyName(this ProjectGraphNode projectNode)
+ => projectNode.ProjectInstance.GetPropertyValue("TargetName");
+
public static string? GetIntermediateOutputDirectory(this ProjectGraphNode projectNode)
=> projectNode.ProjectInstance.GetPropertyValue("IntermediateOutputPath") is { Length: >0 } path ? Path.Combine(projectNode.ProjectInstance.Directory, path) : null;
diff --git a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
index 3e507fffa76c..8e550042fc52 100644
--- a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
+++ b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
@@ -1,5 +1,6 @@
+
@@ -38,39 +39,21 @@
+
+
-
+
-
+
-
+
-
+
-
+
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs
deleted file mode 100644
index 57eda12b7a4d..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/FileManipulation/FileUtil.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-// FileUtil.cs
-//
-// Author:
-// Lluis Sanchez Gual
-//
-// Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-#nullable disable
-
-namespace Microsoft.DotNet.Cli.Sln.Internal.FileManipulation
-{
- internal static class FileUtil
- {
- internal static TextFormatInfo GetTextFormatInfo(string file)
- {
- var info = new TextFormatInfo();
-
- string newLine = null;
- Encoding encoding;
-
- using (FileStream fs = File.OpenRead(file))
- {
- byte[] buf = new byte[1024];
- int nread, i;
-
- if ((nread = fs.Read(buf, 0, buf.Length)) <= 0)
- {
- return info;
- }
-
- if (TryParse(buf, nread, out encoding))
- {
- i = encoding.GetPreamble().Length;
- }
- else
- {
- encoding = null;
- i = 0;
- }
-
- do
- {
- while (i < nread)
- {
- if (buf[i] == '\r')
- {
- newLine = "\r\n";
- break;
- }
- else if (buf[i] == '\n')
- {
- newLine = "\n";
- break;
- }
-
- i++;
- }
-
- if (newLine == null)
- {
- if ((nread = fs.Read(buf, 0, buf.Length)) <= 0)
- {
- newLine = "\n";
- break;
- }
-
- i = 0;
- }
- } while (newLine == null);
-
- info.EndsWithEmptyLine = fs.Seek(-1, SeekOrigin.End) > 0 && fs.ReadByte() == (int)'\n';
- info.NewLine = newLine;
- info.Encoding = encoding;
- return info;
- }
- }
-
- private static bool TryParse(byte[] buffer, int available, out Encoding encoding)
- {
- if (buffer.Length >= 2)
- {
- for (int i = 0; i < table.Length; i++)
- {
- bool matched = true;
-
- if (available < table[i].GetPreamble().Length)
- {
- continue;
- }
-
- for (int j = 0; j < table[i].GetPreamble().Length; j++)
- {
- if (buffer[j] != table[i].GetPreamble()[j])
- {
- matched = false;
- break;
- }
- }
-
- if (matched)
- {
- encoding = table[i];
- return true;
- }
- }
- }
-
- encoding = null;
-
- return false;
- }
-
- private static readonly Encoding[] table = new[] {
- Encoding.UTF8,
- Encoding.UTF32,
- Encoding.ASCII,
- };
- }
-
- internal class TextFormatInfo
- {
- public TextFormatInfo()
- {
- NewLine = Environment.NewLine;
- Encoding = null;
- EndsWithEmptyLine = true;
- }
-
- public string NewLine { get; set; }
- public Encoding Encoding { get; set; }
- public bool EndsWithEmptyLine { get; set; }
- }
-}
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.resx b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.resx
deleted file mode 100644
index 524b72423138..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/LocalizableStrings.resx
+++ /dev/null
@@ -1,153 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- Invalid format in line {0}: {1}
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
-
-
- Property set is missing '{0}'
-
-
- Global section specified more than once
-
-
- Global section not closed
-
-
- File header is missing version
-
-
- Expected file header not found
-
-
- Project section not closed
-
-
- Invalid section type: {0}
-
-
- Section id missing
-
-
- Closing section tag not found
-
-
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Microsoft.DotNet.Cli.Sln.Internal.csproj b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Microsoft.DotNet.Cli.Sln.Internal.csproj
deleted file mode 100644
index 1618b780490d..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Microsoft.DotNet.Cli.Sln.Internal.csproj
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- SLN file reader/writer
- $(SdkTargetFramework)
- portable
- Microsoft.DotNet.Cli.Sln.Internal
- MicrosoftAspNetCore
- true
- true
- true
-
-
-
-
-
-
-
-
-
-
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs
deleted file mode 100644
index 5db6beb30ed7..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/ProjectTypeGuids.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.DotNet.Cli.Sln.Internal
-{
- public static class ProjectTypeGuids
- {
- public const string CSharpProjectTypeGuid = "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}";
- public const string FSharpProjectTypeGuid = "{F2A71F9B-5D33-465A-A702-920D77279786}";
- public const string VBProjectTypeGuid = "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}";
- public const string SolutionFolderGuid = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
- public const string SharedProjectGuid = "{D954291E-2A0B-460D-934E-DC6B0785DB48}";
- }
-}
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Properties/AssemblyInfo.cs b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Properties/AssemblyInfo.cs
deleted file mode 100644
index 98cdd1d19b73..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("Microsoft.DotNet.Cli.Sln.Internal.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
-[assembly: InternalsVisibleTo("dotnet-sln.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
-
-
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs
deleted file mode 100644
index 88f246b9e940..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs
+++ /dev/null
@@ -1,1201 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-//
-// SlnFile.cs
-//
-// Author:
-// Lluis Sanchez Gual
-//
-// Copyright (c) 2016 Xamarin, Inc (http://www.xamarin.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-
-#nullable disable
-
-using System.Collections;
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-using System.Globalization;
-using System.Reflection;
-using Microsoft.DotNet.Cli.Sln.Internal.FileManipulation;
-using Microsoft.DotNet.Tools.Common;
-
-namespace Microsoft.DotNet.Cli.Sln.Internal
-{
- public class SlnFile
- {
- private SlnProjectCollection _projects = new();
- private SlnSectionCollection _sections = new();
- private SlnPropertySet _metadata = new(true);
- private int _prefixBlankLines = 1;
- private TextFormatInfo _format = new();
-
- public string FormatVersion { get; set; }
- public string ProductDescription { get; set; }
-
- public string VisualStudioVersion
- {
- get { return _metadata.GetValue("VisualStudioVersion"); }
- set { _metadata.SetValue("VisualStudioVersion", value); }
- }
-
- public string MinimumVisualStudioVersion
- {
- get { return _metadata.GetValue("MinimumVisualStudioVersion"); }
- set { _metadata.SetValue("MinimumVisualStudioVersion", value); }
- }
-
- public string BaseDirectory
- {
- get { return Path.GetDirectoryName(FullPath); }
- }
-
- public string FullPath { get; set; }
-
- public SlnPropertySet SolutionConfigurationsSection
- {
- get
- {
- return _sections
- .GetOrCreateSection("SolutionConfigurationPlatforms", SlnSectionType.PreProcess)
- .Properties;
- }
- }
-
- public SlnPropertySetCollection ProjectConfigurationsSection
- {
- get
- {
- return _sections
- .GetOrCreateSection("ProjectConfigurationPlatforms", SlnSectionType.PostProcess)
- .NestedPropertySets;
- }
- }
-
- public SlnSectionCollection Sections
- {
- get { return _sections; }
- }
-
- public SlnProjectCollection Projects
- {
- get { return _projects; }
- }
-
- public SlnFile()
- {
- _projects.ParentFile = this;
- _sections.ParentFile = this;
- }
-
- public static SlnFile Read(string file)
- {
- SlnFile slnFile = new()
- {
- FullPath = Path.GetFullPath(file),
- _format = FileUtil.GetTextFormatInfo(file)
- };
-
- using (var sr = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read)))
- {
- slnFile.Read(sr);
- }
-
- return slnFile;
- }
-
- private void Read(TextReader reader)
- {
- const string HeaderPrefix = "Microsoft Visual Studio Solution File, Format Version";
-
- string line;
- int curLineNum = 0;
- bool globalFound = false;
- bool productRead = false;
-
- while ((line = reader.ReadLine()) != null)
- {
- curLineNum++;
- line = line.Trim();
- if (line.StartsWith(HeaderPrefix, StringComparison.Ordinal))
- {
- if (line.Length <= HeaderPrefix.Length)
- {
- throw new InvalidSolutionFormatException(
- curLineNum,
- LocalizableStrings.FileHeaderMissingVersionError);
- }
-
- FormatVersion = line.Substring(HeaderPrefix.Length).Trim();
- _prefixBlankLines = curLineNum - 1;
- }
- if (line.StartsWith("# ", StringComparison.Ordinal))
- {
- if (!productRead)
- {
- productRead = true;
- ProductDescription = line.Substring(2);
- }
- }
- else if (line.StartsWith("Project", StringComparison.Ordinal))
- {
- SlnProject p = new();
- p.Read(reader, line, ref curLineNum);
- _projects.Add(p);
- }
- else if (line == "Global")
- {
- if (globalFound)
- {
- throw new InvalidSolutionFormatException(
- curLineNum,
- LocalizableStrings.GlobalSectionMoreThanOnceError);
- }
- globalFound = true;
- while ((line = reader.ReadLine()) != null)
- {
- curLineNum++;
- line = line.Trim();
- if (line == "EndGlobal")
- {
- break;
- }
- else if (line.StartsWith("GlobalSection", StringComparison.Ordinal))
- {
- var sec = new SlnSection();
- sec.Read(reader, line, ref curLineNum);
- _sections.Add(sec);
- }
- else // Ignore text that's out of place
- {
- continue;
- }
- }
- if (line == null)
- {
- throw new InvalidSolutionFormatException(
- curLineNum,
- LocalizableStrings.GlobalSectionNotClosedError);
- }
- }
- else if (line.IndexOf('=') != -1)
- {
- _metadata.ReadLine(line, curLineNum);
- }
- }
- if (FormatVersion == null)
- {
- throw new InvalidSolutionFormatException(LocalizableStrings.FileHeaderMissingError);
- }
- }
-
- public void Write(string file = null)
- {
- if (!string.IsNullOrEmpty(file))
- {
- FullPath = Path.GetFullPath(file);
- }
- var sw = new StringWriter();
- Write(sw);
- File.WriteAllText(FullPath, sw.ToString(), Encoding.UTF8);
- }
-
- private void Write(TextWriter writer)
- {
- writer.NewLine = _format.NewLine;
- for (int n = 0; n < _prefixBlankLines; n++)
- {
- writer.WriteLine();
- }
- writer.WriteLine("Microsoft Visual Studio Solution File, Format Version " + FormatVersion);
- writer.WriteLine("# " + ProductDescription);
-
- _metadata.Write(writer);
-
- foreach (var p in _projects)
- {
- p.Write(writer);
- }
-
- writer.WriteLine("Global");
- foreach (SlnSection s in _sections)
- {
- s.Write(writer, "GlobalSection");
- }
- writer.WriteLine("EndGlobal");
- }
- }
-
- public class SlnProject
- {
- private SlnSectionCollection _sections = new();
-
- private SlnFile _parentFile;
-
- public SlnFile ParentFile
- {
- get
- {
- return _parentFile;
- }
- internal set
- {
- _parentFile = value;
- _sections.ParentFile = _parentFile;
- }
- }
-
- public string Id { get; set; }
- public string TypeGuid { get; set; }
- public string Name { get; set; }
-
- private string _filePath;
- public string FilePath
- {
- get
- {
- return _filePath;
- }
- set
- {
- _filePath = PathUtility.RemoveExtraPathSeparators(
- PathUtility.GetPathWithDirectorySeparator(value));
- }
- }
-
- public int Line { get; private set; }
- internal bool Processed { get; set; }
-
- public SlnSectionCollection Sections
- {
- get { return _sections; }
- }
-
- public SlnSection Dependencies
- {
- get
- {
- return _sections.GetSection("ProjectDependencies", SlnSectionType.PostProcess);
- }
- }
-
- internal void Read(TextReader reader, string line, ref int curLineNum)
- {
- Line = curLineNum;
-
- int n = 0;
- FindNext(curLineNum, line, ref n, '(');
- n++;
- FindNext(curLineNum, line, ref n, '"');
- int n2 = n + 1;
- FindNext(curLineNum, line, ref n2, '"');
- TypeGuid = line.Substring(n + 1, n2 - n - 1);
-
- n = n2 + 1;
- FindNext(curLineNum, line, ref n, ')');
- FindNext(curLineNum, line, ref n, '=');
-
- FindNext(curLineNum, line, ref n, '"');
- n2 = n + 1;
- FindNext(curLineNum, line, ref n2, '"');
- Name = line.Substring(n + 1, n2 - n - 1);
-
- n = n2 + 1;
- FindNext(curLineNum, line, ref n, ',');
- FindNext(curLineNum, line, ref n, '"');
- n2 = n + 1;
- FindNext(curLineNum, line, ref n2, '"');
- FilePath = line.Substring(n + 1, n2 - n - 1);
-
- n = n2 + 1;
- FindNext(curLineNum, line, ref n, ',');
- FindNext(curLineNum, line, ref n, '"');
- n2 = n + 1;
- FindNext(curLineNum, line, ref n2, '"');
- Id = line.Substring(n + 1, n2 - n - 1);
-
- while ((line = reader.ReadLine()) != null)
- {
- curLineNum++;
- line = line.Trim();
- if (line == "EndProject")
- {
- return;
- }
- if (line.StartsWith("ProjectSection", StringComparison.Ordinal))
- {
- if (_sections == null)
- {
- _sections = new SlnSectionCollection();
- }
- var sec = new SlnSection();
- _sections.Add(sec);
- sec.Read(reader, line, ref curLineNum);
- }
- }
-
- throw new InvalidSolutionFormatException(
- curLineNum,
- LocalizableStrings.ProjectSectionNotClosedError);
- }
-
- private void FindNext(int ln, string line, ref int i, char c)
- {
- var inputIndex = i;
- i = line.IndexOf(c, i);
- if (i == -1)
- {
- throw new InvalidSolutionFormatException(
- ln,
- string.Format(LocalizableStrings.ProjectParsingErrorFormatString, c, inputIndex));
- }
- }
-
- internal void Write(TextWriter writer)
- {
- writer.Write("Project(\"");
- writer.Write(TypeGuid);
- writer.Write("\") = \"");
- writer.Write(Name);
- writer.Write("\", \"");
- writer.Write(PathUtility.GetPathWithBackSlashes(FilePath));
- writer.Write("\", \"");
- writer.Write(Id);
- writer.WriteLine("\"");
- if (_sections != null)
- {
- foreach (SlnSection s in _sections)
- {
- s.Write(writer, "ProjectSection");
- }
- }
- writer.WriteLine("EndProject");
- }
- }
-
- public class SlnSection
- {
- private SlnPropertySetCollection _nestedPropertySets;
- private SlnPropertySet _properties;
- private List _sectionLines;
- private int _baseIndex;
-
- public string Id { get; set; }
- public int Line { get; private set; }
-
- internal bool Processed { get; set; }
-
- public SlnFile ParentFile { get; internal set; }
-
- public bool IsEmpty
- {
- get
- {
- return (_properties == null || _properties.Count == 0) &&
- (_nestedPropertySets == null || _nestedPropertySets.All(t => t.IsEmpty)) &&
- (_sectionLines == null || _sectionLines.Count == 0);
- }
- }
-
- ///
- /// If true, this section won't be written to the file if it is empty
- ///
- /// true if skip if empty; otherwise, false.
- public bool SkipIfEmpty { get; set; }
-
- public void Clear()
- {
- _properties = null;
- _nestedPropertySets = null;
- _sectionLines = null;
- }
-
- public SlnPropertySet Properties
- {
- get
- {
- if (_properties == null)
- {
- _properties = new SlnPropertySet
- {
- ParentSection = this
- };
- if (_sectionLines != null)
- {
- foreach (var line in _sectionLines)
- {
- _properties.ReadLine(line, Line);
- }
- _sectionLines = null;
- }
- }
- return _properties;
- }
- }
-
- public SlnPropertySetCollection NestedPropertySets
- {
- get
- {
- if (_nestedPropertySets == null)
- {
- _nestedPropertySets = new SlnPropertySetCollection(this);
- if (_sectionLines != null)
- {
- LoadPropertySets();
- }
- }
- return _nestedPropertySets;
- }
- }
-
- public void SetContent(IEnumerable> lines)
- {
- _sectionLines = new List(lines.Select(p => p.Key + " = " + p.Value));
- _properties = null;
- _nestedPropertySets = null;
- }
-
- public IEnumerable> GetContent()
- {
- if (_sectionLines != null)
- {
- return _sectionLines.Select(li =>
- {
- int i = li.IndexOf('=');
- if (i != -1)
- {
- return new KeyValuePair(li.Substring(0, i).Trim(), li.Substring(i + 1).Trim());
- }
- else
- {
- return new KeyValuePair(li.Trim(), "");
- }
- });
- }
- else
- {
- return new KeyValuePair[0];
- }
- }
-
- public SlnSectionType SectionType { get; set; }
-
- private SlnSectionType ToSectionType(int curLineNum, string s)
- {
- if (s == "preSolution" || s == "preProject")
- {
- return SlnSectionType.PreProcess;
- }
- if (s == "postSolution" || s == "postProject")
- {
- return SlnSectionType.PostProcess;
- }
- throw new InvalidSolutionFormatException(
- curLineNum,
- string.Format(LocalizableStrings.InvalidSectionTypeError, s));
- }
-
- private string FromSectionType(bool isProjectSection, SlnSectionType type)
- {
- if (type == SlnSectionType.PreProcess)
- {
- return isProjectSection ? "preProject" : "preSolution";
- }
- else
- {
- return isProjectSection ? "postProject" : "postSolution";
- }
- }
-
- internal void Read(TextReader reader, string line, ref int curLineNum)
- {
- Line = curLineNum;
- int k = line.IndexOf('(');
- if (k == -1)
- {
- throw new InvalidSolutionFormatException(
- curLineNum,
- LocalizableStrings.SectionIdMissingError);
- }
- var tag = line.Substring(0, k).Trim();
- var k2 = line.IndexOf(')', k);
- if (k2 == -1)
- {
- throw new InvalidSolutionFormatException(
- curLineNum,
- LocalizableStrings.SectionIdMissingError);
- }
- Id = line.Substring(k + 1, k2 - k - 1);
-
- k = line.IndexOf('=', k2);
- SectionType = ToSectionType(curLineNum, line.Substring(k + 1).Trim());
-
- var endTag = "End" + tag;
-
- _sectionLines = new List();
- _baseIndex = ++curLineNum;
- while ((line = reader.ReadLine()) != null)
- {
- curLineNum++;
- line = line.Trim();
- if (line == endTag)
- {
- break;
- }
- _sectionLines.Add(line);
- }
- if (line == null)
- {
- throw new InvalidSolutionFormatException(
- curLineNum,
- LocalizableStrings.ClosingSectionTagNotFoundError);
- }
- }
-
- private void LoadPropertySets()
- {
- if (_sectionLines != null)
- {
- SlnPropertySet curSet = null;
- for (int n = 0; n < _sectionLines.Count; n++)
- {
- var line = _sectionLines[n];
- if (string.IsNullOrEmpty(line.Trim()))
- {
- continue;
- }
- var i = line.IndexOf('.');
- if (i == -1)
- {
- throw new InvalidSolutionFormatException(
- _baseIndex + n,
- string.Format(LocalizableStrings.InvalidPropertySetFormatString, '.'));
- }
- var id = line.Substring(0, i);
- if (curSet == null || id != curSet.Id)
- {
- curSet = new SlnPropertySet(id);
- _nestedPropertySets.Add(curSet);
- }
- curSet.ReadLine(line.Substring(i + 1), _baseIndex + n);
- }
- _sectionLines = null;
- }
- }
-
- internal void Write(TextWriter writer, string sectionTag)
- {
- if (SkipIfEmpty && IsEmpty)
- {
- return;
- }
-
- writer.Write("\t");
- writer.Write(sectionTag);
- writer.Write('(');
- writer.Write(Id);
- writer.Write(") = ");
- writer.WriteLine(FromSectionType(sectionTag == "ProjectSection", SectionType));
- if (_sectionLines != null)
- {
- foreach (var l in _sectionLines)
- {
- writer.WriteLine("\t\t" + l);
- }
- }
- else if (_properties != null)
- {
- _properties.Write(writer);
- }
- else if (_nestedPropertySets != null)
- {
- foreach (var ps in _nestedPropertySets)
- {
- ps.Write(writer);
- }
- }
- writer.WriteLine("\tEnd" + sectionTag);
- }
- }
-
- ///
- /// A collection of properties
- ///
- public class SlnPropertySet : IDictionary
- {
- private OrderedDictionary _values = new();
- private bool _isMetadata;
-
- internal bool Processed { get; set; }
-
- public SlnFile ParentFile
- {
- get { return ParentSection != null ? ParentSection.ParentFile : null; }
- }
-
- public SlnSection ParentSection { get; set; }
-
- ///
- /// Text file line of this section in the original file
- ///
- /// The line.
- public int Line { get; private set; }
-
- internal SlnPropertySet()
- {
- }
-
- ///
- /// Creates a new property set with the specified ID
- ///
- /// Identifier.
- public SlnPropertySet(string id)
- {
- Id = id;
- }
-
- internal SlnPropertySet(bool isMetadata)
- {
- _isMetadata = isMetadata;
- }
-
- public bool IsEmpty
- {
- get
- {
- return _values.Count == 0;
- }
- }
-
- internal void ReadLine(string line, int currentLine)
- {
- if (Line == 0)
- {
- Line = currentLine;
- }
- int k = line.IndexOf('=');
- if (k != -1)
- {
- var name = line.Substring(0, k).Trim();
- var val = line.Substring(k + 1).Trim();
- _values[name] = val;
- }
- else
- {
- line = line.Trim();
- if (!string.IsNullOrWhiteSpace(line))
- {
- _values.Add(line, null);
- }
- }
- }
-
- internal void Write(TextWriter writer)
- {
- foreach (DictionaryEntry e in _values)
- {
- if (!_isMetadata)
- {
- writer.Write("\t\t");
- }
- if (Id != null)
- {
- writer.Write(Id + ".");
- }
- writer.WriteLine(e.Key + " = " + e.Value);
- }
- }
-
- public string Id { get; private set; }
-
- public string GetValue(string name, string defaultValue = null)
- {
- string res;
- if (TryGetValue(name, out res))
- {
- return res;
- }
- else
- {
- return defaultValue;
- }
- }
-
- public T GetValue(string name)
- {
- return (T)GetValue(name, typeof(T), default(T));
- }
-
- public T GetValue(string name, T defaultValue)
- {
- return (T)GetValue(name, typeof(T), defaultValue);
- }
-
- public object GetValue(string name, Type t, object defaultValue)
- {
- string val;
- if (TryGetValue(name, out val))
- {
- if (t == typeof(bool))
- {
- return (object)val.Equals("true", StringComparison.OrdinalIgnoreCase);
- }
- if (t.GetTypeInfo().IsEnum)
- {
- return Enum.Parse(t, val, true);
- }
- if (t.GetTypeInfo().IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
- {
- var at = t.GetTypeInfo().GetGenericArguments()[0];
- if (string.IsNullOrEmpty(val))
- {
- return null;
- }
- return Convert.ChangeType(val, at, CultureInfo.InvariantCulture);
-
- }
- return Convert.ChangeType(val, t, CultureInfo.InvariantCulture);
- }
- else
- {
- return defaultValue;
- }
- }
-
- public void SetValue(string name, string value, string defaultValue = null, bool preserveExistingCase = false)
- {
- if (value == null && defaultValue == "")
- {
- value = "";
- }
- if (value == defaultValue)
- {
- // if the value is default, only remove the property if it was not already the default
- // to avoid unnecessary project file churn
- string res;
- if (TryGetValue(name, out res) &&
- !string.Equals(defaultValue ?? "",
- res, preserveExistingCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
- {
- Remove(name);
- }
- return;
- }
- string currentValue;
- if (preserveExistingCase && TryGetValue(name, out currentValue) &&
- string.Equals(value, currentValue, StringComparison.OrdinalIgnoreCase))
- {
- return;
- }
- _values[name] = value;
- }
-
- public void SetValue(string name, object value, object defaultValue = null)
- {
- var isDefault = Equals(value, defaultValue);
- if (isDefault)
- {
- // if the value is default, only remove the property if it was not already the default
- // to avoid unnecessary project file churn
- if (ContainsKey(name) && (defaultValue == null ||
- !Equals(defaultValue, GetValue(name, defaultValue.GetType(), null))))
- {
- Remove(name);
- }
- return;
- }
-
- if (value is bool)
- {
- _values[name] = (bool)value ? "TRUE" : "FALSE";
- }
- else
- {
- _values[name] = Convert.ToString(value, CultureInfo.InvariantCulture);
- }
- }
-
- void IDictionary.Add(string key, string value)
- {
- SetValue(key, value);
- }
-
- public bool ContainsKey(string key)
- {
- return _values.Contains(key);
- }
-
- public bool Remove(string key)
- {
- var wasThere = _values.Contains(key);
- _values.Remove(key);
- return wasThere;
- }
-
- public bool TryGetValue(string key, out string value)
- {
- value = (string)_values[key];
- return value != null;
- }
-
- public string this[string index]
- {
- get
- {
- return (string)_values[index];
- }
- set
- {
- _values[index] = value;
- }
- }
-
- public ICollection Values
- {
- get
- {
- return _values.Values.Cast().ToList();
- }
- }
-
- public ICollection Keys
- {
- get { return _values.Keys.Cast().ToList(); }
- }
-
- void ICollection>.Add(KeyValuePair item)
- {
- SetValue(item.Key, item.Value);
- }
-
- public void Clear()
- {
- _values.Clear();
- }
-
- internal void ClearExcept(HashSet keys)
- {
- foreach (var k in _values.Keys.Cast().Except(keys).ToArray())
- {
- _values.Remove(k);
- }
- }
-
- bool ICollection>.Contains(KeyValuePair item)
- {
- var val = GetValue(item.Key);
- return val == item.Value;
- }
-
- public void CopyTo(KeyValuePair[] array, int arrayIndex)
- {
- foreach (DictionaryEntry de in _values)
- {
- array[arrayIndex++] = new KeyValuePair((string)de.Key, (string)de.Value);
- }
- }
-
- bool ICollection>.Remove(KeyValuePair item)
- {
- if (((ICollection>)this).Contains(item))
- {
- Remove(item.Key);
- return true;
- }
- else
- {
- return false;
- }
- }
-
- public int Count
- {
- get
- {
- return _values.Count;
- }
- }
-
- internal void SetLines(IEnumerable> lines)
- {
- _values.Clear();
- foreach (var line in lines)
- {
- _values[line.Key] = line.Value;
- }
- }
-
- bool ICollection>.IsReadOnly
- {
- get
- {
- return false;
- }
- }
-
- public IEnumerator> GetEnumerator()
- {
- foreach (DictionaryEntry de in _values)
- {
- yield return new KeyValuePair((string)de.Key, (string)de.Value);
- }
- }
-
- IEnumerator IEnumerable.GetEnumerator()
- {
- foreach (DictionaryEntry de in _values)
- {
- yield return new KeyValuePair((string)de.Key, (string)de.Value);
- }
- }
- }
-
- public class SlnProjectCollection : Collection
- {
- private SlnFile _parentFile;
-
- internal SlnFile ParentFile
- {
- get
- {
- return _parentFile;
- }
- set
- {
- _parentFile = value;
- foreach (var it in this)
- {
- it.ParentFile = _parentFile;
- }
- }
- }
-
- public SlnProject GetProject(string id)
- {
- return this.FirstOrDefault(s => s.Id == id);
- }
-
- public SlnProject GetOrCreateProject(string id)
- {
- var p = this.FirstOrDefault(s => s.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
- if (p == null)
- {
- p = new SlnProject { Id = id };
- Add(p);
- }
- return p;
- }
-
- protected override void InsertItem(int index, SlnProject item)
- {
- base.InsertItem(index, item);
- item.ParentFile = ParentFile;
- }
-
- protected override void SetItem(int index, SlnProject item)
- {
- base.SetItem(index, item);
- item.ParentFile = ParentFile;
- }
-
- protected override void RemoveItem(int index)
- {
- var it = this[index];
- it.ParentFile = null;
- base.RemoveItem(index);
- }
-
- protected override void ClearItems()
- {
- foreach (var it in this)
- {
- it.ParentFile = null;
- }
- base.ClearItems();
- }
- }
-
- public class SlnSectionCollection : Collection
- {
- private SlnFile _parentFile;
-
- internal SlnFile ParentFile
- {
- get
- {
- return _parentFile;
- }
- set
- {
- _parentFile = value;
- foreach (var it in this)
- {
- it.ParentFile = _parentFile;
- }
- }
- }
-
- public SlnSection GetSection(string id)
- {
- return this.FirstOrDefault(s => s.Id == id);
- }
-
- public SlnSection GetSection(string id, SlnSectionType sectionType)
- {
- return this.FirstOrDefault(s => s.Id == id && s.SectionType == sectionType);
- }
-
- public SlnSection GetOrCreateSection(string id, SlnSectionType sectionType)
- {
- if (id == null)
- {
- throw new ArgumentNullException("id");
- }
- var sec = this.FirstOrDefault(s => s.Id == id);
- if (sec == null)
- {
- sec = new SlnSection
- {
- Id = id,
- SectionType = sectionType
- };
- Add(sec);
- }
- return sec;
- }
-
- public void RemoveSection(string id)
- {
- if (id == null)
- {
- throw new ArgumentNullException("id");
- }
- var s = GetSection(id);
- if (s != null)
- {
- Remove(s);
- }
- }
-
- protected override void InsertItem(int index, SlnSection item)
- {
- base.InsertItem(index, item);
- item.ParentFile = ParentFile;
- }
-
- protected override void SetItem(int index, SlnSection item)
- {
- base.SetItem(index, item);
- item.ParentFile = ParentFile;
- }
-
- protected override void RemoveItem(int index)
- {
- var it = this[index];
- it.ParentFile = null;
- base.RemoveItem(index);
- }
-
- protected override void ClearItems()
- {
- foreach (var it in this)
- {
- it.ParentFile = null;
- }
- base.ClearItems();
- }
- }
-
- public class SlnPropertySetCollection : Collection
- {
- private SlnSection _parentSection;
-
- internal SlnPropertySetCollection(SlnSection parentSection)
- {
- _parentSection = parentSection;
- }
-
- public SlnPropertySet GetPropertySet(string id, bool ignoreCase = false)
- {
- var sc = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
- return this.FirstOrDefault(s => s.Id.Equals(id, sc));
- }
-
- public SlnPropertySet GetOrCreatePropertySet(string id, bool ignoreCase = false)
- {
- var ps = GetPropertySet(id, ignoreCase);
- if (ps == null)
- {
- ps = new SlnPropertySet(id);
- Add(ps);
- }
- return ps;
- }
-
- protected override void InsertItem(int index, SlnPropertySet item)
- {
- base.InsertItem(index, item);
- item.ParentSection = _parentSection;
- }
-
- protected override void SetItem(int index, SlnPropertySet item)
- {
- base.SetItem(index, item);
- item.ParentSection = _parentSection;
- }
-
- protected override void RemoveItem(int index)
- {
- var it = this[index];
- it.ParentSection = null;
- base.RemoveItem(index);
- }
-
- protected override void ClearItems()
- {
- foreach (var it in this)
- {
- it.ParentSection = null;
- }
- base.ClearItems();
- }
- }
-
- public class InvalidSolutionFormatException : Exception
- {
- public InvalidSolutionFormatException(string details)
- : base(details)
- {
- }
-
- public InvalidSolutionFormatException(int line, string details)
- : base(string.Format(LocalizableStrings.ErrorMessageFormatString, line, details))
- {
- }
- }
-
- public enum SlnSectionType
- {
- PreProcess,
- PostProcess
- }
-}
-
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.cs.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.cs.xlf
deleted file mode 100644
index 5d973eb64247..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.cs.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- Několikrát zadaný globální oddíl
-
-
-
- Global section not closed
- Neuzavřený globální oddíl
-
-
-
-
- Project section not closed
- Neuzavřený oddíl projektu
-
-
-
- Invalid section type: {0}
- Neplatný typ oddílu: {0}
-
-
-
- Section id missing
- Chybí ID oddílu
-
-
-
- Closing section tag not found
- Nenalezena koncová značka oddílu
-
-
-
- Invalid format in line {0}: {1}
- Neplatný formát na řádku {0}: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- V oddíle projektu chybí {0}, když analýza řádku začíná na pozici {1}.
-
-
-
- Property set is missing '{0}'
- V sadě vlastností chybí {0}.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.de.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.de.xlf
deleted file mode 100644
index 5e6f488308ac..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.de.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- Globaler Abschnitt mehrmals angegeben
-
-
-
- Global section not closed
- Globaler Abschnitt nicht geschlossen
-
-
-
-
- Project section not closed
- Projektabschnitt nicht geschlossen
-
-
-
- Invalid section type: {0}
- Ungültiger Abschnittstyp: {0}
-
-
-
- Section id missing
- Abschnitts-ID fehlt
-
-
-
- Closing section tag not found
- Schließendes Abschnittstag nicht gefunden
-
-
-
- Invalid format in line {0}: {1}
- Ungültiges Format in Zeile {0}: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- Im Projektabschnitt fehlt "{0}" beim Analysieren des Zeilenbeginns in Position {1}
-
-
-
- Property set is missing '{0}'
- Im Eigenschaftensatz fehlt "{0}"
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.es.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.es.xlf
deleted file mode 100644
index 365664c207aa..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.es.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- Sección global especificada más de una vez
-
-
-
- Global section not closed
- Sección global no cerrada
-
-
-
-
- Project section not closed
- Sección de proyecto no cerrada
-
-
-
- Invalid section type: {0}
- Tipo de sección no válido: {0}
-
-
-
- Section id missing
- Id. de sección omitido
-
-
-
- Closing section tag not found
- No se encuentra la etiqueta de cierre de la sección
-
-
-
- Invalid format in line {0}: {1}
- Formato no válido en línea {0}: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- Falta la sección del proyecto "{0}" al analizar la línea que inicia en la posición {1}
-
-
-
- Property set is missing '{0}'
- A la propiedad establecida le falta "{0}"
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.fr.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.fr.xlf
deleted file mode 100644
index dd00afbe039e..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.fr.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- Section Global spécifiée plusieurs fois
-
-
-
- Global section not closed
- Section Global non fermée
-
-
-
-
- Project section not closed
- Section Project non fermée
-
-
-
- Invalid section type: {0}
- Type de section non valide : {0}
-
-
-
- Section id missing
- ID de section manquant
-
-
-
- Closing section tag not found
- Balise de fermeture de section introuvable
-
-
-
- Invalid format in line {0}: {1}
- Format non valide dans la ligne {0} : {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- '{0}' est manquant dans la section de projet lors de l'analyse de la ligne à partir de la position {1}
-
-
-
- Property set is missing '{0}'
- '{0}' est manquant dans le jeu de propriétés
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.it.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.it.xlf
deleted file mode 100644
index a19ec0313e93..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.it.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- La sezione Global è stata specificata più di una volta
-
-
-
- Global section not closed
- La sezione Global non è stata chiusa
-
-
-
-
- Project section not closed
- La sezione Project non è stata chiusa
-
-
-
- Invalid section type: {0}
- Tipo di sezione non valido: {0}
-
-
-
- Section id missing
- Manca l'ID sezione
-
-
-
- Closing section tag not found
- Il tag di chiusura sessione non è stato trovato
-
-
-
- Invalid format in line {0}: {1}
- Formato non valido alla riga {0}: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- Nella sezione del progetto manca '{0}' quando si analizza la riga a partire dalla posizione {1}
-
-
-
- Property set is missing '{0}'
- Nel set di proprietà manca '{0}'
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ja.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ja.xlf
deleted file mode 100644
index 0f7670d89f28..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ja.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- グローバル セクションが 2 回以上指定されています
-
-
-
- Global section not closed
- グローバル セクションが閉じられていません
-
-
-
-
- Project section not closed
- プロジェクト セクションが閉じられていません
-
-
-
- Invalid section type: {0}
- 無効なセクションの種類: {0}
-
-
-
- Section id missing
- セクション ID がありません
-
-
-
- Closing section tag not found
- 終了セクション タグが見つかりません
-
-
-
- Invalid format in line {0}: {1}
- 行 {0} の形式が無効です: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- 位置 {1} から始まる行の解析中にプロジェクト セクションが見つかりません '{0}'
-
-
-
- Property set is missing '{0}'
- プロパティ セットが見つかりません '{0}'
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ko.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ko.xlf
deleted file mode 100644
index 7d8766ad5dda..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ko.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- 전역 섹션이 두 번 이상 지정됨
-
-
-
- Global section not closed
- 전역 섹션이 닫히지 않음
-
-
-
-
- Project section not closed
- 프로젝트 섹션이 닫히지 않음
-
-
-
- Invalid section type: {0}
- 잘못된 섹션 유형: {0}
-
-
-
- Section id missing
- 섹션 ID가 누락됨
-
-
-
- Closing section tag not found
- 닫기 섹션 태그를 찾을 수 없음
-
-
-
- Invalid format in line {0}: {1}
- {0} 줄에 잘못된 형식: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- {1} 위치에서 시작하는 줄을 구문 분석할 때 프로젝트 섹션에 '{0}'이(가) 없습니다.
-
-
-
- Property set is missing '{0}'
- 속성 설정에 '{0}'이(가) 없습니다.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pl.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pl.xlf
deleted file mode 100644
index 957ee2041b72..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pl.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- Sekcja globalna została określona więcej niż raz
-
-
-
- Global section not closed
- Nie zamknięto sekcji globalnej
-
-
-
-
- Project section not closed
- Nie zamknięto sekcji projektu
-
-
-
- Invalid section type: {0}
- Nieprawidłowy typ sekcji: {0}
-
-
-
- Section id missing
- Brak identyfikatora sekcji
-
-
-
- Closing section tag not found
- Nie odnaleziono tagu zamykającego sekcję
-
-
-
- Invalid format in line {0}: {1}
- Nieprawidłowy format w wierszu {0}: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- W sekcji projektu brakuje elementu „{0}” podczas analizowania wiersza, począwszy od pozycji {1}
-
-
-
- Property set is missing '{0}'
- W zestawie właściwości brakuje elementu „{0}”
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pt-BR.xlf
deleted file mode 100644
index 877faf844fb7..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.pt-BR.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- Seção global especificada mais de uma vez
-
-
-
- Global section not closed
- Seção global não fechada
-
-
-
-
- Project section not closed
- Seção de projeto não fechada
-
-
-
- Invalid section type: {0}
- Tipo de seção inválido: {0}
-
-
-
- Section id missing
- ID de sessão ausente
-
-
-
- Closing section tag not found
- Marca de fechamento de seção não encontrada
-
-
-
- Invalid format in line {0}: {1}
- Formato inválido na linha {0}: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- A seção do projeto está sem o '{0}' ao analisar a linha na posição {1}
-
-
-
- Property set is missing '{0}'
- O conjunto de propriedade está sem '{0}'
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ru.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ru.xlf
deleted file mode 100644
index 7caa01a43eef..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.ru.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- Глобальный раздел указан более одного раза.
-
-
-
- Global section not closed
- Глобальный раздел не закрыт.
-
-
-
-
- Project section not closed
- Раздел проекта не закрыт.
-
-
-
- Invalid section type: {0}
- Недопустимый тип раздела: {0}
-
-
-
- Section id missing
- Отсутствует идентификатор раздела.
-
-
-
- Closing section tag not found
- Закрывающий тег раздела не найден.
-
-
-
- Invalid format in line {0}: {1}
- Формат в строке {0} недопустим: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- В разделе проекта отсутствует "{0}" при анализе строки с начальной позицией {1}
-
-
-
- Property set is missing '{0}'
- В наборе свойств отсутствует "{0}"
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.tr.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.tr.xlf
deleted file mode 100644
index a7c5cba6b88b..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.tr.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- Genel bölüm birden çok kez belirtilmiş
-
-
-
- Global section not closed
- Genel bölüm kapatılmadı
-
-
-
-
- Project section not closed
- Proje bölümü kapatılmadı
-
-
-
- Invalid section type: {0}
- Geçersiz bölüm türü: {0}
-
-
-
- Section id missing
- Bölüm kimliği eksik
-
-
-
- Closing section tag not found
- Kapatma bölümü etiketi bulunamadı
-
-
-
- Invalid format in line {0}: {1}
- {0}. satırda geçersiz biçim: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- {1}. konumdan başlayan satır ayrıştırılırken proje bölümünde '{0}' eksik
-
-
-
- Property set is missing '{0}'
- Özellik kümesinde '{0}' eksik
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hans.xlf
deleted file mode 100644
index b353d12475e4..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hans.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- 多次指定了全局节
-
-
-
- Global section not closed
- 未关闭全局节
-
-
-
-
- Project section not closed
- 未关闭项目节
-
-
-
- Invalid section type: {0}
- 无效的节类型: {0}
-
-
-
- Section id missing
- 缺少节 ID
-
-
-
- Closing section tag not found
- 未找到结束节标记
-
-
-
- Invalid format in line {0}: {1}
- 行 {0} 存在无效格式: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- 分析从位置 {1} 开始的行时,项目节缺少“{0}”
-
-
-
- Property set is missing '{0}'
- 属性集缺少“{0}”
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hant.xlf
deleted file mode 100644
index 7fef8a4f8621..000000000000
--- a/src/Cli/Microsoft.DotNet.Cli.Sln.Internal/xlf/LocalizableStrings.zh-Hant.xlf
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
- Global section specified more than once
- 全域區段指定了一次以上
-
-
-
- Global section not closed
- 全域區段未關閉
-
-
-
-
- Project section not closed
- 專案區段未關閉
-
-
-
- Invalid section type: {0}
- 區段類型無效: {0}
-
-
-
- Section id missing
- 遺漏區段識別碼
-
-
-
- Closing section tag not found
- 找不到關閉區段標記
-
-
-
- Invalid format in line {0}: {1}
- 第 {0} 行的格式不正確: {1}
-
-
-
- Project section is missing '{0}' when parsing the line starting at position {1}
- 從位置 {1} 開始剖析程式行時,專案區段缺少 '{0}'
-
-
-
- Property set is missing '{0}'
- 屬性集缺少 '{0}'
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs
index 60069f1aa672..75e20189e680 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.cs
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/PathUtility.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.Text.RegularExpressions;
using Microsoft.DotNet.Cli.Utils;
using NuGet.Configuration;
@@ -388,5 +389,33 @@ public static void EnsureAllPathsExist(
public static bool IsDirectory(this string path) =>
File.GetAttributes(path).HasFlag(FileAttributes.Directory);
+
+ public static string FixFilePath(string path)
+ {
+ return string.IsNullOrEmpty(path) || Path.DirectorySeparatorChar == '\\' ? path : path.Replace('\\', '/');
+ }
+
+ public static string GetDirectorySeparatorChar()
+ {
+ return Regex.Escape(Path.DirectorySeparatorChar.ToString());
+ }
+
+ public static string? FindFileInParentDirectories(string startDirectory, string relativeFilePath)
+ {
+ var directory = new DirectoryInfo(startDirectory);
+
+ while (directory != null)
+ {
+ var filePath = Path.Combine(directory.FullName, relativeFilePath);
+ if (File.Exists(filePath))
+ {
+ return filePath;
+ }
+
+ directory = directory.Parent;
+ }
+
+ return null;
+ }
}
}
diff --git a/src/Cli/Microsoft.DotNet.Configurer/CliFolderPathCalculator.cs b/src/Cli/Microsoft.DotNet.Configurer/CliFolderPathCalculator.cs
index 3b24e843b147..705ee86a2ab3 100644
--- a/src/Cli/Microsoft.DotNet.Configurer/CliFolderPathCalculator.cs
+++ b/src/Cli/Microsoft.DotNet.Configurer/CliFolderPathCalculator.cs
@@ -42,8 +42,6 @@ public static string WindowsNonExpandedToolsShimPath
public static string ToolsResolverCachePath => Path.Combine(DotnetUserProfileFolderPath, ToolsResolverCacheFolderName);
- public static string PlatformHomeVariableName => CliFolderPathCalculatorCore.PlatformHomeVariableName;
-
public static string DotnetHomePath
{
get
diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/Commands/CliPathInfo.cs b/src/Cli/Microsoft.TemplateEngine.Cli/Commands/CliPathInfo.cs
index f5c4cea88861..668f7f59298c 100644
--- a/src/Cli/Microsoft.TemplateEngine.Cli/Commands/CliPathInfo.cs
+++ b/src/Cli/Microsoft.TemplateEngine.Cli/Commands/CliPathInfo.cs
@@ -24,7 +24,7 @@ public CliPathInfo(
throw new ArgumentException($"{nameof(host.Version)} of {nameof(host)} cannot be null or whitespace.", nameof(host));
}
- UserProfileDir = GetUserProfileDir(environment);
+ UserProfileDir = CliFolderPathCalculator.DotnetHomePath;
GlobalSettingsDir = GetGlobalSettingsDir(settingsLocation);
HostSettingsDir = Path.Combine(GlobalSettingsDir, host.HostIdentifier);
HostVersionSettingsDir = Path.Combine(GlobalSettingsDir, host.HostIdentifier, host.Version);
@@ -38,13 +38,6 @@ public CliPathInfo(
public string HostVersionSettingsDir { get; }
- private static string GetUserProfileDir(IEnvironment environment) => environment.GetEnvironmentVariable(
- RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
- ? "USERPROFILE"
- : "HOME")
- ?? Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
- ?? throw new NotSupportedException("HOME or USERPROFILE environment variable is not defined, the environment is not supported");
-
private static string GetGlobalSettingsDir(string? settingsLocation)
{
var definedSettingsLocation = string.IsNullOrWhiteSpace(settingsLocation)
diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/TemplatePackageCoordinator.cs b/src/Cli/Microsoft.TemplateEngine.Cli/TemplatePackageCoordinator.cs
index b4dda2fd7a46..07235e262599 100644
--- a/src/Cli/Microsoft.TemplateEngine.Cli/TemplatePackageCoordinator.cs
+++ b/src/Cli/Microsoft.TemplateEngine.Cli/TemplatePackageCoordinator.cs
@@ -208,9 +208,9 @@ internal async Task EnterInstallFlowAsync(InstallCommandArgs a
foreach (string installArg in args.TemplatePackages)
{
- string[] splitByColons = installArg.Split(new[] { "::" }, StringSplitOptions.RemoveEmptyEntries);
- string identifier = splitByColons[0];
- string? version = splitByColons.Length > 1 ? splitByColons[1] : null;
+ string[] split = installArg.Split(["::"], StringSplitOptions.RemoveEmptyEntries).SelectMany(arg => arg.Split('@', StringSplitOptions.RemoveEmptyEntries)).ToArray();
+ string identifier = split[0];
+ string? version = split.Length > 1 ? split[1] : null;
foreach (string expandedIdentifier in InstallRequestPathResolution.ExpandMaskedPath(identifier, _engineEnvironmentSettings))
{
installRequests.Add(new InstallRequest(expandedIdentifier, version, details: details, force: args.Force));
diff --git a/src/Cli/dotnet/CommandFactory/CommandResolution/ProjectToolsCommandResolver.cs b/src/Cli/dotnet/CommandFactory/CommandResolution/ProjectToolsCommandResolver.cs
index d4b8733e8c30..16962233b2a1 100644
--- a/src/Cli/dotnet/CommandFactory/CommandResolution/ProjectToolsCommandResolver.cs
+++ b/src/Cli/dotnet/CommandFactory/CommandResolution/ProjectToolsCommandResolver.cs
@@ -337,7 +337,7 @@ internal void GenerateDepsJsonFile(
LocalizableStrings.GeneratingDepsJson,
depsPath));
- var tempDepsFile = Path.GetTempFileName();
+ var tempDepsFile = Path.Combine(PathUtilities.CreateTempSubdirectory(), Path.GetRandomFileName());
var args = new List();
diff --git a/src/Cli/dotnet/CommonLocalizableStrings.resx b/src/Cli/dotnet/CommonLocalizableStrings.resx
index 58c9371ccd01..9ef2b4a474f9 100644
--- a/src/Cli/dotnet/CommonLocalizableStrings.resx
+++ b/src/Cli/dotnet/CommonLocalizableStrings.resx
@@ -698,6 +698,25 @@ setx PATH "%PATH%;{0}"
Specifying both the `-r|--runtime` and `-os` options is not supported.
+
+ 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 VAR1=abc -e VAR2=def -e VAR3=ghi
+
+
+
+ NAME="VALUE"
+
+
+ Incorrectly formatted environment variables: {0}
+
Publish the .NET runtime with your application so the runtime doesn't need to be installed on the target machine.
The default is 'false.' However, when targeting .NET 7 or lower, the default is 'true' if a runtime identifier is specified.
@@ -715,6 +734,10 @@ The default is 'false.' However, when targeting .NET 7 or lower, the default is
Response file '{0}' does not exist.
+
+ Search for available workload versions or what comprises a workload version. Use 'dotnet workload search version --help' for more information.
+ {Locked="dotnet workload search version --help"}
+
The solution file '{0}' is missing EndProject tags or has invalid child-parent project folder mappings around project GUID: '{1}'. Manually repair the solution or try to open and save it in Visual Studio."
diff --git a/src/Cli/dotnet/CommonOptions.cs b/src/Cli/dotnet/CommonOptions.cs
index 95a86ec9984b..f544d89cc38e 100644
--- a/src/Cli/dotnet/CommonOptions.cs
+++ b/src/Cli/dotnet/CommonOptions.cs
@@ -3,6 +3,7 @@
using System.CommandLine;
using System.CommandLine.Completions;
+using System.CommandLine.Parsing;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.Common;
@@ -174,6 +175,52 @@ internal static string ArchOptionValue(ParseResult parseResult) =>
// Flip the argument so that if this option is specified we get selfcontained=false
.SetForwardingFunction((arg, p) => ForwardSelfContainedOptions(!arg, p));
+ public static readonly CliOption> EnvOption = new("--environment", "-e")
+ {
+ Description = CommonLocalizableStrings.CmdEnvironmentVariableDescription,
+ HelpName = CommonLocalizableStrings.CmdEnvironmentVariableExpression,
+ CustomParser = ParseEnvironmentVariables,
+ // Can't allow multiple arguments because the separator needs to be parsed as part of the environment variable value.
+ AllowMultipleArgumentsPerToken = false
+ };
+
+ private static IReadOnlyDictionary ParseEnvironmentVariables(ArgumentResult argumentResult)
+ {
+ var result = new Dictionary(
+ RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal);
+
+ List? invalid = null;
+
+ foreach (var token in argumentResult.Tokens)
+ {
+ var separator = token.Value.IndexOf('=');
+ var (name, value) = (separator >= 0)
+ ? (token.Value[0..separator], token.Value[(separator + 1)..])
+ : (token.Value, "");
+
+ name = name.Trim();
+
+ if (name != "")
+ {
+ result[name] = value;
+ }
+ else
+ {
+ invalid ??= [];
+ invalid.Add(token);
+ }
+ }
+
+ if (invalid != null)
+ {
+ argumentResult.AddError(string.Format(
+ CommonLocalizableStrings.IncorrectlyFormattedEnvironmentVariables,
+ string.Join(", ", invalid.Select(x => $"'{x.Value}'"))));
+ }
+
+ return result;
+ }
+
public static readonly CliOption TestPlatformOption = new("--Platform");
public static readonly CliOption TestFrameworkOption = new("--Framework");
@@ -259,13 +306,6 @@ private static IEnumerable ForwardSelfContainedOptions(bool isSelfContai
return selfContainedProperties;
}
- private static bool UserSpecifiedRidOption(ParseResult parseResult) =>
- (parseResult.GetResult(RuntimeOption) ??
- parseResult.GetResult(LongFormRuntimeOption) ??
- parseResult.GetResult(ArchitectureOption) ??
- parseResult.GetResult(LongFormArchitectureOption) ??
- parseResult.GetResult(OperatingSystemOption)) is not null;
-
internal static CliOption AddCompletions(this CliOption option, Func> completionSource)
{
option.CompletionSources.Add(completionSource);
diff --git a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs
index 3b30a99345a4..201255fbdef9 100644
--- a/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs
+++ b/src/Cli/dotnet/NugetPackageDownloader/NuGetPackageDownloader.cs
@@ -552,7 +552,8 @@ await Task.WhenAll(
IEnumerable<(PackageSource source, IPackageSearchMetadata package)> accumulativeSearchResults =
foundPackagesBySource
- .SelectMany(result => result.foundPackages.Select(package => (result.source, package)));
+ .SelectMany(result => result.foundPackages.Select(package => (result.source, package)))
+ .Distinct();
if (!accumulativeSearchResults.Any())
{
@@ -570,7 +571,8 @@ await Task.WhenAll(
if (stableVersions.Any())
{
- return stableVersions.OrderByDescending(r => r.package.Identity.Version).Take(numberOfResults);
+ var results = stableVersions.OrderByDescending(r => r.package.Identity.Version);
+ return numberOfResults > 0 /* 0 indicates 'all' */ ? results.Take(numberOfResults) : results;
}
}
diff --git a/src/Cli/dotnet/Parser.cs b/src/Cli/dotnet/Parser.cs
index 04374a3e44ea..000f2c20a807 100644
--- a/src/Cli/dotnet/Parser.cs
+++ b/src/Cli/dotnet/Parser.cs
@@ -237,6 +237,8 @@ private static void SetHelpCustomizations(HelpBuilder builder)
};
builder.CustomizeSymbol(option, secondColumnText: descriptionCallback);
}
+
+ builder.CustomizeSymbol(WorkloadSearchVersionsCommandParser.GetCommand(), secondColumnText: CommonLocalizableStrings.ShortWorkloadSearchVersionDescription);
}
public void additionalOption(HelpContext context)
diff --git a/src/Cli/dotnet/ProjectInstanceExtensions.cs b/src/Cli/dotnet/ProjectInstanceExtensions.cs
index 29337fa22c35..eea4778316d3 100644
--- a/src/Cli/dotnet/ProjectInstanceExtensions.cs
+++ b/src/Cli/dotnet/ProjectInstanceExtensions.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Build.Execution;
-using Microsoft.DotNet.Cli.Sln.Internal;
namespace Microsoft.DotNet.Tools.Common
{
@@ -22,7 +21,7 @@ public static string GetDefaultProjectTypeGuid(this ProjectInstance projectInsta
string projectTypeGuid = projectInstance.GetPropertyValue("DefaultProjectTypeGuid");
if (string.IsNullOrEmpty(projectTypeGuid) && projectInstance.FullPath.EndsWith(".shproj", StringComparison.OrdinalIgnoreCase))
{
- projectTypeGuid = ProjectTypeGuids.SharedProjectGuid;
+ projectTypeGuid = "{D954291E-2A0B-460D-934E-DC6B0785DB48}";
}
return projectTypeGuid;
}
diff --git a/src/Cli/dotnet/Properties/launchSettings.json b/src/Cli/dotnet/Properties/launchSettings.json
index 8d0ebf35dc6f..d3e2ca78d63b 100644
--- a/src/Cli/dotnet/Properties/launchSettings.json
+++ b/src/Cli/dotnet/Properties/launchSettings.json
@@ -1,7 +1,9 @@
{
"profiles": {
"dotnet": {
- "commandName": "Project"
+ "commandName": "Project",
+ "commandLineArgs": "run -e MyCoolEnvironmentVariableKey=OverriddenEnvironmentVariableValue",
+ "workingDirectory": "C:\\sdk5\\artifacts\\tmp\\Debug\\EnvOptionLaun---86C8BD0D"
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs
index 2bb50a56e63a..c03f3dfdadaf 100644
--- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs
+++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs
@@ -4,11 +4,13 @@
using System.CommandLine;
using System.Diagnostics;
using Microsoft.Build.Execution;
-using Microsoft.DotNet.Cli.Sln.Internal;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.Common;
using Microsoft.NET.Build.Tasks;
+using Microsoft.VisualStudio.SolutionPersistence;
+using Microsoft.VisualStudio.SolutionPersistence.Model;
+using Microsoft.VisualStudio.SolutionPersistence.Serializer;
namespace Microsoft.DotNet.Cli
{
@@ -128,7 +130,7 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified()
}
catch (GracefulException) // Fall back to looking for a solution if multiple project files are found, or there's no project in the directory.
{
- string? potentialSln = Directory.GetFiles(arg, "*.sln", SearchOption.TopDirectoryOnly).FirstOrDefault();
+ string? potentialSln = SlnFileFactory.ListSolutionFilesInDirectory(arg, false).FirstOrDefault();
if (!string.IsNullOrEmpty(potentialSln))
{
@@ -144,10 +146,15 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified()
/// Throws exception if two+ projects disagree in PublishRelease, PackRelease, or whatever _propertyToCheck is, and have it defined.
public ProjectInstance? GetArbitraryProjectFromSolution(string slnPath, Dictionary globalProps)
{
- SlnFile sln;
+ string slnFullPath = Path.GetFullPath(slnPath);
+ if (!Path.Exists(slnFullPath))
+ {
+ return null;
+ }
+ SolutionModel sln;
try
{
- sln = SlnFileFactory.CreateFromFileOrDirectory(slnPath);
+ sln = SlnFileFactory.CreateFromFileOrDirectory(slnFullPath, false, false);
}
catch (GracefulException)
{
@@ -162,13 +169,13 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified()
if (string.Equals(Environment.GetEnvironmentVariable(EnvironmentVariableNames.DOTNET_CLI_LAZY_PUBLISH_AND_PACK_RELEASE_FOR_SOLUTIONS), "true", StringComparison.OrdinalIgnoreCase))
{
// Evaluate only one project for speed if this environment variable is used. Will break more customers if enabled (adding 8.0 project to SLN with other project TFMs with no Publish or PackRelease.)
- return GetSingleProjectFromSolution(sln, globalProps);
+ return GetSingleProjectFromSolution(sln, slnFullPath, globalProps);
}
- Parallel.ForEach(sln.Projects.AsEnumerable(), (project, state) =>
+ Parallel.ForEach(sln.SolutionProjects.AsEnumerable(), (project, state) =>
{
#pragma warning disable CS8604 // Possible null reference argument.
- string projectFullPath = Path.Combine(Path.GetDirectoryName(sln.FullPath), project.FilePath);
+ string projectFullPath = Path.Combine(Path.GetDirectoryName(slnFullPath), project.FilePath);
#pragma warning restore CS8604 // Possible null reference argument.
if (IsUnanalyzableProjectInSolution(project, projectFullPath))
return;
@@ -207,12 +214,12 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified()
/// The solution to get an arbitrary project from.
/// The global properties to load into the project.
/// null if no project exists in the solution that can be evaluated properly. Else, the first project in the solution that can be.
- private ProjectInstance? GetSingleProjectFromSolution(SlnFile sln, Dictionary globalProps)
+ private ProjectInstance? GetSingleProjectFromSolution(SolutionModel sln, string slnPath, Dictionary globalProps)
{
- foreach (var project in sln.Projects.AsEnumerable())
+ foreach (var project in sln.SolutionProjects.AsEnumerable())
{
#pragma warning disable CS8604 // Possible null reference argument.
- string projectFullPath = Path.Combine(Path.GetDirectoryName(sln.FullPath), project.FilePath);
+ string projectFullPath = Path.Combine(Path.GetDirectoryName(slnPath), project.FilePath);
#pragma warning restore CS8604 // Possible null reference argument.
if (IsUnanalyzableProjectInSolution(project, projectFullPath))
continue;
@@ -233,9 +240,9 @@ public IEnumerable GetCustomDefaultConfigurationValueIfSpecified()
/// The project under a solution to evaluate.
/// The full hard-coded path of the project.
/// True if the project is not supported by ProjectInstance class or appears to be invalid.
- private bool IsUnanalyzableProjectInSolution(SlnProject project, string projectFullPath)
+ private bool IsUnanalyzableProjectInSolution(SolutionProjectModel project, string projectFullPath)
{
- return project.TypeGuid == solutionFolderGuid || project.TypeGuid == sharedProjectGuid || !IsValidProjectFilePath(projectFullPath);
+ return project.TypeId.ToString() == solutionFolderGuid || project.TypeId.ToString() == sharedProjectGuid || !IsValidProjectFilePath(projectFullPath);
}
/// Creates a ProjectInstance if the project is valid, elsewise, fails.
@@ -261,7 +268,7 @@ private bool IsValidProjectFilePath(string path)
/// Returns true if the path exists and is a sln file type.
private bool IsValidSlnFilePath(string path)
{
- return File.Exists(path) && Path.GetExtension(path).EndsWith("sln");
+ return File.Exists(path) && (Path.GetExtension(path).Equals(".sln")|| Path.GetExtension(path).Equals(".slnx"));
}
/// A case-insensitive dictionary of any properties passed from the user and their values.
diff --git a/src/Cli/dotnet/SlnFileExtensions.cs b/src/Cli/dotnet/SlnFileExtensions.cs
deleted file mode 100644
index 22b5cc48c5a8..000000000000
--- a/src/Cli/dotnet/SlnFileExtensions.cs
+++ /dev/null
@@ -1,540 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.Build.Construction;
-using Microsoft.Build.Exceptions;
-using Microsoft.Build.Execution;
-using Microsoft.DotNet.Cli.Sln.Internal;
-using Microsoft.DotNet.Cli.Utils;
-
-namespace Microsoft.DotNet.Tools.Common
-{
- internal static class SlnFileExtensions
- {
- public static void AddProject(this SlnFile slnFile, string fullProjectPath, IList solutionFolders)
- {
- if (string.IsNullOrEmpty(fullProjectPath))
- {
- throw new ArgumentException();
- }
-
- var relativeProjectPath = Path.GetRelativePath(
- PathUtility.EnsureTrailingSlash(slnFile.BaseDirectory),
- fullProjectPath);
-
- if (slnFile.Projects.Any((p) =>
- string.Equals(p.FilePath, relativeProjectPath, StringComparison.OrdinalIgnoreCase)))
- {
- Reporter.Output.WriteLine(string.Format(
- CommonLocalizableStrings.SolutionAlreadyContainsProject,
- slnFile.FullPath,
- relativeProjectPath));
- }
- else
- {
- ProjectRootElement rootElement = null;
- ProjectInstance projectInstance = null;
- try
- {
- rootElement = ProjectRootElement.Open(fullProjectPath);
- projectInstance = new ProjectInstance(rootElement);
- }
- catch (InvalidProjectFileException e)
- {
- Reporter.Error.WriteLine(string.Format(
- CommonLocalizableStrings.InvalidProjectWithExceptionMessage,
- fullProjectPath,
- e.Message));
- return;
- }
-
- var slnProject = new SlnProject
- {
- Id = projectInstance.GetProjectId(),
- TypeGuid = rootElement.GetProjectTypeGuid() ?? projectInstance.GetDefaultProjectTypeGuid(),
- Name = Path.GetFileNameWithoutExtension(relativeProjectPath),
- FilePath = relativeProjectPath
- };
-
- if (string.IsNullOrEmpty(slnProject.TypeGuid))
- {
- Reporter.Error.WriteLine(
- string.Format(
- CommonLocalizableStrings.UnsupportedProjectType,
- projectInstance.FullPath));
- return;
- }
-
- // NOTE: The order you create the sections determines the order they are written to the sln
- // file. In the case of an empty sln file, in order to make sure the solution configurations
- // section comes first we need to add it first. This doesn't affect correctness but does
- // stop VS from re-ordering things later on. Since we are keeping the SlnFile class low-level
- // it shouldn't care about the VS implementation details. That's why we handle this here.
- if (AreBuildConfigurationsApplicable(slnProject.TypeGuid))
- {
- slnFile.AddDefaultBuildConfigurations();
-
- slnFile.MapSolutionConfigurationsToProject(
- projectInstance,
- slnFile.ProjectConfigurationsSection.GetOrCreatePropertySet(slnProject.Id));
- }
-
- SetupSolutionFolders(slnFile, solutionFolders, relativeProjectPath, slnProject);
-
- slnFile.Projects.Add(slnProject);
-
- Reporter.Output.WriteLine(
- string.Format(CommonLocalizableStrings.ProjectAddedToTheSolution, relativeProjectPath));
- }
- }
-
- private static bool AreBuildConfigurationsApplicable(string projectTypeGuid)
- {
- return !projectTypeGuid.Equals(ProjectTypeGuids.SharedProjectGuid, StringComparison.OrdinalIgnoreCase);
- }
-
- private static void SetupSolutionFolders(SlnFile slnFile, IList solutionFolders, string relativeProjectPath, SlnProject slnProject)
- {
- if (solutionFolders != null)
- {
- if (solutionFolders.Any())
- {
- // Before adding a solution folder, check if the name conflicts with any existing projects in the solution
- var duplicateProjects = slnFile.Projects.Where(p => solutionFolders.Contains(p.Name)
- && p.TypeGuid != ProjectTypeGuids.SolutionFolderGuid).ToList();
- foreach (SlnProject duplicateProject in duplicateProjects)
- {
- slnFile.AddSolutionFolders(duplicateProject, new List() { Path.GetDirectoryName(duplicateProject.FilePath) });
- }
- }
- else
- {
- // If a project and solution folder have the same name, add it's own folder as a solution folder
- // eg. foo\extensions.csproj and extensions\library\library.csproj would have a project and solution folder with conflicting names
- var duplicateProject = slnFile.Projects.Where(p => string.Equals(p.Name, slnProject.Name, StringComparison.OrdinalIgnoreCase)
- && p.TypeGuid == ProjectTypeGuids.SolutionFolderGuid).FirstOrDefault();
- if (duplicateProject != null)
- {
- // Try making a new folder for the project to put it under so we can still add it despite there being one with the same name already in the parent folder
- slnFile.AddSolutionFolders(slnProject, new List() { Path.GetDirectoryName(relativeProjectPath) });
- }
- }
- // Even if we added a solution folder above for a duplicate, we still need to add the expected folder for the current project
- slnFile.AddSolutionFolders(slnProject, solutionFolders);
- }
- }
-
- private static void AddDefaultBuildConfigurations(this SlnFile slnFile)
- {
- var configurationsSection = slnFile.SolutionConfigurationsSection;
-
- if (!configurationsSection.IsEmpty)
- {
- return;
- }
-
- var defaultConfigurations = new List()
- {
- "Debug|Any CPU",
- "Debug|x64",
- "Debug|x86",
- "Release|Any CPU",
- "Release|x64",
- "Release|x86",
- };
-
- foreach (var config in defaultConfigurations)
- {
- configurationsSection[config] = config;
- }
- }
-
- private static void MapSolutionConfigurationsToProject(
- this SlnFile slnFile,
- ProjectInstance projectInstance,
- SlnPropertySet solutionProjectConfigs)
- {
- var (projectConfigurations, defaultProjectConfiguration) = GetKeysDictionary(projectInstance.GetConfigurations());
- var (projectPlatforms, defaultProjectPlatform) = GetKeysDictionary(projectInstance.GetPlatforms());
-
- foreach (var solutionConfigKey in slnFile.SolutionConfigurationsSection.Keys)
- {
- var projectConfigKey = MapSolutionConfigKeyToProjectConfigKey(
- solutionConfigKey,
- projectConfigurations,
- defaultProjectConfiguration,
- projectPlatforms,
- defaultProjectPlatform);
- if (projectConfigKey == null)
- {
- continue;
- }
-
- var activeConfigKey = $"{solutionConfigKey}.ActiveCfg";
- if (!solutionProjectConfigs.ContainsKey(activeConfigKey))
- {
- solutionProjectConfigs[activeConfigKey] = projectConfigKey;
- }
-
- var buildKey = $"{solutionConfigKey}.Build.0";
- if (!solutionProjectConfigs.ContainsKey(buildKey))
- {
- solutionProjectConfigs[buildKey] = projectConfigKey;
- }
- }
- }
-
- private static (Dictionary Keys, string DefaultKey) GetKeysDictionary(IEnumerable keys)
- {
- // A dictionary mapping key -> key is used instead of a HashSet so the original case of the key can be retrieved from the set
- var dictionary = new Dictionary(StringComparer.CurrentCultureIgnoreCase);
-
- foreach (var key in keys)
- {
- dictionary[key] = key;
- }
-
- return (dictionary, keys.FirstOrDefault());
- }
-
- private static string GetMatchingProjectKey(IDictionary projectKeys, string solutionKey)
- {
- string projectKey;
- if (projectKeys.TryGetValue(solutionKey, out projectKey))
- {
- return projectKey;
- }
-
- var keyWithoutWhitespace = string.Concat(solutionKey.Where(c => !char.IsWhiteSpace(c)));
- if (projectKeys.TryGetValue(keyWithoutWhitespace, out projectKey))
- {
- return projectKey;
- }
-
- return null;
- }
-
- private static string MapSolutionConfigKeyToProjectConfigKey(
- string solutionConfigKey,
- Dictionary projectConfigurations,
- string defaultProjectConfiguration,
- Dictionary projectPlatforms,
- string defaultProjectPlatform)
- {
- var pair = solutionConfigKey.Split(new char[] { '|' }, 2);
- if (pair.Length != 2)
- {
- return null;
- }
-
- var projectConfiguration = GetMatchingProjectKey(projectConfigurations, pair[0]) ?? defaultProjectConfiguration;
- if (projectConfiguration == null)
- {
- return null;
- }
-
- var projectPlatform = GetMatchingProjectKey(projectPlatforms, pair[1]) ?? defaultProjectPlatform;
- if (projectPlatform == null)
- {
- return null;
- }
-
- // VS stores "Any CPU" platform in the solution regardless of how it is named at the project level
- return $"{projectConfiguration}|{(projectPlatform == "AnyCPU" ? "Any CPU" : projectPlatform)}";
- }
-
- private static void AddSolutionFolders(this SlnFile slnFile, SlnProject slnProject, IList solutionFolders)
- {
- if (solutionFolders.Any())
- {
- var nestedProjectsSection = slnFile.Sections.GetOrCreateSection(
- "NestedProjects",
- SlnSectionType.PreProcess);
-
- var pathToGuidMap = slnFile.GetSolutionFolderPaths(nestedProjectsSection.Properties);
-
- if (slnFile.HasSolutionFolder(nestedProjectsSection.Properties, slnProject))
- {
- return;
- }
-
- string parentDirGuid = null;
- var solutionFolderHierarchy = string.Empty;
- foreach (var dir in solutionFolders)
- {
- solutionFolderHierarchy = Path.Combine(solutionFolderHierarchy, dir);
- if (pathToGuidMap.ContainsKey(solutionFolderHierarchy))
- {
- parentDirGuid = pathToGuidMap[solutionFolderHierarchy];
- }
- else
- {
-
- if (HasDuplicateNameForSameValueOfNestedProjects(nestedProjectsSection, dir, parentDirGuid, slnFile.Projects))
- {
- throw new GracefulException(CommonLocalizableStrings.SolutionFolderAlreadyContainsProject, slnFile.FullPath, slnProject.Name, slnFile.Projects.FirstOrDefault(p => p.Id == parentDirGuid).Name);
- }
-
- var solutionFolder = new SlnProject
- {
- Id = Guid.NewGuid().ToString("B").ToUpper(),
- TypeGuid = ProjectTypeGuids.SolutionFolderGuid,
- Name = dir,
- FilePath = dir
- };
-
- slnFile.Projects.Add(solutionFolder);
-
- if (parentDirGuid != null)
- {
- nestedProjectsSection.Properties[solutionFolder.Id] = parentDirGuid;
- }
- parentDirGuid = solutionFolder.Id;
- }
- }
- if (HasDuplicateNameForSameValueOfNestedProjects(nestedProjectsSection, slnProject.Name, parentDirGuid, slnFile.Projects))
- {
- throw new GracefulException(CommonLocalizableStrings.SolutionFolderAlreadyContainsProject, slnFile.FullPath, slnProject.Name, slnFile.Projects.FirstOrDefault(p => p.Id == parentDirGuid).Name);
- }
- nestedProjectsSection.Properties[slnProject.Id] = parentDirGuid;
- }
- }
-
- private static bool HasDuplicateNameForSameValueOfNestedProjects(SlnSection nestedProjectsSection, string name, string value, IList projects)
- {
- foreach (var property in nestedProjectsSection.Properties)
- {
- if (property.Value == value)
- {
- var existingProject = projects.FirstOrDefault(p => p.Id == property.Key);
-
- if (existingProject != null && existingProject.Name == name)
- {
- return true;
- }
- }
- }
- return false;
- }
-
- private static IDictionary GetSolutionFolderPaths(
- this SlnFile slnFile,
- SlnPropertySet nestedProjects)
- {
- var solutionFolderPaths = new Dictionary(StringComparer.OrdinalIgnoreCase);
-
- var solutionFolderProjects = slnFile.Projects.GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid);
- foreach (var slnProject in solutionFolderProjects)
- {
- var path = slnProject.FilePath;
- var id = slnProject.Id;
- while (nestedProjects.ContainsKey(id))
- {
- id = nestedProjects[id];
- var parentSlnProject = solutionFolderProjects.Where(p => p.Id == id).SingleOrDefault();
- if (parentSlnProject == null) // see: https://github.com/dotnet/sdk/pull/28811
- throw new GracefulException(CommonLocalizableStrings.CorruptSolutionProjectFolderStructure, slnFile.FullPath, id);
- path = Path.Combine(parentSlnProject.FilePath, path);
- }
-
- solutionFolderPaths[path] = slnProject.Id;
- }
-
- return solutionFolderPaths;
- }
-
- private static bool HasSolutionFolder(
- this SlnFile slnFile,
- SlnPropertySet properties,
- SlnProject slnProject)
- {
- return properties.ContainsKey(slnProject.Id);
- }
-
- public static bool RemoveProject(this SlnFile slnFile, string projectPath)
- {
- if (string.IsNullOrEmpty(projectPath))
- {
- throw new ArgumentException();
- }
-
- var projectPathNormalized = PathUtility.GetPathWithDirectorySeparator(projectPath);
-
- var projectsToRemove = slnFile.Projects.Where((p) =>
- string.Equals(p.FilePath, projectPathNormalized, StringComparison.OrdinalIgnoreCase)).ToList();
-
- bool projectRemoved = false;
- if (projectsToRemove.Count == 0)
- {
- Reporter.Output.WriteLine(string.Format(
- CommonLocalizableStrings.ProjectNotFoundInTheSolution,
- projectPath));
- }
- else
- {
- foreach (var slnProject in projectsToRemove)
- {
- var buildConfigsToRemove = slnFile.ProjectConfigurationsSection.GetPropertySet(slnProject.Id);
- if (buildConfigsToRemove != null)
- {
- slnFile.ProjectConfigurationsSection.Remove(buildConfigsToRemove);
- }
-
- var nestedProjectsSection = slnFile.Sections.GetSection(
- "NestedProjects",
- SlnSectionType.PreProcess);
- if (nestedProjectsSection != null && nestedProjectsSection.Properties.ContainsKey(slnProject.Id))
- {
- nestedProjectsSection.Properties.Remove(slnProject.Id);
- }
-
- slnFile.Projects.Remove(slnProject);
- Reporter.Output.WriteLine(
- string.Format(CommonLocalizableStrings.ProjectRemovedFromTheSolution, slnProject.FilePath));
- }
-
- foreach (var project in slnFile.Projects)
- {
- var dependencies = project.Dependencies;
- if (dependencies == null)
- {
- continue;
- }
-
- dependencies.SkipIfEmpty = true;
-
- foreach (var removed in projectsToRemove)
- {
- dependencies.Properties.Remove(removed.Id);
- }
- }
-
- projectRemoved = true;
- }
-
- return projectRemoved;
- }
-
- public static void RemoveEmptyConfigurationSections(this SlnFile slnFile)
- {
- if (slnFile.Projects.Count == 0)
- {
- var solutionConfigs = slnFile.Sections.GetSection("SolutionConfigurationPlatforms");
- if (solutionConfigs != null)
- {
- slnFile.Sections.Remove(solutionConfigs);
- }
-
- var projectConfigs = slnFile.Sections.GetSection("ProjectConfigurationPlatforms");
- if (projectConfigs != null)
- {
- slnFile.Sections.Remove(projectConfigs);
- }
- }
- }
-
- public static void RemoveEmptySolutionFolders(this SlnFile slnFile)
- {
- var solutionFolderProjects = slnFile.Projects
- .GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid)
- .ToList();
-
- if (solutionFolderProjects.Any())
- {
- var nestedProjectsSection = slnFile.Sections.GetSection(
- "NestedProjects",
- SlnSectionType.PreProcess);
-
- if (nestedProjectsSection == null)
- {
- foreach (var solutionFolderProject in solutionFolderProjects)
- {
- if (solutionFolderProject.Sections.Count() == 0)
- {
- slnFile.Projects.Remove(solutionFolderProject);
- }
- }
- }
- else
- {
- var solutionFoldersInUse = slnFile.GetSolutionFoldersThatContainProjectsInItsHierarchy(
- nestedProjectsSection.Properties);
-
- solutionFoldersInUse.UnionWith(slnFile.GetSolutionFoldersThatContainSolutionItemsInItsHierarchy(
- nestedProjectsSection.Properties));
-
- foreach (var solutionFolderProject in solutionFolderProjects)
- {
- if (!solutionFoldersInUse.Contains(solutionFolderProject.Id))
- {
- nestedProjectsSection.Properties.Remove(solutionFolderProject.Id);
- if (solutionFolderProject.Sections.Count() == 0)
- {
- slnFile.Projects.Remove(solutionFolderProject);
- }
- }
- }
-
- if (nestedProjectsSection.IsEmpty)
- {
- slnFile.Sections.Remove(nestedProjectsSection);
- }
- }
- }
- }
-
- private static HashSet GetSolutionFoldersThatContainProjectsInItsHierarchy(
- this SlnFile slnFile,
- SlnPropertySet nestedProjects)
- {
- var solutionFoldersInUse = new HashSet();
-
- IEnumerable nonSolutionFolderProjects;
- nonSolutionFolderProjects = slnFile.Projects.GetProjectsNotOfType(
- ProjectTypeGuids.SolutionFolderGuid);
-
- foreach (var nonSolutionFolderProject in nonSolutionFolderProjects)
- {
- var id = nonSolutionFolderProject.Id;
- while (nestedProjects.ContainsKey(id))
- {
- id = nestedProjects[id];
- solutionFoldersInUse.Add(id);
- }
- }
-
- return solutionFoldersInUse;
- }
-
- private static HashSet GetSolutionFoldersThatContainSolutionItemsInItsHierarchy(
- this SlnFile slnFile,
- SlnPropertySet nestedProjects)
- {
- var solutionFoldersInUse = new HashSet();
-
- var solutionItemsFolderProjects = slnFile.Projects
- .GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid)
- .Where(ContainsSolutionItems);
-
- foreach (var solutionItemsFolderProject in solutionItemsFolderProjects)
- {
- var id = solutionItemsFolderProject.Id;
- solutionFoldersInUse.Add(id);
-
- while (nestedProjects.ContainsKey(id))
- {
- id = nestedProjects[id];
- solutionFoldersInUse.Add(id);
- }
- }
-
- return solutionFoldersInUse;
- }
-
- private static bool ContainsSolutionItems(SlnProject project)
- {
- return project.Sections
- .GetSection("SolutionItems", SlnSectionType.PreProcess) != null;
- }
- }
-}
diff --git a/src/Cli/dotnet/SlnFileFactory.cs b/src/Cli/dotnet/SlnFileFactory.cs
index 49a33de4c782..c93e30b3b0e8 100644
--- a/src/Cli/dotnet/SlnFileFactory.cs
+++ b/src/Cli/dotnet/SlnFileFactory.cs
@@ -1,86 +1,148 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Microsoft.DotNet.Cli.Sln.Internal;
+using System.Text.Json;
+using System.Text.Json.Nodes;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.VisualStudio.SolutionPersistence;
+using Microsoft.VisualStudio.SolutionPersistence.Model;
+using Microsoft.VisualStudio.SolutionPersistence.Serializer;
namespace Microsoft.DotNet.Tools.Common
{
public static class SlnFileFactory
{
- public static SlnFile CreateFromFileOrDirectory(string fileOrDirectory)
+ public static string GetSolutionFileFullPath(string slnFileOrDirectory, bool includeSolutionFilterFiles = false, bool includeSolutionXmlFiles = true)
{
- if (File.Exists(fileOrDirectory))
+ // Throw error if slnFileOrDirectory is an invalid path
+ if (string.IsNullOrWhiteSpace(slnFileOrDirectory) || slnFileOrDirectory.IndexOfAny(Path.GetInvalidPathChars()) != -1)
{
- return FromFile(fileOrDirectory);
+ throw new GracefulException(CommonLocalizableStrings.CouldNotFindSolutionOrDirectory);
}
- else
+ if (File.Exists(slnFileOrDirectory))
{
- return FromDirectory(fileOrDirectory);
+ return Path.GetFullPath(slnFileOrDirectory);
}
+ if (Directory.Exists(slnFileOrDirectory))
+ {
+ string[] files = ListSolutionFilesInDirectory(slnFileOrDirectory, includeSolutionFilterFiles, includeSolutionXmlFiles);
+ if (files.Length == 0)
+ {
+ throw new GracefulException(
+ CommonLocalizableStrings.CouldNotFindSolutionIn,
+ slnFileOrDirectory);
+ }
+ if (files.Length > 1)
+ {
+ throw new GracefulException(
+ CommonLocalizableStrings.MoreThanOneSolutionInDirectory,
+ slnFileOrDirectory);
+ }
+ return Path.GetFullPath(files.Single());
+ }
+ throw new GracefulException(
+ CommonLocalizableStrings.CouldNotFindSolutionOrDirectory,
+ slnFileOrDirectory);
}
- private static SlnFile FromFile(string solutionPath)
+
+ public static string[] ListSolutionFilesInDirectory(string directory, bool includeSolutionFilterFiles = false, bool includeSolutionXmlFiles = true)
{
- SlnFile slnFile = null;
- try
- {
- slnFile = SlnFile.Read(solutionPath);
- }
- catch (InvalidSolutionFormatException e)
+ return [
+ ..Directory.GetFiles(directory, "*.sln", SearchOption.TopDirectoryOnly),
+ ..(includeSolutionXmlFiles ? Directory.GetFiles(directory, "*.slnx", SearchOption.TopDirectoryOnly) : []),
+ ..(includeSolutionFilterFiles ? Directory.GetFiles(directory, "*.slnf", SearchOption.TopDirectoryOnly) : [])
+ ];
+ }
+
+ public static SolutionModel CreateFromFileOrDirectory(string fileOrDirectory, bool includeSolutionFilterFiles = false, bool includeSolutionXmlFiles = true)
+ {
+ string solutionPath = GetSolutionFileFullPath(fileOrDirectory, includeSolutionFilterFiles, includeSolutionXmlFiles);
+
+ if (solutionPath.HasExtension(".slnf"))
{
- throw new GracefulException(
- CommonLocalizableStrings.InvalidSolutionFormatString,
- solutionPath,
- e.Message);
+ return CreateFromFilteredSolutionFile(solutionPath);
}
- return slnFile;
+ ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(solutionPath) ?? throw new GracefulException(
+ CommonLocalizableStrings.CouldNotFindSolutionOrDirectory,
+ solutionPath);
+
+ return serializer.OpenAsync(solutionPath, CancellationToken.None).Result;
}
- private static SlnFile FromDirectory(string solutionDirectory)
+ public static SolutionModel CreateFromFilteredSolutionFile(string filteredSolutionPath)
{
- DirectoryInfo dir;
+ JsonDocument jsonDocument;
+ JsonElement jsonElement;
+ JsonElement filteredSolutionJsonElement;
+ string originalSolutionPath;
+ string originalSolutionPathAbsolute;
+ string[] filteredSolutionProjectPaths;
+
try
{
- dir = new DirectoryInfo(solutionDirectory);
- if (!dir.Exists)
+ jsonDocument = JsonDocument.Parse(File.ReadAllText(filteredSolutionPath));
+ jsonElement = jsonDocument.RootElement;
+ filteredSolutionJsonElement = jsonElement.GetProperty("solution");
+ originalSolutionPath = filteredSolutionJsonElement.GetProperty("path").GetString();
+ originalSolutionPathAbsolute = Path.GetFullPath(originalSolutionPath, Path.GetDirectoryName(filteredSolutionPath));
+ if (!File.Exists(originalSolutionPathAbsolute))
{
- throw new GracefulException(
- CommonLocalizableStrings.CouldNotFindSolutionOrDirectory,
- solutionDirectory);
+ throw new Exception();
}
+ filteredSolutionProjectPaths = filteredSolutionJsonElement.GetProperty("projects")
+ .EnumerateArray()
+ .Select(project => project.GetString())
+ .ToArray();
}
- catch (ArgumentException)
+ catch (Exception ex)
{
throw new GracefulException(
- CommonLocalizableStrings.CouldNotFindSolutionOrDirectory,
- solutionDirectory);
+ CommonLocalizableStrings.InvalidSolutionFormatString,
+ filteredSolutionPath, ex.Message);
}
- FileInfo[] files = dir.GetFiles("*.sln");
- if (files.Length == 0)
+ SolutionModel filteredSolution = new SolutionModel();
+ SolutionModel originalSolution = CreateFromFileOrDirectory(originalSolutionPathAbsolute);
+
+ foreach (var platform in originalSolution.Platforms)
{
- throw new GracefulException(
- CommonLocalizableStrings.CouldNotFindSolutionIn,
- solutionDirectory);
+ filteredSolution.AddPlatform(platform);
+ }
+ foreach (var buildType in originalSolution.BuildTypes)
+ {
+ filteredSolution.AddBuildType(buildType);
}
- if (files.Length > 1)
+ foreach (string path in filteredSolutionProjectPaths)
{
- throw new GracefulException(
- CommonLocalizableStrings.MoreThanOneSolutionInDirectory,
- solutionDirectory);
+ // Normalize path to use correct directory separator
+ string normalizedPath = path.Replace('\\', Path.DirectorySeparatorChar);
+
+ SolutionProjectModel project = originalSolution.FindProject(normalizedPath) ?? throw new GracefulException(
+ CommonLocalizableStrings.ProjectNotFoundInTheSolution,
+ normalizedPath,
+ originalSolutionPath);
+ filteredSolution.AddProject(project.FilePath, project.Type, project.Parent is null ? null : filteredSolution.AddFolder(project.Parent.Path));
}
- FileInfo solutionFile = files.Single();
- if (!solutionFile.Exists)
+ return filteredSolution;
+ }
+
+ public static string GetSolutionPathFromFilteredSolutionFile(string filteredSolutionPath)
+ {
+ try
+ {
+ JsonNode? jsonNode = JsonNode.Parse(File.ReadAllText(filteredSolutionPath));
+ string? originalSolutionPath = jsonNode?["solution"]?["path"]?.GetValue();
+ return originalSolutionPath!;
+ }
+ catch (Exception ex)
{
throw new GracefulException(
- CommonLocalizableStrings.CouldNotFindSolutionIn,
- solutionDirectory);
+ CommonLocalizableStrings.InvalidSolutionFormatString,
+ filteredSolutionPath, ex.Message);
}
-
- return FromFile(solutionFile.FullName);
}
}
}
diff --git a/src/Cli/dotnet/SlnProjectCollectionExtensions.cs b/src/Cli/dotnet/SlnProjectCollectionExtensions.cs
deleted file mode 100644
index f0d70ae4e640..000000000000
--- a/src/Cli/dotnet/SlnProjectCollectionExtensions.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.DotNet.Cli.Sln.Internal;
-
-namespace Microsoft.DotNet.Tools.Common
-{
- internal static class SlnProjectCollectionExtensions
- {
- public static IEnumerable GetProjectsByType(
- this SlnProjectCollection projects,
- string typeGuid)
- {
- return projects.Where(p => p.TypeGuid == typeGuid);
- }
-
- public static IEnumerable GetProjectsNotOfType(
- this SlnProjectCollection projects,
- string typeGuid)
- {
- return projects.Where(p => p.TypeGuid != typeGuid);
- }
- }
-}
diff --git a/src/Cli/dotnet/SlnProjectExtensions.cs b/src/Cli/dotnet/SlnProjectExtensions.cs
deleted file mode 100644
index 405c3b43cb88..000000000000
--- a/src/Cli/dotnet/SlnProjectExtensions.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using Microsoft.DotNet.Cli.Sln.Internal;
-
-namespace Microsoft.DotNet.Tools.Common
-{
- internal static class SlnProjectExtensions
- {
- public static string GetFullSolutionFolderPath(this SlnProject slnProject)
- {
- var slnFile = slnProject.ParentFile;
- var nestedProjects = slnFile.Sections
- .GetOrCreateSection("NestedProjects", SlnSectionType.PreProcess)
- .Properties;
- var solutionFolders = slnFile.Projects
- .GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid)
- .ToArray();
-
- string path = slnProject.Name;
- string id = slnProject.Id;
-
- // If the nested projects contains this project's id then it has a parent
- // Traverse from the project to each parent prepending the solution folder to the path
- while (nestedProjects.ContainsKey(id))
- {
- id = nestedProjects[id];
-
- string solutionFolderPath = solutionFolders.Single(p => p.Id == id).FilePath;
- path = Path.Combine(solutionFolderPath, path);
- }
-
- return path;
- }
- }
-}
diff --git a/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs b/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs
index 66c0f405f8e9..e04f071b6693 100644
--- a/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs
+++ b/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs
@@ -3,6 +3,7 @@
using System.CommandLine;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Configurer;
using NuGet.Common;
using NuGet.Configuration;
@@ -32,8 +33,8 @@ public static void OverrideEnvironmentVariableToTmp(ParseResult parseResult)
if (!OperatingSystem.IsWindows() && IsRunningUnderSudo() && IsRunningWorkloadCommand(parseResult))
{
string sudoHome = PathUtilities.CreateTempSubdirectory();
- var homeBeforeOverride = Path.Combine(Environment.GetEnvironmentVariable("HOME"));
- Environment.SetEnvironmentVariable("HOME", sudoHome);
+ var homeBeforeOverride = Path.Combine(Environment.GetEnvironmentVariable(CliFolderPathCalculator.DotnetHomeVariableName));
+ Environment.SetEnvironmentVariable(CliFolderPathCalculator.DotnetHomeVariableName, sudoHome);
CopyUserNuGetConfigToOverriddenHome(homeBeforeOverride);
}
diff --git a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
index 8f8f2295333b..3da0e9887571 100644
--- a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
+++ b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
@@ -2,12 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.CommandLine;
-using System.IO;
-using System.Linq;
-using System.Net.Http.Json;
-using System.Runtime.CompilerServices;
-using System.Text.Json;
-using System.Text.Json.Nodes;
using Microsoft.Deployment.DotNet.Releases;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Commands.DotNetWorkloads;
@@ -16,6 +10,7 @@
using Microsoft.DotNet.ToolPackage;
using Microsoft.DotNet.Workloads.Workload.History;
using Microsoft.DotNet.Workloads.Workload.Install;
+using Microsoft.DotNet.Workloads.Workload.Search;
using Microsoft.DotNet.Workloads.Workload.Update;
using Microsoft.Extensions.EnvironmentAbstractions;
using Microsoft.NET.Sdk.WorkloadManifestReader;
@@ -41,7 +36,7 @@ internal abstract class InstallingWorkloadCommand : WorkloadCommandBase
protected readonly string _fromRollbackDefinition;
protected int _fromHistorySpecified;
protected bool _historyManifestOnlyOption;
- protected string _workloadSetVersionFromCommandLine;
+ protected IEnumerable _workloadSetVersionFromCommandLine;
protected string _globalJsonPath;
protected string _workloadSetVersionFromGlobalJson;
protected readonly PackageSourceLocation _packageSourceLocation;
@@ -55,7 +50,7 @@ internal abstract class InstallingWorkloadCommand : WorkloadCommandBase
protected bool UseRollback => !string.IsNullOrWhiteSpace(_fromRollbackDefinition);
protected bool FromHistory => _fromHistorySpecified != 0;
- protected bool SpecifiedWorkloadSetVersionOnCommandLine => !string.IsNullOrWhiteSpace(_workloadSetVersionFromCommandLine);
+ protected bool SpecifiedWorkloadSetVersionOnCommandLine => _workloadSetVersionFromCommandLine?.Any() == true;
protected bool SpecifiedWorkloadSetVersionInGlobalJson => !string.IsNullOrWhiteSpace(_workloadSetVersionFromGlobalJson);
protected WorkloadHistoryState _WorkloadHistoryRecord
{
@@ -186,7 +181,7 @@ protected void UpdateWorkloadManifests(WorkloadHistoryRecorder recorder, ITransa
{
// This is essentially the same as updating to a specific workload set version, and we're now past the error check,
// so we can just use the same code path.
- _workloadSetVersionFromCommandLine = _WorkloadHistoryRecord.WorkloadSetVersion;
+ _workloadSetVersionFromCommandLine = [_WorkloadHistoryRecord.WorkloadSetVersion];
}
else if ((UseRollback || FromHistory) && updateToLatestWorkloadSet)
{
@@ -214,7 +209,39 @@ protected void UpdateWorkloadManifests(WorkloadHistoryRecorder recorder, ITransa
}
}
- string resolvedWorkloadSetVersion = _workloadSetVersionFromGlobalJson ??_workloadSetVersionFromCommandLine;
+ string resolvedWorkloadSetVersion = null;
+
+ if (_workloadSetVersionFromCommandLine?.Any(v => v.Contains('@')) == true)
+ {
+ var versions = WorkloadSearchVersionsCommand.FindBestWorkloadSetsFromComponents(
+ _sdkFeatureBand,
+ _workloadInstaller is not NetSdkMsiInstallerClient ? _workloadInstaller : null,
+ _sdkFeatureBand.IsPrerelease,
+ PackageDownloader,
+ _workloadSetVersionFromCommandLine,
+ _workloadResolver,
+ numberOfWorkloadSetsToTake: 1);
+
+ if (versions is null)
+ {
+ return;
+ }
+ else if (!versions.Any())
+ {
+ Reporter.WriteLine(Update.LocalizableStrings.NoWorkloadUpdateFound);
+ return;
+ }
+ else
+ {
+ resolvedWorkloadSetVersion = versions.Single();
+ }
+ }
+ else if (SpecifiedWorkloadSetVersionOnCommandLine)
+ {
+ resolvedWorkloadSetVersion = _workloadSetVersionFromCommandLine.Single();
+ }
+
+ resolvedWorkloadSetVersion = _workloadSetVersionFromGlobalJson ?? resolvedWorkloadSetVersion;
if (string.IsNullOrWhiteSpace(resolvedWorkloadSetVersion) && !UseRollback && !FromHistory)
{
_workloadManifestUpdater.UpdateAdvertisingManifestsAsync(_includePreviews, updateToLatestWorkloadSet, offlineCache).Wait();
@@ -441,9 +468,10 @@ protected IEnumerable WriteSDKInstallRecordsForVSWorkloads(IEnumerab
internal static class InstallingWorkloadCommandParser
{
- public static readonly CliOption WorkloadSetVersionOption = new("--version")
+ public static readonly CliOption> WorkloadSetVersionOption = new("--version")
{
- Description = Strings.WorkloadSetVersionOptionDescription
+ Description = Strings.WorkloadSetVersionOptionDescription,
+ AllowMultipleArgumentsPerToken = true
};
public static readonly CliOption PrintDownloadLinkOnlyOption = new("--print-download-link-only")
diff --git a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs
index 9ca3b27a500f..cbd10c1016f0 100644
--- a/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-list/dotnet-list-package/ListPackageReferencesCommand.cs
@@ -4,6 +4,7 @@
using System.CommandLine;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Tools.Common;
using Microsoft.DotNet.Tools.NuGet;
namespace Microsoft.DotNet.Tools.List.PackageReferences
@@ -71,7 +72,7 @@ private string GetProjectOrSolution()
if (Directory.Exists(resultPath))
{
- var possibleSolutionPath = Directory.GetFiles(resultPath, "*.sln", SearchOption.TopDirectoryOnly);
+ string[] possibleSolutionPath = SlnFileFactory.ListSolutionFilesInDirectory(resultPath, false);
//If more than a single sln file is found, an error is thrown since we can't determine which one to choose.
if (possibleSolutionPath.Count() > 1)
diff --git a/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs b/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs
index 8321f77d69ed..00c0d66fa6b7 100644
--- a/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs
+++ b/src/Cli/dotnet/commands/dotnet-new/PostActions/DotnetSlnPostActionProcessor.cs
@@ -25,7 +25,8 @@ public DotnetSlnPostActionProcessor(Func, string?,
internal static IReadOnlyList FindSolutionFilesAtOrAbovePath(IPhysicalFileSystem fileSystem, string outputBasePath)
{
- return FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.sln");
+ return FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.sln")
+ ?? FileFindHelpers.FindFilesAtOrAbovePath(fileSystem, outputBasePath, "*.slnx");
}
// The project files to add are a subset of the primary outputs, specifically the primary outputs indicated by the primaryOutputIndexes post action argument (semicolon separated)
diff --git a/src/Cli/dotnet/commands/dotnet-run/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-run/LocalizableStrings.resx
index ca6c5bee84ad..7835c0542477 100644
--- a/src/Cli/dotnet/commands/dotnet-run/LocalizableStrings.resx
+++ b/src/Cli/dotnet/commands/dotnet-run/LocalizableStrings.resx
@@ -139,6 +139,9 @@
Do not attempt to use launchSettings.json to configure the application.
+
+ Do not use arguments specified in launch profile to run the application.
+
The configuration to run for. The default for most projects is 'Debug'.
diff --git a/src/Cli/dotnet/commands/dotnet-run/Program.cs b/src/Cli/dotnet/commands/dotnet-run/Program.cs
index cefc00ba84b6..1dee09851a17 100644
--- a/src/Cli/dotnet/commands/dotnet-run/Program.cs
+++ b/src/Cli/dotnet/commands/dotnet-run/Program.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 Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
@@ -58,11 +59,13 @@ public static RunCommand FromParseResult(ParseResult parseResult)
projectFileOrDirectory: parseResult.GetValue(RunCommandParser.ProjectOption),
launchProfile: parseResult.GetValue(RunCommandParser.LaunchProfileOption),
noLaunchProfile: parseResult.HasOption(RunCommandParser.NoLaunchProfileOption),
+ noLaunchProfileArguments: parseResult.HasOption(RunCommandParser.NoLaunchProfileArgumentsOption),
noRestore: parseResult.HasOption(RunCommandParser.NoRestoreOption) || parseResult.HasOption(RunCommandParser.NoBuildOption),
interactive: parseResult.HasOption(RunCommandParser.InteractiveOption),
verbosity: parseResult.HasOption(CommonOptions.VerbosityOption) ? parseResult.GetValue(CommonOptions.VerbosityOption) : null,
- restoreArgs: restoreArgs.ToArray(),
- args: nonBinLogArgs.ToArray()
+ restoreArgs: [.. restoreArgs],
+ args: [.. nonBinLogArgs],
+ environmentVariables: parseResult.GetValue(CommonOptions.EnvOption) ?? ImmutableDictionary.Empty
);
return command;
diff --git a/src/Cli/dotnet/commands/dotnet-run/RunCommand.cs b/src/Cli/dotnet/commands/dotnet-run/RunCommand.cs
index 6f802257c3be..e9598d7b1643 100644
--- a/src/Cli/dotnet/commands/dotnet-run/RunCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-run/RunCommand.cs
@@ -18,40 +18,55 @@ public partial class RunCommand
{
private record RunProperties(string? RunCommand, string? RunArguments, string? RunWorkingDirectory);
- public bool NoBuild { get; private set; }
- public string ProjectFileFullPath { get; private set; }
+ public bool NoBuild { get; }
+ public string? ProjectFileOrDirectory { get; }
+ public string ProjectFileFullPath { get; }
public string[] Args { get; set; }
- public bool NoRestore { get; private set; }
+ public bool NoRestore { get; }
public VerbosityOptions? Verbosity { get; }
- public bool Interactive { get; private set; }
- public string[] RestoreArgs { get; private set; }
+ public bool Interactive { get; }
+ public string[] RestoreArgs { get; }
+
+ ///
+ /// Environment variables specified on command line via -e option.
+ ///
+ public IReadOnlyDictionary EnvironmentVariables { get; }
private bool ShouldBuild => !NoBuild;
- public string LaunchProfile { get; private set; }
- public bool NoLaunchProfile { get; private set; }
- private bool UseLaunchProfile => !NoLaunchProfile;
+ public string LaunchProfile { get; }
+ public bool NoLaunchProfile { get; }
+
+ ///
+ /// True to ignore command line arguments specified by launch profile.
+ ///
+ public bool NoLaunchProfileArguments { get; }
public RunCommand(
bool noBuild,
string? projectFileOrDirectory,
string launchProfile,
bool noLaunchProfile,
+ bool noLaunchProfileArguments,
bool noRestore,
bool interactive,
VerbosityOptions? verbosity,
string[] restoreArgs,
- string[] args)
+ string[] args,
+ IReadOnlyDictionary environmentVariables)
{
NoBuild = noBuild;
+ ProjectFileOrDirectory = projectFileOrDirectory;
ProjectFileFullPath = DiscoverProjectFilePath(projectFileOrDirectory);
LaunchProfile = launchProfile;
NoLaunchProfile = noLaunchProfile;
+ NoLaunchProfileArguments = noLaunchProfileArguments;
Args = args;
Interactive = interactive;
NoRestore = noRestore;
Verbosity = verbosity;
RestoreArgs = GetRestoreArguments(restoreArgs);
+ EnvironmentVariables = environmentVariables;
}
public int Execute()
@@ -74,10 +89,18 @@ public int Execute()
try
{
ICommand targetCommand = GetTargetCommand();
- var launchSettingsCommand = ApplyLaunchSettingsProfileToCommand(targetCommand, launchSettings);
+ ApplyLaunchSettingsProfileToCommand(targetCommand, launchSettings);
+
+ // Env variables specified on command line override those specified in launch profile:
+ foreach (var (name, value) in EnvironmentVariables)
+ {
+ targetCommand.EnvironmentVariable(name, value);
+ }
+
// Ignore Ctrl-C for the remainder of the command's execution
Console.CancelKeyPress += (sender, e) => { e.Cancel = true; };
- return launchSettingsCommand.Execute().ExitCode;
+
+ return targetCommand.Execute().ExitCode;
}
catch (InvalidProjectFileException e)
{
@@ -87,35 +110,37 @@ public int Execute()
}
}
- private ICommand ApplyLaunchSettingsProfileToCommand(ICommand targetCommand, ProjectLaunchSettingsModel? launchSettings)
+ private void ApplyLaunchSettingsProfileToCommand(ICommand targetCommand, ProjectLaunchSettingsModel? launchSettings)
{
- if (launchSettings != null)
+ if (launchSettings == null)
{
- if (!string.IsNullOrEmpty(launchSettings.ApplicationUrl))
- {
- targetCommand.EnvironmentVariable("ASPNETCORE_URLS", launchSettings.ApplicationUrl);
- }
+ return;
+ }
- targetCommand.EnvironmentVariable("DOTNET_LAUNCH_PROFILE", launchSettings.LaunchProfileName);
+ if (!string.IsNullOrEmpty(launchSettings.ApplicationUrl))
+ {
+ targetCommand.EnvironmentVariable("ASPNETCORE_URLS", launchSettings.ApplicationUrl);
+ }
- foreach (var entry in launchSettings.EnvironmentVariables)
- {
- string value = Environment.ExpandEnvironmentVariables(entry.Value);
- //NOTE: MSBuild variables are not expanded like they are in VS
- targetCommand.EnvironmentVariable(entry.Key, value);
- }
- if (string.IsNullOrEmpty(targetCommand.CommandArgs) && launchSettings.CommandLineArgs != null)
- {
- targetCommand.SetCommandArgs(launchSettings.CommandLineArgs);
- }
+ targetCommand.EnvironmentVariable("DOTNET_LAUNCH_PROFILE", launchSettings.LaunchProfileName);
+
+ foreach (var entry in launchSettings.EnvironmentVariables)
+ {
+ string value = Environment.ExpandEnvironmentVariables(entry.Value);
+ //NOTE: MSBuild variables are not expanded like they are in VS
+ targetCommand.EnvironmentVariable(entry.Key, value);
+ }
+
+ if (!NoLaunchProfileArguments && string.IsNullOrEmpty(targetCommand.CommandArgs) && launchSettings.CommandLineArgs != null)
+ {
+ targetCommand.SetCommandArgs(launchSettings.CommandLineArgs);
}
- return targetCommand;
}
private bool TryGetLaunchProfileSettingsIfNeeded(out ProjectLaunchSettingsModel? launchSettingsModel)
{
launchSettingsModel = default;
- if (!UseLaunchProfile)
+ if (NoLaunchProfile)
{
return true;
}
diff --git a/src/Cli/dotnet/commands/dotnet-run/RunCommandParser.cs b/src/Cli/dotnet/commands/dotnet-run/RunCommandParser.cs
index f2fff28a7e69..22a6e3784be3 100644
--- a/src/Cli/dotnet/commands/dotnet-run/RunCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-run/RunCommandParser.cs
@@ -34,6 +34,11 @@ internal static class RunCommandParser
Description = LocalizableStrings.CommandOptionNoLaunchProfileDescription
};
+ public static readonly CliOption NoLaunchProfileArgumentsOption = new("--no-launch-profile-arguments")
+ {
+ Description = LocalizableStrings.CommandOptionNoLaunchProfileArgumentsDescription
+ };
+
public static readonly CliOption NoBuildOption = new("--no-build")
{
Description = LocalizableStrings.CommandOptionNoBuildDescription
@@ -81,6 +86,7 @@ private static CliCommand ConstructCommand()
command.Options.Add(CommonOptions.OperatingSystemOption);
command.Options.Add(CommonOptions.DisableBuildServersOption);
command.Options.Add(CommonOptions.ArtifactsPathOption);
+ command.Options.Add(CommonOptions.EnvOption);
command.Arguments.Add(ApplicationArguments);
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.cs.xlf
index ad803e3945c6..59ada52ff3af 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.cs.xlf
@@ -17,6 +17,11 @@
K sestavení a spuštění aplikace se použije určená platforma. Tato platforma musí být zadaná v souboru projektu.
+
+ Do not use arguments specified in launch profile to run the application.
+ Ke spuštění aplikace nepoužívejte argumenty zadané v profilu spuštění.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
Cesta ke spouštěnému souboru projektu (výchozí je aktuální adresář, pokud existuje jenom jeden projekt)
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.de.xlf
index 44a232687444..ecec47ebfcae 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.de.xlf
@@ -17,6 +17,11 @@
Erstellt die App mit dem angegebenen Framework und führt sie aus. Das Framework muss in der Projektdatei angegeben werden.
+
+ Do not use arguments specified in launch profile to run the application.
+ Verwenden Sie die im Startprofil angegebenen Argumente nicht zum Ausführen der Anwendung.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
Der Pfad zur auszuführenden Projektdatei (standardmäßig das aktuelle Verzeichnis, falls nur ein einzelnes Projekt vorhanden ist).
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.es.xlf
index 7f8d692f3ddd..63695e47e069 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.es.xlf
@@ -17,6 +17,11 @@
Compila y ejecuta la aplicación con la plataforma especificada. La plataforma se debe especificar en el archivo de proyecto.
+
+ Do not use arguments specified in launch profile to run the application.
+ No use los argumentos especificados en el perfil de inicio para ejecutar la aplicación.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
Ruta de acceso al archivo del proyecto que debe ejecutarse (si solo hay un proyecto, es el directorio actual de forma predeterminada).
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.fr.xlf
index 765e8c7a91eb..286a7a541634 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.fr.xlf
@@ -17,6 +17,11 @@
Générez et exécutez l'application à l'aide du framework spécifié. Le framework doit être spécifié dans le fichier projet.
+
+ Do not use arguments specified in launch profile to run the application.
+ N’utilisez pas les arguments spécifiés dans le profil de lancement pour exécuter l’application.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
Chemin du fichier projet à exécuter (la valeur par défaut est le répertoire actif s'il n'y a qu'un seul projet).
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.it.xlf
index 64c2db2f1e9c..5f52c5e48fae 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.it.xlf
@@ -17,6 +17,11 @@
Consente di compilare ed eseguire l'app con il framework specificato. Il framework deve essere specificato nel file di progetto.
+
+ Do not use arguments specified in launch profile to run the application.
+ Non utilizzare gli argomenti specificati nel profilo di avvio per eseguire l'applicazione.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
Percorso del file di progetto da eseguire. Se è presente un solo progetto, per impostazione predefinita viene usata la directory corrente.
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ja.xlf
index c1e3dc0c821d..ab29ec8d15c3 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ja.xlf
@@ -17,6 +17,11 @@
指定されたフレームワークを使用して、アプリを構築して実行します。フレームワークはプロジェクト ファイルで指定する必要があります。
+
+ Do not use arguments specified in launch profile to run the application.
+ 起動プロファイルで指定された引数を使用してアプリケーションを実行しないでください。
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
実行するプロジェクト ファイルへのパス (プロジェクトが 1 つのみの場合、既定は現在のディレクトリです)。
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ko.xlf
index fedd253c635a..a2b77c1bc8bf 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ko.xlf
@@ -17,6 +17,11 @@
지정된 프레임워크를 사용하여 앱을 빌드 및 실행합니다. 프로젝트 파일에서 프레임워크를 지정해야 합니다.
+
+ Do not use arguments specified in launch profile to run the application.
+ 시작 프로필에 지정된 인수를 사용하여 애플리케이션을 실행하지 마세요.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
실행할 프로젝트 파일의 경로입니다(프로젝트가 하나만 있는 경우 현재 디렉터리로 기본 설정됨).
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pl.xlf
index 36aba105fc80..d013b5a9a62f 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pl.xlf
@@ -17,6 +17,11 @@
Skompiluj i uruchom aplikację przy użyciu określonej platformy. Platforma musi być określona w pliku projektu.
+
+ Do not use arguments specified in launch profile to run the application.
+ Nie używaj argumentów określonych w profilu uruchamiania do uruchamiania aplikacji.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
Ścieżka do pliku projektu, który ma zostać uruchomiony (w przypadku tylko jednego projektu wartością domyślną jest bieżący katalog).
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pt-BR.xlf
index 334e435c446c..258d2670d7e7 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.pt-BR.xlf
@@ -17,6 +17,11 @@
Compila e executa um aplicativo usando a estrutura especificada. A estrutura deve ser especificada no arquivo de projeto.
+
+ Do not use arguments specified in launch profile to run the application.
+ Não use argumentos especificados no perfil de inicialização para executar o aplicativo.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
O caminho para o arquivo de projeto a ser executado (usará como padrão o diretório atual se houver apenas um projeto).
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ru.xlf
index 4d27d2d19ecc..78de4b5ebcd1 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.ru.xlf
@@ -17,6 +17,11 @@
Сборка и выполнение приложения с использованием определенной платформы. Платформа должна быть указана в файле проекта.
+
+ Do not use arguments specified in launch profile to run the application.
+ Не используйте аргументы, указанные в профиле запуска, для запуска приложения.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
Путь к выполняемому файлу проекта (по умолчанию — текущий каталог, если имеется только один проект).
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.tr.xlf
index 3a96699af96c..167786e6277a 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.tr.xlf
@@ -17,6 +17,11 @@
Uygulamayı belirtilen çerçeveyi kullanarak derleyin ve çalıştırın. Çerçevenin proje dosyasında belirtilmesi gerekir.
+
+ Do not use arguments specified in launch profile to run the application.
+ Uygulamayı çalıştırmak için başlatma profilinde belirtilen bağımsız değişkenleri kullanma.
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
Çalıştırılacak proje dosyasının yolu (yalnızca bir proje varsa, geçerli dizin varsayılan olarak kullanılır).
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hans.xlf
index 995d5b6ab239..799e30f616a5 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hans.xlf
@@ -17,6 +17,11 @@
使用指定的框架生成和运行应用。框架必须在项目文件中指定。
+
+ Do not use arguments specified in launch profile to run the application.
+ 不要使用启动配置文件中指定的参数来运行应用程序。
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
要运行的项目文件的路径(如果只有一个项目,则默认使用当前目录)。
diff --git a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hant.xlf
index a2aa993f7344..cec07d6b8f89 100644
--- a/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/commands/dotnet-run/xlf/LocalizableStrings.zh-Hant.xlf
@@ -17,6 +17,11 @@
使用指定的架構建置及執行應用程式。架構必須在專案檔中指定。
+
+ Do not use arguments specified in launch profile to run the application.
+ 不要使用啟動配置檔中指定的自變數來執行應用程式。
+
+
The path to the project file to run (defaults to the current directory if there is only one project).
要執行之專案檔的路徑 (如果只有一個專案,預設為目前的目錄)。
diff --git a/src/Cli/dotnet/commands/dotnet-sln/SlnCommandParser.cs b/src/Cli/dotnet/commands/dotnet-sln/SlnCommandParser.cs
index b2a2f685b752..0155ddd5e203 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/SlnCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-sln/SlnCommandParser.cs
@@ -47,39 +47,6 @@ private static CliCommand ConstructCommand()
return command;
}
-
- internal static string GetSlnFileFullPath(string slnFileOrDirectory)
- {
- if (File.Exists(slnFileOrDirectory))
- {
- return Path.GetFullPath(slnFileOrDirectory);
- }
- if (Directory.Exists(slnFileOrDirectory))
- {
- string[] files = [
- ..Directory.GetFiles(slnFileOrDirectory, "*.sln", SearchOption.TopDirectoryOnly),
- ..Directory.GetFiles(slnFileOrDirectory, "*.slnx", SearchOption.TopDirectoryOnly)];
- if (files.Length == 0)
- {
- throw new GracefulException(CommonLocalizableStrings.CouldNotFindSolutionIn, slnFileOrDirectory);
- }
- if (files.Length > 1)
- {
- throw new GracefulException(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, slnFileOrDirectory);
- }
- return Path.GetFullPath(files.Single());
- }
- throw new GracefulException(CommonLocalizableStrings.CouldNotFindSolutionOrDirectory, slnFileOrDirectory);
- }
-
- internal static ISolutionSerializer GetSolutionSerializer(string solutionFilePath)
- {
- ISolutionSerializer? serializer = SolutionSerializers.GetSerializerByMoniker(solutionFilePath);
- if (serializer is null)
- {
- throw new GracefulException(LocalizableStrings.SerializerNotFound, solutionFilePath);
- }
- return serializer;
- }
}
+
}
diff --git a/src/Cli/dotnet/commands/dotnet-sln/add/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/add/Program.cs
index e7c7d39e1291..4fee53f6d44d 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/add/Program.cs
+++ b/src/Cli/dotnet/commands/dotnet-sln/add/Program.cs
@@ -46,7 +46,7 @@ public override int Execute()
{
throw new GracefulException(CommonLocalizableStrings.SpecifyAtLeastOneProjectToAdd);
}
- string solutionFileFullPath = SlnCommandParser.GetSlnFileFullPath(_fileOrDirectory);
+ string solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory);
try
{
@@ -73,8 +73,8 @@ public override int Execute()
private async Task AddProjectsToSolutionAsync(string solutionFileFullPath, IEnumerable projectPaths, CancellationToken cancellationToken)
{
- ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(solutionFileFullPath);
- SolutionModel solution = await serializer.OpenAsync(solutionFileFullPath, cancellationToken);
+ SolutionModel solution = SlnFileFactory.CreateFromFileOrDirectory(solutionFileFullPath);
+ ISolutionSerializer serializer = solution.SerializerExtension.Serializer;
// set UTF8 BOM encoding for .sln
if (serializer is ISolutionSerializer v12Serializer)
{
@@ -82,15 +82,15 @@ private async Task AddProjectsToSolutionAsync(string solutionFileFullPath, IEnum
{
Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)
});
- }
- // Set default configurations and platforms for sln file
- foreach (var platform in _defaultPlatforms)
- {
- solution.AddPlatform(platform);
- }
- foreach (var buildType in _defaultBuildTypes)
- {
- solution.AddBuildType(buildType);
+ // Set default configurations and platforms for sln file
+ foreach (var platform in _defaultPlatforms)
+ {
+ solution.AddPlatform(platform);
+ }
+ foreach (var buildType in _defaultBuildTypes)
+ {
+ solution.AddBuildType(buildType);
+ }
}
SolutionFolderModel? solutionFolder = (!_inRoot && !string.IsNullOrEmpty(_solutionFolderPath))
diff --git a/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs
index 99b948207011..b3957e2bff01 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs
+++ b/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs
@@ -3,8 +3,8 @@
using System.CommandLine;
using Microsoft.DotNet.Cli;
-using Microsoft.DotNet.Cli.Sln.Internal;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Tools.Common;
using Microsoft.VisualStudio.SolutionPersistence;
using Microsoft.VisualStudio.SolutionPersistence.Model;
using CommandLocalizableStrings = Microsoft.DotNet.Tools.CommonLocalizableStrings;
@@ -25,10 +25,10 @@ public ListProjectsInSolutionCommand(
public override int Execute()
{
- string solutionFileFullPath = SlnCommandParser.GetSlnFileFullPath(_fileOrDirectory);
+ string solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory, includeSolutionFilterFiles: true);
try
{
- ListAllProjectsAsync(solutionFileFullPath, CancellationToken.None).Wait();
+ ListAllProjectsAsync(solutionFileFullPath);
return 0;
}
catch (Exception ex)
@@ -37,10 +37,9 @@ public override int Execute()
}
}
- private async Task ListAllProjectsAsync(string solutionFileFullPath, CancellationToken cancellationToken)
+ private void ListAllProjectsAsync(string solutionFileFullPath)
{
- ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(solutionFileFullPath);
- SolutionModel solution = await serializer.OpenAsync(solutionFileFullPath, cancellationToken);
+ SolutionModel solution = SlnFileFactory.CreateFromFileOrDirectory(solutionFileFullPath);
string[] paths;
if (_displaySolutionFolders)
{
diff --git a/src/Cli/dotnet/commands/dotnet-sln/migrate/SlnMigrateCommand.cs b/src/Cli/dotnet/commands/dotnet-sln/migrate/SlnMigrateCommand.cs
index 1ac8dde1468e..56170cc4992a 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/migrate/SlnMigrateCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-sln/migrate/SlnMigrateCommand.cs
@@ -33,7 +33,7 @@ public SlnMigrateCommand(
public override int Execute()
{
- string slnFileFullPath = SlnCommandParser.GetSlnFileFullPath(_slnFileOrDirectory);
+ string slnFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_slnFileOrDirectory);
if (slnFileFullPath.HasExtension(".slnx"))
{
throw new GracefulException(LocalizableStrings.CannotMigrateSlnx);
@@ -50,8 +50,7 @@ public override int Execute()
private async Task ConvertToSlnxAsync(string filePath, string slnxFilePath, CancellationToken cancellationToken)
{
- ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(filePath);
- SolutionModel solution = await serializer.OpenAsync(filePath, cancellationToken);
+ SolutionModel solution = SlnFileFactory.CreateFromFileOrDirectory(filePath);
await SolutionSerializers.SlnXml.SaveAsync(slnxFilePath, solution, cancellationToken);
_reporter.WriteLine(LocalizableStrings.SlnxGenerated, slnxFilePath);
}
diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs
index 766ce64d9b7c..a48ea20f50ce 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs
+++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs
@@ -19,28 +19,6 @@ internal class RemoveProjectFromSolutionCommand : CommandBase
private readonly string _fileOrDirectory;
private readonly IReadOnlyCollection _projects;
- private int CountNonFolderDescendants(
- SolutionModel solution,
- SolutionFolderModel item,
- Dictionary solutionItemsGroupedByParent,
- Dictionary cached)
- {
- if (cached.ContainsKey(item))
- {
- return cached[item];
- }
- int count = item.Files?.Count ?? 0;
- var children = solutionItemsGroupedByParent.TryGetValue(item, out var items) ? items : Array.Empty();
- foreach (var child in children)
- {
- count += child is SolutionFolderModel folderModel
- ? CountNonFolderDescendants(solution, folderModel, solutionItemsGroupedByParent, cached)
- : 1;
- }
- cached.Add(item, count);
- return count;
- }
-
public RemoveProjectFromSolutionCommand(ParseResult parseResult) : base(parseResult)
{
_fileOrDirectory = parseResult.GetValue(SlnCommandParser.SlnArgument);
@@ -52,7 +30,7 @@ public RemoveProjectFromSolutionCommand(ParseResult parseResult) : base(parseRes
public override int Execute()
{
- string solutionFileFullPath = SlnCommandParser.GetSlnFileFullPath(_fileOrDirectory);
+ string solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory);
if (_projects.Count == 0)
{
throw new GracefulException(CommonLocalizableStrings.SpecifyAtLeastOneProjectToRemove);
@@ -69,7 +47,7 @@ public override int Execute()
? MsbuildProject.GetProjectFileFromDirectory(fullPath).FullName
: fullPath);
});
- RemoveProjectsAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).Wait();
+ RemoveProjectsAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).GetAwaiter().GetResult();
return 0;
}
catch (Exception ex) when (ex is not GracefulException)
@@ -78,20 +56,16 @@ public override int Execute()
{
throw new GracefulException(CommonLocalizableStrings.InvalidSolutionFormatString, solutionFileFullPath, ex.Message);
}
- if (ex.InnerException is GracefulException)
- {
- throw ex.InnerException;
- }
throw new GracefulException(ex.Message, ex);
}
}
private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable projectPaths, CancellationToken cancellationToken)
{
- ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(solutionFileFullPath);
- SolutionModel solution = await serializer.OpenAsync(solutionFileFullPath, cancellationToken);
+ SolutionModel solution = SlnFileFactory.CreateFromFileOrDirectory(solutionFileFullPath);
+ ISolutionSerializer serializer = solution.SerializerExtension.Serializer;
- // set UTF8 BOM encoding for .sln
+ // set UTF-8 BOM encoding for .sln
if (serializer is ISolutionSerializer v12Serializer)
{
solution.SerializerExtension = v12Serializer.CreateModelExtension(new()
@@ -114,21 +88,40 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable<
}
}
- Dictionary solutionItemsGroupedByParent = solution.SolutionItems
- .Where(i => i.Parent != null)
- .GroupBy(i => i.Parent)
- .ToDictionary(g => g.Key, g => g.ToArray());
-
- Dictionary nonFolderDescendantsCount = new();
- foreach (var item in solution.SolutionFolders)
+ for (int i = 0; i < solution.SolutionFolders.Count; i++)
{
- CountNonFolderDescendants(solution, item, solutionItemsGroupedByParent, nonFolderDescendantsCount);
- }
+ var folder = solution.SolutionFolders[i];
+ int nonFolderDescendants = 0;
+ Stack stack = new();
+ stack.Push(folder);
- var emptyFolders = nonFolderDescendantsCount.Where(i => i.Value == 0).Select(i => i.Key);
- foreach (var folder in emptyFolders)
- {
- solution.RemoveFolder(folder);
+ while (stack.Count > 0)
+ {
+ var current = stack.Pop();
+
+ nonFolderDescendants += current.Files?.Count ?? 0;
+ foreach (var child in solution.SolutionItems)
+ {
+ if (child is { Parent: var parent } && parent == current)
+ {
+ if (child is SolutionFolderModel childFolder)
+ {
+ stack.Push(childFolder);
+ }
+ else
+ {
+ nonFolderDescendants++;
+ }
+ }
+ }
+ }
+
+ if (nonFolderDescendants == 0)
+ {
+ solution.RemoveFolder(folder);
+ // After removal, adjust index and continue to avoid skipping folders after removal
+ i--;
+ }
}
await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken);
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.cs.xlf
index f5b32233e7d0..b88b3ff411f1 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.cs.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ Do formátu .slnx lze migrovat pouze .sln soubory.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ Vygeneruje soubor .slnx ze souboru .sln.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ Nepovedlo se přečíst {0} souboru řešení. Podporované soubory jsou .sln a platná řešení .slnx.
.slnx file {0} generated.
- .slnx file {0} generated.
+ Soubor .slnx {0} vygenerován.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.de.xlf
index f8eb231f7950..b216ac99c71c 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.de.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ Nur .sln Dateien können in das SLNX-Format migriert werden.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ Generiert eine SLNX-Datei aus einer .sln Datei.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ Die Projektmappendatei konnte nicht {0} gelesen werden. Unterstützte Dateien sind .sln und SLNX gültige Lösungen.
.slnx file {0} generated.
- .slnx file {0} generated.
+ Die SLNX-Datei {0} generiert.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.es.xlf
index 6ed23a5e39fe..195656606e24 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.es.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ Solo se pueden migrar archivos .sln al formato .slnx.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ Generar un archivo .slnx a partir de un archivo .sln.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ No se pudo leer la {0} del archivo de solución. Los archivos admitidos son soluciones válidas .sln y .slnx.
.slnx file {0} generated.
- .slnx file {0} generated.
+ {0} generar el archivo .slnx.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.fr.xlf
index b1d8c5880aff..25c9c55032dd 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.fr.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ Seuls les fichiers .sln peuvent être migrés au format .slnx.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ Générez un fichier .slnx à partir d’un fichier .sln.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ Impossible de lire le fichier solution {0}. Les fichiers pris en charge sont des solutions valides .sln et .slnx.
.slnx file {0} generated.
- .slnx file {0} generated.
+ Fichier .slnx {0} généré.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.it.xlf
index 989a4f33844e..fb7fdb0bd069 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.it.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ È possibile eseguire la migrazione al formato slnx solo dei file .sln.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ Genera un file con estensione slnx da un file .sln.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ Impossibile leggere il file di soluzione {0}. I file supportati sono soluzioni valide .sln e slnx.
.slnx file {0} generated.
- .slnx file {0} generated.
+ Il file con estensione slnx {0} generato.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ja.xlf
index 2683028f11aa..b44d3d00aa3b 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ja.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ .slnx 形式に移行できるのは、.sln ファイルのみです。
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ .sln ファイルから .slnx ファイルを生成します。
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ ソリューション ファイル {0} を読み取ることができませんでした。サポートされているファイルは、.slnおよび .slnx の有効なソリューションです。
.slnx file {0} generated.
- .slnx file {0} generated.
+ .slnx ファイル {0} 生成されました。
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ko.xlf
index 895400338f29..b27efa5d8fb1 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ko.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ .sln 파일만 .slnx 형식으로 마이그레이션할 수 있습니다.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ .sln 파일에서 .slnx 파일을 생성합니다.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ {0} 솔루션 파일을 읽을 수 없습니다. 지원되는 파일은 .sln .slnx 유효한 솔루션입니다.
.slnx file {0} generated.
- .slnx file {0} generated.
+ .slnx 파일이 생성되지 {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pl.xlf
index e384d5d00475..bacdde040cfe 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pl.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ Tylko .sln pliki można migrować do formatu slnx.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ Wygeneruj plik slnx z pliku .sln.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ Nie można odczytać {0} pliku rozwiązania. Obsługiwane pliki to prawidłowe rozwiązania .sln i slnx.
.slnx file {0} generated.
- .slnx file {0} generated.
+ plik slnx {0} wygenerowany.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pt-BR.xlf
index ba4d879b8340..6237aac0db8e 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pt-BR.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ Somente .sln arquivos podem ser migrados para o formato .slnx.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ Gerar um arquivo .slnx a partir de um .sln arquivo.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ Não foi possível ler o arquivo de {0}. Os arquivos com suporte são .sln soluções válidas .slnx e válidas.
.slnx file {0} generated.
- .slnx file {0} generated.
+ Arquivo .slnx {0} gerado.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ru.xlf
index c5dfbd52a60c..17207eca647a 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ru.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ В формат SLNX можно перенести только .sln файлы.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ Создать SLNX-файл из .sln файла.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ Не удалось прочитать файл решения {0}. Поддерживаемые файлы .sln и slnx допустимыми решениями.
.slnx file {0} generated.
- .slnx file {0} generated.
+ Файл SLNX {0} создан.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.tr.xlf
index fea7bcc4ea34..07c21a22f826 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.tr.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ Yalnızca .sln dosyaları .slnx biçimine geçirilemedi.
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ Bir dosyadan .slnx dosyası .sln oluştur.
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ Çözüm dosyası dosyası {0}. Desteklenen dosyalar .sln .slnx geçerli çözümlerdir.
.slnx file {0} generated.
- .slnx file {0} generated.
+ .slnx dosyası {0} değil.
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hans.xlf
index 82241d2d4a6a..8c96d8369796 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hans.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ 只有.sln文件可以迁移到 .slnx 格式。
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ 从.sln文件生成 .slnx 文件。
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ 无法读取解决方案文件 {0}。受支持的文件是.sln和 .slnx 有效解决方案。
.slnx file {0} generated.
- .slnx file {0} generated.
+ {0} 生成 .slnx 文件。
diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hant.xlf
index 5b061ec4f5f2..0ebfb6bc8468 100644
--- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hant.xlf
@@ -29,7 +29,7 @@
Only .sln files can be migrated to .slnx format.
- Only .sln files can be migrated to .slnx format.
+ 只有.sln檔案可以移轉為 .slnx 格式。
@@ -44,7 +44,7 @@
Generate a .slnx file from a .sln file.
- Generate a .slnx file from a .sln file.
+ 從.sln檔案產生 .slnx 檔案。
@@ -69,12 +69,12 @@
Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
- Could not read solution file {0}. Supported files are .sln and .slnx valid solutions.
+ 無法讀取解決方案檔案 {0}。支援的檔案.sln和 .slnx 有效解決方案。
.slnx file {0} generated.
- .slnx file {0} generated.
+ {0} 產生 .slnx 檔案。
diff --git a/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs b/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs
index 8f578b5f31dc..d101a594c6ec 100644
--- a/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/CliConstants.cs
@@ -22,19 +22,23 @@ internal static class CliConstants
public const string TestSectionKey = "test";
- public const string RestoreCommand = "restore";
+ public const string RestoreCommand = "Restore";
+ public const string BuildCommand = "Build";
+ public const string Configuration = "Configuration";
+ public const string RuntimeIdentifier = "RuntimeIdentifier";
public static readonly string[] ProjectExtensions = { ".proj", ".csproj", ".vbproj", ".fsproj" };
- public static readonly string[] SolutionExtensions = { ".sln", ".slnx" };
+ public static readonly string[] SolutionExtensions = { ".sln", ".slnx", ".slnf" };
public const string ProjectExtensionPattern = "*.*proj";
public const string SolutionExtensionPattern = "*.sln";
public const string SolutionXExtensionPattern = "*.slnx";
+ public const string SolutionFilterExtensionPattern = "*.slnf";
public const string BinLogFileName = "msbuild.binlog";
public const string TestingPlatformVsTestBridgeRunSettingsFileEnvVar = "TESTINGPLATFORM_VSTESTBRIDGE_RUNSETTINGS_FILE";
- public const string DLLExtension = "dll";
+ public const string DLLExtension = ".dll";
}
internal static class TestStates
diff --git a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx
index df9c46f2a035..613f47c7fc95 100644
--- a/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx
+++ b/src/Cli/dotnet/commands/dotnet-test/LocalizableStrings.resx
@@ -287,22 +287,6 @@ For MSTest before 2.2.4, the timeout is used for all testcases.
TIMESPAN
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
- NAME="VALUE"
-
No serializer registered with ID '{0}'
diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs
index b1adead6ad14..c8c107f02eac 100644
--- a/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildHandler.cs
@@ -2,11 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Concurrent;
-using Microsoft.Build.Evaluation;
-using Microsoft.Build.Execution;
-using Microsoft.Build.Framework;
-using Microsoft.Build.Logging;
+using Microsoft.DotNet.Tools.Common;
using Microsoft.DotNet.Tools.Test;
+using Microsoft.Testing.Platform.OutputDevice.Terminal;
namespace Microsoft.DotNet.Cli
{
@@ -14,116 +12,73 @@ internal sealed class MSBuildHandler : IDisposable
{
private readonly List _args;
private readonly TestApplicationActionQueue _actionQueue;
- private readonly int _degreeOfParallelism;
+ private TerminalTestReporter _output;
private readonly ConcurrentBag _testApplications = new();
private bool _areTestingPlatformApplications = true;
- private static readonly Lock buildLock = new();
-
- public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, int degreeOfParallelism)
+ public MSBuildHandler(List args, TestApplicationActionQueue actionQueue, TerminalTestReporter output)
{
_args = args;
_actionQueue = actionQueue;
- _degreeOfParallelism = degreeOfParallelism;
+ _output = output;
}
- public async Task RunMSBuild(BuildPathsOptions buildPathOptions)
+ public bool RunMSBuild(BuildOptions buildOptions)
{
- if (!ValidateBuildPathOptions(buildPathOptions))
+ if (!ValidationUtility.ValidateBuildPathOptions(buildOptions, _output))
{
return false;
}
- int msbuildExitCode;
+ int msBuildExitCode;
+ string path;
- if (!string.IsNullOrEmpty(buildPathOptions.ProjectPath))
+ if (!string.IsNullOrEmpty(buildOptions.ProjectPath))
{
- msbuildExitCode = await RunBuild(buildPathOptions.ProjectPath, isSolution: false);
+ path = PathUtility.GetFullPath(buildOptions.ProjectPath);
+ msBuildExitCode = RunBuild(path, isSolution: false, buildOptions);
}
- else if (!string.IsNullOrEmpty(buildPathOptions.SolutionPath))
+ else if (!string.IsNullOrEmpty(buildOptions.SolutionPath))
{
- msbuildExitCode = await RunBuild(buildPathOptions.SolutionPath, isSolution: true);
+ path = PathUtility.GetFullPath(buildOptions.SolutionPath);
+ msBuildExitCode = RunBuild(path, isSolution: true, buildOptions);
}
else
{
- msbuildExitCode = await RunBuild(buildPathOptions.DirectoryPath ?? Directory.GetCurrentDirectory());
- }
-
- if (msbuildExitCode != ExitCodes.Success)
- {
- VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorDescription, msbuildExitCode));
- return false;
- }
-
- return true;
- }
-
- private bool ValidateBuildPathOptions(BuildPathsOptions buildPathOptions)
- {
- if ((!string.IsNullOrEmpty(buildPathOptions.ProjectPath) && !string.IsNullOrEmpty(buildPathOptions.SolutionPath)) ||
- (!string.IsNullOrEmpty(buildPathOptions.ProjectPath) && !string.IsNullOrEmpty(buildPathOptions.DirectoryPath)) ||
- (!string.IsNullOrEmpty(buildPathOptions.SolutionPath) && !string.IsNullOrEmpty(buildPathOptions.DirectoryPath)))
- {
- VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleBuildPathOptionsErrorDescription);
- return false;
- }
-
- if (!string.IsNullOrEmpty(buildPathOptions.ProjectPath))
- {
- return ValidateFilePath(buildPathOptions.ProjectPath, CliConstants.ProjectExtensions, LocalizableStrings.CmdInvalidProjectFileExtensionErrorDescription);
- }
-
- if (!string.IsNullOrEmpty(buildPathOptions.SolutionPath))
- {
- return ValidateFilePath(buildPathOptions.SolutionPath, CliConstants.SolutionExtensions, LocalizableStrings.CmdInvalidSolutionFileExtensionErrorDescription);
+ path = PathUtility.GetFullPath(buildOptions.DirectoryPath ?? Directory.GetCurrentDirectory());
+ msBuildExitCode = RunBuild(path, buildOptions);
}
- if (!string.IsNullOrEmpty(buildPathOptions.DirectoryPath) && !Directory.Exists(buildPathOptions.DirectoryPath))
+ if (msBuildExitCode != ExitCodes.Success)
{
- VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentDirectoryErrorDescription, Path.GetFullPath(buildPathOptions.DirectoryPath)));
+ _output.WriteMessage(string.Format(LocalizableStrings.CmdMSBuildProjectsPropertiesErrorDescription, msBuildExitCode));
return false;
}
return true;
}
- private static bool ValidateFilePath(string filePath, string[] validExtensions, string errorMessage)
+ private int RunBuild(string directoryPath, BuildOptions buildOptions)
{
- if (!validExtensions.Contains(Path.GetExtension(filePath)))
- {
- VSTestTrace.SafeWriteTrace(() => string.Format(errorMessage, filePath));
- return false;
- }
-
- if (!File.Exists(filePath))
- {
- VSTestTrace.SafeWriteTrace(() => string.Format(LocalizableStrings.CmdNonExistentFileErrorDescription, Path.GetFullPath(filePath)));
- return false;
- }
-
- return true;
- }
-
- private async Task RunBuild(string directoryPath)
- {
- bool solutionOrProjectFileFound = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(directoryPath, out string projectOrSolutionFilePath, out bool isSolution);
+ (bool solutionOrProjectFileFound, string message) = SolutionAndProjectUtility.TryGetProjectOrSolutionFilePath(directoryPath, out string projectOrSolutionFilePath, out bool isSolution);
if (!solutionOrProjectFileFound)
{
+ _output.WriteMessage(message);
return ExitCodes.GenericFailure;
}
- (IEnumerable modules, bool restored) = await GetProjectsProperties(projectOrSolutionFilePath, isSolution);
+ (IEnumerable modules, bool restored) = GetProjectsProperties(projectOrSolutionFilePath, isSolution, buildOptions);
InitializeTestApplications(modules);
return restored ? ExitCodes.Success : ExitCodes.GenericFailure;
}
- private async Task RunBuild(string filePath, bool isSolution)
+ private int RunBuild(string filePath, bool isSolution, BuildOptions buildOptions)
{
- (IEnumerable modules, bool restored) = await GetProjectsProperties(filePath, isSolution);
+ (IEnumerable modules, bool restored) = GetProjectsProperties(filePath, isSolution, buildOptions);
InitializeTestApplications(modules);
@@ -166,187 +121,37 @@ public bool EnqueueTestApplications()
return true;
}
- private async Task<(IEnumerable, bool Restored)> GetProjectsProperties(string solutionOrProjectFilePath, bool isSolution)
- {
- var allProjects = new ConcurrentBag();
- bool restored = true;
-
- if (isSolution)
- {
- string fileDirectory = Path.GetDirectoryName(solutionOrProjectFilePath);
- string rootDirectory = string.IsNullOrEmpty(fileDirectory)
- ? Directory.GetCurrentDirectory()
- : fileDirectory;
-
- var projects = await SolutionAndProjectUtility.ParseSolution(solutionOrProjectFilePath, rootDirectory);
- ProcessProjectsInParallel(projects, allProjects, ref restored);
- }
- else
- {
- bool allowBinLog = IsBinaryLoggerEnabled(_args, out string binLogFileName);
-
- var (relatedProjects, isProjectBuilt) = GetProjectPropertiesInternal(solutionOrProjectFilePath, allowBinLog, binLogFileName);
- foreach (var relatedProject in relatedProjects)
- {
- allProjects.Add(relatedProject);
- }
-
- if (!isProjectBuilt)
- {
- restored = false;
- }
- }
- return (allProjects, restored);
- }
-
- private void ProcessProjectsInParallel(IEnumerable projects, ConcurrentBag allProjects, ref bool restored)
+ private (IEnumerable, bool Restored) GetProjectsProperties(string solutionOrProjectFilePath, bool isSolution, BuildOptions buildOptions)
{
- bool allProjectsRestored = true;
- bool allowBinLog = IsBinaryLoggerEnabled(_args, out string binLogFileName);
+ (IEnumerable allProjects, bool isBuiltOrRestored) = isSolution ?
+ MSBuildUtility.GetProjectsFromSolution(solutionOrProjectFilePath, buildOptions) :
+ MSBuildUtility.GetProjectsFromProject(solutionOrProjectFilePath, buildOptions);
- Parallel.ForEach(
- projects,
- new ParallelOptions { MaxDegreeOfParallelism = _degreeOfParallelism },
- () => true,
- (project, state, localRestored) =>
- {
- var (relatedProjects, isRestored) = GetProjectPropertiesInternal(project, allowBinLog, binLogFileName);
- foreach (var relatedProject in relatedProjects)
- {
- allProjects.Add(relatedProject);
- }
-
- return localRestored && isRestored;
- },
- localRestored =>
- {
- if (!localRestored)
- {
- allProjectsRestored = false;
- }
- });
+ LogProjectProperties(allProjects);
- restored = allProjectsRestored;
+ return (allProjects, isBuiltOrRestored);
}
- private static (IEnumerable Modules, bool Restored) GetProjectPropertiesInternal(string projectFilePath, bool allowBinLog, string binLogFileName)
+ private void LogProjectProperties(IEnumerable modules)
{
- var projectCollection = new ProjectCollection();
- var project = projectCollection.LoadProject(projectFilePath);
- var buildResult = RestoreProject(projectFilePath, projectCollection, allowBinLog, binLogFileName);
-
- bool restored = buildResult.OverallResult == BuildResultCode.Success;
-
- if (!restored)
+ if (!VSTestTrace.TraceEnabled)
{
- return (Array.Empty(), restored);
+ return;
}
- return (ExtractModulesFromProject(project), restored);
- }
-
- private static IEnumerable ExtractModulesFromProject(Project project)
- {
- _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestingPlatformApplication), out bool isTestingPlatformApplication);
- _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestProject), out bool isTestProject);
-
- string targetFramework = project.GetPropertyValue(ProjectProperties.TargetFramework);
- string targetFrameworks = project.GetPropertyValue(ProjectProperties.TargetFrameworks);
- string targetPath = project.GetPropertyValue(ProjectProperties.TargetPath);
- string projectFullPath = project.GetPropertyValue(ProjectProperties.ProjectFullPath);
- string runSettingsFilePath = project.GetPropertyValue(ProjectProperties.RunSettingsFilePath);
-
- var projects = new List();
-
- if (string.IsNullOrEmpty(targetFrameworks))
- {
- projects.Add(new Module(targetPath, projectFullPath, targetFramework, runSettingsFilePath, isTestingPlatformApplication, isTestProject));
- }
- else
+ foreach (var module in modules)
{
- var frameworks = targetFrameworks.Split(CliConstants.SemiColon, StringSplitOptions.RemoveEmptyEntries);
- foreach (var framework in frameworks)
- {
- project.SetProperty(ProjectProperties.TargetFramework, framework);
- project.ReevaluateIfNecessary();
-
- projects.Add(new Module(project.GetPropertyValue(ProjectProperties.TargetPath),
- projectFullPath,
- framework,
- runSettingsFilePath,
- isTestingPlatformApplication,
- isTestProject));
- }
- }
+ Console.WriteLine();
- return projects;
- }
-
- private static BuildResult RestoreProject(string projectFilePath, ProjectCollection projectCollection, bool allowBinLog, string binLogFileName)
- {
- BuildParameters parameters = new(projectCollection)
- {
- Loggers = [new ConsoleLogger(LoggerVerbosity.Quiet)]
- };
+ VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.ProjectFullPath}: {module.ProjectFullPath}");
+ VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.IsTestProject}: {module.IsTestProject}");
+ VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.IsTestingPlatformApplication}: {module.IsTestingPlatformApplication}");
+ VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.TargetFramework}: {module.TargetFramework}");
+ VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.TargetPath}: {module.TargetPath}");
+ VSTestTrace.SafeWriteTrace(() => $"{ProjectProperties.RunSettingsFilePath}: {module.RunSettingsFilePath}");
- if (allowBinLog)
- {
- parameters.Loggers = parameters.Loggers.Concat([
- new BinaryLogger
- {
- Parameters = binLogFileName
- }
- ]);
+ Console.WriteLine();
}
-
- var buildRequestData = new BuildRequestData(projectFilePath, new Dictionary(), null, [CliConstants.RestoreCommand], null);
- BuildResult buildResult;
- lock (buildLock)
- {
- buildResult = BuildManager.DefaultBuildManager.Build(parameters, buildRequestData);
- }
-
- return buildResult;
- }
-
- private static bool IsBinaryLoggerEnabled(List args, out string binLogFileName)
- {
- binLogFileName = string.Empty;
- var binLogArgs = new List();
-
- foreach (var arg in args)
- {
- if (arg.StartsWith("/bl:") || arg.Equals("/bl")
- || arg.StartsWith("--binaryLogger:") || arg.Equals("--binaryLogger")
- || arg.StartsWith("-bl:") || arg.Equals("-bl"))
- {
- binLogArgs.Add(arg);
-
- }
- }
-
- if (binLogArgs.Count > 0)
- {
- // Remove all BinLog args from the list of args
- args.RemoveAll(arg => binLogArgs.Contains(arg));
-
- // Get BinLog filename
- var binLogArg = binLogArgs.LastOrDefault();
-
- if (binLogArg.Contains(CliConstants.Colon))
- {
- var parts = binLogArg.Split(CliConstants.Colon, 2);
- binLogFileName = !string.IsNullOrEmpty(parts[1]) ? parts[1] : CliConstants.BinLogFileName;
- }
- else
- {
- binLogFileName = CliConstants.BinLogFileName;
- }
-
- return true;
- }
-
- return false;
}
public void Dispose()
diff --git a/src/Cli/dotnet/commands/dotnet-test/MSBuildUtility.cs b/src/Cli/dotnet/commands/dotnet-test/MSBuildUtility.cs
new file mode 100644
index 000000000000..b808312416ee
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-test/MSBuildUtility.cs
@@ -0,0 +1,209 @@
+// 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.Concurrent;
+using System.Collections.Immutable;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Logging;
+using Microsoft.DotNet.Tools.Common;
+using Microsoft.VisualStudio.SolutionPersistence.Model;
+
+namespace Microsoft.DotNet.Cli
+{
+ internal static class MSBuildUtility
+ {
+ public static (IEnumerable, bool) GetProjectsFromSolution(string solutionFilePath, BuildOptions buildOptions)
+ {
+ var projectCollection = new ProjectCollection();
+ string rootDirectory = SolutionAndProjectUtility.GetRootDirectory(solutionFilePath);
+
+ SolutionModel solutionModel = SlnFileFactory.CreateFromFileOrDirectory(solutionFilePath, includeSolutionFilterFiles: true, includeSolutionXmlFiles: true);
+
+ if (solutionFilePath.HasExtension(".slnf"))
+ {
+ solutionFilePath = HandleFilteredSolutionFilePath(solutionFilePath, out rootDirectory);
+ }
+
+ bool isBuiltOrRestored = BuildOrRestoreProjectOrSolution(
+ solutionFilePath,
+ projectCollection,
+ buildOptions,
+ GetCommands(buildOptions.HasNoRestore, buildOptions.HasNoBuild));
+
+ ConcurrentBag projects = GetProjectsProperties(projectCollection, solutionModel.SolutionProjects.Select(p => Path.Combine(rootDirectory, p.FilePath)), buildOptions.DegreeOfParallelism);
+ return (projects, isBuiltOrRestored);
+ }
+
+ public static (IEnumerable, bool) GetProjectsFromProject(string projectFilePath, BuildOptions buildOptions)
+ {
+ var projectCollection = new ProjectCollection();
+ bool isBuiltOrRestored = true;
+
+ if (!buildOptions.HasNoRestore)
+ {
+ isBuiltOrRestored = BuildOrRestoreProjectOrSolution(
+ projectFilePath,
+ projectCollection,
+ buildOptions,
+ [CliConstants.RestoreCommand]);
+ }
+
+ if (!buildOptions.HasNoBuild)
+ {
+ isBuiltOrRestored = isBuiltOrRestored && BuildOrRestoreProjectOrSolution(
+ projectFilePath,
+ projectCollection,
+ buildOptions,
+ [CliConstants.BuildCommand]);
+ }
+
+ IEnumerable projects = SolutionAndProjectUtility.GetProjectProperties(projectFilePath, projectCollection);
+
+ return (projects, isBuiltOrRestored);
+ }
+
+ private static bool BuildOrRestoreProjectOrSolution(string filePath, ProjectCollection projectCollection, BuildOptions buildOptions, string[] commands)
+ {
+ var parameters = GetBuildParameters(projectCollection, buildOptions);
+ var globalProperties = GetGlobalProperties(buildOptions);
+
+ var buildRequestData = new BuildRequestData(filePath, globalProperties, null, commands, null);
+
+ BuildResult buildResult = BuildManager.DefaultBuildManager.Build(parameters, buildRequestData);
+
+ return buildResult.OverallResult == BuildResultCode.Success;
+ }
+
+ private static ConcurrentBag GetProjectsProperties(ProjectCollection projectCollection, IEnumerable projects, int degreeOfParallelism)
+ {
+ var allProjects = new ConcurrentBag();
+
+ Parallel.ForEach(
+ projects,
+ new ParallelOptions { MaxDegreeOfParallelism = degreeOfParallelism },
+ (project) =>
+ {
+ IEnumerable projectsMetadata = SolutionAndProjectUtility.GetProjectProperties(project, projectCollection);
+ foreach (var projectMetadata in projectsMetadata)
+ {
+ allProjects.Add(projectMetadata);
+ }
+ });
+
+ return allProjects;
+ }
+
+ private static string HandleFilteredSolutionFilePath(string solutionFilterFilePath, out string rootDirectory)
+ {
+ string solution = SlnFileFactory.GetSolutionPathFromFilteredSolutionFile(solutionFilterFilePath);
+
+ // Resolve the solution path relative to the .slnf file directory
+ string solutionFilterDirectory = Path.GetDirectoryName(solutionFilterFilePath);
+ string solutionFullPath = Path.GetFullPath(solution, solutionFilterDirectory);
+ rootDirectory = Path.GetDirectoryName(solutionFullPath);
+
+ return solutionFullPath;
+ }
+
+ internal static bool IsBinaryLoggerEnabled(List args, out string binLogFileName)
+ {
+ binLogFileName = string.Empty;
+ var binLogArgs = new List();
+
+ foreach (var arg in args)
+ {
+ if (arg.StartsWith("/bl:") || arg.Equals("/bl")
+ || arg.StartsWith("--binaryLogger:") || arg.Equals("--binaryLogger")
+ || arg.StartsWith("-bl:") || arg.Equals("-bl"))
+ {
+ binLogArgs.Add(arg);
+
+ }
+ }
+
+ if (binLogArgs.Count > 0)
+ {
+ // Remove all BinLog args from the list of args
+ args.RemoveAll(arg => binLogArgs.Contains(arg));
+
+ // Get BinLog filename
+ var binLogArg = binLogArgs.LastOrDefault();
+
+ if (binLogArg.Contains(CliConstants.Colon))
+ {
+ var parts = binLogArg.Split(CliConstants.Colon, 2);
+ binLogFileName = !string.IsNullOrEmpty(parts[1]) ? parts[1] : CliConstants.BinLogFileName;
+ }
+ else
+ {
+ binLogFileName = CliConstants.BinLogFileName;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private static BuildParameters GetBuildParameters(ProjectCollection projectCollection, BuildOptions buildOptions)
+ {
+ BuildParameters parameters = new(projectCollection)
+ {
+ Loggers = [new ConsoleLogger(LoggerVerbosity.Quiet)]
+ };
+
+ if (!buildOptions.AllowBinLog)
+ return parameters;
+
+ parameters.Loggers =
+ [
+ .. parameters.Loggers,
+ .. new[]
+ {
+ new BinaryLogger
+ {
+ Parameters = buildOptions.BinLogFileName
+ }
+ },
+ ];
+
+ return parameters;
+ }
+
+ private static Dictionary GetGlobalProperties(BuildOptions buildOptions)
+ {
+ var globalProperties = new Dictionary();
+
+ if (!string.IsNullOrEmpty(buildOptions.Configuration))
+ {
+ globalProperties[CliConstants.Configuration] = buildOptions.Configuration;
+ }
+
+ if (!string.IsNullOrEmpty(buildOptions.RuntimeIdentifier))
+ {
+ globalProperties[CliConstants.RuntimeIdentifier] = buildOptions.RuntimeIdentifier;
+ }
+
+ return globalProperties;
+ }
+
+ private static string[] GetCommands(bool hasNoRestore, bool hasNoBuild)
+ {
+ var commands = ImmutableArray.CreateBuilder();
+
+ if (!hasNoRestore)
+ {
+ commands.Add(CliConstants.RestoreCommand);
+ }
+
+ if (!hasNoBuild)
+ {
+ commands.Add(CliConstants.BuildCommand);
+ }
+
+ return [.. commands];
+ }
+ }
+}
diff --git a/src/Cli/dotnet/commands/dotnet-test/Models.cs b/src/Cli/dotnet/commands/dotnet-test/Models.cs
index 17ca6a4261ec..bbfbe4864c3f 100644
--- a/src/Cli/dotnet/commands/dotnet-test/Models.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/Models.cs
@@ -3,7 +3,7 @@
namespace Microsoft.DotNet.Cli
{
- internal sealed record Module(string? DllOrExePath, string? ProjectPath, string? TargetFramework, string? RunSettingsFilePath, bool IsTestingPlatformApplication, bool IsTestProject);
+ internal sealed record Module(string? TargetPath, string? ProjectFullPath, string? TargetFramework, string? RunSettingsFilePath, bool IsTestingPlatformApplication, bool IsTestProject);
internal sealed record Handshake(Dictionary? Properties);
@@ -20,4 +20,6 @@ internal sealed record FlatException(string? ErrorMessage, string? ErrorType, st
internal sealed record FileArtifact(string? FullPath, string? DisplayName, string? Description, string? TestUid, string? TestDisplayName, string? SessionUid);
internal sealed record TestSession(byte? SessionType, string? SessionUid, string? ExecutionId);
+
+ internal record MSBuildBuildAndRestoreSettings(string[] Commands, string Configuration, string RuntimeIdentifier, bool AllowBinLog, string BinLogFileName);
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/Options.cs b/src/Cli/dotnet/commands/dotnet-test/Options.cs
index 598e3cb7d722..69789cd3328f 100644
--- a/src/Cli/dotnet/commands/dotnet-test/Options.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/Options.cs
@@ -3,7 +3,7 @@
namespace Microsoft.DotNet.Cli
{
- internal record BuildConfigurationOptions(bool HasNoRestore, bool HasNoBuild, bool HasListTests, string Configuration, string Architecture);
+ internal record BuildConfigurationOptions(bool HasListTests, string Configuration, string Architecture);
- internal record BuildPathsOptions(string ProjectPath, string SolutionPath, string DirectoryPath);
+ internal record BuildOptions(string ProjectPath, string SolutionPath, string DirectoryPath, bool HasNoRestore, bool HasNoBuild, string Configuration, string RuntimeIdentifier, bool AllowBinLog, string BinLogFileName, int DegreeOfParallelism);
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/Program.cs b/src/Cli/dotnet/commands/dotnet-test/Program.cs
index ec586616a4e1..6714489eae66 100644
--- a/src/Cli/dotnet/commands/dotnet-test/Program.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/Program.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.Runtime.Versioning;
using System.Text.RegularExpressions;
@@ -228,8 +229,14 @@ private static TestCommand FromParseResult(ParseResult result, string[] settings
noRestore,
msbuildPath);
- // Apply environment variables provided by the user via --environment (-e) parameter, if present
- SetEnvironmentVariablesFromParameters(testCommand, result);
+ // Apply environment variables provided by the user via --environment (-e) option, if present
+ if (result.GetValue(CommonOptions.EnvOption) is { } environmentVariables)
+ {
+ foreach (var (name, value) in environmentVariables)
+ {
+ testCommand.EnvironmentVariable(name, value);
+ }
+ }
// Set DOTNET_PATH if it isn't already set in the environment as it is required
// by the testhost which uses the apphost feature (Windows only).
@@ -303,31 +310,6 @@ private static bool ContainsBuiltTestSources(string[] args)
return false;
}
- private static void SetEnvironmentVariablesFromParameters(TestCommand testCommand, ParseResult parseResult)
- {
- CliOption> option = TestCommandParser.EnvOption;
-
- if (parseResult.GetResult(option) is null)
- {
- return;
- }
-
- foreach (string env in parseResult.GetValue(option))
- {
- string name = env;
- string value = string.Empty;
-
- int equalsIndex = env.IndexOf('=');
- if (equalsIndex > 0)
- {
- name = env.Substring(0, equalsIndex);
- value = env.Substring(equalsIndex + 1);
- }
-
- testCommand.EnvironmentVariable(name, value);
- }
- }
-
/// A case-insensitive dictionary of any properties passed from the user and their values.
private static Dictionary GetUserSpecifiedExplicitMSBuildProperties(ParseResult parseResult)
{
diff --git a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs
index a3ca1af6b4c6..02e3bb1b72ea 100644
--- a/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/SolutionAndProjectUtility.cs
@@ -1,111 +1,152 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Build.Evaluation;
using Microsoft.DotNet.Tools;
-using Microsoft.DotNet.Tools.Test;
-using Microsoft.VisualStudio.SolutionPersistence;
-using Microsoft.VisualStudio.SolutionPersistence.Model;
-using Microsoft.VisualStudio.SolutionPersistence.Serializer;
+using Microsoft.DotNet.Tools.Common;
+using NuGet.Packaging;
+using LocalizableStrings = Microsoft.DotNet.Tools.Test.LocalizableStrings;
namespace Microsoft.DotNet.Cli
{
internal static class SolutionAndProjectUtility
{
- public static bool TryGetProjectOrSolutionFilePath(string directory, out string projectOrSolutionFilePath, out bool isSolution)
+ public static (bool SolutionOrProjectFileFound, string Message) TryGetProjectOrSolutionFilePath(string directory, out string projectOrSolutionFilePath, out bool isSolution)
{
projectOrSolutionFilePath = string.Empty;
isSolution = false;
if (!Directory.Exists(directory))
{
- return false;
+ return (false, string.Format(LocalizableStrings.CmdNonExistentDirectoryErrorDescription, directory));
}
- var possibleSolutionPaths = GetSolutionFilePaths(directory);
+ var solutionPaths = GetSolutionFilePaths(directory);
// If more than a single sln file is found, an error is thrown since we can't determine which one to choose.
- if (possibleSolutionPaths.Length > 1)
+ if (solutionPaths.Length > 1)
{
- VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory));
- return false;
+ return (false, string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory));
}
- if (possibleSolutionPaths.Length == 1)
+ if (solutionPaths.Length == 1)
{
- var possibleProjectPaths = GetProjectFilePaths(directory);
+ var projectPaths = GetProjectFilePaths(directory);
- if (possibleProjectPaths.Length == 0)
+ if (projectPaths.Length == 0)
{
- projectOrSolutionFilePath = possibleSolutionPaths[0];
+ projectOrSolutionFilePath = solutionPaths[0];
isSolution = true;
- return true;
+ return (true, string.Empty);
}
- VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorDescription);
- return false;
+ return (false, LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorDescription);
}
else // If no solutions are found, look for a project file
{
- string[] possibleProjectPath = GetProjectFilePaths(directory);
+ string[] projectPaths = GetProjectFilePaths(directory);
- if (possibleProjectPath.Length == 0)
+ if (projectPaths.Length == 0)
{
- VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdNoProjectOrSolutionFileErrorDescription);
- return false;
+ var solutionFilterPaths = GetSolutionFilterFilePaths(directory);
+
+ if (solutionFilterPaths.Length == 0)
+ {
+ return (false, LocalizableStrings.CmdNoProjectOrSolutionFileErrorDescription);
+ }
+
+ if (solutionFilterPaths.Length == 1)
+ {
+ projectOrSolutionFilePath = solutionFilterPaths[0];
+ isSolution = true;
+ return (true, string.Empty);
+ }
+ else
+ {
+ return (false, LocalizableStrings.CmdMultipleProjectOrSolutionFilesErrorDescription);
+ }
}
- if (possibleProjectPath.Length == 1)
+ if (projectPaths.Length == 1)
{
- projectOrSolutionFilePath = possibleProjectPath[0];
- return true;
+ projectOrSolutionFilePath = projectPaths[0];
+ return (true, string.Empty);
}
- VSTestTrace.SafeWriteTrace(() => string.Format(CommonLocalizableStrings.MoreThanOneProjectInDirectory, directory));
-
- return false;
+ return (false, string.Format(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, directory));
}
}
private static string[] GetSolutionFilePaths(string directory)
{
- return Directory.EnumerateFiles(directory, CliConstants.SolutionExtensionPattern, SearchOption.TopDirectoryOnly)
- .Concat(Directory.EnumerateFiles(directory, CliConstants.SolutionXExtensionPattern, SearchOption.TopDirectoryOnly))
- .ToArray();
+ string[] solutionFiles = Directory.GetFiles(directory, CliConstants.SolutionExtensionPattern, SearchOption.TopDirectoryOnly);
+ solutionFiles.AddRange(Directory.GetFiles(directory, CliConstants.SolutionXExtensionPattern, SearchOption.TopDirectoryOnly));
+
+ return solutionFiles;
+ }
+
+ private static string[] GetSolutionFilterFilePaths(string directory)
+ {
+ return Directory.GetFiles(directory, CliConstants.SolutionFilterExtensionPattern, SearchOption.TopDirectoryOnly);
}
private static string[] GetProjectFilePaths(string directory) => [.. Directory.EnumerateFiles(directory, CliConstants.ProjectExtensionPattern, SearchOption.TopDirectoryOnly).Where(IsProjectFile)];
private static bool IsProjectFile(string filePath) => CliConstants.ProjectExtensions.Contains(Path.GetExtension(filePath), StringComparer.OrdinalIgnoreCase);
- public static async Task> ParseSolution(string solutionFilePath, string directory)
+ public static string GetRootDirectory(string solutionOrProjectFilePath)
+ {
+ string fileDirectory = Path.GetDirectoryName(solutionOrProjectFilePath);
+ return string.IsNullOrEmpty(fileDirectory) ? Directory.GetCurrentDirectory() : fileDirectory;
+ }
+
+ public static IEnumerable GetProjectProperties(string projectFilePath, ProjectCollection projectCollection)
+ {
+ var project = projectCollection.LoadProject(projectFilePath);
+ return GetModulesFromProject(project);
+ }
+
+ private static List GetModulesFromProject(Project project)
{
- if (string.IsNullOrEmpty(solutionFilePath))
+ _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestProject), out bool isTestProject);
+
+ if (!isTestProject)
{
- VSTestTrace.SafeWriteTrace(() => $"Solution file path cannot be null or empty: {solutionFilePath}");
return [];
}
- var projectsPaths = new List();
- SolutionModel solution = null;
+ _ = bool.TryParse(project.GetPropertyValue(ProjectProperties.IsTestingPlatformApplication), out bool isTestingPlatformApplication);
- try
- {
- solution = SolutionSerializers.GetSerializerByMoniker(solutionFilePath) is ISolutionSerializer serializer
- ? await serializer.OpenAsync(solutionFilePath, CancellationToken.None)
- : null;
- }
- catch (Exception ex)
+ string targetFramework = project.GetPropertyValue(ProjectProperties.TargetFramework);
+ string targetFrameworks = project.GetPropertyValue(ProjectProperties.TargetFrameworks);
+ string targetPath = project.GetPropertyValue(ProjectProperties.TargetPath);
+ string projectFullPath = project.GetPropertyValue(ProjectProperties.ProjectFullPath);
+ string runSettingsFilePath = project.GetPropertyValue(ProjectProperties.RunSettingsFilePath);
+
+ var projects = new List();
+
+ if (string.IsNullOrEmpty(targetFrameworks))
{
- VSTestTrace.SafeWriteTrace(() => $"Failed to parse solution file '{solutionFilePath}': {ex.Message}");
- return [];
+ projects.Add(new Module(targetPath, PathUtility.FixFilePath(projectFullPath), targetFramework, runSettingsFilePath, isTestingPlatformApplication, isTestProject));
}
-
- if (solution is not null)
+ else
{
- projectsPaths.AddRange(solution.SolutionProjects.Select(project => Path.Combine(directory, project.FilePath)));
+ var frameworks = targetFrameworks.Split(CliConstants.SemiColon, StringSplitOptions.RemoveEmptyEntries);
+ foreach (var framework in frameworks)
+ {
+ project.SetProperty(ProjectProperties.TargetFramework, framework);
+ project.ReevaluateIfNecessary();
+
+ projects.Add(new Module(project.GetPropertyValue(ProjectProperties.TargetPath),
+ PathUtility.FixFilePath(projectFullPath),
+ framework,
+ runSettingsFilePath,
+ isTestingPlatformApplication,
+ isTestProject));
+ }
}
- return projectsPaths;
+ return projects;
}
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/Terminal/TerminalTestReporter.cs b/src/Cli/dotnet/commands/dotnet-test/Terminal/TerminalTestReporter.cs
index d9c621862c39..ef1e789df7a4 100644
--- a/src/Cli/dotnet/commands/dotnet-test/Terminal/TerminalTestReporter.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/Terminal/TerminalTestReporter.cs
@@ -47,6 +47,7 @@ internal event EventHandler OnProgressStopUpdate
private readonly uint? _originalConsoleMode;
private bool _isDiscovery;
+ private bool _isHelp;
private DateTimeOffset? _testExecutionStartTime;
private DateTimeOffset? _testExecutionEndTime;
@@ -147,9 +148,10 @@ public TerminalTestReporter(IConsole console, TerminalTestReporterOptions option
_terminalWithProgress = terminalWithProgress;
}
- public void TestExecutionStarted(DateTimeOffset testStartTime, int workerCount, bool isDiscovery)
+ public void TestExecutionStarted(DateTimeOffset testStartTime, int workerCount, bool isDiscovery, bool isHelp)
{
_isDiscovery = isDiscovery;
+ _isHelp = isHelp;
_testExecutionStartTime = testStartTime;
_terminalWithProgress.StartShowingProgress(workerCount);
}
@@ -189,7 +191,10 @@ public void TestExecutionCompleted(DateTimeOffset endTime)
_testExecutionEndTime = endTime;
_terminalWithProgress.StopShowingProgress();
- _terminalWithProgress.WriteToTerminal(_isDiscovery ? AppendTestDiscoverySummary : AppendTestRunSummary);
+ if (!_isHelp)
+ {
+ _terminalWithProgress.WriteToTerminal(_isDiscovery ? AppendTestDiscoverySummary : AppendTestRunSummary);
+ }
NativeMethods.RestoreConsoleMode(_originalConsoleMode);
_assemblies.Clear();
@@ -757,7 +762,7 @@ internal void AssemblyRunCompleted(string assembly, string? targetFramework, str
_terminalWithProgress.RemoveWorker(assemblyRun.SlotIndex);
- if (!_isDiscovery && _options.ShowAssembly && _options.ShowAssemblyStartAndComplete)
+ if (!_isHelp && !_isDiscovery && _options.ShowAssembly && _options.ShowAssemblyStartAndComplete)
{
_terminalWithProgress.WriteToTerminal(terminal => AppendAssemblySummary(assemblyRun, terminal));
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs
index 6918d2bcc2a7..5ba3f603ead7 100644
--- a/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/TestApplication.cs
@@ -31,12 +31,8 @@ internal sealed class TestApplication : IDisposable
public event EventHandler SessionEventReceived;
public event EventHandler ErrorReceived;
public event EventHandler TestProcessExited;
- public event EventHandler Run;
public event EventHandler ExecutionIdReceived;
- private const string TestingPlatformVsTestBridgeRunSettingsFileEnvVar = "TESTINGPLATFORM_VSTESTBRIDGE_RUNSETTINGS_FILE";
- private const string DLLExtension = "dll";
-
public Module Module => _module;
public TestApplication(Module module, List args)
@@ -52,14 +48,12 @@ public void AddExecutionId(string executionId)
public async Task RunAsync(bool hasFilterMode, bool enableHelp, BuildConfigurationOptions buildConfigurationOptions)
{
- Run?.Invoke(this, EventArgs.Empty);
-
if (hasFilterMode && !ModulePathExists())
{
return 1;
}
- bool isDll = _module.DllOrExePath.HasExtension(CliConstants.DLLExtension);
+ bool isDll = _module.TargetPath.HasExtension(CliConstants.DLLExtension);
var processStartInfo = CreateProcessStartInfo(hasFilterMode, isDll, buildConfigurationOptions, enableHelp);
_testAppPipeConnectionLoop = Task.Run(async () => await WaitConnectionAsync(_cancellationToken.Token), _cancellationToken.Token);
@@ -75,7 +69,7 @@ private ProcessStartInfo CreateProcessStartInfo(bool hasFilterMode, bool isDll,
{
var processStartInfo = new ProcessStartInfo
{
- FileName = hasFilterMode ? (isDll ? Environment.ProcessPath : _module.DllOrExePath) : Environment.ProcessPath,
+ FileName = hasFilterMode ? (isDll ? Environment.ProcessPath : _module.TargetPath) : Environment.ProcessPath,
Arguments = hasFilterMode ? BuildArgs(isDll) : BuildArgsWithDotnetRun(enableHelp, buildConfigurationOptions),
RedirectStandardOutput = true,
RedirectStandardError = true
@@ -89,11 +83,10 @@ private ProcessStartInfo CreateProcessStartInfo(bool hasFilterMode, bool isDll,
return processStartInfo;
}
-
private void WaitOnTestApplicationPipeConnectionLoop()
{
_cancellationToken.Cancel();
- _testAppPipeConnectionLoop.Wait((int)TimeSpan.FromSeconds(30).TotalMilliseconds);
+ _testAppPipeConnectionLoop?.Wait((int)TimeSpan.FromSeconds(30).TotalMilliseconds);
}
private async Task WaitConnectionAsync(CancellationToken token)
@@ -109,9 +102,14 @@ private async Task WaitConnectionAsync(CancellationToken token)
_testAppPipeConnections.Add(pipeConnection);
}
}
- catch (OperationCanceledException ex) when (ex.CancellationToken == token)
+ catch (OperationCanceledException ex)
{
// We are exiting
+ if (VSTestTrace.TraceEnabled)
+ {
+ string tokenType = ex.CancellationToken == token ? "internal token" : "external token";
+ VSTestTrace.SafeWriteTrace(() => $"WaitConnectionAsync() throws OperationCanceledException with {tokenType}");
+ }
}
catch (Exception ex)
{
@@ -212,7 +210,7 @@ private async Task StartProcess(ProcessStartInfo processStartInfo)
{
if (VSTestTrace.TraceEnabled)
{
- VSTestTrace.SafeWriteTrace(() => $"Updated args: {processStartInfo.Arguments}");
+ VSTestTrace.SafeWriteTrace(() => $"Test application arguments: {processStartInfo.Arguments}");
}
var process = Process.Start(processStartInfo);
@@ -248,9 +246,9 @@ private void StoreOutputAndErrorData(Process process)
private bool ModulePathExists()
{
- if (!File.Exists(_module.DllOrExePath))
+ if (!File.Exists(_module.TargetPath))
{
- ErrorReceived.Invoke(this, new ErrorEventArgs { ErrorMessage = $"Test module '{_module.DllOrExePath}' not found. Build the test application before or run 'dotnet test'." });
+ ErrorReceived.Invoke(this, new ErrorEventArgs { ErrorMessage = $"Test module '{_module.TargetPath}' not found. Build the test application before or run 'dotnet test'." });
return false;
}
return true;
@@ -260,17 +258,11 @@ private string BuildArgsWithDotnetRun(bool hasHelp, BuildConfigurationOptions bu
{
StringBuilder builder = new();
- builder.Append($"{CliConstants.DotnetRunCommand} {TestingPlatformOptions.ProjectOption.Name} \"{_module.ProjectPath}\"");
+ builder.Append($"{CliConstants.DotnetRunCommand} {TestingPlatformOptions.ProjectOption.Name} \"{_module.ProjectFullPath}\"");
- if (buildConfigurationOptions.HasNoRestore)
- {
- builder.Append($" {TestingPlatformOptions.NoRestoreOption.Name}");
- }
-
- if (buildConfigurationOptions.HasNoBuild)
- {
- builder.Append($" {TestingPlatformOptions.NoBuildOption.Name}");
- }
+ // Because we restored and built before in MSHandler, we will skip those with dotnet run
+ builder.Append($" {TestingPlatformOptions.NoRestoreOption.Name}");
+ builder.Append($" {TestingPlatformOptions.NoBuildOption.Name}");
if (buildConfigurationOptions.HasListTests)
{
@@ -314,7 +306,7 @@ private string BuildArgs(bool isDll)
if (isDll)
{
- builder.Append($"exec {_module.DllOrExePath} ");
+ builder.Append($"exec {_module.TargetPath} ");
}
builder.Append(_args.Count != 0
@@ -331,7 +323,7 @@ public void OnHandshakeMessage(HandshakeMessage handshakeMessage)
if (handshakeMessage.Properties.TryGetValue(HandshakeMessagePropertyNames.ExecutionId, out string executionId))
{
AddExecutionId(executionId);
- ExecutionIdReceived?.Invoke(this, new ExecutionEventArgs { ModulePath = _module.DllOrExePath, ExecutionId = executionId });
+ ExecutionIdReceived?.Invoke(this, new ExecutionEventArgs { ModulePath = _module.TargetPath, ExecutionId = executionId });
}
HandshakeReceived?.Invoke(this, new HandshakeArgs { Handshake = new Handshake(handshakeMessage.Properties) });
}
@@ -374,20 +366,22 @@ public override string ToString()
{
StringBuilder builder = new();
- if (!string.IsNullOrEmpty(_module.DllOrExePath))
+ if (!string.IsNullOrEmpty(_module.TargetPath))
{
- builder.Append($"DLL: {_module.DllOrExePath}");
+ builder.Append($"DLL: {_module.TargetPath}");
}
- if (!string.IsNullOrEmpty(_module.ProjectPath))
+ if (!string.IsNullOrEmpty(_module.ProjectFullPath))
{
- builder.Append($"Project: {_module.ProjectPath}");
- };
+ builder.Append($"Project: {_module.ProjectFullPath}");
+ }
+ ;
if (!string.IsNullOrEmpty(_module.TargetFramework))
{
builder.Append($"Target Framework: {_module.TargetFramework}");
- };
+ }
+ ;
return builder.ToString();
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/TestApplicationEventHandlers.cs b/src/Cli/dotnet/commands/dotnet-test/TestApplicationEventHandlers.cs
new file mode 100644
index 000000000000..4a93abd53184
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-test/TestApplicationEventHandlers.cs
@@ -0,0 +1,219 @@
+// 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.Concurrent;
+using Microsoft.DotNet.Tools.Test;
+using Microsoft.Testing.Platform.OutputDevice;
+using Microsoft.Testing.Platform.OutputDevice.Terminal;
+
+namespace Microsoft.DotNet.Cli
+{
+ internal sealed class TestApplicationsEventHandlers
+ {
+ private readonly ConcurrentDictionary _executions;
+ private readonly TerminalTestReporter _output;
+
+ public TestApplicationsEventHandlers(
+ ConcurrentDictionary executions,
+ TerminalTestReporter output)
+ {
+ _executions = executions;
+ _output = output;
+ }
+
+ public void OnHandshakeReceived(object sender, HandshakeArgs args)
+ {
+ var testApplication = (TestApplication)sender;
+ var executionId = args.Handshake.Properties[HandshakeMessagePropertyNames.ExecutionId];
+ var arch = args.Handshake.Properties[HandshakeMessagePropertyNames.Architecture]?.ToLower();
+ var tfm = TargetFrameworkParser.GetShortTargetFramework(args.Handshake.Properties[HandshakeMessagePropertyNames.Framework]);
+ (string ModulePath, string TargetFramework, string Architecture, string ExecutionId) appInfo = new(testApplication.Module.TargetPath, tfm, arch, executionId);
+ _executions[testApplication] = appInfo;
+ _output.AssemblyRunStarted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId);
+
+ if (!VSTestTrace.TraceEnabled) return;
+
+ foreach (var property in args.Handshake.Properties)
+ {
+ VSTestTrace.SafeWriteTrace(() => $"{GetHandshakePropertyName(property.Key)}: {property.Value}");
+ }
+ }
+
+ private static string GetHandshakePropertyName(byte propertyId) =>
+ propertyId switch
+ {
+ HandshakeMessagePropertyNames.PID => nameof(HandshakeMessagePropertyNames.PID),
+ HandshakeMessagePropertyNames.Architecture => nameof(HandshakeMessagePropertyNames.Architecture),
+ HandshakeMessagePropertyNames.Framework => nameof(HandshakeMessagePropertyNames.Framework),
+ HandshakeMessagePropertyNames.OS => nameof(HandshakeMessagePropertyNames.OS),
+ HandshakeMessagePropertyNames.SupportedProtocolVersions => nameof(HandshakeMessagePropertyNames.SupportedProtocolVersions),
+ HandshakeMessagePropertyNames.HostType => nameof(HandshakeMessagePropertyNames.HostType),
+ HandshakeMessagePropertyNames.ModulePath => nameof(HandshakeMessagePropertyNames.ModulePath),
+ HandshakeMessagePropertyNames.ExecutionId => nameof(HandshakeMessagePropertyNames.ExecutionId),
+ _ => string.Empty,
+ };
+
+ public void OnDiscoveredTestsReceived(object sender, DiscoveredTestEventArgs args)
+ {
+ var testApp = (TestApplication)sender;
+ var appInfo = _executions[testApp];
+
+ foreach (var test in args.DiscoveredTests)
+ {
+ _output.TestDiscovered(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
+ test.DisplayName,
+ test.Uid);
+ }
+
+ if (!VSTestTrace.TraceEnabled) return;
+
+ VSTestTrace.SafeWriteTrace(() => $"DiscoveredTests Execution Id: {args.ExecutionId}");
+ foreach (var discoveredTestMessage in args.DiscoveredTests)
+ {
+ VSTestTrace.SafeWriteTrace(() => $"DiscoveredTest: {discoveredTestMessage.Uid}, {discoveredTestMessage.DisplayName}");
+ }
+ }
+
+ public void OnTestResultsReceived(object sender, TestResultEventArgs args)
+ {
+ foreach (var testResult in args.SuccessfulTestResults)
+ {
+ var testApp = (TestApplication)sender;
+ var appInfo = _executions[testApp];
+ _output.TestCompleted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
+ testResult.Uid,
+ testResult.DisplayName,
+ ToOutcome(testResult.State),
+ TimeSpan.FromTicks(testResult.Duration ?? 0),
+ exceptions: null,
+ expected: null,
+ actual: null,
+ standardOutput: null,
+ errorOutput: null);
+ }
+
+ foreach (var testResult in args.FailedTestResults)
+ {
+ var testApp = (TestApplication)sender;
+ var appInfo = _executions[testApp];
+ _output.TestCompleted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
+ testResult.Uid,
+ testResult.DisplayName,
+ ToOutcome(testResult.State),
+ TimeSpan.FromTicks(testResult.Duration ?? 0),
+ exceptions: testResult.Exceptions.Select(fe => new Microsoft.Testing.Platform.OutputDevice.Terminal.FlatException(fe.ErrorMessage, fe.ErrorType, fe.StackTrace)).ToArray(),
+ expected: null,
+ actual: null,
+ standardOutput: null,
+ errorOutput: null);
+ }
+
+ if (!VSTestTrace.TraceEnabled) return;
+
+ VSTestTrace.SafeWriteTrace(() => $"TestResults Execution Id: {args.ExecutionId}");
+
+ foreach (SuccessfulTestResult successfulTestResult in args.SuccessfulTestResults)
+ {
+ VSTestTrace.SafeWriteTrace(() => $"SuccessfulTestResult: {successfulTestResult.Uid}, {successfulTestResult.DisplayName}, " +
+ $"{successfulTestResult.State}, {successfulTestResult.Duration}, {successfulTestResult.Reason}, {successfulTestResult.StandardOutput}," +
+ $"{successfulTestResult.ErrorOutput}, {successfulTestResult.SessionUid}");
+ }
+
+ foreach (FailedTestResult failedTestResult in args.FailedTestResults)
+ {
+ VSTestTrace.SafeWriteTrace(() => $"FailedTestResult: {failedTestResult.Uid}, {failedTestResult.DisplayName}, " +
+ $"{failedTestResult.State}, {failedTestResult.Duration}, {failedTestResult.Reason}, {string.Join(", ", failedTestResult.Exceptions?.Select(e => $"{e.ErrorMessage}, {e.ErrorType}, {e.StackTrace}"))}" +
+ $"{failedTestResult.StandardOutput}, {failedTestResult.ErrorOutput}, {failedTestResult.SessionUid}");
+ }
+ }
+
+ public void OnFileArtifactsReceived(object sender, FileArtifactEventArgs args)
+ {
+ var testApp = (TestApplication)sender;
+ var appInfo = _executions[testApp];
+
+ foreach (var artifact in args.FileArtifacts)
+ {
+ _output.ArtifactAdded(
+ outOfProcess: false,
+ appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
+ artifact.TestDisplayName, artifact.FullPath);
+ }
+
+ if (!VSTestTrace.TraceEnabled) return;
+
+ VSTestTrace.SafeWriteTrace(() => $"FileArtifactMessages Execution Id: {args.ExecutionId}");
+
+ foreach (FileArtifact fileArtifactMessage in args.FileArtifacts)
+ {
+ VSTestTrace.SafeWriteTrace(() => $"FileArtifact: {fileArtifactMessage.FullPath}, {fileArtifactMessage.DisplayName}, " +
+ $"{fileArtifactMessage.Description}, {fileArtifactMessage.TestUid}, {fileArtifactMessage.TestDisplayName}, " +
+ $"{fileArtifactMessage.SessionUid}");
+ }
+ }
+
+ public void OnSessionEventReceived(object sender, SessionEventArgs args)
+ {
+ if (!VSTestTrace.TraceEnabled) return;
+
+ var sessionEvent = args.SessionEvent;
+ VSTestTrace.SafeWriteTrace(() => $"TestSessionEvent: {sessionEvent.SessionType}, {sessionEvent.SessionUid}, {sessionEvent.ExecutionId}");
+ }
+
+ public void OnErrorReceived(object sender, ErrorEventArgs args)
+ {
+ if (!VSTestTrace.TraceEnabled) return;
+
+ VSTestTrace.SafeWriteTrace(() => args.ErrorMessage);
+ }
+
+ public void OnTestProcessExited(object sender, TestProcessExitEventArgs args)
+ {
+ var testApplication = (TestApplication)sender;
+
+ if (_executions.TryGetValue(testApplication, out var appInfo))
+ {
+ _output.AssemblyRunCompleted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId, args.ExitCode, string.Join(Environment.NewLine, args.OutputData), string.Join(Environment.NewLine, args.ErrorData));
+ }
+ else
+ {
+ _output.AssemblyRunCompleted(testApplication.Module.TargetPath ?? testApplication.Module.ProjectFullPath, testApplication.Module.TargetFramework, architecture: null, null, args.ExitCode, string.Join(Environment.NewLine, args.OutputData), string.Join(Environment.NewLine, args.ErrorData));
+ }
+
+ if (!VSTestTrace.TraceEnabled) return;
+
+ if (args.ExitCode != ExitCodes.Success)
+ {
+ VSTestTrace.SafeWriteTrace(() => $"Test Process exited with non-zero exit code: {args.ExitCode}");
+ }
+
+ if (args.OutputData.Count > 0)
+ {
+ VSTestTrace.SafeWriteTrace(() => $"Output Data: {string.Join("\n", args.OutputData)}");
+ }
+
+ if (args.ErrorData.Count > 0)
+ {
+ VSTestTrace.SafeWriteTrace(() => $"Error Data: {string.Join("\n", args.ErrorData)}");
+ }
+ }
+
+ public void OnExecutionIdReceived(object sender, ExecutionEventArgs args)
+ {
+ }
+
+ public static TestOutcome ToOutcome(byte? testState)
+ {
+ return testState switch
+ {
+ TestStates.Passed => TestOutcome.Passed,
+ TestStates.Skipped => TestOutcome.Skipped,
+ TestStates.Failed => TestOutcome.Fail,
+ TestStates.Error => TestOutcome.Error,
+ TestStates.Timeout => TestOutcome.Timeout,
+ TestStates.Cancelled => TestOutcome.Canceled,
+ _ => throw new ArgumentOutOfRangeException(nameof(testState), $"Invalid test state value {testState}")
+ };
+ }
+ }
+}
diff --git a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs
index df414cbfa4e9..3ce15d512dbf 100644
--- a/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/TestCommandParser.cs
@@ -22,12 +22,6 @@ internal static class TestCommandParser
Description = LocalizableStrings.CmdListTestsDescription
}.ForwardAs("-property:VSTestListTests=true");
- public static readonly CliOption> EnvOption = new CliOption>("--environment", "-e")
- {
- Description = LocalizableStrings.CmdEnvironmentVariableDescription,
- HelpName = LocalizableStrings.CmdEnvironmentVariableExpression
- }.AllowSingleArgPerToken();
-
public static readonly CliOption FilterOption = new ForwardedOption("--filter")
{
Description = LocalizableStrings.CmdTestCaseFilterDescription,
@@ -168,9 +162,6 @@ private static bool IsTestingPlatformEnabled()
private static CliCommand ConstructCommand()
{
-#if RELEASE
- return GetVSTestCliCommand();
-#else
bool isTestingPlatformEnabled = IsTestingPlatformEnabled();
string testingSdkName = isTestingPlatformEnabled ? "testingplatform" : "vstest";
@@ -184,7 +175,6 @@ private static CliCommand ConstructCommand()
}
throw new InvalidOperationException($"Testing sdk not supported: {testingSdkName}");
-#endif
}
private static CliCommand GetTestingPlatformCliCommand()
@@ -219,7 +209,7 @@ private static CliCommand GetVSTestCliCommand()
command.Options.Add(SettingsOption);
command.Options.Add(ListTestsOption);
- command.Options.Add(EnvOption);
+ command.Options.Add(CommonOptions.EnvOption);
command.Options.Add(FilterOption);
command.Options.Add(AdapterOption);
command.Options.Add(LoggerOption);
diff --git a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs
index e73dddebf244..5fb118ff256e 100644
--- a/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-test/TestingPlatformCommand.cs
@@ -1,30 +1,28 @@
// 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.Collections.Concurrent;
using System.CommandLine;
using Microsoft.DotNet.Tools.Test;
using Microsoft.TemplateEngine.Cli.Commands;
using Microsoft.Testing.Platform.Helpers;
-using Microsoft.Testing.Platform.OutputDevice;
using Microsoft.Testing.Platform.OutputDevice.Terminal;
namespace Microsoft.DotNet.Cli
{
internal partial class TestingPlatformCommand : CliCommand, ICustomHelp
{
- private readonly ConcurrentBag _testApplications = [];
-
private MSBuildHandler _msBuildHandler;
private TestModulesFilterHandler _testModulesFilterHandler;
private TerminalTestReporter _output;
private bool _isHelp;
+ private int _degreeOfParallelism;
private TestApplicationActionQueue _actionQueue;
private List _args;
private ConcurrentDictionary _executions = new();
private byte _cancelled;
private bool _isDiscovery;
+ private TestApplicationsEventHandlers _eventHandlers;
public TestingPlatformCommand(string name, string description = null) : base(name, description)
{
@@ -36,107 +34,49 @@ public int Run(ParseResult parseResult)
bool hasFailed = false;
try
{
- Console.CancelKeyPress += (s, e) =>
- {
- _output?.StartCancelling();
- CompleteRun();
- };
+ SetupCancelKeyPressHandler();
- int degreeOfParallelism = GetDegreeOfParallelism(parseResult);
+ _degreeOfParallelism = GetDegreeOfParallelism(parseResult);
BuildConfigurationOptions buildConfigurationOptions = GetBuildConfigurationOptions(parseResult);
- InitializeActionQueue(parseResult, degreeOfParallelism, buildConfigurationOptions);
-
- bool filterModeEnabled = parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption);
-
- if (filterModeEnabled && parseResult.HasOption(TestingPlatformOptions.ArchitectureOption))
- {
- VSTestTrace.SafeWriteTrace(() => $"The --arch option is not supported yet.");
- }
-
- if (parseResult.HasOption(TestingPlatformOptions.ListTestsOption))
- {
- _isDiscovery = true;
- }
- BuildConfigurationOptions builtInOptions = new(
- parseResult.HasOption(TestingPlatformOptions.NoRestoreOption),
- parseResult.HasOption(TestingPlatformOptions.NoBuildOption),
- parseResult.HasOption(TestingPlatformOptions.ListTestsOption),
- parseResult.GetValue(TestingPlatformOptions.ConfigurationOption),
- parseResult.GetValue(TestingPlatformOptions.ArchitectureOption));
+ _isDiscovery = parseResult.HasOption(TestingPlatformOptions.ListTestsOption);
+ _args = [.. parseResult.UnmatchedTokens];
+ _isHelp = ContainsHelpOption(parseResult.GetArguments());
- var console = new SystemConsole();
- var output = new TerminalTestReporter(console, new TerminalTestReporterOptions()
- {
- ShowPassedTests = Environment.GetEnvironmentVariable("SHOW_PASSED") == "1" ? () => true : () => false,
- ShowProgress = () => Environment.GetEnvironmentVariable("NO_PROGRESS") != "1",
- UseAnsi = Environment.GetEnvironmentVariable("NO_ANSI") != "1",
- ShowAssembly = true,
- ShowAssemblyStartAndComplete = true,
- });
- _output = output;
+ InitializeOutput(_degreeOfParallelism);
- _isHelp = false;
- if (ContainsHelpOption(parseResult.GetArguments()))
+ bool filterModeEnabled = parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption);
+ if (_isHelp)
{
- _isHelp = true;
- _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) =>
- {
- testApp.HelpRequested += OnHelpRequested;
- testApp.ErrorReceived += OnErrorReceived;
- testApp.TestProcessExited += OnTestProcessExited;
- testApp.Run += OnTestApplicationRun;
- testApp.ExecutionIdReceived += OnExecutionIdReceived;
-
- var result = await testApp.RunAsync(filterModeEnabled, enableHelp: true, builtInOptions);
- return result;
- });
+ InitializeHelpActionQueue(_degreeOfParallelism, buildConfigurationOptions, filterModeEnabled);
}
else
{
- _output.TestExecutionStarted(DateTimeOffset.Now, degreeOfParallelism, _isDiscovery);
-
- _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) =>
- {
- testApp.HandshakeReceived += OnHandshakeReceived;
- testApp.DiscoveredTestsReceived += OnDiscoveredTestsReceived;
- testApp.TestResultsReceived += OnTestResultsReceived;
- testApp.FileArtifactsReceived += OnFileArtifactsReceived;
- testApp.SessionEventReceived += OnSessionEventReceived;
- testApp.ErrorReceived += OnErrorReceived;
- testApp.TestProcessExited += OnTestProcessExited;
- testApp.Run += OnTestApplicationRun;
- testApp.ExecutionIdReceived += OnExecutionIdReceived;
-
- return await testApp.RunAsync(filterModeEnabled, enableHelp: false, builtInOptions);
- });
+ InitializeTestExecutionActionQueue(_degreeOfParallelism, buildConfigurationOptions, filterModeEnabled);
}
- _args = [.. parseResult.UnmatchedTokens];
- _msBuildHandler = new(_args, _actionQueue, degreeOfParallelism);
+ _msBuildHandler = new(_args, _actionQueue, _output);
_testModulesFilterHandler = new(_args, _actionQueue);
- if (parseResult.HasOption(TestingPlatformOptions.TestModulesFilterOption))
+ _eventHandlers = new TestApplicationsEventHandlers(_executions, _output);
+
+ if (filterModeEnabled)
{
if (!_testModulesFilterHandler.RunWithTestModulesFilter(parseResult))
{
- CompleteRun();
return ExitCodes.GenericFailure;
}
}
else
{
- var buildPathOptions = GetBuildPathOptions(parseResult);
- if (!_msBuildHandler.RunMSBuild(buildPathOptions).GetAwaiter().GetResult())
+ if (!_msBuildHandler.RunMSBuild(GetBuildOptions(parseResult)))
{
- CompleteRun();
return ExitCodes.GenericFailure;
}
if (!_msBuildHandler.EnqueueTestApplications())
{
- VSTestTrace.SafeWriteTrace(() => LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription);
- CompleteRun();
+ _output.WriteMessage(LocalizableStrings.CmdUnsupportedVSTestTestApplicationsDescription);
return ExitCodes.GenericFailure;
}
}
@@ -146,278 +86,117 @@ public int Run(ParseResult parseResult)
}
finally
{
+ CompleteRun();
CleanUp();
}
- CompleteRun();
return hasFailed ? ExitCodes.GenericFailure : ExitCodes.Success;
}
- private static int GetDegreeOfParallelism(ParseResult parseResult)
- {
- if (!int.TryParse(parseResult.GetValue(TestingPlatformOptions.MaxParallelTestModulesOption), out int degreeOfParallelism) || degreeOfParallelism <= 0)
- degreeOfParallelism = Environment.ProcessorCount;
- return degreeOfParallelism;
- }
-
- private static BuildConfigurationOptions GetBuildConfigurationOptions(ParseResult parseResult) =>
- new(parseResult.HasOption(TestingPlatformOptions.NoRestoreOption),
- parseResult.HasOption(TestingPlatformOptions.NoBuildOption),
- parseResult.HasOption(TestingPlatformOptions.ListTestsOption),
- parseResult.GetValue(TestingPlatformOptions.ConfigurationOption),
- parseResult.GetValue(TestingPlatformOptions.ArchitectureOption));
-
- private static BuildPathsOptions GetBuildPathOptions(ParseResult parseResult) =>
- new(parseResult.GetValue(TestingPlatformOptions.ProjectOption),
- parseResult.GetValue(TestingPlatformOptions.SolutionOption),
- parseResult.GetValue(TestingPlatformOptions.DirectoryOption));
-
- private void InitializeActionQueue(ParseResult parseResult, int degreeOfParallelism, BuildConfigurationOptions buildConfigurationOptions)
- {
- if (!ContainsHelpOption(parseResult.GetArguments()))
- {
- _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) =>
- {
- testApp.HandshakeReceived += OnHandshakeReceived;
- testApp.DiscoveredTestsReceived += OnDiscoveredTestsReceived;
- testApp.TestResultsReceived += OnTestResultsReceived;
- testApp.FileArtifactsReceived += OnFileArtifactsReceived;
- testApp.SessionEventReceived += OnSessionEventReceived;
- testApp.ErrorReceived += OnErrorReceived;
- testApp.TestProcessExited += OnTestProcessExited;
- testApp.Run += OnTestApplicationRun;
- testApp.ExecutionIdReceived += OnExecutionIdReceived;
-
- return await testApp.RunAsync(hasFilterMode: false, enableHelp: false, buildConfigurationOptions);
- });
- }
- else
- {
- _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) =>
- {
- testApp.HelpRequested += OnHelpRequested;
- testApp.ErrorReceived += OnErrorReceived;
- testApp.TestProcessExited += OnTestProcessExited;
- testApp.Run += OnTestApplicationRun;
- testApp.ExecutionIdReceived += OnExecutionIdReceived;
-
- return await testApp.RunAsync(hasFilterMode: true, enableHelp: true, buildConfigurationOptions);
- });
- }
- }
-
- private static bool ContainsHelpOption(IEnumerable args) => args.Contains(CliConstants.HelpOptionKey) || args.Contains(CliConstants.HelpOptionKey.Substring(0, 2));
-
- private void CompleteRun()
+ private void SetupCancelKeyPressHandler()
{
- if (Interlocked.CompareExchange(ref _cancelled, 1, 0) == 0)
+ Console.CancelKeyPress += (s, e) =>
{
- if (!_isHelp)
- {
- _output?.TestExecutionCompleted(DateTimeOffset.Now);
- }
- }
+ _output?.StartCancelling();
+ CompleteRun();
+ };
}
- private void CleanUp()
+ private void InitializeOutput(int degreeOfParallelism)
{
- _msBuildHandler.Dispose();
- foreach (var testApplication in _testApplications)
+ var console = new SystemConsole();
+ _output = new TerminalTestReporter(console, new TerminalTestReporterOptions()
{
- testApplication.Dispose();
- }
- }
-
- private void OnHandshakeReceived(object sender, HandshakeArgs args)
- {
- var testApplication = (TestApplication)sender;
- var executionId = args.Handshake.Properties[HandshakeMessagePropertyNames.ExecutionId];
- var arch = args.Handshake.Properties[HandshakeMessagePropertyNames.Architecture]?.ToLower();
- var tfm = TargetFrameworkParser.GetShortTargetFramework(args.Handshake.Properties[HandshakeMessagePropertyNames.Framework]);
- (string ModulePath, string TargetFramework, string Architecture, string ExecutionId) appInfo = new(testApplication.Module.DllOrExePath, tfm, arch, executionId);
- _executions[testApplication] = appInfo;
- _output.AssemblyRunStarted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId);
+ ShowPassedTests = Environment.GetEnvironmentVariable("SHOW_PASSED") == "1" ? () => true : () => false,
+ ShowProgress = () => Environment.GetEnvironmentVariable("NO_PROGRESS") != "1",
+ UseAnsi = Environment.GetEnvironmentVariable("NO_ANSI") != "1",
+ ShowAssembly = true,
+ ShowAssemblyStartAndComplete = true,
+ });
- if (!VSTestTrace.TraceEnabled) return;
-
- foreach (var property in args.Handshake.Properties)
+ if (!_isHelp)
{
- VSTestTrace.SafeWriteTrace(() => $"{property.Key}: {property.Value}");
+ _output.TestExecutionStarted(DateTimeOffset.Now, degreeOfParallelism, _isDiscovery, _isHelp);
}
}
- private void OnDiscoveredTestsReceived(object sender, DiscoveredTestEventArgs args)
+ private void InitializeHelpActionQueue(int degreeOfParallelism, BuildConfigurationOptions buildConfigurationOptions, bool filterModeEnabled)
{
- var testApp = (TestApplication)sender;
- var appInfo = _executions[testApp];
-
- foreach (var test in args.DiscoveredTests)
+ _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) =>
{
- _output.TestDiscovered(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
- test.DisplayName,
- test.Uid);
- }
+ testApp.HelpRequested += OnHelpRequested;
+ testApp.ErrorReceived += _eventHandlers.OnErrorReceived;
+ testApp.TestProcessExited += _eventHandlers.OnTestProcessExited;
+ testApp.ExecutionIdReceived += _eventHandlers.OnExecutionIdReceived;
- if (!VSTestTrace.TraceEnabled) return;
-
- VSTestTrace.SafeWriteTrace(() => $"DiscoveredTests Execution Id: {args.ExecutionId}");
- foreach (var discoveredTestMessage in args.DiscoveredTests)
- {
- VSTestTrace.SafeWriteTrace(() => $"DiscoveredTest: {discoveredTestMessage.Uid}, {discoveredTestMessage.DisplayName}");
- }
+ return await testApp.RunAsync(filterModeEnabled, enableHelp: true, buildConfigurationOptions);
+ });
}
- private void OnTestResultsReceived(object sender, TestResultEventArgs args)
+ private void InitializeTestExecutionActionQueue(int degreeOfParallelism, BuildConfigurationOptions buildConfigurationOptions, bool filterModeEnabled)
{
- foreach (var testResult in args.SuccessfulTestResults)
- {
- var testApp = (TestApplication)sender;
- var appInfo = _executions[testApp];
- _output.TestCompleted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
- testResult.Uid,
- testResult.DisplayName,
- ToOutcome(testResult.State),
- TimeSpan.FromTicks(testResult.Duration ?? 0),
- exceptions: null,
- expected: null,
- actual: null,
- standardOutput: null,
- errorOutput: null);
- }
-
- foreach (var testResult in args.FailedTestResults)
- {
- var testApp = (TestApplication)sender;
- // TODO: expected
- // TODO: actual
- var appInfo = _executions[testApp];
- _output.TestCompleted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
- testResult.Uid,
- testResult.DisplayName,
- ToOutcome(testResult.State),
- TimeSpan.FromTicks(testResult.Duration ?? 0),
- exceptions: testResult.Exceptions.Select(fe => new Microsoft.Testing.Platform.OutputDevice.Terminal.FlatException(fe.ErrorMessage, fe.ErrorType, fe.StackTrace)).ToArray(),
- expected: null,
- actual: null,
- standardOutput: null,
- errorOutput: null);
- }
-
- if (!VSTestTrace.TraceEnabled) return;
-
- VSTestTrace.SafeWriteTrace(() => $"TestResults Execution Id: {args.ExecutionId}");
-
- foreach (SuccessfulTestResult successfulTestResult in args.SuccessfulTestResults)
+ _actionQueue = new(degreeOfParallelism, async (TestApplication testApp) =>
{
- VSTestTrace.SafeWriteTrace(() => $"SuccessfulTestResult: {successfulTestResult.Uid}, {successfulTestResult.DisplayName}, " +
- $"{successfulTestResult.State}, {successfulTestResult.Duration}, {successfulTestResult.Reason}, {successfulTestResult.StandardOutput}," +
- $"{successfulTestResult.ErrorOutput}, {successfulTestResult.SessionUid}");
- }
+ testApp.HandshakeReceived += _eventHandlers.OnHandshakeReceived;
+ testApp.DiscoveredTestsReceived += _eventHandlers.OnDiscoveredTestsReceived;
+ testApp.TestResultsReceived += _eventHandlers.OnTestResultsReceived;
+ testApp.FileArtifactsReceived += _eventHandlers.OnFileArtifactsReceived;
+ testApp.SessionEventReceived += _eventHandlers.OnSessionEventReceived;
+ testApp.ErrorReceived += _eventHandlers.OnErrorReceived;
+ testApp.TestProcessExited += _eventHandlers.OnTestProcessExited;
+ testApp.ExecutionIdReceived += _eventHandlers.OnExecutionIdReceived;
- foreach (FailedTestResult failedTestResult in args.FailedTestResults)
- {
- VSTestTrace.SafeWriteTrace(() => $"FailedTestResult: {failedTestResult.Uid}, {failedTestResult.DisplayName}, " +
- $"{failedTestResult.State}, {failedTestResult.Duration}, {failedTestResult.Reason}, {string.Join(", ", failedTestResult.Exceptions?.Select(e => $"{e.ErrorMessage}, {e.ErrorType}, {e.StackTrace}"))}" +
- $"{failedTestResult.StandardOutput}, {failedTestResult.ErrorOutput}, {failedTestResult.SessionUid}");
- }
+ return await testApp.RunAsync(filterModeEnabled, enableHelp: false, buildConfigurationOptions);
+ });
}
- public static TestOutcome ToOutcome(byte? testState)
+ private static int GetDegreeOfParallelism(ParseResult parseResult)
{
- return testState switch
- {
- TestStates.Passed => TestOutcome.Passed,
- TestStates.Skipped => TestOutcome.Skipped,
- TestStates.Failed => TestOutcome.Fail,
- TestStates.Error => TestOutcome.Error,
- TestStates.Timeout => TestOutcome.Timeout,
- TestStates.Cancelled => TestOutcome.Canceled,
- _ => throw new ArgumentOutOfRangeException(nameof(testState), $"Invalid test state value {testState}")
- };
+ if (!int.TryParse(parseResult.GetValue(TestingPlatformOptions.MaxParallelTestModulesOption), out int degreeOfParallelism) || degreeOfParallelism <= 0)
+ degreeOfParallelism = Environment.ProcessorCount;
+ return degreeOfParallelism;
}
- private void OnFileArtifactsReceived(object sender, FileArtifactEventArgs args)
- {
- var testApp = (TestApplication)sender;
- var appInfo = _executions[testApp];
-
- foreach (var artifact in args.FileArtifacts)
- {
- // TODO: Is artifact out of process
- _output.ArtifactAdded(
- outOfProcess: false,
- appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId,
- artifact.TestDisplayName, artifact.FullPath);
- }
-
- if (!VSTestTrace.TraceEnabled) return;
-
- VSTestTrace.SafeWriteTrace(() => $"FileArtifactMessages Execution Id: {args.ExecutionId}");
-
- foreach (FileArtifact fileArtifactMessage in args.FileArtifacts)
- {
- VSTestTrace.SafeWriteTrace(() => $"FileArtifact: {fileArtifactMessage.FullPath}, {fileArtifactMessage.DisplayName}, " +
- $"{fileArtifactMessage.Description}, {fileArtifactMessage.TestUid}, {fileArtifactMessage.TestDisplayName}, " +
- $"{fileArtifactMessage.SessionUid}");
- }
- }
+ private static BuildConfigurationOptions GetBuildConfigurationOptions(ParseResult parseResult) =>
+ new(parseResult.HasOption(TestingPlatformOptions.ListTestsOption),
+ parseResult.GetValue(TestingPlatformOptions.ConfigurationOption),
+ parseResult.GetValue(TestingPlatformOptions.ArchitectureOption));
- private void OnSessionEventReceived(object sender, SessionEventArgs args)
+ private BuildOptions GetBuildOptions(ParseResult parseResult)
{
- if (!VSTestTrace.TraceEnabled) return;
+ bool allowBinLog = MSBuildUtility.IsBinaryLoggerEnabled([.. parseResult.UnmatchedTokens], out string binLogFileName);
- var sessionEvent = args.SessionEvent;
- VSTestTrace.SafeWriteTrace(() => $"TestSessionEvent: {sessionEvent.SessionType}, {sessionEvent.SessionUid}, {sessionEvent.ExecutionId}");
+ return new BuildOptions(parseResult.GetValue(TestingPlatformOptions.ProjectOption),
+ parseResult.GetValue(TestingPlatformOptions.SolutionOption),
+ parseResult.GetValue(TestingPlatformOptions.DirectoryOption),
+ parseResult.HasOption(TestingPlatformOptions.NoRestoreOption),
+ parseResult.HasOption(TestingPlatformOptions.NoBuildOption),
+ parseResult.GetValue(TestingPlatformOptions.ConfigurationOption),
+ parseResult.HasOption(TestingPlatformOptions.ArchitectureOption) ?
+ CommonOptions.ResolveRidShorthandOptionsToRuntimeIdentifier(string.Empty, parseResult.GetValue(TestingPlatformOptions.ArchitectureOption)) :
+ string.Empty,
+ allowBinLog,
+ binLogFileName,
+ _degreeOfParallelism);
}
- private void OnErrorReceived(object sender, ErrorEventArgs args)
- {
- if (!VSTestTrace.TraceEnabled) return;
-
- VSTestTrace.SafeWriteTrace(() => args.ErrorMessage);
- }
+ private static bool ContainsHelpOption(IEnumerable args) => args.Contains(CliConstants.HelpOptionKey) || args.Contains(CliConstants.HelpOptionKey.Substring(0, 2));
- private void OnTestProcessExited(object sender, TestProcessExitEventArgs args)
+ private void CompleteRun()
{
- var testApplication = (TestApplication)sender;
-
- // If the application exits too early we might not start the execution,
- // e.g. if the parameter is incorrect.
- if (_executions.TryGetValue(testApplication, out var appInfo))
- {
- _output.AssemblyRunCompleted(appInfo.ModulePath, appInfo.TargetFramework, appInfo.Architecture, appInfo.ExecutionId, args.ExitCode, string.Join(Environment.NewLine, args.OutputData), string.Join(Environment.NewLine, args.ErrorData));
- }
- else
- {
- _output.AssemblyRunCompleted(testApplication.Module.DllOrExePath ?? testApplication.Module.ProjectPath, testApplication.Module.TargetFramework, architecture: null, null, args.ExitCode, string.Join(Environment.NewLine, args.OutputData), string.Join(Environment.NewLine, args.ErrorData));
- }
-
- if (!VSTestTrace.TraceEnabled) return;
-
- if (args.ExitCode != ExitCodes.Success)
- {
- VSTestTrace.SafeWriteTrace(() => $"Test Process exited with non-zero exit code: {args.ExitCode}");
- }
-
- if (args.OutputData.Count > 0)
- {
- VSTestTrace.SafeWriteTrace(() => $"Output Data: {string.Join("\n", args.OutputData)}");
- }
-
- if (args.ErrorData.Count > 0)
+ if (Interlocked.CompareExchange(ref _cancelled, 1, 0) == 0)
{
- VSTestTrace.SafeWriteTrace(() => $"Error Data: {string.Join("\n", args.ErrorData)}");
+ _output?.TestExecutionCompleted(DateTimeOffset.Now);
}
}
- private void OnTestApplicationRun(object sender, EventArgs args)
- {
- TestApplication testApp = sender as TestApplication;
- _testApplications.Add(testApp);
- }
-
- private void OnExecutionIdReceived(object sender, ExecutionEventArgs args)
+ private void CleanUp()
{
+ _msBuildHandler.Dispose();
+ foreach (var execution in _executions)
+ {
+ execution.Key.Dispose();
+ }
}
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-test/ValidationUtility.cs b/src/Cli/dotnet/commands/dotnet-test/ValidationUtility.cs
new file mode 100644
index 000000000000..efb5d3996de3
--- /dev/null
+++ b/src/Cli/dotnet/commands/dotnet-test/ValidationUtility.cs
@@ -0,0 +1,57 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.DotNet.Tools.Test;
+using Microsoft.Testing.Platform.OutputDevice.Terminal;
+
+namespace Microsoft.DotNet.Cli
+{
+ internal static class ValidationUtility
+ {
+ public static bool ValidateBuildPathOptions(BuildOptions buildPathOptions, TerminalTestReporter output)
+ {
+ if ((!string.IsNullOrEmpty(buildPathOptions.ProjectPath) && !string.IsNullOrEmpty(buildPathOptions.SolutionPath)) ||
+ (!string.IsNullOrEmpty(buildPathOptions.ProjectPath) && !string.IsNullOrEmpty(buildPathOptions.DirectoryPath)) ||
+ (!string.IsNullOrEmpty(buildPathOptions.SolutionPath) && !string.IsNullOrEmpty(buildPathOptions.DirectoryPath)))
+ {
+ output.WriteMessage(LocalizableStrings.CmdMultipleBuildPathOptionsErrorDescription);
+ return false;
+ }
+
+ if (!string.IsNullOrEmpty(buildPathOptions.ProjectPath))
+ {
+ return ValidateFilePath(buildPathOptions.ProjectPath, CliConstants.ProjectExtensions, LocalizableStrings.CmdInvalidProjectFileExtensionErrorDescription, output);
+ }
+
+ if (!string.IsNullOrEmpty(buildPathOptions.SolutionPath))
+ {
+ return ValidateFilePath(buildPathOptions.SolutionPath, CliConstants.SolutionExtensions, LocalizableStrings.CmdInvalidSolutionFileExtensionErrorDescription, output);
+ }
+
+ if (!string.IsNullOrEmpty(buildPathOptions.DirectoryPath) && !Directory.Exists(buildPathOptions.DirectoryPath))
+ {
+ output.WriteMessage(string.Format(LocalizableStrings.CmdNonExistentDirectoryErrorDescription, buildPathOptions.DirectoryPath));
+ return false;
+ }
+
+ return true;
+ }
+
+ private static bool ValidateFilePath(string filePath, string[] validExtensions, string errorMessage, TerminalTestReporter output)
+ {
+ if (!validExtensions.Contains(Path.GetExtension(filePath)))
+ {
+ output.WriteMessage(string.Format(errorMessage, filePath));
+ return false;
+ }
+
+ if (!File.Exists(filePath))
+ {
+ output.WriteMessage(string.Format(LocalizableStrings.CmdNonExistentFileErrorDescription, Path.GetFullPath(filePath)));
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf
index 1a467d0ffc2e..5232acc9dae7 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.cs.xlf
@@ -118,36 +118,6 @@ Pro MSTest před 2.2.4 se časový limit použije pro všechny testovací příp
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 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.
-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
-
-
-
-
- NAME="VALUE"
- NÁZEV="HODNOTA"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf
index 7f363065066d..07f28f763619 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.de.xlf
@@ -118,36 +118,6 @@ für MSTest vor 2.2.4 wird das Timeout für alle Testfälle verwendet.
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- 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:
--e VARIABLE=abc
--e VARIABLE="wert mit leerzeichen"
--e VARIABLE="wert;getrennt durch;semikolons"
--e VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="WERT"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf
index 759ad3d5c656..f1e79eedeea6 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.es.xlf
@@ -120,36 +120,6 @@ Para MSTest antes de 2.2.4, el tiempo de espera se usa para todos los casos de p
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- 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:
--e VARIABLE=abc
--e VARIABLE="value with spaces"
--e VARIABLE="value;seperated with;semicolons"
--e VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="VALUE"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf
index 158ab5127a98..5ae2232e016b 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.fr.xlf
@@ -118,36 +118,6 @@ Pour MSTest avant la version 2.2.4, le délai d’expiration est utilisé pour
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 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é.
-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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="VALUE"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf
index 19be826da372..49d98e288b86 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.it.xlf
@@ -118,36 +118,6 @@ Per MSTest prima di 2.2.4, il timeout viene usato per tutti i test case.Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- 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.
-
-Esempi:
--e VARIABLE=abc
--e VARIABLE="valore con spazi"
--e VARIABLE="valore;delimitato da;punti e virgola"
--e VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="VALORE"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf
index 2a42214138ea..cee3b0dc4092 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ja.xlf
@@ -118,36 +118,6 @@ MSTest 2.2.4 以前の場合、タイムアウトはすべてのテスト ケー
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- 環境変数の値を設定します。
-変数が存在しない場合は作成され、存在する場合はオーバーライドされます。
-これにより、テストは強制的に分離プロセスで実行されます。
-この引数は、複数の変数を指定するために複数回指定できます。
-
-例:
--e VARIABLE=abc
--e VARIABLE="value with spaces"
--e VARIABLE="value;seperated with;semicolons"
--e VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="VALUE"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf
index 9d51e9159532..4ca212886e5d 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ko.xlf
@@ -118,36 +118,6 @@ For MSTest before 2.2.4, the timeout is used for all testcases.
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- 환경 변수의 값을 설정합니다.
-변수가 없는 경우 변수를 만들고, 변수가 있으면 재정의합니다.
-이는 테스트가 격리된 프로세스에서 강제로 실행되도록 합니다.
-이 인수를 여러 번 지정하여 여러 변수를 제공할 수 있습니다.
-
-예:
--e VARIABLE=abc
--e VARIABLE="value with spaces"
--e VARIABLE="value;seperated with;semicolons"
--e VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="VALUE"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf
index b4ebe3500c92..06dc75aeec0e 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pl.xlf
@@ -118,36 +118,6 @@ W przypadku platformy MSTest przed wersją 2.2.4 limit czasu jest używany dla w
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- 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 ZM1=abc -e ZM2=def -e ZM3=ghi
-
-
-
-
- NAME="VALUE"
- NAZWA="WARTOŚĆ"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf
index c4f3190d52e3..fbff9cc212eb 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.pt-BR.xlf
@@ -118,36 +118,6 @@ Para MSTest antes de 2.2.4, o tempo limite é usado para todos os casos de teste
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 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.
-
-Exemplos:
--e VARIABLE=abc
--e VARIABLE="valor com espaços"
--e VARIABLE="valor;separado com;ponto e vírgula"
--e VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="VALUE"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf
index 74c94acd1e44..e92b6da91bbe 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.ru.xlf
@@ -118,36 +118,6 @@ For MSTest before 2.2.4, the timeout is used for all testcases.
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- Устанавливает значение переменной среды.
-Если переменной среды не существует, она создается. Если переменная среды существует, она переопределяется.
-Этот аргумент подразумевает принудительное выполнение тестов в изолированном процессе.
-Этот аргумент может быть указан несколько раз для нескольких переменных среды.
-
-Примеры:
--e VARIABLE=abc
--e VARIABLE="значение с пробелами"
--e VARIABLE="значение;разделенное;точками;с;запятыми"
--e VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="VALUE"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf
index 2f0947f5b3f6..bb1b7f37cd0f 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.tr.xlf
@@ -118,36 +118,6 @@ Zaman aşımı davranışı veri tabanlı testlerle birlikte kullanıldığında
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- 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:
--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
-
-
-
-
- NAME="VALUE"
- AD="DEĞER"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf
index 860e119ca0fc..eb07d78e1ba2 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hans.xlf
@@ -118,36 +118,6 @@ For MSTest before 2.2.4, the timeout is used for all testcases.
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- 设置环境变量的值。
-如果该变量不存在,则创建它;如果它已存在,则替代它。
-这将在隔离的进程中强制运行测试。
-可多次指定此参数来提供多个变量。
-
-示例:
--e VARIABLE=abc
--e VARIABLE="value with spaces"
--e VARIABLE="value;seperated with;semicolons"
--e VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="VALUE"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf
index c5553741971d..24bb05a297fc 100644
--- a/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/commands/dotnet-test/xlf/LocalizableStrings.zh-Hant.xlf
@@ -118,36 +118,6 @@ For MSTest before 2.2.4, the timeout is used for all testcases.
Defines the path of directory to run. If not specified, it defaults to the current directory.
-
- 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 VAR1=abc -e VAR2=def -e VAR3=ghi
-
- 設定環境變數的值。
-若變數不存在,則加以建立; 若有,則予以覆寫。
-這會強制在隔離流程中執行測試。
-此引數可多次指定,以提供多項變數。
-
-範例:
--e VARIABLE=abc
--e VARIABLE="value with spaces"
--e VARIABLE="value;seperated with;semicolons"
--e VAR1=abc -e VAR2=def -e VAR3=ghi
-
-
-
-
- NAME="VALUE"
- NAME="VALUE"
-
-
The provided project file has an invalid extension: {0}.
The provided project file has an invalid extension: {0}.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.cs.xlf
index 8735c705a192..40980d8fd9f5 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.cs.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ Příkaz 'dotnet tool search' bez podmínek přistupuje nuget.org k hledání nástrojů, ale není k dispozici ve vašem nuget.config. Pokud chcete tento příkaz spustit, přidejte ho.
+ To lze provést pomocí tohoto příkazu:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.de.xlf
index e2a2fb2c47e9..bbe92e4a72eb 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.de.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ Der Befehl "dotnet tool search" greift ohne Bedingungen auf nuget.org zu, um Tools zu finden, ist jedoch in Ihrem nuget.config nicht vorhanden. Fügen Sie ihn hinzu, um diesen Befehl auszuführen.
+ Dies kann mit diesem Befehl erfolgen:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.es.xlf
index 8912b64e886e..a356d37d1703 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.es.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ El comando "dotnet tool search" accede incondicionalmente a nuget.org para buscar herramientas, pero no está presente en el nuget.config. Agréguelo para ejecutar este comando.
+ Esto se puede hacer con este comando:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.fr.xlf
index adf9625fdc88..16f3f3f946a5 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.fr.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ La commande « dotnet tool search » accède de manière inconditionnelle nuget.org à la recherche d’outils, mais elle n’est pas présente dans votre nuget.config. Ajoutez-le pour exécuter cette commande.
+ Cette opération peut être effectuée avec cette commande :
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.it.xlf
index ca389bb04a5b..11dbc2daa672 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.it.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ Il comando 'dotnet tool search' accede in modo incondizionato nuget.org per trovare strumenti, ma non è presente nel nuget.config. Aggiungerlo per eseguire questo comando.
+ Questa operazione può essere eseguita con questo comando:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ja.xlf
index 59c7f632178f..0dc0d4f8e5e8 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ja.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ 'dotnet tool search' コマンドは、nuget.org に無条件でアクセスしてツールを検索しますが、nuget.config には存在しません。このコマンドを実行するには、追加してください。
+ これは、次のコマンドで実行できます:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ko.xlf
index e3f997df3bd3..6985c4ce628f 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ko.xlf
@@ -51,9 +51,9 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
- dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
+ 'dotnet tool search' 명령은 도구를 찾기 위해 nuget.org 무조건 액세스하지만 nuget.config 없습니다. 이 명령을 실행하려면 추가하세요.
+ 이 명령은 다음 명령을 사용하여 수행할 수 있습니다.
+ dotnet nuget 소스 추가 https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.pl.xlf
index 87564f82fde7..e0a3b1b985a8 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.pl.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ Polecenie "dotnet tool search" bezwarunkowo uzyskuje dostęp do nuget.org w celu znalezienia narzędzi, ale nie występuje w nuget.config. Dodaj go, aby uruchomić to polecenie.
+ Można to zrobić za pomocą tego polecenia:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.pt-BR.xlf
index 2367aeb40b37..a33000d9851f 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.pt-BR.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ O comando 'dotnet tool search' acessa incondicionalmente nuget.org para localizar ferramentas, mas não está presente em seu nuget.config. Adicione-o para executar este comando.
+ Isso pode ser feito com este comando:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ru.xlf
index 1cb904fa5f8c..913234dfe843 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.ru.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ Команда "dotnet tool search" безусловно nuget.org для поиска инструментов, но отсутствует в nuget.config. Добавьте его для выполнения этой команды.
+ Это можно сделать с помощью этой команды:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.tr.xlf
index fca2bd8d4de3..aaa0f3f83887 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.tr.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ 'dotnet tool search' komutu koşulsuz olarak nuget.org araçları bulmak için bu komuta erişer, ancak bu komut nuget.config. Bu komutu çalıştırmak için ekleyin.
+ Bu, şu komutla kullanılabilir:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.zh-Hans.xlf
index bf04ef763834..7a123c60a16e 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.zh-Hans.xlf
@@ -51,9 +51,9 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
- dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
+ “dotnet 工具搜索”命令无条件访问 nuget.org 以查找工具,但 nuget.config 中不存在该命令。添加它以运行此命令。
+ 可以使用此命令完成此操作:
+ dotnet nuget 添加源 https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.zh-Hant.xlf
index 9fda02783409..45277714d064 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/commands/dotnet-tool/search/xlf/LocalizableStrings.zh-Hant.xlf
@@ -51,8 +51,8 @@
The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
This can be done with this command:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
- The 'dotnet tool search' command unconditionally accesses nuget.org to find tools, but it is not present in your nuget.config. Add it to run this command.
- This can be done with this command:
+ 「dotnet 工具搜尋」命令無條件存取 nuget.org 以尋找工具,但該命令不存在於您的 nuget.config 中。新增它以執行此命令。
+ 這可以使用此指令來完成:
dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org
Do not translate 'dotnet tool search' or 'nuget.config'. Do not localize the last line at all.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs
index 7267a5492bc5..8dfaa13357c7 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/IInstaller.cs
@@ -14,6 +14,8 @@ internal interface IInstaller : IWorkloadManifestInstaller
{
int ExitCode { get; }
+ WorkloadSet GetWorkloadSetContents(string workloadVersion);
+
void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, ITransactionContext transactionContext, DirectoryPath? offlineCache = null);
void RepairWorkloads(IEnumerable workloadIds, SdkFeatureBand sdkFeatureBand, DirectoryPath? offlineCache = null);
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx
index cd04c02ff902..42c3799c85ee 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/LocalizableStrings.resx
@@ -327,7 +327,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Skip signature verification of workload packages and installers.
@@ -348,7 +348,7 @@
Manifest MSI not found in NuGet package {0}
- Update to the specified workload version.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
Installing workload version {0}.
@@ -365,4 +365,8 @@
Failed to install workload version {0}: {1}
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ {Locked="--version"}
+
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs
index a9acfa057fee..2f26860f62b0 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs
@@ -1183,5 +1183,10 @@ void IInstaller.UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool? newMode)
string newModeString = newMode == null ? "" : newMode.Value ? WorkloadConfigCommandParser.UpdateMode_WorkloadSet : WorkloadConfigCommandParser.UpdateMode_Manifests;
Reporter.WriteLine(string.Format(LocalizableStrings.UpdatedWorkloadMode, newModeString));
}
+
+ // This method should never be called for this kind of installer. It is challenging to get this information from an MSI
+ // and totally unnecessary as the information is identical from a file-based installer. It was added to IInstaller only
+ // to facilitate testing. As a consequence, it does not need to be implemented.
+ public WorkloadSet GetWorkloadSetContents(string workloadVersion) => throw new NotImplementedException();
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs
index 3f3e984d6eac..61b707bf36a3 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadInstallCommand.cs
@@ -11,8 +11,6 @@
using Microsoft.NET.Sdk.WorkloadManifestReader;
using NuGet.Common;
using NuGet.Versioning;
-using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadResolver;
-using System.Text;
namespace Microsoft.DotNet.Workloads.Workload.Install
{
@@ -39,7 +37,22 @@ public WorkloadInstallCommand(
tempDirPath: tempDirPath)
{
_skipManifestUpdate = skipWorkloadManifestUpdate ?? parseResult.GetValue(WorkloadInstallCommandParser.SkipManifestUpdateOption);
- _workloadIds = workloadIds ?? parseResult.GetValue(WorkloadInstallCommandParser.WorkloadIdArgument).ToList().AsReadOnly();
+ var unprocessedWorkloadIds = workloadIds ?? parseResult.GetValue(WorkloadInstallCommandParser.WorkloadIdArgument);
+ if (unprocessedWorkloadIds?.Any(id => id.Contains('@')) == true)
+ {
+ _workloadIds = unprocessedWorkloadIds.Select(id => id.Split('@')[0]).ToList().AsReadOnly();
+ if (SpecifiedWorkloadSetVersionOnCommandLine)
+ {
+ throw new GracefulException(LocalizableStrings.CannotSpecifyVersionAndWorkloadIdsByComponent, isUserError: true);
+ }
+
+ _workloadSetVersionFromCommandLine = unprocessedWorkloadIds;
+ }
+ else
+ {
+ _workloadIds = unprocessedWorkloadIds.ToList().AsReadOnly();
+ }
+
var resolvedReporter = _printDownloadLinkOnly ? NullReporter.Instance : Reporter;
_workloadInstaller = _workloadInstallerFromConstructor ??
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf
index f30fd75f4019..6865ac80971d 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.cs.xlf
@@ -27,6 +27,11 @@
Možnosti {0} a {1} nelze použít společně. Jednu z možností odeberte.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ Verzi úlohy nejde zadat pomocí možnosti --version a zároveň zadat verze, které se mají nainstalovat, pomocí syntaxe workload@version.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
Na příkazovém řádku nelze pomocí možnosti --version nebo --from-history zadat konkrétní verzi úlohy, pokud je již verze zadána v souboru global.json {0}. Pokud chcete aktualizovat globálně nainstalovanou verzi úlohy, spusťte příkaz mimo cestu obsahující daný soubor global.json nebo aktualizujte verzi uvedenou v souboru global.json a spusťte příkaz dotnet workload update.
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
Možnost {0} se nedá použít, pokud je v global.json zadaná verze úlohy. Odeberte možnost {0} nebo odeberte element workloadVersion z {1}.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- Aktualizuje verzi na zadanou verzi úlohy.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ Verze úlohy, která se má zobrazit, nebo jedna nebo více úloh a jejich verzí připojených znakem @
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf
index f6fae7626734..be03107879fc 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.de.xlf
@@ -27,6 +27,11 @@
Die Optionen {0} und {1} können nicht zusammen verwendet werden. Entfernen Sie eine der Optionen.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ Es kann keine Workloadversion mithilfe der Option --version angegeben werden, es können jedoch auch Versionen angegeben werden, die mithilfe workload@version Syntax installiert werden sollen.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
Eine bestimmte Workload-Version kann in der Befehlszeile nicht über „--version“ oder „--from-history“ angegeben werden, wenn bereits eine Version in der global.json-Datei „{0}“ angegeben ist. Um die global installierte Workload-Version zu aktualisieren, führen Sie den Befehl außerhalb des Pfads aus, der diese global.json-Datei enthält, oder aktualisieren Sie die in der global.json Datei angegebene Version, und führen Sie „dotnet workload update“ aus.
@@ -34,8 +39,8 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
- Die Option {0} kann nicht verwendet werden, wenn die Workloadversion in global.json angegeben ist. Entfernen Sie die Option {0}, oder entfernen Sie das WorkloadVersion-Element aus {1}.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ Die Option {0} kann nicht verwendet werden, wenn die workloadVersion in global.json angegeben ist. Entfernen Sie die Option {0}, oder entfernen Sie das workloadVersion-Element aus {1}.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- Aktualisieren Sie auf die angegebene Workloadversion.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ Eine Workloadversion zum Anzeigen oder mindestens eine Workload und deren Versionen, die mit dem Zeichen "@" verknüpft sind.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf
index 65251c1545d6..347522e64aa2 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.es.xlf
@@ -27,6 +27,11 @@
No se pueden usar las opciones {0} y {1} juntas. Quite una de las opciones.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ No se puede especificar una versión de carga de trabajo con la opción --version y especificar también las versiones que se van a instalar con la sintaxis workload@version.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
No se puede especificar una versión de carga de trabajo determinada en la línea de comandos mediante --version o --from-history cuando ya hay una versión especificada en el archivo global.json {0}. Para actualizar la versión de carga de trabajo instalada globalmente, ejecute el comando fuera de la ruta de acceso que contiene ese archivo global.json o actualice la versión especificada en el archivo global.json y ejecute "dotnet workload update".
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
No se puede usar la opción {0} cuando se especifica la versión de la carga de trabajo en global.json. Quite la opción {0} o quite el elemento "workloadVersion" de {1}.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- Actualice a la versión de carga de trabajo especificada.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ Una versión de carga de trabajo para mostrar o una o más cargas de trabajo y sus versiones unidas por el carácter "@".
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf
index be7157ed7cf8..fee46cc522fa 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.fr.xlf
@@ -27,6 +27,11 @@
Impossible d’utiliser les options {0} et {1} ensemble. Supprimez l’une des options.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ Impossible de spécifier une version de charge de travail à l’aide de l’option --version tout en spécifiant également les versions à installer à l’aide de la syntaxe workload@version.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
Impossible de spécifier une version de charge de travail particulière sur la ligne de commande via --version ou --from-history lorsqu’une version est déjà spécifiée dans global.json fichier {0}. Pour mettre à jour la version de la charge de travail installée globalement, exécutez la commande en dehors du chemin contenant ce fichier global.json ou mettez à jour la version spécifiée dans le fichier global.json et exécutez « dotnet workload update »
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
Impossible d’utiliser l’option {0} lorsque la version de la charge de travail est spécifiée dans global.json. Supprimez l’option {0} ou supprimez l’élément « workloadVersion » de {1}.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- Mettre à jour vers la version de charge de travail spécifiée.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ Version de charge de travail à afficher ou une ou plusieurs charges de travail et leurs versions jointes par le caractère « @ ».
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf
index 037c31d480a2..1a5d0a4f63eb 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.it.xlf
@@ -27,6 +27,11 @@
Impossibile utilizzare contemporaneamente le opzioni {0} e {1}. Rimuove una delle opzioni.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ Non è possibile specificare una versione del carico di lavoro usando l'opzione --version specificando anche le versioni da installare con workload@version sintassi.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
Impossibile specificare una determinata versione del carico di lavoro nella riga di comando tramite --version o --from-history quando è già specificata una versione nel file global.json {0}. Per aggiornare la versione del carico di lavoro installata a livello globale, eseguire il comando all'esterno del percorso contenente tale file global.json o aggiornare la versione specificata nel file di global.json ed eseguire "dotnet workload update".
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
Non è possibile usare l'opzione {0} quando la versione del carico di lavoro è specificata in global.json. Rimuovere l'opzione {0} o rimuovere l'elemento 'workloadVersion' da {1}.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- Eseguire l'aggiornamento alla versione del carico di lavoro specificata.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ Versione del carico di lavoro da visualizzare o da uno o più carichi di lavoro e relative versioni aggiunte dal carattere '@'.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf
index a0c30d7676b7..9b77158b059f 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ja.xlf
@@ -27,6 +27,11 @@
{0} オプションと {1} オプションを同時に使用することはできません。いずれかのオプションを削除してください。
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ --version オプションを使用してワークロードのバージョンを指定する一方で、workload@version構文を使用してインストールするバージョンを指定することはできません。
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
global.json ファイル {0} に既にバージョンが指定されている場合は、コマンド ラインで --version または --from-history を使用して特定のワークロード バージョンを指定することはできません。グローバルにインストールされたワークロード バージョンを更新するには、その global.json ファイルを含むパスの外部でコマンドを実行するか、global.json ファイルで指定されたバージョンを更新して、"dotnet workload update" を実行します。
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
ワークロードのバージョンが global.json で指定されている場合、{0} オプションは使用できません。{0} オプションを削除するか、{1} から 'workloadVersion' 要素を削除してください。
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- 指定されたワークロード バージョンに更新します。
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ 表示するワークロードのバージョン、または '@' 文字で結合された 1 つ以上のワークロードとそのバージョン。
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf
index 35c637fc89c6..afc4c6730e4c 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ko.xlf
@@ -27,6 +27,11 @@
{0} 및 {1} 옵션을 함께 사용할 수 없습니다. 옵션 중 하나를 제거하세요.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ workload@version 구문을 사용하여 설치할 버전도 지정하면서 --version 옵션을 사용하여 작업 버전을 지정할 수 없습니다.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
global.json 파일 {0}에 지정된 버전이 이미 있는 경우 --version 또는 --from-history를 통해 명령줄에서 특정 워크로드 버전을 지정할 수 없습니다. 전역으로 설치된 워크로드 버전을 업데이트하려면 해당 global.json 파일이 포함된 경로 외부에서 명령을 실행하거나 global.json 파일에 지정된 버전을 업데이트하고 "dotnet 워크로드 업데이트"를 실행합니다.
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
global.json에 워크로드 버전이 지정된 경우 {0} 옵션을 사용할 수 없습니다. {0} 옵션을 제거하거나, {1}에서 'workloadVersion' 요소를 제거하세요.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- 지정된 워크로드 버전으로 업데이트합니다.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ 표시할 워크로드 버전 또는 하나 이상의 워크로드 및 해당 버전이 '@' 문자로 결합되었습니다.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf
index 6c5f1cae1360..a4400738fd41 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pl.xlf
@@ -27,6 +27,11 @@
Nie można używać opcji {0} i {1} razem. Usuń jedną z opcji.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ Nie można określić wersji obciążenia przy użyciu opcji --version, określając jednocześnie wersje do zainstalowania przy użyciu składni workload@version.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
Nie można określić określonej wersji obciążenia w wierszu polecenia za pomocą opcji --version lub --from-history, jeśli istnieje już wersja określona w pliku global.json {0}. Aby zaktualizować wersję obciążenia zainstalowaną globalnie, uruchom polecenie poza ścieżką zawierającą ten plik global.json lub zaktualizuj wersję określoną w pliku global.json i uruchom polecenie „dotnet workload update”.
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
Nie można użyć opcji {0}, jeśli wersja obciążenia jest określona w pliku global.json. Usuń opcję {0} lub usuń element „workloadVersion” z {1}.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- Zaktualizuj do określonej wersji obciążenia.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ Wersja obciążenia do wyświetlenia lub co najmniej jednego obciążenia i ich wersji dołączonych za pomocą znaku "@".
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf
index 2f0f51b1011c..1f1f7157d4b2 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.pt-BR.xlf
@@ -27,6 +27,11 @@
Não é possível usar as opções {0} e {1} juntas. Escolha uma das opções.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ Não é possível especificar uma versão de carga de trabalho usando --version opção ao especificar também as versões a serem instaladas usando workload@version sintaxe.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
Não é possível especificar uma versão específica da carga de trabalho na linha de comando por meio de --version ou --from-history quando já existe uma versão especificada no arquivo global.json {0}. Para atualizar a versão da carga de trabalho instalada globalmente, execute o comando fora do caminho que contém o arquivo global.json ou atualize a versão especificada no arquivo global.json e execute "dotnet workload update".
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
Não é possível usar a opção {0} quando a versão da carga de trabalho é especificada em global.json. Remova a opção {0} ou remova o elemento 'workloadVersion' de {1}.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- Atualize para a versão de carga de trabalho especificada.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ Uma versão de carga de trabalho a ser exibida ou uma ou mais cargas de trabalho e suas versões ingressadas pelo caractere '@'.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf
index 86708c461815..b7470731454e 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.ru.xlf
@@ -27,6 +27,11 @@
Невозможно использовать параметры {0} и {1} одновременно. Удалите один из параметров.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ Невозможно указать версию рабочей нагрузки с помощью параметра --version при указании версий для установки с workload@version синтаксиса.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
Невозможно указать конкретную версию рабочей нагрузки в командной строке с помощью --version или --from-history, если в файле global.json уже указана версия {0}. Чтобы обновить глобально установленную версию рабочей нагрузки, выполните команду вне пути, содержащего этот файл global.json, либо измените версию в файле global.json и выполните команду "dotnet workload update".
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
Невозможно использовать параметр {0}, если версия рабочей нагрузки указана в файле global.json. Удалите параметр {0} или удалите элемент "workloadVersion" из {1}.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- Обновление до указанной версии рабочей нагрузки.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ Версия рабочей нагрузки для отображения или одна или несколько рабочих нагрузок и их версий, присоединенных символом "@".
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf
index bc36b4debfa5..44be8dc036e7 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.tr.xlf
@@ -27,6 +27,11 @@
{0} ve {1} seçenekleri birlikte kullanılamaz. Seçeneklerden birini kaldırın.
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ İş yükü sürümü, --version kullanılarak yüklenecek sürümler de belirtilirken workload@version belirtilemez.
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
{0} global.json dosyasında zaten belirtilen bir sürüm varsa, “--version” veya “--from-history” aracılığıyla komut satırında belirli bir iş yükü sürümü belirtilemez. Genel olarak yüklenen iş yükü sürümünü güncelleştirmek için, komutu global.json dosyasını içeren yolun dışında çalıştırın veya global.json dosyasında belirtilen sürümü güncelleştirin ve “dotnet workload update” komutunu çalıştırın.
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
Global.json'da iş yükü sürümü belirtildiğinde {0} seçeneği kullanılamaz. {0} seçeneğini kaldırın veya "workloadVersion" öğesini {1}‘den kaldırın.
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- Belirtilen iş yükü sürümünde güncelleştirme var.
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ Görüntülenecek iş yükü sürümü veya bir veya daha fazla iş yükü ve '@' karakteriyle birleştirilmiş sürümleri.
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf
index deb2a9a86c41..37bd4d6583df 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hans.xlf
@@ -27,6 +27,11 @@
不能同时使用 {0} 和 {1} 选项。移除其中一个选项。
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ 在指定要使用workload@version语法安装的版本时,不能使用 --version 选项指定工作负载版本。
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
当 global.json 文件 {0} 中已指定版本时,无法在命令行上通过 --version 或 --from-history 指定特定工作负载版本。若要更新全局安装的工作负载版本,请在包含该 global.json 文件的路径外部运行命令,或者更新 global.json 文件中指定的版本,然后运行 "dotnet workload update"。
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
在 global.json 中指定工作负载版本时,无法使用 {0} 选项。移除 {0} 选项,或从 {1} 中移除“workloadVersion”元素。
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- 更新到指定的工作负载版本。
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ 要显示的工作负载版本或一个或多个工作负载及其版本由 “@” 字符联接。
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf
index a0af290f566c..f589e4de7e1c 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/xlf/LocalizableStrings.zh-Hant.xlf
@@ -27,6 +27,11 @@
無法同時使用 {0} 與 {1} 選項。移除其中一個選項。
+
+ Cannot specify a workload version using the --version option while also specifying versions to install using workload@version syntax.
+ 同時指定要使用workload@version語法安裝的版本時,無法使用 --version 選項指定工作負載版本。
+ {Locked="--version"}
+
Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."
當 global.json 檔案 {0} 中已指定版本時,無法在命令列上透過 --version 或 --from-history 指定特定工作負載版本。若要更新全域安裝的工作負載版本,請在包含該 global.json 檔案的路徑外執行命令,或更新 global.json 檔案中指定的版本,然後執行 "dotnet workload update"。
@@ -35,7 +40,7 @@
Cannot use the {0} option when workload version is specified in global.json. Remove the {0} option, or remove the 'workloadVersion' element from {1}.
global.json 中指定工作負載版本時,無法使用 {0} 選項。移除 {0} 選項,或從 {1} 移除 'workloadVersion' 元素。
- "workloadVersion" and "global.json" are literal values and should not be translated.
+ {Locked="workloadVersion"} Locked={"global.json"}
Checking for updated workload version.
@@ -393,8 +398,8 @@
- Update to the specified workload version.
- 更新至指定的工作負載版本。
+ A workload version to display or one or more workloads and their versions joined by the '@' character.
+ 要顯示的工作負載版本,或是一或多個工作負載及其由 '@' 字元加入的版本。
diff --git a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs
index 41fa1049bf3b..f25690d52963 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/restore/WorkloadRestoreCommand.cs
@@ -8,6 +8,7 @@
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Tools.Common;
using Microsoft.DotNet.Workloads.Workload.Install;
using Microsoft.DotNet.Workloads.Workload.Update;
using Microsoft.Extensions.EnvironmentAbstractions;
@@ -122,13 +123,13 @@ internal static List DiscoverAllProjects(string currentDirectory,
var projectFiles = new List();
if (slnOrProjectArgument == null || !slnOrProjectArgument.Any())
{
- slnFiles = Directory.GetFiles(currentDirectory, "*.sln").ToList();
+ slnFiles = SlnFileFactory.ListSolutionFilesInDirectory(currentDirectory, false).ToList();
projectFiles.AddRange(Directory.GetFiles(currentDirectory, "*.*proj"));
}
else
{
slnFiles = slnOrProjectArgument
- .Where(s => Path.GetExtension(s).Equals(".sln", StringComparison.OrdinalIgnoreCase))
+ .Where(s => Path.GetExtension(s).Equals(".sln", StringComparison.OrdinalIgnoreCase) || Path.GetExtension(s).Equals(".slnx", StringComparison.OrdinalIgnoreCase))
.Select(Path.GetFullPath).ToList();
projectFiles = slnOrProjectArgument
.Where(s => Path.GetExtension(s).EndsWith("proj", StringComparison.OrdinalIgnoreCase))
@@ -137,12 +138,8 @@ internal static List DiscoverAllProjects(string currentDirectory,
foreach (string file in slnFiles)
{
- var solutionFile = SolutionFile.Parse(file);
- var projects = solutionFile.ProjectsInOrder.Where(p => p.ProjectType != SolutionProjectType.SolutionFolder);
- foreach (var p in projects)
- {
- projectFiles.Add(p.AbsolutePath);
- }
+ var solutionFile = SlnFileFactory.CreateFromFileOrDirectory(file);
+ projectFiles.AddRange(solutionFile.SolutionProjects.Select(p => p.FilePath));
}
if (projectFiles.Count == 0)
diff --git a/src/Cli/dotnet/commands/dotnet-workload/search/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-workload/search/LocalizableStrings.resx
index cce5241a328e..656a228086ae 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/search/LocalizableStrings.resx
+++ b/src/Cli/dotnet/commands/dotnet-workload/search/LocalizableStrings.resx
@@ -136,8 +136,37 @@
Platforms
- Output a list of the latest released workload versions. Takes the --take option to specify how many to provide and --format to alter the format.
- Do not localize --take or --format
+ 'dotnet workload search version' has three functions depending on its argument:
+ 1. If no argument is specified, it outputs a list of the latest released workload versions from this feature band. Takes the --take option to specify how many to provide and --format to alter the format.
+ Example:
+ dotnet workload search version --take 2 --format json
+ [{"workloadVersion":"9.0.201"},{"workloadVersion":"9.0.200.1"}]
+ 2. If a workload version is provided as an argument, it outputs a table of various workloads and their versions for the specified workload version. Takes the --format option to alter the output format.
+ Example:
+ dotnet workload search version 9.0.201
+ Workload manifest ID Manifest feature band Manifest Version
+ ------------------------------------------------------------------------------------------------
+ microsoft.net.workload.emscripten.current 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.workload.emscripten.net6 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.workload.emscripten.net7 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.workload.emscripten.net8 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.sdk.android 9.0.100-rc.1 35.0.0-rc.1.80
+ microsoft.net.sdk.ios 9.0.100-rc.1 17.5.9270-net9-rc1
+ microsoft.net.sdk.maccatalyst 9.0.100-rc.1 17.5.9270-net9-rc1
+ microsoft.net.sdk.macos 9.0.100-rc.1 14.5.9270-net9-rc1
+ microsoft.net.sdk.maui 9.0.100-rc.1 9.0.0-rc.1.24453.9
+ microsoft.net.sdk.tvos 9.0.100-rc.1 17.5.9270-net9-rc1
+ microsoft.net.workload.mono.toolchain.current 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.workload.mono.toolchain.net6 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.workload.mono.toolchain.net7 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.workload.mono.toolchain.net8 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.sdk.aspire 8.0.100 8.2.0
+ 3. If one or more workloads are provided along with their versions (by joining them with the '@' character), it outputs workload versions that match the provided versions. Takes the --take option to specify how many to provide and --format to alter the format.
+ Example:
+ dotnet workload search version maui@9.0.0-rc.1.24453.9 ios@17.5.9270-net9-rc1
+ 9.0.201
+
+ {Locked="--take"} {Locked="--format"} {Locked="dotnet workload search version"} {Locked="workloadVersion"}
Changes the format of outputted workload versions. Can take 'json' or 'list'
@@ -163,5 +192,8 @@
Manifest feature band
+
+ No workload version matching {0} was found.
+
diff --git a/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchVersionsCommand.cs b/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchVersionsCommand.cs
index 64e14d4fc4d1..82c308f7cae2 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchVersionsCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchVersionsCommand.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;
using System.CommandLine;
using System.Text.Json;
using Microsoft.Deployment.DotNet.Releases;
@@ -9,11 +8,9 @@
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
-using Microsoft.DotNet.ToolPackage;
using Microsoft.DotNet.Workloads.Workload.Install;
using Microsoft.NET.Sdk.WorkloadManifestReader;
using Microsoft.TemplateEngine.Cli.Commands;
-using NuGet.Versioning;
using InformationStrings = Microsoft.DotNet.Workloads.Workload.LocalizableStrings;
@@ -24,16 +21,21 @@ internal class WorkloadSearchVersionsCommand : WorkloadCommandBase
private readonly ReleaseVersion _sdkVersion;
private readonly int _numberOfWorkloadSetsToTake;
private readonly string _workloadSetOutputFormat;
- private readonly FileBasedInstaller _installer;
- private readonly string _workloadVersion;
+ private readonly IInstaller _installer;
+ private readonly IEnumerable _workloadVersion;
private readonly bool _includePreviews;
+ private readonly IWorkloadResolver _resolver;
public WorkloadSearchVersionsCommand(
ParseResult result,
IReporter reporter = null,
- IWorkloadResolverFactory workloadResolverFactory = null) : base(result, CommonOptions.HiddenVerbosityOption, reporter)
+ IWorkloadResolverFactory workloadResolverFactory = null,
+ IInstaller installer = null,
+ INuGetPackageDownloader nugetPackageDownloader = null,
+ IWorkloadResolver resolver = null,
+ ReleaseVersion sdkVersion = null) : base(result, CommonOptions.HiddenVerbosityOption, reporter, nugetPackageDownloader: nugetPackageDownloader)
{
- workloadResolverFactory = workloadResolverFactory ?? new WorkloadResolverFactory();
+ workloadResolverFactory ??= new WorkloadResolverFactory();
if (!string.IsNullOrEmpty(result.GetValue(WorkloadSearchCommandParser.VersionOption)))
{
@@ -42,56 +44,81 @@ public WorkloadSearchVersionsCommand(
var creationResult = workloadResolverFactory.Create();
- _sdkVersion = creationResult.SdkVersion;
- var workloadResolver = creationResult.WorkloadResolver;
+ _sdkVersion = sdkVersion ?? creationResult.SdkVersion;
+ _resolver = resolver ?? creationResult.WorkloadResolver;
_numberOfWorkloadSetsToTake = result.GetValue(WorkloadSearchVersionsCommandParser.TakeOption);
_workloadSetOutputFormat = result.GetValue(WorkloadSearchVersionsCommandParser.FormatOption);
// For these operations, we don't have to respect 'msi' because they're equivalent between the two workload
// install types, and FileBased is much easier to work with.
- _installer = new FileBasedInstaller(
+ _installer = installer ?? GenerateInstaller(Reporter, new SdkFeatureBand(_sdkVersion), _resolver, Verbosity, result.HasOption(SharedOptions.InteractiveOption));
+
+ _workloadVersion = result.GetValue(WorkloadSearchVersionsCommandParser.WorkloadVersionArgument);
+
+ _includePreviews = result.HasOption(WorkloadSearchVersionsCommandParser.IncludePreviewsOption) ?
+ result.GetValue(WorkloadSearchVersionsCommandParser.IncludePreviewsOption) :
+ new SdkFeatureBand(_sdkVersion).IsPrerelease;
+
+ }
+
+ private static IInstaller GenerateInstaller(IReporter reporter, SdkFeatureBand sdkFeatureBand, IWorkloadResolver workloadResolver, VerbosityOptions verbosity, bool interactive)
+ {
+ return new FileBasedInstaller(
reporter,
- new SdkFeatureBand(_sdkVersion),
+ sdkFeatureBand,
workloadResolver,
CliFolderPathCalculator.DotnetUserProfileFolderPath,
nugetPackageDownloader: null,
dotnetDir: Path.GetDirectoryName(Environment.ProcessPath),
tempDirPath: null,
- verbosity: Verbosity,
+ verbosity: verbosity,
packageSourceLocation: null,
- restoreActionConfig: new RestoreActionConfig(result.HasOption(SharedOptions.InteractiveOption)),
+ restoreActionConfig: new RestoreActionConfig(interactive),
nugetPackageDownloaderVerbosity: VerbosityOptions.quiet
);
-
- _workloadVersion = result.GetValue(WorkloadSearchVersionsCommandParser.WorkloadVersionArgument);
-
- _includePreviews = result.HasOption(WorkloadSearchVersionsCommandParser.IncludePreviewsOption) ?
- result.GetValue(WorkloadSearchVersionsCommandParser.IncludePreviewsOption) :
- new SdkFeatureBand(_sdkVersion).IsPrerelease;
}
public override int Execute()
{
- if (_workloadVersion is null)
+ if (_workloadVersion.Count() == 0)
{
- var featureBand = new SdkFeatureBand(_sdkVersion);
- var packageId = _installer.GetManifestPackageId(new ManifestId("Microsoft.NET.Workloads"), featureBand);
-
List versions;
try
{
- versions = PackageDownloader.GetLatestPackageVersions(packageId, _numberOfWorkloadSetsToTake, packageSourceLocation: null, includePreview: _includePreviews)
- .GetAwaiter().GetResult()
- .Select(version => WorkloadSetVersion.FromWorkloadSetPackageVersion(featureBand, version.ToString()))
- .ToList();
+ versions = GetVersions(_numberOfWorkloadSetsToTake);
}
catch (NuGetPackageNotFoundException)
{
- Microsoft.DotNet.Cli.Utils.Reporter.Error.WriteLine(string.Format(LocalizableStrings.NoWorkloadVersionsFound, featureBand));
+ Cli.Utils.Reporter.Error.WriteLine(string.Format(LocalizableStrings.NoWorkloadVersionsFound, new SdkFeatureBand(_sdkVersion)));
return 0;
}
+
if (_workloadSetOutputFormat?.Equals("json", StringComparison.OrdinalIgnoreCase) == true)
+ {
+ Reporter.WriteLine(JsonSerializer.Serialize(versions.Select(version => new Dictionary()
+ {
+ { "workloadVersion", version }
+ })));
+ }
+ else
+ {
+ Reporter.WriteLine(string.Join('\n', versions));
+ }
+ }
+ else if (_workloadVersion.Any(v => v.Contains('@')))
+ {
+ var versions = FindBestWorkloadSetsFromComponents()?.Take(_numberOfWorkloadSetsToTake);
+ if (versions is null)
+ {
+ return 0;
+ }
+
+ if (!versions.Any())
+ {
+ Reporter.WriteLine(string.Format(LocalizableStrings.WorkloadVersionWithSpecifiedManifestNotFound, string.Join(' ', _workloadVersion)));
+ }
+ else if (_workloadSetOutputFormat?.Equals("json", StringComparison.OrdinalIgnoreCase) == true)
{
Reporter.WriteLine(JsonSerializer.Serialize(versions.Select(version => version.ToDictionary(_ => "workloadVersion", v => v))));
}
@@ -102,7 +129,7 @@ public override int Execute()
}
else
{
- var workloadSet = _installer.GetWorkloadSetContents(_workloadVersion);
+ var workloadSet = _installer.GetWorkloadSetContents(_workloadVersion.Single());
if (_workloadSetOutputFormat?.Equals("json", StringComparison.OrdinalIgnoreCase) == true)
{
var set = new WorkloadSet() { ManifestVersions = workloadSet.ManifestVersions };
@@ -123,5 +150,55 @@ public override int Execute()
return 0;
}
+
+ private List GetVersions(int numberOfWorkloadSetsToTake)
+ {
+ return GetVersions(numberOfWorkloadSetsToTake, new SdkFeatureBand(_sdkVersion), _installer, _includePreviews, PackageDownloader, _resolver);
+ }
+
+ private static List GetVersions(int numberOfWorkloadSetsToTake, SdkFeatureBand featureBand, IInstaller installer, bool includePreviews, INuGetPackageDownloader packageDownloader, IWorkloadResolver resolver)
+ {
+ installer ??= GenerateInstaller(Cli.Utils.Reporter.NullReporter, featureBand, resolver, VerbosityOptions.d, interactive: false);
+ var packageId = installer.GetManifestPackageId(new ManifestId("Microsoft.NET.Workloads"), featureBand);
+
+ return packageDownloader.GetLatestPackageVersions(packageId, numberOfWorkloadSetsToTake, packageSourceLocation: null, includePreview: includePreviews)
+ .GetAwaiter().GetResult()
+ .Select(version => WorkloadSetVersion.FromWorkloadSetPackageVersion(featureBand, version.ToString()))
+ .ToList();
+ }
+
+ private IEnumerable FindBestWorkloadSetsFromComponents()
+ {
+ return FindBestWorkloadSetsFromComponents(new SdkFeatureBand(_sdkVersion), _installer, _includePreviews, PackageDownloader, _workloadVersion, _resolver, _numberOfWorkloadSetsToTake);
+ }
+
+ public static IEnumerable FindBestWorkloadSetsFromComponents(SdkFeatureBand featureBand, IInstaller installer, bool includePreviews, INuGetPackageDownloader packageDownloader, IEnumerable workloadVersions, IWorkloadResolver resolver, int numberOfWorkloadSetsToTake)
+ {
+ installer ??= GenerateInstaller(Cli.Utils.Reporter.NullReporter, featureBand, resolver, VerbosityOptions.d, interactive: false);
+ List versions;
+ try
+ {
+ // 0 indicates 'give all versions'. Not all will match, so we don't know how many we will need
+ versions = GetVersions(0, featureBand, installer, includePreviews, packageDownloader, resolver);
+ }
+ catch (NuGetPackageNotFoundException)
+ {
+ Cli.Utils.Reporter.Error.WriteLine(string.Format(LocalizableStrings.NoWorkloadVersionsFound, featureBand));
+ return null;
+ }
+
+ var manifestIdsAndVersions = workloadVersions.Select(version =>
+ {
+ var split = version.Split('@');
+ return (new ManifestId(resolver.GetManifestFromWorkload(new WorkloadId(split[0])).Id), new ManifestVersion(split[1]));
+ });
+
+ // Since these are ordered by version (descending), the first is the highest version
+ return versions.Where(version =>
+ {
+ var manifestVersions = installer.GetWorkloadSetContents(version).ManifestVersions;
+ return manifestIdsAndVersions.All(tuple => manifestVersions.ContainsKey(tuple.Item1) && manifestVersions[tuple.Item1].Version.Equals(tuple.Item2));
+ }).Take(numberOfWorkloadSetsToTake);
+ }
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchVersionsCommandParser.cs b/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchVersionsCommandParser.cs
index b733f0e01a93..90afc267dd85 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchVersionsCommandParser.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/search/WorkloadSearchVersionsCommandParser.cs
@@ -10,10 +10,10 @@ namespace Microsoft.DotNet.Cli
{
internal static class WorkloadSearchVersionsCommandParser
{
- public static readonly CliArgument WorkloadVersionArgument =
+ public static readonly CliArgument> WorkloadVersionArgument =
new(LocalizableStrings.WorkloadVersionArgument)
{
- Arity = ArgumentArity.ZeroOrOne,
+ Arity = ArgumentArity.ZeroOrMore,
Description = LocalizableStrings.WorkloadVersionArgumentDescription
};
@@ -60,9 +60,9 @@ private static CliCommand ConstructCommand()
command.Validators.Add(result =>
{
var versionArgument = result.GetValue(WorkloadVersionArgument);
- if (versionArgument is not null && !WorkloadSetVersion.IsWorkloadSetPackageVersion(versionArgument))
+ if (versionArgument is not null && !versionArgument.All(v => v.Contains('@')) && !WorkloadSetVersion.IsWorkloadSetPackageVersion(versionArgument.SingleOrDefault(defaultValue: string.Empty)))
{
- result.AddError(string.Format(CommandLineValidation.LocalizableStrings.UnrecognizedCommandOrArgument, versionArgument));
+ result.AddError(string.Format(CommandLineValidation.LocalizableStrings.UnrecognizedCommandOrArgument, string.Join(' ', versionArgument)));
}
});
diff --git a/src/Cli/dotnet/commands/dotnet-workload/search/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-workload/search/xlf/LocalizableStrings.cs.xlf
index d4d0e1231331..af9877180d73 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/search/xlf/LocalizableStrings.cs.xlf
+++ b/src/Cli/dotnet/commands/dotnet-workload/search/xlf/LocalizableStrings.cs.xlf
@@ -33,9 +33,67 @@
- Output a list of the latest released workload versions. Takes the --take option to specify how many to provide and --format to alter the format.
- Vypsat seznam nejnovějších vydaných verzí úloh. Pomocí možnosti --take určíte, kolik jich má být poskytnuto, a pomocí --format změníte formát.
- Do not localize --take or --format
+ 'dotnet workload search version' has three functions depending on its argument:
+ 1. If no argument is specified, it outputs a list of the latest released workload versions from this feature band. Takes the --take option to specify how many to provide and --format to alter the format.
+ Example:
+ dotnet workload search version --take 2 --format json
+ [{"workloadVersion":"9.0.201"},{"workloadVersion":"9.0.200.1"}]
+ 2. If a workload version is provided as an argument, it outputs a table of various workloads and their versions for the specified workload version. Takes the --format option to alter the output format.
+ Example:
+ dotnet workload search version 9.0.201
+ Workload manifest ID Manifest feature band Manifest Version
+ ------------------------------------------------------------------------------------------------
+ microsoft.net.workload.emscripten.current 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.workload.emscripten.net6 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.workload.emscripten.net7 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.workload.emscripten.net8 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.sdk.android 9.0.100-rc.1 35.0.0-rc.1.80
+ microsoft.net.sdk.ios 9.0.100-rc.1 17.5.9270-net9-rc1
+ microsoft.net.sdk.maccatalyst 9.0.100-rc.1 17.5.9270-net9-rc1
+ microsoft.net.sdk.macos 9.0.100-rc.1 14.5.9270-net9-rc1
+ microsoft.net.sdk.maui 9.0.100-rc.1 9.0.0-rc.1.24453.9
+ microsoft.net.sdk.tvos 9.0.100-rc.1 17.5.9270-net9-rc1
+ microsoft.net.workload.mono.toolchain.current 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.workload.mono.toolchain.net6 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.workload.mono.toolchain.net7 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.workload.mono.toolchain.net8 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.sdk.aspire 8.0.100 8.2.0
+ 3. If one or more workloads are provided along with their versions (by joining them with the '@' character), it outputs workload versions that match the provided versions. Takes the --take option to specify how many to provide and --format to alter the format.
+ Example:
+ dotnet workload search version maui@9.0.0-rc.1.24453.9 ios@17.5.9270-net9-rc1
+ 9.0.201
+
+ 'dotnet workload search version' má v závislosti na argumentu tři funkce:
+ 1. Pokud se nezadá žádný argument, vypíše se z tohoto pásma funkcí seznam nejnovějších verzí uvolněných úloh. Přijímá možnost --take určující, kolik má být zadáno, a --format změnit formát.
+ Příklad:
+ dotnet workload search version --take 2 --format JSON
+ [{"workloadVersion":"9.0.201"},{"workloadVersion":"9.0.200.1"}]
+ 2. Pokud se jako argument zadá verze úlohy, zobrazí se pro zadanou verzi úlohy tabulka různých úloh a jejich verze. Převezme možnost --format pro změnu výstupního formátu.
+ Příklad:
+ dotnet workload search version 9.0.201
+ Verze manifestu skupiny funkcí manifestu ID manifestu úlohy
+ ------------------------------------------------------------------------------------------------
+ microsoft.net.workload.emscripten.current 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.workload.emscripten.net6 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.workload.emscripten.net7 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.workload.emscripten.net8 9.0.100-rc.1 9.0.0-rc.1.24430.3
+ microsoft.net.sdk.android 9.0.100-rc.1 35.0.0-rc.1.80
+ microsoft.net.sdk.ios 9.0.100-rc.1 17.5.9270-net9-rc1
+ microsoft.net.sdk.mac 9.0.100-rc.1 17.5.9270-net9-rc1
+ microsoft.net.sdk.macos 9.0.100-rc.1 14.5.9270-net9-rc1
+ microsoft.net.sdk.maui 9.0.100-rc.1 9.0.0-rc.1.24453.9
+ microsoft.net.sdk.tvos 9.0.100-rc.1 17.5.9270-net9-rc1
+ microsoft.net.workload.mono.toolchain.current 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.workload.mono.toolchain.net6 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.workload.mono.toolchain.net7 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.workload.mono.toolchain.net8 9.0.100-rc.1 9.0.0-rc.1.24431.7
+ microsoft.net.sdk.aspire 8.0.100 8.2.0
+ 3. Pokud je k dispozici nejméně jedna úloha spolu s jejich verzemi (spojením pomocí znaku @), vyjdou výstupy verzí úloh, které odpovídají zadaným verzím. Přebírá možnost --take určující, kolik má být zadáno, a --format změnit formát.
+ Příklad:
+ dotnet workload search version maui@9.0.0-rc.1.24453.9 ios@17.5.9270-net9-rc1
+ 9.0.201
+
+ {Locked="--take"} {Locked="--format"} {Locked="dotnet workload search version"} {Locked="workloadVersion"}
The --take option must be positive.
@@ -77,6 +135,11 @@
Verze manifestu výstupní úlohy přidružené k zadané verzi úlohy.
+
+ No workload version matching {0} was found.
+ Nenašla se žádná verze úlohy odpovídající {0}.
+
+