From 8498a03388fe099f11a622b443a3e436cb93bbae Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 30 Oct 2024 14:02:51 +0000 Subject: [PATCH 001/106] Update dependencies from https://github.com/nuget/nuget.client build 6.13.0.46 NuGet.Build.Tasks From Version 6.12.0-rc.127 -> To Version 6.13.0-preview.1.46 --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 06f7d43782d..17c178c5877 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -85,9 +85,9 @@ https://github.com/dotnet/arcade 3c393bbd85ae16ddddba20d0b75035b0c6f1a52d - + https://github.com/nuget/nuget.client - 19756345139c45de23bd196e9b4be01d48e84fdd + dddfdd8617e80ec5a2ea0dd94a094b243f355d09 https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index ea964044ec8..d5acf64591d 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -52,7 +52,7 @@ 4.2.0-1.22102.8 9.0.0-beta.24516.2 4.13.0-2.24528.6 - 6.12.0-rc.127 + 6.13.0-preview.1.46 9.0.200-preview.0.24523.19 From dc06f8dca8e522b77e291ef1102e4487fa9ad732 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Thu, 19 Dec 2024 18:14:34 +0100 Subject: [PATCH 002/106] write up --- documentation/specs/proposed/VS-OpenTelemetry | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 documentation/specs/proposed/VS-OpenTelemetry diff --git a/documentation/specs/proposed/VS-OpenTelemetry b/documentation/specs/proposed/VS-OpenTelemetry new file mode 100644 index 00000000000..af6626ea9e5 --- /dev/null +++ b/documentation/specs/proposed/VS-OpenTelemetry @@ -0,0 +1,123 @@ +# Telemetry via OpenTelemetry design + +VS OTel provide packages compatible with ingesting data to their backend if we instrument it via OpenTelemetry traces (System.Diagnostics.Activity). +VS OTel packages are not open source so we need to conditionally include them in our build only for VS and MSBuild.exe + +Onepager: [telemetry onepager by JanProvaznik · Pull Request #11013 · dotnet/msbuild](https://github.com/dotnet/msbuild/pull/11013) + +## Requirements + + +### Performance + +- If not sampled, no infra initialization overhead. +- Avoid allocations when not sampled. +- Has to have no impact on Core without opting into tracing, small impact on Framework + +### Privacy + +- Hashing data points that could identify customers (e.g. names of targets) +- Opt out capability + +### Security + +- Providing a method for creating a hook in Framework MSBuild +- document the security implications of hooking custom telemetry Exporters/Collectors in Framework +- other security requirements (transportation, rate limiting, sanitization, data access) are implemented by VS Telemetry library or the backend + +### Data handling + +- Implement head [Sampling](https://opentelemetry.io/docs/concepts/sampling/) with the granularity of a MSBuild.exe invocation/VS instance. +- VS Data handle tail sampling in their infrastructure not to overwhelm storage with a lot of build events. + +#### Data points + +The data sent via VS OpenTelemetry is neither a subset neither a superset of what is sent to SDK telemetry and it is not a purpose of this design to unify them. + +##### Basic info + +- Build duration +- Host +- Build success/failure +- Version +- Target (hashed) + +##### Evnironment + +- SAC (Smart app control) enabled + +##### Features + +- BuildCheck enabled? + +The design allows for easy instrumentation of additional data points. + +## Core `dotnet build` scenario + +- Telemetry should not be collected via VS OpenTelemetry mechanism because it's already collected in sdk. +- There should be an opt in to initialize the ActivitySource to avoid degrading performance. +- [baronfel/otel-startup-hook: A .NET CLR Startup Hook that exports OpenTelemetry metrics via the OTLP Exporter to an OpenTelemetry Collector](https://github.com/baronfel/otel-startup-hook/) and similar enable collecting telemetry data locally by listening to the ActivitySource name defined in MSBuild. + +## Standalone MSBuild.exe scenario + +- Initialize and finalize in Xmake.cs + - ActivitySource, TracerProvider, VS Collector + - overhead of starting VS collector is fairly big (0.3s on Devbox)[JanProvaznik/VSCollectorBenchmarks](https://github.com/JanProvaznik/VSCollectorBenchmarks) + - head sampling should avoid initializing if not sampled + +## VS scenario + +- VS can call `BuildManager` in a thread unsafe way the telemetry implementation has to be mindful of [BuildManager instances acquire its own BuildTelemetry instance by rokonec · Pull Request #8444 · dotnet/msbuild](https://github.com/dotnet/msbuild/pull/8444) + - ensure no race conditions in initialization + - only 1 TracerProvider with VS defined processing should exist +- Visual Studio should be responsible for having a running collector, we don't want this overhead in MSBuild and eventually many components can use it + +## Implementation and MSBuild developer experience + +### Initialization at entrypoints + +- There are 2 entrypoints: + - for VS in BuildManager.BeginBuild + - for standalone in Xmake.cs Main + +### Exiting +Force flush TracerProvider's exporter in BuildManager.EndBuild. +Dispose collector in Xmake.cs at the end of Main. + +### Configuration + +- Class that's responsible for configuring and initializing telemetry and handles optouts, holding tracer and collector. +- Wrapping source so that it has correct prefixes for VS backend to ingest. + +### Instrumenting + +Instrument areas in code running in the main process. + +```csharp +using (Activity? myActivity = OpenTelemetryManager.DefaultActivitySource?.StartActivity(TelemetryConstants.NameFromAConstantToAvoidAllocation)) +{ +// something happens here + +// add data to the trace +myActivity?.WithTag("SpecialEvent","fail") +} +``` + +Interface for classes holding telemetry data + +```csharp +IActivityTelemetryDataHolder data = new SomeData(); +... +myActivity?.WithTags(data); +``` + +## Looking ahead + +- Create a way of using a "HighPrioActivitySource" which would override sampling and initialize Collector in MSBuild.exe scenario/tracerprovider in VS. + - this would enable us to catch rare events + +## Uncertainties + +- Configuring tail sampling in VS telemetry server side infrastructure to not overflow them with data. +- How much head sampling. +- In standalone we could start the collector async without waiting which would potentially miss some earlier traces (unlikely to miss the important end of build trace though) but would degrade performance less than waiting for it's startup. The performance and utility tradeoff is not clear. From 4c0605b098e29eb2d97ccd9c9465d1c213abdbf8 Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Fri, 20 Dec 2024 16:56:22 +0100 Subject: [PATCH 003/106] rename, add some details --- .../{VS-OpenTelemetry => VS-OpenTelemetry.md} | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) rename documentation/specs/proposed/{VS-OpenTelemetry => VS-OpenTelemetry.md} (85%) diff --git a/documentation/specs/proposed/VS-OpenTelemetry b/documentation/specs/proposed/VS-OpenTelemetry.md similarity index 85% rename from documentation/specs/proposed/VS-OpenTelemetry rename to documentation/specs/proposed/VS-OpenTelemetry.md index af6626ea9e5..3ee56b8754e 100644 --- a/documentation/specs/proposed/VS-OpenTelemetry +++ b/documentation/specs/proposed/VS-OpenTelemetry.md @@ -3,11 +3,10 @@ VS OTel provide packages compatible with ingesting data to their backend if we instrument it via OpenTelemetry traces (System.Diagnostics.Activity). VS OTel packages are not open source so we need to conditionally include them in our build only for VS and MSBuild.exe -Onepager: [telemetry onepager by JanProvaznik · Pull Request #11013 · dotnet/msbuild](https://github.com/dotnet/msbuild/pull/11013) +[Onepager](https://github.com/dotnet/msbuild/blob/main/documentation/specs/proposed/telemetry-onepager.md) ## Requirements - ### Performance - If not sampled, no infra initialization overhead. @@ -74,6 +73,12 @@ The design allows for easy instrumentation of additional data points. ## Implementation and MSBuild developer experience +### Sampling + +- We need to sample before initalizing infrastructure to avoid overhead. +- Enables opt-in and opt-out for guaranteed sample or not sampled. +- nullable ActivitySource, using `?` when working with them, we can be initialized but not sampled -> it will not reinitialize but not collect telemetry. + ### Initialization at entrypoints - There are 2 entrypoints: @@ -81,6 +86,7 @@ The design allows for easy instrumentation of additional data points. - for standalone in Xmake.cs Main ### Exiting + Force flush TracerProvider's exporter in BuildManager.EndBuild. Dispose collector in Xmake.cs at the end of Main. @@ -91,7 +97,9 @@ Dispose collector in Xmake.cs at the end of Main. ### Instrumenting -Instrument areas in code running in the main process. +2 ways of instrumenting: + +#### Instrument areas in code running in the main process ```csharp using (Activity? myActivity = OpenTelemetryManager.DefaultActivitySource?.StartActivity(TelemetryConstants.NameFromAConstantToAvoidAllocation)) @@ -111,6 +119,10 @@ IActivityTelemetryDataHolder data = new SomeData(); myActivity?.WithTags(data); ``` +#### Add data to activity in EndBuild + +- this activity would always be created at the same point when sdk telemetry is sent in Core and we can add data to it + ## Looking ahead - Create a way of using a "HighPrioActivitySource" which would override sampling and initialize Collector in MSBuild.exe scenario/tracerprovider in VS. @@ -121,3 +133,6 @@ myActivity?.WithTags(data); - Configuring tail sampling in VS telemetry server side infrastructure to not overflow them with data. - How much head sampling. - In standalone we could start the collector async without waiting which would potentially miss some earlier traces (unlikely to miss the important end of build trace though) but would degrade performance less than waiting for it's startup. The performance and utility tradeoff is not clear. +- Can we make collector startup faster? +- We could let users configure sample rate via env variable. +- Do we want to send antivirus state? It seems slow. From 68ecf056fa933042635663259c1342aa37aae14b Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Fri, 3 Jan 2025 18:45:32 +0100 Subject: [PATCH 004/106] update with comments --- .../specs/proposed/VS-OpenTelemetry.md | 61 ++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/documentation/specs/proposed/VS-OpenTelemetry.md b/documentation/specs/proposed/VS-OpenTelemetry.md index 3ee56b8754e..656f27f7ac1 100644 --- a/documentation/specs/proposed/VS-OpenTelemetry.md +++ b/documentation/specs/proposed/VS-OpenTelemetry.md @@ -5,6 +5,18 @@ VS OTel packages are not open source so we need to conditionally include them in [Onepager](https://github.com/dotnet/msbuild/blob/main/documentation/specs/proposed/telemetry-onepager.md) +## Concepts + +It's a bit confusing how things are named in OpenTelemetry and .NET and VS Telemetry and what they do. + +| OTel concept | .NET/VS | Description | +| --- | --- | --- | +| Span/Trace | System.Diagnostics.Activity | Trace is a tree of Spans. Activities can be nested.| +| Tracer | System.Diagnostics.ActivitySource | Creates and listens to activites. | +| Processor/Exporter | VS OTel provided default config | filters and saves telemetry as files in a desired format | +| TracerProvider | OTel SDK TracerProvider | Singleton that is aware of processors, exporters and Tracers (in .NET a bit looser relationship because it does not create Tracers just hooks to them) | +| Collector | VS OTel Collector | Sends to VS backend, expensive to initialize and finalize | + ## Requirements ### Performance @@ -20,8 +32,8 @@ VS OTel packages are not open source so we need to conditionally include them in ### Security -- Providing a method for creating a hook in Framework MSBuild -- document the security implications of hooking custom telemetry Exporters/Collectors in Framework +- Providing or/and documenting a method for creating a hook in Framework MSBuild +- If custom hooking solution will be used - document the security implications of hooking custom telemetry Exporters/Collectors in Framework - other security requirements (transportation, rate limiting, sanitization, data access) are implemented by VS Telemetry library or the backend ### Data handling @@ -47,14 +59,14 @@ The data sent via VS OpenTelemetry is neither a subset neither a superset of wha ##### Features -- BuildCheck enabled? +- BuildCheck enabled The design allows for easy instrumentation of additional data points. ## Core `dotnet build` scenario - Telemetry should not be collected via VS OpenTelemetry mechanism because it's already collected in sdk. -- There should be an opt in to initialize the ActivitySource to avoid degrading performance. +- opt in to initialize the ActivitySource to avoid degrading performance. - [baronfel/otel-startup-hook: A .NET CLR Startup Hook that exports OpenTelemetry metrics via the OTLP Exporter to an OpenTelemetry Collector](https://github.com/baronfel/otel-startup-hook/) and similar enable collecting telemetry data locally by listening to the ActivitySource name defined in MSBuild. ## Standalone MSBuild.exe scenario @@ -73,9 +85,19 @@ The design allows for easy instrumentation of additional data points. ## Implementation and MSBuild developer experience +### ActivitySource names + +... + ### Sampling -- We need to sample before initalizing infrastructure to avoid overhead. +Our estimation from VS and SDK data is that there are 10M-100M build events per day. +For proportion estimation (of fairly common occurence in the builds), with not very strict confidnece (95%) and margin for error (5%) sampling 1:25000 would be enough. + +- this would apply for the DefaultActivitySource +- other ActivitySources could be sampled more frequently to get enough data +- Collecting has a cost, especially in standalone scenario where we have to start the collector. We might decide to undersample in standalone to avoid performance frequent impact. +- We want to avoid that cost when not sampled, therefore we prefer head sampling. - Enables opt-in and opt-out for guaranteed sample or not sampled. - nullable ActivitySource, using `?` when working with them, we can be initialized but not sampled -> it will not reinitialize but not collect telemetry. @@ -119,20 +141,31 @@ IActivityTelemetryDataHolder data = new SomeData(); myActivity?.WithTags(data); ``` -#### Add data to activity in EndBuild +#### Default Build activity in EndBuild + +- this activity would always be created at the same point when sdk telemetry is sent in Core +- we can add data to it that we want in general builds +- the desired count of data from this should control the sample rate of DefaultActivitySource + +#### Multiple Activity Sources -- this activity would always be created at the same point when sdk telemetry is sent in Core and we can add data to it +We can create ActivitySources with different sample rates. Ultimately this is limited by the need to initialize a collector. -## Looking ahead +We potentially want apart from the Default ActivitySource: + +1. Other activity sources with different sample rates (in order to get significant data for rarer events such as custom tasks). +2. a way to override sampling decision - ad hoc starting telemetry infrastructure to catch rare events - Create a way of using a "HighPrioActivitySource" which would override sampling and initialize Collector in MSBuild.exe scenario/tracerprovider in VS. - - this would enable us to catch rare events +- this would enable us to catch rare events + ## Uncertainties -- Configuring tail sampling in VS telemetry server side infrastructure to not overflow them with data. -- How much head sampling. +- Configuring tail sampling in VS telemetry server side infrastructure. +- Sampling rare events details. - In standalone we could start the collector async without waiting which would potentially miss some earlier traces (unlikely to miss the important end of build trace though) but would degrade performance less than waiting for it's startup. The performance and utility tradeoff is not clear. -- Can we make collector startup faster? -- We could let users configure sample rate via env variable. -- Do we want to send antivirus state? It seems slow. +- Can collector startup/shutdown be faster? +- We could let users configure sample rate via env variable, VS profile +- Do we want to send antivirus state? Figuring it out is expensive: https://github.com/dotnet/msbuild/compare/main...proto/get-av ~100ms +- ability to configure the overal and per-namespace sampling from server side (e.g. storing it in the .msbuild folder in user profile if different then default values set from server side - this would obviously have a delay of the default sample rate # of executions) From 8f426477899d4dfdc95bc0749c9de141cfbf13be Mon Sep 17 00:00:00 2001 From: Jan Provaznik Date: Tue, 28 Jan 2025 17:05:47 +0100 Subject: [PATCH 005/106] update doc --- .../specs/proposed/VS-OpenTelemetry.md | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/documentation/specs/proposed/VS-OpenTelemetry.md b/documentation/specs/proposed/VS-OpenTelemetry.md index 656f27f7ac1..fbb5f701d9a 100644 --- a/documentation/specs/proposed/VS-OpenTelemetry.md +++ b/documentation/specs/proposed/VS-OpenTelemetry.md @@ -24,6 +24,7 @@ It's a bit confusing how things are named in OpenTelemetry and .NET and VS Telem - If not sampled, no infra initialization overhead. - Avoid allocations when not sampled. - Has to have no impact on Core without opting into tracing, small impact on Framework +- No regression in VS perf ddrit scenarios. ### Privacy @@ -60,6 +61,7 @@ The data sent via VS OpenTelemetry is neither a subset neither a superset of wha ##### Features - BuildCheck enabled +- Custom tasks and targets counts and durations The design allows for easy instrumentation of additional data points. @@ -73,21 +75,21 @@ The design allows for easy instrumentation of additional data points. - Initialize and finalize in Xmake.cs - ActivitySource, TracerProvider, VS Collector - - overhead of starting VS collector is fairly big (0.3s on Devbox)[JanProvaznik/VSCollectorBenchmarks](https://github.com/JanProvaznik/VSCollectorBenchmarks) + - overhead of starting VS collector is nonzero - head sampling should avoid initializing if not sampled -## VS scenario +## VS in proc (devenv) scenario - VS can call `BuildManager` in a thread unsafe way the telemetry implementation has to be mindful of [BuildManager instances acquire its own BuildTelemetry instance by rokonec · Pull Request #8444 · dotnet/msbuild](https://github.com/dotnet/msbuild/pull/8444) - ensure no race conditions in initialization - only 1 TracerProvider with VS defined processing should exist -- Visual Studio should be responsible for having a running collector, we don't want this overhead in MSBuild and eventually many components can use it +- Visual Studio should be responsible for having a running collector, we don't want this overhead in MSBuild and eventually many will use it ## Implementation and MSBuild developer experience ### ActivitySource names -... +- Microsoft.VisualStudio.OpenTelemetry.MSBuild.Default ### Sampling @@ -101,6 +103,10 @@ For proportion estimation (of fairly common occurence in the builds), with not v - Enables opt-in and opt-out for guaranteed sample or not sampled. - nullable ActivitySource, using `?` when working with them, we can be initialized but not sampled -> it will not reinitialize but not collect telemetry. +- for Dev17 we can't use the new OTel assemblies and their dependencies, so everything has to be opt in. +- for Dev18 OpenTelemetry will be available and usable by default +- We can use experiments in VS to pass the environment variable to initialize + ### Initialization at entrypoints - There are 2 entrypoints: @@ -149,7 +155,7 @@ myActivity?.WithTags(data); #### Multiple Activity Sources -We can create ActivitySources with different sample rates. Ultimately this is limited by the need to initialize a collector. +We want to create ActivitySources with different sample rates, this requires either implementation server side or a custom Processor. We potentially want apart from the Default ActivitySource: @@ -166,6 +172,6 @@ We potentially want apart from the Default ActivitySource: - Sampling rare events details. - In standalone we could start the collector async without waiting which would potentially miss some earlier traces (unlikely to miss the important end of build trace though) but would degrade performance less than waiting for it's startup. The performance and utility tradeoff is not clear. - Can collector startup/shutdown be faster? -- We could let users configure sample rate via env variable, VS profile +- Exceptions in Collector code paths - Do we want to send antivirus state? Figuring it out is expensive: https://github.com/dotnet/msbuild/compare/main...proto/get-av ~100ms -- ability to configure the overal and per-namespace sampling from server side (e.g. storing it in the .msbuild folder in user profile if different then default values set from server side - this would obviously have a delay of the default sample rate # of executions) +- ability to configure the overall and per-namespace sampling from server side (e.g. storing it in the .msbuild folder in user profile if different then default values set from server side - this would obviously have a delay of the default sample rate # of executions) From 0931b45d4644a4f8575773919a5b403434d29839 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 14 Feb 2025 16:46:32 -0500 Subject: [PATCH 006/106] Address some low-hanging fruit to use newer/better .NET features This is based only on code inspection / static analysis / searching around. It is not based on profiling. --- .../BackEnd/CacheAggregator_Tests.cs | 8 +- .../BackEnd/ConfigCache_Tests.cs | 4 +- .../BackEnd/ResultsCache_Tests.cs | 2 +- .../FileUtilitiesRegex_Tests.cs | 1 - .../Globbing/MSBuildGlob_Tests.cs | 17 +- .../Graph/IsolateProjects_Tests.cs | 4 +- .../Graph/ResultCacheBasedBuilds_Tests.cs | 4 +- .../BackEnd/BuildManager/BuildManager.cs | 3 +- .../BackEnd/BuildManager/CacheAggregator.cs | 5 +- .../BackEnd/Client/MSBuildClientPacketPump.cs | 4 +- .../BuildRequestEngine/BuildRequestEngine.cs | 2 +- .../BuildRequestEngine/BuildRequestEntry.cs | 7 +- .../Communications/NodeEndpointInProc.cs | 2 +- .../Communications/NodeProviderInProc.cs | 2 +- .../NodeProviderOutOfProcBase.cs | 20 +- .../Communications/TranslatorExtensions.cs | 2 +- .../FileAccesses/FileAccessManager.cs | 2 +- .../Components/Logging/LoggingService.cs | 4 +- .../ProjectCache/ProjectCacheService.cs | 12 +- .../IntrinsicTasks/ItemGroupIntrinsicTask.cs | 2 +- .../RequestBuilder/IntrinsicTasks/MSBuild.cs | 9 +- .../Components/RequestBuilder/Lookup.cs | 2 +- .../RequestBuilder/RequestBuilder.cs | 21 +- .../RequestBuilder/TargetBuilder.cs | 4 +- .../Components/RequestBuilder/TargetEntry.cs | 2 +- .../RequestBuilder/TargetUpToDateChecker.cs | 14 +- .../Components/RequestBuilder/TaskBuilder.cs | 8 +- .../Components/RequestBuilder/TaskHost.cs | 6 +- .../Components/Scheduler/ScheduleResponse.cs | 14 +- .../BackEnd/Components/Scheduler/Scheduler.cs | 6 +- .../Components/Scheduler/SchedulingPlan.cs | 6 +- .../SdkResolution/SdkResolverService.cs | 4 +- .../Shared/BuildRequestConfiguration.cs | 2 +- .../BuildCheckAcquisitionModule.cs | 6 +- .../EditorConfig/EditorConfigFile.cs | 31 +-- .../EditorConfig/EditorConfigParser.cs | 7 +- src/Build/Collections/CopyOnReadEnumerable.cs | 2 +- .../Construction/ProjectElementContainer.cs | 2 +- src/Build/Construction/ProjectRootElement.cs | 16 +- .../Construction/ProjectTargetElement.cs | 2 +- .../Solution/ProjectInSolution.cs | 30 ++- .../Construction/Solution/SolutionFile.cs | 146 +++++------ .../Solution/SolutionProjectGenerator.cs | 16 +- src/Build/Definition/Project.cs | 10 +- .../Definition/ToolsetConfigurationReader.cs | 2 +- src/Build/Definition/ToolsetReader.cs | 30 ++- src/Build/ElementLocation/ElementLocation.cs | 4 +- .../XmlDocumentWithLocation.cs | 2 +- src/Build/Evaluation/ConditionEvaluator.cs | 2 +- .../Conditionals/CharacterUtilities.cs | 9 +- src/Build/Evaluation/Conditionals/Scanner.cs | 4 +- src/Build/Evaluation/Evaluator.cs | 24 +- src/Build/Evaluation/Expander.cs | 98 +++----- .../Evaluation/Expander/WellKnownFunctions.cs | 4 +- src/Build/Evaluation/ExpressionShredder.cs | 2 +- src/Build/Evaluation/IntrinsicFunctions.cs | 41 ++-- .../LazyItemEvaluator.RemoveOperation.cs | 4 +- src/Build/Evaluation/Preprocessor.cs | 13 +- ...EvaluationLocationMarkdownPrettyPrinter.cs | 11 +- .../EvaluationLocationPrettyPrinterBase.cs | 22 +- src/Build/Evaluation/ProjectParser.cs | 2 +- .../Evaluation/ProjectRootElementCache.cs | 8 +- src/Build/Evaluation/StringMetadataTable.cs | 2 +- src/Build/Globbing/MSBuildGlob.cs | 2 +- src/Build/Graph/GraphBuilder.cs | 4 +- src/Build/Graph/ParallelWorkSet.cs | 7 +- src/Build/Graph/ProjectInterpretation.cs | 2 +- src/Build/Instance/HostObjectException.cs | 2 +- src/Build/Instance/ProjectInstance.cs | 9 +- src/Build/Instance/ProjectItemInstance.cs | 2 +- src/Build/Instance/ProjectMetadataInstance.cs | 2 +- src/Build/Instance/ProjectPropertyInstance.cs | 2 +- src/Build/Instance/TaskRegistry.cs | 24 +- src/Build/Logging/BaseConsoleLogger.cs | 12 +- .../Logging/BinaryLogger/BinaryLogger.cs | 4 +- .../BinaryLogger/BuildEventArgsReader.cs | 4 +- .../BinaryLogger/Postprocessing/SubStream.cs | 10 +- .../Postprocessing/TransparentReadStream.cs | 10 +- .../DistributedFileLogger.cs | 2 +- src/Build/Logging/LogFormatter.cs | 2 +- src/Build/Logging/LoggerDescription.cs | 2 +- src/Build/Logging/OptimizedStringIndenter.cs | 6 +- .../ParallelLogger/ParallelConsoleLogger.cs | 16 +- .../ParallelLogger/ParallelLoggerHelpers.cs | 22 +- src/Build/Logging/ProfilerLogger.cs | 2 +- src/Build/Resources/Constants.cs | 4 +- src/Build/Utilities/SimpleVersion.cs | 6 +- src/Build/Utilities/Utilities.cs | 44 ++-- src/Framework/InternalErrorException.cs | 2 +- src/Framework/LazyFormattedBuildEventArgs.cs | 4 +- src/Framework/NativeMethods.cs | 12 +- src/Framework/OperatingSystem.cs | 2 +- src/MSBuild/AutomaticEncodingRestorer.cs | 4 +- src/MSBuild/PerformanceLogEventListener.cs | 6 +- src/MSBuild/TerminalLogger/NodeStatus.cs | 21 +- src/MSBuild/TerminalLogger/TerminalLogger.cs | 28 +-- src/MSBuild/XMake.cs | 17 +- .../AssemblyFolders/AssemblyFoldersEx.cs | 4 +- .../AssemblyFoldersFromConfig.cs | 2 +- src/Shared/AssemblyNameExtension.cs | 26 +- src/Shared/AwaitExtensions.cs | 4 +- src/Shared/CanonicalError.cs | 231 +++++++++++------- src/Shared/CommunicationsUtilities.cs | 20 +- src/Shared/Constants.cs | 47 ++-- src/Shared/ConversionUtilities.cs | 22 +- .../Debugging/PrintLineDebuggerWriters.cs | 2 +- src/Shared/EnvironmentUtilities.cs | 5 - src/Shared/EscapingUtilities.cs | 2 +- src/Shared/FileMatcher.cs | 85 +++---- src/Shared/FileUtilities.cs | 69 ++++-- src/Shared/FileUtilitiesRegex.cs | 4 + src/Shared/FrameworkLocationHelper.cs | 17 +- src/Shared/LogMessagePacketBase.cs | 4 +- src/Shared/MSBuildNameIgnoreCaseComparer.cs | 4 + src/Shared/Modifiers.cs | 2 +- src/Shared/NamedPipeUtil.cs | 11 +- src/Shared/NodeEndpointOutOfProcBase.cs | 6 +- src/Shared/PlatformNegotiation.cs | 12 +- src/Shared/ProcessExtensions.cs | 2 +- src/Shared/ProjectWriter.cs | 43 ++-- src/Shared/PropertyParser.cs | 10 +- src/Shared/RegisteredTaskObjectCacheBase.cs | 2 +- src/Shared/ResourceUtilities.cs | 2 +- src/Shared/StringUtils.cs | 15 +- src/Shared/TempFileUtilities.cs | 2 +- src/Shared/ToolsetElement.cs | 2 +- src/Shared/Tracing.cs | 8 +- src/Shared/TypeLoader.cs | 11 +- .../UnitTests/NativeMethodsShared_Tests.cs | 2 +- src/Shared/UnitTests/XmakeAttributes_Tests.cs | 5 +- src/Shared/VersionUtilities.cs | 2 +- src/Shared/XMakeElements.cs | 8 +- src/StringTools/InternableString.cs | 2 +- src/StringTools/WeakStringCacheInterner.cs | 16 +- src/Tasks/AppConfig/BindingRedirect.cs | 14 +- .../AssemblyFoldersFromConfigCache.cs | 10 +- .../AssemblyDependency/AssemblyInformation.cs | 2 +- .../AssemblyDependency/GlobalAssemblyCache.cs | 2 +- src/Tasks/AssemblyDependency/Reference.cs | 2 +- .../AssemblyDependency/ReferenceTable.cs | 24 +- .../ResolveAssemblyReference.cs | 32 +-- .../BootstrapperUtil/BootstrapperBuilder.cs | 24 +- src/Tasks/BootstrapperUtil/BuildMessage.cs | 11 +- src/Tasks/BootstrapperUtil/Product.cs | 2 +- src/Tasks/BootstrapperUtil/Util.cs | 5 + src/Tasks/ComReference.cs | 2 +- src/Tasks/CultureInfoCache.cs | 6 +- src/Tasks/DownloadFile.cs | 8 +- src/Tasks/FileIO/GetFileHash.cs | 2 +- src/Tasks/FindInvalidProjectReferences.cs | 15 +- src/Tasks/FormatVersion.cs | 9 +- src/Tasks/GenerateApplicationManifest.cs | 4 +- src/Tasks/GenerateManifestBase.cs | 6 +- src/Tasks/GenerateResource.cs | 29 +-- src/Tasks/GetAssemblyIdentity.cs | 5 + src/Tasks/GetSDKReferenceFiles.cs | 2 +- src/Tasks/Hash.cs | 4 + src/Tasks/MSBuild.cs | 5 +- src/Tasks/ManifestUtil/ApplicationManifest.cs | 12 +- src/Tasks/ManifestUtil/AssemblyIdentity.cs | 30 ++- src/Tasks/ManifestUtil/ConvertUtil.cs | 4 +- .../ManifestUtil/EmbeddedManifestReader.cs | 2 +- src/Tasks/ManifestUtil/Manifest.cs | 4 +- src/Tasks/ManifestUtil/ManifestFormatter.cs | 4 +- src/Tasks/ManifestUtil/ManifestReader.cs | 2 +- src/Tasks/ManifestUtil/ManifestWriter.cs | 4 +- src/Tasks/ManifestUtil/MetadataReader.cs | 7 +- src/Tasks/ManifestUtil/PathUtil.cs | 17 +- src/Tasks/ManifestUtil/TrustInfo.cs | 2 +- src/Tasks/ManifestUtil/Util.cs | 58 ++--- src/Tasks/ManifestUtil/XmlUtil.cs | 16 +- src/Tasks/ManifestUtil/mansign2.cs | 12 + src/Tasks/NativeMethods.cs | 35 ++- src/Tasks/RedistList.cs | 4 +- src/Tasks/RequiresFramework35SP1Assembly.cs | 7 +- src/Tasks/ResolveComReference.cs | 2 +- src/Tasks/ResolveManifestFiles.cs | 31 ++- src/Tasks/ResolveSDKReference.cs | 29 ++- .../ResourceHandling/MSBuildResXReader.cs | 10 +- .../RoslynCodeTaskFactory.cs | 4 +- src/Tasks/SystemState.cs | 2 +- src/Tasks/Unzip.cs | 2 +- src/Tasks/WriteCodeFragment.cs | 8 +- .../XamlTaskFactory/CommandLineGenerator.cs | 34 ++- src/Tasks/XamlTaskFactory/XamlTaskFactory.cs | 4 +- .../ToolLocationHelper_Tests.cs | 10 +- src/Utilities/CommandLineBuilder.cs | 13 +- src/Utilities/LockCheck.cs | 2 +- src/Utilities/SDKManifest.cs | 8 +- src/Utilities/TargetPlatformSDK.cs | 2 +- src/Utilities/ToolLocationHelper.cs | 27 +- 191 files changed, 1315 insertions(+), 1084 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/CacheAggregator_Tests.cs b/src/Build.UnitTests/BackEnd/CacheAggregator_Tests.cs index bbe0f749387..488f5f8bfd9 100644 --- a/src/Build.UnitTests/BackEnd/CacheAggregator_Tests.cs +++ b/src/Build.UnitTests/BackEnd/CacheAggregator_Tests.cs @@ -33,10 +33,10 @@ public void NoCachesProducesEmptyCaches() var aggregation = aggregator.Aggregate(); aggregation.ConfigCache.ShouldNotBeNull(); - aggregation.ConfigCache.GetEnumerator().ToEnumerable().ShouldBeEmpty(); + aggregation.ConfigCache.ShouldBeEmpty(); aggregation.ResultsCache.ShouldNotBeNull(); - aggregation.ResultsCache.GetEnumerator().ToEnumerable().ShouldBeEmpty(); + aggregation.ResultsCache.ShouldBeEmpty(); aggregation.LastConfigurationId.ShouldBe(0); } @@ -246,9 +246,9 @@ private void AssertAggregation((ConfigCache configCache, ResultsCache resultsCac var currentConfigurationIndex = 0; var currentBuildResultIndex = 0; - var aggregatedConfigs = aggregation.ConfigCache.GetEnumerator().ToArray(); + var aggregatedConfigs = aggregation.ConfigCache.ToArray(); - var aggregatedResults = aggregation.ResultsCache.GetEnumerator().ToArray(); + var aggregatedResults = aggregation.ResultsCache.ToArray(); foreach (var (configCache, resultsCache) in inputCaches) { diff --git a/src/Build.UnitTests/BackEnd/ConfigCache_Tests.cs b/src/Build.UnitTests/BackEnd/ConfigCache_Tests.cs index 3ca18bc9832..bf40853c4e8 100644 --- a/src/Build.UnitTests/BackEnd/ConfigCache_Tests.cs +++ b/src/Build.UnitTests/BackEnd/ConfigCache_Tests.cs @@ -101,8 +101,8 @@ public void ConfigCacheShouldBeTranslatable(object obj) TranslationHelpers.GetReadTranslator().Translate(ref copy); // test _configurations - var initialConfigurations = initial.GetEnumerator().ToArray(); - var copiedConfigurations = copy.GetEnumerator().ToArray(); + var initialConfigurations = initial.ToArray(); + var copiedConfigurations = copy.ToArray(); Assert.Equal(copiedConfigurations, initialConfigurations, EqualityComparer.Default); diff --git a/src/Build.UnitTests/BackEnd/ResultsCache_Tests.cs b/src/Build.UnitTests/BackEnd/ResultsCache_Tests.cs index 7fc43eccc59..eceb5123067 100644 --- a/src/Build.UnitTests/BackEnd/ResultsCache_Tests.cs +++ b/src/Build.UnitTests/BackEnd/ResultsCache_Tests.cs @@ -80,7 +80,7 @@ public void CacheCanBeEnumerated() result2.AddResultsForTarget("result2target1", BuildResultUtilities.GetEmptyFailingTargetResult()); cache.AddResult(result2); - var results = cache.GetEnumerator().ToArray(); + var results = cache.ToArray(); results.Length.ShouldBe(2); diff --git a/src/Build.UnitTests/FileUtilitiesRegex_Tests.cs b/src/Build.UnitTests/FileUtilitiesRegex_Tests.cs index 72a46b6d23b..80781a43671 100644 --- a/src/Build.UnitTests/FileUtilitiesRegex_Tests.cs +++ b/src/Build.UnitTests/FileUtilitiesRegex_Tests.cs @@ -532,7 +532,6 @@ public void PatternEmptyString_LegacyRegex() { UncPattern.IsMatch(string.Empty).ShouldBeFalse(); StartsWithUncPattern.IsMatch(string.Empty).ShouldBeFalse(); - StartsWithUncPattern.Match(string.Empty).Success.ShouldBeFalse(); } [Fact] diff --git a/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs b/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs index cc551762e11..8156ed4fb52 100644 --- a/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs +++ b/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; @@ -70,9 +71,12 @@ public void GlobFromRelativeGlobRootNormalizesRootAgainstCurrentDirectory() [Fact] public void GlobFromRootWithInvalidPathThrows() { - foreach (var invalidPathChar in FileUtilities.InvalidPathChars) + for (int i = 0; i < 128; i++) { - Assert.Throws(() => MSBuildGlob.Parse(invalidPathChar.ToString(), "*")); + if (FileUtilities.InvalidPathChars.Contains((char)i)) + { + Assert.Throws(() => MSBuildGlob.Parse(((char)i).ToString(), "*")); + } } } @@ -182,12 +186,15 @@ public void GlobMatchShouldReturnFalseIfArgumentContainsInvalidPathOrFileCharact { var glob = MSBuildGlob.Parse("*"); - foreach (var invalidPathChar in FileUtilities.InvalidPathChars) + for (int i = 0; i < 128; i++) { - Assert.False(glob.IsMatch(invalidPathChar.ToString())); + if (FileUtilities.InvalidPathChars.Contains((char)i)) + { + Assert.False(glob.IsMatch(((char)i).ToString())); + } } - foreach (var invalidFileChar in FileUtilities.InvalidFileNameChars) + foreach (var invalidFileChar in FileUtilities.InvalidFileNameCharsArray) { if (invalidFileChar == '\\' || invalidFileChar == '/') { diff --git a/src/Build.UnitTests/Graph/IsolateProjects_Tests.cs b/src/Build.UnitTests/Graph/IsolateProjects_Tests.cs index 063df0be739..d799c4267b1 100644 --- a/src/Build.UnitTests/Graph/IsolateProjects_Tests.cs +++ b/src/Build.UnitTests/Graph/IsolateProjects_Tests.cs @@ -338,8 +338,8 @@ public void UndeclaredReferenceBuildResultNotPresentInOutputCache() var deserializedOutputCacheRoot = CacheSerialization.DeserializeCaches(outputCaches[topoSortedProjectGraphNodes[1]]); deserializedOutputCacheDeclaredReference.exception.ShouldBeNull(); deserializedOutputCacheRoot.exception.ShouldBeNull(); - BuildResult[] declaredReferenceBuildResults = deserializedOutputCacheDeclaredReference.ResultsCache.GetEnumerator().ToArray(); - BuildResult[] rootBuildResults = deserializedOutputCacheRoot.ResultsCache.GetEnumerator().ToArray(); + BuildResult[] declaredReferenceBuildResults = deserializedOutputCacheDeclaredReference.ResultsCache.ToArray(); + BuildResult[] rootBuildResults = deserializedOutputCacheRoot.ResultsCache.ToArray(); // Both the root and declared reference projects should only have one build result. declaredReferenceBuildResults.Length.ShouldBe(1); diff --git a/src/Build.UnitTests/Graph/ResultCacheBasedBuilds_Tests.cs b/src/Build.UnitTests/Graph/ResultCacheBasedBuilds_Tests.cs index b1a0b664c80..8ff5f52352f 100644 --- a/src/Build.UnitTests/Graph/ResultCacheBasedBuilds_Tests.cs +++ b/src/Build.UnitTests/Graph/ResultCacheBasedBuilds_Tests.cs @@ -356,13 +356,13 @@ public void OutputCacheShouldNotContainInformationFromInputCaches() deserializationInfo.exception.ShouldBeNull(); - var buildResults = deserializationInfo.ResultsCache.GetEnumerator().ToArray(); + var buildResults = deserializationInfo.ResultsCache.ToArray(); buildResults.ShouldHaveSingleItem(); var rootNodeBuildResult = buildResults.First(); rootNodeBuildResult.ResultsByTarget["Build"].Items.Select(i => i.ItemSpec).ToArray().ShouldBe(expectedOutput[rootNode]); - var configEntries = deserializationInfo.ConfigCache.GetEnumerator().ToArray(); + var configEntries = deserializationInfo.ConfigCache.ToArray(); configEntries.ShouldHaveSingleItem(); configEntries.First().ConfigurationId.ShouldBe(rootNodeBuildResult.ConfigurationId); diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 10cf3577bf1..444589b67a4 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -2774,12 +2774,11 @@ private NodeConfiguration GetNodeConfiguration() { // Get the remote loggers ILoggingService loggingService = ((IBuildComponentHost)this).GetComponent(BuildComponentType.LoggingService); - var remoteLoggers = new List(loggingService.LoggerDescriptions); _nodeConfiguration = new NodeConfiguration( -1, /* must be assigned by the NodeManager */ _buildParameters, - remoteLoggers.ToArray() + loggingService.LoggerDescriptions.ToArray() #if FEATURE_APPDOMAIN , AppDomain.CurrentDomain.SetupInformation #endif diff --git a/src/Build/BackEnd/BuildManager/CacheAggregator.cs b/src/Build/BackEnd/BuildManager/CacheAggregator.cs index aca3b8ef9c7..ddbe4524e06 100644 --- a/src/Build/BackEnd/BuildManager/CacheAggregator.cs +++ b/src/Build/BackEnd/BuildManager/CacheAggregator.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; using Microsoft.Build.Internal; @@ -55,8 +56,8 @@ public CacheAggregation Aggregate() private void InsertCaches(IConfigCache configCache, IResultsCache resultsCache) { - var configs = configCache.GetEnumerator().ToArray(); - var results = resultsCache.GetEnumerator().ToArray(); + var configs = configCache.ToArray(); + var results = resultsCache.ToArray(); ErrorUtilities.VerifyThrow(configs.Length == results.Length, "Assuming 1-to-1 mapping between configs and results. Otherwise it means the caches are either not minimal or incomplete"); diff --git a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs index 65a7b72a4dd..a7234030b5c 100644 --- a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs +++ b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs @@ -195,7 +195,7 @@ private void RunReadLoop(Stream localStream, ManualResetEvent localPacketPumpShu #if FEATURE_APM IAsyncResult result = localStream.BeginRead(headerByte, 0, headerByte.Length, null, null); #else - Task readTask = CommunicationsUtilities.ReadAsync(localStream, headerByte, headerByte.Length); + Task readTask = CommunicationsUtilities.ReadAsync(localStream, headerByte, headerByte.Length).AsTask(); #endif bool continueReading = true; @@ -294,7 +294,7 @@ private void RunReadLoop(Stream localStream, ManualResetEvent localPacketPumpShu #if FEATURE_APM result = localStream.BeginRead(headerByte, 0, headerByte.Length, null, null); #else - readTask = CommunicationsUtilities.ReadAsync(localStream, headerByte, headerByte.Length); + readTask = CommunicationsUtilities.ReadAsync(localStream, headerByte, headerByte.Length).AsTask(); #endif } } diff --git a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs index 9c633d14b8c..cbc2cb0d4f7 100644 --- a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs +++ b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs @@ -1429,7 +1429,7 @@ private void TraceEngine(string format, params object[] stuff) using (StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, @"EngineTrace_{0}.txt"), Process.GetCurrentProcess().Id), append: true)) { string message = String.Format(CultureInfo.CurrentCulture, format, stuff); - file.WriteLine("{0}({1})-{2}: {3}", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId, DateTime.UtcNow.Ticks, message); + file.WriteLine("{0}({1})-{2}: {3}", Thread.CurrentThread.Name, Environment.CurrentManagedThreadId, DateTime.UtcNow.Ticks, message); file.Flush(); } } diff --git a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEntry.cs b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEntry.cs index 28466f039ca..2450c2debfd 100644 --- a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEntry.cs +++ b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEntry.cs @@ -511,12 +511,13 @@ private void WaitForResult(BuildRequest newRequest, bool addToIssueList) ErrorUtilities.VerifyThrow(addToIssueList, "Requests with unresolved configurations should always be added to the issue list."); _unresolvedConfigurations ??= new Dictionary>(); - if (!_unresolvedConfigurations.ContainsKey(newRequest.ConfigurationId)) + if (!_unresolvedConfigurations.TryGetValue(newRequest.ConfigurationId, out List value)) { - _unresolvedConfigurations.Add(newRequest.ConfigurationId, new List()); + value = new List(); + _unresolvedConfigurations.Add(newRequest.ConfigurationId, value); } - _unresolvedConfigurations[newRequest.ConfigurationId].Add(newRequest); + value.Add(newRequest); } if (addToIssueList) diff --git a/src/Build/BackEnd/Components/Communications/NodeEndpointInProc.cs b/src/Build/BackEnd/Components/Communications/NodeEndpointInProc.cs index 6fdad8b8128..2c90dda389c 100644 --- a/src/Build/BackEnd/Components/Communications/NodeEndpointInProc.cs +++ b/src/Build/BackEnd/Components/Communications/NodeEndpointInProc.cs @@ -29,7 +29,7 @@ internal class NodeEndpointInProc : INodeEndpoint /// /// An object for the two inproc endpoints to synchronize on. /// - private static Object s_locker = new Object(); + private static readonly Object s_locker = new Object(); /// /// The current communication status of the node. diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs index 15c815fb9cf..61ff495b01f 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs @@ -382,7 +382,7 @@ private bool InstantiateNode(INodePacketFactory factory) InProcNodeThreadProc(); }); #endif - _inProcNodeThread.Name = String.Format(CultureInfo.CurrentCulture, "In-proc Node ({0})", _componentHost.Name); + _inProcNodeThread.Name = $"In-proc Node ({_componentHost.Name})"; _inProcNodeThread.IsBackground = true; #if FEATURE_THREAD_CULTURE _inProcNodeThread.CurrentCulture = _componentHost.BuildParameters.Culture; diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index 87602148672..b545f644a1e 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -10,6 +10,7 @@ using System.IO.Pipes; using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; #if FEATURE_PIPE_SECURITY using System.Security.Principal; @@ -17,8 +18,6 @@ #if FEATURE_APM using Microsoft.Build.Eventing; -#else -using System.Threading; #endif using Microsoft.Build.Internal; using Microsoft.Build.Shared; @@ -413,7 +412,11 @@ void CreateNodeContext(int nodeId, Process nodeToReuse, Stream nodeStream) /// private string GetProcessesToIgnoreKey(Handshake hostHandshake, int nodeProcessId) { - return hostHandshake.ToString() + "|" + nodeProcessId.ToString(CultureInfo.InvariantCulture); +#if NET + return string.Create(CultureInfo.InvariantCulture, $"{hostHandshake}|{nodeProcessId}"); +#else + return $"{hostHandshake}|{nodeProcessId.ToString(CultureInfo.InvariantCulture)}"; +#endif } #if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY @@ -832,8 +835,17 @@ public async Task WaitForExitAsync(ILoggingService loggingService) { // Wait up to 100ms until all remaining packets are sent. // We don't need to wait long, just long enough for the Task to start running on the ThreadPool. - await Task.WhenAny(_packetWriteDrainTask, Task.Delay(100)); +#if NET + await _packetWriteDrainTask.WaitAsync(TimeSpan.FromMilliseconds(100)).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); +#else + using (var cts = new CancellationTokenSource(100)) + { + await Task.WhenAny(_packetWriteDrainTask, Task.Delay(100, cts.Token)); + cts.Cancel(); + } +#endif } + if (_exitPacketState == ExitPacketState.ExitPacketSent) { CommunicationsUtilities.Trace("Waiting for node with pid = {0} to exit", _process.Id); diff --git a/src/Build/BackEnd/Components/Communications/TranslatorExtensions.cs b/src/Build/BackEnd/Components/Communications/TranslatorExtensions.cs index 912c37ee0ca..ab2b1bb2da0 100644 --- a/src/Build/BackEnd/Components/Communications/TranslatorExtensions.cs +++ b/src/Build/BackEnd/Components/Communications/TranslatorExtensions.cs @@ -20,7 +20,7 @@ namespace Microsoft.Build.BackEnd /// internal static class TranslatorExtensions { - private static Lazy> parameterlessConstructorCache = new Lazy>(() => new ConcurrentDictionary()); + private static readonly Lazy> parameterlessConstructorCache = new Lazy>(() => new ConcurrentDictionary()); /// /// Translates a PropertyDictionary of ProjectPropertyInstances. diff --git a/src/Build/BackEnd/Components/FileAccesses/FileAccessManager.cs b/src/Build/BackEnd/Components/FileAccesses/FileAccessManager.cs index de28c100d1d..5936bf90ac5 100644 --- a/src/Build/BackEnd/Components/FileAccesses/FileAccessManager.cs +++ b/src/Build/BackEnd/Components/FileAccesses/FileAccessManager.cs @@ -22,7 +22,7 @@ private record Handlers(Action FileAccessHander, A // is used to mark when the file accesses should be considered complete. Only after both this special file access is seen // and the build result is reported can plugins be notified about project completion. // NOTE! This is currently Windows-specific and will need to change once this feature is opened up to more scenarios. - private static readonly string FileAccessCompletionPrefix = BuildParameters.StartupDirectory[0] + @":\{MSBuildFileAccessCompletion}\"; + private static readonly string FileAccessCompletionPrefix = $@"{BuildParameters.StartupDirectory[0]}:\{{MSBuildFileAccessCompletion}}\"; private IScheduler? _scheduler; private IConfigCache? _configCache; diff --git a/src/Build/BackEnd/Components/Logging/LoggingService.cs b/src/Build/BackEnd/Components/Logging/LoggingService.cs index c08622d49eb..1e4121594e2 100644 --- a/src/Build/BackEnd/Components/Logging/LoggingService.cs +++ b/src/Build/BackEnd/Components/Logging/LoggingService.cs @@ -94,7 +94,7 @@ internal partial class LoggingService : ILoggingService, INodePacketHandler /// We use a BindingFlags.Public flag here because the getter is public, so although the setter is internal, /// it is only discoverable with Reflection using the Public flag (go figure!) /// - private static Lazy s_projectStartedEventArgsGlobalProperties = new Lazy(() => typeof(ProjectStartedEventArgs).GetProperty("GlobalProperties", BindingFlags.Public | BindingFlags.Instance), LazyThreadSafetyMode.PublicationOnly); + private static readonly Lazy s_projectStartedEventArgsGlobalProperties = new Lazy(() => typeof(ProjectStartedEventArgs).GetProperty("GlobalProperties", BindingFlags.Public | BindingFlags.Instance), LazyThreadSafetyMode.PublicationOnly); /// /// A cached reflection accessor for an internal member. @@ -103,7 +103,7 @@ internal partial class LoggingService : ILoggingService, INodePacketHandler /// We use a BindingFlags.Public flag here because the getter is public, so although the setter is internal, /// it is only discoverable with Reflection using the Public flag (go figure!) /// - private static Lazy s_projectStartedEventArgsToolsVersion = new Lazy(() => typeof(ProjectStartedEventArgs).GetProperty("ToolsVersion", BindingFlags.Public | BindingFlags.Instance), LazyThreadSafetyMode.PublicationOnly); + private static readonly Lazy s_projectStartedEventArgsToolsVersion = new Lazy(() => typeof(ProjectStartedEventArgs).GetProperty("ToolsVersion", BindingFlags.Public | BindingFlags.Instance), LazyThreadSafetyMode.PublicationOnly); #region Data diff --git a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs index bd1cb0fd8d7..8906f10ba5a 100644 --- a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs +++ b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs @@ -32,7 +32,7 @@ internal sealed class ProjectCacheService : IAsyncDisposable { private static readonly ParallelOptions s_parallelOptions = new() { MaxDegreeOfParallelism = Environment.ProcessorCount }; - private static HashSet s_projectSpecificPropertyNames = new(StringComparer.OrdinalIgnoreCase) { "TargetFramework", "Configuration", "Platform", "TargetPlatform", "OutputType" }; + private static readonly HashSet s_projectSpecificPropertyNames = new(StringComparer.OrdinalIgnoreCase) { "TargetFramework", "Configuration", "Platform", "TargetPlatform", "OutputType" }; private readonly BuildManager _buildManager; private readonly IBuildComponentHost _componentHost; @@ -115,8 +115,7 @@ public void InitializePluginsForGraph( foreach (ProjectCacheDescriptor projectCacheDescriptor in GetProjectCacheDescriptors(node.ProjectInstance)) { // Intentionally fire-and-forget to asynchronously initialize the plugin. Any exceptions will bubble up later when querying. - _ = GetProjectCachePluginAsync(projectCacheDescriptor, projectGraph, buildRequestConfiguration: null, requestedTargets, cancellationToken) - .ContinueWith(t => { }, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted); + _ = GetProjectCachePluginAsync(projectCacheDescriptor, projectGraph, buildRequestConfiguration: null, requestedTargets, cancellationToken); } }); }, @@ -149,8 +148,7 @@ public void InitializePluginsForVsScenario( projectCacheDescriptor => { // Intentionally fire-and-forget to asynchronously initialize the plugin. Any exceptions will bubble up later when querying. - _ = GetProjectCachePluginAsync(projectCacheDescriptor, projectGraph: null, buildRequestConfiguration, requestedTargets, cancellationToken) - .ContinueWith(t => { }, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted); + _ = GetProjectCachePluginAsync(projectCacheDescriptor, projectGraph: null, buildRequestConfiguration, requestedTargets, cancellationToken); }); }, cancellationToken); @@ -449,7 +447,7 @@ public void PostCacheRequest(CacheRequest cacheRequest, CancellationToken cancel }, cancellationToken); - async Task<(CacheResult Result, int ProjectContextId)> ProcessCacheRequestAsync() + async ValueTask<(CacheResult Result, int ProjectContextId)> ProcessCacheRequestAsync() { EvaluateProjectIfNecessary(cacheRequest.Submission, cacheRequest.Configuration); @@ -499,7 +497,7 @@ void EvaluateProjectIfNecessary(BuildSubmission submission, BuildRequestConfigur } } - private async Task GetCacheResultAsync(BuildRequestData buildRequest, BuildRequestConfiguration buildRequestConfiguration, BuildEventContext buildEventContext, CancellationToken cancellationToken) + private async ValueTask GetCacheResultAsync(BuildRequestData buildRequest, BuildRequestConfiguration buildRequestConfiguration, BuildEventContext buildEventContext, CancellationToken cancellationToken) { ErrorUtilities.VerifyThrowInternalNull(buildRequest.ProjectInstance, nameof(buildRequest.ProjectInstance)); diff --git a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs index 423679a1f6e..c33c0ab6563 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs @@ -186,7 +186,7 @@ private void ExecuteAdd(ProjectItemGroupTaskItemInstance child, ItemBucket bucke if (// If multiple buckets were expanded - we do not want to repeat same error for same metadatum on a same line bucket.BucketSequenceNumber == 0 && // Referring to unqualified metadata of other item (transform) is fine. - child.Include.IndexOf("@(", StringComparison.Ordinal) == -1) + !child.Include.Contains("@(")) { expanderOptions |= ExpanderOptions.LogOnItemMetadataSelfReference; } diff --git a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs index 73f84dd94a0..9c2271ff635 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs @@ -259,7 +259,7 @@ public async Task ExecuteInternal() undefinePropertiesArray = RemoveProperties.Split(MSBuildConstants.SemicolonChar, StringSplitOptions.RemoveEmptyEntries); foreach (string property in undefinePropertiesArray) { - Log.LogMessageFromText(String.Format(CultureInfo.InvariantCulture, " {0}", property), MessageImportance.Low); + Log.LogMessageFromText($" {property}", MessageImportance.Low); } } @@ -296,10 +296,7 @@ public async Task ExecuteInternal() if (BuildInParallel) { skipProjects = new bool[Projects.Length]; - for (int i = 0; i < skipProjects.Length; i++) - { - skipProjects[i] = true; - } + skipProjects.AsSpan().Fill(true); } else { @@ -594,7 +591,7 @@ internal static async Task ExecuteTargets( foreach (string property in propertiesToUndefine) { undefinePropertiesPerProject[i].Add(property); - log.LogMessageFromText(String.Format(CultureInfo.InvariantCulture, " {0}", property), MessageImportance.Low); + log.LogMessageFromText($" {property}", MessageImportance.Low); } } } diff --git a/src/Build/BackEnd/Components/RequestBuilder/Lookup.cs b/src/Build/BackEnd/Components/RequestBuilder/Lookup.cs index 6a882eb87f0..c2b180dd2b9 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/Lookup.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/Lookup.cs @@ -1373,7 +1373,7 @@ internal Scope(Lookup lookup, string description, IItemDictionary resultsList = new List(results.Values); - resultsList.Sort(delegate (BuildResult left, BuildResult right) - { - if (left.NodeRequestId < right.NodeRequestId) - { - return -1; - } - else if (left.NodeRequestId == right.NodeRequestId) - { - return 0; - } - - return 1; - }); - - return resultsList.ToArray(); + BuildResult[] resultsArray = results.Values.ToArray(); + Array.Sort(resultsArray, (left, right) => left.NodeRequestId.CompareTo(right.NodeRequestId)); + return resultsArray; } /// diff --git a/src/Build/BackEnd/Components/RequestBuilder/TargetBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/TargetBuilder.cs index 7cd08affeb2..37661d47cef 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TargetBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TargetBuilder.cs @@ -406,7 +406,7 @@ private async Task ProcessTargetStack(ITaskBuilder taskBuilder) ( !_cancellationToken.IsCancellationRequested && !stopProcessingStack && - _targetsToBuild.Any()) + !_targetsToBuild.IsEmpty) { TargetEntry currentTargetEntry = _targetsToBuild.Peek(); switch (currentTargetEntry.State) @@ -611,7 +611,7 @@ private void PopDependencyTargetsOnTargetFailure(TargetEntry topEntry, TargetRes // Pop down to our parent, since any other dependencies our parent had should no longer // execute. If we encounter an error target on the way down, also stop since the failure // of one error target in a set declared in OnError should not cause the others to stop running. - while ((_targetsToBuild.Any()) && (_targetsToBuild.Peek() != topEntry.ParentEntry) && !_targetsToBuild.Peek().ErrorTarget) + while ((!_targetsToBuild.IsEmpty) && (_targetsToBuild.Peek() != topEntry.ParentEntry) && !_targetsToBuild.Peek().ErrorTarget) { TargetEntry entry = _targetsToBuild.Pop(); entry.LeaveLegacyCallTargetScopes(); diff --git a/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs b/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs index c85b4f41f54..30179f2e7a9 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs @@ -810,7 +810,7 @@ internal void LeaveLegacyCallTargetScopes() /// /// The result of the tasks, based on the last task which ran. /// - private async Task ProcessBucket(ITaskBuilder taskBuilder, TargetLoggingContext targetLoggingContext, TaskExecutionMode mode, Lookup lookupForInference, Lookup lookupForExecution) + private async ValueTask ProcessBucket(ITaskBuilder taskBuilder, TargetLoggingContext targetLoggingContext, TaskExecutionMode mode, Lookup lookupForInference, Lookup lookupForExecution) { WorkUnitResultCode aggregatedTaskResult = WorkUnitResultCode.Success; WorkUnitActionCode finalActionCode = WorkUnitActionCode.Continue; diff --git a/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs b/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs index a75c73a91ae..58e250f6c97 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; @@ -838,15 +839,14 @@ private void SeparateItemVectorsFromDiscreteItems( } // Do we already have a partition for this? - if (!itemVectorCollection.ContainsKey(itemVectorType)) + if (!itemVectorCollection.TryGetValue(itemVectorType, out ItemVectorPartition itemVectorPartition)) { // Nope, create one. - itemVectorCollection[itemVectorType] = new ItemVectorPartition(MSBuildNameIgnoreCaseComparer.Default); + itemVectorPartition = new ItemVectorPartition(MSBuildNameIgnoreCaseComparer.Default); + itemVectorCollection[itemVectorType] = itemVectorPartition; } - ItemVectorPartition itemVectorPartition = itemVectorCollection[itemVectorType]; - - ErrorUtilities.VerifyThrow(!itemVectorCollection[itemVectorType].ContainsKey(item), "ItemVectorPartition already contains a vector for items with the expression '{0}'", item); + ErrorUtilities.VerifyThrow(!itemVectorPartition.ContainsKey(item), "ItemVectorPartition already contains a vector for items with the expression '{0}'", item); itemVectorPartition[item] = itemVectorContents; ErrorUtilities.VerifyThrow((itemVectorTransforms == null) || (itemVectorCollection.Equals(itemVectorTransforms)) || (itemVectorPartition.Count == 1), @@ -1129,8 +1129,8 @@ private bool IsOutOfDate(string input, string output, string inputItemName, stri { input = EscapingUtilities.UnescapeAll(FileUtilities.FixFilePath(input)); output = EscapingUtilities.UnescapeAll(FileUtilities.FixFilePath(output)); - ProjectErrorUtilities.VerifyThrowInvalidProject(input.IndexOfAny(Path.GetInvalidPathChars()) == -1, _project.ProjectFileLocation, "IllegalCharactersInFileOrDirectory", input, inputItemName); - ProjectErrorUtilities.VerifyThrowInvalidProject(output.IndexOfAny(Path.GetInvalidPathChars()) == -1, _project.ProjectFileLocation, "IllegalCharactersInFileOrDirectory", output, outputItemName); + ProjectErrorUtilities.VerifyThrowInvalidProject(input.AsSpan().IndexOfAny(MSBuildConstants.InvalidPathChars) < 0, _project.ProjectFileLocation, "IllegalCharactersInFileOrDirectory", input, inputItemName); + ProjectErrorUtilities.VerifyThrowInvalidProject(output.AsSpan().IndexOfAny(MSBuildConstants.InvalidPathChars) < 0, _project.ProjectFileLocation, "IllegalCharactersInFileOrDirectory", output, outputItemName); bool outOfDate = (CompareLastWriteTimes(input, output, out bool inputDoesNotExist, out bool outputDoesNotExist) == 1) || inputDoesNotExist; // Only if we are not logging just critical events should we be gathering full details diff --git a/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs index c45130602a1..e7a019fea8c 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs @@ -291,7 +291,7 @@ private List CreateListOfParameterValues() /// Called to execute a task within a target. This method instantiates the task, sets its parameters, and executes it. /// /// true, if successful - private async Task ExecuteTask(TaskExecutionMode mode, Lookup lookup) + private async ValueTask ExecuteTask(TaskExecutionMode mode, Lookup lookup) { ErrorUtilities.VerifyThrowArgumentNull(lookup); @@ -366,7 +366,7 @@ private async Task ExecuteTask(TaskExecutionMode mode, Lookup lo /// Execute a single bucket /// /// true if execution succeeded - private async Task ExecuteBucket(TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask, Dictionary lookupHash) + private async ValueTask ExecuteBucket(TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask, Dictionary lookupHash) { // On Intrinsic tasks, we do not allow batchable params, therefore metadata is excluded. ParserOptions parserOptions = (_taskNode == null) ? ParserOptions.AllowPropertiesAndItemLists : ParserOptions.AllowAll; @@ -734,7 +734,7 @@ private void UpdateContinueOnError(ItemBucket bucket, TaskHost taskHost) /// The batching bucket /// The task execution mode /// The result of running the task. - private async Task ExecuteInstantiatedTask(TaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask) + private async ValueTask ExecuteInstantiatedTask(TaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask) { UpdateContinueOnError(bucket, taskHost); @@ -855,7 +855,7 @@ private async Task ExecuteInstantiatedTask(TaskExecutionHost tas } else if (type == typeof(ThreadAbortException)) { -#if !NET6_0_OR_GREATER && !NET6_0 // This is redundant but works around https://github.com/dotnet/sdk/issues/20700 +#if !NET Thread.ResetAbort(); #endif _continueOnError = ContinueOnError.ErrorAndStop; diff --git a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs index 45414d7cf5c..0c4bb721766 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs @@ -42,7 +42,7 @@ internal class TaskHost : /// /// Help diagnose tasks that log after they return. /// - private static bool s_breakOnLogAfterTaskReturns = Environment.GetEnvironmentVariable("MSBUILDBREAKONLOGAFTERTASKRETURNS") == "1"; + private static readonly bool s_breakOnLogAfterTaskReturns = Environment.GetEnvironmentVariable("MSBUILDBREAKONLOGAFTERTASKRETURNS") == "1"; /// /// The build component host @@ -357,7 +357,7 @@ public void Yield() { IRequestBuilderCallback builderCallback = _requestEntry.Builder as IRequestBuilderCallback; ErrorUtilities.VerifyThrow(_yieldThreadId == -1, "Cannot call Yield() while yielding."); - _yieldThreadId = Thread.CurrentThread.ManagedThreadId; + _yieldThreadId = Environment.CurrentManagedThreadId; MSBuildEventSource.Log.ExecuteTaskYieldStart(_taskLoggingContext.TaskName, _taskLoggingContext.BuildEventContext.TaskId); builderCallback.Yield(); } @@ -386,7 +386,7 @@ public void Reacquire() { IRequestBuilderCallback builderCallback = _requestEntry.Builder as IRequestBuilderCallback; ErrorUtilities.VerifyThrow(_yieldThreadId != -1, "Cannot call Reacquire() before Yield()."); - ErrorUtilities.VerifyThrow(_yieldThreadId == Thread.CurrentThread.ManagedThreadId, "Cannot call Reacquire() on thread {0} when Yield() was called on thread {1}", Thread.CurrentThread.ManagedThreadId, _yieldThreadId); + ErrorUtilities.VerifyThrow(_yieldThreadId == Environment.CurrentManagedThreadId, "Cannot call Reacquire() on thread {0} when Yield() was called on thread {1}", Environment.CurrentManagedThreadId, _yieldThreadId); MSBuildEventSource.Log.ExecuteTaskYieldStop(_taskLoggingContext.TaskName, _taskLoggingContext.BuildEventContext.TaskId); MSBuildEventSource.Log.ExecuteTaskReacquireStart(_taskLoggingContext.TaskName, _taskLoggingContext.BuildEventContext.TaskId); builderCallback.Reacquire(); diff --git a/src/Build/BackEnd/Components/Scheduler/ScheduleResponse.cs b/src/Build/BackEnd/Components/Scheduler/ScheduleResponse.cs index bc5e8f86256..c335c2cebf1 100644 --- a/src/Build/BackEnd/Components/Scheduler/ScheduleResponse.cs +++ b/src/Build/BackEnd/Components/Scheduler/ScheduleResponse.cs @@ -237,26 +237,26 @@ public override string ToString() { case ScheduleActionType.ReportResults: case ScheduleActionType.ResumeExecution: - return String.Format(CultureInfo.CurrentCulture, "Act: {0} Node: {1} Request: {2}", Action, NodeId, Unblocker.BlockedRequestId); + return $"Act: {Action} Node: {NodeId} Request: {Unblocker.BlockedRequestId}"; case ScheduleActionType.Schedule: - return String.Format(CultureInfo.CurrentCulture, "Act: {0} Node: {1} Request: {2} Parent {3}", Action, NodeId, BuildRequest.GlobalRequestId, BuildRequest.ParentGlobalRequestId); + return $"Act: {Action} Node: {NodeId} Request: {BuildRequest.GlobalRequestId} Parent {BuildRequest.ParentGlobalRequestId}"; case ScheduleActionType.ScheduleWithConfiguration: - return String.Format(CultureInfo.CurrentCulture, "Act: {0} Node: {1} Request: {2} Parent {3} Configuration: {4}", Action, NodeId, BuildRequest.GlobalRequestId, BuildRequest.ParentGlobalRequestId, BuildRequest.ConfigurationId); + return $"Act: {Action} Node: {NodeId} Request: {BuildRequest.GlobalRequestId} Parent {BuildRequest.ParentGlobalRequestId} Configuration: {BuildRequest.ConfigurationId}"; case ScheduleActionType.CircularDependency: - return String.Format(CultureInfo.CurrentCulture, "Act: {0} Node: {1} Request: {2} Parent {3} Configuration: {4}", Action, NodeId, BuildRequest.GlobalRequestId, BuildRequest.ParentGlobalRequestId, BuildRequest.ConfigurationId); + return $"Act: {Action} Node: {NodeId} Request: {BuildRequest.GlobalRequestId} Parent {BuildRequest.ParentGlobalRequestId} Configuration: {BuildRequest.ConfigurationId}"; case ScheduleActionType.SubmissionComplete: - return String.Format(CultureInfo.CurrentCulture, "Act: {0} Submission: {1}", Action, BuildResult.SubmissionId); + return $"Act: {Action} Submission: {BuildResult.SubmissionId}"; case ScheduleActionType.CreateNode: - return String.Format(CultureInfo.CurrentCulture, "Act: {0} Count: {1}", Action, NumberOfNodesToCreate); + return $"Act: {Action} Count: {NumberOfNodesToCreate}"; case ScheduleActionType.NoAction: default: - return String.Format(CultureInfo.CurrentCulture, "Act: {0}", Action); + return $"Act: {Action}"; } } } diff --git a/src/Build/BackEnd/Components/Scheduler/Scheduler.cs b/src/Build/BackEnd/Components/Scheduler/Scheduler.cs index 74adf85158f..2d5f3f89c20 100644 --- a/src/Build/BackEnd/Components/Scheduler/Scheduler.cs +++ b/src/Build/BackEnd/Components/Scheduler/Scheduler.cs @@ -2434,7 +2434,7 @@ private void WriteNodeUtilizationGraphLine(ILoggingService loggingService, Build bool haveNonIdleNode = false; StringBuilder stringBuilder = new StringBuilder(64); - stringBuilder.AppendFormat("{0}: ", previousEventTime.Ticks); + stringBuilder.Append(previousEventTime.Ticks).Append(": "); for (int i = 0; i < currentWork.Length; i++) { if (currentWork[i] == invalidWorkId) @@ -2570,7 +2570,7 @@ private void TraceScheduler(string format, params object[] stuff) FileUtilities.EnsureDirectoryExists(_debugDumpPath); using StreamWriter file = FileUtilities.OpenWrite(String.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerTrace_{0}.txt"), Process.GetCurrentProcess().Id), append: true); - file.Write("{0}({1})-{2}: ", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId, _schedulingData.EventTime.Ticks); + file.Write("{0}({1})-{2}: ", Thread.CurrentThread.Name, Environment.CurrentManagedThreadId, _schedulingData.EventTime.Ticks); file.WriteLine(format, stuff); file.Flush(); } @@ -2817,7 +2817,7 @@ private void DumpRequestSpec(StreamWriter file, SchedulableRequest request, int request.State, buildRequest.ConfigurationId, _configCache[buildRequest.ConfigurationId].ProjectFullPath, - string.Join(", ", buildRequest.Targets.ToArray())); + string.Join(", ", buildRequest.Targets)); } /// diff --git a/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs b/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs index c15b5a25063..19a63a6eb5f 100644 --- a/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs +++ b/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs @@ -363,9 +363,8 @@ private void DetermineExpensiveConfigs() /// private void ReadHierarchy(StreamReader file) { - while (!file.EndOfStream) + while (file.ReadLine() is string line) { - string line = file.ReadLine(); if (line.Length == 0) { return; @@ -394,9 +393,8 @@ private void ReadHierarchy(StreamReader file) /// private void ReadTimes(StreamReader file) { - while (!file.EndOfStream) + while (file.ReadLine() is string line) { - string line = file.ReadLine(); if (line.Length == 0) { return; diff --git a/src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs b/src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs index a9ea6b37548..1d6ec92f64c 100644 --- a/src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs +++ b/src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs @@ -136,7 +136,7 @@ public virtual SdkResult ResolveSdk(int submissionId, SdkReference sdk, LoggingC // // Overall, while Sdk resolvers look like a general plug-in system, there are good reasons why some of the logic is hard-coded. // It's not really meant to be modified outside of very special/internal scenarios. -#if NETCOREAPP +#if NET if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_10)) { if (TryResolveSdkUsingSpecifiedResolvers( @@ -479,7 +479,7 @@ private void RegisterResolversManifests(ElementLocation location) _manifestToResolvers = new Dictionary>(); SdkResolverManifest sdkDefaultResolversManifest = null; -#if NETCOREAPP +#if NET if (!ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_10)) #endif { diff --git a/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs b/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs index f90f99d14d3..d2c22d49f76 100644 --- a/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs +++ b/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs @@ -869,7 +869,7 @@ public override int GetHashCode() /// String representation of the object public override string ToString() { - return String.Format(CultureInfo.CurrentCulture, "{0} {1} {2} {3}", _configId, _projectFullPath, _toolsVersion, _globalProperties); + return $"{_configId} {_projectFullPath} {_toolsVersion} {_globalProperties}"; } /// diff --git a/src/Build/BuildCheck/Acquisition/BuildCheckAcquisitionModule.cs b/src/Build/BuildCheck/Acquisition/BuildCheckAcquisitionModule.cs index ccedf3bab7f..286e1bf9073 100644 --- a/src/Build/BuildCheck/Acquisition/BuildCheckAcquisitionModule.cs +++ b/src/Build/BuildCheck/Acquisition/BuildCheckAcquisitionModule.cs @@ -40,8 +40,8 @@ public List CreateCheckFactories( assembly = Assembly.LoadFrom(checkAcquisitionData.AssemblyPath); #endif - IList availableTypes = assembly.GetExportedTypes(); - IList checkTypes = availableTypes.Where(t => typeof(Check).IsAssignableFrom(t)).ToArray(); + Type[] availableTypes = assembly.GetExportedTypes(); + Type[] checkTypes = availableTypes.Where(t => typeof(Check).IsAssignableFrom(t)).ToArray(); foreach (Type checkCandidate in checkTypes) { @@ -49,7 +49,7 @@ public List CreateCheckFactories( checkContext.DispatchAsComment(MessageImportance.Normal, "CustomCheckRegistered", checkCandidate.Name, checkCandidate.Assembly); } - if (availableTypes.Count != checkTypes.Count) + if (availableTypes.Length != checkTypes.Length) { availableTypes.Except(checkTypes).ToList() .ForEach(t => checkContext.DispatchAsComment(MessageImportance.Normal, "CustomCheckBaseTypeNotAssignable", t.Name, t.Assembly)); diff --git a/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigFile.cs b/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigFile.cs index e40d33efb34..1039bfa67cc 100644 --- a/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigFile.cs +++ b/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigFile.cs @@ -23,23 +23,16 @@ internal partial class EditorConfigFile // Matches EditorConfig property such as "indent_style = space", see https://editorconfig.org for details private const string s_propertyMatcherPattern = @"^\s*([\w\.\-_]+)\s*[=:]\s*(.*?)\s*([#;].*)?$"; -#if NETCOREAPP - +#if NET [GeneratedRegex(s_sectionMatcherPattern)] - private static partial Regex GetSectionMatcherRegex(); + private static partial Regex SectionMatcherRegex { get; } [GeneratedRegex(s_propertyMatcherPattern)] - private static partial Regex GetPropertyMatcherRegex(); - + private static partial Regex PropertyMatcherRegex { get; } #else - private static readonly Regex s_sectionMatcher = new Regex(s_sectionMatcherPattern, RegexOptions.Compiled); - - private static readonly Regex s_propertyMatcher = new Regex(s_propertyMatcherPattern, RegexOptions.Compiled); - - private static Regex GetSectionMatcherRegex() => s_sectionMatcher; - - private static Regex GetPropertyMatcherRegex() => s_propertyMatcher; + private static Regex SectionMatcherRegex { get; } = new Regex(s_sectionMatcherPattern, RegexOptions.Compiled); + private static Regex PropertyMatcherRegex { get; } = new Regex(s_propertyMatcherPattern, RegexOptions.Compiled); #endif internal Section GlobalSection { get; } @@ -90,12 +83,12 @@ internal static EditorConfigFile Parse(string text) continue; } - var sectionMatches = GetSectionMatcherRegex().Matches(line); - if (sectionMatches.Count > 0 && sectionMatches[0].Groups.Count > 0) + var sectionMatch = SectionMatcherRegex.Match(line); + if (sectionMatch.Success && sectionMatch.Groups.Count > 0) { addNewSection(); - var sectionName = sectionMatches[0].Groups[1].Value; + var sectionName = sectionMatch.Groups[1].Value; Debug.Assert(!string.IsNullOrEmpty(sectionName)); activeSectionName = sectionName; @@ -103,11 +96,11 @@ internal static EditorConfigFile Parse(string text) continue; } - var propMatches = GetPropertyMatcherRegex().Matches(line); - if (propMatches.Count > 0 && propMatches[0].Groups.Count > 1) + var propMatch = PropertyMatcherRegex.Match(line); + if (propMatch.Success && propMatch.Groups.Count > 1) { - var key = propMatches[0].Groups[1].Value.ToLower(); - var value = propMatches[0].Groups[2].Value; + var key = propMatch.Groups[1].Value.ToLower(); + var value = propMatch.Groups[2].Value; Debug.Assert(!string.IsNullOrEmpty(key)); Debug.Assert(key == key.Trim()); diff --git a/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs b/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs index 56b0842acf7..393a9f7612c 100644 --- a/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs +++ b/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs @@ -41,12 +41,7 @@ internal List DiscoverEditorConfigFiles(string filePath) { var editorConfig = _editorConfigFileCache.GetOrAdd(editorConfigFilePath, (key) => { - using (FileStream stream = new FileStream(editorConfigFilePath, FileMode.Open, System.IO.FileAccess.Read, FileShare.Read)) - { - using StreamReader sr = new StreamReader(editorConfigFilePath); - var editorConfigfileContent = sr.ReadToEnd(); - return EditorConfigFile.Parse(editorConfigfileContent); - } + return EditorConfigFile.Parse(File.ReadAllText(editorConfigFilePath)); }); editorConfigDataFromFilesList.Add(editorConfig); diff --git a/src/Build/Collections/CopyOnReadEnumerable.cs b/src/Build/Collections/CopyOnReadEnumerable.cs index 6ed48e0eed1..056f5804cfd 100644 --- a/src/Build/Collections/CopyOnReadEnumerable.cs +++ b/src/Build/Collections/CopyOnReadEnumerable.cs @@ -62,7 +62,7 @@ public IEnumerator GetEnumerator() { List list; -#if NETCOREAPP +#if NET if (_backingEnumerable.TryGetNonEnumeratedCount(out int count)) { #else diff --git a/src/Build/Construction/ProjectElementContainer.cs b/src/Build/Construction/ProjectElementContainer.cs index c8fabf3559e..6c6584e52c7 100644 --- a/src/Build/Construction/ProjectElementContainer.cs +++ b/src/Build/Construction/ProjectElementContainer.cs @@ -584,7 +584,7 @@ private static string GetElementIndentation(XmlElementWithLocation xmlElement) var leadingWhiteSpace = xmlElement.PreviousSibling.Value; - var lastIndexOfNewLine = leadingWhiteSpace.LastIndexOf("\n", StringComparison.Ordinal); + var lastIndexOfNewLine = leadingWhiteSpace.LastIndexOf('\n'); if (lastIndexOfNewLine == -1) { diff --git a/src/Build/Construction/ProjectRootElement.cs b/src/Build/Construction/ProjectRootElement.cs index cc416e5d409..95e03d8bedf 100644 --- a/src/Build/Construction/ProjectRootElement.cs +++ b/src/Build/Construction/ProjectRootElement.cs @@ -43,7 +43,7 @@ namespace Microsoft.Build.Construction /// to control its lifetime and not be surprised by edits via another project collection. /// [DebuggerDisplay("{FullPath} #Children={Count} DefaultTargets={DefaultTargets} ToolsVersion={ToolsVersion} InitialTargets={InitialTargets} ExplicitlyLoaded={IsExplicitlyLoaded}")] - public class ProjectRootElement : ProjectElementContainer + public partial class ProjectRootElement : ProjectElementContainer { // Constants for default (empty) project file. private const string EmptyProjectFileContent = "{0}\r\n"; @@ -58,10 +58,18 @@ public class ProjectRootElement : ProjectElementContainer private static readonly ProjectRootElementCacheBase.OpenProjectRootElement s_openLoaderPreserveFormattingDelegate = OpenLoaderPreserveFormatting; + private const string XmlDeclarationPattern = @"\A\s*\<\?\s*xml.*\?\>\s*\Z"; + /// /// Used to determine if a file is an empty XML file if it ONLY contains an XML declaration like <?xml version="1.0" encoding="utf-8"?>. /// - private static readonly Lazy XmlDeclarationRegEx = new Lazy(() => new Regex(@"\A\s*\<\?\s*xml.*\?\>\s*\Z"), isThreadSafe: true); +#if NET + [GeneratedRegex(XmlDeclarationPattern)] + private static partial Regex XmlDeclarationRegex { get; } +#else + private static Regex XmlDeclarationRegex => s_xmlDeclarationRegex ??= new Regex(XmlDeclarationPattern); + private static Regex s_xmlDeclarationRegex; +#endif /// /// The default encoding to use / assume for a new project. @@ -1961,9 +1969,9 @@ internal static bool IsEmptyXmlFile(string path) string contents = File.ReadAllText(path); - // If the file is only whitespace or the XML declaration then it empty + // If the file is only whitespace or the XML declaration then it is empty // - return String.IsNullOrEmpty(contents) || XmlDeclarationRegEx.Value.IsMatch(contents); + return String.IsNullOrEmpty(contents) || XmlDeclarationRegex.IsMatch(contents); } catch (Exception) { diff --git a/src/Build/Construction/ProjectTargetElement.cs b/src/Build/Construction/ProjectTargetElement.cs index db079f86773..4fdf9c21495 100644 --- a/src/Build/Construction/ProjectTargetElement.cs +++ b/src/Build/Construction/ProjectTargetElement.cs @@ -106,7 +106,7 @@ public string Name string unescapedValue = EscapingUtilities.UnescapeAll(value); - int indexOfSpecialCharacter = unescapedValue.IndexOfAny(XMakeElements.InvalidTargetNameCharacters); + int indexOfSpecialCharacter = unescapedValue.AsSpan().IndexOfAny(XMakeElements.InvalidTargetNameCharacters); if (indexOfSpecialCharacter >= 0) { ErrorUtilities.ThrowArgument("OM_NameInvalid", unescapedValue, unescapedValue[indexOfSpecialCharacter]); diff --git a/src/Build/Construction/Solution/ProjectInSolution.cs b/src/Build/Construction/Solution/ProjectInSolution.cs index 60f69b97b54..4eb3b5e82bd 100644 --- a/src/Build/Construction/Solution/ProjectInSolution.cs +++ b/src/Build/Construction/Solution/ProjectInSolution.cs @@ -2,9 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; +using System.Linq; using System.Security; using System.Text; using System.Xml; @@ -16,8 +19,6 @@ using ProjectFileErrorUtilities = Microsoft.Build.Shared.ProjectFileErrorUtilities; using BuildEventFileInfo = Microsoft.Build.Shared.BuildEventFileInfo; using ErrorUtilities = Microsoft.Build.Shared.ErrorUtilities; -using System.Collections.ObjectModel; -using System.Linq; #nullable disable @@ -83,7 +84,12 @@ public sealed class ProjectInSolution /// /// Characters that need to be cleansed from a project name. /// - private static readonly char[] s_charsToCleanse = { '%', '$', '@', ';', '.', '(', ')', '\'' }; +#if NET + private static readonly SearchValues s_charsToCleanse = SearchValues.Create( +#else + private static readonly char[] s_charsToCleanse = ( +#endif + ['%', '$', '@', ';', '.', '(', ')', '\'']); /// /// Project names that need to be disambiguated when forming a target name @@ -501,12 +507,25 @@ private static string CleanseProjectName(string projectName) // If there are no special chars, just return the original string immediately. // Don't even instantiate the StringBuilder. - int indexOfChar = projectName.IndexOfAny(s_charsToCleanse); + int indexOfChar = projectName.AsSpan().IndexOfAny(s_charsToCleanse); if (indexOfChar == -1) { return projectName; } +#if NET + return string.Create(projectName.Length, (projectName, indexOfChar), static (dest, state) => + { + state.projectName.AsSpan().CopyTo(dest); + int pos = state.indexOfChar; + do + { + dest[pos] = cleanCharacter; + dest = dest.Slice(pos + 1); + } + while ((pos = dest.IndexOfAny(s_charsToCleanse)) >= 0); + }); +#else // This is where we're going to work on the final string to return to the caller. var cleanProjectName = new StringBuilder(projectName); @@ -517,6 +536,7 @@ private static string CleanseProjectName(string projectName) } return cleanProjectName.ToString(); +#endif } /// @@ -561,7 +581,7 @@ private static bool ElementContainsInvalidNamespaceDefitions(XmlElement mainProj return false; } - #endregion +#endregion #region Constants diff --git a/src/Build/Construction/Solution/SolutionFile.cs b/src/Build/Construction/Solution/SolutionFile.cs index 63ce5b9dcee..c964a674d5d 100644 --- a/src/Build/Construction/Solution/SolutionFile.cs +++ b/src/Build/Construction/Solution/SolutionFile.cs @@ -35,38 +35,47 @@ namespace Microsoft.Build.Construction /// This class contains the functionality to parse a solution file and return a corresponding /// MSBuild project file containing the projects and dependencies defined in the solution. /// - public sealed class SolutionFile + public sealed partial class SolutionFile { #region Solution specific constants // An example of a project line looks like this: // Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassLibrary1", "ClassLibrary1\ClassLibrary1.csproj", "{05A5AD00-71B5-4612-AF2F-9EA9121C4111}" - private static readonly Lazy s_crackProjectLine = new Lazy( - () => new Regex( - "^" // Beginning of line - + "Project\\(\"(?.*)\"\\)" - + "\\s*=\\s*" // Any amount of whitespace plus "=" plus any amount of whitespace - + "\"(?.*)\"" - + "\\s*,\\s*" // Any amount of whitespace plus "," plus any amount of whitespace - + "\"(?.*)\"" - + "\\s*,\\s*" // Any amount of whitespace plus "," plus any amount of whitespace - + "\"(?.*)\"" - + "$", // End-of-line - RegexOptions.Compiled)); + private const string CrackProjectLinePattern = + "^" // Beginning of line + + "Project\\(\"(?.*)\"\\)" + + "\\s*=\\s*" // Any amount of whitespace plus "=" plus any amount of whitespace + + "\"(?.*)\"" + + "\\s*,\\s*" // Any amount of whitespace plus "," plus any amount of whitespace + + "\"(?.*)\"" + + "\\s*,\\s*" // Any amount of whitespace plus "," plus any amount of whitespace + + "\"(?.*)\"" + + "$"; // End-of-line // An example of a property line looks like this: // AspNetCompiler.VirtualPath = "/webprecompile" // Because website projects now include the target framework moniker as // one of their properties, may now have '=' in it. - - private static readonly Lazy s_crackPropertyLine = new Lazy( - () => new Regex( - "^" // Beginning of line - + "(?[^=]*)" - + "\\s*=\\s*" // Any amount of whitespace plus "=" plus any amount of whitespace - + "(?.*)" - + "$", // End-of-line - RegexOptions.Compiled)); + private const string CrackPropertyLinePattern = + "^" // Beginning of line + + "(?[^=]*)" + + "\\s*=\\s*" // Any amount of whitespace plus "=" plus any amount of whitespace + + "(?.*)" + + "$"; // End-of-line + +#if NET + [GeneratedRegex(CrackProjectLinePattern)] + private static partial Regex CrackProjectLineRegex { get; } + + [GeneratedRegex(CrackPropertyLinePattern)] + private static partial Regex CrackPropertyLineRegex { get; } +#else + private static Regex CrackProjectLineRegex => s_crackProjectLineRegex ??= new Regex(CrackProjectLinePattern, RegexOptions.Compiled); + private static Regex CrackPropertyLineRegex => s_crackPropertyLineRegex ??= new Regex(CrackPropertyLinePattern, RegexOptions.Compiled); + + private static Regex s_crackProjectLineRegex; + private static Regex s_crackPropertyLineRegex; +#endif internal const int slnFileMinUpgradableVersion = 7; // Minimum version for MSBuild to give a nice message internal const int slnFileMinVersion = 9; // Minimum version for MSBuild to actually do anything useful @@ -89,7 +98,7 @@ public sealed class SolutionFile private const string sharedProjectGuid = "{D954291E-2A0B-460D-934E-DC6B0785DB48}"; private const char CommentStartChar = '#'; - #endregion +#endregion #region Member data private string _solutionFile; // Could be absolute or relative path to the .SLN file. private string _solutionFilterFile; // Could be absolute or relative path to the .SLNF file. @@ -121,7 +130,7 @@ public sealed class SolutionFile // TODO: Unify to NativeMethodsShared.OSUsesCaseSensitive paths // when possible. - private static StringComparer _pathComparer = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) + private static readonly StringComparer _pathComparer = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; @@ -561,7 +570,12 @@ internal static void GetSolutionFileAndVisualStudioMajorVersions(string solution if (line.Trim().StartsWith(slnFileHeaderNoVersion, StringComparison.Ordinal)) { // Found it. Validate the version. - string fileVersionFromHeader = line.Substring(slnFileHeaderNoVersion.Length); + var fileVersionFromHeader = +#if NET + line.AsSpan(slnFileHeaderNoVersion.Length); +#else + line.Substring(slnFileHeaderNoVersion.Length); +#endif if (!System.Version.TryParse(fileVersionFromHeader, out Version version)) { @@ -1060,7 +1074,7 @@ private void ParseProject(string firstLine) { // This should be a dependency. The GUID identifying the parent project should // be both the property name and the property value. - Match match = s_crackPropertyLine.Value.Match(line); + Match match = CrackPropertyLineRegex.Match(line); ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(match.Success, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(FullPath, _currentLineNumber, 0), "SolutionParseProjectDepGuidError", proj.ProjectName); @@ -1078,7 +1092,7 @@ private void ParseProject(string firstLine) line = ReadLine(); while ((line?.StartsWith("EndProjectSection", StringComparison.Ordinal) == false)) { - Match match = s_crackPropertyLine.Value.Match(line); + Match match = CrackPropertyLineRegex.Match(line); ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(match.Success, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(FullPath, _currentLineNumber, 0), "SolutionParseWebProjectPropertiesError", proj.ProjectName); @@ -1289,7 +1303,7 @@ private void ValidateProjectRelativePath(ProjectInSolution proj) ErrorUtilities.VerifyThrow(proj.RelativePath != null, "Project relative path cannot be null."); // Verify the relative path does not contain invalid characters - ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(proj.RelativePath.IndexOfAny(Path.GetInvalidPathChars()) == -1, + ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(proj.RelativePath.AsSpan().IndexOfAny(MSBuildConstants.InvalidPathChars) < 0, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(FullPath, _currentLineNumber, 0), "SolutionParseInvalidProjectFileNameCharacters", @@ -1356,7 +1370,13 @@ private static void ParseAspNetCompilerProperty( string configurationName = propertyName.Substring(0, indexOfFirstDot); // The rest of it is the actual property name. - string aspNetPropertyName = ((propertyName.Length - indexOfFirstDot) > 0) ? propertyName.Substring(indexOfFirstDot + 1, propertyName.Length - indexOfFirstDot - 1) : ""; + var aspNetPropertyName = ((propertyName.Length - indexOfFirstDot) > 0) ? +#if NET + propertyName.AsSpan(indexOfFirstDot + 1) : +#else + propertyName.Substring(indexOfFirstDot + 1) : +#endif + ""; // And the part after the sign is the property value (which was parsed out for us prior // to calling this method). @@ -1391,49 +1411,19 @@ private static void ParseAspNetCompilerProperty( } // Update the appropriate field within the parameters struct. - if (aspNetPropertyName == "AspNetCompiler.VirtualPath") - { - aspNetCompilerParameters.aspNetVirtualPath = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.PhysicalPath") - { - aspNetCompilerParameters.aspNetPhysicalPath = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.TargetPath") - { - aspNetCompilerParameters.aspNetTargetPath = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.ForceOverwrite") - { - aspNetCompilerParameters.aspNetForce = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.Updateable") - { - aspNetCompilerParameters.aspNetUpdateable = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.Debug") + switch (aspNetPropertyName) { - aspNetCompilerParameters.aspNetDebug = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.KeyFile") - { - aspNetCompilerParameters.aspNetKeyFile = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.KeyContainer") - { - aspNetCompilerParameters.aspNetKeyContainer = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.DelaySign") - { - aspNetCompilerParameters.aspNetDelaySign = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.AllowPartiallyTrustedCallers") - { - aspNetCompilerParameters.aspNetAPTCA = propertyValue; - } - else if (aspNetPropertyName == "AspNetCompiler.FixedNames") - { - aspNetCompilerParameters.aspNetFixedNames = propertyValue; + case "AspNetCompiler.VirtualPath": aspNetCompilerParameters.aspNetVirtualPath = propertyValue; break; + case "AspNetCompiler.PhysicalPath": aspNetCompilerParameters.aspNetPhysicalPath = propertyValue; break; + case "AspNetCompiler.TargetPath": aspNetCompilerParameters.aspNetTargetPath = propertyValue; break; + case "AspNetCompiler.ForceOverwrite": aspNetCompilerParameters.aspNetForce = propertyValue; break; + case "AspNetCompiler.Updateable": aspNetCompilerParameters.aspNetUpdateable = propertyValue; break; + case "AspNetCompiler.Debug": aspNetCompilerParameters.aspNetDebug = propertyValue; break; + case "AspNetCompiler.KeyFile": aspNetCompilerParameters.aspNetKeyFile = propertyValue; break; + case "AspNetCompiler.KeyContainer": aspNetCompilerParameters.aspNetKeyContainer = propertyValue; break; + case "AspNetCompiler.DelaySign": aspNetCompilerParameters.aspNetDelaySign = propertyValue; break; + case "AspNetCompiler.AllowPartiallyTrustedCallers": aspNetCompilerParameters.aspNetAPTCA = propertyValue; break; + case "AspNetCompiler.FixedNames": aspNetCompilerParameters.aspNetFixedNames = propertyValue; break; } // Store the updated parameters struct back into the hashtable by configuration name. @@ -1510,7 +1500,7 @@ internal void ParseFirstProjectLine( string firstLine, ProjectInSolution proj) { - Match match = s_crackProjectLine.Value.Match(firstLine); + Match match = CrackProjectLineRegex.Match(firstLine); ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(match.Success, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(FullPath, _currentLineNumber, 0), "SolutionParseProjectError"); @@ -1523,7 +1513,7 @@ internal void ParseFirstProjectLine( // This allows us to at least generate reasonable target names etc. instead of crashing. if (String.IsNullOrEmpty(proj.ProjectName)) { - proj.ProjectName = "EmptyProjectName." + Guid.NewGuid(); + proj.ProjectName = $"EmptyProjectName.{Guid.NewGuid()}"; } // Validate project relative path @@ -1610,7 +1600,7 @@ internal void ParseNestedProjects() continue; } - Match match = s_crackPropertyLine.Value.Match(str); + Match match = CrackPropertyLineRegex.Match(str); ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(match.Success, "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(FullPath, _currentLineNumber, 0), "SolutionParseNestedProjectError"); @@ -1769,16 +1759,14 @@ internal void ProcessProjectConfigurationSection(Dictionary rawP { // The "ActiveCfg" entry defines the active project configuration in the given solution configuration // This entry must be present for every possible solution configuration/project combination. - string entryNameActiveConfig = string.Format(CultureInfo.InvariantCulture, "{0}.{1}.ActiveCfg", - project.ProjectGuid, solutionConfiguration.FullName); + string entryNameActiveConfig = $"{project.ProjectGuid}.{solutionConfiguration.FullName}.ActiveCfg"; // The "Build.0" entry tells us whether to build the project configuration in the given solution configuration. // Technically, it specifies a configuration name of its own which seems to be a remnant of an initial, // more flexible design of solution configurations (as well as the '.0' suffix - no higher values are ever used). // The configuration name is not used, and the whole entry means "build the project configuration" // if it's present in the solution file, and "don't build" if it's not. - string entryNameBuild = string.Format(CultureInfo.InvariantCulture, "{0}.{1}.Build.0", - project.ProjectGuid, solutionConfiguration.FullName); + string entryNameBuild = $"{project.ProjectGuid}.{solutionConfiguration.FullName}.Build.0"; if (rawProjectConfigurationsEntries.TryGetValue(entryNameActiveConfig, out string configurationPlatform)) { @@ -1904,6 +1892,6 @@ internal string GetProjectRelativePathByGuid(string projectGuid) return null; } - #endregion +#endregion } // class SolutionFile } // namespace Microsoft.Build.Construction diff --git a/src/Build/Construction/Solution/SolutionProjectGenerator.cs b/src/Build/Construction/Solution/SolutionProjectGenerator.cs index 760fcb390f3..fdc30f97fad 100644 --- a/src/Build/Construction/Solution/SolutionProjectGenerator.cs +++ b/src/Build/Construction/Solution/SolutionProjectGenerator.cs @@ -860,7 +860,7 @@ private ProjectInstance CreateTraversalInstance(string wrapperProjectToolsVersio traversalProject.ToolsVersion = wrapperProjectToolsVersion; traversalProject.DefaultTargets = "Build"; traversalProject.InitialTargets = "ValidateSolutionConfiguration;ValidateToolsVersions;ValidateProjects"; - traversalProject.FullPath = _solutionFile.FullPath + ".metaproj"; + traversalProject.FullPath = $"{_solutionFile.FullPath}.metaproj"; // Add default solution configuration/platform names in case the user doesn't specify them on the command line AddConfigurationPlatformDefaults(traversalProject); @@ -956,7 +956,7 @@ private ProjectInstance CreateTraversalInstance(string wrapperProjectToolsVersio localFile = Path.Combine(escapedSolutionDirectory, "after." + escapedSolutionFile + ".targets"); ProjectImportElement importAfterLocal = traversalProject.CreateImportElement(localFile); - importAfterLocal.Condition = @"exists('" + localFile + "')"; + importAfterLocal.Condition = $@"exists('{localFile}')"; // Put locals second so they can override globals if they want traversalProject.PrependChild(importBeforeLocal); @@ -990,7 +990,7 @@ private ProjectInstance CreateTraversalInstance(string wrapperProjectToolsVersio // For debugging purposes: some information is lost when evaluating into a project instance, // so make it possible to see what we have at this point. string path = traversalProject.FullPath; - string metaprojectPath = _solutionFile.FullPath + ".metaproj.tmp"; + string metaprojectPath = $"{_solutionFile.FullPath}.metaproj.tmp"; EmitMetaproject(traversalProject, metaprojectPath); traversalProject.FullPath = path; @@ -1336,7 +1336,7 @@ private static void AddMetaprojectTargetForManagedProject(ProjectInstance traver string outputItemAsItem = null; if (!String.IsNullOrEmpty(outputItem)) { - outputItemAsItem = "@(" + outputItem + ")"; + outputItemAsItem = $"@({outputItem})"; } ProjectTargetInstance target = metaprojectInstance.AddTarget(targetName ?? "Build", String.Empty, String.Empty, outputItemAsItem, null, String.Empty, String.Empty, String.Empty, String.Empty, false /* legacy target returns behaviour */); @@ -1383,7 +1383,7 @@ private static void AddProjectBuildTask(ProjectInstance traversalProject, Projec /// private void AddMetaprojectBuildTask(ProjectInSolution project, ProjectTargetInstance target, string targetToBuild, string outputItem) { - ProjectTaskInstance task = target.AddTask("MSBuild", Strings.WeakIntern("'%(ProjectReference.Identity)' == '" + GetMetaprojectName(project) + "'"), String.Empty); + ProjectTaskInstance task = target.AddTask("MSBuild", Strings.WeakIntern($"'%(ProjectReference.Identity)' == '{GetMetaprojectName(project)}'"), String.Empty); task.SetParameter("Projects", "@(ProjectReference)"); if (targetToBuild != null) @@ -1966,7 +1966,7 @@ private static void AddTraversalReferencesTarget(ProjectInstance traversalProjec string outputItemAsItem = null; if (!String.IsNullOrEmpty(outputItem)) { - outputItemAsItem = "@(" + outputItem + ")"; + outputItemAsItem = $"@({outputItem})"; } string correctedTargetName = targetName ?? "Build"; @@ -2039,13 +2039,13 @@ private void AddTraversalTargetForProject(ProjectInstance traversalProject, Proj if (!String.IsNullOrEmpty(outputItem)) { outputItemName = MakeIntoSafeItemName(baseProjectName) + outputItem; - outputItemAsItem = "@(" + outputItemName + ")"; + outputItemAsItem = $"@({outputItemName})"; } ProjectTargetInstance targetElement = traversalProject.AddTarget(actualTargetName, null, null, outputItemAsItem, null, null, null, null, null, false /* legacy target returns behaviour */); if (canBuildDirectly) { - AddProjectBuildTask(traversalProject, projectConfiguration, targetElement, targetToBuild, "@(ProjectReference)", "'%(ProjectReference.Identity)' == '" + EscapingUtilities.Escape(project.AbsolutePath) + "'", outputItemName); + AddProjectBuildTask(traversalProject, projectConfiguration, targetElement, targetToBuild, "@(ProjectReference)", $"'%(ProjectReference.Identity)' == '{EscapingUtilities.Escape(project.AbsolutePath)}'", outputItemName); } else { diff --git a/src/Build/Definition/Project.cs b/src/Build/Definition/Project.cs index 1348a7cffcc..247a63b1036 100644 --- a/src/Build/Definition/Project.cs +++ b/src/Build/Definition/Project.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -56,7 +57,12 @@ public class Project : ILinkableObject /// /// * and ? are invalid file name characters, but they occur in globs as wild cards. /// - private static readonly char[] s_invalidGlobChars = FileUtilities.InvalidFileNameChars.Where(c => c != '*' && c != '?' && c != '/' && c != '\\' && c != ':').ToArray(); +#if NET + private static readonly SearchValues s_invalidGlobChars = SearchValues.Create( +#else + private static readonly char[] s_invalidGlobChars = ( +#endif + FileUtilities.InvalidFileNameCharsArray.Where(c => c is not ('*' or '?' or '/' or '\\' or ':')).ToArray()); /// /// Context to log messages and events in. @@ -2618,7 +2624,7 @@ private GlobResult BuildGlobResultFromIncludeItem(ProjectItemElement itemElement { var includeItemspec = new EvaluationItemSpec(itemElement.Include, _data.Expander, itemElement.IncludeLocation, itemElement.ContainingProject.DirectoryPath); - ImmutableArray includeGlobFragments = includeItemspec.Fragments.Where(f => f is GlobFragment && f.TextFragment.IndexOfAny(s_invalidGlobChars) == -1).ToImmutableArray(); + ItemSpecFragment[] includeGlobFragments = includeItemspec.Fragments.Where(f => f is GlobFragment && f.TextFragment.AsSpan().IndexOfAny(s_invalidGlobChars) < 0).ToArray(); if (includeGlobFragments.Length == 0) { return null; diff --git a/src/Build/Definition/ToolsetConfigurationReader.cs b/src/Build/Definition/ToolsetConfigurationReader.cs index eb461785d00..204de4952f8 100644 --- a/src/Build/Definition/ToolsetConfigurationReader.cs +++ b/src/Build/Definition/ToolsetConfigurationReader.cs @@ -207,7 +207,7 @@ protected override IEnumerable GetSubToolsetPropertyD protected override Dictionary GetProjectImportSearchPathsTable(string toolsVersion, string os) { Dictionary kindToPathsCache; - var key = toolsVersion + ":" + os; + var key = $"{toolsVersion}:{os}"; if (_projectImportSearchPathsCache.TryGetValue(key, out kindToPathsCache)) { return kindToPathsCache; diff --git a/src/Build/Definition/ToolsetReader.cs b/src/Build/Definition/ToolsetReader.cs index fdc817b930c..2f3ca882084 100644 --- a/src/Build/Definition/ToolsetReader.cs +++ b/src/Build/Definition/ToolsetReader.cs @@ -686,22 +686,26 @@ private MSBuildExtensionsPathReferenceKind(string value) /// public static MSBuildExtensionsPathReferenceKind FindIn(string expression) { - if (expression.IndexOf("$(MSBuildExtensionsPath)") >= 0) + const string PathBase = "$(MSBuildExtensionsPath"; + int pos = expression.IndexOf(PathBase, StringComparison.Ordinal); + if (pos >= 0) { - return MSBuildExtensionsPathReferenceKind.Default; - } - - if (expression.IndexOf("$(MSBuildExtensionsPath32)") >= 0) - { - return MSBuildExtensionsPathReferenceKind.Path32; - } - - if (expression.IndexOf("$(MSBuildExtensionsPath64)") >= 0) - { - return MSBuildExtensionsPathReferenceKind.Path64; + ReadOnlySpan remainder = expression.AsSpan(pos + PathBase.Length); + if (remainder.StartsWith(")".AsSpan())) + { + return Default; + } + else if (remainder.StartsWith("32)".AsSpan())) + { + return Path32; + } + else if (remainder.StartsWith("64)".AsSpan())) + { + return Path64; + } } - return MSBuildExtensionsPathReferenceKind.None; + return None; } } } diff --git a/src/Build/ElementLocation/ElementLocation.cs b/src/Build/ElementLocation/ElementLocation.cs index 8ca5594c946..29879ac6180 100644 --- a/src/Build/ElementLocation/ElementLocation.cs +++ b/src/Build/ElementLocation/ElementLocation.cs @@ -25,7 +25,7 @@ public abstract class ElementLocation : IElementLocation, ITranslatable, IImmuta /// /// The singleton empty element location. /// - private static ElementLocation s_emptyElementLocation = new SmallElementLocation(null, 0, 0); + private static readonly ElementLocation s_emptyElementLocation = new SmallElementLocation(null, 0, 0); /// /// The file from which this particular element originated. It may @@ -214,7 +214,7 @@ private static string GetLocationString(string file, int line, int column) } else if (line != 0) { - locationString = file + " (" + line + ")"; + locationString = $"{file} ({line})"; } else { diff --git a/src/Build/ElementLocation/XmlDocumentWithLocation.cs b/src/Build/ElementLocation/XmlDocumentWithLocation.cs index cfa910da6a8..eb8c3356873 100644 --- a/src/Build/ElementLocation/XmlDocumentWithLocation.cs +++ b/src/Build/ElementLocation/XmlDocumentWithLocation.cs @@ -27,7 +27,7 @@ internal class XmlDocumentWithLocation : XmlDocument /// /// Used to cache tag names in loaded files. /// - private static NameTable s_nameTable = new XmlNameTableThreadSafe(); + private static readonly NameTable s_nameTable = new XmlNameTableThreadSafe(); /// /// Whether we can selectively load as read-only (eg just when in program files directory) diff --git a/src/Build/Evaluation/ConditionEvaluator.cs b/src/Build/Evaluation/ConditionEvaluator.cs index 67455f8b4c4..f6a5f70330f 100644 --- a/src/Build/Evaluation/ConditionEvaluator.cs +++ b/src/Build/Evaluation/ConditionEvaluator.cs @@ -166,7 +166,7 @@ public ConcurrentStack GetOrAdd(string condition, Func s_cachedExpressionTrees = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary s_cachedExpressionTrees = new ConcurrentDictionary(); /// /// For debugging leaks, a way to disable caching expression trees, to reduce noise diff --git a/src/Build/Evaluation/Conditionals/CharacterUtilities.cs b/src/Build/Evaluation/Conditionals/CharacterUtilities.cs index 06e7345b1b0..362815294ea 100644 --- a/src/Build/Evaluation/Conditionals/CharacterUtilities.cs +++ b/src/Build/Evaluation/Conditionals/CharacterUtilities.cs @@ -22,15 +22,10 @@ internal static bool IsSimpleStringChar(char candidate) return IsSimpleStringStart(candidate) || char.IsDigit(candidate); } - internal static bool IsHexAlphabetic(char candidate) - { - return candidate == 'a' || candidate == 'b' || candidate == 'c' || candidate == 'd' || candidate == 'e' || candidate == 'f' || - candidate == 'A' || candidate == 'B' || candidate == 'C' || candidate == 'D' || candidate == 'E' || candidate == 'F'; - } - internal static bool IsHexDigit(char candidate) { - return char.IsDigit(candidate) || IsHexAlphabetic(candidate); + return char.IsDigit(candidate) || ((uint)((candidate | 0x20) - 'a') <= 'f' - 'a'); + // TODO: Is the intent here really to include Unicode digits, or could this be char.IsAsciiHexChar? } } } diff --git a/src/Build/Evaluation/Conditionals/Scanner.cs b/src/Build/Evaluation/Conditionals/Scanner.cs index a9d31698a28..718ae73d23d 100644 --- a/src/Build/Evaluation/Conditionals/Scanner.cs +++ b/src/Build/Evaluation/Conditionals/Scanner.cs @@ -692,11 +692,11 @@ private bool ParseRemaining() private bool ParseSimpleStringOrFunction(int start) { SkipSimpleStringChars(); - if (string.Equals(_expression.Substring(start, _parsePoint - start), "and", StringComparison.OrdinalIgnoreCase)) + if (_expression.AsSpan(start, _parsePoint - start).Equals("and".AsSpan(), StringComparison.OrdinalIgnoreCase)) { _lookahead = Token.And; } - else if (string.Equals(_expression.Substring(start, _parsePoint - start), "or", StringComparison.OrdinalIgnoreCase)) + else if (_expression.AsSpan(start, _parsePoint - start).Equals("or".AsSpan(), StringComparison.OrdinalIgnoreCase)) { _lookahead = Token.Or; } diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index e285a948aa4..63a62895e03 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -809,7 +809,7 @@ private void Evaluate() { if (!String.Equals(entry.Name, "currentsolutionconfigurationcontents", StringComparison.OrdinalIgnoreCase)) { - propertyDump += entry.Name + "=" + entry.EvaluatedValue + "\n"; + propertyDump += $"{entry.Name}={entry.EvaluatedValue}\n"; } } @@ -1886,19 +1886,17 @@ static string EvaluateProperty(string value, IElementLocation location, // Creates a project to set the properties and include the items from an SdkResult private ProjectRootElement CreateProjectForSdkResult(SdkResult sdkResult) { - int propertiesAndItemsHash; - -#if NETCOREAPP - HashCode hash = new HashCode(); +#if NET + HashCode hash = default; #else - propertiesAndItemsHash = -849885975; + int propertiesAndItemsHash = -849885975; #endif if (sdkResult.PropertiesToAdd != null) { foreach (var property in sdkResult.PropertiesToAdd) { -#if NETCOREAPP +#if NET hash.Add(property.Key); hash.Add(property.Value); #else @@ -1911,7 +1909,7 @@ private ProjectRootElement CreateProjectForSdkResult(SdkResult sdkResult) { foreach (var item in sdkResult.ItemsToAdd) { -#if NETCOREAPP +#if NET hash.Add(item.Key); hash.Add(item.Value); #else @@ -1922,12 +1920,12 @@ private ProjectRootElement CreateProjectForSdkResult(SdkResult sdkResult) } } -#if NETCOREAPP - propertiesAndItemsHash = hash.ToHashCode(); +#if NET + int propertiesAndItemsHash = hash.ToHashCode(); #endif // Generate a unique filename for the generated project for each unique set of properties and items. - string projectPath = _projectRootElement.FullPath + ".SdkResolver." + propertiesAndItemsHash + ".proj"; + string projectPath = $"{_projectRootElement.FullPath}.SdkResolver.{propertiesAndItemsHash}.proj"; ProjectRootElement InnerCreate(string _, ProjectRootElementCacheBase __) { @@ -2136,7 +2134,7 @@ private LoadImportsResult ExpandAndLoadImportsFromUnescapedImportExpression(stri // If neither file involved is the project itself, append its path in square brackets if (previouslyImportedAt.ContainingProject != _projectRootElement && importElement.ContainingProject != _projectRootElement) { - parenthesizedProjectLocation = "[" + _projectRootElement.FullPath + "]"; + parenthesizedProjectLocation = $"[{_projectRootElement.FullPath}]"; } // TODO: Detect if the duplicate import came from an SDK attribute _evaluationLoggingContext.LogWarning(null, new BuildEventFileInfo(importLocationInProject), "DuplicateImport", importFileUnescaped, previouslyImportedAt.Location.LocationString, parenthesizedProjectLocation); @@ -2577,7 +2575,7 @@ private void SetAllProjectsProperty() if (_lastModifiedProject != null) { P oldValue = _data.GetProperty(Constants.MSBuildAllProjectsPropertyName); - string streamImports = string.Join(";", _streamImports.ToArray()); + string streamImports = string.Join(";", _streamImports); _data.SetProperty( Constants.MSBuildAllProjectsPropertyName, oldValue == null diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index 84b34d5c67a..7397dee024f 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -28,6 +28,7 @@ using ParseArgs = Microsoft.Build.Evaluation.Expander.ArgumentParser; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; using TaskItemFactory = Microsoft.Build.Execution.ProjectItemInstance.TaskItem.TaskItemFactory; +using System.Buffers; #nullable disable @@ -279,17 +280,11 @@ private void FlushFirstValueIfNeeded() private static readonly char[] s_backtickChar = { '`' }; private static readonly char[] s_doubleQuoteChar = { '"' }; - /// - /// Those characters which indicate that an expression may contain expandable - /// expressions. - /// - private static char[] s_expandableChars = { '$', '%', '@' }; - /// /// The CultureInfo from the invariant culture. Used to avoid allocations for /// performing IndexOf etc. /// - private static CompareInfo s_invariantCompareInfo = CultureInfo.InvariantCulture.CompareInfo; + private static readonly CompareInfo s_invariantCompareInfo = CultureInfo.InvariantCulture.CompareInfo; /// /// Properties to draw on for expansion. @@ -454,7 +449,7 @@ internal PropertiesUseTracker PropertiesUseTracker /// internal static bool ExpressionMayContainExpandableExpressions(string expression) { - return expression.IndexOfAny(s_expandableChars) > -1; + return expression.AsSpan().IndexOfAny('$', '%', '@') >= 0; } /// @@ -1110,7 +1105,12 @@ _metadata is IItemTypeDefinition itemMetadata && if (IsTruncationEnabled(_options) && metadataValue.Length > CharacterLimitPerExpansion) { - metadataValue = metadataValue.Substring(0, CharacterLimitPerExpansion - 3) + "..."; + metadataValue = +#if NET + $"{metadataValue.AsSpan(0, CharacterLimitPerExpansion - 3)}..."; +#else + $"{metadataValue.Substring(0, CharacterLimitPerExpansion - 3)}..."; +#endif } } @@ -1313,7 +1313,12 @@ internal static object ExpandPropertiesLeaveTypedAndEscaped( var value = propertyValue.ToString(); if (value.Length > CharacterLimitPerExpansion) { - propertyValue = value.Substring(0, CharacterLimitPerExpansion - 3) + "..."; + propertyValue = +#if NET + $"{value.AsSpan(0, CharacterLimitPerExpansion - 3)}..."; +#else + $"{value.Substring(0, CharacterLimitPerExpansion - 3)}..."; +#endif } } @@ -1730,7 +1735,7 @@ private static string ExpandRegistryValue(string registryExpression, IElementLoc } catch (Exception ex) when (!ExceptionHandling.NotExpectedRegistryException(ex)) { - ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "InvalidRegistryPropertyExpression", "$(" + registryExpression + ")", ex.Message); + ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "InvalidRegistryPropertyExpression", $"$({registryExpression})", ex.Message); } } @@ -1858,7 +1863,7 @@ internal static ExpressionShredder.ItemExpressionCapture ExpandSingleItemVectorE } List matches; - if (expression.IndexOf('@') == -1) + if (!expression.Contains('@')) { return null; } @@ -2239,7 +2244,7 @@ internal static class IntrinsicItemFunctions /// /// A cache of previously created item function delegates. /// - private static ConcurrentDictionary s_transformFunctionDelegateCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private static readonly ConcurrentDictionary s_transformFunctionDelegateCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); /// /// Delegate that represents the signature of all item transformation functions @@ -2254,7 +2259,7 @@ internal static class IntrinsicItemFunctions internal static ItemTransformFunction GetItemTransformFunction(IElementLocation elementLocation, string functionName, Type itemType) { ItemTransformFunction transformFunction = null; - string qualifiedFunctionName = itemType.FullName + "::" + functionName; + string qualifiedFunctionName = $"{itemType.FullName}::{functionName}"; // We may have seen this delegate before, if so grab the one we already created if (!s_transformFunctionDelegateCache.TryGetValue(qualifiedFunctionName, out transformFunction)) @@ -2631,7 +2636,7 @@ internal static IEnumerable> Metadata(Expander exp { // It may be that the itemspec has unescaped ';'s in it so we need to split here to handle // that case. - if (metadataValue.IndexOf(';') >= 0) + if (metadataValue.Contains(';')) { var splits = ExpressionShredder.SplitSemiColonSeparatedList(metadataValue); @@ -3095,36 +3100,23 @@ private static partial class RegularExpressions * description of an item vector changes, the expressions must be updated in both places. *************************************************************************************************************************/ - - -#if NET7_0_OR_GREATER +#if NET [GeneratedRegex(ItemMetadataSpecification, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture)] - internal static partial Regex ItemMetadataPattern(); + internal static partial Regex ItemMetadataRegex { get; } #else /// /// Regular expression used to match item metadata references embedded in strings. /// For example, %(Compile.DependsOn) or %(DependsOn). /// - internal static readonly Lazy ItemMetadataPattern = new Lazy( - () => new Regex(ItemMetadataSpecification, - RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture | RegexOptions.Compiled)); -#endif + internal static Regex ItemMetadataRegex => s_itemMetadataRegex ??= + new Regex(ItemMetadataSpecification, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture | RegexOptions.Compiled); - internal static Regex ItemMetadataRegex - { - get - { -#if NET7_0_OR_GREATER - return ItemMetadataPattern(); -#else - return ItemMetadataPattern.Value; + internal static Regex s_itemMetadataRegex; #endif - } - } - /// - /// Name of the group matching the "name" of a metadatum. - /// + /// + /// Name of the group matching the "name" of a metadatum. + /// internal const string NameGroup = "NAME"; /// @@ -3143,29 +3135,19 @@ internal static Regex ItemMetadataRegex ItemVectorWithTransformLHS + @")" + ItemMetadataSpecification + @"(?!" + ItemVectorWithTransformRHS + @"))"; -#if NET7_0_OR_GREATER +#if NET [GeneratedRegex(NonTransformItemMetadataSpecification, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture)] - internal static partial Regex NonTransformItemMetadataPattern(); + internal static partial Regex NonTransformItemMetadataRegex { get; } #else /// /// regular expression used to match item metadata references outside of item vector transforms. /// /// PERF WARNING: this Regex is complex and tends to run slowly. - internal static readonly Lazy NonTransformItemMetadataPattern = new Lazy( - () => new Regex(NonTransformItemMetadataSpecification, - RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture | RegexOptions.Compiled)); -#endif - internal static Regex NonTransformItemMetadataRegex - { - get - { -#if NET7_0_OR_GREATER - return NonTransformItemMetadataPattern(); -#else - return NonTransformItemMetadataPattern.Value; + private static Regex s_nonTransformItemMetadataPattern; + + internal static Regex NonTransformItemMetadataRegex => s_nonTransformItemMetadataPattern ??= + new Regex(NonTransformItemMetadataSpecification, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture | RegexOptions.Compiled); #endif - } - } /// /// Complete description of an item metadata reference, including the optional qualifying item type. @@ -4114,8 +4096,8 @@ private static object[] CoerceArguments(object[] args, ParameterInfo[] parameter else if (parameters[n].ParameterType.GetTypeInfo().IsEnum && args[n] is string v && v.Contains(".")) { Type enumType = parameters[n].ParameterType; - string typeLeafName = enumType.Name + "."; - string typeFullName = enumType.FullName + "."; + string typeLeafName = $"{enumType.Name}."; + string typeFullName = $"{enumType.FullName}."; // Enum.parse expects commas between enum components // We'll support the C# type | syntax too @@ -4200,11 +4182,11 @@ private string GenerateStringOfMethodExecuted(string expression, object objectIn } if ((_bindingFlags & BindingFlags.InvokeMethod) == BindingFlags.InvokeMethod) { - return "[" + typeName + "]::" + name + "(" + parameters + ")"; + return $"[{typeName}]::{name}({parameters})"; } else { - return "[" + typeName + "]::" + name; + return $"[{typeName}]::{name}"; } } else @@ -4213,11 +4195,11 @@ private string GenerateStringOfMethodExecuted(string expression, object objectIn if ((_bindingFlags & BindingFlags.InvokeMethod) == BindingFlags.InvokeMethod) { - return propertyValue + "." + name + "(" + parameters + ")"; + return $"{propertyValue}.{name}({parameters})"; } else { - return propertyValue + "." + name; + return $"{propertyValue}.{name}"; } } } diff --git a/src/Build/Evaluation/Expander/WellKnownFunctions.cs b/src/Build/Evaluation/Expander/WellKnownFunctions.cs index 72f8b247933..bc7a74e0a74 100644 --- a/src/Build/Evaluation/Expander/WellKnownFunctions.cs +++ b/src/Build/Evaluation/Expander/WellKnownFunctions.cs @@ -236,7 +236,7 @@ internal static bool TryExecuteStringFunction(string methodName, out object? ret { if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null) { - returnVal = text.IndexOfAny(arg0.ToCharArray()); + returnVal = text.AsSpan().IndexOfAny(arg0.AsSpan()); return true; } } @@ -262,7 +262,7 @@ internal static bool TryExecuteStringFunction(string methodName, out object? ret { if (ParseArgs.TryGetArg(args, out string? arg0) && arg0 != null) { - returnVal = text.LastIndexOfAny(arg0.ToCharArray()); + returnVal = text.AsSpan().LastIndexOfAny(arg0.AsSpan()); return true; } } diff --git a/src/Build/Evaluation/ExpressionShredder.cs b/src/Build/Evaluation/ExpressionShredder.cs index fd102dff143..a7d9e48b9a5 100644 --- a/src/Build/Evaluation/ExpressionShredder.cs +++ b/src/Build/Evaluation/ExpressionShredder.cs @@ -428,7 +428,7 @@ private static void GetReferencedItemNamesAndMetadata(string expression, int sta itemName = firstPart; metadataName = expression.Substring(startOfText, i - startOfText); - qualifiedMetadataName = itemName + "." + metadataName; + qualifiedMetadataName = $"{itemName}.{metadataName}"; } else { diff --git a/src/Build/Evaluation/IntrinsicFunctions.cs b/src/Build/Evaluation/IntrinsicFunctions.cs index 032f95a251c..7d6b051cf99 100644 --- a/src/Build/Evaluation/IntrinsicFunctions.cs +++ b/src/Build/Evaluation/IntrinsicFunctions.cs @@ -22,6 +22,8 @@ // Needed for DoesTaskHostExistForParameters using NodeProviderOutOfProcTaskHost = Microsoft.Build.BackEnd.NodeProviderOutOfProcTaskHost; +using System.Security.Cryptography; +using System.Buffers.Text; #nullable disable @@ -40,25 +42,14 @@ internal static partial class IntrinsicFunctions private static readonly object[] DefaultRegistryViews = [RegistryView.Default]; #pragma warning restore CA1416 -#if NET7_0_OR_GREATER +#if NET [GeneratedRegex(RegistrySdkSpecification, RegexOptions.IgnoreCase)] - private static partial Regex RegistrySdkPattern(); + private static partial Regex RegistrySdkRegex { get; } #else - private static readonly Lazy RegistrySdkPattern = new Lazy(() => new Regex(RegistrySdkSpecification, RegexOptions.IgnoreCase)); + private static Regex s_registrySdkRegex; + private static Regex RegistrySdkRegex => s_registrySdkRegex ??= new Regex(RegistrySdkSpecification, RegexOptions.IgnoreCase); #endif - private static Regex RegistrySdkRegex - { - get - { -#if NET7_0_OR_GREATER - return RegistrySdkPattern(); -#else - return RegistrySdkPattern.Value; -#endif - } - } - private static readonly Lazy NuGetFramework = new Lazy(() => NuGetFrameworkWrapper.CreateInstance()); /// @@ -284,8 +275,8 @@ internal static object GetRegistryValueFromView(string keyName, string valueName { if (viewObject is string viewAsString) { - string typeLeafName = typeof(RegistryView).Name + "."; - string typeFullName = typeof(RegistryView).FullName + "."; + string typeLeafName = $"{typeof(RegistryView).Name}."; + string typeFullName = $"{typeof(RegistryView).FullName}."; // We'll allow the user to specify the leaf or full type name on the RegistryView enum viewAsString = viewAsString.Replace(typeFullName, "").Replace(typeLeafName, ""); @@ -466,7 +457,12 @@ internal static object StableStringHash(string toHash, StringHashingAlgorithm al private static string CalculateSha256(string toHash) { - using var sha = System.Security.Cryptography.SHA256.Create(); +#if NET + Span hash = stackalloc byte[SHA256.HashSizeInBytes]; + SHA256.HashData(Encoding.UTF8.GetBytes(toHash), hash); + return Convert.ToHexStringLower(hash); +#else + using var sha = SHA256.Create(); var hashResult = new StringBuilder(); foreach (byte theByte in sha.ComputeHash(Encoding.UTF8.GetBytes(toHash))) { @@ -474,6 +470,7 @@ private static string CalculateSha256(string toHash) } return hashResult.ToString(); +#endif } /// @@ -651,14 +648,15 @@ internal static string SubstringByAsciiChars(string input, int start, int length { return string.Empty; } + if (start + length > input.Length) { length = input.Length - start; } + StringBuilder sb = new StringBuilder(); - for (int i = start; i < start + length; i++) + foreach (char c in input.AsSpan(start, length)) { - char c = input[i]; if (c >= 32 && c <= 126 && !FileUtilities.InvalidFileNameChars.Contains(c)) { sb.Append(c); @@ -668,6 +666,7 @@ internal static string SubstringByAsciiChars(string input, int start, int length sb.Append('_'); } } + return sb.ToString(); } @@ -804,7 +803,7 @@ private static RegistryKey GetBaseKeyFromKeyName(string keyName, RegistryView vi } else { - subKeyName = keyName.Substring(i + 1, keyName.Length - i - 1); + subKeyName = keyName.Substring(i + 1); } return basekey; diff --git a/src/Build/Evaluation/LazyItemEvaluator.RemoveOperation.cs b/src/Build/Evaluation/LazyItemEvaluator.RemoveOperation.cs index 771b0e6ce40..51d91cce6d7 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.RemoveOperation.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.RemoveOperation.cs @@ -29,7 +29,7 @@ public RemoveOperation(RemoveOperationBuilder builder, LazyItemEvaluator(builder.MatchOnMetadataOptions, _matchOnMetadata, _itemSpec); } @@ -48,7 +48,7 @@ protected override void ApplyImpl(OrderedItemDataCollection.Builder listBuilder, return; } - bool matchingOnMetadata = _matchOnMetadata.Any(); + bool matchingOnMetadata = !_matchOnMetadata.IsEmpty; if (!matchingOnMetadata) { if (ItemspecContainsASingleBareItemReference(_itemSpec, _itemElement.ItemType)) diff --git a/src/Build/Evaluation/Preprocessor.cs b/src/Build/Evaluation/Preprocessor.cs index 0d38472e450..d70e8327285 100644 --- a/src/Build/Evaluation/Preprocessor.cs +++ b/src/Build/Evaluation/Preprocessor.cs @@ -26,6 +26,9 @@ namespace Microsoft.Build.Evaluation /// internal class Preprocessor { + /// 140 equal signs. + private const string Equals140 = "============================================================================================================================================"; + /// /// Project to preprocess /// @@ -99,7 +102,7 @@ private XmlDocument Preprocess() if (!String.IsNullOrEmpty(_project.FullPath)) // Ignore in-memory projects { - destinationDocument.AppendChild(destinationDocument.CreateComment("\r\n" + new String('=', 140) + "\r\n" + _project.FullPath.Replace("--", "__") + "\r\n" + new String('=', 140) + "\r\n")); + destinationDocument.AppendChild(destinationDocument.CreateComment($"\r\n{Equals140}\r\n{_project.FullPath.Replace("--", "__")}\r\n{Equals140}\r\n")); } CloneChildrenResolvingImports(outerDocument, destinationDocument); @@ -310,7 +313,7 @@ private void CloneChildrenResolvingImports(XmlNode source, XmlNode destination) } destination.AppendChild(destinationDocument.CreateComment( - $"\r\n{new String('=', 140)}\r\n{importTag}\r\n\r\n{resolved.FullPath.Replace("--", "__")}\r\n{new String('=', 140)}\r\n")); + $"\r\n{Equals140}\r\n{importTag}\r\n\r\n{resolved.FullPath.Replace("--", "__")}\r\n{Equals140}\r\n")); _filePaths.Push(resolved.FullPath); CloneChildrenResolvingImports(innerDocument, destination); @@ -318,11 +321,11 @@ private void CloneChildrenResolvingImports(XmlNode source, XmlNode destination) if (i < resolvedList.Count - 1) { - destination.AppendChild(destinationDocument.CreateComment("\r\n" + new String('=', 140) + "\r\n \r\n" + new String('=', 140) + "\r\n")); + destination.AppendChild(destinationDocument.CreateComment($"\r\n{Equals140}\r\n \r\n{Equals140}\r\n")); } else { - destination.AppendChild(destinationDocument.CreateComment("\r\n" + new String('=', 140) + "\r\n \r\n\r\n" + _filePaths.Peek()?.Replace("--", "__") + "\r\n" + new String('=', 140) + "\r\n")); + destination.AppendChild(destinationDocument.CreateComment($"\r\n{Equals140}\r\n \r\n\r\n{_filePaths.Peek()?.Replace("--", "__")}\r\n{Equals140}\r\n")); } } @@ -339,7 +342,7 @@ private void CloneChildrenResolvingImports(XmlNode source, XmlNode destination) CloneChildrenResolvingImports(child, destination); - destination.AppendChild(destinationDocument.CreateComment("")); + destination.AppendChild(destinationDocument.CreateComment($"")); continue; } diff --git a/src/Build/Evaluation/Profiler/EvaluationLocationMarkdownPrettyPrinter.cs b/src/Build/Evaluation/Profiler/EvaluationLocationMarkdownPrettyPrinter.cs index 065f756fede..99f4bfdf8c6 100644 --- a/src/Build/Evaluation/Profiler/EvaluationLocationMarkdownPrettyPrinter.cs +++ b/src/Build/Evaluation/Profiler/EvaluationLocationMarkdownPrettyPrinter.cs @@ -38,14 +38,19 @@ protected override string NormalizeExpression(string description, EvaluationLoca return null; } - text = text.Replace(Separator, "\\" + Separator); + text = text.Replace(Separator, $"\\{Separator}"); if (text.Length > 100) { - text = text.Remove(100) + "..."; + text = +#if NET + $"{text.AsSpan(0, 100)}..."; +#else + $"{text.Remove(100)}..."; +#endif } - return '`' + text + '`'; + return $"`{text}`"; } } } diff --git a/src/Build/Evaluation/Profiler/EvaluationLocationPrettyPrinterBase.cs b/src/Build/Evaluation/Profiler/EvaluationLocationPrettyPrinterBase.cs index a1e8dc7f30d..ea8876e874f 100644 --- a/src/Build/Evaluation/Profiler/EvaluationLocationPrettyPrinterBase.cs +++ b/src/Build/Evaluation/Profiler/EvaluationLocationPrettyPrinterBase.cs @@ -65,7 +65,7 @@ protected static string GetElementOrConditionText(string description, Evaluation var outerXml = description; outerXml = outerXml.Replace(@"xmlns=""http://schemas.microsoft.com/developer/msbuild/2003""", ""); - var newLineIndex = outerXml.IndexOfAny(['\r', '\n']); + var newLineIndex = outerXml.AsSpan().IndexOfAny('\r', '\n'); return newLineIndex == -1 ? outerXml : outerXml.Remove(newLineIndex); } @@ -74,9 +74,19 @@ protected static string GetElementOrConditionText(string description, Evaluation /// protected void AppendDefaultHeaderWithSeparator(StringBuilder stringBuilder, string separator) { - stringBuilder.AppendLine( - string.Join(separator, "Id", "ParentId", "Pass", "File", "Line #", "Expression", "Inc (ms)", "Inc (%)", "Exc (ms)", - "Exc (%)", "#", "Kind", "Bug")); + stringBuilder.Append("Id").Append(separator) + .Append("ParentId").Append(separator) + .Append("Pass").Append(separator) + .Append("File").Append(separator) + .Append("Line #").Append(separator) + .Append("Expression").Append(separator) + .Append("Inc (ms)").Append(separator) + .Append("Inc (%)").Append(separator) + .Append("Exc (ms)").Append(separator) + .Append("Exc (%)").Append(separator) + .Append('#').Append(separator) + .Append("Kind").Append(separator) + .Append("Bug").AppendLine(); } /// @@ -92,9 +102,9 @@ protected void AppendDefaultLocationWithSeparator(StringBuilder stringBuilder, T evaluationLocation.Line?.ToString() ?? string.Empty, NormalizeExpression(evaluationLocation.ElementDescription, evaluationLocation.Kind) ?? string.Empty, GetMilliseconds(profiledLocation.InclusiveTime), - GetPercentage(totalTime, profiledLocation.InclusiveTime) + "%", + $"{GetPercentage(totalTime, profiledLocation.InclusiveTime)}%", GetMilliseconds(profiledLocation.ExclusiveTime), - GetPercentage(totalTime, profiledLocation.ExclusiveTime) + "%", + $"{GetPercentage(totalTime, profiledLocation.ExclusiveTime)}%", profiledLocation.NumberOfHits, evaluationLocation.Kind + separator)); } diff --git a/src/Build/Evaluation/ProjectParser.cs b/src/Build/Evaluation/ProjectParser.cs index 3427a49260d..a71a572677d 100644 --- a/src/Build/Evaluation/ProjectParser.cs +++ b/src/Build/Evaluation/ProjectParser.cs @@ -577,7 +577,7 @@ private ProjectTargetElement ParseProjectTargetElement(XmlElementWithLocation el // Orcas compat: all target names are automatically unescaped string targetName = EscapingUtilities.UnescapeAll(ProjectXmlUtilities.GetAttributeValue(element, XMakeAttributes.name)); - int indexOfSpecialCharacter = targetName.IndexOfAny(XMakeElements.InvalidTargetNameCharacters); + int indexOfSpecialCharacter = targetName.AsSpan().IndexOfAny(XMakeElements.InvalidTargetNameCharacters); if (indexOfSpecialCharacter >= 0) { ProjectErrorUtilities.ThrowInvalidProject(element.GetAttributeLocation(XMakeAttributes.name), "NameInvalid", targetName, targetName[indexOfSpecialCharacter]); diff --git a/src/Build/Evaluation/ProjectRootElementCache.cs b/src/Build/Evaluation/ProjectRootElementCache.cs index e086298a380..30e364ae5bd 100644 --- a/src/Build/Evaluation/ProjectRootElementCache.cs +++ b/src/Build/Evaluation/ProjectRootElementCache.cs @@ -79,7 +79,7 @@ internal class ProjectRootElementCache : ProjectRootElementCacheBase /// /// Whether the cache should log activity to the Debug.Out stream /// - private static bool s_debugLogCacheActivity = Environment.GetEnvironmentVariable("MSBUILDDEBUGXMLCACHE") == "1"; + private static readonly bool s_debugLogCacheActivity = Environment.GetEnvironmentVariable("MSBUILDDEBUGXMLCACHE") == "1"; /// /// Whether the cache should check file content for cache entry invalidation. @@ -87,7 +87,7 @@ internal class ProjectRootElementCache : ProjectRootElementCacheBase /// /// Value shall be true only in case of testing. Outside QA tests it shall be false. /// - private static bool s_сheckFileContent = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDCACHECHECKFILECONTENT")); + private static readonly bool s_сheckFileContent = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDCACHECHECKFILECONTENT")); #if DEBUG /// @@ -100,7 +100,7 @@ private struct ReentrancyGuard : IDisposable /// Shall be always 0 or 1. Reentrance to the Get function (value > 1) could lead to race condition. /// [ThreadStatic] - private static int s_getEntriesNumber = 0; + private static int s_getEntriesNumber; public ReentrancyGuard() { @@ -674,7 +674,7 @@ private void DebugTraceCache(string message, string param1) if (s_debugLogCacheActivity) { string prefix = OutOfProcNode.IsOutOfProcNode ? "C" : "P"; - Trace.WriteLine(prefix + " " + Process.GetCurrentProcess().Id + " | " + message + param1); + Trace.WriteLine($"{prefix} {Process.GetCurrentProcess().Id} | {message}{param1}"); } } } diff --git a/src/Build/Evaluation/StringMetadataTable.cs b/src/Build/Evaluation/StringMetadataTable.cs index 5e920a99c23..277c5d829d9 100644 --- a/src/Build/Evaluation/StringMetadataTable.cs +++ b/src/Build/Evaluation/StringMetadataTable.cs @@ -69,7 +69,7 @@ public string GetEscapedValueIfPresent(string itemType, string name) } else { - key = itemType + "." + name; + key = $"{itemType}.{name}"; } string value; diff --git a/src/Build/Globbing/MSBuildGlob.cs b/src/Build/Globbing/MSBuildGlob.cs index e6cb8ab1ac5..38915e44cd3 100644 --- a/src/Build/Globbing/MSBuildGlob.cs +++ b/src/Build/Globbing/MSBuildGlob.cs @@ -47,7 +47,7 @@ public GlobState(string globRoot, string fileSpec, bool isLegal, string fixedDir } // Cache of Regex objects that we have created and are still alive. - private static WeakValueDictionary s_regexCache = new WeakValueDictionary(); + private static readonly WeakValueDictionary s_regexCache = new WeakValueDictionary(); private readonly Lazy _state; diff --git a/src/Build/Graph/GraphBuilder.cs b/src/Build/Graph/GraphBuilder.cs index 311392df81f..09c7709e062 100644 --- a/src/Build/Graph/GraphBuilder.cs +++ b/src/Build/Graph/GraphBuilder.cs @@ -205,9 +205,9 @@ private static void AddEdgesFromSolution(IReadOnlyDictionary value)) { - projectsByPath[projectPath].Add(project.Value.GraphNode); + value.Add(project.Value.GraphNode); } else { diff --git a/src/Build/Graph/ParallelWorkSet.cs b/src/Build/Graph/ParallelWorkSet.cs index ed4ecb41537..8474968ed2c 100644 --- a/src/Build/Graph/ParallelWorkSet.cs +++ b/src/Build/Graph/ParallelWorkSet.cs @@ -145,7 +145,12 @@ internal void WaitForAllWorkAndComplete() // Release one thread that will release all the threads when all the elements are processed. _semaphore.Release(); - Task.WaitAll(_tasks.ToArray()); + Task.WaitAll( +#if NET + _tasks); +#else + _tasks.ToArray()); +#endif if (_exceptions.Count > 0) { diff --git a/src/Build/Graph/ProjectInterpretation.cs b/src/Build/Graph/ProjectInterpretation.cs index d927eaa7e8c..12ae373485d 100644 --- a/src/Build/Graph/ProjectInterpretation.cs +++ b/src/Build/Graph/ProjectInterpretation.cs @@ -64,7 +64,7 @@ public TargetSpecification(string target, bool skipIfNonexistent) ErrorUtilities.VerifyThrow( !skipIfNonexistent || (!target.Equals(MSBuildConstants.DefaultTargetsMarker) && !target.Equals(MSBuildConstants.ProjectReferenceTargetsOrDefaultTargetsMarker)), - target + " cannot be marked as SkipNonexistentTargets"); + $"{target} cannot be marked as SkipNonexistentTargets"); Target = target; SkipIfNonexistent = skipIfNonexistent; } diff --git a/src/Build/Instance/HostObjectException.cs b/src/Build/Instance/HostObjectException.cs index ee4613d1b14..47ba77e5d17 100644 --- a/src/Build/Instance/HostObjectException.cs +++ b/src/Build/Instance/HostObjectException.cs @@ -40,7 +40,7 @@ internal HostObjectException( Exception innerException) : base(ErrorMessagePrefix + string.Format(ErrorMessageProjectTargetTask, projectFile, targetName, taskName) - + (innerException == null ? string.Empty : ("\n=============\n" + innerException.ToString() + "\n\n")), + + (innerException == null ? string.Empty : ($"\n=============\n{innerException}\n\n")), innerException) { } diff --git a/src/Build/Instance/ProjectInstance.cs b/src/Build/Instance/ProjectInstance.cs index 938fcc5cba9..f77da95817b 100644 --- a/src/Build/Instance/ProjectInstance.cs +++ b/src/Build/Instance/ProjectInstance.cs @@ -2640,7 +2640,12 @@ private static ProjectInstance[] CalculateToolsVersionAndGenerateSolutionWrapper } else /* Dev 12 and above */ { - toolsVersion = visualStudioVersion.ToString(CultureInfo.InvariantCulture) + ".0"; + toolsVersion = +#if NET + string.Create(CultureInfo.InvariantCulture, $"{visualStudioVersion}.0"); +#else + $"{visualStudioVersion.ToString(CultureInfo.InvariantCulture)}.0"; +#endif } string toolsVersionToUse = Utilities.GenerateToolsVersionToUse( @@ -3161,7 +3166,7 @@ private void Initialize( if (Traits.Instance.EscapeHatches.DebugEvaluation) { - Trace.WriteLine(String.Format(CultureInfo.InvariantCulture, "MSBUILD: Creating a ProjectInstance from an unevaluated state [{0}]", FullPath)); + Trace.WriteLine($"MSBUILD: Creating a ProjectInstance from an unevaluated state [{FullPath}]"); } ErrorUtilities.VerifyThrow(EvaluationId == BuildEventContext.InvalidEvaluationId, "Evaluation ID is invalid prior to evaluation"); diff --git a/src/Build/Instance/ProjectItemInstance.cs b/src/Build/Instance/ProjectItemInstance.cs index 8dd5127eba5..36f280f88bb 100644 --- a/src/Build/Instance/ProjectItemInstance.cs +++ b/src/Build/Instance/ProjectItemInstance.cs @@ -2112,7 +2112,7 @@ internal class TaskItemFactory : IItemFactory, IItemFacto /// /// The singleton instance. /// - private static TaskItemFactory s_instance = new TaskItemFactory(); + private static readonly TaskItemFactory s_instance = new TaskItemFactory(); /// /// Private constructor for singleton creation. diff --git a/src/Build/Instance/ProjectMetadataInstance.cs b/src/Build/Instance/ProjectMetadataInstance.cs index ec764cbbbb3..15136b19c1d 100644 --- a/src/Build/Instance/ProjectMetadataInstance.cs +++ b/src/Build/Instance/ProjectMetadataInstance.cs @@ -164,7 +164,7 @@ internal string EvaluatedValueEscaped /// public override string ToString() { - return _name + "=" + _escapedValue; + return $"{_name}={_escapedValue}"; } #region INodePacketTranslatable Members diff --git a/src/Build/Instance/ProjectPropertyInstance.cs b/src/Build/Instance/ProjectPropertyInstance.cs index dc7cb2b6624..2d0de613ea9 100644 --- a/src/Build/Instance/ProjectPropertyInstance.cs +++ b/src/Build/Instance/ProjectPropertyInstance.cs @@ -186,7 +186,7 @@ void ITranslatable.Translate(ITranslator translator) /// public override string ToString() { - return _name + "=" + _escapedValue; + return $"{_name}={_escapedValue}"; } /// diff --git a/src/Build/Instance/TaskRegistry.cs b/src/Build/Instance/TaskRegistry.cs index c15332a79e8..9a27133c009 100644 --- a/src/Build/Instance/TaskRegistry.cs +++ b/src/Build/Instance/TaskRegistry.cs @@ -67,64 +67,64 @@ internal sealed class TaskRegistry : ITranslatable /// callbacks; as forcing those out of proc would be just setting them up for /// known failure. /// - private static bool s_forceTaskHostLaunch = (Environment.GetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC") == "1"); + private static readonly bool s_forceTaskHostLaunch = (Environment.GetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC") == "1"); /// /// Simple name for the MSBuild tasks (v4), used for shimming in loading /// task factory UsingTasks /// - private static string s_tasksV4SimpleName = "Microsoft.Build.Tasks.v4.0"; + private const string s_tasksV4SimpleName = "Microsoft.Build.Tasks.v4.0"; /// /// Filename for the MSBuild tasks (v4), used for shimming in loading /// task factory UsingTasks /// - private static string s_tasksV4Filename = s_tasksV4SimpleName + ".dll"; + private const string s_tasksV4Filename = $"{s_tasksV4SimpleName}.dll"; /// /// Expected location that MSBuild tasks (v4) is picked up from if the user /// references it with just a simple name, used for shimming in loading /// task factory UsingTasks /// - private static string s_potentialTasksV4Location = Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, s_tasksV4Filename); + private static readonly string s_potentialTasksV4Location = Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, s_tasksV4Filename); /// /// Simple name for the MSBuild tasks (v12), used for shimming in loading /// task factory UsingTasks /// - private static string s_tasksV12SimpleName = "Microsoft.Build.Tasks.v12.0"; + private const string s_tasksV12SimpleName = "Microsoft.Build.Tasks.v12.0"; /// /// Filename for the MSBuild tasks (v12), used for shimming in loading /// task factory UsingTasks /// - private static string s_tasksV12Filename = s_tasksV12SimpleName + ".dll"; + private const string s_tasksV12Filename = $"{s_tasksV12SimpleName}.dll"; /// /// Expected location that MSBuild tasks (v12) is picked up from if the user /// references it with just a simple name, used for shimming in loading /// task factory UsingTasks /// - private static string s_potentialTasksV12Location = Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, s_tasksV12Filename); + private static readonly string s_potentialTasksV12Location = Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, s_tasksV12Filename); /// /// Simple name for the MSBuild tasks (v14+), used for shimming in loading /// task factory UsingTasks /// - private static string s_tasksCoreSimpleName = "Microsoft.Build.Tasks.Core"; + private const string s_tasksCoreSimpleName = "Microsoft.Build.Tasks.Core"; /// /// Filename for the MSBuild tasks (v14+), used for shimming in loading /// task factory UsingTasks /// - private static string s_tasksCoreFilename = s_tasksCoreSimpleName + ".dll"; + private const string s_tasksCoreFilename = $"{s_tasksCoreSimpleName}.dll"; /// /// Expected location that MSBuild tasks (v14+) is picked up from if the user /// references it with just a simple name, used for shimming in loading /// task factory UsingTasks /// - private static string s_potentialTasksCoreLocation = Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, s_tasksCoreFilename); + private static readonly string s_potentialTasksCoreLocation = Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, s_tasksCoreFilename); /// /// Monotonically increasing counter for registered tasks. @@ -843,13 +843,13 @@ internal class RegisteredTaskIdentityComparer : IEqualityComparer /// The singleton comparer to use when an exact match is desired /// - private static RegisteredTaskIdentityComparer s_exact = new RegisteredTaskIdentityComparer(true /* exact match */); + private static readonly RegisteredTaskIdentityComparer s_exact = new RegisteredTaskIdentityComparer(true /* exact match */); /// /// The singleton comparer to use when a fuzzy match is desired. Note that this still does an exact match on the /// name, but does a fuzzy match on the task identity parameters. /// - private static RegisteredTaskIdentityComparer s_fuzzy = new RegisteredTaskIdentityComparer(false /* fuzzy match */); + private static readonly RegisteredTaskIdentityComparer s_fuzzy = new RegisteredTaskIdentityComparer(false /* fuzzy match */); /// /// Keeps track of whether we're doing exact or fuzzy equivalency diff --git a/src/Build/Logging/BaseConsoleLogger.cs b/src/Build/Logging/BaseConsoleLogger.cs index 508223e628b..364487c1e72 100644 --- a/src/Build/Logging/BaseConsoleLogger.cs +++ b/src/Build/Logging/BaseConsoleLogger.cs @@ -518,7 +518,7 @@ internal virtual void OutputEnvironment(IDictionary environment) foreach (KeyValuePair entry in environment) { setColor(ConsoleColor.Gray); - WritePretty(String.Format(CultureInfo.CurrentCulture, "{0,-30} = ", entry.Key)); + WritePretty($"{entry.Key,-30} = "); setColor(ConsoleColor.DarkGray); WriteLinePretty(entry.Value); } @@ -536,7 +536,7 @@ internal virtual void OutputProperties(List list) foreach (DictionaryEntry prop in list) { setColor(ConsoleColor.Gray); - WritePretty(String.Format(CultureInfo.CurrentCulture, "{0,-30} = ", prop.Key)); + WritePretty($"{prop.Key,-30} = "); setColor(ConsoleColor.DarkGray); WriteLinePretty(EscapingUtilities.UnescapeAll((string)prop.Value)); } @@ -656,12 +656,12 @@ protected virtual void WriteItemType(string itemType) protected virtual void WriteItemSpec(string itemSpec) { - WriteLinePretty(" " + itemSpec); + WriteLinePretty($" {itemSpec}"); } protected virtual void WriteMetadata(string name, string value) { - WriteLinePretty(" " + name + " = " + value); + WriteLinePretty($" {name} = {value}"); } /// @@ -821,8 +821,8 @@ internal virtual void PrintCounterMessage(WriteLinePrettyFromResourceDelegate wr 2, "PerformanceLine", time, - String.Format(CultureInfo.CurrentCulture, "{0,-40}" /* pad to 40 align left */, scopeName), - String.Format(CultureInfo.CurrentCulture, "{0,3}", calls)); + $"{scopeName,-40}", // pad to 40 align left + $"{calls,3}"); } /// diff --git a/src/Build/Logging/BinaryLogger/BinaryLogger.cs b/src/Build/Logging/BinaryLogger/BinaryLogger.cs index adadde7eb8b..5e7c55f6531 100644 --- a/src/Build/Logging/BinaryLogger/BinaryLogger.cs +++ b/src/Build/Logging/BinaryLogger/BinaryLogger.cs @@ -504,13 +504,13 @@ private string GetUniqueStamp() => (PathParameterExpander ?? ExpandPathParameter)(string.Empty); private static string ExpandPathParameter(string parameters) - => $"{DateTime.UtcNow.ToString("yyyyMMdd-HHmmss")}--{ProcessId}--{StringUtils.GenerateRandomString(6)}"; + => $"{DateTime.UtcNow:yyyyMMdd-HHmmss}--{ProcessId}--{StringUtils.GenerateRandomString(6)}"; private static int ProcessId #if NET => Environment.ProcessId; #else - => System.Diagnostics.Process.GetCurrentProcess().Id; + { get; } = System.Diagnostics.Process.GetCurrentProcess().Id; #endif } } diff --git a/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs b/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs index 4b48d9a8592..69afeee1674 100644 --- a/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs +++ b/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs @@ -56,9 +56,9 @@ public class BuildEventArgsReader : IBuildEventArgsReaderNotifications, IDisposa // reflection is needed to set these three fields because public constructors don't provide // a way to set these from the outside - private static FieldInfo? buildEventArgsFieldThreadId = + private static readonly FieldInfo? buildEventArgsFieldThreadId = typeof(BuildEventArgs).GetField("threadId", BindingFlags.Instance | BindingFlags.NonPublic); - private static FieldInfo? buildEventArgsFieldSenderName = + private static readonly FieldInfo? buildEventArgsFieldSenderName = typeof(BuildEventArgs).GetField("senderName", BindingFlags.Instance | BindingFlags.NonPublic); /// diff --git a/src/Build/Logging/BinaryLogger/Postprocessing/SubStream.cs b/src/Build/Logging/BinaryLogger/Postprocessing/SubStream.cs index 8c6e0c6e2b8..af92788d62a 100644 --- a/src/Build/Logging/BinaryLogger/Postprocessing/SubStream.cs +++ b/src/Build/Logging/BinaryLogger/Postprocessing/SubStream.cs @@ -70,9 +70,13 @@ public override int ReadByte() public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { count = Math.Min((int)Math.Max(Length - _position, 0), count); -#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - int read = await _stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); -#pragma warning restore CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' + int read = await _stream.ReadAsync( +#if NET + buffer.AsMemory(offset, count), +#else + buffer, offset, count, +#endif + cancellationToken).ConfigureAwait(false); _position += read; return read; } diff --git a/src/Build/Logging/BinaryLogger/Postprocessing/TransparentReadStream.cs b/src/Build/Logging/BinaryLogger/Postprocessing/TransparentReadStream.cs index ea3fcb3c9c7..bd427fbb3bc 100644 --- a/src/Build/Logging/BinaryLogger/Postprocessing/TransparentReadStream.cs +++ b/src/Build/Logging/BinaryLogger/Postprocessing/TransparentReadStream.cs @@ -116,9 +116,13 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, count = (int)(_maxAllowedPosition - _position); } -#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - int cnt = await _stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); -#pragma warning restore CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' + int cnt = await _stream.ReadAsync( +#if NET + buffer.AsMemory(offset, count), +#else + buffer, offset, count, +#endif + cancellationToken).ConfigureAwait(false); _position += cnt; return cnt; } diff --git a/src/Build/Logging/DistributedLoggers/DistributedFileLogger.cs b/src/Build/Logging/DistributedLoggers/DistributedFileLogger.cs index 591df6df9f3..bfd1d41a51f 100644 --- a/src/Build/Logging/DistributedLoggers/DistributedFileLogger.cs +++ b/src/Build/Logging/DistributedLoggers/DistributedFileLogger.cs @@ -113,7 +113,7 @@ public void Initialize(IEventSource eventSource) // but avoids confusion by being consistent with the Engine and any error messages it may produce. fileName = _logFile.Replace(extension, _nodeId + extension); _nodeFileLogger.Verbosity = LoggerVerbosity.Detailed; - _nodeFileLogger.Parameters = "ShowEventId;ShowCommandLine;logfile=" + fileName + ";" + _parameters; + _nodeFileLogger.Parameters = $"ShowEventId;ShowCommandLine;logfile={fileName};{_parameters}"; } catch (ArgumentException e) // Catching Exception, but rethrowing unless it's a well-known exception. { diff --git a/src/Build/Logging/LogFormatter.cs b/src/Build/Logging/LogFormatter.cs index b03391a34a1..22c0ab05658 100644 --- a/src/Build/Logging/LogFormatter.cs +++ b/src/Build/Logging/LogFormatter.cs @@ -51,7 +51,7 @@ internal static string FormatTimeSpan(TimeSpan t) string rawTime = t.ToString(); // Timespan is a value type and can't be null. int rawTimeLength = rawTime.Length; int prettyLength = System.Math.Min(11, rawTimeLength); - return t.ToString().Substring(0, prettyLength); + return rawTime.Substring(0, prettyLength); } } } diff --git a/src/Build/Logging/LoggerDescription.cs b/src/Build/Logging/LoggerDescription.cs index 8f949afb9ad..9b4e34e5c34 100644 --- a/src/Build/Logging/LoggerDescription.cs +++ b/src/Build/Logging/LoggerDescription.cs @@ -96,7 +96,7 @@ public string Name if (!string.IsNullOrEmpty(_loggerClassName) && !string.IsNullOrEmpty(_loggerAssembly.AssemblyFile)) { - return _loggerClassName + ":" + _loggerAssembly.AssemblyFile; + return $"{_loggerClassName}:{_loggerAssembly.AssemblyFile}"; } else if (!string.IsNullOrEmpty(_loggerClassName)) { diff --git a/src/Build/Logging/OptimizedStringIndenter.cs b/src/Build/Logging/OptimizedStringIndenter.cs index d98f1d62094..ed28c80e8fd 100644 --- a/src/Build/Logging/OptimizedStringIndenter.cs +++ b/src/Build/Logging/OptimizedStringIndenter.cs @@ -4,7 +4,7 @@ using System; using System.Buffers; -#if NET7_0_OR_GREATER +#if NET using System.Runtime.CompilerServices; #else using System.Text; @@ -49,7 +49,7 @@ namespace Microsoft.Build.BackEnd.Logging; internal static class OptimizedStringIndenter { #nullable enable -#if NET7_0_OR_GREATER +#if NET [SkipLocalsInit] #endif internal static unsafe string IndentString(string? s, int indent, IStringBuilderProvider stringBuilderProvider) @@ -67,7 +67,7 @@ internal static unsafe string IndentString(string? s, int indent, IStringBuilder indentedStringLength += segment.Length; } -#if NET7_0_OR_GREATER +#if NET #pragma warning disable CS8500 string result = string.Create(indentedStringLength, (s, (IntPtr)(&segments), indent), static (output, state) => { diff --git a/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs b/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs index d59bbb81e46..9e2368df650 100644 --- a/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs +++ b/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs @@ -760,7 +760,7 @@ internal override void OutputProperties(List list) foreach (DictionaryEntry prop in list) { setColor(ConsoleColor.Gray); - string propertyString = String.Format(CultureInfo.CurrentCulture, "{0} = {1}", prop.Key, EscapingUtilities.UnescapeAll((string)(prop.Value))); + string propertyString = $"{prop.Key} = {EscapingUtilities.UnescapeAll((string)(prop.Value))}"; WriteMessageAligned(propertyString, false); } resetColor(); @@ -781,7 +781,7 @@ internal override void OutputEnvironment(IDictionary environment foreach (KeyValuePair entry in environment) { setColor(ConsoleColor.Gray); - string environmentMessage = String.Format(CultureInfo.CurrentCulture, "{0} = {1}", entry.Key, entry.Value); + string environmentMessage = $"{entry.Key} = {entry.Value}"; WriteMessageAligned(environmentMessage, false); } } @@ -897,7 +897,7 @@ public override void TargetFinishedHandler(object sender, TargetFinishedEventArg foreach (DictionaryEntry metadatum in metadata) { - WriteMessageAligned(new String(' ', 4 * tabWidth) + metadatum.Key + " = " + item.GetMetadata(metadatum.Key as string), false); + WriteMessageAligned($"{new String(' ', 4 * tabWidth)}{metadatum.Key} = {item.GetMetadata(metadatum.Key as string)}", false); } } } @@ -1343,7 +1343,7 @@ private void PrintTargetNamePerMessage(BuildMessageEventArgs e, bool lightenText } else { - WriteMessageAligned(targetName + ":", prefixAlreadyWritten); + WriteMessageAligned($"{targetName}:", prefixAlreadyWritten); } if (lightenText) @@ -1618,11 +1618,11 @@ private void WriteLinePrefix(string key, DateTime eventTimeStamp, bool isMessage if (!isMessagePrefix || IsVerbosityAtLeast(LoggerVerbosity.Detailed)) { - prefixString = ResourceUtilities.FormatResourceStringStripCodeAndKeyword("BuildEventContext", context, key) + ">"; + prefixString = $"{ResourceUtilities.FormatResourceStringStripCodeAndKeyword("BuildEventContext", context, key)}>"; } else { - prefixString = ResourceUtilities.FormatResourceStringStripCodeAndKeyword("BuildEventContext", context, string.Empty) + " "; + prefixString = $"{ResourceUtilities.FormatResourceStringStripCodeAndKeyword("BuildEventContext", context, string.Empty)} "; } WritePretty(prefixString); @@ -1763,8 +1763,8 @@ internal override void PrintCounterMessage(WriteLinePrettyFromResourceDelegate W MessageIndentLevel, "PerformanceLine", time, - String.Format(CultureInfo.CurrentCulture, "{0,-40}" /* pad to 40 align left */, scopeName), - String.Format(CultureInfo.CurrentCulture, "{0,3}", calls)); + $"{scopeName,-40}", // pad to 40 align left + $"{calls,3}"); if (_internalPerformanceCounters?.Count > 0) { diff --git a/src/Build/Logging/ParallelLogger/ParallelLoggerHelpers.cs b/src/Build/Logging/ParallelLogger/ParallelLoggerHelpers.cs index dd73599bec2..b066b31fea4 100644 --- a/src/Build/Logging/ParallelLogger/ParallelLoggerHelpers.cs +++ b/src/Build/Logging/ParallelLogger/ParallelLoggerHelpers.cs @@ -140,7 +140,6 @@ internal string[] ProjectCallStackFromProject(BuildEventContext e) ProjectStartedEventMinimumFields startedEvent = GetProjectStartedEvent(currentKey); - List stackTrace = new List(); // If there is no started event then there should be no stack trace // this is a valid situation if the event occures in the engine or outside the context of a project // or the event is raised before the project started event @@ -150,19 +149,18 @@ internal string[] ProjectCallStackFromProject(BuildEventContext e) } List projectStackTrace = GetProjectCallStack(e); - foreach (ProjectStartedEventMinimumFields projectStartedEvent in projectStackTrace) + + string[] stackTrace = new string[projectStackTrace.Count]; + for (int i = 0; i < stackTrace.Length; i++) { - if (!string.IsNullOrEmpty(projectStartedEvent.TargetNames)) - { - stackTrace.Add(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("ProjectStackWithTargetNames", projectStartedEvent.ProjectFile, projectStartedEvent.TargetNames, projectStartedEvent.FullProjectKey)); - } - else - { - stackTrace.Add(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("ProjectStackWithDefaultTargets", projectStartedEvent.ProjectFile, projectStartedEvent.FullProjectKey)); - } + ProjectStartedEventMinimumFields projectStartedEvent = projectStackTrace[i]; + + stackTrace[stackTrace.Length - i - 1] = !string.IsNullOrEmpty(projectStartedEvent.TargetNames) ? + ResourceUtilities.FormatResourceStringStripCodeAndKeyword("ProjectStackWithTargetNames", projectStartedEvent.ProjectFile, projectStartedEvent.TargetNames, projectStartedEvent.FullProjectKey) : + ResourceUtilities.FormatResourceStringStripCodeAndKeyword("ProjectStackWithDefaultTargets", projectStartedEvent.ProjectFile, projectStartedEvent.FullProjectKey); } - stackTrace.Reverse(); - return stackTrace.ToArray(); + + return stackTrace; } /// diff --git a/src/Build/Logging/ProfilerLogger.cs b/src/Build/Logging/ProfilerLogger.cs index b80dcf8cf0e..85f659336d8 100644 --- a/src/Build/Logging/ProfilerLogger.cs +++ b/src/Build/Logging/ProfilerLogger.cs @@ -134,7 +134,7 @@ internal ProfilerResult GetAggregatedResult(bool pruneSmallItems = true) // So keeping that map here var originalLocations = new Dictionary(EvaluationLocationIdAgnosticComparer.Singleton); - while (_profiledResults.Any()) + while (!_profiledResults.IsEmpty) { ProfilerResult profiledResult; var result = _profiledResults.TryDequeue(out profiledResult); diff --git a/src/Build/Resources/Constants.cs b/src/Build/Resources/Constants.cs index f6c7a968081..68a6ab4f4bc 100644 --- a/src/Build/Resources/Constants.cs +++ b/src/Build/Resources/Constants.cs @@ -178,7 +178,7 @@ internal static class AvailableStaticMethods /// /// Locker to protect initialization /// - private static Object s_locker = new Object(); + private static readonly Object s_locker = new Object(); static AvailableStaticMethods() { @@ -365,7 +365,7 @@ private static void InitializeAvailableMethods() availableStaticMethods.TryAdd("Microsoft.Build.Utilities.ToolLocationHelper", new Tuple("Microsoft.Build.Utilities.ToolLocationHelper, Microsoft.Build.Utilities.Core, Version=" + MSBuildConstants.CurrentAssemblyVersion + ", Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", null)); availableStaticMethods.TryAdd("System.Runtime.InteropServices.RuntimeInformation", runtimeInformationType); availableStaticMethods.TryAdd("System.Runtime.InteropServices.OSPlatform", osPlatformType); -#if NET5_0_OR_GREATER +#if NET var operatingSystemType = new Tuple(null, typeof(OperatingSystem)); availableStaticMethods.TryAdd("System.OperatingSystem", operatingSystemType); #else diff --git a/src/Build/Utilities/SimpleVersion.cs b/src/Build/Utilities/SimpleVersion.cs index b12e865dfd1..ba17560035f 100644 --- a/src/Build/Utilities/SimpleVersion.cs +++ b/src/Build/Utilities/SimpleVersion.cs @@ -125,21 +125,19 @@ public static SimpleVersion Parse(string input) return new SimpleVersion(major, minor, build, revision); } - private static readonly char[] s_semverSeparators = ['-', '+']; - private static ReadOnlySpan RemoveTrivia(string input) { // Ignore leading/trailing whitespace in input. ReadOnlySpan span = input.AsSpan().Trim(); // Ignore a leading "v". - if (span.Length > 0 && (span[0] == 'v' || span[0] == 'V')) + if (span.Length > 0 && (span[0] is 'v' or 'V')) { span = span.Slice(1); } // Ignore semver separator and anything after. - int separatorIndex = span.IndexOfAny(s_semverSeparators); + int separatorIndex = span.IndexOfAny('-', '+'); if (separatorIndex >= 0) { span = span.Slice(0, separatorIndex); diff --git a/src/Build/Utilities/Utilities.cs b/src/Build/Utilities/Utilities.cs index f68d76b00c1..3571dc50851 100644 --- a/src/Build/Utilities/Utilities.cs +++ b/src/Build/Utilities/Utilities.cs @@ -2,11 +2,13 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using System.Xml; using Microsoft.Build.BackEnd; @@ -26,7 +28,7 @@ namespace Microsoft.Build.Internal /// /// This class contains utility methods for the MSBuild engine. /// - internal static class Utilities + internal static partial class Utilities { /// /// Save off the contents of the environment variable that specifies whether we should treat higher toolsversions as the current @@ -81,7 +83,7 @@ internal static void SetXmlNodeInnerContents(XmlElementWithLocation node, string { ErrorUtilities.VerifyThrow(s != null, "Need value to set."); - if (s.IndexOf('<') != -1) + if (s.Contains('<')) { // If the value looks like it probably contains XML markup ... try @@ -295,7 +297,12 @@ private static bool ContainsNoTagsOtherThanComments(string innerXml, int firstLe } // used to find the xmlns attribute - private static readonly Regex s_xmlnsPattern = new Regex("xmlns=\"[^\"]*\"\\s*"); +#if NET + [GeneratedRegex("xmlns=\"[^\"]*\"\\s*")] + private static partial Regex XmlnsPattern { get; } +#else + private static Regex XmlnsPattern { get; } = new Regex("xmlns=\"[^\"]*\"\\s*"); +#endif /// /// Removes the xmlns attribute from an XML string. @@ -304,7 +311,7 @@ private static bool ContainsNoTagsOtherThanComments(string innerXml, int firstLe /// The modified XML string. internal static string RemoveXmlNamespace(string xml) { - return s_xmlnsPattern.Replace(xml, String.Empty); + return XmlnsPattern.Replace(xml, String.Empty); } /// @@ -312,19 +319,19 @@ internal static string RemoveXmlNamespace(string xml) /// internal static string CreateToolsVersionListString(IEnumerable toolsets) { - string toolsVersionList = String.Empty; + StringBuilder sb = StringBuilderCache.Acquire(); + foreach (Toolset toolset in toolsets) { - toolsVersionList += "\"" + toolset.ToolsVersion + "\", "; - } + if (sb.Length != 0) + { + sb.Append(", "); + } - // Remove trailing comma and space - if (toolsVersionList.Length > 0) - { - toolsVersionList = toolsVersionList.Substring(0, toolsVersionList.Length - 2); + sb.Append('"').Append(toolset.ToolsVersion).Append('"'); } - return toolsVersionList; + return StringBuilderCache.GetStringAndRelease(sb); } /// @@ -613,19 +620,6 @@ public static IEnumerable Values(this IEnumerable> } } - public static IEnumerable ToEnumerable(this IEnumerator enumerator) - { - while (enumerator.MoveNext()) - { - yield return enumerator.Current; - } - } - - public static T[] ToArray(this IEnumerator enumerator) - { - return enumerator.ToEnumerable().ToArray(); - } - /// /// Iterates through the nongeneric enumeration and provides generic strong-typed enumeration of properties. /// diff --git a/src/Framework/InternalErrorException.cs b/src/Framework/InternalErrorException.cs index e024e3f67a5..73180f14fb8 100644 --- a/src/Framework/InternalErrorException.cs +++ b/src/Framework/InternalErrorException.cs @@ -60,7 +60,7 @@ private InternalErrorException(string message, Exception innerException, bool ca ? message : "MSB0001: Internal MSBuild Error: " + message + (innerException == null ? String.Empty - : ("\n=============\n" + innerException.ToString() + "\n\n")), + : $"\n=============\n{innerException}\n\n"), innerException) { if (!calledFromDeserialization) diff --git a/src/Framework/LazyFormattedBuildEventArgs.cs b/src/Framework/LazyFormattedBuildEventArgs.cs index 209c30536d4..5cb00f9a8b2 100644 --- a/src/Framework/LazyFormattedBuildEventArgs.cs +++ b/src/Framework/LazyFormattedBuildEventArgs.cs @@ -193,7 +193,7 @@ private static string FormatString([StringSyntax(StringSyntaxAttribute.Composite // another one, add it here. if (param != null && param.ToString() == param.GetType().FullName) { - throw new InvalidOperationException(string.Format("Invalid type for message formatting argument, was {0}", param.GetType().FullName)); + throw new InvalidOperationException($"Invalid type for message formatting argument, was {param.GetType().FullName}"); } } #endif @@ -218,7 +218,7 @@ private static string FormatString([StringSyntax(StringSyntaxAttribute.Composite // Done executing task "Crash". // // T - formatted = string.Format("\"{0}\"\n{1}", unformatted, ex.ToString()); + formatted = $"\"{unformatted}\"\n{ex}"; } } diff --git a/src/Framework/NativeMethods.cs b/src/Framework/NativeMethods.cs index bbc62463b1e..8086d126771 100644 --- a/src/Framework/NativeMethods.cs +++ b/src/Framework/NativeMethods.cs @@ -461,7 +461,7 @@ public SystemInformationData() { ProcessorArchitectures processorArchitecture = ProcessorArchitectures.Unknown; -#if NETCOREAPP || NETSTANDARD1_1_OR_GREATER +#if NET || NETSTANDARD1_1_OR_GREATER // Get the architecture from the runtime. processorArchitecture = RuntimeInformation.OSArchitecture switch { @@ -469,13 +469,9 @@ public SystemInformationData() Architecture.Arm64 => ProcessorArchitectures.ARM64, Architecture.X64 => ProcessorArchitectures.X64, Architecture.X86 => ProcessorArchitectures.X86, -#if NET5_0_OR_GREATER +#if NET Architecture.Wasm => ProcessorArchitectures.WASM, -#endif -#if NET6_0_OR_GREATER Architecture.S390x => ProcessorArchitectures.S390X, -#endif -#if NET7_0_OR_GREATER Architecture.LoongArch64 => ProcessorArchitectures.LOONGARCH64, Architecture.Armv6 => ProcessorArchitectures.ARMV6, Architecture.Ppc64le => ProcessorArchitectures.PPC64LE, @@ -1394,7 +1390,7 @@ internal static int GetParentProcessId(int processId) // using (var r = FileUtilities.OpenRead("/proc/" + processId + "/stat")) // and could be again when FileUtilities moves to Framework - using var fileStream = new FileStream("/proc/" + processId + "/stat", FileMode.Open, System.IO.FileAccess.Read); + using var fileStream = new FileStream($"/proc/{processId}/stat", FileMode.Open, System.IO.FileAccess.Read); using StreamReader r = new(fileStream); line = r.ReadLine(); @@ -1562,7 +1558,7 @@ private static unsafe bool AreStringsEqual(char* buffer, int len, string s) return true; #else - return MemoryExtensions.SequenceEqual(new ReadOnlySpan(buffer, len), s.AsSpan()); + return s.AsSpan().SequenceEqual(new ReadOnlySpan(buffer, len)); #endif } diff --git a/src/Framework/OperatingSystem.cs b/src/Framework/OperatingSystem.cs index 883ec55b924..51756afe1da 100644 --- a/src/Framework/OperatingSystem.cs +++ b/src/Framework/OperatingSystem.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#if !NET5_0_OR_GREATER +#if !NET using System; diff --git a/src/MSBuild/AutomaticEncodingRestorer.cs b/src/MSBuild/AutomaticEncodingRestorer.cs index b5696d62ab8..034a0366786 100644 --- a/src/MSBuild/AutomaticEncodingRestorer.cs +++ b/src/MSBuild/AutomaticEncodingRestorer.cs @@ -24,7 +24,7 @@ public AutomaticEncodingRestorer() { try { -#if NET7_0_OR_GREATER +#if NET if (OperatingSystem.IsIOS() || OperatingSystem.IsAndroid() || OperatingSystem.IsTvOS()) // Output + Input Encoding are unavailable on these platforms per docs, and they're only available past net 5. { return; @@ -32,7 +32,7 @@ public AutomaticEncodingRestorer() #endif _originalOutputEncoding = Console.OutputEncoding; -#if NET7_0_OR_GREATER +#if NET if (OperatingSystem.IsBrowser()) // Input Encoding is also unavailable in this platform. (No concern for net472 as browser is unavailable.) { return; diff --git a/src/MSBuild/PerformanceLogEventListener.cs b/src/MSBuild/PerformanceLogEventListener.cs index 3eb6090cd4b..387b612e590 100644 --- a/src/MSBuild/PerformanceLogEventListener.cs +++ b/src/MSBuild/PerformanceLogEventListener.cs @@ -22,7 +22,7 @@ internal struct ProviderConfiguration internal EventLevel Level { get; set; } } - private static ProviderConfiguration[] s_config = + private static readonly ProviderConfiguration[] s_config = [ new ProviderConfiguration() { @@ -82,7 +82,7 @@ internal void Initialize(string logDirectory) _processIDStr = Process.GetCurrentProcess().Id.ToString(); // Use a GUID disambiguator to make sure that we have a unique file name. - string logFilePath = Path.Combine(logDirectory, $"perf-{_processIDStr}-{Guid.NewGuid().ToString("N")}.log"); + string logFilePath = Path.Combine(logDirectory, $"perf-{_processIDStr}-{Guid.NewGuid():N}.log"); Stream outputStream = new FileStream( logFilePath, @@ -143,7 +143,7 @@ protected override void OnEventWritten(EventWrittenEventArgs eventData) s_builder.Clear(); } - s_builder.Append($"[{DateTime.UtcNow.ToString("o")}] Event={eventData.EventSource.Name}/{eventData.EventName} ProcessID={_processIDStr} ThreadID={System.Threading.Thread.CurrentThread.ManagedThreadId}\t "); + s_builder.Append($"[{DateTime.UtcNow:o}] Event={eventData.EventSource.Name}/{eventData.EventName} ProcessID={_processIDStr} ThreadID={Environment.CurrentManagedThreadId}\t "); for (int i = 0; i < eventData.PayloadNames.Count; i++) { s_builder.Append($"{eventData.PayloadNames[i]}=\"{eventData.Payload[i]}\" "); diff --git a/src/MSBuild/TerminalLogger/NodeStatus.cs b/src/MSBuild/TerminalLogger/NodeStatus.cs index 3b3de635dee..695b01a8a59 100644 --- a/src/MSBuild/TerminalLogger/NodeStatus.cs +++ b/src/MSBuild/TerminalLogger/NodeStatus.cs @@ -67,23 +67,10 @@ obj is NodeStatus status && TargetPrefixColor == status.TargetPrefixColor && TargetPrefix == status.TargetPrefix; - public override string ToString() - { - string duration = Stopwatch.ElapsedSeconds.ToString("F1"); - - return string.IsNullOrEmpty(TargetFramework) - ? string.Format("{0}{1} {2} ({3}s)", - TerminalLogger.Indentation, - Project, - Target, - duration) - : string.Format("{0}{1} {2} {3} ({4}s)", - TerminalLogger.Indentation, - Project, - AnsiCodes.Colorize(TargetFramework, TerminalLogger.TargetFrameworkColor), - Target, - duration); - } + public override string ToString() => + string.IsNullOrEmpty(TargetFramework) ? + $"{TerminalLogger.Indentation}{Project} {Target} ({Stopwatch.ElapsedSeconds:F1}s)" : + $"{TerminalLogger.Indentation}{Project} {AnsiCodes.Colorize(TargetFramework, TerminalLogger.TargetFrameworkColor)} {Target} ({Stopwatch.ElapsedSeconds:F1}s)"; public override int GetHashCode() { diff --git a/src/MSBuild/TerminalLogger/TerminalLogger.cs b/src/MSBuild/TerminalLogger/TerminalLogger.cs index d4dc4346cf9..33727a6252c 100644 --- a/src/MSBuild/TerminalLogger/TerminalLogger.cs +++ b/src/MSBuild/TerminalLogger/TerminalLogger.cs @@ -13,8 +13,10 @@ using Microsoft.Build.Framework.Logging; using System.Globalization; -#if NET7_0_OR_GREATER +#if NET using System.Diagnostics.CodeAnalysis; +using System.Buffers; + #endif #if NETFRAMEWORK using Microsoft.IO; @@ -34,15 +36,10 @@ internal sealed partial class TerminalLogger : INodeLogger { private const string FilePathPattern = " -> "; -#if NET7_0_OR_GREATER - [StringSyntax(StringSyntaxAttribute.Regex)] - private const string ImmediateMessagePattern = @"\[CredentialProvider\]|--interactive"; - private const RegexOptions Options = RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.ExplicitCapture; - - [GeneratedRegex(ImmediateMessagePattern, Options)] - private static partial Regex ImmediateMessageRegex(); +#if NET + private static readonly SearchValues _immediateMessageKeywords = SearchValues.Create(["[CredentialProvider]", "--interactive"], StringComparison.OrdinalIgnoreCase); #else - private static readonly string[] _immediateMessageKeywords = { "[CredentialProvider]", "--interactive" }; + private static readonly string[] _immediateMessageKeywords = ["[CredentialProvider]", "--interactive"]; #endif private static readonly string[] newLineStrings = { "\r\n", "\n" }; @@ -186,11 +183,6 @@ public ProjectContext(BuildEventContext context) "ERROREVENT" }; - /// - /// The two directory separator characters to be passed to methods like . - /// - private static readonly char[] PathSeparators = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; - /// /// One summary per finished project test run. /// @@ -974,8 +966,8 @@ private void WarningRaised(object sender, BuildWarningEventArgs e) /// Raised event. /// true if marker is detected. private bool IsImmediateMessage(string message) => -#if NET7_0_OR_GREATER - ImmediateMessageRegex().IsMatch(message); +#if NET + message.AsSpan().ContainsAny(_immediateMessageKeywords); #else _immediateMessageKeywords.Any(imk => message.IndexOf(imk, StringComparison.OrdinalIgnoreCase) >= 0); #endif @@ -1137,7 +1129,7 @@ private int NodeIndexForContext(BuildEventContext context) return null; } - int index = path.LastIndexOfAny(PathSeparators); + int index = path.AsSpan().LastIndexOfAny(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); return index >= 0 ? $"{path.Substring(0, index + 1)}{AnsiCodes.MakeBold(path.Substring(index + 1))}" : path; @@ -1235,7 +1227,7 @@ private string FormatEventMessage( builder.Append($"{category} {code}: "); // render multi-line message in a special way - if (message.IndexOf('\n') >= 0) + if (message.Contains('\n')) { // Place the multiline message under the project in case of minimal and higher verbosity. string[] lines = message.Split(newLineStrings, StringSplitOptions.None); diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index c12cd96ec57..473ba0d372c 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -1774,7 +1774,7 @@ private static bool PrintTargets(string projectFile, string toolsVersion, Dictio ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword( "LongPaths", ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword( - "LongPaths_" + longPaths.ToString())), + $"LongPaths_{longPaths}")), MessageImportance.Low)); } @@ -1786,7 +1786,7 @@ private static bool PrintTargets(string projectFile, string toolsVersion, Dictio ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword( "SAC", ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword( - "SAC_" + SAC_State.ToString())), + $"SAC_{SAC_State}")), MessageImportance.Low)); } @@ -3088,7 +3088,7 @@ private static bool WarningsAsErrorsSwitchIsEmpty(CommandLineSwitches commandLin return false; } - int indexOfColon = val.IndexOf(":"); + int indexOfColon = val.IndexOf(':'); return indexOfColon < 0 || indexOfColon == val.Length - 1; } @@ -3657,13 +3657,13 @@ private static void ValidateExtensions(string[] projectExtensionsToIgnore) InitializationException.VerifyThrow(extension?.Length >= 2, "InvalidExtensionToIgnore", extension); // There is an invalid char in the extensionToIgnore. - InitializationException.VerifyThrow(extension.IndexOfAny(Path.GetInvalidPathChars()) == -1, "InvalidExtensionToIgnore", extension, null, false); + InitializationException.VerifyThrow(extension.AsSpan().IndexOfAny(MSBuildConstants.InvalidPathChars) < 0, "InvalidExtensionToIgnore", extension, null, false); // There were characters before the extension. InitializationException.VerifyThrow(string.Equals(extension, Path.GetExtension(extension), StringComparison.OrdinalIgnoreCase), "InvalidExtensionToIgnore", extension, null, false); // Make sure that no wild cards are in the string because for now we don't allow wild card extensions. - InitializationException.VerifyThrow(extension.IndexOfAny(s_wildcards) == -1, "InvalidExtensionToIgnore", extension, null, false); + InitializationException.VerifyThrow(extension.IndexOfAny(MSBuildConstants.WildcardChars) == -1, "InvalidExtensionToIgnore", extension, null, false); } } } @@ -3717,7 +3717,7 @@ private static string[] ProcessTargetSwitch(string[] parameters) { foreach (string parameter in parameters) { - int indexOfSpecialCharacter = parameter.IndexOfAny(XMakeElements.InvalidTargetNameCharacters); + int indexOfSpecialCharacter = parameter.AsSpan().IndexOfAny(XMakeElements.InvalidTargetNameCharacters); if (indexOfSpecialCharacter >= 0) { CommandLineSwitchException.Throw("NameInvalid", nameof(XMakeElements.target), parameter, parameter[indexOfSpecialCharacter].ToString()); @@ -3731,11 +3731,6 @@ private static string[] ProcessTargetSwitch(string[] parameters) /// private static readonly char[] s_propertyValueSeparator = MSBuildConstants.EqualsChar; - /// - /// This is a set of wildcard chars which can cause a file extension to be invalid - /// - private static readonly char[] s_wildcards = MSBuildConstants.WildcardChars; - /// /// Determines which ToolsVersion was specified on the command line. If more than /// one ToolsVersion was specified, we honor only the final ToolsVersion. diff --git a/src/Shared/AssemblyFolders/AssemblyFoldersEx.cs b/src/Shared/AssemblyFolders/AssemblyFoldersEx.cs index 6820a134454..a15f6e62113 100644 --- a/src/Shared/AssemblyFolders/AssemblyFoldersEx.cs +++ b/src/Shared/AssemblyFolders/AssemblyFoldersEx.cs @@ -82,7 +82,7 @@ internal AssemblyFoldersEx( return; } - bool is64bitOS = EnvironmentUtilities.Is64BitOperatingSystem; + bool is64bitOS = Environment.Is64BitOperatingSystem; bool targeting64bit = targetProcessorArchitecture == ProcessorArchitecture.Amd64 || targetProcessorArchitecture == ProcessorArchitecture.IA64; // The registry lookup should be as follows: @@ -367,7 +367,7 @@ internal static List GatherVersionStrings(string ta // Loop over versions from registry. foreach (string version in versions) { - if ((version.Length > 0) && (String.Equals(version.Substring(0, 1), "v", StringComparison.OrdinalIgnoreCase))) + if ((version.Length > 0) && version[0] is 'v' or 'V') { Version candidateVersion = VersionUtilities.ConvertToVersion(version); diff --git a/src/Shared/AssemblyFolders/AssemblyFoldersFromConfig.cs b/src/Shared/AssemblyFolders/AssemblyFoldersFromConfig.cs index b546b28ccb9..4f79794879d 100644 --- a/src/Shared/AssemblyFolders/AssemblyFoldersFromConfig.cs +++ b/src/Shared/AssemblyFolders/AssemblyFoldersFromConfig.cs @@ -41,7 +41,7 @@ internal AssemblyFoldersFromConfig(string configFile, string targetRuntimeVersio // Platform-agnostic folders first. FindDirectories(assemblyTargets, target => string.IsNullOrEmpty(target.Platform)); - if (EnvironmentUtilities.Is64BitOperatingSystem) + if (Environment.Is64BitOperatingSystem) { if (targeting64Bit) { diff --git a/src/Shared/AssemblyNameExtension.cs b/src/Shared/AssemblyNameExtension.cs index f29b9e8e443..12017934f1f 100644 --- a/src/Shared/AssemblyNameExtension.cs +++ b/src/Shared/AssemblyNameExtension.cs @@ -577,6 +577,11 @@ private static int CompareBaseNamesStringWise(string asString1, string asString2 baseLenThat = asString2.Length; } +#if NET + ReadOnlySpan nameThis = asString1.AsSpan(0, baseLenThis); + ReadOnlySpan nameThat = asString2.AsSpan(0, baseLenThat); + return nameThis.CompareTo(nameThat, StringComparison.OrdinalIgnoreCase); +#else // If the lengths are the same then we can compare without copying. if (baseLenThis == baseLenThat) { @@ -587,6 +592,7 @@ private static int CompareBaseNamesStringWise(string asString1, string asString2 string nameThis = asString1.Substring(0, baseLenThis); string nameThat = asString2.Substring(0, baseLenThat); return string.Compare(nameThis, nameThat, StringComparison.OrdinalIgnoreCase); +#endif } /// @@ -778,24 +784,18 @@ internal bool ComparePublicKeyToken(AssemblyNameExtension that) /// internal static bool ComparePublicKeyTokens(byte[] aPKT, byte[] bPKT) { +#if NET + return aPKT.AsSpan().SequenceEqual(bPKT.AsSpan()); +#else // Some assemblies (real case was interop assembly) may have null PKTs. - if (aPKT == null) - { -#pragma warning disable CA1825 // Avoid zero-length array allocations - aPKT = new byte[0]; -#pragma warning restore CA1825 // Avoid zero-length array allocations - } - if (bPKT == null) - { -#pragma warning disable CA1825 // Avoid zero-length array allocations - bPKT = new byte[0]; -#pragma warning restore CA1825 // Avoid zero-length array allocations - } + aPKT ??= []; + bPKT ??= []; if (aPKT.Length != bPKT.Length) { return false; } + for (int i = 0; i < aPKT.Length; ++i) { if (aPKT[i] != bPKT[i]) @@ -803,7 +803,9 @@ internal static bool ComparePublicKeyTokens(byte[] aPKT, byte[] bPKT) return false; } } + return true; +#endif } /// diff --git a/src/Shared/AwaitExtensions.cs b/src/Shared/AwaitExtensions.cs index 711d2dde947..f864c62bb23 100644 --- a/src/Shared/AwaitExtensions.cs +++ b/src/Shared/AwaitExtensions.cs @@ -19,7 +19,7 @@ internal static class AwaitExtensions /// /// Synchronizes access to the staScheduler field. /// - private static Object s_staSchedulerSync = new Object(); + private static readonly Object s_staSchedulerSync = new Object(); /// /// The singleton STA scheduler object. @@ -155,7 +155,7 @@ private class OneSTAThreadPerTaskScheduler : TaskScheduler /// /// The current queue of tasks. /// - private ConcurrentQueue _queuedTasks = new ConcurrentQueue(); + private readonly ConcurrentQueue _queuedTasks = new ConcurrentQueue(); /// /// Returns the list of queued tasks. diff --git a/src/Shared/CanonicalError.cs b/src/Shared/CanonicalError.cs index 011818c3f1c..4c4a8ea96cf 100644 --- a/src/Shared/CanonicalError.cs +++ b/src/Shared/CanonicalError.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers; using System.Globalization; using System.Text.RegularExpressions; @@ -49,99 +50,141 @@ namespace Microsoft.Build.Shared /// /// <text> : warning [num]: <msg> /// - internal static class CanonicalError + internal static partial class CanonicalError { // Defines the main pattern for matching messages. - private static readonly Lazy s_originCategoryCodeTextExpression = new Lazy( - () => new Regex( - // Beginning of line and any amount of whitespace. - @"^\s*" - // Match a [optional project number prefix 'ddd>'], single letter + colon + remaining filename, or - // string with no colon followed by a colon. - + @"(((?(((\d+>)?[a-zA-Z]?:[^:]*)|([^:]*))):)" - // Origin may also be empty. In this case there's no trailing colon. - + "|())" - // Match the empty string or a string without a colon that ends with a space - + "(?(()|([^:]*? )))" - // Match 'error' or 'warning'. - + @"(?(error|warning))" - // Match anything starting with a space that's not a colon/space, followed by a colon. - // Error code is optional in which case "error"/"warning" can be followed immediately by a colon. - + @"( \s*(?[^: ]*))?\s*:" - // Whatever's left on this line, including colons. - + "(?.*)$", - RegexOptions.IgnoreCase | RegexOptions.Compiled)); - - private static readonly Lazy s_originCategoryCodeTextExpression2 = new Lazy( - () => new Regex( - @"^\s*(?(?.*):(?(?[0-9]*):(?[0-9]*))):(? error| warning):(?.*)", - RegexOptions.IgnoreCase | RegexOptions.Compiled)); + private const string OriginCategoryCodeTextExpressionPattern = + // Beginning of line and any amount of whitespace. + @"^\s*" + // Match a [optional project number prefix 'ddd>'], single letter + colon + remaining filename, or + // string with no colon followed by a colon. + + @"(((?(((\d+>)?[a-zA-Z]?:[^:]*)|([^:]*))):)" + // Origin may also be empty. In this case there's no trailing colon. + + "|())" + // Match the empty string or a string without a colon that ends with a space + + "(?(()|([^:]*? )))" + // Match 'error' or 'warning'. + + @"(?(error|warning))" + // Match anything starting with a space that's not a colon/space, followed by a colon. + // Error code is optional in which case "error"/"warning" can be followed immediately by a colon. + + @"( \s*(?[^: ]*))?\s*:" + // Whatever's left on this line, including colons. + + "(?.*)$"; + + private const string OriginCategoryCodeTextExpression2Pattern = + @"^\s*(?(?.*):(?(?[0-9]*):(?[0-9]*))):(? error| warning):(?.*)"; // Matches and extracts filename and location from an 'origin' element. - private static readonly Lazy s_filenameLocationFromOrigin = new Lazy( - () => new Regex( - "^" // Beginning of line - + @"(\d+>)?" // Optional ddd> project number prefix - + "(?.*)" // Match anything. - + @"\(" // Find a parenthesis. - + @"(?[\,,0-9,-]*)" // Match any combination of numbers and ',' and '-' - + @"\)\s*" // Find the closing paren then any amount of spaces. - + "$", // End-of-line - RegexOptions.IgnoreCase | RegexOptions.Compiled)); + private const string FilenameLocationFromOriginPattern = + "^" // Beginning of line + + @"(\d+>)?" // Optional ddd> project number prefix + + "(?.*)" // Match anything. + + @"\(" // Find a parenthesis. + + @"(?[\,,0-9,-]*)" // Match any combination of numbers and ',' and '-' + + @"\)\s*" // Find the closing paren then any amount of spaces. + + "$"; // End-of-line // Matches location that is a simple number. - private static readonly Lazy s_lineFromLocation = new Lazy( - () => new Regex( // Example: line - "^" // Beginning of line - + "(?[0-9]*)" // Match any number. - + "$", // End-of-line - RegexOptions.IgnoreCase | RegexOptions.Compiled)); + private const string LineFromLocationPattern = // Example: line + "^" // Beginning of line + + "(?[0-9]*)" // Match any number. + + "$"; // End-of-line // Matches location that is a range of lines. - private static readonly Lazy s_lineLineFromLocation = new Lazy( - () => new Regex( // Example: line-line - "^" // Beginning of line - + "(?[0-9]*)" // Match any number. - + "-" // Dash - + "(?[0-9]*)" // Match any number. - + "$", // End-of-line - RegexOptions.IgnoreCase | RegexOptions.Compiled)); + private const string LineLineFromLocationPattern = // Example: line-line + "^" // Beginning of line + + "(?[0-9]*)" // Match any number. + + "-" // Dash + + "(?[0-9]*)" // Match any number. + + "$"; // End-of-line // Matches location that is a line and column - private static readonly Lazy s_lineColFromLocation = new Lazy( - () => new Regex( // Example: line,col - "^" // Beginning of line - + "(?[0-9]*)" // Match any number. - + "," // Comma - + "(?[0-9]*)" // Match any number. - + "$", // End-of-line - RegexOptions.IgnoreCase | RegexOptions.Compiled)); + private const string LineColFromLocationPattern = // Example: line,col + "^" // Beginning of line + + "(?[0-9]*)" // Match any number. + + "," // Comma + + "(?[0-9]*)" // Match any number. + + "$"; // End-of-line // Matches location that is a line and column-range - private static readonly Lazy s_lineColColFromLocation = new Lazy( - () => new Regex( // Example: line,col-col - "^" // Beginning of line - + "(?[0-9]*)" // Match any number. - + "," // Comma - + "(?[0-9]*)" // Match any number. - + "-" // Dash - + "(?[0-9]*)" // Match any number. - + "$", // End-of-line - RegexOptions.IgnoreCase | RegexOptions.Compiled)); + private const string LineColColFromLocationPattern = // Example: line,col-col + "^" // Beginning of line + + "(?[0-9]*)" // Match any number. + + "," // Comma + + "(?[0-9]*)" // Match any number. + + "-" // Dash + + "(?[0-9]*)" // Match any number. + + "$"; // End-of-line // Matches location that is line,col,line,col - private static readonly Lazy s_lineColLineColFromLocation = new Lazy( - () => new Regex( // Example: line,col,line,col - "^" // Beginning of line - + "(?[0-9]*)" // Match any number. - + "," // Comma - + "(?[0-9]*)" // Match any number. - + "," // Dash - + "(?[0-9]*)" // Match any number. - + "," // Dash - + "(?[0-9]*)" // Match any number. - + "$", // End-of-line - RegexOptions.IgnoreCase | RegexOptions.Compiled)); + private const string LineColLineColFromLocationPattern = // Example: line,col,line,col + "^" // Beginning of line + + "(?[0-9]*)" // Match any number. + + "," // Comma + + "(?[0-9]*)" // Match any number. + + "," // Dash + + "(?[0-9]*)" // Match any number. + + "," // Dash + + "(?[0-9]*)" // Match any number. + + "$"; // End-of-line + +#if NET + [GeneratedRegex(OriginCategoryCodeTextExpressionPattern, RegexOptions.IgnoreCase)] + private static partial Regex OriginCategoryCodeTextExpression { get; } + + [GeneratedRegex(OriginCategoryCodeTextExpression2Pattern, RegexOptions.IgnoreCase)] + private static partial Regex OriginCategoryCodeTextExpression2 { get; } + + [GeneratedRegex(FilenameLocationFromOriginPattern, RegexOptions.IgnoreCase)] + private static partial Regex FilenameLocationFromOrigin { get; } + + [GeneratedRegex(LineFromLocationPattern, RegexOptions.IgnoreCase)] + private static partial Regex LineFromLocation { get; } + + [GeneratedRegex(LineLineFromLocationPattern, RegexOptions.IgnoreCase)] + private static partial Regex LineLineFromLocation { get; } + + [GeneratedRegex(LineColFromLocationPattern, RegexOptions.IgnoreCase)] + private static partial Regex LineColFromLocation { get; } + + [GeneratedRegex(LineColColFromLocationPattern, RegexOptions.IgnoreCase)] + private static partial Regex LineColColFromLocation { get; } + + [GeneratedRegex(LineColLineColFromLocationPattern, RegexOptions.IgnoreCase)] + private static partial Regex LineColLineColFromLocation { get; } +#else + private static Regex s_originCategoryCodeTextExpression; + private static Regex OriginCategoryCodeTextExpression => s_originCategoryCodeTextExpression ??= + new Regex(OriginCategoryCodeTextExpressionPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static Regex s_originCategoryCodeTextExpression2; + private static Regex OriginCategoryCodeTextExpression2 => s_originCategoryCodeTextExpression2 ??= + new Regex(OriginCategoryCodeTextExpression2Pattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static Regex s_filenameLocationFromOrigin; + private static Regex FilenameLocationFromOrigin => s_filenameLocationFromOrigin ??= + new Regex(FilenameLocationFromOriginPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static Regex s_lineFromLocation; + private static Regex LineFromLocation => s_lineFromLocation ??= + new Regex(LineFromLocationPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static Regex s_lineLineFromLocation; + private static Regex LineLineFromLocation => s_lineLineFromLocation ??= + new Regex(LineLineFromLocationPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static Regex s_lineColFromLocation; + private static Regex LineColFromLocation => s_lineColFromLocation ??= + new Regex(LineColFromLocationPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static Regex s_lineColColFromLocation; + private static Regex LineColColFromLocation => s_lineColColFromLocation ??= + new Regex(LineColColFromLocationPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static Regex s_lineColLineColFromLocation; + private static Regex LineColLineColFromLocation => s_lineColLineColFromLocation ??= + new Regex(LineColLineColFromLocationPattern, RegexOptions.IgnoreCase | RegexOptions.Compiled); +#endif /// /// Represents the parts of a decomposed canonical message. @@ -234,6 +277,10 @@ private static int ConvertToIntWithDefault(string value) return result; } +#if NET + private static readonly SearchValues s_warningOrError = SearchValues.Create(["warning", "error"], StringComparison.OrdinalIgnoreCase); +#endif + /// /// Decompose an error or warning message into constituent parts. If the message isn't in the canonical form, return null. /// @@ -256,8 +303,12 @@ internal static Parts Parse(string message) // If a tool has a large amount of output that isn't an error or warning (eg., "dir /s %hugetree%") // the regex below is slow. It's faster to pre-scan for "warning" and "error" // and bail out if neither are present. - if (message.IndexOf("warning", StringComparison.OrdinalIgnoreCase) == -1 && - message.IndexOf("error", StringComparison.OrdinalIgnoreCase) == -1) +#if NET + if (message.AsSpan().IndexOfAny(s_warningOrError) < 0) +#else + if (message.IndexOf("warning", StringComparison.OrdinalIgnoreCase) < 0 && + message.IndexOf("error", StringComparison.OrdinalIgnoreCase) < 0) +#endif { return null; } @@ -283,7 +334,7 @@ internal static Parts Parse(string message) // Here's an example from the Japanese version of LINK.EXE: // AssemblyInfo.cpp : fatal error LNK1106: ???????????? ??????????????: 0x6580 ?????????? // - Match match = s_originCategoryCodeTextExpression.Value.Match(message); + Match match = OriginCategoryCodeTextExpression.Match(message); string category; if (!match.Success) { @@ -292,7 +343,7 @@ internal static Parts Parse(string message) // err.cpp:6:3: error: use of undeclared identifier 'force_an_error' // ----------- ----- --------------------------------------------- // Origin Cat. Text - match = s_originCategoryCodeTextExpression2.Value.Match(message); + match = OriginCategoryCodeTextExpression2.Match(message); if (!match.Success) { return null; @@ -320,7 +371,7 @@ internal static Parts Parse(string message) string[] explodedText = parsedMessage.text.Split(MSBuildConstants.SingleQuoteChar, StringSplitOptions.RemoveEmptyEntries); if (explodedText.Length > 0) { - parsedMessage.code = "G" + explodedText[0].GetHashCode().ToString("X8"); + parsedMessage.code = $"G{explodedText[0].GetHashCode():X8}"; } else { @@ -353,7 +404,7 @@ internal static Parts Parse(string message) // Origin is not a simple file, but it still could be of the form, // foo.cpp(location) - match = s_filenameLocationFromOrigin.Value.Match(origin); + match = FilenameLocationFromOrigin.Match(origin); if (match.Success) { @@ -373,14 +424,14 @@ internal static Parts Parse(string message) // (line,col,line,col) if (location.Length > 0) { - match = s_lineFromLocation.Value.Match(location); + match = LineFromLocation.Match(location); if (match.Success) { parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim()); } else { - match = s_lineLineFromLocation.Value.Match(location); + match = LineLineFromLocation.Match(location); if (match.Success) { parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim()); @@ -388,7 +439,7 @@ internal static Parts Parse(string message) } else { - match = s_lineColFromLocation.Value.Match(location); + match = LineColFromLocation.Match(location); if (match.Success) { parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim()); @@ -396,7 +447,7 @@ internal static Parts Parse(string message) } else { - match = s_lineColColFromLocation.Value.Match(location); + match = LineColColFromLocation.Match(location); if (match.Success) { parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim()); @@ -405,7 +456,7 @@ internal static Parts Parse(string message) } else { - match = s_lineColLineColFromLocation.Value.Match(location); + match = LineColLineColFromLocation.Match(location); if (match.Success) { parsedMessage.line = ConvertToIntWithDefault(match.Groups["LINE"].Value.Trim()); diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index d9d361df7ba..471e782e366 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -99,10 +99,10 @@ protected internal Handshake(HandshakeOptions nodeType) CommunicationsUtilities.Trace("Building handshake for node type {0}, (version {1}): options {2}.", nodeType, handshakeVersion, options); string handshakeSalt = Environment.GetEnvironmentVariable("MSBUILDNODEHANDSHAKESALT"); - CommunicationsUtilities.Trace("Handshake salt is " + handshakeSalt); + CommunicationsUtilities.Trace("Handshake salt is {0}", handshakeSalt); string toolsDirectory = BuildEnvironmentHelper.Instance.MSBuildToolsDirectoryRoot; - CommunicationsUtilities.Trace("Tools directory root is " + toolsDirectory); - salt = CommunicationsUtilities.GetHashCode(handshakeSalt + toolsDirectory); + CommunicationsUtilities.Trace("Tools directory root is {0}", toolsDirectory); + salt = CommunicationsUtilities.GetHashCode($"{handshakeSalt}{toolsDirectory}"); Version fileVersion = new Version(FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion); fileVersionMajor = fileVersion.Major; fileVersionMinor = fileVersion.Minor; @@ -114,7 +114,7 @@ protected internal Handshake(HandshakeOptions nodeType) // This is used as a key, so it does not need to be human readable. public override string ToString() { - return String.Format("{0} {1} {2} {3} {4} {5} {6}", options, salt, fileVersionMajor, fileVersionMinor, fileVersionBuild, fileVersionPrivate, sessionId); + return $"{options} {salt} {fileVersionMajor} {fileVersionMinor} {fileVersionBuild} {fileVersionPrivate} {sessionId}"; } public virtual int[] RetrieveHandshakeComponents() @@ -177,8 +177,14 @@ public string ComputeHash() if (_computedHash == null) { var input = GetKey(); + byte[] utf8 = Encoding.UTF8.GetBytes(input); +#if NET + Span bytes = stackalloc byte[SHA256.HashSizeInBytes]; + SHA256.HashData(utf8, bytes); +#else using var sha = SHA256.Create(); - var bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(input)); + var bytes = sha.ComputeHash(utf8); +#endif _computedHash = Convert.ToBase64String(bytes) .Replace("/", "_") .Replace("=", string.Empty); @@ -210,7 +216,7 @@ internal static class CommunicationsUtilities /// /// Whether to trace communications /// - private static bool s_trace = Traits.Instance.DebugNodeCommunication; + private static readonly bool s_trace = Traits.Instance.DebugNodeCommunication; /// /// Lock trace to ensure we are logging in serial fashion. @@ -585,7 +591,7 @@ internal static int ReadIntForHandshake(this PipeStream stream, byte? byteToAcce #nullable disable #if !FEATURE_APM - internal static async Task ReadAsync(Stream stream, byte[] buffer, int bytesToRead) + internal static async ValueTask ReadAsync(Stream stream, byte[] buffer, int bytesToRead) { int totalBytesRead = 0; while (totalBytesRead < bytesToRead) diff --git a/src/Shared/Constants.cs b/src/Shared/Constants.cs index e435d354935..4aa800ef2d2 100644 --- a/src/Shared/Constants.cs +++ b/src/Shared/Constants.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET +using System.Buffers; +#endif using System.IO; #nullable disable @@ -112,25 +115,31 @@ internal static class MSBuildConstants internal const string ProjectReferenceTargetsOrDefaultTargetsMarker = ".projectReferenceTargetsOrDefaultTargets"; // One-time allocations to avoid implicit allocations for Split(), Trim(). - internal static readonly char[] SemicolonChar = { ';' }; - internal static readonly char[] SpaceChar = { ' ' }; - internal static readonly char[] SingleQuoteChar = { '\'' }; - internal static readonly char[] EqualsChar = { '=' }; - internal static readonly char[] ColonChar = { ':' }; - internal static readonly char[] BackslashChar = { '\\' }; - internal static readonly char[] NewlineChar = { '\n' }; - internal static readonly char[] CrLf = { '\r', '\n' }; - internal static readonly char[] ForwardSlash = { '/' }; - internal static readonly char[] ForwardSlashBackslash = { '/', '\\' }; - internal static readonly char[] WildcardChars = { '*', '?' }; - internal static readonly string[] CharactersForExpansion = { "*", "?", "$(", "@(", "%" }; - internal static readonly char[] CommaChar = { ',' }; - internal static readonly char[] HyphenChar = { '-' }; - internal static readonly char[] DirectorySeparatorChar = { Path.DirectorySeparatorChar }; - internal static readonly char[] DotChar = { '.' }; - internal static readonly string[] EnvironmentNewLine = { Environment.NewLine }; - internal static readonly char[] PipeChar = { '|' }; - internal static readonly char[] PathSeparatorChar = { Path.PathSeparator }; + internal static readonly char[] SemicolonChar = [';']; + internal static readonly char[] SpaceChar = [' ']; + internal static readonly char[] SingleQuoteChar = ['\'']; + internal static readonly char[] EqualsChar = ['=']; + internal static readonly char[] ColonChar = [':']; + internal static readonly char[] BackslashChar = ['\\']; + internal static readonly char[] NewlineChar = ['\n']; + internal static readonly char[] CrLf = ['\r', '\n']; + internal static readonly char[] ForwardSlash = ['/']; + internal static readonly char[] ForwardSlashBackslash = ['/', '\\']; + internal static readonly char[] WildcardChars = ['*', '?']; + internal static readonly string[] CharactersForExpansion = ["*", "?", "$(", "@(", "%"]; + internal static readonly char[] CommaChar = [',']; + internal static readonly char[] HyphenChar = ['-']; + internal static readonly char[] DirectorySeparatorChar = [Path.DirectorySeparatorChar]; + internal static readonly char[] DotChar = ['.']; + internal static readonly string[] EnvironmentNewLine = [Environment.NewLine]; + internal static readonly char[] PipeChar = ['|']; + internal static readonly char[] PathSeparatorChar = [Path.PathSeparator]; + +#if NET + internal static readonly SearchValues InvalidPathChars = SearchValues.Create(Path.GetInvalidPathChars()); +#else + internal static readonly char[] InvalidPathChars = Path.GetInvalidPathChars(); +#endif } internal static class PropertyNames diff --git a/src/Shared/ConversionUtilities.cs b/src/Shared/ConversionUtilities.cs index 10bdc82790e..9f1e756baeb 100644 --- a/src/Shared/ConversionUtilities.cs +++ b/src/Shared/ConversionUtilities.cs @@ -57,6 +57,9 @@ internal static bool ConvertStringToBool(string parameterValue, bool nullOrWhite /// A string byte types formated as X2. internal static string ConvertByteArrayToHex(byte[] bytes) { +#if NET + return Convert.ToHexString(bytes); +#else var sb = new StringBuilder(); foreach (var b in bytes) { @@ -64,6 +67,7 @@ internal static string ConvertByteArrayToHex(byte[] bytes) } return sb.ToString(); +#endif } internal static bool TryConvertStringToBool(string parameterValue, out bool boolValue) @@ -131,7 +135,13 @@ internal static double ConvertDecimalToDouble(string number) /// internal static double ConvertHexToDouble(string number) { - return (double)Int32.Parse(number.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat); + return (double)Int32.Parse( +#if NET + number.AsSpan(2), +#else + number.Substring(2), +#endif + NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat); } /// @@ -172,9 +182,15 @@ private static bool ValidHexNumber(string number, out int value) { bool canConvert = false; value = 0; - if (number.Length >= 3 && number[0] == '0' && (number[1] == 'x' || number[1] == 'X')) + if (number.Length >= 3 && number[0] is '0' && number[1] is 'x' or 'X') { - canConvert = Int32.TryParse(number.Substring(2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat, out value); + canConvert = Int32.TryParse( +#if NET + number.AsSpan(2), +#else + number.Substring(2), +#endif + NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat, out value); } return canConvert; } diff --git a/src/Shared/Debugging/PrintLineDebuggerWriters.cs b/src/Shared/Debugging/PrintLineDebuggerWriters.cs index e0dc425c320..bcdadc22a24 100644 --- a/src/Shared/Debugging/PrintLineDebuggerWriters.cs +++ b/src/Shared/Debugging/PrintLineDebuggerWriters.cs @@ -68,7 +68,7 @@ public CompositeWriter(IEnumerable writers) public static CommonWriterType StdOutWriter = (id, callsite, args) => Console.WriteLine(SimpleFormat(id, callsite, args)); - private static Lazy _artifactsLogs = new Lazy( + private static readonly Lazy _artifactsLogs = new Lazy( () => { var executingAssembly = FileUtilities.ExecutingAssemblyPath; diff --git a/src/Shared/EnvironmentUtilities.cs b/src/Shared/EnvironmentUtilities.cs index 3b275ef40b5..a611a5e9433 100644 --- a/src/Shared/EnvironmentUtilities.cs +++ b/src/Shared/EnvironmentUtilities.cs @@ -8,11 +8,6 @@ namespace Microsoft.Build.Shared { internal static partial class EnvironmentUtilities { - public static bool Is64BitProcess => Marshal.SizeOf() == 8; - - public static bool Is64BitOperatingSystem => - Environment.Is64BitOperatingSystem; - public static bool IsWellKnownEnvironmentDerivedProperty(string propertyName) { return propertyName.StartsWith("MSBUILD", StringComparison.OrdinalIgnoreCase) || diff --git a/src/Shared/EscapingUtilities.cs b/src/Shared/EscapingUtilities.cs index f84ddf86632..8bde0027840 100644 --- a/src/Shared/EscapingUtilities.cs +++ b/src/Shared/EscapingUtilities.cs @@ -26,7 +26,7 @@ internal static class EscapingUtilities /// Optional cache of escaped strings for use when needing to escape in performance-critical scenarios with significant /// expected string reuse. /// - private static Dictionary s_unescapedToEscapedStrings = new Dictionary(StringComparer.Ordinal); + private static readonly Dictionary s_unescapedToEscapedStrings = new Dictionary(StringComparer.Ordinal); private static bool TryDecodeHexDigit(char character, out int value) { diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index d6cd177e8ad..9d97c12de8d 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -26,14 +26,19 @@ internal class FileMatcher private readonly IFileSystem _fileSystem; private const string recursiveDirectoryMatch = "**"; - private static readonly string s_directorySeparator = new string(Path.DirectorySeparatorChar, 1); + private static readonly string s_directorySeparatorString = Path.DirectorySeparatorChar.ToString(); + private static readonly string s_twoDirectorySeparators = s_directorySeparatorString + s_directorySeparatorString; - private static readonly string s_thisDirectory = "." + s_directorySeparator; + private static readonly string s_thisDirectory = $".{s_directorySeparatorString}"; private static readonly char[] s_wildcardCharacters = { '*', '?' }; private static readonly char[] s_wildcardAndSemicolonCharacters = { '*', '?', ';' }; - private static readonly string[] s_propertyAndItemReferences = { "$(", "@(" }; +#if NET + private static readonly SearchValues s_propertyAndItemReferences = SearchValues.Create(["$(", "@("], StringComparison.Ordinal); +#else + private static readonly string[] s_propertyAndItemReferences = ["$(", "@("]; +#endif // on OSX both System.IO.Path separators are '/', so we have to use the literals internal static readonly char[] directorySeparatorCharacters = FileUtilities.Slashes; @@ -45,12 +50,6 @@ internal class FileMatcher private readonly ConcurrentDictionary> _cachedGlobExpansions; private readonly Lazy> _cachedGlobExpansionsLock = new Lazy>(() => new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase)); - /// - /// Cache of the list of invalid path characters, because this method returns a clone (for security reasons) - /// which can cause significant transient allocations - /// - private static readonly char[] s_invalidPathChars = Path.GetInvalidPathChars(); - public const RegexOptions DefaultRegexOptions = RegexOptions.IgnoreCase; private readonly GetFileSystemEntries _getFileSystemEntries; @@ -186,7 +185,7 @@ internal static bool HasWildcards(string filespec) // Choose LastIndexOfAny instead of IndexOfAny because it seems more likely // that wildcards will tend to be towards the right side. - return -1 != filespec.LastIndexOfAny(s_wildcardCharacters); + return filespec.LastIndexOfAny(s_wildcardCharacters) >= 0; } /// @@ -195,10 +194,8 @@ internal static bool HasWildcards(string filespec) internal static bool HasWildcardsSemicolonItemOrPropertyReferences(string filespec) { return - - (-1 != filespec.IndexOfAny(s_wildcardAndSemicolonCharacters)) || - HasPropertyOrItemReferences(filespec) - ; + (filespec.IndexOfAny(s_wildcardAndSemicolonCharacters) >= 0) || + HasPropertyOrItemReferences(filespec); } /// @@ -206,7 +203,12 @@ internal static bool HasWildcardsSemicolonItemOrPropertyReferences(string filesp /// internal static bool HasPropertyOrItemReferences(string filespec) { - return s_propertyAndItemReferences.Any(filespec.Contains); + return +#if NET + filespec.AsSpan().ContainsAny(s_propertyAndItemReferences); +#else + s_propertyAndItemReferences.Any(filespec.Contains); +#endif } /// @@ -288,10 +290,10 @@ private static bool ShouldEnforceMatching(string searchPattern) // extensions that start with the same three characters e.g. "*.htm" would match both "file.htm" and "file.html" // 3) if the ? wildcard is to the left of a period, it matches files with shorter name e.g. ???.txt would match // foo.txt, fo.txt and also f.txt - return searchPattern.IndexOf("?.", StringComparison.Ordinal) != -1 || + return searchPattern.Contains("?.") || ( Path.GetExtension(searchPattern).Length == (3 + 1 /* +1 for the period */) && - searchPattern.IndexOf('*') != -1) || + searchPattern.Contains('*')) || searchPattern.EndsWith("?", StringComparison.Ordinal); } @@ -440,7 +442,7 @@ internal static string GetLongPathName( string path, GetFileSystemEntries getFileSystemEntries) { - if (path.IndexOf("~", StringComparison.Ordinal) == -1) + if (!path.Contains('~')) { // A path with no '~' must not be a short name. return path; @@ -451,15 +453,11 @@ internal static string GetLongPathName( string[] parts = path.Split(directorySeparatorCharacters); string pathRoot; - bool isUnc = path.StartsWith(s_directorySeparator + s_directorySeparator, StringComparison.Ordinal); + bool isUnc = path.StartsWith(s_twoDirectorySeparators, StringComparison.Ordinal); int startingElement; if (isUnc) { - pathRoot = s_directorySeparator + s_directorySeparator; - pathRoot += parts[2]; - pathRoot += s_directorySeparator; - pathRoot += parts[3]; - pathRoot += s_directorySeparator; + pathRoot = $"{s_twoDirectorySeparators}{parts[2]}{s_directorySeparatorString}{parts[3]}{s_directorySeparatorString}"; startingElement = 4; } else @@ -468,7 +466,7 @@ internal static string GetLongPathName( if (path.Length > 2 && path[1] == ':') { // Not relative - pathRoot = parts[0] + s_directorySeparator; + pathRoot = parts[0] + s_directorySeparatorString; startingElement = 1; } else @@ -493,7 +491,7 @@ internal static string GetLongPathName( } else { - if (parts[i].IndexOf("~", StringComparison.Ordinal) == -1) + if (!parts[i].Contains('~')) { // If there's no ~, don't hit the disk. longParts[i - startingElement] = parts[i]; @@ -529,7 +527,7 @@ internal static string GetLongPathName( } } - return pathRoot + string.Join(s_directorySeparator, longParts); + return pathRoot + string.Join(s_directorySeparatorString, longParts); } /// @@ -562,8 +560,7 @@ internal void SplitFileSpec( */ if (recursiveDirectoryMatch == filenamePart) { - wildcardDirectoryPart += recursiveDirectoryMatch; - wildcardDirectoryPart += s_directorySeparator; + wildcardDirectoryPart = $"{wildcardDirectoryPart}{recursiveDirectoryMatch}{s_directorySeparatorString}"; filenamePart = "*.*"; } @@ -1107,7 +1104,7 @@ private static RecursiveStepResult GetFilesRecursiveStep( // or we've reached the end of the wildcard directory elements, considerFiles = true; } - else if (recursionState.RemainingWildcardDirectory.IndexOf(recursiveDirectoryMatch, StringComparison.Ordinal) == 0) + else if (recursionState.RemainingWildcardDirectory.StartsWith(recursiveDirectoryMatch, StringComparison.Ordinal)) { // or, we've reached a "**" so everything else is matched recursively. considerFiles = true; @@ -1211,22 +1208,10 @@ internal static string RegularExpressionFromFileSpec( /// /// True if both parts meet all conditions for a legal filespec. private static bool IsLegalFileSpec(string wildcardDirectoryPart, string filenamePart) => - !HasDotDot(wildcardDirectoryPart) + !wildcardDirectoryPart.Contains("..") && !HasMisplacedRecursiveOperator(wildcardDirectoryPart) && !HasMisplacedRecursiveOperator(filenamePart); - private static bool HasDotDot(string str) - { - for (int i = 0; i < str.Length - 1; i++) - { - if (str[i] == '.' && str[i + 1] == '.') - { - return true; - } - } - return false; - } - private static bool HasMisplacedRecursiveOperator(string str) { for (int i = 0; i < str.Length - 1; i++) @@ -1585,7 +1570,7 @@ internal void GetFileSpecInfo( internal static bool RawFileSpecIsValid(string filespec) { // filespec cannot contain illegal characters - if (-1 != filespec.IndexOfAny(s_invalidPathChars)) + if (filespec.AsSpan().IndexOfAny(MSBuildConstants.InvalidPathChars) >= 0) { return false; } @@ -1595,7 +1580,7 @@ internal static bool RawFileSpecIsValid(string filespec) * * Any path with "..." in it is illegal. */ - if (-1 != filespec.IndexOf("...", StringComparison.Ordinal)) + if (filespec.Contains("...")) { return false; } @@ -1607,12 +1592,12 @@ internal static bool RawFileSpecIsValid(string filespec) * http://www.website.com * */ - int rightmostColon = filespec.LastIndexOf(":", StringComparison.Ordinal); + int rightmostColon = filespec.LastIndexOf(':'); if ( - -1 != rightmostColon - && 1 != rightmostColon) + rightmostColon >= 0 + && rightmostColon != 1) { return false; } @@ -2229,7 +2214,7 @@ internal static string Normalize(string aString) // replace multiple slashes with the OS separator else if (afterSlashesIndex > index) { - sb.Append(s_directorySeparator); + sb.Append(s_directorySeparatorString); } // skip non-slashes @@ -2526,7 +2511,7 @@ private static string[] CreateArrayWithSingleItemIfNotExcluded(string filespecUn Debug.Assert(excludeState.SearchData.RegexFileMatch != null || excludeState.SearchData.DirectoryPattern != null, "Expected Regex or directory pattern to be used for exclude file matching"); excludeState.BaseDirectory = state.BaseDirectory; - excludeState.RemainingWildcardDirectory = recursiveDirectoryMatch + s_directorySeparator; + excludeState.RemainingWildcardDirectory = recursiveDirectoryMatch + s_directorySeparatorString; searchesToExclude.Add(excludeState); } } diff --git a/src/Shared/FileUtilities.cs b/src/Shared/FileUtilities.cs index bbb4413632e..5399e5f9e91 100644 --- a/src/Shared/FileUtilities.cs +++ b/src/Shared/FileUtilities.cs @@ -7,6 +7,9 @@ #else using Microsoft.Build.Shared.Concurrent; #endif +#if NET +using System.Buffers; +#endif using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -71,7 +74,7 @@ public static bool GetIsFileSystemCaseSensitive() { try { - string pathWithUpperCase = Path.Combine(Path.GetTempPath(), "CASESENSITIVETEST" + Guid.NewGuid().ToString("N")); + string pathWithUpperCase = Path.Combine(Path.GetTempPath(), $"CASESENSITIVETEST{Guid.NewGuid():N}"); using (new FileStream(pathWithUpperCase, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 0x1000, FileOptions.DeleteOnClose)) { string lowerCased = pathWithUpperCase.ToLowerInvariant(); @@ -91,20 +94,24 @@ public static bool GetIsFileSystemCaseSensitive() /// Copied from https://github.com/dotnet/corefx/blob/056715ff70e14712419d82d51c8c50c54b9ea795/src/Common/src/System/IO/PathInternal.Windows.cs#L61 /// MSBuild should support the union of invalid path chars across the supported OSes, so builds can have the same behaviour crossplatform: https://github.com/dotnet/msbuild/issues/781#issuecomment-243942514 /// - internal static readonly char[] InvalidPathChars = +#if NET + internal static readonly SearchValues InvalidPathChars = SearchValues.Create( +#else + internal static readonly char[] InvalidPathChars = ( +#endif [ '|', '\0', (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, (char)31 - ]; + ]); /// /// Copied from https://github.com/dotnet/corefx/blob/387cf98c410bdca8fd195b28cbe53af578698f94/src/System.Runtime.Extensions/src/System/IO/Path.Windows.cs#L18 /// MSBuild should support the union of invalid path chars across the supported OSes, so builds can have the same behaviour crossplatform: https://github.com/dotnet/msbuild/issues/781#issuecomment-243942514 /// - internal static readonly char[] InvalidFileNameChars = + internal static readonly char[] InvalidFileNameCharsArray = [ '\"', '<', '>', '|', '\0', (char)1, (char)2, (char)3, (char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)10, @@ -113,6 +120,12 @@ public static bool GetIsFileSystemCaseSensitive() (char)31, ':', '*', '?', '\\', '/' ]; +#if NET + internal static readonly SearchValues InvalidFileNameChars = SearchValues.Create(InvalidFileNameCharsArray); +#else + internal static char[] InvalidFileNameChars => InvalidFileNameCharsArray; +#endif + internal static readonly char[] Slashes = { '/', '\\' }; internal static readonly string DirectorySeparatorString = Path.DirectorySeparatorChar.ToString(); @@ -179,7 +192,7 @@ internal static bool CanWriteToDirectory(string directory) { try { - string testFilePath = Path.Combine(directory, $"MSBuild_{Guid.NewGuid().ToString("N")}_testFile.txt"); + string testFilePath = Path.Combine(directory, $"MSBuild_{Guid.NewGuid():N}_testFile.txt"); FileInfo file = new(testFilePath); file.Directory.Create(); // If the directory already exists, this method does nothing. File.WriteAllText(testFilePath, $"MSBuild process {Process.GetCurrentProcess().Id} successfully wrote to file."); @@ -258,7 +271,11 @@ internal static string EnsureTrailingNoLeadingSlash(string path, int start) return FixFilePath(start < stop && IsSlash(path[stop - 1]) ? path.Substring(start) : +#if NET + string.Concat(path.AsSpan(start), new(in Path.DirectorySeparatorChar))); +#else path.Substring(start) + Path.DirectorySeparatorChar); +#endif } /// @@ -315,7 +332,11 @@ internal static string EnsureQuotes(string path, bool isSingleQuote = true) // Special case: convert the quotes. if (path.Length > 1 && path[0] == convertQuote && path[path.Length - 1] == convertQuote) { +#if NET + path = $"{targetQuote}{path.AsSpan(1, path.Length - 2)}{targetQuote}"; +#else path = $"{targetQuote}{path.Substring(1, path.Length - 2)}{targetQuote}"; +#endif } // Enclose the path in a set of the 'target' quote unless the string is already quoted with the 'target' quotes. else if (path.Length == 1 || path[0] != targetQuote || path[path.Length - 1] != targetQuote) @@ -838,17 +859,23 @@ internal static string NormalizePathForComparisonNoThrow(string path, string cur internal static bool PathIsInvalid(string path) { - if (path.IndexOfAny(InvalidPathChars) >= 0) - { - return true; - } - // Path.GetFileName does not react well to malformed filenames. // For example, Path.GetFileName("a/b/foo:bar") returns bar instead of foo:bar // It also throws exceptions on illegal path characters - var lastDirectorySeparator = path.LastIndexOfAny(Slashes); - - return path.IndexOfAny(InvalidFileNameChars, lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0) >= 0; +#if NET + if (!path.AsSpan().ContainsAny(InvalidPathChars)) + { + int lastDirectorySeparator = path.LastIndexOfAny(Slashes); + return path.AsSpan(lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0).ContainsAny(InvalidFileNameChars); + } +#else + if (path.IndexOfAny(InvalidPathChars) < 0) + { + int lastDirectorySeparator = path.LastIndexOfAny(Slashes); + return path.IndexOfAny(InvalidFileNameChars, lastDirectorySeparator >= 0 ? lastDirectorySeparator + 1 : 0) >= 0; + } +#endif + return true; } /// @@ -1532,12 +1559,20 @@ internal static void ClearFileExistenceCache() internal static void ReadFromStream(this Stream stream, byte[] content, int startIndex, int length) { -#if NET7_0_OR_GREATER +#if NET stream.ReadExactly(content, startIndex, length); #else -#pragma warning disable CA2022 // Avoid inexact read with 'Stream.Read' - stream.Read(content, 0, length); -#pragma warning restore CA2022 // Avoid inexact read with 'Stream.Read' + int bytesRead = 0; + while (bytesRead < length) + { + int read = stream.Read(content, startIndex + bytesRead, length - bytesRead); + if (read == 0) + { + throw new EndOfStreamException(); + } + + bytesRead += read; + } #endif } } diff --git a/src/Shared/FileUtilitiesRegex.cs b/src/Shared/FileUtilitiesRegex.cs index 76e283a1a2a..c35f1f9ed5f 100644 --- a/src/Shared/FileUtilitiesRegex.cs +++ b/src/Shared/FileUtilitiesRegex.cs @@ -52,7 +52,11 @@ internal static bool StartsWithDrivePattern(string pattern) // first character must be a letter, // second character must be a ":" return pattern.Length >= 2 && +#if NET + char.IsAsciiLetter(pattern[0]) && +#else ((pattern[0] >= 'A' && pattern[0] <= 'Z') || (pattern[0] >= 'a' && pattern[0] <= 'z')) && +#endif pattern[1] == ':'; } diff --git a/src/Shared/FrameworkLocationHelper.cs b/src/Shared/FrameworkLocationHelper.cs index 2bc28819c6e..458272bcc30 100644 --- a/src/Shared/FrameworkLocationHelper.cs +++ b/src/Shared/FrameworkLocationHelper.cs @@ -97,7 +97,7 @@ internal static class FrameworkLocationHelper private const string dotNetFrameworkRegistryKeyV20 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionV20; internal static string dotNetFrameworkVersionFolderPrefixV30 = NativeMethodsShared.IsWindows ? "v3.0" : "3.0"; // v3.0 is for WinFx. - private static string s_dotNetFrameworkRegistryKeyV30 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionFolderPrefixV30 + "\\Setup"; + private static readonly string s_dotNetFrameworkRegistryKeyV30 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionFolderPrefixV30 + "\\Setup"; #if FEATURE_WIN32_REGISTRY private const string fallbackDotNetFrameworkSdkRegistryInstallPath = "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows"; @@ -111,7 +111,7 @@ internal static class FrameworkLocationHelper private const string fullDotNetFrameworkSdkRegistryPathForV35ToolsOnManagedToolsSDK80A = "HKEY_LOCAL_MACHINE\\" + dotNetFrameworkSdkRegistryPathForV35ToolsOnManagedToolsSDK80A; internal static string dotNetFrameworkVersionFolderPrefixV35 = NativeMethodsShared.IsWindows ? "v3.5" : "3.5"; // v3.5 is for Orcas. - private static string s_dotNetFrameworkRegistryKeyV35 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionFolderPrefixV35; + private static readonly string s_dotNetFrameworkRegistryKeyV35 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionFolderPrefixV35; internal const string fullDotNetFrameworkSdkRegistryKeyV35OnVS10 = fullDotNetFrameworkSdkRegistryPathForV35ToolsOnWinSDK70A; internal const string fullDotNetFrameworkSdkRegistryKeyV35OnVS11 = fullDotNetFrameworkSdkRegistryPathForV35ToolsOnManagedToolsSDK80A; @@ -512,7 +512,7 @@ private static string FallbackDotNetFrameworkSdkInstallPath fallbackDotNetFrameworkSdkRegistryInstallPath, fallbackDotNetFrameworkSdkInstallKeyValue); - if (EnvironmentUtilities.Is64BitProcess && s_fallbackDotNetFrameworkSdkInstallPath == null) + if (Environment.Is64BitProcess && s_fallbackDotNetFrameworkSdkInstallPath == null) { // Since we're 64-bit, what we just checked was the 64-bit fallback key -- so now let's // check the 32-bit one too, just in case. @@ -773,8 +773,7 @@ internal static string FindDotNetFrameworkPath( { if (!NativeMethodsShared.IsWindows) { - if (!string.IsNullOrEmpty(prefix) - && prefix.Substring(0, 1).Equals("v", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(prefix) && prefix[0] is 'v' or 'V') { prefix = prefix.Substring(1); } @@ -813,8 +812,12 @@ internal static string FindDotNetFrameworkPath( // the path is something like 'C:\MyPath\64\Framework64'. 9 = length of 'Framework', to make the index match // the location of the '64'. int indexOf64 = indexOfFramework64 + 9; - string tempLocation = baseLocation; - baseLocation = tempLocation.Substring(0, indexOf64) + tempLocation.Substring(indexOf64 + 2, tempLocation.Length - indexOf64 - 2); + baseLocation = +#if NET + string.Concat(baseLocation.AsSpan(0, indexOf64), baseLocation.AsSpan(indexOf64 + 2)); +#else + baseLocation.Substring(0, indexOf64) + baseLocation.Substring(indexOf64 + 2); +#endif } else if (indexOfFramework64 == -1 && architecture == DotNetFrameworkArchitecture.Bitness64) { diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 7c8994d4522..5882c5489ad 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -268,12 +268,12 @@ internal abstract class LogMessagePacketBase : INodePacket /// /// Dictionary of methods used to read BuildEventArgs. /// - private static Dictionary s_readMethodCache = new Dictionary(); + private static readonly Dictionary s_readMethodCache = new Dictionary(); /// /// Dictionary of methods used to write BuildEventArgs. /// - private static Dictionary s_writeMethodCache = new Dictionary(); + private static readonly Dictionary s_writeMethodCache = new Dictionary(); /// /// Delegate for translating targetfinished events. diff --git a/src/Shared/MSBuildNameIgnoreCaseComparer.cs b/src/Shared/MSBuildNameIgnoreCaseComparer.cs index 9517b5f2646..e8e9a65b9eb 100644 --- a/src/Shared/MSBuildNameIgnoreCaseComparer.cs +++ b/src/Shared/MSBuildNameIgnoreCaseComparer.cs @@ -64,6 +64,9 @@ public bool Equals(string compareToString, string constrainedString, int start, return false; } +#if NET + return compareToString.AsSpan().Equals(constrainedString.AsSpan(start, lengthToCompare), StringComparison.OrdinalIgnoreCase); +#else if (lengthToCompare != compareToString.Length) { return false; @@ -104,6 +107,7 @@ public bool Equals(string compareToString, string constrainedString, int start, } return true; +#endif } /// diff --git a/src/Shared/Modifiers.cs b/src/Shared/Modifiers.cs index a6c203525d2..d7e77955644 100644 --- a/src/Shared/Modifiers.cs +++ b/src/Shared/Modifiers.cs @@ -69,7 +69,7 @@ internal static class ItemSpecModifiers DefiningProjectExtension }; - private static HashSet s_tableOfItemSpecModifiers = new HashSet(All, StringComparer.OrdinalIgnoreCase); + private static readonly HashSet s_tableOfItemSpecModifiers = new HashSet(All, StringComparer.OrdinalIgnoreCase); /// /// Indicates if the given name is reserved for an item-spec modifier. diff --git a/src/Shared/NamedPipeUtil.cs b/src/Shared/NamedPipeUtil.cs index 5c5290b40c8..cea8f16f7b5 100644 --- a/src/Shared/NamedPipeUtil.cs +++ b/src/Shared/NamedPipeUtil.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; using System.Diagnostics; using System.IO; @@ -10,10 +11,12 @@ internal static class NamedPipeUtil { internal static string GetPlatformSpecificPipeName(int? processId = null) { - if (processId is null) - { - processId = Process.GetCurrentProcess().Id; - } + processId ??= +#if NET + Environment.ProcessId; +#else + Process.GetCurrentProcess().Id; +#endif string pipeName = $"MSBuild{processId}"; diff --git a/src/Shared/NodeEndpointOutOfProcBase.cs b/src/Shared/NodeEndpointOutOfProcBase.cs index 7e8e8506a8c..69498a2fa9c 100644 --- a/src/Shared/NodeEndpointOutOfProcBase.cs +++ b/src/Shared/NodeEndpointOutOfProcBase.cs @@ -495,7 +495,7 @@ private void PacketPumpProc() { if (localPipeServer.IsConnected) { -#if NETCOREAPP // OperatingSystem.IsWindows() is new in .NET 5.0 +#if NET // OperatingSystem.IsWindows() is new in .NET 5.0 if (OperatingSystem.IsWindows()) #endif { @@ -523,7 +523,7 @@ private void RunReadLoop(Stream localReadPipe, Stream localWritePipe, #if FEATURE_APM IAsyncResult result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); #else - Task readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length); + Task readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length).AsTask(); #endif bool exitLoop = false; @@ -612,7 +612,7 @@ private void RunReadLoop(Stream localReadPipe, Stream localWritePipe, #if FEATURE_APM result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); #else - readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length); + readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length).AsTask(); #endif } diff --git a/src/Shared/PlatformNegotiation.cs b/src/Shared/PlatformNegotiation.cs index 18b9b361e3b..c21db16aa0e 100644 --- a/src/Shared/PlatformNegotiation.cs +++ b/src/Shared/PlatformNegotiation.cs @@ -61,18 +61,18 @@ internal static string GetNearestPlatform(string overridePlatformValue, string r // Prioritize platformLookupTable **metadata** attached to the ProjectReference item // before the current project's table. We do this to allow per-ProjectReference fine tuning. else if (projectReferenceLookupTable != null && - projectReferenceLookupTable.ContainsKey(currentProjectPlatform) && - projectReferencePlatforms.Contains(projectReferenceLookupTable[currentProjectPlatform])) + projectReferenceLookupTable.TryGetValue(currentProjectPlatform, out string? value) && + projectReferencePlatforms.Contains(value)) { - buildProjectReferenceAs = projectReferenceLookupTable[currentProjectPlatform]; + buildProjectReferenceAs = value; log?.LogMessageFromResources(MessageImportance.Low, "GetCompatiblePlatform.FoundMappingInTable", currentProjectPlatform, buildProjectReferenceAs, projectReferenceLookupTableMetadata); } // Current project's translation table follows else if (currentProjectLookupTable != null && - currentProjectLookupTable.ContainsKey(currentProjectPlatform) && - projectReferencePlatforms.Contains(currentProjectLookupTable[currentProjectPlatform])) + currentProjectLookupTable.TryGetValue(currentProjectPlatform, out value) && + projectReferencePlatforms.Contains(value)) { - buildProjectReferenceAs = currentProjectLookupTable[currentProjectPlatform]; + buildProjectReferenceAs = value; log?.LogMessageFromResources(MessageImportance.Low, "GetCompatiblePlatform.FoundMappingInTable", currentProjectPlatform, buildProjectReferenceAs, platformLookupTable); } // AnyCPU if possible diff --git a/src/Shared/ProcessExtensions.cs b/src/Shared/ProcessExtensions.cs index 362a8b0a8c1..d13ebbac5fe 100644 --- a/src/Shared/ProcessExtensions.cs +++ b/src/Shared/ProcessExtensions.cs @@ -11,7 +11,7 @@ internal static class ProcessExtensions { public static void KillTree(this Process process, int timeoutMilliseconds) { -#if NETCOREAPP +#if NET process.Kill(entireProcessTree: true); #else if (NativeMethodsShared.IsWindows) diff --git a/src/Shared/ProjectWriter.cs b/src/Shared/ProjectWriter.cs index fa2e6f76a1c..d4351f3a76c 100644 --- a/src/Shared/ProjectWriter.cs +++ b/src/Shared/ProjectWriter.cs @@ -15,7 +15,7 @@ namespace Microsoft.Build.Shared /// This class is used to save MSBuild project files. It contains special handling for MSBuild notations that are not saved /// correctly by the XML DOM's default save mechanism. /// - internal sealed class ProjectWriter : XmlTextWriter + internal sealed partial class ProjectWriter : XmlTextWriter { #region Regular expressions for item vector transforms @@ -28,9 +28,8 @@ internal sealed class ProjectWriter : XmlTextWriter // Note that the pattern is more strict than the rules for valid XML element names. internal const string itemTypeOrMetadataNameSpecification = @"[A-Za-z_][A-Za-z_0-9\-]*"; - // the portion of an item transform that is the function that we wish to execute on the item - internal const string itemFunctionNameSpecification = @"[A-Za-z]*"; - + // regular expression used to match item vector transforms + // internal for unit testing only // description of an item vector transform, including the optional separator specification private const string itemVectorTransformSpecification = @"(?@\(\s*) @@ -40,15 +39,10 @@ internal sealed class ProjectWriter : XmlTextWriter (?\s*\))"; // ) - // regular expression used to match item vector transforms - // internal for unit testing only - internal static readonly Lazy itemVectorTransformPattern = new Lazy( - () => - new Regex(itemVectorTransformSpecification, - RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture | RegexOptions.Compiled)); - // description of an item vector transform, including the optional separator specification, but with no (named) capturing // groups -- see the WriteString() method for details + // regular expression used to match item vector transforms, with no (named) capturing groups + // internal for unit testing only private const string itemVectorTransformRawSpecification = @"@\(\s* (" + itemTypeOrMetadataNameSpecification + @") @@ -56,12 +50,21 @@ internal sealed class ProjectWriter : XmlTextWriter (\s*,\s*'[^']*')? \s*\)"; - // regular expression used to match item vector transforms, with no (named) capturing groups - // internal for unit testing only - internal static readonly Lazy itemVectorTransformRawPattern = new Lazy( - () => - new Regex(itemVectorTransformRawSpecification, - RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture | RegexOptions.Compiled)); +#if NET + [GeneratedRegex(itemVectorTransformSpecification, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture)] + private static partial Regex ItemVectorTransformRegex { get; } + + [GeneratedRegex(itemVectorTransformRawSpecification, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture)] + private static partial Regex ItemVectorTransformRawRegex { get; } +#else + private static Regex ItemVectorTransformRegex => itemVectorTransformPattern ??= + new Regex(itemVectorTransformSpecification, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture | RegexOptions.Compiled); + private static Regex itemVectorTransformPattern; + + private static Regex ItemVectorTransformRawRegex => itemVectorTransformRawPattern ??= + new Regex(itemVectorTransformRawSpecification, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture | RegexOptions.Compiled); + private static Regex itemVectorTransformRawPattern; +#endif /************************************************************************************************************************** * WARNING: The regular expressions above MUST be kept in sync with the expressions in the ItemExpander class. @@ -130,14 +133,14 @@ internal void Initialize(XmlDocument project, XmlDeclaration projectRootElementD /// public override void WriteString(string text) { - MatchCollection itemVectorTransforms = itemVectorTransformRawPattern.Value.Matches(text); + MatchCollection itemVectorTransforms = ItemVectorTransformRawRegex.Matches(text); // if the string contains any item vector transforms if (itemVectorTransforms.Count > 0) { // separate out the text that surrounds the transforms // NOTE: use the Regex with no (named) capturing groups, otherwise Regex.Split() will split on them - string[] surroundingTextPieces = itemVectorTransformRawPattern.Value.Split(text); + string[] surroundingTextPieces = ItemVectorTransformRawRegex.Split(text); ErrorUtilities.VerifyThrow(itemVectorTransforms.Count == (surroundingTextPieces.Length - 1), "We must have two pieces of surrounding text for every item vector transform found."); @@ -149,7 +152,7 @@ public override void WriteString(string text) base.WriteString(surroundingTextPieces[i]); // break up the transform into its constituent pieces - Match itemVectorTransform = itemVectorTransformPattern.Value.Match(itemVectorTransforms[i].Value); + Match itemVectorTransform = ItemVectorTransformRegex.Match(itemVectorTransforms[i].Value); ErrorUtilities.VerifyThrow(itemVectorTransform.Success, "Item vector transform must be matched by both the raw and decorated regular expressions."); diff --git a/src/Shared/PropertyParser.cs b/src/Shared/PropertyParser.cs index a9e1c29d72c..48110e0ca4c 100644 --- a/src/Shared/PropertyParser.cs +++ b/src/Shared/PropertyParser.cs @@ -47,10 +47,10 @@ internal static bool GetTable(TaskLoggingHelper log, string parameterName, strin // whitespace from beginning and end of both name and value. (When authoring a // project/targets file, people like to use whitespace and newlines to pretty up // the file format.) - if (indexOfEqualsSign != -1) + if (indexOfEqualsSign >= 0) { - propertyName = propertyNameValuePair.Substring(0, indexOfEqualsSign).Trim(); - propertyValue = propertyNameValuePair.Substring(indexOfEqualsSign + 1).Trim(); + propertyName = propertyNameValuePair.AsSpan(0, indexOfEqualsSign).Trim().ToString(); + propertyValue = propertyNameValuePair.AsSpan(indexOfEqualsSign + 1).Trim().ToString(); } // Make sure we have a property name and property value (though the value is allowed to be blank). @@ -103,8 +103,8 @@ internal static bool GetTableWithEscaping(TaskLoggingHelper log, string paramete // whitespace from beginning and end of both name and value. (When authoring a // project/targets file, people like to use whitespace and newlines to pretty up // the file format.) - string propertyName = propertyNameValueString.Substring(0, indexOfEqualsSign).Trim(); - string propertyValue = EscapingUtilities.Escape(propertyNameValueString.Substring(indexOfEqualsSign + 1).Trim()); + string propertyName = propertyNameValueString.AsSpan(0, indexOfEqualsSign).Trim().ToString(); + string propertyValue = EscapingUtilities.Escape(propertyNameValueString.AsSpan(indexOfEqualsSign + 1).Trim().ToString()); // Make sure we have a property name and property value (though the value is allowed to be blank). if (propertyName.Length == 0) diff --git a/src/Shared/RegisteredTaskObjectCacheBase.cs b/src/Shared/RegisteredTaskObjectCacheBase.cs index d7d25b7d962..10391f5d336 100644 --- a/src/Shared/RegisteredTaskObjectCacheBase.cs +++ b/src/Shared/RegisteredTaskObjectCacheBase.cs @@ -22,7 +22,7 @@ internal class RegisteredTaskObjectCacheBase /// /// The cache for AppDomain lifetime objects. /// - private static Lazy> s_appDomainLifetimeObjects = new Lazy>(); + private static readonly Lazy> s_appDomainLifetimeObjects = new Lazy>(); /// /// The cache for Build lifetime objects. diff --git a/src/Shared/ResourceUtilities.cs b/src/Shared/ResourceUtilities.cs index e046c7d4c5a..7ff74c83f19 100644 --- a/src/Shared/ResourceUtilities.cs +++ b/src/Shared/ResourceUtilities.cs @@ -122,7 +122,7 @@ internal static string ExtractMessageCode(bool msbuildCodeOnly, string message, if (i < message.Length) { - message = message.Substring(i, message.Length - i); + message = message.Substring(i); } return message; diff --git a/src/Shared/StringUtils.cs b/src/Shared/StringUtils.cs index 10152956f27..cb109d5f9d6 100644 --- a/src/Shared/StringUtils.cs +++ b/src/Shared/StringUtils.cs @@ -16,6 +16,10 @@ internal static class StringUtils /// Random generated string of the specified length. public static string GenerateRandomString(int length) { +#if NET + return string.Create(length, 0, static (dest, _) => + Random.Shared.GetItems("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_", dest)); +#else // Base64, 2^6 = 64 const int eachStringCharEncodesBites = 6; const int eachByteHasBits = 8; @@ -32,6 +36,7 @@ public static string GenerateRandomString(int length) string randomBase64String = Convert.ToBase64String(randomBytes).Replace('/', '_'); return randomBase64String.Substring(0, length); +#endif } /// @@ -45,10 +50,14 @@ public static string RemoveLastInstanceOf(this string fromString, string substri { int lastOccurrenceIndex = fromString.LastIndexOf(substring, comparison); - if (lastOccurrenceIndex != -1) + if (lastOccurrenceIndex >= 0) { - fromString = fromString.Substring(0, lastOccurrenceIndex) + - fromString.Substring(lastOccurrenceIndex + substring.Length); + fromString = +#if NET + $"{fromString.AsSpan(0, lastOccurrenceIndex)}{fromString.AsSpan(lastOccurrenceIndex + substring.Length)}"; +#else + $"{fromString.Substring(0, lastOccurrenceIndex)}{fromString.Substring(lastOccurrenceIndex + substring.Length)}"; +#endif } return fromString; diff --git a/src/Shared/TempFileUtilities.cs b/src/Shared/TempFileUtilities.cs index 190f0dddf2b..4c607dfd2b4 100644 --- a/src/Shared/TempFileUtilities.cs +++ b/src/Shared/TempFileUtilities.cs @@ -82,7 +82,7 @@ private static string CreateFolderUnderTemp() /// internal static string GetTemporaryDirectory(bool createDirectory = true, string subfolder = null) { - string temporaryDirectory = Path.Combine(TempFileDirectory, "Temporary" + Guid.NewGuid().ToString("N"), subfolder ?? string.Empty); + string temporaryDirectory = Path.Combine(TempFileDirectory, $"Temporary{Guid.NewGuid():N}", subfolder ?? string.Empty); if (createDirectory) { diff --git a/src/Shared/ToolsetElement.cs b/src/Shared/ToolsetElement.cs index 0e2f7591b55..70002db51f0 100644 --- a/src/Shared/ToolsetElement.cs +++ b/src/Shared/ToolsetElement.cs @@ -279,7 +279,7 @@ private void UpdateOSMap(ConfigurationElement element) { if (element.ElementInformation.LineNumber != 0) { - locationString = String.Format("{0} ({1})", element.ElementInformation.Source, element.ElementInformation.LineNumber); + locationString = $"{element.ElementInformation.Source} ({element.ElementInformation.LineNumber})"; } else { diff --git a/src/Shared/Tracing.cs b/src/Shared/Tracing.cs index 87b3a3b7e67..154c6ff6f05 100644 --- a/src/Shared/Tracing.cs +++ b/src/Shared/Tracing.cs @@ -25,7 +25,7 @@ internal static class Tracing /// /// A dictionary of named counters /// - private static Dictionary s_counts; + private static readonly Dictionary s_counts; /// /// Last time logging happened @@ -35,7 +35,7 @@ internal static class Tracing /// /// How often to log /// - private static TimeSpan s_interval; + private static readonly TimeSpan s_interval; /// /// A place callers can put something worth logging later @@ -45,7 +45,7 @@ internal static class Tracing /// /// Short name of the current assembly - to distinguish statics when this type is shared into different assemblies /// - private static string s_currentAssemblyName; + private static readonly string s_currentAssemblyName; #pragma warning restore 649 #if DEBUG @@ -95,7 +95,7 @@ internal static void Slot(string tag, string value) [Conditional("DEBUG")] internal static void Slot(string tag, KeyValuePair value) { - Slot(tag, value.Key.ToString() + "=" + value.Key.ToString()); + Slot(tag, $"{value.Key}={value.Key}"); } /// diff --git a/src/Shared/TypeLoader.cs b/src/Shared/TypeLoader.cs index 3a3013b36aa..7c5efa8ce3d 100644 --- a/src/Shared/TypeLoader.cs +++ b/src/Shared/TypeLoader.cs @@ -31,12 +31,12 @@ internal class TypeLoader /// /// Cache to keep track of the assemblyLoadInfos based on a given type filter. /// - private static ConcurrentDictionary, ConcurrentDictionary> s_cacheOfLoadedTypesByFilter = new ConcurrentDictionary, ConcurrentDictionary>(); + private static readonly ConcurrentDictionary, ConcurrentDictionary> s_cacheOfLoadedTypesByFilter = new ConcurrentDictionary, ConcurrentDictionary>(); /// /// Cache to keep track of the assemblyLoadInfos based on a given type filter for assemblies which are to be loaded for reflectionOnlyLoads. /// - private static ConcurrentDictionary, ConcurrentDictionary> s_cacheOfReflectionOnlyLoadedTypesByFilter = new ConcurrentDictionary, ConcurrentDictionary>(); + private static readonly ConcurrentDictionary, ConcurrentDictionary> s_cacheOfReflectionOnlyLoadedTypesByFilter = new ConcurrentDictionary, ConcurrentDictionary>(); /// /// Type filter for this typeloader @@ -45,7 +45,7 @@ internal class TypeLoader private static MetadataLoadContext _context; - private static string[] runtimeAssemblies = findRuntimeAssembliesWithMicrosoftBuildFramework(); + private static readonly string[] runtimeAssemblies = findRuntimeAssembliesWithMicrosoftBuildFramework(); private static string microsoftBuildFrameworkPath; // We need to append Microsoft.Build.Framework from next to the executing assembly first to make sure it's loaded before the runtime variant. @@ -56,10 +56,7 @@ private static string[] findRuntimeAssembliesWithMicrosoftBuildFramework() string[] msbuildAssemblies = Directory.GetFiles(msbuildDirectory, "*.dll"); string[] runtimeAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll"); - List runtimeAssembliesList = new(runtimeAssemblies); - runtimeAssembliesList.AddRange(msbuildAssemblies); - - return runtimeAssembliesList.ToArray(); + return [.. runtimeAssemblies, .. msbuildAssemblies]; } /// diff --git a/src/Shared/UnitTests/NativeMethodsShared_Tests.cs b/src/Shared/UnitTests/NativeMethodsShared_Tests.cs index efb5d7297ab..b96ee0700d4 100644 --- a/src/Shared/UnitTests/NativeMethodsShared_Tests.cs +++ b/src/Shared/UnitTests/NativeMethodsShared_Tests.cs @@ -123,7 +123,7 @@ public void SetCurrentDirectoryDoesNotSetNonexistentFolder() { for (int i = 0; i < 10; i++) { - nonexistentDirectory = Path.Combine(currentDirectory, "foo", "bar", "baz") + Guid.NewGuid(); + nonexistentDirectory = $"{Path.Combine(currentDirectory, "foo", "bar", "baz")}{Guid.NewGuid()}"; if (!Directory.Exists(nonexistentDirectory)) { diff --git a/src/Shared/UnitTests/XmakeAttributes_Tests.cs b/src/Shared/UnitTests/XmakeAttributes_Tests.cs index 25e7cb3a50d..0ccd5682302 100644 --- a/src/Shared/UnitTests/XmakeAttributes_Tests.cs +++ b/src/Shared/UnitTests/XmakeAttributes_Tests.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; using Microsoft.Build.Shared; using Shouldly; @@ -130,7 +131,7 @@ public void TestMergeRuntimeValuesCurrentToCore() public void TestArchitectureValuesMatch() { string currentArchitecture = XMakeAttributes.GetCurrentMSBuildArchitecture(); - string notCurrentArchitecture = EnvironmentUtilities.Is64BitProcess ? XMakeAttributes.MSBuildArchitectureValues.x86 : XMakeAttributes.MSBuildArchitectureValues.x64; + string notCurrentArchitecture = Environment.Is64BitProcess ? XMakeAttributes.MSBuildArchitectureValues.x86 : XMakeAttributes.MSBuildArchitectureValues.x64; Assert.True(XMakeAttributes.ArchitectureValuesMatch(XMakeAttributes.MSBuildArchitectureValues.any, XMakeAttributes.MSBuildArchitectureValues.currentArchitecture)); Assert.True(XMakeAttributes.ArchitectureValuesMatch(XMakeAttributes.MSBuildArchitectureValues.any, XMakeAttributes.MSBuildArchitectureValues.x64)); @@ -145,7 +146,7 @@ public void TestArchitectureValuesMatch() public void TestMergeArchitectureValues() { string currentArchitecture = XMakeAttributes.GetCurrentMSBuildArchitecture(); - string notCurrentArchitecture = EnvironmentUtilities.Is64BitProcess ? XMakeAttributes.MSBuildArchitectureValues.x86 : XMakeAttributes.MSBuildArchitectureValues.x64; + string notCurrentArchitecture = Environment.Is64BitProcess ? XMakeAttributes.MSBuildArchitectureValues.x86 : XMakeAttributes.MSBuildArchitectureValues.x64; string mergedArchitecture; Assert.True(XMakeAttributes.TryMergeArchitectureValues(XMakeAttributes.MSBuildArchitectureValues.any, XMakeAttributes.MSBuildArchitectureValues.currentArchitecture, out mergedArchitecture)); diff --git a/src/Shared/VersionUtilities.cs b/src/Shared/VersionUtilities.cs index 91c4721f00c..99e1e36774d 100644 --- a/src/Shared/VersionUtilities.cs +++ b/src/Shared/VersionUtilities.cs @@ -74,7 +74,7 @@ internal static Version ConvertToVersion(string version, bool throwException) // Versions must have at least a Major and a Minor (e.g. 10.0), so if it's // just one number without a decimal, add a decimal and a 0. Random strings // like "tmp" will be filtered out in the Parse() or TryParse() steps - if (version.IndexOf(".") == -1) + if (version.IndexOf('.') == -1) { version += ".0"; } diff --git a/src/Shared/XMakeElements.cs b/src/Shared/XMakeElements.cs index 991feb5796c..40d22e96234 100644 --- a/src/Shared/XMakeElements.cs +++ b/src/Shared/XMakeElements.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.Buffers; using System.Collections.Generic; #nullable disable @@ -35,7 +36,12 @@ internal static class XMakeElements internal const string usingTaskBody = "Task"; internal const string sdk = "Sdk"; - internal static readonly char[] InvalidTargetNameCharacters = [ '$', '@', '(', ')', '%', '*', '?', '.' ]; +#if NET + internal static readonly SearchValues InvalidTargetNameCharacters = SearchValues.Create( +#else + internal static readonly char[] InvalidTargetNameCharacters = ( +#endif + ['$', '@', '(', ')', '%', '*', '?', '.']); // Names that cannot be used as property or item names because they are reserved internal static readonly HashSet ReservedItemNames = diff --git a/src/StringTools/InternableString.cs b/src/StringTools/InternableString.cs index 7e657d56cdb..d19afe2a662 100644 --- a/src/StringTools/InternableString.cs +++ b/src/StringTools/InternableString.cs @@ -387,7 +387,7 @@ private static unsafe uint GetHashCodeHelper(char* charPtr, int length, uint has [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint RotateLeft(uint value, int offset) { -#if NETCOREAPP +#if NET return System.Numerics.BitOperations.RotateLeft(value, offset); #else // Copied from System\Numerics\BitOperations.cs in dotnet/runtime as the routine is not available on .NET Framework. diff --git a/src/StringTools/WeakStringCacheInterner.cs b/src/StringTools/WeakStringCacheInterner.cs index 2eb3fd23231..cc53c501ce3 100644 --- a/src/StringTools/WeakStringCacheInterner.cs +++ b/src/StringTools/WeakStringCacheInterner.cs @@ -85,7 +85,7 @@ public string InternableToString(ref InternableString candidate) string expectedString = candidate.ExpensiveConvertToString(); if (!String.Equals(internedString, expectedString)) { - throw new InvalidOperationException(String.Format("Interned string {0} should have been {1}", internedString, expectedString)); + throw new InvalidOperationException($"Interned string {internedString} should have been {expectedString}"); } #endif @@ -137,12 +137,12 @@ public string FormatStatistics() if (_internCallCountsByString != null) { - result.AppendLine(string.Format("\n{0}{1}{0}", new string('=', 41 - (title.Length / 2)), title)); - result.AppendLine(string.Format("||{0,50}|{1,20:N0}|{2,8}|", "WeakStringCache Hits", _regularInternHits, "hits")); - result.AppendLine(string.Format("||{0,50}|{1,20:N0}|{2,8}|", "WeakStringCache Misses", _regularInternMisses, "misses")); - result.AppendLine(string.Format("||{0,50}|{1,20:N0}|{2,8}|", "Eliminated Strings*", _internEliminatedStrings, "strings")); - result.AppendLine(string.Format("||{0,50}|{1,20:N0}|{2,8}|", "Eliminated Chars", _internEliminatedChars, "chars")); - result.AppendLine(string.Format("||{0,50}|{1,20:N0}|{2,8}|", "Estimated Eliminated Bytes", _internEliminatedChars * 2, "bytes")); + result.AppendLine($"\n{new string('=', 41 - (title.Length / 2))}{title}{new string('=', 41 - (title.Length / 2))}"); + result.AppendLine($"||{"WeakStringCache Hits",50}|{_regularInternHits,20:N0}|{"hits",8}|"); + result.AppendLine($"||{"WeakStringCache Misses",50}|{_regularInternMisses,20:N0}|{"misses",8}|"); + result.AppendLine($"||{"Eliminated Strings*",50}|{_internEliminatedStrings,20:N0}|{"strings",8}|"); + result.AppendLine($"||{"Eliminated Chars",50}|{_internEliminatedChars,20:N0}|{"chars",8}|"); + result.AppendLine($"||{"Estimated Eliminated Bytes",50}|{_internEliminatedChars * 2,20:N0}|{"bytes",8}|"); result.AppendLine("Elimination assumes that strings provided were unique objects."); result.AppendLine("|---------------------------------------------------------------------------------|"); @@ -158,7 +158,7 @@ public string FormatStatistics() WeakStringCache.DebugInfo debugInfo = _weakStringCache.GetDebugInfo(); result.AppendLine("WeakStringCache statistics:"); - result.AppendLine(string.Format("String count live/collected/total = {0}/{1}/{2}", debugInfo.LiveStringCount, debugInfo.CollectedStringCount, debugInfo.LiveStringCount + debugInfo.CollectedStringCount)); + result.AppendLine($"String count live/collected/total = {debugInfo.LiveStringCount}/{debugInfo.CollectedStringCount}/{debugInfo.LiveStringCount + debugInfo.CollectedStringCount}"); } else { diff --git a/src/Tasks/AppConfig/BindingRedirect.cs b/src/Tasks/AppConfig/BindingRedirect.cs index 8be7d2413c7..d29808b9b59 100644 --- a/src/Tasks/AppConfig/BindingRedirect.cs +++ b/src/Tasks/AppConfig/BindingRedirect.cs @@ -43,17 +43,21 @@ internal void Read(XmlReader reader) try { - if (dashPosition != -1) + if (dashPosition >= 0) { // This is a version range. - OldVersionLow = new Version(oldVersion.Substring(0, dashPosition)); - OldVersionHigh = new Version(oldVersion.Substring(dashPosition + 1)); +#if NET + OldVersionLow = Version.Parse(oldVersion.AsSpan(0, dashPosition)); + OldVersionHigh = Version.Parse(oldVersion.AsSpan(dashPosition + 1)); +#else + OldVersionLow = Version.Parse(oldVersion.Substring(0, dashPosition)); + OldVersionHigh = Version.Parse(oldVersion.Substring(dashPosition + 1)); +#endif } else { // This is a single version. - OldVersionLow = new Version(oldVersion); - OldVersionHigh = new Version(oldVersion); + OldVersionLow = OldVersionHigh = new Version(oldVersion); } } catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) diff --git a/src/Tasks/AssemblyDependency/AssemblyFoldersFromConfig/AssemblyFoldersFromConfigCache.cs b/src/Tasks/AssemblyDependency/AssemblyFoldersFromConfig/AssemblyFoldersFromConfigCache.cs index 1453e8ab6d8..ded0c8b46bf 100644 --- a/src/Tasks/AssemblyDependency/AssemblyFoldersFromConfig/AssemblyFoldersFromConfigCache.cs +++ b/src/Tasks/AssemblyDependency/AssemblyFoldersFromConfig/AssemblyFoldersFromConfigCache.cs @@ -2,7 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Immutable; +using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.Build.Shared; @@ -19,7 +19,7 @@ internal class AssemblyFoldersFromConfigCache /// /// Set of files in ALL AssemblyFolderFromConfig directories /// - private readonly ImmutableHashSet _filesInDirectories = ImmutableHashSet.Empty; + private readonly HashSet _filesInDirectories; /// /// File exists delegate we are replacing @@ -45,12 +45,12 @@ internal AssemblyFoldersFromConfigCache(AssemblyFoldersFromConfig assemblyFolder } else { - _filesInDirectories = assemblyFoldersFromConfig.AsParallel() + _filesInDirectories = new(assemblyFoldersFromConfig.AsParallel() .Where(assemblyFolder => FileUtilities.DirectoryExistsNoThrow(assemblyFolder.DirectoryPath)) .SelectMany( assemblyFolder => - Directory.GetFiles(assemblyFolder.DirectoryPath, "*.*", SearchOption.TopDirectoryOnly)) - .ToImmutableHashSet(StringComparer.OrdinalIgnoreCase); + Directory.GetFiles(assemblyFolder.DirectoryPath, "*.*", SearchOption.TopDirectoryOnly)), + StringComparer.OrdinalIgnoreCase); } } diff --git a/src/Tasks/AssemblyDependency/AssemblyInformation.cs b/src/Tasks/AssemblyDependency/AssemblyInformation.cs index 531d95a8fc5..7f6fa93139c 100644 --- a/src/Tasks/AssemblyDependency/AssemblyInformation.cs +++ b/src/Tasks/AssemblyDependency/AssemblyInformation.cs @@ -50,7 +50,7 @@ internal class AssemblyInformation : DisposableBase #endif #if !FEATURE_ASSEMBLYLOADCONTEXT - private static string s_targetFrameworkAttribute = "System.Runtime.Versioning.TargetFrameworkAttribute"; + private const string s_targetFrameworkAttribute = "System.Runtime.Versioning.TargetFrameworkAttribute"; #endif #if !FEATURE_ASSEMBLYLOADCONTEXT // Borrowed from genman. diff --git a/src/Tasks/AssemblyDependency/GlobalAssemblyCache.cs b/src/Tasks/AssemblyDependency/GlobalAssemblyCache.cs index daeaae94b3b..1a6656c607d 100644 --- a/src/Tasks/AssemblyDependency/GlobalAssemblyCache.cs +++ b/src/Tasks/AssemblyDependency/GlobalAssemblyCache.cs @@ -283,7 +283,7 @@ internal static string GetLocation( bool useGacRarCache = Environment.GetEnvironmentVariable("MSBUILDDISABLEGACRARCACHE") == null; if (buildEngine != null && useGacRarCache) { - string key = "44d78b60-3bbe-48fe-9493-04119ebf515f" + "|" + targetProcessorArchitecture.ToString() + "|" + targetedRuntimeVersion.ToString() + "|" + fullFusionName.ToString() + "|" + specificVersion.ToString(); + string key = $"44d78b60-3bbe-48fe-9493-04119ebf515f|{targetProcessorArchitecture}|{targetedRuntimeVersion}|{fullFusionName}|{specificVersion}"; fusionNameToResolvedPath = buildEngine.GetRegisteredTaskObject(key, RegisteredTaskObjectLifetime.Build) as ConcurrentDictionary; if (fusionNameToResolvedPath == null) { diff --git a/src/Tasks/AssemblyDependency/Reference.cs b/src/Tasks/AssemblyDependency/Reference.cs index d9ba3671e32..78648beda64 100644 --- a/src/Tasks/AssemblyDependency/Reference.cs +++ b/src/Tasks/AssemblyDependency/Reference.cs @@ -725,7 +725,7 @@ internal HashSet RemappedAssemblyNames() /// internal void AddPreUnificationVersion(String referencePath, Version version, UnificationReason reason) { - string key = referencePath + version.ToString() + reason.ToString(); + string key = $"{referencePath}{version}{reason}"; // Only add a reference, version, and reason once. UnificationVersion unificationVersion; diff --git a/src/Tasks/AssemblyDependency/ReferenceTable.cs b/src/Tasks/AssemblyDependency/ReferenceTable.cs index de1a4b26b20..9e3b0c07d36 100644 --- a/src/Tasks/AssemblyDependency/ReferenceTable.cs +++ b/src/Tasks/AssemblyDependency/ReferenceTable.cs @@ -789,9 +789,7 @@ private static void TryGatherAssemblyNameEssentials(string fusionName, ref Assem return; } - string newFusionName = String.Format(CultureInfo.InvariantCulture, - "{0}, Version={1}, Culture={2}, PublicKeyToken={3}", - name, version, culture, publicKeyToken); + string newFusionName = $"{name}, Version={version}, Culture={culture}, PublicKeyToken={publicKeyToken}"; // Now try to convert to an AssemblyName. try @@ -812,19 +810,20 @@ private static void TryGatherAssemblyNameEssentials(string fusionName, ref Assem private static void TryGetAssemblyNameComponent(string fusionName, string component, ref string value) { int position = fusionName.IndexOf(component + "=", StringComparison.Ordinal); - if (position == -1) + if (position < 0) { return; } + position += component.Length + 1; - int nextDelimiter = fusionName.IndexOfAny([',', ' '], position); - if (nextDelimiter == -1) + int nextDelimiter = fusionName.AsSpan(position).IndexOfAny(',', ' '); + if (nextDelimiter < 0) { value = fusionName.Substring(position); } else { - value = fusionName.Substring(position, nextDelimiter - position); + value = fusionName.Substring(position, nextDelimiter); } } @@ -2301,20 +2300,11 @@ private static bool CompareRefToDef(AssemblyName @ref, AssemblyName def) byte[] rpkt = @ref.GetPublicKeyToken(); byte[] dpkt = def.GetPublicKeyToken(); - - if (rpkt.Length != dpkt.Length) + if (!rpkt.AsSpan().SequenceEqual(dpkt.AsSpan())) { return false; } - for (int i = 0; i < rpkt.Length; i++) - { - if (rpkt[i] != dpkt[i]) - { - return false; - } - } - if (@ref.Version != def.Version) { return false; diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index b322052e386..b331aeb86be 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -1311,6 +1311,9 @@ internal static string ByteArrayToString(byte[] a) return null; } +#if NET + return Convert.ToHexStringLower(a); +#else var buffer = new StringBuilder(a.Length * 2); for (int i = 0; i < a.Length; ++i) { @@ -1318,6 +1321,7 @@ internal static string ByteArrayToString(byte[] a) } return buffer.ToString(); +#endif } /// @@ -1536,22 +1540,22 @@ private void LogInputs() } Log.LogMessage(importance, property, "AppConfigFile"); - Log.LogMessage(importance, indent + AppConfigFile); + Log.LogMessage(importance, $"{indent}{AppConfigFile}"); Log.LogMessage(importance, property, "AutoUnify"); - Log.LogMessage(importance, indent + AutoUnify.ToString()); + Log.LogMessage(importance, $"{indent}{AutoUnify}"); Log.LogMessage(importance, property, "CopyLocalDependenciesWhenParentReferenceInGac"); - Log.LogMessage(importance, indent + _copyLocalDependenciesWhenParentReferenceInGac); + Log.LogMessage(importance, $"{indent}{_copyLocalDependenciesWhenParentReferenceInGac}"); Log.LogMessage(importance, property, "FindDependencies"); - Log.LogMessage(importance, indent + _findDependencies); + Log.LogMessage(importance, $"{indent}{_findDependencies}"); Log.LogMessage(importance, property, "TargetProcessorArchitecture"); - Log.LogMessage(importance, indent + TargetProcessorArchitecture); + Log.LogMessage(importance, $"{indent}{TargetProcessorArchitecture}"); Log.LogMessage(importance, property, "StateFile"); - Log.LogMessage(importance, indent + StateFile); + Log.LogMessage(importance, $"{indent}{StateFile}"); Log.LogMessage(importance, property, "InstalledAssemblySubsetTables"); foreach (ITaskItem installedAssemblySubsetTable in InstalledAssemblySubsetTables) @@ -1561,33 +1565,33 @@ private void LogInputs() } Log.LogMessage(importance, property, "IgnoreInstalledAssemblySubsetTable"); - Log.LogMessage(importance, indent + _ignoreDefaultInstalledAssemblySubsetTables); + Log.LogMessage(importance, $"{indent}{_ignoreDefaultInstalledAssemblySubsetTables}"); Log.LogMessage(importance, property, "TargetFrameworkSubsets"); foreach (string subset in _targetFrameworkSubsets) { - Log.LogMessage(importance, indent + subset); + Log.LogMessage(importance, $"{indent}{subset}"); } Log.LogMessage(importance, property, "FullTargetFrameworkSubsetNames"); foreach (string subset in FullTargetFrameworkSubsetNames) { - Log.LogMessage(importance, indent + subset); + Log.LogMessage(importance, $"{indent}{subset}"); } Log.LogMessage(importance, property, "ProfileName"); - Log.LogMessage(importance, indent + ProfileName); + Log.LogMessage(importance, $"{indent}{ProfileName}"); Log.LogMessage(importance, property, "FullFrameworkFolders"); foreach (string fullFolder in FullFrameworkFolders) { - Log.LogMessage(importance, indent + fullFolder); + Log.LogMessage(importance, $"{indent}{fullFolder}"); } Log.LogMessage(importance, property, "LatestTargetFrameworkDirectories"); foreach (string latestFolder in _latestTargetFrameworkDirectories) { - Log.LogMessage(importance, indent + latestFolder); + Log.LogMessage(importance, $"{indent}{latestFolder}"); } Log.LogMessage(importance, property, "ProfileTablesLocation"); @@ -2041,7 +2045,7 @@ private void LogConflict(Reference reference, string fusionName, StringBuilder l break; } } - #endregion +#endregion #region StateFile /// @@ -2843,7 +2847,7 @@ internal static string GenerateSubSetName(string[] frameworkSubSetNames, ITaskIt } } - return String.Join(", ", subsetNames.ToArray()); + return String.Join(", ", subsetNames); } /// diff --git a/src/Tasks/BootstrapperUtil/BootstrapperBuilder.cs b/src/Tasks/BootstrapperUtil/BootstrapperBuilder.cs index c22ab73bc9f..8e7c85a73b3 100644 --- a/src/Tasks/BootstrapperUtil/BootstrapperBuilder.cs +++ b/src/Tasks/BootstrapperUtil/BootstrapperBuilder.cs @@ -492,9 +492,9 @@ internal string[] Cultures Refresh(); } - List list = _cultures.Values.Select(v => v.ToString()).ToList(); - list.Sort(); - return list.ToArray(); + string[] array = _cultures.Values.Select(v => v.ToString()).ToArray(); + Array.Sort(array); + return array; } } @@ -603,7 +603,7 @@ private void RefreshProducts() foreach (string strSubDirectory in Directory.GetDirectories(packagePath)) { int nStartIndex = packagePath.Length; - if ((strSubDirectory.ToCharArray())[nStartIndex] == System.IO.Path.DirectorySeparatorChar) + if (strSubDirectory[nStartIndex] == System.IO.Path.DirectorySeparatorChar) { nStartIndex++; } @@ -948,7 +948,7 @@ private void ExploreDirectory(string strSubDirectory, XmlElement rootElement, st } XmlNode langNode = langDoc.SelectSingleNode(BOOTSTRAPPER_PREFIX + ":Package", _xmlNamespaceManager); - Debug.Assert(langNode != null, string.Format(CultureInfo.CurrentCulture, "Unable to find a package node in {0}", strLangManifestFilename)); + Debug.Assert(langNode != null, $"Unable to find a package node in {strLangManifestFilename}"); if (langNode != null) { XmlElement langElement = (XmlElement)(_document.ImportNode(langNode, true)); @@ -1040,7 +1040,7 @@ private void ExploreDirectory(string strSubDirectory, XmlElement rootElement, st } else { - Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "Validation results already added for Product Code '{0}'", productCodeAttribute)); + Debug.WriteLine($"Validation results already added for Product Code '{productCodeAttribute}'"); } } } @@ -1512,7 +1512,7 @@ private bool BuildPackages(BuildSettings settings, XmlElement configElement, Res // Add the file size to the PackageFileNode XmlAttribute sizeAttribute = packageFileNode.OwnerDocument.CreateAttribute("Size"); var fi = new FileInfo(packageFileSource.Value); - sizeAttribute.Value = "" + (fi.Length.ToString(CultureInfo.InvariantCulture)); + sizeAttribute.Value = fi.Length.ToString(CultureInfo.InvariantCulture); MergeAttribute(packageFileNode, sizeAttribute); } } @@ -1547,7 +1547,7 @@ private bool BuildPackages(BuildSettings settings, XmlElement configElement, Res if (configElement != null) { configElement.AppendChild(configElement.OwnerDocument.ImportNode(node, true)); - DumpXmlToFile(node, string.Format(CultureInfo.CurrentCulture, "{0}.{1}.xml", package.Product.ProductCode, package.Culture)); + DumpXmlToFile(node, $"{package.Product.ProductCode}.{package.Culture}.xml"); } } @@ -1602,13 +1602,17 @@ private static string ByteArrayToString(byte[] byteArray) return null; } - var output = new StringBuilder(byteArray.Length); +#if NET + return Convert.ToHexString(byteArray); +#else + var output = new StringBuilder(byteArray.Length * 2); foreach (byte byteValue in byteArray) { output.Append(byteValue.ToString("X02", CultureInfo.InvariantCulture)); } return output.ToString(); +#endif } private static string GetFileHash(string filePath) @@ -1983,7 +1987,7 @@ private static Stream GetEmbeddedResourceStream(string name) { Assembly a = Assembly.GetExecutingAssembly(); Stream s = a.GetManifestResourceStream(String.Format(CultureInfo.InvariantCulture, "{0}.{1}", typeof(BootstrapperBuilder).Namespace, name)); - Debug.Assert(s != null, String.Format(CultureInfo.CurrentCulture, "EmbeddedResource '{0}' not found", name)); + Debug.Assert(s != null, $"EmbeddedResource '{name}' not found"); return s; } diff --git a/src/Tasks/BootstrapperUtil/BuildMessage.cs b/src/Tasks/BootstrapperUtil/BuildMessage.cs index bc4272ed8d8..db219a6a7d0 100644 --- a/src/Tasks/BootstrapperUtil/BuildMessage.cs +++ b/src/Tasks/BootstrapperUtil/BuildMessage.cs @@ -13,9 +13,14 @@ namespace Microsoft.Build.Tasks.Deployment.Bootstrapper /// /// Represents messages that occur during the BootstrapperBuilder's Build operation. /// - public class BuildMessage : IBuildMessage + public partial class BuildMessage : IBuildMessage { - private static readonly Regex s_msbuildMessageCodePattern = new Regex(@"(\d+)$"); +#if NET + [GeneratedRegex(@"\d+$")] + private static partial Regex MsbuildMessageCodePattern { get; } +#else + private static Regex MsbuildMessageCodePattern { get; } = new Regex(@"\d+$"); +#endif private BuildMessage(BuildMessageSeverity severity, string message, string helpKeyword, string helpCode) { @@ -25,7 +30,7 @@ private BuildMessage(BuildMessageSeverity severity, string message, string helpK HelpCode = helpCode; if (!String.IsNullOrEmpty(HelpCode)) { - Match match = s_msbuildMessageCodePattern.Match(HelpCode); + Match match = MsbuildMessageCodePattern.Match(HelpCode); if (match.Success) { HelpId = int.Parse(match.Value, CultureInfo.InvariantCulture); diff --git a/src/Tasks/BootstrapperUtil/Product.cs b/src/Tasks/BootstrapperUtil/Product.cs index 6e9d0e1ceca..bf788c343b5 100644 --- a/src/Tasks/BootstrapperUtil/Product.cs +++ b/src/Tasks/BootstrapperUtil/Product.cs @@ -168,7 +168,7 @@ internal void AddPackage(Package package) } else { - Debug.WriteLine(String.Format(CultureInfo.CurrentCulture, "A package with culture '{0}' has already been added to product '{1}'", package.Culture.ToLowerInvariant(), ProductCode)); + Debug.WriteLine($"A package with culture '{package.Culture.ToLowerInvariant()}' has already been added to product '{ProductCode}'"); } } diff --git a/src/Tasks/BootstrapperUtil/Util.cs b/src/Tasks/BootstrapperUtil/Util.cs index 973ca74a61f..bfd845b297f 100644 --- a/src/Tasks/BootstrapperUtil/Util.cs +++ b/src/Tasks/BootstrapperUtil/Util.cs @@ -124,7 +124,12 @@ public static string GetDefaultPath(string visualStudioVersion) { dotIndex = visualStudioVersion.Length; } + +#if NET + if (Int32.TryParse(visualStudioVersion.AsSpan(0, dotIndex), out int majorVersion) && (majorVersion < 11)) +#else if (Int32.TryParse(visualStudioVersion.Substring(0, dotIndex), out int majorVersion) && (majorVersion < 11)) +#endif { visualStudioVersion = BOOTSTRAPPER_REGISTRY_PATH_VERSION_VS2010; } diff --git a/src/Tasks/ComReference.cs b/src/Tasks/ComReference.cs index 340be0e84b1..67e50ba29d6 100644 --- a/src/Tasks/ComReference.cs +++ b/src/Tasks/ComReference.cs @@ -263,7 +263,7 @@ internal static bool GetTypeLibNameForITypeLib(TaskLoggingHelper log, bool silen if (typeLibName.Length >= 4) { - if (string.Equals(typeLibName.Substring(typeLibName.Length - 4), ".dll", StringComparison.OrdinalIgnoreCase)) + if (typeLibName.AsSpan().EndsWith(".dll".AsSpan(), StringComparison.OrdinalIgnoreCase)) { typeLibName = typeLibName.Substring(0, typeLibName.Length - 4); } diff --git a/src/Tasks/CultureInfoCache.cs b/src/Tasks/CultureInfoCache.cs index aed8b824d4f..53448eb1ba3 100644 --- a/src/Tasks/CultureInfoCache.cs +++ b/src/Tasks/CultureInfoCache.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Globalization; -#if NET5_0_OR_GREATER +#if NET using System.Linq; using Microsoft.Build.Framework; #endif @@ -23,7 +23,7 @@ namespace Microsoft.Build.Tasks /// internal static class CultureInfoCache { -#if !NET5_0_OR_GREATER +#if !NET private static readonly Lazy> ValidCultureNames = new Lazy>(() => InitializeValidCultureNames()); #endif @@ -63,7 +63,7 @@ private static HashSet InitializeValidCultureNames() /// True if the culture is determined to be valid. internal static bool IsValidCultureString(string name) { -#if NET5_0_OR_GREATER +#if NET try { // GetCultureInfo throws if the culture doesn't exist diff --git a/src/Tasks/DownloadFile.cs b/src/Tasks/DownloadFile.cs index 71dc72e4c91..9028bd1f386 100644 --- a/src/Tasks/DownloadFile.cs +++ b/src/Tasks/DownloadFile.cs @@ -156,7 +156,7 @@ private async Task DownloadAsync(Uri uri, CancellationToken cancellationToken) { response.EnsureSuccessStatusCode(); } -#if NET6_0_OR_GREATER +#if NET catch (HttpRequestException) { throw; @@ -203,7 +203,7 @@ private async Task DownloadAsync(Uri uri, CancellationToken cancellationToken) Log.LogMessageFromResources(MessageImportance.High, "DownloadFile.Downloading", SourceUrl, destinationFile.FullName, response.Content.Headers.ContentLength); #pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter using (Stream responseStream = await response.Content.ReadAsStreamAsync( -#if NET6_0_OR_GREATER +#if NET cancellationToken #endif ).ConfigureAwait(false)) @@ -260,7 +260,7 @@ private static bool IsRetriable(Exception exception, out Exception actualExcepti } } -#if NET6_0_OR_GREATER +#if NET // net5.0 included StatusCode in the HttpRequestException. switch (httpRequestException.StatusCode) { @@ -329,7 +329,7 @@ private bool TryGetFileName(HttpResponseMessage response, out string filename) return !String.IsNullOrWhiteSpace(filename); } -#if !NET6_0_OR_GREATER +#if !NET /// /// Represents a wrapper around the that also contains the . /// DEPRECATED as of net5.0, which included the StatusCode in the HttpRequestException class. diff --git a/src/Tasks/FileIO/GetFileHash.cs b/src/Tasks/FileIO/GetFileHash.cs index 2d1ec4f5308..b975be5fd5e 100644 --- a/src/Tasks/FileIO/GetFileHash.cs +++ b/src/Tasks/FileIO/GetFileHash.cs @@ -143,7 +143,7 @@ internal static byte[] ComputeHash(Func algorithmFactory, string using (var stream = File.OpenRead(filePath)) using (var algorithm = algorithmFactory()) { -#if NET5_0_OR_GREATER +#if NET return algorithm.ComputeHashAsync(stream, ct).Result; #else return algorithm.ComputeHash(stream); diff --git a/src/Tasks/FindInvalidProjectReferences.cs b/src/Tasks/FindInvalidProjectReferences.cs index 791ac147af6..607df8b83cd 100644 --- a/src/Tasks/FindInvalidProjectReferences.cs +++ b/src/Tasks/FindInvalidProjectReferences.cs @@ -13,17 +13,22 @@ namespace Microsoft.Build.Tasks /// /// Returns the reference assembly paths to the various frameworks /// - public class FindInvalidProjectReferences : TaskExtension + public partial class FindInvalidProjectReferences : TaskExtension { #region Fields + private const string PlatformMonikerFormatPattern = @"(?^[^,]*),\s*Version=(?.*)"; + /// /// Regex for breaking up the platform moniker /// Example: XNA, Version=8.0 /// - private static readonly Regex s_platformMonikerFormat = new Regex( - @"(?^[^,]*),\s*Version=(?.*)", - RegexOptions.IgnoreCase); +#if NET + [GeneratedRegex(PlatformMonikerFormatPattern, RegexOptions.IgnoreCase)] + private static partial Regex PlatformMonikerRegex { get; } +#else + private static Regex PlatformMonikerRegex { get; } = new Regex(PlatformMonikerFormatPattern, RegexOptions.IgnoreCase); +#endif /// /// Reference moniker metadata @@ -111,7 +116,7 @@ public override bool Execute() /// private static bool ParseMoniker(string reference, out string platformIdentity, out Version platformVersion) { - Match match = s_platformMonikerFormat.Match(reference); + Match match = PlatformMonikerRegex.Match(reference); platformIdentity = String.Empty; bool parsedVersion = false; diff --git a/src/Tasks/FormatVersion.cs b/src/Tasks/FormatVersion.cs index 88cfc595cb5..86182dca239 100644 --- a/src/Tasks/FormatVersion.cs +++ b/src/Tasks/FormatVersion.cs @@ -47,9 +47,14 @@ public override bool Execute() { OutputVersion = "1.0.0.0"; } - else if (Version.EndsWith("*", StringComparison.Ordinal)) + else if (Version[Version.Length - 1] == '*') { - OutputVersion = Version.Substring(0, Version.Length - 1) + Revision.ToString("G", CultureInfo.InvariantCulture); + OutputVersion = +#if NET + string.Create(CultureInfo.InvariantCulture, $"{Version.AsSpan(0, Version.Length - 1)}{Revision:G}"); +#else + Version.Substring(0, Version.Length - 1) + Revision.ToString("G", CultureInfo.InvariantCulture); +#endif } else { diff --git a/src/Tasks/GenerateApplicationManifest.cs b/src/Tasks/GenerateApplicationManifest.cs index 1c32b6feb36..c91044032b0 100644 --- a/src/Tasks/GenerateApplicationManifest.cs +++ b/src/Tasks/GenerateApplicationManifest.cs @@ -241,7 +241,7 @@ private bool AddIsolatedComReferences(ApplicationManifest manifest) } } - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "GenerateApplicationManifest.AddIsolatedComReferences t={0}", Environment.TickCount - t1)); + Util.WriteLog($"GenerateApplicationManifest.AddIsolatedComReferences t={Environment.TickCount - t1}"); return success; } @@ -326,7 +326,7 @@ private bool AddClickOnceFiles(ApplicationManifest manifest) } } - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "GenerateApplicationManifest.AddClickOnceFiles t={0}", Environment.TickCount - t1)); + Util.WriteLog($"GenerateApplicationManifest.AddClickOnceFiles t={Environment.TickCount - t1}"); return true; } diff --git a/src/Tasks/GenerateManifestBase.cs b/src/Tasks/GenerateManifestBase.cs index a58209b9053..96ae300d52a 100644 --- a/src/Tasks/GenerateManifestBase.cs +++ b/src/Tasks/GenerateManifestBase.cs @@ -519,7 +519,7 @@ private bool ResolveFiles() return false; } - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "GenerateManifestBase.ResolveFiles t={0}", Environment.TickCount - t1)); + Util.WriteLog($"GenerateManifestBase.ResolveFiles t={Environment.TickCount - t1}"); return true; } @@ -625,8 +625,8 @@ private bool WriteManifest() return false; } - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "GenerateManifestBase.WriteManifest t={0}", Environment.TickCount - t1)); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "Total time to generate manifest '{1}': t={0}", Environment.TickCount - _startTime, Path.GetFileName(OutputManifest.ItemSpec))); + Util.WriteLog($"GenerateManifestBase.WriteManifest t={Environment.TickCount - t1}"); + Util.WriteLog($"Total time to generate manifest '{Path.GetFileName(OutputManifest.ItemSpec)}': t={Environment.TickCount - _startTime}"); return true; } } diff --git a/src/Tasks/GenerateResource.cs b/src/Tasks/GenerateResource.cs index b888da26a59..b7e981bea5f 100644 --- a/src/Tasks/GenerateResource.cs +++ b/src/Tasks/GenerateResource.cs @@ -145,7 +145,7 @@ public sealed partial class GenerateResource : TaskExtension, IIncrementalTask #if FEATURE_RESGEN // Our calculation is not quite correct. Using a number substantially less than 32768 in order to // be sure we don't exceed it. - private static int s_maximumCommandLength = 28000; + private const int s_maximumCommandLength = 28000; #endif // FEATURE_RESGEN // Contains the list of paths from which inputs will not be taken into account during up-to-date check. @@ -1793,7 +1793,7 @@ private bool NeedSeparateAppDomain() string resolvedTypeName = typeName; // This type name might be an alias, so first resolve that if any. - int indexOfSeperator = typeName.IndexOf(",", StringComparison.Ordinal); + int indexOfSeperator = typeName.IndexOf(','); if (indexOfSeperator != -1) { @@ -2017,18 +2017,13 @@ private bool DetermineWhetherSerializedObjectLoads(string data) } #endif - /// - /// Chars that should be ignored in the nicely justified block of base64 - /// - private static readonly char[] s_specialChars = [' ', '\r', '\n']; - /// /// Turns the nicely justified block of base64 found in a resx into a byte array. /// Copied from fx\src\winforms\managed\system\winforms\control.cs /// private static byte[] ByteArrayFromBase64WrappedString(string text) { - if (text.IndexOfAny(s_specialChars) != -1) + if (text.AsSpan().IndexOfAny(' ', '\r', '\n') != -1) // Chars that should be ignored in the nicely justified block of base64 { StringBuilder sb = new StringBuilder(text.Length); for (int i = 0; i < text.Length; i++) @@ -3044,7 +3039,7 @@ private void ReadResources(String filename, bool shouldUseSourcePath, String out default: // We should never get here, we've already checked the format - Debug.Fail("Unknown format " + format.ToString()); + Debug.Fail($"Unknown format {format}"); return; } _logger.LogMessageFromResources(MessageImportance.Low, "GenerateResource.ReadResourceMessage", reader.resources.Count, filename); @@ -3325,7 +3320,7 @@ private void WriteResources(ReaderInfo reader, String filename) #if FEATURE_RESXREADER_LIVEDESERIALIZATION WriteResources(reader, new ResXResourceWriter(filename)); // closes writer for us #else - _logger.LogError(format.ToString() + " not supported on .NET Core MSBuild"); + _logger.LogError($"{format} not supported on .NET Core MSBuild"); #endif break; @@ -3339,7 +3334,7 @@ private void WriteResources(ReaderInfo reader, String filename) default: // We should never get here, we've already checked the format - Debug.Fail("Unknown format " + format.ToString()); + Debug.Fail($"Unknown format {format}"); break; } } @@ -3695,7 +3690,13 @@ private void ReadTextResources(ReaderInfo reader, String fileName) } try { - ch = (char)UInt16.Parse(new String(hex), NumberStyles.HexNumber, CultureInfo.CurrentCulture); + ch = (char)UInt16.Parse( +#if NET + hex, +#else + new String(hex), +#endif + NumberStyles.HexNumber, CultureInfo.CurrentCulture); } catch (FormatException) { @@ -4013,7 +4014,7 @@ internal int LinePosition get { return column; } } } - #endregion // Code from ResGen.EXE +#endregion // Code from ResGen.EXE } #if !FEATURE_ASSEMBLYLOADCONTEXT @@ -4150,7 +4151,7 @@ public Type GetType(string name, bool throwOnError, bool ignoreCase) result = a.GetType(name, false, ignoreCase); if (result == null) { - int indexOfComma = name.IndexOf(",", StringComparison.Ordinal); + int indexOfComma = name.IndexOf(','); if (indexOfComma != -1) { string shortName = name.Substring(0, indexOfComma); diff --git a/src/Tasks/GetAssemblyIdentity.cs b/src/Tasks/GetAssemblyIdentity.cs index 1d5c78c929e..f818366bc9f 100644 --- a/src/Tasks/GetAssemblyIdentity.cs +++ b/src/Tasks/GetAssemblyIdentity.cs @@ -45,12 +45,17 @@ private static string ByteArrayToHex(Byte[] a) { return null; } + +#if NET + return Convert.ToHexString(a); +#else var s = new StringBuilder(a.Length * 2); foreach (Byte b in a) { s.Append(b.ToString("X02", CultureInfo.InvariantCulture)); } return s.ToString(); +#endif } public override bool Execute() diff --git a/src/Tasks/GetSDKReferenceFiles.cs b/src/Tasks/GetSDKReferenceFiles.cs index b1a24a00d6a..34f84047a3f 100644 --- a/src/Tasks/GetSDKReferenceFiles.cs +++ b/src/Tasks/GetSDKReferenceFiles.cs @@ -1090,7 +1090,7 @@ internal bool IsAssemblyListCacheFileUpToDate(string sdkIdentity, string sdkRoot string currentAssembly = String.Empty; try { -#if NETCOREAPP +#if NET currentAssembly = Assembly.GetExecutingAssembly().Location; #else currentAssembly = Assembly.GetExecutingAssembly().CodeBase; diff --git a/src/Tasks/Hash.cs b/src/Tasks/Hash.cs index 0bc42f56bfe..828012a2add 100644 --- a/src/Tasks/Hash.cs +++ b/src/Tasks/Hash.cs @@ -94,6 +94,9 @@ public override bool Execute() sha.TransformFinalBlock(shaBuffer, 0, shaBufferPosition); +#if NET + HashResult = Convert.ToHexStringLower(sha.Hash); +#else using (var stringBuilder = new ReuseableStringBuilder(sha.HashSize)) { foreach (var b in sha.Hash) @@ -102,6 +105,7 @@ public override bool Execute() } HashResult = stringBuilder.ToString(); } +#endif } finally { diff --git a/src/Tasks/MSBuild.cs b/src/Tasks/MSBuild.cs index 3169eaec219..12426e0c9aa 100644 --- a/src/Tasks/MSBuild.cs +++ b/src/Tasks/MSBuild.cs @@ -261,10 +261,7 @@ public override bool Execute() if (BuildInParallel) { skipProjects = new bool[Projects.Length]; - for (int i = 0; i < skipProjects.Length; i++) - { - skipProjects[i] = true; - } + skipProjects.AsSpan().Fill(true); } else { diff --git a/src/Tasks/ManifestUtil/ApplicationManifest.cs b/src/Tasks/ManifestUtil/ApplicationManifest.cs index 081762e8b84..555734fde12 100644 --- a/src/Tasks/ManifestUtil/ApplicationManifest.cs +++ b/src/Tasks/ManifestUtil/ApplicationManifest.cs @@ -508,7 +508,7 @@ private void ValidateCom() } } - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "GenerateManifest.CheckForComDuplicates t={0}", Environment.TickCount - t1)); + Util.WriteLog($"GenerateManifest.CheckForComDuplicates t={Environment.TickCount - t1}"); } private void ValidateConfig() @@ -661,11 +661,11 @@ private void ValidateReferencesForClickOnceApplication() // Check for two or more items with the same TargetPath... string key = assembly.TargetPath.ToLowerInvariant(); - if (!targetPathList.ContainsKey(key)) + if (!targetPathList.TryGetValue(key, out bool value)) { targetPathList.Add(key, false); } - else if (!targetPathList[key]) + else if (!value) { OutputMessages.AddWarningMessage("GenerateManifest.DuplicateTargetPath", assembly.ToString()); targetPathList[key] = true; // only warn once per path @@ -707,18 +707,18 @@ private void ValidateReferencesForClickOnceApplication() // Check for two or more items with the same TargetPath... string key = file.TargetPath.ToLowerInvariant(); - if (!targetPathList.ContainsKey(key)) + if (!targetPathList.TryGetValue(key, out bool value)) { targetPathList.Add(key, false); } - else if (!targetPathList[key]) + else if (!value) { OutputMessages.AddWarningMessage("GenerateManifest.DuplicateTargetPath", file.TargetPath); targetPathList[key] = true; // only warn once per path } } } - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "GenerateManifest.CheckManifestReferences t={0}", Environment.TickCount - t1)); + Util.WriteLog($"GenerateManifest.CheckManifestReferences t={Environment.TickCount - t1}"); } private void ValidateReferenceForPartialTrust(AssemblyReference assembly, TrustInfo trustInfo) diff --git a/src/Tasks/ManifestUtil/AssemblyIdentity.cs b/src/Tasks/ManifestUtil/AssemblyIdentity.cs index 6328476020f..28dbbfef17f 100644 --- a/src/Tasks/ManifestUtil/AssemblyIdentity.cs +++ b/src/Tasks/ManifestUtil/AssemblyIdentity.cs @@ -26,7 +26,7 @@ namespace Microsoft.Build.Tasks.Deployment.ManifestUtilities /// This is a serialization format, do not remove or change the private fields. [ComVisible(false)] [XmlRoot("AssemblyIdentity")] - public sealed class AssemblyIdentity + public sealed partial class AssemblyIdentity { /// /// Specifies which attributes are to be returned by the GetFullName function. @@ -59,6 +59,17 @@ public enum FullNameFlags private string _processorArchitecture; private string _type; + private const string AssemblyNamePattern = + "^(?[^,]*)(, Version=(?[^,]*))?(, Culture=(?[^,]*))?(, PublicKeyToken=(?[^,]*))?(, ProcessorArchitecture=(?[^,]*))?(, Type=(?[^,]*))?"; + +#if NET + [GeneratedRegex(AssemblyNamePattern)] + private static partial Regex AssemblyNameRegex { get; } +#else + private static Regex AssemblyNameRegex => _assemblyNameRegex ??= new Regex(AssemblyNamePattern); + private static Regex _assemblyNameRegex; +#endif + /// /// Initializes a new instance of the AssemblyIdentity class. /// @@ -165,7 +176,7 @@ public AssemblyIdentity(AssemblyIdentity identity) public static AssemblyIdentity FromAssemblyName(string assemblyName) { // NOTE: We're not using System.Reflection.AssemblyName class here because we need ProcessorArchitecture and Type attributes. - Regex re = new Regex("^(?[^,]*)(, Version=(?[^,]*))?(, Culture=(?[^,]*))?(, PublicKeyToken=(?[^,]*))?(, ProcessorArchitecture=(?[^,]*))?(, Type=(?[^,]*))?"); + Regex re = AssemblyNameRegex; Match m = re.Match(assemblyName); string name = m.Result("${name}"); string version = m.Result("${version}"); @@ -367,10 +378,15 @@ public bool IsInFramework(string frameworkIdentifier, string frameworkVersion) Version version = null; if (!string.IsNullOrEmpty(frameworkVersion)) { - // CA1307:Specify StringComparison. Suppressed since a valid string representation of a version would be parsed correctly even if the the first character is not "v". - if (frameworkVersion.StartsWith("v")) + if (frameworkVersion[0] == 'v') { - System.Version.TryParse(frameworkVersion.Substring(1), out version); + System.Version.TryParse( +#if NET + frameworkVersion.AsSpan(1), +#else + frameworkVersion.Substring(1), +#endif + out version); } else { @@ -514,14 +530,14 @@ internal string Resolve(string[] searchPaths, bool specificVersion) foreach (string searchPath in searchPaths) { - string file = String.Format(CultureInfo.InvariantCulture, "{0}.dll", _name); + string file = $"{_name}.dll"; string path = Path.Combine(searchPath, file); if (FileSystems.Default.FileExists(path) && IsEqual(this, FromFile(path), specificVersion)) { return path; } - file = String.Format(CultureInfo.InvariantCulture, "{0}.manifest", _name); + file = $"{_name}.manifest"; path = Path.Combine(searchPath, file); if (FileSystems.Default.FileExists(path) && IsEqual(this, FromManifest(path), specificVersion)) { diff --git a/src/Tasks/ManifestUtil/ConvertUtil.cs b/src/Tasks/ManifestUtil/ConvertUtil.cs index 7945fed64e5..88be5e23f8a 100644 --- a/src/Tasks/ManifestUtil/ConvertUtil.cs +++ b/src/Tasks/ManifestUtil/ConvertUtil.cs @@ -26,11 +26,11 @@ public static bool ToBoolean(string value, bool defaultValue) } catch (FormatException) { - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Invalid value '{0}' for {1}, returning {2}", value, typeof(bool).Name, defaultValue.ToString())); + Debug.Fail($"Invalid value '{value}' for {typeof(bool).Name}, returning {defaultValue}"); } catch (ArgumentException) { - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Invalid value '{0}' for {1}, returning {2}", value, typeof(bool).Name, defaultValue.ToString())); + Debug.Fail($"Invalid value '{value}' for {typeof(bool).Name}, returning {defaultValue}"); } } return defaultValue; diff --git a/src/Tasks/ManifestUtil/EmbeddedManifestReader.cs b/src/Tasks/ManifestUtil/EmbeddedManifestReader.cs index 01cb5f8f5d3..ff62bb0b948 100644 --- a/src/Tasks/ManifestUtil/EmbeddedManifestReader.cs +++ b/src/Tasks/ManifestUtil/EmbeddedManifestReader.cs @@ -74,7 +74,7 @@ public static Stream Read(string path) int t1 = Environment.TickCount; EmbeddedManifestReader r = new EmbeddedManifestReader(path); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "EmbeddedManifestReader.Read t={0}", Environment.TickCount - t1)); + Util.WriteLog($"EmbeddedManifestReader.Read t={Environment.TickCount - t1}"); return r._manifest; } } diff --git a/src/Tasks/ManifestUtil/Manifest.cs b/src/Tasks/ManifestUtil/Manifest.cs index d8e5b2a8fdd..63b307cd6df 100644 --- a/src/Tasks/ManifestUtil/Manifest.cs +++ b/src/Tasks/ManifestUtil/Manifest.cs @@ -630,11 +630,11 @@ private void ValidateReferences() // Check for two or more assemblies with the same identity... string identity = assembly.AssemblyIdentity.GetFullName(AssemblyIdentity.FullNameFlags.All); string key = identity.ToLowerInvariant(); - if (!identityList.ContainsKey(key)) + if (!identityList.TryGetValue(key, out bool value)) { identityList.Add(key, false); } - else if (!identityList[key]) + else if (!value) { OutputMessages.AddWarningMessage("GenerateManifest.DuplicateAssemblyIdentity", identity); identityList[key] = true; // only warn once per identity diff --git a/src/Tasks/ManifestUtil/ManifestFormatter.cs b/src/Tasks/ManifestUtil/ManifestFormatter.cs index d7d7ee01eb1..91405ecaac7 100644 --- a/src/Tasks/ManifestUtil/ManifestFormatter.cs +++ b/src/Tasks/ManifestUtil/ManifestFormatter.cs @@ -97,8 +97,8 @@ public static Stream Format(Stream input) w.WriteEndDocument(); w.Flush(); m.Position = 0; - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "ManifestWriter.Format t={0}", Environment.TickCount - t1)); + Util.WriteLog($"ManifestWriter.Format t={Environment.TickCount - t1}"); return m; } } -} \ No newline at end of file +} diff --git a/src/Tasks/ManifestUtil/ManifestReader.cs b/src/Tasks/ManifestUtil/ManifestReader.cs index 131de566c33..1bd48abe2db 100644 --- a/src/Tasks/ManifestUtil/ManifestReader.cs +++ b/src/Tasks/ManifestUtil/ManifestReader.cs @@ -246,7 +246,7 @@ private static Manifest Deserialize(Stream s) using (XmlReader xr = XmlReader.Create(s, xrSettings)) { var m = (Manifest)xs.Deserialize(xr); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "ManifestReader.Deserialize t={0}", Environment.TickCount - t1)); + Util.WriteLog($"ManifestReader.Deserialize t={Environment.TickCount - t1}"); return m; } } diff --git a/src/Tasks/ManifestUtil/ManifestWriter.cs b/src/Tasks/ManifestUtil/ManifestWriter.cs index 8da08fbacde..c5aff0aeae1 100644 --- a/src/Tasks/ManifestUtil/ManifestWriter.cs +++ b/src/Tasks/ManifestUtil/ManifestWriter.cs @@ -28,7 +28,7 @@ private static Stream Serialize(Manifest manifest) int t1 = Environment.TickCount; s.Serialize(w, manifest); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "ManifestWriter.Serialize t={0}", Environment.TickCount - t1)); + Util.WriteLog($"ManifestWriter.Serialize t={Environment.TickCount - t1}"); w.Flush(); m.Position = 0; @@ -188,7 +188,7 @@ private static void WriteManifest(Manifest manifest, Stream output, string targe Util.WriteLogFile(n + ".write.3-formatted.xml", s4); Util.CopyStream(s4, output); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "ManifestWriter.WriteManifest t={0}", Environment.TickCount - t1)); + Util.WriteLog($"ManifestWriter.WriteManifest t={Environment.TickCount - t1}"); } } } diff --git a/src/Tasks/ManifestUtil/MetadataReader.cs b/src/Tasks/ManifestUtil/MetadataReader.cs index fe8269ecdca..efd6d271087 100644 --- a/src/Tasks/ManifestUtil/MetadataReader.cs +++ b/src/Tasks/ManifestUtil/MetadataReader.cs @@ -173,7 +173,12 @@ private string GetPublicKeyToken() an.SetPublicKey(pk); byte[] pkt = an.GetPublicKeyToken(); - publicKeyToken = BitConverter.ToString(pkt).Replace("-", ""); + publicKeyToken = +#if NET + Convert.ToHexString(pkt); +#else + BitConverter.ToString(pkt).Replace("-", ""); +#endif } if (!String.IsNullOrEmpty(publicKeyToken)) diff --git a/src/Tasks/ManifestUtil/PathUtil.cs b/src/Tasks/ManifestUtil/PathUtil.cs index 81c678e17cf..705f2a79d3b 100644 --- a/src/Tasks/ManifestUtil/PathUtil.cs +++ b/src/Tasks/ManifestUtil/PathUtil.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Linq; +using System.Text; using Microsoft.Build.Shared; #nullable disable @@ -222,7 +223,13 @@ public static string Resolve(string path) { // Unfortunately Uri.Host is read-only, so we need to reconstruct it manually... int i = path.IndexOf(localHost, StringComparison.OrdinalIgnoreCase); - return i >= 0 ? path.Substring(0, i) + Environment.MachineName.ToLowerInvariant() + path.Substring(i + localHost.Length) : path; + return i >= 0 ? +#if NET + $"{path.AsSpan(0, i)}{Environment.MachineName.ToLowerInvariant()}{path.AsSpan(i + localHost.Length)}" : +#else + $"{path.Substring(0, i)}{Environment.MachineName.ToLowerInvariant()}{path.Substring(i + localHost.Length)}" : +#endif + path; } return path; } @@ -231,7 +238,11 @@ public static string Resolve(string path) return Path.GetFullPath(path); // make sure it's a full path } - private static bool IsAsciiString(string str) - => str.All(c => c <= 127); + private static bool IsAsciiString(string str) => +#if NET + Ascii.IsValid(str); +#else + str.All(c => c <= 127); +#endif } } diff --git a/src/Tasks/ManifestUtil/TrustInfo.cs b/src/Tasks/ManifestUtil/TrustInfo.cs index bc10cb1d02c..07eaa9e03d2 100644 --- a/src/Tasks/ManifestUtil/TrustInfo.cs +++ b/src/Tasks/ManifestUtil/TrustInfo.cs @@ -790,7 +790,7 @@ public void WriteManifest(Stream input, Stream output) output.Flush(); } document.Save(output); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "ManifestWriter.WriteTrustInfo t={0}", Environment.TickCount - t1)); + Util.WriteLog($"ManifestWriter.WriteTrustInfo t={Environment.TickCount - t1}"); } } } diff --git a/src/Tasks/ManifestUtil/Util.cs b/src/Tasks/ManifestUtil/Util.cs index a6b50bd028f..a2d89b437c7 100644 --- a/src/Tasks/ManifestUtil/Util.cs +++ b/src/Tasks/ManifestUtil/Util.cs @@ -67,47 +67,25 @@ public static string ByteArrayToHex(Byte[] a) return null; } - StringBuilder s = new StringBuilder(a.Length); +#if NET + return Convert.ToHexString(a); +#else + StringBuilder s = new StringBuilder(a.Length * 2); foreach (Byte b in a) { s.Append(b.ToString("X02", CultureInfo.InvariantCulture)); } return s.ToString(); +#endif } - public static string ByteArrayToString(Byte[] a) - { - if (a == null) - { - return null; - } - - StringBuilder s = new StringBuilder(a.Length); - foreach (Byte b in a) - { - s.Append(Convert.ToChar(b)); - } - - return s.ToString(); - } - - public static int CopyStream(Stream input, Stream output) + public static void CopyStream(Stream input, Stream output) { const int bufferSize = 0x4000; - byte[] buffer = new byte[bufferSize]; - int bytesCopied = 0; - int bytesRead; - do - { - bytesRead = input.Read(buffer, 0, bufferSize); - output.Write(buffer, 0, bytesRead); - bytesCopied += bytesRead; - } while (bytesRead > 0); - output.Flush(); + input.CopyTo(output, bufferSize); input.Position = 0; output.Position = 0; - return bytesCopied; } public static string FilterNonprintableChars(string value) @@ -194,9 +172,15 @@ public static Version GetTargetFrameworkVersion(string targetFramework) Version frameworkVersion = null; if (!String.IsNullOrEmpty(targetFramework)) { - if (targetFramework.StartsWith("v", StringComparison.OrdinalIgnoreCase)) + if (targetFramework[0] is 'v' or 'V') { - Version.TryParse(targetFramework.Substring(1), out frameworkVersion); + Version.TryParse( +#if NET + targetFramework.AsSpan(1), +#else + targetFramework.Substring(1), +#endif + out frameworkVersion); } else { @@ -216,8 +200,8 @@ public static string GetEmbeddedResourceString(string name) public static Stream GetEmbeddedResourceStream(string name) { Assembly a = Assembly.GetExecutingAssembly(); - Stream s = a.GetManifestResourceStream(String.Format(CultureInfo.InvariantCulture, "{0}.{1}", typeof(Util).Namespace, name)); - Debug.Assert(s != null, String.Format(CultureInfo.CurrentCulture, "EmbeddedResource '{0}' not found", name)); + Stream s = a.GetManifestResourceStream($"{typeof(Util).Namespace}.{name}"); + Debug.Assert(s != null, $"EmbeddedResource '{name}' not found"); return s; } @@ -634,8 +618,14 @@ public static Version ConvertFrameworkVersionToString(string version) { if (version.StartsWith("v", StringComparison.OrdinalIgnoreCase)) { - return new Version(version.Substring(1)); + return Version.Parse( +#if NET + version.AsSpan(1)); +#else + version.Substring(1)); +#endif } + return new Version(version); } diff --git a/src/Tasks/ManifestUtil/XmlUtil.cs b/src/Tasks/ManifestUtil/XmlUtil.cs index 32f985124aa..39c110eebb0 100644 --- a/src/Tasks/ManifestUtil/XmlUtil.cs +++ b/src/Tasks/ManifestUtil/XmlUtil.cs @@ -82,14 +82,14 @@ public static Stream XslTransform(string resource, Stream input, params Dictiona int t2 = Environment.TickCount; XPathDocument d = new XPathDocument(s); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "new XPathDocument(1) t={0}", Environment.TickCount - t2)); + Util.WriteLog($"new XPathDocument(1) t={Environment.TickCount - t2}"); int t3 = Environment.TickCount; var xslc = new XslCompiledTransform(); // Using the Trusted Xslt is fine as the style sheet comes from our own assemblies. // This is similar to the prior this.GetType().Assembly/Evidence method that was used in the now depricated XslTransform. xslc.Load(d, XsltSettings.TrustedXslt, s_resolver); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "XslCompiledTransform.Load t={0}", Environment.TickCount - t3)); + Util.WriteLog($"XslCompiledTransform.Load t={Environment.TickCount - t3}"); // Need to copy input stream because XmlReader will close it, // causing errors for later callers that access the same stream @@ -99,7 +99,7 @@ public static Stream XslTransform(string resource, Stream input, params Dictiona int t4 = Environment.TickCount; using (XmlReader reader = XmlReader.Create(clonedInput)) { - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "new XmlReader(2) t={0}", Environment.TickCount - t4)); + Util.WriteLog($"new XmlReader(2) t={Environment.TickCount - t4}"); XsltArgumentList args = null; if (entries.Length > 0) @@ -110,7 +110,7 @@ public static Stream XslTransform(string resource, Stream input, params Dictiona string key = entry.Key.ToString(); object val = entry.Value.ToString(); args.AddParam(key, "", val); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "arg: key='{0}' value='{1}'", key, val.ToString())); + Util.WriteLog($"arg: key='{key}' value='{val}'"); } } @@ -122,13 +122,13 @@ public static Stream XslTransform(string resource, Stream input, params Dictiona int t5 = Environment.TickCount; xslc.Transform(reader, args, w, s_resolver); - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "XslCompiledTransform.Transform t={0}", Environment.TickCount - t4)); + Util.WriteLog($"XslCompiledTransform.Transform t={Environment.TickCount - t4}"); w.WriteEndDocument(); w.Flush(); m.Position = 0; - Util.WriteLog(String.Format(CultureInfo.CurrentCulture, "XslCompiledTransform(\"{0}\") t={1}", resource, Environment.TickCount - t1)); + Util.WriteLog($"XslCompiledTransform(\"{resource}\") t={Environment.TickCount - t1}"); return m; } @@ -153,7 +153,7 @@ public override Object GetEntity(Uri uri, string role, Type t) { // First look in assembly resources... Assembly a = Assembly.GetExecutingAssembly(); - s = a.GetManifestResourceStream(String.Format(CultureInfo.InvariantCulture, "{0}.{1}", typeof(Util).Namespace, filename)); + s = a.GetManifestResourceStream($"{typeof(Util).Namespace}.{filename}"); if (s != null) { @@ -191,7 +191,7 @@ public override Object GetEntity(Uri uri, string role, Type t) } // Didn't find the resource... - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "ResourceResolver could not find file '{0}'", filename)); + Debug.Fail($"ResourceResolver could not find file '{filename}'"); return null; } } diff --git a/src/Tasks/ManifestUtil/mansign2.cs b/src/Tasks/ManifestUtil/mansign2.cs index 862bfb6b88c..d23e63c0870 100644 --- a/src/Tasks/ManifestUtil/mansign2.cs +++ b/src/Tasks/ManifestUtil/mansign2.cs @@ -817,12 +817,17 @@ private static string ObtainRFC3161Timestamp(string timeStampUrl, string signatu try { +#if NET + Span nonce = stackalloc byte[32]; + RandomNumberGenerator.Fill(nonce); +#else byte[] nonce = new byte[32]; using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { rng.GetBytes(nonce); } +#endif // Eventually, CryptEncodeObjectEx(...) is called on a CRYPT_TIMESTAMP_REQUEST with this nonce, // and CryptEncodeObjectEx(...) interprets the nonce as a little endian, DER-encoded integer value @@ -1044,13 +1049,19 @@ private static void StrongNameSignManifestDom(XmlDocument manifestDom, XmlDocume // Insert the signature now. signatureParent.AppendChild(xmlDigitalSignature); } + +#if !NET private static readonly char[] s_hexValues = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; +#endif private static string BytesToHexString(byte[] array, int start, int end) { string result = null; if (array != null) { +#if NET + return Convert.ToHexStringLower(array.AsSpan(start, end - start)); +#else char[] hexOrder = new char[(end - start) * 2]; int i = end; int digit, j = 0; @@ -1062,6 +1073,7 @@ private static string BytesToHexString(byte[] array, int start, int end) hexOrder[j++] = s_hexValues[digit]; } result = new String(hexOrder); +#endif } return result; } diff --git a/src/Tasks/NativeMethods.cs b/src/Tasks/NativeMethods.cs index 111880e1d30..8c761db013d 100644 --- a/src/Tasks/NativeMethods.cs +++ b/src/Tasks/NativeMethods.cs @@ -520,7 +520,7 @@ internal struct PROCESS_INFORMATION /// /// Interop methods. /// - internal static class NativeMethods + internal static partial class NativeMethods { #region Constants @@ -1160,7 +1160,7 @@ internal static unsafe bool TryReadMetadataString(string fullPath, IntPtr attrDa } // And convert it to the output string. - strValue = new String(Encoding.UTF8.GetChars(bytes)); + strValue = Encoding.UTF8.GetString(bytes); } else { @@ -1235,19 +1235,24 @@ internal static unsafe int CorSigUncompressData(IntPtr data, out int uncompresse /// This class is a wrapper over the native GAC enumeration API. /// [ComVisible(false)] - internal class AssemblyCacheEnum : IEnumerable + internal partial class AssemblyCacheEnum : IEnumerable { /// /// Path to the gac /// private static readonly string s_gacPath = Path.Combine(NativeMethodsShared.FrameworkBasePath, "gac"); + private const string AssemblyVersionPattern = @"^([.\d]+)_([^_]*)_([a-fA-F\d]{16})$"; + /// /// Regex for directory version parsing /// - private static readonly Regex s_assemblyVersionRegex = new Regex( - @"^([.\d]+)_([^_]*)_([a-fA-F\d]{16})$", - RegexOptions.CultureInvariant | RegexOptions.Compiled); +#if NET + [GeneratedRegex(AssemblyVersionPattern, RegexOptions.CultureInvariant)] + private static partial Regex AssemblyVersionRegex { get; } +#else + private static Regex AssemblyVersionRegex { get; } = new Regex(AssemblyVersionPattern, RegexOptions.CultureInvariant | RegexOptions.Compiled); +#endif /// /// The IAssemblyEnum interface which allows us to ask for the next assembly from the GAC enumeration. @@ -1387,7 +1392,7 @@ public IEnumerator GetEnumerator() var versionString = Path.GetFileName(version); if (!string.IsNullOrWhiteSpace(versionString)) { - var match = s_assemblyVersionRegex.Match(versionString); + var match = AssemblyVersionRegex.Match(versionString); if (match.Success) { var name = new AssemblyName @@ -1407,10 +1412,16 @@ public IEnumerator GetEnumerator() if (!string.IsNullOrWhiteSpace(match.Groups[3].Value)) { var value = match.Groups[3].Value; - name.SetPublicKeyToken( + byte[] key = +#if NET + Convert.FromHexString(value.AsSpan(0, 16)); +#else Enumerable.Range(0, 16) - .Where(x => x % 2 == 0) - .Select(x => Convert.ToByte(value.Substring(x, 2), 16)).ToArray()); + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(value.Substring(x, 2), 16)) + .ToArray(); +#endif + name.SetPublicKeyToken(key); } yield return new AssemblyNameExtension(name); @@ -1455,8 +1466,12 @@ public static string AssemblyPathFromStrongName(string strongName) "{0}_{1}_{2}", assemblyNameVersion.Version.ToString(4), assemblyNameVersion.CultureName != "neutral" ? assemblyNameVersion.CultureName : string.Empty, +#if NET + Convert.ToHexStringLower(assemblyNameVersion.GetPublicKeyToken())), +#else assemblyNameVersion.GetPublicKeyToken() .Aggregate(new StringBuilder(), (builder, v) => builder.Append(v.ToString("x2")))), +#endif assemblyNameVersion.Name + ".dll"); if (FileSystems.Default.FileExists(path)) diff --git a/src/Tasks/RedistList.cs b/src/Tasks/RedistList.cs index f5476e2f9fc..7d8e6acd7ce 100644 --- a/src/Tasks/RedistList.cs +++ b/src/Tasks/RedistList.cs @@ -372,7 +372,7 @@ private static string GetSimpleName(string assemblyName) throw new ArgumentNullException(nameof(assemblyName)); } - int i = assemblyName.IndexOf(",", StringComparison.Ordinal); + int i = assemblyName.IndexOf(','); return i > 0 ? assemblyName.Substring(0, i) : assemblyName; } @@ -794,7 +794,7 @@ private static void ParseFileListSection(AssemblyTableInfo assemblyTableInfo, st { // When comparing the assembly entries we want to compare the FullName which is a formatted as name, version, publicKeyToken and culture and whether the entry is a redistroot flag // We do not need to add the redistName and the framework directory because this will be the same for all entries in the current redist list being read. - string hashIndex = String.Format(CultureInfo.InvariantCulture, "{0},{1}", newEntry.FullName, newEntry.IsRedistRoot == null ? "null" : newEntry.IsRedistRoot.ToString()); + string hashIndex = $"{newEntry.FullName},{(newEntry.IsRedistRoot == null ? "null" : newEntry.IsRedistRoot.ToString())}"; assemblyEntries.TryGetValue(hashIndex, out AssemblyEntry dictionaryEntry); // If the entry is not in the dictionary or the entry is in the dictionary but the new entry has the ingac flag true, make sure the dictionary contains the entry with the ingac true. diff --git a/src/Tasks/RequiresFramework35SP1Assembly.cs b/src/Tasks/RequiresFramework35SP1Assembly.cs index 4714f818e73..741dcbe59fc 100644 --- a/src/Tasks/RequiresFramework35SP1Assembly.cs +++ b/src/Tasks/RequiresFramework35SP1Assembly.cs @@ -79,7 +79,12 @@ private static Version ConvertFrameworkVersionToString(string version) { if (version.StartsWith("v", StringComparison.OrdinalIgnoreCase)) { - return new Version(version.Substring(1)); + return Version.Parse( +#if NET + version.AsSpan(1)); +#else + version.Substring(1)); +#endif } return new Version(version); } diff --git a/src/Tasks/ResolveComReference.cs b/src/Tasks/ResolveComReference.cs index c8ba6d686f4..c339ea9c5be 100644 --- a/src/Tasks/ResolveComReference.cs +++ b/src/Tasks/ResolveComReference.cs @@ -5,7 +5,7 @@ #if !RUNTIME_TYPE_NETCORE using System.Collections.Generic; #endif -#if !NET7_0_OR_GREATER +#if !NET using System.Diagnostics; using System.Globalization; using System.IO; diff --git a/src/Tasks/ResolveManifestFiles.cs b/src/Tasks/ResolveManifestFiles.cs index 9a78f010f16..d8a61ab1579 100644 --- a/src/Tasks/ResolveManifestFiles.cs +++ b/src/Tasks/ResolveManifestFiles.cs @@ -215,7 +215,12 @@ private static Version ConvertFrameworkVersionToString(string version) { if (version.StartsWith("v", StringComparison.OrdinalIgnoreCase)) { - return new Version(version.Substring(1)); + return Version.Parse( +#if NET + version.AsSpan(1)); +#else + version.Substring(1)); +#endif } return new Version(version); } @@ -346,7 +351,7 @@ private static CultureInfo GetItemCulture(ITaskItem item) // Infer culture from path (i.e. "obj\debug\fr\WindowsApplication1.resources.dll" -> "fr") string[] pathSegments = PathUtil.GetPathSegments(item.ItemSpec); itemCulture = pathSegments.Length > 1 ? pathSegments[pathSegments.Length - 2] : null; - Debug.Assert(!String.IsNullOrEmpty(itemCulture), String.Format(CultureInfo.CurrentCulture, "Satellite item '{0}' is missing expected attribute '{1}'", item.ItemSpec, "Culture")); + Debug.Assert(!String.IsNullOrEmpty(itemCulture), $"Satellite item '{item.ItemSpec}' is missing expected attribute 'Culture'"); item.SetMetadata("Culture", itemCulture); } return new CultureInfo(itemCulture); @@ -862,7 +867,7 @@ public void Add(ITaskItem item) // Add to map with full name, for SpecificVersion=true case string key = fusionName.ToLowerInvariant(); - Debug.Assert(!_dictionary.ContainsKey(key), String.Format(CultureInfo.CurrentCulture, "Two or more items with same key '{0}' detected", key)); + Debug.Assert(!_dictionary.ContainsKey(key), $"Two or more items with same key '{key}' detected"); if (!_dictionary.ContainsKey(key)) { _dictionary.Add(key, entry); @@ -921,7 +926,7 @@ public void Add(ITaskItem item) { // Use satellite assembly strong name signature as key string key = identity.ToString(); - Debug.Assert(!_dictionary.ContainsKey(key), String.Format(CultureInfo.CurrentCulture, "Two or more items with same key '{0}' detected", key)); + Debug.Assert(!_dictionary.ContainsKey(key), $"Two or more items with same key '{key}' detected"); if (!_dictionary.ContainsKey(key)) { _dictionary.Add(key, entry); @@ -962,7 +967,7 @@ public void Add(ITaskItem item, bool includedByDefault) } string key = targetPath.ToLowerInvariant(); - Debug.Assert(!_dictionary.ContainsKey(key), String.Format(CultureInfo.CurrentCulture, "Two or more items with same '{0}' attribute detected", ItemMetadataNames.targetPath)); + Debug.Assert(!_dictionary.ContainsKey(key), $"Two or more items with same '{(object)ItemMetadataNames.targetPath}' attribute detected"); var entry = new MapEntry(item, includedByDefault); if (!_dictionary.ContainsKey(key)) { @@ -997,11 +1002,11 @@ private static PublishState StringToPublishState(string value) } catch (FormatException) { - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Invalid value '{0}' for {1}", value, "PublishState")); + Debug.Fail($"Invalid value '{value}' for PublishState"); } catch (ArgumentException) { - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Invalid value '{0}' for {1}", value, "PublishState")); + Debug.Fail($"Invalid value '{value}' for PublishState"); } } return PublishState.Auto; @@ -1036,14 +1041,14 @@ public static PublishFlags GetAssemblyFlags(PublishState state, bool copyLocal) isPublished = false; break; case PublishState.DataFile: - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "PublishState.DataFile is invalid for an assembly")); + Debug.Fail("PublishState.DataFile is invalid for an assembly"); break; case PublishState.Prerequisite: isPrerequisite = true; isPublished = false; break; default: - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Unhandled value PublishFlags.{0}", state.ToString())); + Debug.Fail($"Unhandled value PublishFlags.{state}"); break; } return new PublishFlags(isDataFile, isPrerequisite, isPublished); @@ -1073,10 +1078,10 @@ public static PublishFlags GetFileFlags(PublishState state, string fileExtension isPublished = true; break; case PublishState.Prerequisite: - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "PublishState.Prerequisite is invalid for a file")); + Debug.Fail("PublishState.Prerequisite is invalid for a file"); break; default: - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Unhandled value PublishFlags.{0}", state.ToString())); + Debug.Fail($"Unhandled value PublishFlags.{state}"); break; } return new PublishFlags(isDataFile, isPrerequisite, isPublished); @@ -1103,14 +1108,14 @@ public static PublishFlags GetSatelliteFlags(PublishState state, CultureInfo sat isPublished = false; break; case PublishState.DataFile: - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "PublishState.DataFile is invalid for an assembly")); + Debug.Fail("PublishState.DataFile is invalid for an assembly"); break; case PublishState.Prerequisite: isPrerequisite = true; isPublished = false; break; default: - Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Unhandled value PublishFlags.{0}", state.ToString())); + Debug.Fail($"Unhandled value PublishFlags.{state}"); break; } return new PublishFlags(isDataFile, isPrerequisite, isPublished); diff --git a/src/Tasks/ResolveSDKReference.cs b/src/Tasks/ResolveSDKReference.cs index 4cf06aa29e8..6a1a82c1ced 100644 --- a/src/Tasks/ResolveSDKReference.cs +++ b/src/Tasks/ResolveSDKReference.cs @@ -20,7 +20,7 @@ namespace Microsoft.Build.Tasks /// Resolves an SDKReference to a full path on disk /// #pragma warning disable RS0022 // Constructor make noninheritable base class inheritable: Longstanding API design that we shouldn't change now - public class ResolveSDKReference : TaskExtension + public partial class ResolveSDKReference : TaskExtension #pragma warning restore RS0022 // Constructor make noninheritable base class inheritable { #region fields @@ -33,13 +33,18 @@ public class ResolveSDKReference : TaskExtension { "UAP", "Windows" } }; + private const string SdkReferenceFormatPattern = @"(?^[^,]*),\s*Version=(?.*)"; + /// /// Regex for breaking up the sdk reference include into pieces. /// Example: XNA, Version=8.0 /// - private static readonly Regex s_sdkReferenceFormat = new Regex( - @"(?^[^,]*),\s*Version=(?.*)", - RegexOptions.IgnoreCase); +#if NET + [GeneratedRegex(SdkReferenceFormatPattern, RegexOptions.IgnoreCase)] + private static partial Regex SdkReferenceFormatRegex { get; } +#else + private static Regex SdkReferenceFormatRegex { get; } = new Regex(SdkReferenceFormatPattern, RegexOptions.IgnoreCase); +#endif /// /// SimpleName group @@ -409,7 +414,7 @@ public override bool Execute() { if (!sdksAlreadyErrorOrWarnedFor.Contains(incompatibleReference) && incompatibleReference != notCompatibleReference /*cannot be incompatible with self*/) { - listOfIncompatibleReferences.Add(String.Format(CultureInfo.CurrentCulture, "\"{0}\"", incompatibleReference.SDKName)); + listOfIncompatibleReferences.Add($"\"{incompatibleReference.SDKName}\""); sdksAlreadyErrorOrWarnedFor.Add(incompatibleReference); } } @@ -438,7 +443,7 @@ public override bool Execute() { if (!sdksAlreadyErrorOrWarnedFor.Contains(incompatibleReference) && incompatibleReference != notCompatibleReference /*cannot be incompatible with self*/) { - listOfIncompatibleReferences.Add(String.Format(CultureInfo.CurrentCulture, "\"{0}\"", incompatibleReference.SDKName)); + listOfIncompatibleReferences.Add($"\"{incompatibleReference.SDKName}\""); sdksAlreadyErrorOrWarnedFor.Add(incompatibleReference); } } @@ -481,7 +486,7 @@ internal static void AddMetadataToReferences(TaskLoggingHelper log, HashSet sdkRef // Return true if no reference could be found return resolvedReference == null; }) - .Select(y => String.Format(CultureInfo.CurrentCulture, "\"{0}\"", y)) + .Select(y => $"\"{y}\"") .ToArray(); return unresolvedDependencyIdentities; @@ -574,7 +579,7 @@ internal SDKReference ParseSDKReference(ITaskItem referenceItem) /// private static bool ParseSDKReference(string reference, out string sdkSimpleName, out string rawSdkVersion) { - Match match = s_sdkReferenceFormat.Match(reference); + Match match = SdkReferenceFormatRegex.Match(reference); sdkSimpleName = String.Empty; bool parsedVersion = false; @@ -733,7 +738,7 @@ public SDKReference(ITaskItem taskItem, string sdkName, string sdkVersion) ReferenceItem = taskItem; SimpleName = sdkName; Version = sdkVersion; - SDKName = String.Format(CultureInfo.InvariantCulture, "{0}, Version={1}", SimpleName, Version); + SDKName = $"{SimpleName}, Version={Version}"; FrameworkIdentitiesFromManifest = new Dictionary(StringComparer.OrdinalIgnoreCase); AppxLocationsFromManifest = new Dictionary(StringComparer.OrdinalIgnoreCase); ResolutionErrors = new List>(); @@ -1289,13 +1294,13 @@ private void CreateResolvedReferenceItem(string targetConfiguration, string targ { // Try and find a framework identity that matches on both the configuration and architecture "FrameworkIdentity--" FrameworkIdentity = null; - string frameworkIdentityKey = String.Format(CultureInfo.InvariantCulture, "{0}-{1}-{2}", SDKManifest.Attributes.FrameworkIdentity, sdkConfiguration, sdkArchitecture); + string frameworkIdentityKey = $"{SDKManifest.Attributes.FrameworkIdentity}-{sdkConfiguration}-{sdkArchitecture}"; FrameworkIdentity = FindFrameworkIdentity(frameworkIdentityKey); // Try and find a framework identity that matches on the configuration , Element must be named "FrameworkIdentity-" only. if (FrameworkIdentity == null) { - frameworkIdentityKey = String.Format(CultureInfo.InvariantCulture, "{0}-{1}", SDKManifest.Attributes.FrameworkIdentity, sdkConfiguration); + frameworkIdentityKey = $"{SDKManifest.Attributes.FrameworkIdentity}-{sdkConfiguration}"; FrameworkIdentity = FindFrameworkIdentity(frameworkIdentityKey); } diff --git a/src/Tasks/ResourceHandling/MSBuildResXReader.cs b/src/Tasks/ResourceHandling/MSBuildResXReader.cs index ea1ffae0211..1f6b95b7067 100644 --- a/src/Tasks/ResourceHandling/MSBuildResXReader.cs +++ b/src/Tasks/ResourceHandling/MSBuildResXReader.cs @@ -89,7 +89,11 @@ private static string GetFullTypeNameFromAlias(string aliasedTypeName, Dictionar int indexStart = aliasedTypeName.IndexOf(','); if (aliases.TryGetValue(aliasedTypeName.Substring(indexStart + 2), out string fullAssemblyIdentity)) { +#if NET + return string.Concat(aliasedTypeName.AsSpan(0, indexStart + 2), fullAssemblyIdentity); +#else return aliasedTypeName.Substring(0, indexStart + 2) + fullAssemblyIdentity; +#endif } // Allow "System.String" bare @@ -290,7 +294,7 @@ private static void AddLinkedResource(string resxFilename, bool pathsRelativeToB /// private static bool IsByteArray(string fileRefType) { - return fileRefType.IndexOf("System.Byte[]") != -1 && fileRefType.IndexOf("mscorlib") != -1; + return fileRefType.Contains("System.Byte[]") && fileRefType.Contains("mscorlib"); } internal static bool IsString(string fileRefType) @@ -333,7 +337,7 @@ internal static string[] ParseResxFileRefString(string stringValue) string remainingString; if (stringValue.StartsWith("\"")) { - int lastIndexOfQuote = stringValue.LastIndexOf("\""); + int lastIndexOfQuote = stringValue.LastIndexOf('"'); if (lastIndexOfQuote - 1 < 0) { throw new ArgumentException(nameof(stringValue)); @@ -349,7 +353,7 @@ internal static string[] ParseResxFileRefString(string stringValue) } else { - int nextSemiColumn = stringValue.IndexOf(";"); + int nextSemiColumn = stringValue.IndexOf(';'); if (nextSemiColumn == -1) { throw new ArgumentException(nameof(stringValue)); diff --git a/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactory.cs b/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactory.cs index 535156bc1fd..d5d9ebda785 100644 --- a/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactory.cs +++ b/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactory.cs @@ -536,10 +536,10 @@ internal bool TryResolveAssemblyReferences(TaskLoggingHelper log, RoslynCodeTask // Start with the user specified references and include all of the default references that are language agnostic IEnumerable references = taskInfo.References.Union(DefaultReferences[String.Empty]); - if (DefaultReferences.ContainsKey(taskInfo.CodeLanguage)) + if (DefaultReferences.TryGetValue(taskInfo.CodeLanguage, out IEnumerable value)) { // Append default references for the specific language - references = references.Union(DefaultReferences[taskInfo.CodeLanguage]); + references = references.Union(value); } List directoriesToAddToAppDomain = new(); diff --git a/src/Tasks/SystemState.cs b/src/Tasks/SystemState.cs index f85dc93eb7e..cbb66f13907 100644 --- a/src/Tasks/SystemState.cs +++ b/src/Tasks/SystemState.cs @@ -70,7 +70,7 @@ internal sealed class SystemState : StateFileBase, ITranslatable /// /// Additional level of caching kept at the process level. /// - private static ConcurrentDictionary s_processWideFileStateCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); + private static readonly ConcurrentDictionary s_processWideFileStateCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); /// /// XML tables of installed assemblies. diff --git a/src/Tasks/Unzip.cs b/src/Tasks/Unzip.cs index 690308135f0..01026579232 100644 --- a/src/Tasks/Unzip.cs +++ b/src/Tasks/Unzip.cs @@ -327,7 +327,7 @@ private void ParsePattern(string pattern, out string[] patterns) // Supporting property references would require access to Expander which is unavailable in Microsoft.Build.Tasks Log.LogErrorWithCodeFromResources("Unzip.ErrorParsingPatternPropertyReferences", pattern); } - else if (pattern.IndexOfAny(FileUtilities.InvalidPathChars) != -1) + else if (pattern.AsSpan().IndexOfAny(FileUtilities.InvalidPathChars) >= 0) { Log.LogErrorWithCodeFromResources("Unzip.ErrorParsingPatternInvalidPath", pattern); } diff --git a/src/Tasks/WriteCodeFragment.cs b/src/Tasks/WriteCodeFragment.cs index ecfe9bad0d6..9e08cea56bc 100644 --- a/src/Tasks/WriteCodeFragment.cs +++ b/src/Tasks/WriteCodeFragment.cs @@ -210,7 +210,13 @@ private string GenerateCode(out string extension) if (name.StartsWith("_Parameter", StringComparison.OrdinalIgnoreCase)) { - if (!Int32.TryParse(name.Substring("_Parameter".Length), out int index)) + if (!Int32.TryParse( +#if NET + name.AsSpan("_Parameter".Length), +#else + name.Substring("_Parameter".Length), +#endif + out int index)) { Log.LogErrorWithCodeFromResources("General.InvalidValue", name, "WriteCodeFragment"); return null; diff --git a/src/Tasks/XamlTaskFactory/CommandLineGenerator.cs b/src/Tasks/XamlTaskFactory/CommandLineGenerator.cs index 1ef6c1ec726..f2fbe04a6b2 100644 --- a/src/Tasks/XamlTaskFactory/CommandLineGenerator.cs +++ b/src/Tasks/XamlTaskFactory/CommandLineGenerator.cs @@ -413,22 +413,21 @@ private static void EmitStringArraySwitch(CommandLineBuilder clb, CommandLineToo /// private static bool PerformSwitchValueSubstition(CommandLineBuilder clb, CommandLineToolSwitch commandLineToolSwitch, string switchValue) { - Regex regex = new Regex(@"\[value]", RegexOptions.IgnoreCase); - Match match = regex.Match(commandLineToolSwitch.SwitchValue); - if (match.Success) + const string Value = "[value]"; + int valuePos = commandLineToolSwitch.SwitchValue.IndexOf(Value, StringComparison.OrdinalIgnoreCase); + if (valuePos >= 0) { - string prefixToAppend = commandLineToolSwitch.SwitchValue.Substring(match.Index + match.Length, commandLineToolSwitch.SwitchValue.Length - (match.Index + match.Length)); - string valueToAppend; - if (!switchValue.EndsWith("\\\\", StringComparison.OrdinalIgnoreCase) && switchValue.EndsWith("\\", StringComparison.OrdinalIgnoreCase) && prefixToAppend.Length > 0 && prefixToAppend[0] == '\"') - { - // If the combined string would create \" then we need to escape it - // if the combined string would create \\" then we ignore it as as assume it is already escaped. - valueToAppend = commandLineToolSwitch.SwitchValue.Substring(0, match.Index) + switchValue + "\\" + prefixToAppend; - } - else - { - valueToAppend = commandLineToolSwitch.SwitchValue.Substring(0, match.Index) + switchValue + prefixToAppend; - } + string prefixToAppend = commandLineToolSwitch.SwitchValue.Substring(valuePos + Value.Length); + + // If the combined string would create \" then we need to escape it + // if the combined string would create \\" then we ignore it as as assume it is already escaped. + bool needsEscaping = + !switchValue.EndsWith("\\\\", StringComparison.OrdinalIgnoreCase) && + switchValue.EndsWith("\\", StringComparison.OrdinalIgnoreCase) && + prefixToAppend.Length > 0 && + prefixToAppend[0] == '\"'; + + string valueToAppend = $"{commandLineToolSwitch.SwitchValue.Substring(0, valuePos)}{switchValue}{(needsEscaping ? "\\" : "")}{prefixToAppend}"; clb.AppendSwitch(valueToAppend); return true; @@ -645,8 +644,7 @@ private void GenerateTemplatedCommandLine(CommandLineBuilder builder) // Match all instances of [asdf], where "asdf" can be any combination of any // characters *except* a [ or an ]. i.e., if "[ [ sdf ]" is passed, then we will // match "[ sdf ]" - string matchString = @"\[[^\[\]]+\]"; - Regex regex = new Regex(matchString, RegexOptions.ECMAScript); + Regex regex = new Regex(@"\[[^\[\]]+\]", RegexOptions.ECMAScript); MatchCollection matches = regex.Matches(CommandLineTemplate); int indexOfEndOfLastSubstitution = 0; @@ -735,7 +733,7 @@ private void GenerateTemplatedCommandLine(CommandLineBuilder builder) indexOfEndOfLastSubstitution = match.Index + match.Length; } - builder.AppendTextUnquoted(CommandLineTemplate.Substring(indexOfEndOfLastSubstitution, CommandLineTemplate.Length - indexOfEndOfLastSubstitution)); + builder.AppendTextUnquoted(CommandLineTemplate.Substring(indexOfEndOfLastSubstitution)); } } } diff --git a/src/Tasks/XamlTaskFactory/XamlTaskFactory.cs b/src/Tasks/XamlTaskFactory/XamlTaskFactory.cs index e74251b8bba..18e0ce17d1d 100644 --- a/src/Tasks/XamlTaskFactory/XamlTaskFactory.cs +++ b/src/Tasks/XamlTaskFactory/XamlTaskFactory.cs @@ -71,7 +71,7 @@ public Type TaskType { if (_taskType == null) { - _taskType = _taskAssembly.GetType(String.Concat(XamlTaskNamespace, ".", TaskName), true); + _taskType = _taskAssembly.GetType($"{XamlTaskNamespace}.{TaskName}", true); } return _taskType; @@ -195,7 +195,7 @@ public bool Initialize(string taskName, IDictionary ta /// The task factory logging host will log messages in the context of the task. public ITask CreateTask(IBuildEngine taskFactoryLoggingHost) { - string fullTaskName = String.Concat(TaskNamespace, ".", TaskName); + string fullTaskName = $"{TaskNamespace}.{TaskName}"; return (ITask)_taskAssembly.CreateInstance(fullTaskName); } diff --git a/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs b/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs index a34a45ea3ec..7fbd0431575 100644 --- a/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs +++ b/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs @@ -2142,7 +2142,7 @@ public void GetPathToStandardLibraries64Bit35() string frameworkDirectory2064bit = FrameworkLocationHelper.GetPathToDotNetFrameworkV20(SharedDotNetFrameworkArchitecture.Bitness64); string frameworkDirectory20Current = FrameworkLocationHelper.GetPathToDotNetFrameworkV20(SharedDotNetFrameworkArchitecture.Current); - if (!EnvironmentUtilities.Is64BitOperatingSystem) + if (!Environment.Is64BitOperatingSystem) { // "Not 64 bit OS " return; @@ -2163,7 +2163,7 @@ public void GetPathToStandardLibraries64Bit35() pathToFramework = ToolLocationHelper.GetPathToStandardLibraries(".NetFramework", "v3.5", string.Empty, "itanium"); pathToFramework.ShouldBe(frameworkDirectory2064bit, StringCompareShould.IgnoreCase); - if (!EnvironmentUtilities.Is64BitProcess) + if (!Environment.Is64BitProcess) { pathToFramework = ToolLocationHelper.GetPathToStandardLibraries(".NetFramework", "v3.5", string.Empty, "RandomPlatform"); pathToFramework.ShouldBe(frameworkDirectory2032bit, StringCompareShould.IgnoreCase); @@ -2188,7 +2188,7 @@ public void GetPathToStandardLibraries64Bit40() { IList referencePaths = ToolLocationHelper.GetPathToReferenceAssemblies(new FrameworkNameVersioning(".NETFramework", new Version("4.0"))); - if (!EnvironmentUtilities.Is64BitOperatingSystem) + if (!Environment.Is64BitOperatingSystem) { // "Not 64 bit OS " return; @@ -2233,7 +2233,7 @@ public void GetPathToStandardLibraries32Bit35() string frameworkDirectory2032bit = FrameworkLocationHelper.GetPathToDotNetFrameworkV20(SharedDotNetFrameworkArchitecture.Bitness32); string frameworkDirectory20Current = FrameworkLocationHelper.GetPathToDotNetFrameworkV20(SharedDotNetFrameworkArchitecture.Current); - if (EnvironmentUtilities.Is64BitOperatingSystem) + if (Environment.Is64BitOperatingSystem) { // "Is a 64 bit OS " return; @@ -2271,7 +2271,7 @@ public void GetPathToStandardLibraries32Bit40() { IList referencePaths = ToolLocationHelper.GetPathToReferenceAssemblies(new FrameworkNameVersioning(".NETFramework", new Version("4.0"))); - if (EnvironmentUtilities.Is64BitOperatingSystem) + if (Environment.Is64BitOperatingSystem) { // "Is 64 bit OS " return; diff --git a/src/Utilities/CommandLineBuilder.cs b/src/Utilities/CommandLineBuilder.cs index c41b18d74c0..07ac12544e9 100644 --- a/src/Utilities/CommandLineBuilder.cs +++ b/src/Utilities/CommandLineBuilder.cs @@ -154,14 +154,14 @@ public CommandLineBuilder(bool quoteHyphensOnCommandLine, bool useNewLineSeparat /// /// Use a private property so that we can lazy initialize the regex /// - private Regex DefinitelyNeedQuotes => _definitelyNeedQuotes - ?? (_definitelyNeedQuotes = new Regex(_quoteHyphens ? s_definitelyNeedQuotesRegexWithHyphen : s_definitelyNeedQuotesRegexNoHyphen, RegexOptions.CultureInvariant)); + private Regex DefinitelyNeedQuotes => _definitelyNeedQuotes ??= + new Regex(_quoteHyphens ? s_definitelyNeedQuotesRegexWithHyphen : s_definitelyNeedQuotesRegexNoHyphen, RegexOptions.CultureInvariant); /// /// Use a private getter property to we can lazy initialize the regex /// - private Regex AllowedUnquoted => _allowedUnquoted - ?? (_allowedUnquoted = new Regex(_quoteHyphens ? s_allowedUnquotedRegexNoHyphen : s_allowedUnquotedRegexWithHyphen, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)); + private Regex AllowedUnquoted => _allowedUnquoted ??= + new Regex(_quoteHyphens ? s_allowedUnquotedRegexNoHyphen : s_allowedUnquotedRegexWithHyphen, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); /// /// Checks the given switch parameter to see if it must/can be quoted. @@ -259,6 +259,9 @@ protected void AppendQuotedTextToBuffer(StringBuilder buffer, string unquotedTex } // Count the number of quotes +#if NET + int literalQuotes = unquotedTextToAppend.AsSpan().Count('"'); +#else int literalQuotes = 0; for (int i = 0; i < unquotedTextToAppend.Length; i++) { @@ -267,6 +270,8 @@ protected void AppendQuotedTextToBuffer(StringBuilder buffer, string unquotedTex literalQuotes++; } } +#endif + if (literalQuotes > 0) { // Replace any \" sequences with \\" diff --git a/src/Utilities/LockCheck.cs b/src/Utilities/LockCheck.cs index 7b191851e70..f9ec08b314a 100644 --- a/src/Utilities/LockCheck.cs +++ b/src/Utilities/LockCheck.cs @@ -241,7 +241,7 @@ public override bool Equals(object obj) public override string ToString() { - return ProcessId + "@" + StartTime.ToString("s"); + return $"{ProcessId}@{StartTime:s}"; } } diff --git a/src/Utilities/SDKManifest.cs b/src/Utilities/SDKManifest.cs index 86e0be400ce..d8c06a2c234 100644 --- a/src/Utilities/SDKManifest.cs +++ b/src/Utilities/SDKManifest.cs @@ -45,22 +45,22 @@ public class SDKManifest /// /// Pattern in path to extension SDK used to help determine if manifest is from a framework SDK /// - private static string s_extensionSDKPathPattern = @"\MICROSOFT SDKS\WINDOWS\V8.0\EXTENSIONSDKS"; + private const string s_extensionSDKPathPattern = @"\MICROSOFT SDKS\WINDOWS\V8.0\EXTENSIONSDKS"; /// /// Default version of MaxPlatformVersion in framework extension SDKs with manifest not containing such a property /// - private static string s_defaultMaxPlatformVersion = "8.0"; + private const string s_defaultMaxPlatformVersion = "8.0"; /// /// Default version of MinOSVersion in framework extension SDKs with manifest not containing such a property /// - private static string s_defaultMinOSVersion = "6.2.1"; + private const string s_defaultMinOSVersion = "6.2.1"; /// /// Default version of MaxOSVersionTested in framework extension SDKs with manifest not containing such a property /// - private static string s_defaultMaxOSVersionTested = "6.2.1"; + private const string s_defaultMaxOSVersionTested = "6.2.1"; /// /// What should happen if this sdk is resolved with other sdks of the same productfamily or same sdk name. diff --git a/src/Utilities/TargetPlatformSDK.cs b/src/Utilities/TargetPlatformSDK.cs index 8740fe39e10..f0c7992c74b 100644 --- a/src/Utilities/TargetPlatformSDK.cs +++ b/src/Utilities/TargetPlatformSDK.cs @@ -189,6 +189,6 @@ public bool ContainsPlatform(string targetPlatformIdentifier, string targetPlatf /// /// Given an identifier and version, construct a string to use as a key for that combination. /// - internal static string GetSdkKey(string sdkIdentifier, string sdkVersion) => string.Format(CultureInfo.InvariantCulture, "{0}, Version={1}", sdkIdentifier, sdkVersion); + internal static string GetSdkKey(string sdkIdentifier, string sdkVersion) => $"{sdkIdentifier}, Version={sdkVersion}"; } } diff --git a/src/Utilities/ToolLocationHelper.cs b/src/Utilities/ToolLocationHelper.cs index 3f13658e0dd..598b9539c43 100644 --- a/src/Utilities/ToolLocationHelper.cs +++ b/src/Utilities/ToolLocationHelper.cs @@ -1821,7 +1821,7 @@ public static string GetPathToStandardLibraries(string targetFrameworkIdentifier if (NativeMethodsShared.IsWindows && platformTarget != null) { // If we are a 32 bit operating system the we should always return the 32 bit directory, or we are targeting x86, arm is also 32 bit - if (!EnvironmentUtilities.Is64BitOperatingSystem || platformTarget.Equals("x86", StringComparison.OrdinalIgnoreCase) || platformTarget.Equals("arm", StringComparison.OrdinalIgnoreCase)) + if (!Environment.Is64BitOperatingSystem || platformTarget.Equals("x86", StringComparison.OrdinalIgnoreCase) || platformTarget.Equals("arm", StringComparison.OrdinalIgnoreCase)) { targetedArchitecture = SharedDotNetFrameworkArchitecture.Bitness32; } @@ -2863,7 +2863,7 @@ private static void GatherSDKListFromRegistry(string registryRoot, Dictionary GetFrameworkVersions(string frameworkReferenceRoot, // only add if the version folder name is of the right format if (folder.Name.Length >= 4 && folder.Name.StartsWith("v", StringComparison.OrdinalIgnoreCase)) { - Version ver; - if (Version.TryParse(folder.Name.Substring(1), out ver)) + if (Version.TryParse( +#if NET + folder.Name.AsSpan(1), +#else + folder.Name.Substring(1), +#endif + out _)) { frameworkVersions.Add(folder.Name); } @@ -3982,10 +3991,14 @@ private VersionComparer() public int Compare(string versionX, string versionY) { +#if NET + return Version.Parse(versionX.AsSpan(1)).CompareTo(Version.Parse(versionY.AsSpan(1))); +#else return new Version(versionX.Substring(1)).CompareTo(new Version(versionY.Substring(1))); +#endif } } - #endregion +#endregion } } From aa508b87d8c4a6b5560647a223f78fc8197a1bd5 Mon Sep 17 00:00:00 2001 From: Rainer Sigwald Date: Tue, 18 Feb 2025 17:02:38 -0600 Subject: [PATCH 007/106] IsAsciiHexDigit behind a changewave (to be super conservative) --- .../Conditionals/CharacterUtilities.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Build/Evaluation/Conditionals/CharacterUtilities.cs b/src/Build/Evaluation/Conditionals/CharacterUtilities.cs index 362815294ea..8318bdd2d42 100644 --- a/src/Build/Evaluation/Conditionals/CharacterUtilities.cs +++ b/src/Build/Evaluation/Conditionals/CharacterUtilities.cs @@ -3,6 +3,8 @@ #nullable disable +using Microsoft.Build.Framework; + namespace Microsoft.Build.Evaluation { internal static class CharacterUtilities @@ -24,8 +26,18 @@ internal static bool IsSimpleStringChar(char candidate) internal static bool IsHexDigit(char candidate) { - return char.IsDigit(candidate) || ((uint)((candidate | 0x20) - 'a') <= 'f' - 'a'); - // TODO: Is the intent here really to include Unicode digits, or could this be char.IsAsciiHexChar? + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_14)) + { +#if NET + return char.IsAsciiHexDigit(candidate); +#else + return (candidate - '0' <= '9' - '0') || ((uint)((candidate | 0x20) - 'a') <= 'f' - 'a'); +#endif + } + else + { + return char.IsDigit(candidate) || ((uint)((candidate | 0x20) - 'a') <= 'f' - 'a'); + } } } } From 7f81a3d09fe0fd453b837bcc388c5d40de14231c Mon Sep 17 00:00:00 2001 From: SimaTian Date: Wed, 26 Feb 2025 11:03:25 +0100 Subject: [PATCH 008/106] asking terminal for dimensions during every frame is expensive --- .../Logging/TerminalLogger/TerminalLogger.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Build/Logging/TerminalLogger/TerminalLogger.cs b/src/Build/Logging/TerminalLogger/TerminalLogger.cs index 33696ada520..d186b1e8f0c 100644 --- a/src/Build/Logging/TerminalLogger/TerminalLogger.cs +++ b/src/Build/Logging/TerminalLogger/TerminalLogger.cs @@ -1058,11 +1058,21 @@ private void ErrorRaised(object sender, BuildErrorEventArgs e) private void ThreadProc() { // 1_000 / 30 is a poor approx of 30Hz + var count = 0; while (!_cts.Token.WaitHandle.WaitOne(1_000 / 30)) { + count++; lock (_lock) { - DisplayNodes(); + if (count > 30) + { + count = 0; + DisplayNodes(true); + } + else + { + DisplayNodes(); + } } } @@ -1073,9 +1083,11 @@ private void ThreadProc() /// Render Nodes section. /// It shows what all build nodes do. /// - internal void DisplayNodes() + internal void DisplayNodes(bool updateSize = false) { - TerminalNodesFrame newFrame = new TerminalNodesFrame(_nodes, width: Terminal.Width, height: Terminal.Height); + var width = updateSize ? Terminal.Width : _currentFrame.Width; + var height = updateSize ? Terminal.Height : _currentFrame.Height; + TerminalNodesFrame newFrame = new TerminalNodesFrame(_nodes, width: width, height: height); // Do not render delta but clear everything if Terminal width or height have changed. if (newFrame.Width != _currentFrame.Width || newFrame.Height != _currentFrame.Height) From 68351f80535a60345137a72e941b52c568499fc8 Mon Sep 17 00:00:00 2001 From: SimaTian Date: Wed, 26 Feb 2025 12:16:43 +0100 Subject: [PATCH 009/106] default change --- src/Build/Logging/TerminalLogger/TerminalLogger.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Build/Logging/TerminalLogger/TerminalLogger.cs b/src/Build/Logging/TerminalLogger/TerminalLogger.cs index d186b1e8f0c..0cb65a97212 100644 --- a/src/Build/Logging/TerminalLogger/TerminalLogger.cs +++ b/src/Build/Logging/TerminalLogger/TerminalLogger.cs @@ -1064,14 +1064,14 @@ private void ThreadProc() count++; lock (_lock) { - if (count > 30) + if (count >= 30) { count = 0; - DisplayNodes(true); + DisplayNodes(); } else { - DisplayNodes(); + DisplayNodes(false); } } } @@ -1083,7 +1083,7 @@ private void ThreadProc() /// Render Nodes section. /// It shows what all build nodes do. /// - internal void DisplayNodes(bool updateSize = false) + internal void DisplayNodes(bool updateSize = true) { var width = updateSize ? Terminal.Width : _currentFrame.Width; var height = updateSize ? Terminal.Height : _currentFrame.Height; From 47a95b09c0a1feadcf91c703d6ce3b96fbe5b3f1 Mon Sep 17 00:00:00 2001 From: SimaTian Date: Thu, 27 Feb 2025 09:21:18 +0100 Subject: [PATCH 010/106] using virtual fuction instead of reflection --- src/Framework/BuildEventArgs.cs | 11 +++++++++++ src/Shared/LogMessagePacketBase.cs | 16 +++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Framework/BuildEventArgs.cs b/src/Framework/BuildEventArgs.cs index e7d5868c1d8..014a1ce3111 100644 --- a/src/Framework/BuildEventArgs.cs +++ b/src/Framework/BuildEventArgs.cs @@ -200,6 +200,17 @@ internal virtual void WriteToStream(BinaryWriter writer) WriteToStreamWithExplicitMessage(writer, RawMessage); } + + /// + /// Convenience access point for CreateFromStream method to avoid making everything public. + /// + /// + /// + public void PublicCreateFromStream(BinaryReader reader, int version) + { + CreateFromStream(reader, version); + } + /// /// Deserializes from a stream through a binary reader /// diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 36e8e9db0df..db098934c20 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -18,6 +18,8 @@ using Microsoft.Build.Framework.Profiler; using System.Collections; using System.Linq; +using System.Diagnostics; + #endif #if FEATURE_APPDOMAIN @@ -270,11 +272,12 @@ internal abstract class LogMessagePacketBase : INodePacket /// private static readonly int s_defaultPacketVersion = (Environment.Version.Major * 10) + Environment.Version.Minor; +#if TASKHOST /// /// Dictionary of methods used to read BuildEventArgs. /// private static Dictionary s_readMethodCache = new Dictionary(); - +#endif /// /// Dictionary of methods used to write BuildEventArgs. /// @@ -468,16 +471,18 @@ internal void ReadFromStream(ITranslator translator) _buildEvent = GetBuildEventArgFromId(); + // The other side is telling us whether the event knows how to log itself, or whether we're going to have // to do it manually int packetVersion = s_defaultPacketVersion; translator.Translate(ref packetVersion); - bool eventCanSerializeItself = true; translator.Translate(ref eventCanSerializeItself); if (eventCanSerializeItself) { + +#if TASKHOST MethodInfo methodInfo = null; lock (s_readMethodCache) { @@ -488,10 +493,15 @@ internal void ReadFromStream(ITranslator translator) s_readMethodCache.Add(_eventType, methodInfo); } } - ArgsReaderDelegate readerMethod = (ArgsReaderDelegate)CreateDelegateRobust(typeof(ArgsReaderDelegate), _buildEvent, methodInfo); readerMethod(translator.Reader, packetVersion); + +#else + _buildEvent.PublicCreateFromStream(translator.Reader, packetVersion); +#endif + + if (_eventType == LoggingEventType.TargetFinishedEvent && _targetFinishedTranslator != null) { _targetFinishedTranslator(translator, (TargetFinishedEventArgs)_buildEvent); From e35816485d93efea2c1116872172826ff11a99b6 Mon Sep 17 00:00:00 2001 From: Tomas Bartonek Date: Thu, 27 Feb 2025 09:34:13 +0100 Subject: [PATCH 011/106] Remove unused System.Diagnostics import --- src/Shared/LogMessagePacketBase.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index db098934c20..4bada688cc5 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; @@ -18,8 +18,6 @@ using Microsoft.Build.Framework.Profiler; using System.Collections; using System.Linq; -using System.Diagnostics; - #endif #if FEATURE_APPDOMAIN From 20a1cfee1c4e4cddd644cf4e604819724a98b715 Mon Sep 17 00:00:00 2001 From: SimaTian Date: Thu, 27 Feb 2025 09:38:12 +0100 Subject: [PATCH 012/106] added a comment for the future --- src/Build/Logging/TerminalLogger/TerminalLogger.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Build/Logging/TerminalLogger/TerminalLogger.cs b/src/Build/Logging/TerminalLogger/TerminalLogger.cs index 0cb65a97212..64f2cd341d6 100644 --- a/src/Build/Logging/TerminalLogger/TerminalLogger.cs +++ b/src/Build/Logging/TerminalLogger/TerminalLogger.cs @@ -1064,6 +1064,7 @@ private void ThreadProc() count++; lock (_lock) { + // Querying the terminal for it's dimensions is expensive, so we only do it every 30 frames e.g. once a second. if (count >= 30) { count = 0; From 1f645545c5fb868a240986568674723fd62ac2bf Mon Sep 17 00:00:00 2001 From: Rainer Sigwald Date: Tue, 11 Mar 2025 16:53:04 -0500 Subject: [PATCH 013/106] Revert "IsAsciiHexDigit behind a changewave (to be super conservative)" This reverts commit aa508b87d8c4a6b5560647a223f78fc8197a1bd5. No need to worry about speed on this path so let's just match existing behavior. --- .../Evaluation/Conditionals/CharacterUtilities.cs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/Build/Evaluation/Conditionals/CharacterUtilities.cs b/src/Build/Evaluation/Conditionals/CharacterUtilities.cs index 8318bdd2d42..ed5502e91ca 100644 --- a/src/Build/Evaluation/Conditionals/CharacterUtilities.cs +++ b/src/Build/Evaluation/Conditionals/CharacterUtilities.cs @@ -3,8 +3,6 @@ #nullable disable -using Microsoft.Build.Framework; - namespace Microsoft.Build.Evaluation { internal static class CharacterUtilities @@ -26,18 +24,7 @@ internal static bool IsSimpleStringChar(char candidate) internal static bool IsHexDigit(char candidate) { - if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_14)) - { -#if NET - return char.IsAsciiHexDigit(candidate); -#else - return (candidate - '0' <= '9' - '0') || ((uint)((candidate | 0x20) - 'a') <= 'f' - 'a'); -#endif - } - else - { - return char.IsDigit(candidate) || ((uint)((candidate | 0x20) - 'a') <= 'f' - 'a'); - } + return char.IsDigit(candidate) || ((uint)((candidate | 0x20) - 'a') <= 'f' - 'a'); } } } From d9a95b44fb64402465d212ea6557db45bc0b584c Mon Sep 17 00:00:00 2001 From: Jenny Bai Date: Wed, 12 Mar 2025 01:58:43 +0000 Subject: [PATCH 014/106] Fix the head branch when search the created PRs (#11569) Fix the issue https://github.com/dotnet/msbuild/actions/runs/13778304840/job/38531781822 that failed to create the PR since the head parameter doesn't include the owner --- .github/workflows/SyncAnalyzerTemplateMSBuildVersion.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/SyncAnalyzerTemplateMSBuildVersion.yml b/.github/workflows/SyncAnalyzerTemplateMSBuildVersion.yml index 24498a60544..d70b263742e 100644 --- a/.github/workflows/SyncAnalyzerTemplateMSBuildVersion.yml +++ b/.github/workflows/SyncAnalyzerTemplateMSBuildVersion.yml @@ -142,14 +142,19 @@ jobs: const { data: pullRequests } = await github.rest.pulls.list({ owner: context.repo.owner, repo: context.repo.repo, - head: newBranch, + head: `${context.repo.owner}:${newBranch}`, base: baseBranch, state: 'open', }); if (pullRequests.length === 0) { + console.log(`No open pull requests found for branch ${newBranch} against ${baseBranch}.`); return true; } else { + // Log pull request details + pullRequests.forEach(pr => { + console.log(`Pull request #${pr.number}: ${pr.title} (created by ${pr.user.login})`); + }); return false; } } From 1ab203b112dc80159d6fe1af895d5ea2e08f422f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Wed, 12 Mar 2025 10:01:36 +0800 Subject: [PATCH 015/106] Update dependencies from https://github.com/nuget/nuget.client build 6.14.0.66 (#11552) NuGet.Build.Tasks From Version 6.14.0-preview.1.53 -> To Version 6.14.0-preview.1.66 Co-authored-by: dotnet-maestro[bot] Co-authored-by: Gang Wang --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 176bd3256ab..f3b3c5786ae 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -119,9 +119,9 @@ https://github.com/dotnet/arcade 5da211e1c42254cb35e7ef3d5a8428fb24853169 - + https://github.com/nuget/nuget.client - 9202ddad5fabd4d7737fa0c717524fbe2455c972 + 181b65dad9f440c7a31fe673abc59c258f224ada https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index 0af4a6c8940..65a86a6ec47 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -79,7 +79,7 @@ 4.2.0-1.22102.8 9.0.0-beta.25111.5 4.14.0-3.25157.4 - 6.14.0-preview.1.53 + 6.14.0-preview.1.66 9.0.200-preview.0.24603.3 From 617f6789cf496437a71357039a834aae69cc334a Mon Sep 17 00:00:00 2001 From: Jenny Bai Date: Wed, 12 Mar 2025 08:58:47 +0000 Subject: [PATCH 016/106] Fix test to take warning MSB5018 (#11499) Fixes #9176 Context Occasionally temp files fail to delete because of virus checkers, so generate MSB5018 warning. The commit d7788d6#diff-3abd8382aac3bdfa59d5c1ca41dd089795d6ca539a00b3c50eab4fd6a0996314 including string lockedFileMessage = LockCheck.GetLockedFileMessage(fileName); doesn't output the related process. Changes Made Add the warning MSB5018 --- src/Tasks.UnitTests/Exec_Tests.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Tasks.UnitTests/Exec_Tests.cs b/src/Tasks.UnitTests/Exec_Tests.cs index 6997613405b..50732b70703 100644 --- a/src/Tasks.UnitTests/Exec_Tests.cs +++ b/src/Tasks.UnitTests/Exec_Tests.cs @@ -171,9 +171,20 @@ public void Timeout() Assert.Equal(expectedExitCode, exec.ExitCode); ((MockEngine)exec.BuildEngine).AssertLogContains("MSB5002"); int warningsCount = ((MockEngine)exec.BuildEngine).Warnings; - warningsCount.ShouldBe(1, + if (warningsCount == 1) + { + warningsCount.ShouldBe(1, $"Expected 1 warning, encountered {warningsCount}: " + string.Join(",", ((MockEngine)exec.BuildEngine).WarningEvents.Select(w => w.Message))); + } + else + { + // Occasionally temp files fail to delete because of virus checkers, so generate MSB5018 warning + ((MockEngine)exec.BuildEngine).AssertLogContains("MSB5018"); + warningsCount.ShouldBe(2, + $"Expected 2 warnings, encountered {warningsCount}: " + string.Join(",", + ((MockEngine)exec.BuildEngine).WarningEvents.Select(w => w.Message))); + } // ToolTask does not log an error on timeout. Assert.Equal(0, ((MockEngine)exec.BuildEngine).Errors); From 374f568c9fd141416dbd10492b6e4a4f999b989d Mon Sep 17 00:00:00 2001 From: SimaTian Date: Wed, 12 Mar 2025 10:58:20 +0100 Subject: [PATCH 017/106] removing an unnecessary method --- src/Framework/BuildEventArgs.cs | 10 ---------- src/Shared/LogMessagePacketBase.cs | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Framework/BuildEventArgs.cs b/src/Framework/BuildEventArgs.cs index 014a1ce3111..e98e2d5bc16 100644 --- a/src/Framework/BuildEventArgs.cs +++ b/src/Framework/BuildEventArgs.cs @@ -201,16 +201,6 @@ internal virtual void WriteToStream(BinaryWriter writer) } - /// - /// Convenience access point for CreateFromStream method to avoid making everything public. - /// - /// - /// - public void PublicCreateFromStream(BinaryReader reader, int version) - { - CreateFromStream(reader, version); - } - /// /// Deserializes from a stream through a binary reader /// diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 4bada688cc5..15e838eef7b 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -496,7 +496,7 @@ internal void ReadFromStream(ITranslator translator) readerMethod(translator.Reader, packetVersion); #else - _buildEvent.PublicCreateFromStream(translator.Reader, packetVersion); + _buildEvent.CreateFromStream(translator.Reader, packetVersion); #endif From 55c1ad34bf5486a068617ce7d0e20f49cc068bd6 Mon Sep 17 00:00:00 2001 From: Tomas Bartonek Date: Wed, 12 Mar 2025 14:43:54 +0100 Subject: [PATCH 018/106] Remove an extra newline in BuildEventArgs.cs --- src/Framework/BuildEventArgs.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Framework/BuildEventArgs.cs b/src/Framework/BuildEventArgs.cs index e98e2d5bc16..1e1264246ec 100644 --- a/src/Framework/BuildEventArgs.cs +++ b/src/Framework/BuildEventArgs.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; @@ -200,7 +200,6 @@ internal virtual void WriteToStream(BinaryWriter writer) WriteToStreamWithExplicitMessage(writer, RawMessage); } - /// /// Deserializes from a stream through a binary reader /// From f5f5a61ebb73186f938d9adee6a19e0e419d3858 Mon Sep 17 00:00:00 2001 From: Mariana Dematte Date: Wed, 12 Mar 2025 15:07:57 +0100 Subject: [PATCH 019/106] update versioning --- eng/Versions.props | 4 ++-- src/Tasks/CompatibilitySuppressions.xml | 9 --------- src/Utilities/CompatibilitySuppressions.xml | 5 ----- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 65a86a6ec47..ffa21ffdbfa 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -2,8 +2,8 @@ - 17.14.0 - 17.13.0-preview-24611-01 + 17.15.0 + 17.14.0-preview-25161-14 15.1.0.0 preview true diff --git a/src/Tasks/CompatibilitySuppressions.xml b/src/Tasks/CompatibilitySuppressions.xml index 6e6f63a5eb6..fafd80dcb79 100644 --- a/src/Tasks/CompatibilitySuppressions.xml +++ b/src/Tasks/CompatibilitySuppressions.xml @@ -1,10 +1,6 @@  - CP0007 T:Microsoft.Build.Tasks.AL @@ -71,11 +67,6 @@ ref/netstandard2.0/Microsoft.Build.Tasks.Core.dll ref/net472/Microsoft.Build.Tasks.Core.dll - PKV004 .NETCoreApp,Version=v2.0 diff --git a/src/Utilities/CompatibilitySuppressions.xml b/src/Utilities/CompatibilitySuppressions.xml index eeee8bb3f8a..d61bb3c6b1f 100644 --- a/src/Utilities/CompatibilitySuppressions.xml +++ b/src/Utilities/CompatibilitySuppressions.xml @@ -1,11 +1,6 @@  - PKV004 .NETCoreApp,Version=v2.0 From 76b7b33938e42dc9c47987d785923f67e23e0b68 Mon Sep 17 00:00:00 2001 From: AR-May <67507805+AR-May@users.noreply.github.com> Date: Wed, 12 Mar 2025 15:23:24 +0100 Subject: [PATCH 020/106] Update tsa config (#11578) --- .config/tsaoptions.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json index 53a4a691178..3f3b1eb1cde 100644 --- a/.config/tsaoptions.json +++ b/.config/tsaoptions.json @@ -6,5 +6,6 @@ "iterationPath": "DevDiv", "notificationAliases": [ "msbtm@microsoft.com" ], "repositoryName": "MSBuild", - "codebaseName": "MSBuild" + "codebaseName": "MSBuild", + "serviceTreeId": "d0ebbe59-0779-4466-8280-a0ff9cab5550" } From 6474ab07d51da624a99a6696bc129c3becd8a405 Mon Sep 17 00:00:00 2001 From: Rainer Sigwald Date: Wed, 12 Mar 2025 10:19:40 -0500 Subject: [PATCH 021/106] Switch to AwesomeAssertions (#11577) * Switch to AwesomeAssertions * Account for library breaking change --- eng/dependabot/Packages.props | 4 ++-- src/Build.UnitTests/BackEnd/NodePackets_Tests.cs | 4 ++-- .../Microsoft.Build.Engine.UnitTests.csproj | 8 ++++---- .../Microsoft.Build.Framework.UnitTests.csproj | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/eng/dependabot/Packages.props b/eng/dependabot/Packages.props index f5c2c790f82..2c9a1ee8d64 100644 --- a/eng/dependabot/Packages.props +++ b/eng/dependabot/Packages.props @@ -13,8 +13,8 @@ - - + + diff --git a/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs b/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs index 3da36a531ab..74963e26794 100644 --- a/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs +++ b/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs @@ -331,12 +331,12 @@ public void TestTranslation() LogMessagePacket deserializedPacket = tempPacket as LogMessagePacket; packet.Should().BeEquivalentTo(deserializedPacket, options => options - .RespectingRuntimeTypes()); + .PreferringRuntimeMemberTypes()); BuildEventArgs args = packet.NodeBuildEvent?.Value; BuildEventArgs desArgs = deserializedPacket?.NodeBuildEvent?.Value; desArgs.Should().BeEquivalentTo(args, options => options - .RespectingRuntimeTypes() + .PreferringRuntimeMemberTypes() // Since we use struct DictionaryEntry of class TaskItemData, generated DictionaryEntry.Equals compare TaskItemData by references. // Bellow will instruct equivalency test to not use DictionaryEntry.Equals but its public members for equivalency tests. .ComparingByMembers() diff --git a/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj b/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj index e1c81f71814..e1bab7e113d 100644 --- a/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj +++ b/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj @@ -18,7 +18,7 @@ - + @@ -30,7 +30,7 @@ - + @@ -39,7 +39,7 @@ - + TargetFramework=$(FullFrameworkTFM) TargetFramework=$(LatestDotNetCoreForMSBuild) @@ -50,7 +50,7 @@ TargetFramework=$(FullFrameworkTFM) TargetFramework=$(LatestDotNetCoreForMSBuild) - + diff --git a/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj b/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj index 53222f7643a..aba4ae6c2d2 100644 --- a/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj +++ b/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj @@ -8,7 +8,7 @@ - + From bd1d5ff86ceabf1cdf04a9b3b6cd499b6a838326 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Wed, 12 Mar 2025 22:25:20 +0000 Subject: [PATCH 022/106] Update dependencies from https://github.com/dotnet/arcade build 20250311.4 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.25111.5 -> To Version 9.0.0-beta.25161.4 --- eng/Version.Details.xml | 70 ++++++++++++------- eng/Versions.props | 2 +- .../core-templates/steps/generate-sbom.yml | 2 +- eng/common/generate-sbom-prep.ps1 | 20 ++++-- eng/common/generate-sbom-prep.sh | 17 +++-- eng/common/templates-official/job/job.yml | 1 + global.json | 4 +- 7 files changed, 74 insertions(+), 42 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f3b3c5786ae..2b6fb885763 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -16,108 +16,126 @@ https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + - + https://github.com/dotnet/arcade - 5da211e1c42254cb35e7ef3d5a8428fb24853169 + f33d9e642f0e68a61312164cd9e0baf4e142a999 - + https://github.com/dotnet/arcade - 5da211e1c42254cb35e7ef3d5a8428fb24853169 + f33d9e642f0e68a61312164cd9e0baf4e142a999 - + https://github.com/dotnet/arcade - 5da211e1c42254cb35e7ef3d5a8428fb24853169 + f33d9e642f0e68a61312164cd9e0baf4e142a999 https://github.com/nuget/nuget.client @@ -132,9 +150,9 @@ 46223204b646f96104bac46f9dfa4959da9d86ac - + https://github.com/dotnet/arcade - 5da211e1c42254cb35e7ef3d5a8428fb24853169 + f33d9e642f0e68a61312164cd9e0baf4e142a999 diff --git a/eng/Versions.props b/eng/Versions.props index 65a86a6ec47..1f44ddaafb8 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -77,7 +77,7 @@ Otherwise, this version of dotnet will not be installed and the build will error out. --> $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) 4.2.0-1.22102.8 - 9.0.0-beta.25111.5 + 9.0.0-beta.25161.4 4.14.0-3.25157.4 6.14.0-preview.1.66 diff --git a/eng/common/core-templates/steps/generate-sbom.yml b/eng/common/core-templates/steps/generate-sbom.yml index d938b60e1bb..56a09009482 100644 --- a/eng/common/core-templates/steps/generate-sbom.yml +++ b/eng/common/core-templates/steps/generate-sbom.yml @@ -38,7 +38,7 @@ steps: PackageName: ${{ parameters.packageName }} BuildDropPath: ${{ parameters.buildDropPath }} PackageVersion: ${{ parameters.packageVersion }} - ManifestDirPath: ${{ parameters.manifestDirPath }} + ManifestDirPath: ${{ parameters.manifestDirPath }}/$(ARTIFACT_NAME) ${{ if ne(parameters.IgnoreDirectories, '') }}: AdditionalComponentDetectorArgs: '--IgnoreDirectories ${{ parameters.IgnoreDirectories }}' diff --git a/eng/common/generate-sbom-prep.ps1 b/eng/common/generate-sbom-prep.ps1 index 3e5c1c74a1c..a0c7d792a76 100644 --- a/eng/common/generate-sbom-prep.ps1 +++ b/eng/common/generate-sbom-prep.ps1 @@ -4,18 +4,26 @@ Param( . $PSScriptRoot\pipeline-logging-functions.ps1 +# Normally - we'd listen to the manifest path given, but 1ES templates will overwrite if this level gets uploaded directly +# with their own overwriting ours. So we create it as a sub directory of the requested manifest path. +$ArtifactName = "${env:SYSTEM_STAGENAME}_${env:AGENT_JOBNAME}_SBOM" +$SafeArtifactName = $ArtifactName -replace '["/:<>\\|?@*"() ]', '_' +$SbomGenerationDir = Join-Path $ManifestDirPath $SafeArtifactName + +Write-Host "Artifact name before : $ArtifactName" +Write-Host "Artifact name after : $SafeArtifactName" + Write-Host "Creating dir $ManifestDirPath" + # create directory for sbom manifest to be placed -if (!(Test-Path -path $ManifestDirPath)) +if (!(Test-Path -path $SbomGenerationDir)) { - New-Item -ItemType Directory -path $ManifestDirPath - Write-Host "Successfully created directory $ManifestDirPath" + New-Item -ItemType Directory -path $SbomGenerationDir + Write-Host "Successfully created directory $SbomGenerationDir" } else{ Write-PipelineTelemetryError -category 'Build' "Unable to create sbom folder." } Write-Host "Updating artifact name" -$artifact_name = "${env:SYSTEM_STAGENAME}_${env:AGENT_JOBNAME}_SBOM" -replace '["/:<>\\|?@*"() ]', '_' -Write-Host "Artifact name $artifact_name" -Write-Host "##vso[task.setvariable variable=ARTIFACT_NAME]$artifact_name" +Write-Host "##vso[task.setvariable variable=ARTIFACT_NAME]$SafeArtifactName" diff --git a/eng/common/generate-sbom-prep.sh b/eng/common/generate-sbom-prep.sh index d5c76dc827b..b8ecca72bbf 100644 --- a/eng/common/generate-sbom-prep.sh +++ b/eng/common/generate-sbom-prep.sh @@ -14,19 +14,24 @@ done scriptroot="$( cd -P "$( dirname "$source" )" && pwd )" . $scriptroot/pipeline-logging-functions.sh + +# replace all special characters with _, some builds use special characters like : in Agent.Jobname, that is not a permissible name while uploading artifacts. +artifact_name=$SYSTEM_STAGENAME"_"$AGENT_JOBNAME"_SBOM" +safe_artifact_name="${artifact_name//["/:<>\\|?@*$" ]/_}" manifest_dir=$1 -if [ ! -d "$manifest_dir" ] ; then - mkdir -p "$manifest_dir" - echo "Sbom directory created." $manifest_dir +# Normally - we'd listen to the manifest path given, but 1ES templates will overwrite if this level gets uploaded directly +# with their own overwriting ours. So we create it as a sub directory of the requested manifest path. +sbom_generation_dir="$manifest_dir/$safe_artifact_name" + +if [ ! -d "$sbom_generation_dir" ] ; then + mkdir -p "$sbom_generation_dir" + echo "Sbom directory created." $sbom_generation_dir else Write-PipelineTelemetryError -category 'Build' "Unable to create sbom folder." fi -artifact_name=$SYSTEM_STAGENAME"_"$AGENT_JOBNAME"_SBOM" echo "Artifact name before : "$artifact_name -# replace all special characters with _, some builds use special characters like : in Agent.Jobname, that is not a permissible name while uploading artifacts. -safe_artifact_name="${artifact_name//["/:<>\\|?@*$" ]/_}" echo "Artifact name after : "$safe_artifact_name export ARTIFACT_NAME=$safe_artifact_name echo "##vso[task.setvariable variable=ARTIFACT_NAME]$safe_artifact_name" diff --git a/eng/common/templates-official/job/job.yml b/eng/common/templates-official/job/job.yml index 605692d2fb7..817555505aa 100644 --- a/eng/common/templates-official/job/job.yml +++ b/eng/common/templates-official/job/job.yml @@ -16,6 +16,7 @@ jobs: parameters: PackageVersion: ${{ parameters.packageVersion }} BuildDropPath: ${{ parameters.buildDropPath }} + ManifestDirPath: $(Build.ArtifactStagingDirectory)/sbom publishArtifacts: false # publish artifacts diff --git a/global.json b/global.json index ee7246df20f..22086e77c49 100644 --- a/global.json +++ b/global.json @@ -3,13 +3,13 @@ "allowPrerelease": true }, "tools": { - "dotnet": "9.0.103", + "dotnet": "9.0.104", "vs": { "version": "17.12.0" }, "xcopy-msbuild": "17.12.0" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25111.5" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25161.4" } } From 48ccaa0f4f7ea3c8962f25dfa96d54095b7066ce Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 13 Mar 2025 11:10:09 +0100 Subject: [PATCH 023/106] Update MicrosoftBuildVersion to 17.14.0 (#11581) Co-authored-by: github-actions Co-authored-by: Jenny Bai --- .../Microsoft.CheckTemplate/.template.config/template.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template_feed/content/Microsoft.CheckTemplate/.template.config/template.json b/template_feed/content/Microsoft.CheckTemplate/.template.config/template.json index c3b83f0c8e8..91b192b37a9 100644 --- a/template_feed/content/Microsoft.CheckTemplate/.template.config/template.json +++ b/template_feed/content/Microsoft.CheckTemplate/.template.config/template.json @@ -27,7 +27,7 @@ "type": "parameter", "description": "Overrides the default Microsoft.Build version where check's interfaces are placed", "datatype": "text", - "defaultValue": "17.13.0", + "defaultValue": "17.14.0", "replaces": "1.0.0-MicrosoftBuildPackageVersion", "displayName": "Microsoft.Build default package version override" } From 50f5298f3ace5c9a8328b3a71a0cff6e7f32f2ca Mon Sep 17 00:00:00 2001 From: Tomas Bartonek Date: Thu, 13 Mar 2025 11:45:41 +0100 Subject: [PATCH 024/106] Remove unnecessary blank line --- src/Shared/LogMessagePacketBase.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 15e838eef7b..b93be7653fa 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -469,7 +469,6 @@ internal void ReadFromStream(ITranslator translator) _buildEvent = GetBuildEventArgFromId(); - // The other side is telling us whether the event knows how to log itself, or whether we're going to have // to do it manually int packetVersion = s_defaultPacketVersion; From 8212312ff14301b17438699ee0105a50cb66bc01 Mon Sep 17 00:00:00 2001 From: Tomas Bartonek Date: Thu, 13 Mar 2025 11:46:51 +0100 Subject: [PATCH 025/106] Add missing new lines in LogMessagePacketBase.cs --- src/Shared/LogMessagePacketBase.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index b93be7653fa..44c7527df49 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -473,6 +473,7 @@ internal void ReadFromStream(ITranslator translator) // to do it manually int packetVersion = s_defaultPacketVersion; translator.Translate(ref packetVersion); + bool eventCanSerializeItself = true; translator.Translate(ref eventCanSerializeItself); @@ -490,6 +491,7 @@ internal void ReadFromStream(ITranslator translator) s_readMethodCache.Add(_eventType, methodInfo); } } + ArgsReaderDelegate readerMethod = (ArgsReaderDelegate)CreateDelegateRobust(typeof(ArgsReaderDelegate), _buildEvent, methodInfo); readerMethod(translator.Reader, packetVersion); From 8f5fe7f9d9a1547e1c1088495da06e2b202f9d48 Mon Sep 17 00:00:00 2001 From: Tomas Bartonek Date: Thu, 13 Mar 2025 11:49:26 +0100 Subject: [PATCH 026/106] Remove trailing whitespace in LogMessagePacketBase.cs --- src/Shared/LogMessagePacketBase.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 44c7527df49..04216a42dad 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -500,7 +500,6 @@ internal void ReadFromStream(ITranslator translator) _buildEvent.CreateFromStream(translator.Reader, packetVersion); #endif - if (_eventType == LoggingEventType.TargetFinishedEvent && _targetFinishedTranslator != null) { _targetFinishedTranslator(translator, (TargetFinishedEventArgs)_buildEvent); From bd5e36c7c01dbea80c329bc1859c078a6fecda83 Mon Sep 17 00:00:00 2001 From: SimaTian Date: Fri, 14 Mar 2025 15:33:56 +0100 Subject: [PATCH 027/106] removing an unused import --- src/Shared/NamedPipeUtil.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Shared/NamedPipeUtil.cs b/src/Shared/NamedPipeUtil.cs index fa081ca6c39..bf31c0193f6 100644 --- a/src/Shared/NamedPipeUtil.cs +++ b/src/Shared/NamedPipeUtil.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.Diagnostics; using System.IO; From e0b8f2b685ca10fedce90bdeb946e0ff1e5f4ba7 Mon Sep 17 00:00:00 2001 From: Tomas Bartonek Date: Fri, 14 Mar 2025 15:34:39 +0100 Subject: [PATCH 028/106] Update src/Shared/ProjectWriter.cs Co-authored-by: Rainer Sigwald --- src/Shared/ProjectWriter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Shared/ProjectWriter.cs b/src/Shared/ProjectWriter.cs index d4351f3a76c..b71dff55fc5 100644 --- a/src/Shared/ProjectWriter.cs +++ b/src/Shared/ProjectWriter.cs @@ -42,7 +42,6 @@ internal sealed partial class ProjectWriter : XmlTextWriter // description of an item vector transform, including the optional separator specification, but with no (named) capturing // groups -- see the WriteString() method for details // regular expression used to match item vector transforms, with no (named) capturing groups - // internal for unit testing only private const string itemVectorTransformRawSpecification = @"@\(\s* (" + itemTypeOrMetadataNameSpecification + @") From 4c6a1cc70c1480c8edd70b9ad0f145282785bbc2 Mon Sep 17 00:00:00 2001 From: Tomas Bartonek Date: Fri, 14 Mar 2025 15:34:49 +0100 Subject: [PATCH 029/106] Update src/Shared/ProjectWriter.cs Co-authored-by: Rainer Sigwald --- src/Shared/ProjectWriter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Shared/ProjectWriter.cs b/src/Shared/ProjectWriter.cs index b71dff55fc5..d5a5a00003f 100644 --- a/src/Shared/ProjectWriter.cs +++ b/src/Shared/ProjectWriter.cs @@ -29,7 +29,6 @@ internal sealed partial class ProjectWriter : XmlTextWriter internal const string itemTypeOrMetadataNameSpecification = @"[A-Za-z_][A-Za-z_0-9\-]*"; // regular expression used to match item vector transforms - // internal for unit testing only // description of an item vector transform, including the optional separator specification private const string itemVectorTransformSpecification = @"(?@\(\s*) From eca8a9ba67d47d60e6f09191b05f1d7fc08f5608 Mon Sep 17 00:00:00 2001 From: SimaTian Date: Fri, 14 Mar 2025 15:40:17 +0100 Subject: [PATCH 030/106] merge aftermath --- src/Build/BackEnd/Components/Scheduler/Scheduler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Build/BackEnd/Components/Scheduler/Scheduler.cs b/src/Build/BackEnd/Components/Scheduler/Scheduler.cs index 656d7374c5f..5911b984a72 100644 --- a/src/Build/BackEnd/Components/Scheduler/Scheduler.cs +++ b/src/Build/BackEnd/Components/Scheduler/Scheduler.cs @@ -2567,7 +2567,7 @@ private void TraceScheduler(string format, params object[] stuff) FileUtilities.EnsureDirectoryExists(_debugDumpPath); using StreamWriter file = FileUtilities.OpenWrite(string.Format(CultureInfo.CurrentCulture, Path.Combine(_debugDumpPath, "SchedulerTrace_{0}.txt"), EnvironmentUtilities.CurrentProcessId), append: true); - file.Write("{0}({1})-{2}: ", Thread.CurrentThread.Name, Thread.CurrentThread.ManagedThreadId, _schedulingData.EventTime.Ticks); + file.Write("{0}({1})-{2}: ", Thread.CurrentThread.Name, Environment.CurrentManagedThreadId, _schedulingData.EventTime.Ticks); file.WriteLine(format, stuff); file.Flush(); } From b72917def04e3332598fa07e5525111848bb25b7 Mon Sep 17 00:00:00 2001 From: SimaTian Date: Fri, 14 Mar 2025 17:11:52 +0100 Subject: [PATCH 031/106] fixing some merge issues --- src/Build/Logging/TerminalLogger/TerminalLogger.cs | 5 ----- src/Shared/NodeEndpointOutOfProcBase.cs | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Build/Logging/TerminalLogger/TerminalLogger.cs b/src/Build/Logging/TerminalLogger/TerminalLogger.cs index 0812312f57b..44d5727c627 100644 --- a/src/Build/Logging/TerminalLogger/TerminalLogger.cs +++ b/src/Build/Logging/TerminalLogger/TerminalLogger.cs @@ -162,11 +162,6 @@ public ProjectContext(BuildEventContext context) /// private bool _loggedPreviewMessage; - /// - /// The two directory separator characters to be passed to methods like . - /// - private static readonly char[] PathSeparators = { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; - /// /// One summary per finished project test run. /// diff --git a/src/Shared/NodeEndpointOutOfProcBase.cs b/src/Shared/NodeEndpointOutOfProcBase.cs index 3b7e40801d7..846f7716ec4 100644 --- a/src/Shared/NodeEndpointOutOfProcBase.cs +++ b/src/Shared/NodeEndpointOutOfProcBase.cs @@ -522,7 +522,7 @@ private void RunReadLoop(BufferedReadStream localReadPipe, NamedPipeServerStream #if NET451_OR_GREATER Task readTask = localReadPipe.ReadAsync(headerByte, 0, headerByte.Length, CancellationToken.None); #elif NETCOREAPP - Task readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length); + Task readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length).AsTask(); #else IAsyncResult result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); #endif @@ -614,7 +614,7 @@ private void RunReadLoop(BufferedReadStream localReadPipe, NamedPipeServerStream #if NET451_OR_GREATER readTask = localReadPipe.ReadAsync(headerByte, 0, headerByte.Length, CancellationToken.None); #elif NETCOREAPP - readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length); + readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length).AsTask(); #else result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); #endif From 73849cf449f7a89b7cc9c759f4ef76194e6e1f45 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Mon, 17 Mar 2025 05:02:19 +0000 Subject: [PATCH 032/106] Update dependencies from https://github.com/dotnet/arcade build 20250314.2 Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.25111.5 -> To Version 9.0.0-beta.25164.2 --- eng/Version.Details.xml | 16 ++++++++-------- eng/Versions.props | 2 +- eng/common/tools.ps1 | 4 ++-- eng/common/tools.sh | 4 ++-- global.json | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2b6fb885763..c256c9044ec 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -123,19 +123,19 @@ - + https://github.com/dotnet/arcade - f33d9e642f0e68a61312164cd9e0baf4e142a999 + 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee - + https://github.com/dotnet/arcade - f33d9e642f0e68a61312164cd9e0baf4e142a999 + 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee - + https://github.com/dotnet/arcade - f33d9e642f0e68a61312164cd9e0baf4e142a999 + 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee https://github.com/nuget/nuget.client @@ -150,9 +150,9 @@ 46223204b646f96104bac46f9dfa4959da9d86ac - + https://github.com/dotnet/arcade - f33d9e642f0e68a61312164cd9e0baf4e142a999 + 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee diff --git a/eng/Versions.props b/eng/Versions.props index 1f44ddaafb8..c1fcdcadf46 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -77,7 +77,7 @@ Otherwise, this version of dotnet will not be installed and the build will error out. --> $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) 4.2.0-1.22102.8 - 9.0.0-beta.25161.4 + 9.0.0-beta.25164.2 4.14.0-3.25157.4 6.14.0-preview.1.66 diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index a46b6deb759..22b49e09d09 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://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.ps1" + $uri = "https://builds.dotnet.microsoft.com/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.ps1" Retry({ Write-Host "GET $uri" diff --git a/eng/common/tools.sh b/eng/common/tools.sh index 1159726a10f..01b09b65796 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://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.sh" + local install_script_url="https://builds.dotnet.microsoft.com/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.sh" if [[ ! -a "$install_script" ]]; then mkdir -p "$root" diff --git a/global.json b/global.json index 22086e77c49..e2e3c22ae34 100644 --- a/global.json +++ b/global.json @@ -10,6 +10,6 @@ "xcopy-msbuild": "17.12.0" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25161.4" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25164.2" } } From b9c9d42fb639b462ec4f4dad0732d532ae2becc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Provazn=C3=ADk?= Date: Mon, 17 Mar 2025 12:50:21 +0100 Subject: [PATCH 033/106] Delete .exp-insertions.yml (#11601) remove unused pipeline definition --- .exp-insertions.yml | 236 -------------------------------------------- 1 file changed, 236 deletions(-) delete mode 100644 .exp-insertions.yml diff --git a/.exp-insertions.yml b/.exp-insertions.yml deleted file mode 100644 index dac0bddd22a..00000000000 --- a/.exp-insertions.yml +++ /dev/null @@ -1,236 +0,0 @@ -# Pipeline creates experimental msbuild insertions. - -trigger: none # Prevents this pipeline from triggering on check-ins -pr: none # don't run this on PR as well - -parameters: - # Dotnet installer channel from which to take the latest dotnet bits. - - name: DotnetInstallerChannel - displayName: Dotnet installer channel - type: string - default: 'none' - # VS version for which to take the latest Retail MSBuild bits. - - name: VSVersionName - displayName: VS Version - type: string - default: 'none' - # Branch from the MSBuild Build CI pipeline. Default: main - # Top run for the branch would be used to create an experimental insertion. - - name: MSBuildBranch - displayName: MSBuild Branch - type: string - default: 'refs/heads/main' - # BuildID from the MSBuild Build CI pipeline. Overrides the choice of MSBuildBranch parameter - - name: MSBuildBuildID - displayName: MSBuild CI Run Override - type: string - default: 'default' - -variables: - - name: _MsBuildCiPipelineId - value: 9434 - - name: _MSBuildConfigFilePathRequestURL - value: 'https://dev.azure.com/cloudbuild/CloudBuild/_apis/git/repositories/CloudBuildConfig/items?versionDescriptor.version=main&path=config/batmon/Q-Prod-Co3/Coordinator/ToolsReleaseConfig-GeneralPublic.json&api-version=5.0' - - name: VSVersion - value: ${{parameters.VSVersionName}} - -pool: - vmImage: windows-latest - -jobs: -- job: CreateExpDotnet - displayName: Create Experimental Dotnet - condition: ne('${{ parameters.DotnetInstallerChannel }}', 'none') - steps: - - powershell: | - mkdir '$(System.ArtifactsDirectory)/installer' - - $dotnetChannel = '${{parameters.DotnetInstallerChannel}}' - $sdks = "dotnet-sdk-win-x64.zip", "dotnet-sdk-linux-x64.tar.gz" - - foreach ($sdk in $sdks) - { - Write-Host "Downloading dotnet $sdk from channel $dotnetChannel" - Invoke-WebRequest ` - -Uri "https://aka.ms/dotnet/$dotnetChannel/daily/$sdk" ` - -OutFile "$(System.ArtifactsDirectory)/installer/$sdk" - } - mkdir '$(Pipeline.Workspace)/artifacts' - displayName: Download latest dotnet sdks - - - task: DownloadBuildArtifacts@1 - inputs: - buildType: specific - project: DevDiv - pipeline: $(_MsBuildCiPipelineId) - ${{ if eq(parameters.MSBuildBuildID, 'default') }}: - buildVersionToDownload: latestFromBranch - branchName: '${{parameters.MSBuildBranch}}' - ${{ else }}: - buildVersionToDownload: specific - buildId: ${{parameters.MSBuildBuildID}} - artifactName: bin - itemPattern: 'MSBuild.Bootstrap/**' - downloadPath: '$(System.ArtifactsDirectory)/msbuild/artifacts/bin' - displayName: Download msbuild artifacts - - - powershell: | - $sdk = "dotnet-sdk-win-x64" - - Write-Host "Extracting $(System.ArtifactsDirectory)/installer/$sdk.zip" - Expand-Archive "$(System.ArtifactsDirectory)/installer/$sdk.zip" -DestinationPath "$(Pipeline.Workspace)/exp-dotnet/$sdk" - - $dotnetDirectory = Get-ChildItem -Directory -Path "$(Pipeline.Workspace)/exp-dotnet/$sdk/sdk" - $dotnetVersion = $dotnetDirectory.Name - Write-Host "Detected dotnet version: $dotnetVersion" - - Write-Host "Updating MSBuild dlls." - $(Build.SourcesDirectory)/scripts/Deploy-MSBuild.ps1 ` - -destination "$(Pipeline.Workspace)/exp-dotnet/$sdk/sdk/$dotnetVersion" ` - -binDirectory "$(System.ArtifactsDirectory)/msbuild/artifacts/bin" ` - -configuration Release ` - -makeBackup $false - - Write-Host "Compressing dotnet sdk files" - Get-ChildItem -Path "$(Pipeline.Workspace)/exp-dotnet/$sdk" | Compress-Archive -DestinationPath "$(Pipeline.Workspace)/artifacts/$sdk.zip" - - displayName: Dogfood msbuild dlls to dotnet sdk win-x64 - - - powershell: | - $sdk = "dotnet-sdk-linux-x64" - - mkdir "$(Pipeline.Workspace)/exp-dotnet/$sdk" - - Write-Host "Extracting $(System.ArtifactsDirectory)/installer/$sdk.tar.gz" - tar -xzvf "$(System.ArtifactsDirectory)/installer/$sdk.tar.gz" -C "$(Pipeline.Workspace)/exp-dotnet/$sdk" - - $dotnetDirectory = Get-ChildItem -Directory -Path $(Pipeline.Workspace)/exp-dotnet/$sdk/sdk - $dotnetVersion = $dotnetDirectory.Name - Write-Host "Detected dotnet version: $dotnetVersion" - - Write-Host "Updating MSBuild dlls." - $(Build.SourcesDirectory)/scripts/Deploy-MSBuild.ps1 ` - -destination "$(Pipeline.Workspace)/exp-dotnet/$sdk/sdk/$dotnetVersion" ` - -binDirectory "$(System.ArtifactsDirectory)/msbuild/artifacts/bin" ` - -configuration Release ` - -makeBackup $false - - Write-Host "Compressing dotnet sdk files" - tar -czvf "$(Pipeline.Workspace)/artifacts/$sdk.tar.gz" -C "$(Pipeline.Workspace)/exp-dotnet/$sdk" . - displayName: Dogfood msbuild dlls to dotnet sdk linux-x64 - - - task: PublishPipelineArtifact@1 - inputs: - targetPath: '$(Pipeline.Workspace)/artifacts' - artifactName: ExperimentalDotnet - parallel: true - condition: always() - displayName: Publish crank assests artifacts - - -- job: CreateExpMSBuild - displayName: "Create Experimental MSBuild" - condition: ne('${{ parameters.VSVersionName }}', 'none') - steps: - - powershell: | - $token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("PAT:$env:ACCESSTOKEN")) - $headers = @{ Authorization = "Basic $token" }; - $response = Invoke-RestMethod -Uri "$(_MSBuildConfigFilePathRequestURL)" -Headers $headers -Method Get - $MSBuildDropPath = $response.Tools.MSBuild.Locations - Write-Host "##vso[task.setvariable variable=MSBuildDropPath]$MSBuildDropPath" - Write-Host "MSBuild Drop Path directory: $MSBuildDropPath" - displayName: Get Retail MSBuild Drop Path - env: - ACCESSTOKEN: $(cloudbuild-token) - - - task: NuGetToolInstaller@1 - displayName: 'Install NuGet.exe' - - - task: NuGetCommand@2 - displayName: Restore internal tools - inputs: - command: restore - feedsToUse: config - restoreSolution: '$(Build.SourcesDirectory)\eng\common\internal\Tools.csproj' - nugetConfigPath: '$(Build.SourcesDirectory)\eng\common\internal\NuGet.config' - restoreDirectory: '$(Build.SourcesDirectory)\.packages' - - # https://eng.ms/docs/cloud-ai-platform/devdiv/one-engineering-system-1es/1es-docs/1es-security-configuration/configuration-guides/pat-burndown-guidance#authentication-from-pipelines - # Requires Azure client 2.x - - task: AzureCLI@2 - displayName: 'Set AzDO.DotnetPerfStarToken' - enabled: true - inputs: - azureSubscription: 'dotnet-perfstar at app.vssps.visualstudio.com' # Azure DevOps service connection - scriptType: 'pscore' - scriptLocation: 'inlineScript' - inlineScript: | - # '499b84ac-1321-427f-aa17-267ca6975798' for Azure DevOps - $token = az account get-access-token --query accessToken --resource 499b84ac-1321-427f-aa17-267ca6975798 -o tsv - Write-Host "Setting AzDO.DotnetPerfStarToken" - Write-Host "##vso[task.setvariable variable=AzDO.DotnetPerfStarToken]${token}" - - - powershell: | - mkdir "$(Pipeline.Workspace)/artifacts" - - $dropAppDirectory = Get-ChildItem -Directory -Path "$(Build.SourcesDirectory)/.packages/drop.app" - $dropAppVersion = $dropAppDirectory.Name - Write-Host "Detected drop.exe version: $dropAppVersion" - - $dropExePath = "$(Build.SourcesDirectory)/.packages/drop.app/$dropAppVersion/lib/net45/drop.exe" - Write-Host "Detected drop.exe path: $dropExePath" - - Write-Host "Downloading VS msbuild" - $patAuthEnvVar = "patVariable" - & "$dropExePath" get --patAuthEnvVar $patAuthEnvVar -u "$(MSBuildDropPath)\$(VSVersion)" -d "$(System.ArtifactsDirectory)/VSMSBuildDrop" - Write-Host "Download of VS msbuild finished" - - Write-Host "Copying VS msbuild to $(Pipeline.Workspace)/VSMSBuild" - Copy-Item -Path "$(System.ArtifactsDirectory)/VSMSBuildDrop/*" -Destination "$(Pipeline.Workspace)/VSMSBuild" -Recurse - Write-Host "Copy of VS msbuild finished" - displayName: Download msbuild vs drop - env: - patVariable: $(AzDO.DotnetPerfStarToken) - - - task: DownloadBuildArtifacts@1 - inputs: - buildType: specific - project: DevDiv - pipeline: $(_MsBuildCiPipelineId) - ${{ if eq(parameters.MSBuildBuildID, 'default') }}: - buildVersionToDownload: latestFromBranch - branchName: '${{parameters.MSBuildBranch}}' - ${{ else }}: - buildVersionToDownload: specific - buildId: ${{parameters.MSBuildBuildID}} - artifactName: bin - itemPattern: | - MSBuild.Bootstrap/*/net472/** - Microsoft.Build.Conversion/*/net472/Microsoft.Build.Conversion.Core.dll - Microsoft.Build.Engine/*/net472/Microsoft.Build.Engine.dll - MSBuildTaskHost/**/MSBuildTaskHost.exe - MSBuildTaskHost/**/MSBuildTaskHost.pdb - MSBuild/*/*/net472/MSBuild.exe* - downloadPath: '$(System.ArtifactsDirectory)/msbuild/artifacts/bin' - displayName: Download msbuild artifacts - - - powershell: | - Write-Host "Updating MSBuild dlls." - $(Build.SourcesDirectory)/scripts/Deploy-MSBuild.ps1 ` - -destination "$(Pipeline.Workspace)/VSMSBuild/$(VSVersion)/MSBuild/Current/Bin" ` - -binDirectory "$(System.ArtifactsDirectory)/msbuild/artifacts/bin" ` - -configuration Release ` - -makeBackup $false - - ls "$(Pipeline.Workspace)/VSMSBuild/$(VSVersion)" - Write-Host "Compressing msbuild files" - Get-ChildItem -Path "$(Pipeline.Workspace)/VSMSBuild/$(VSVersion)" | Compress-Archive -DestinationPath "$(Pipeline.Workspace)/artifacts/MSBuild.zip" - displayName: Dogfood msbuild dlls - - - task: PublishPipelineArtifact@1 - inputs: - targetPath: '$(Pipeline.Workspace)/artifacts' - artifactName: ExperimentalMSBuild - parallel: true - condition: always() - displayName: Publish crank assests artifacts From 1c27ccf4061d8d4aad98371e23b922e50c6c6da7 Mon Sep 17 00:00:00 2001 From: vikukush <98849971+vikukush@users.noreply.github.com> Date: Mon, 17 Mar 2025 06:31:01 -0700 Subject: [PATCH 034/106] Update Microsoft.Common.CurrentVersion.targets (#11167) --- src/Tasks/Microsoft.Common.CurrentVersion.targets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 80694ad6d50..48b9de51827 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -1639,7 +1639,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. --> + Name="_SplitProjectReferencesByFileExistence" + DependsOnTargets="AssignProjectConfiguration"> https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + https://github.com/dotnet/runtime - + + @@ -123,13 +141,13 @@ https://github.com/nuget/nuget.client 181b65dad9f440c7a31fe673abc59c258f224ada - + https://github.com/dotnet/roslyn - 46223204b646f96104bac46f9dfa4959da9d86ac + 517e95f9430d387e0e387a23fa2c8351a0863c4a - + https://github.com/dotnet/roslyn - 46223204b646f96104bac46f9dfa4959da9d86ac + 517e95f9430d387e0e387a23fa2c8351a0863c4a diff --git a/eng/Versions.props b/eng/Versions.props index ffa21ffdbfa..509057e0c15 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -78,7 +78,7 @@ $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) 4.2.0-1.22102.8 9.0.0-beta.25111.5 - 4.14.0-3.25157.4 + 4.14.0-3.25164.10 6.14.0-preview.1.66 From bd9741ebde4e10008172c772cfb4813ce403efdf Mon Sep 17 00:00:00 2001 From: Meera Ruxmohan Date: Wed, 19 Mar 2025 06:12:09 -0700 Subject: [PATCH 036/106] Fix NRE in terminal logger for cache plugin builds (#11606) --- src/Build/Logging/TerminalLogger/TerminalLogger.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Build/Logging/TerminalLogger/TerminalLogger.cs b/src/Build/Logging/TerminalLogger/TerminalLogger.cs index 44d5727c627..754165ab923 100644 --- a/src/Build/Logging/TerminalLogger/TerminalLogger.cs +++ b/src/Build/Logging/TerminalLogger/TerminalLogger.cs @@ -808,15 +808,17 @@ private void TargetFinished(object sender, TargetFinishedEventArgs e) // For cache plugin projects which result in a cache hit, ensure the output path is set // to the item spec corresponding to the GetTargetPath target upon completion. var buildEventContext = e.BuildEventContext; + var targetOutputs = e.TargetOutputs; if (_restoreContext is null && buildEventContext is not null + && targetOutputs is not null && _hasUsedCache && e.TargetName == "GetTargetPath" && _projects.TryGetValue(new ProjectContext(buildEventContext), out TerminalProjectInfo? project)) { - if (project.IsCachePluginProject) + if (project is not null && project.IsCachePluginProject) { - foreach (ITaskItem output in e.TargetOutputs) + foreach (ITaskItem output in targetOutputs) { project.OutputPath = output.ItemSpec.AsMemory(); break; From 0a41c5f9005e97f4d58f02b59818fa8e77374e41 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 15:55:55 +0100 Subject: [PATCH 037/106] [automated] Merge branch 'vs17.14' => 'main' (#11572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11081403 * Update Versions.props VersionPrefix * Update Versions.props VersionPrefix * [vs17.14] Remove deprecated 4.3.* System package references & fix entries in Version.Details.xml (#11571) * Remove deprecated 4.3.* package references Both "System.Runtime" and "System.Private.Uri" are inbox in .NETCoreApp since ~2017 and don't need to be referenced explicitly anymore. They were referenced here as external dependencies brought vulnerable netstandard1.x dependencies in which were then flagged by CG. That isn't the case anymore. xunit, shouldly and other packages with their corresponding versions used in this repo don't bring netstandard1.x in anymore. Don't reference "System.Net.Http" for the same reason. It is inbox on .NET Framework, .NETCoreApp and .NET Standard. On .NET Framework a "" item is needed as it isn't part of the default referenced assemblies. Note that this change will help when starting to consume a .NET 10 SDK as those would get flagged by NuGet Prune Package Reference and NuGet Audit. * Avoid netstandard1.x dependencies * fix build * Fix entries in Version.Details.xml and make version overriding clearer (#11561) Follow-up to https://github.com/dotnet/msbuild/pull/11145. We were missing the entry for System.Text.Encoding.CodePages in Version.Details.xml which caused a prebuild in https://github.com/dotnet/sdk/pull/47377. Also simplified the way we reference the different package versions a bit to make it clearer. Remove Microsoft.VisualStudio.SolutionPersistence from SourceBuildPrebuiltBaseline.xml, since we now properly reference it from source-build-externals --------- Co-authored-by: Viktor Hofer Co-authored-by: Alexander Köplinger * Don't ngen StringTools.net35 (#11544) This assembly shouldn't ever be loaded in the net4x context so don't spend the install time ngening it. * Make SolutionParser package reference private (#11603) Context The VS insertion is currently failing due to the SolutionParser version being upgraded beyond the version used by VS. Made the change so this reference is not exposed and so the insertions do not fail. --------- Co-authored-by: dotnet bot Co-authored-by: Jenny Bai Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Viktor Hofer Co-authored-by: Alexander Köplinger Co-authored-by: Rainer Sigwald Co-authored-by: Mariana Dematte --- ...Microsoft.Build.Engine.OM.UnitTests.csproj | 1 + .../Microsoft.Build.Engine.UnitTests.csproj | 1 + src/Build/Microsoft.Build.csproj | 2 +- src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf | 26 +++++++++---------- src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf | 18 ++++++------- src/Package/MSBuild.VSSetup/files.swr | 4 +-- 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj b/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj index dc35f5d7d0b..c5433f3112c 100644 --- a/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj +++ b/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj @@ -65,6 +65,7 @@ + diff --git a/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj b/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj index e1bab7e113d..854159dc86d 100644 --- a/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj +++ b/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj @@ -24,6 +24,7 @@ + all diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index e6fc5f4cccd..b28ac113cd2 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -31,7 +31,7 @@ - + diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf index 0968a8a75fb..24c3f4dcb45 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf @@ -841,9 +841,9 @@ WarningsOnly -- 仅显示警告。 NoItemAndPropertyList -- 在开始生成每个项目时不显示 项和属性的列表。 - ShowCommandLine -- 显示 TaskCommandLineEvent 消息 + ShowCommandLine -- 显示 TaskCommandLineEvent 消息 ShowTimestamp -- 将时间戳作为所有消息的前缀 - 显示。 + 显示。 ShowEventId -- 显示已开始事件、已完成事件和消息 的事件 ID。 ForceNoAlign -- 不将文本与控制台缓冲区的大小 @@ -900,10 +900,10 @@ Example: -validate:MyExtendedBuildSchema.xsd - -validate 依据默认架构验证项目。(缩写: + -validate 依据默认架构验证项目。(缩写: -val) - -validate:<schema> 依据指定的架构验证项目。(缩写: + -validate:<schema> 依据指定的架构验证项目。(缩写: -val) 示例: -validate:MyExtendedBuildSchema.xsd @@ -1081,7 +1081,7 @@ -toolsversion:<version> 要在生成过程中使用的 MSBuild 工具集 (任务、目标等)的版本。此版本将重写 - 各个项目指定的版本。(缩写: + 各个项目指定的版本。(缩写: -tv) 示例: -toolsversion:3.5 @@ -1137,17 +1137,17 @@ template and append the node id to this fileName to create a log file for each node. - -distributedFileLogger + -distributedFileLogger 将生成输出记录到多个日志文件,每个 MSBuild 节点 一个日志文件。这些文件的初始位置为 当前目录。默认情况下,这些文件名为 “MSBuild<nodeid>.log”。可通过添加 - “-fileLoggerParameters”开关来指定 + “-fileLoggerParameters”开关来指定 这些文件的位置和 fileLogger 的其他参数。 如果日志文件名是通过 fileLoggerParameters 开关设置的,分布式记录器将使用 fileName 作为 - 模板并将节点 ID 附加到此 fileName + 模板并将节点 ID 附加到此 fileName 以便为每个节点创建一个日志文件。 @@ -1189,12 +1189,12 @@ -flp1:warningsonly;logfile=msbuild.wrn -flp2:errorsonly;logfile=msbuild.err - -fileloggerparameters[n]:<parameters> + -fileloggerparameters[n]:<parameters> 为文件记录器提供任何额外的参数。 存在此开关意味着 存在对应的 -filelogger[n] 开关。 “n”(如果存在)可以为 1-9 的数字。 - 任何分布式文件记录器也可以使用 + 任何分布式文件记录器也可以使用 -fileloggerparameters,具体可参阅 -distributedFileLogger 的说明。 (缩写: -flp[n]) 为控制台记录器列出的相同参数 @@ -1214,8 +1214,8 @@ -fileLoggerParameters:LogFile=MyLog.log;Append; Verbosity=diagnostic;Encoding=UTF-8 - -flp:Summary;Verbosity=minimal;LogFile=msbuild.sum - -flp1:warningsonly;logfile=msbuild.wrn + -flp:Summary;Verbosity=minimal;LogFile=msbuild.sum + -flp1:warningsonly;logfile=msbuild.wrn -flp2:errorsonly;logfile=msbuild.err @@ -2200,4 +2200,4 @@ - \ No newline at end of file + diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf index b2b8fb45067..58b406ea531 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf @@ -214,7 +214,7 @@ 終端機記錄器的參數。(簡短形式: -tlp) 可用的參數。 default -- 指定終端機記錄器的預設值。 - 其需要下列其中一值: + 其需要下列其中一值: 。 - 'on'、'true' 會強制使用 TerminalLogger,即使 其之後可能會停用。 @@ -227,7 +227,7 @@ -verbosity showCommandLine -- 顯示 TaskCommandLineEvent 訊息 - 範例: + 範例: -tlp:default=auto;verbosity=diag;shownCommandLine @@ -246,7 +246,7 @@ -getResultOutputFile:file 將輸出從 get* 重新導向至檔案。 - 範例: + 範例: -getProperty:Bar -getResultOutputFile:Biz.txt 這會將屬性列的值寫入 Biz.txt。 @@ -263,7 +263,7 @@ -check 在建置期間啟用 BuildChecks。 - BuildCheck 會啟用評估規則以確保組建的 + BuildCheck 會啟用評估規則以確保組建的 屬性。如需詳細資訊,請參閱 aka.ms/buildcheck @@ -446,8 +446,8 @@ -isolateProjects[:True|MessageUponIsolationViolation|False] 導致 MSBuild 在隔離中建置每個專案。 - 設定為 "MessageUponIsolationViolation" - (或其簡短形式 "Message") 時,如果提供 + 設定為 "MessageUponIsolationViolation" + (或其簡短形式 "Message") 時,如果提供 -outputResultsCache 切換,則只會序列化來自 頂層目標的結果。這是為了降低相依性專案上, 由於其相依性位於快取目標上 (其副作用 @@ -1081,8 +1081,8 @@ -toolsversion:<版本> 建置期間所使用的 MSBuild 工具組 (工作、目標等) - 版本。此版本將會覆寫 - 個別專案所指定的版本。(簡短形式: + 版本。此版本將會覆寫 + 個別專案所指定的版本。(簡短形式: -tv) 範例: -toolsVersion:3.5 @@ -2201,4 +2201,4 @@ - \ No newline at end of file + diff --git a/src/Package/MSBuild.VSSetup/files.swr b/src/Package/MSBuild.VSSetup/files.swr index 3330195df6e..e5f3ce39f93 100644 --- a/src/Package/MSBuild.VSSetup/files.swr +++ b/src/Package/MSBuild.VSSetup/files.swr @@ -61,7 +61,7 @@ folder InstallDir:\MSBuild\Current\Bin file source=$(X86BinPath)System.Collections.Immutable.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 file source=$(X86BinPath)Microsoft.Bcl.HashCode.dll file source=$(X86BinPath)Microsoft.NET.StringTools.dll vs.file.ngenArchitecture=all - file source=$(TaskHostBinPath)Microsoft.NET.StringTools.net35.dll vs.file.ngenArchitecture=all + file source=$(TaskHostBinPath)Microsoft.NET.StringTools.net35.dll file source=$(X86BinPath)Microsoft.Common.CurrentVersion.targets file source=$(X86BinPath)Microsoft.Common.CrossTargeting.targets file source=$(X86BinPath)Microsoft.Common.overridetasks @@ -230,7 +230,7 @@ folder InstallDir:\MSBuild\Current\Bin\amd64 file source=$(X86BinPath)System.Collections.Immutable.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)Microsoft.Bcl.HashCode.dll file source=$(X86BinPath)Microsoft.NET.StringTools.dll vs.file.ngenArchitecture=all - file source=$(TaskHostBinPath)Microsoft.NET.StringTools.net35.dll vs.file.ngenArchitecture=all + file source=$(TaskHostBinPath)Microsoft.NET.StringTools.net35.dll file source=$(X86BinPath)Microsoft.Common.CurrentVersion.targets file source=$(X86BinPath)Microsoft.Common.CrossTargeting.targets file source=$(X86BinPath)Microsoft.Common.overridetasks From 88496b9c8a4e9b4a84bee8889e47cf106dd7d96d Mon Sep 17 00:00:00 2001 From: Rainer Sigwald Date: Thu, 20 Mar 2025 06:04:14 -0500 Subject: [PATCH 038/106] Assert instead of ! for nullable (#11545) --- src/Build/Utilities/EngineFileUtilities.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Build/Utilities/EngineFileUtilities.cs b/src/Build/Utilities/EngineFileUtilities.cs index 5e522888bb8..270938a073a 100644 --- a/src/Build/Utilities/EngineFileUtilities.cs +++ b/src/Build/Utilities/EngineFileUtilities.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -588,7 +589,9 @@ private static List PopulateRegexFromEnvironment() private static bool MatchesLazyWildcard(string fileSpec) { - return _regexMatchCache.Value.GetOrAdd(fileSpec, file => s_lazyWildCardExpansionRegexes!.Any(regex => regex.IsMatch(fileSpec))); + Debug.Assert(s_lazyWildCardExpansionRegexes is not null, $"If the user provided lazy wildcard regexes, {nameof(s_lazyWildCardExpansionRegexes)} should be populated"); + + return _regexMatchCache.Value.GetOrAdd(fileSpec, file => s_lazyWildCardExpansionRegexes.Any(regex => regex.IsMatch(fileSpec))); } /// From 59d013bf47fa4a1964a0460a5304dd3f4a43adfa Mon Sep 17 00:00:00 2001 From: Christian Castaneda <32187633+ccastanedaucf@users.noreply.github.com> Date: Mon, 24 Mar 2025 10:36:59 -0700 Subject: [PATCH 039/106] Consolidate common IPC / named pipe code (#11546) * Encapsulate common IPC/pipe logic * Simplify handshake timeout ifdefs * Update node impls to use common i'PC/pipe code * Delete BufferedReadStream * Split DeserializeAndRoutePacket (plumbing) * proj file changes * Port ValueTask and NET flag changes from merge --- .../BackEnd/NodeEndpointInProc_Tests.cs | 2 +- src/Build/BackEnd/Client/MSBuildClient.cs | 60 +-- .../BackEnd/Client/MSBuildClientPacketPump.cs | 131 +---- .../Components/Communications/NodeManager.cs | 12 +- .../Communications/NodeProviderInProc.cs | 3 +- .../NodeProviderOutOfProcBase.cs | 486 ++---------------- .../NodeProviderOutOfProcTaskHost.cs | 14 +- .../Communications/TaskHostNodeManager.cs | 3 +- src/Build/BackEnd/Node/InProcNode.cs | 3 +- src/Build/BackEnd/Node/OutOfProcNode.cs | 7 +- src/Build/BackEnd/Node/OutOfProcServerNode.cs | 7 +- .../Instance/TaskFactories/TaskHostTask.cs | 7 +- src/Build/Microsoft.Build.csproj | 4 +- src/MSBuild/MSBuild.csproj | 4 +- src/MSBuild/OutOfProcTaskHostNode.cs | 7 +- src/MSBuildTaskHost/MSBuildTaskHost.csproj | 10 +- src/Shared/BufferedReadStream.cs | 210 -------- src/Shared/CommunicationsUtilities.cs | 48 +- src/Shared/INodePacketFactory.cs | 5 +- src/Shared/NodeEndpointOutOfProcBase.cs | 366 ++----------- src/Shared/NodePacketFactory.cs | 21 +- src/Shared/NodePipeBase.cs | 272 ++++++++++ src/Shared/NodePipeClient.cs | 90 ++++ src/Shared/NodePipeServer.cs | 220 ++++++++ 24 files changed, 775 insertions(+), 1217 deletions(-) delete mode 100644 src/Shared/BufferedReadStream.cs create mode 100644 src/Shared/NodePipeBase.cs create mode 100644 src/Shared/NodePipeClient.cs create mode 100644 src/Shared/NodePipeServer.cs diff --git a/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs b/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs index 5a1eb10715b..fd8178dcadd 100644 --- a/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs +++ b/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs @@ -101,7 +101,7 @@ public void UnregisterPacketHandler(NodePacketType packetType) throw new NotImplementedException(); } - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { throw new NotImplementedException(); } diff --git a/src/Build/BackEnd/Client/MSBuildClient.cs b/src/Build/BackEnd/Client/MSBuildClient.cs index 0abf86a2aa2..5cceeb22d66 100644 --- a/src/Build/BackEnd/Client/MSBuildClient.cs +++ b/src/Build/BackEnd/Client/MSBuildClient.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Globalization; using System.IO; -using System.IO.Pipes; using System.Threading; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Client; @@ -75,19 +74,9 @@ public sealed class MSBuildClient private readonly string _pipeName; /// - /// The named pipe stream for client-server communication. + /// The named pipe client for client-server communication. /// - private NamedPipeClientStream _nodeStream = null!; - - /// - /// A way to cache a byte array when writing out packets - /// - private readonly MemoryStream _packetMemoryStream; - - /// - /// A binary writer to help write into - /// - private readonly BinaryWriter _binaryWriter; + private NodePipeClient _pipeClient = null!; /// /// Used to estimate the size of the build with an ETW trace. @@ -130,26 +119,14 @@ public MSBuildClient( // Client <-> Server communication stream _handshake = GetHandshake(); _pipeName = OutOfProcServerNode.GetPipeName(_handshake); - _packetMemoryStream = new MemoryStream(); - _binaryWriter = new BinaryWriter(_packetMemoryStream); CreateNodePipeStream(); } private void CreateNodePipeStream() { -#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - _nodeStream = new NamedPipeClientStream( - serverName: ".", - _pipeName, - PipeDirection.InOut, - PipeOptions.Asynchronous -#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY - | PipeOptions.CurrentUserOnly -#endif - ); -#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter - _packetPump = new MSBuildClientPacketPump(_nodeStream); + _pipeClient = new NodePipeClient(_pipeName, _handshake); + _packetPump = new MSBuildClientPacketPump(_pipeClient); } /// @@ -423,7 +400,7 @@ private bool TrySendPacket(Func packetResolver) try { packet = packetResolver(); - WritePacket(_nodeStream, packet); + _pipeClient.WritePacket(packet); CommunicationsUtilities.Trace("Command packet of type '{0}' sent...", packet.Type); } catch (Exception ex) @@ -621,7 +598,7 @@ private bool TryConnectToServer(int timeoutMilliseconds) tryAgain = false; try { - NodeProviderOutOfProcBase.ConnectToPipeStream(_nodeStream, _pipeName, _handshake, Math.Max(1, timeoutMilliseconds - (int)sw.ElapsedMilliseconds)); + _pipeClient.ConnectToServer(Math.Max(1, timeoutMilliseconds - (int)sw.ElapsedMilliseconds)); } catch (Exception ex) { @@ -644,30 +621,5 @@ private bool TryConnectToServer(int timeoutMilliseconds) return true; } - - private void WritePacket(Stream nodeStream, INodePacket packet) - { - MemoryStream memoryStream = _packetMemoryStream; - memoryStream.SetLength(0); - - ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(memoryStream); - - // Write header - memoryStream.WriteByte((byte)packet.Type); - - // Pad for packet length - _binaryWriter.Write(0); - - // Reset the position in the write buffer. - packet.Translate(writeTranslator); - - int packetStreamLength = (int)memoryStream.Position; - - // Now write in the actual packet length - memoryStream.Position = 1; - _binaryWriter.Write(packetStreamLength - 5); - - nodeStream.Write(memoryStream.GetBuffer(), 0, packetStreamLength); - } } } diff --git a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs index a7234030b5c..34ec9d28df1 100644 --- a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs +++ b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs @@ -2,15 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Buffers.Binary; using System.Collections.Concurrent; -using System.IO; using System.Threading; using Microsoft.Build.Internal; using Microsoft.Build.Shared; -#if !FEATURE_APM using System.Threading.Tasks; -#endif namespace Microsoft.Build.BackEnd.Client { @@ -46,25 +42,15 @@ internal sealed class MSBuildClientPacketPump : INodePacketHandler, INodePacketF /// private readonly NodePacketFactory _packetFactory; - /// - /// The memory stream for a read buffer. - /// - private readonly MemoryStream _readBufferMemoryStream; - /// /// The thread which runs the asynchronous packet pump /// private Thread? _packetPumpThread; /// - /// The stream from where to read packets. - /// - private readonly Stream _stream; - - /// - /// The binary translator for reading packets. + /// The pipe client from where to read packets. /// - private readonly ITranslator _binaryReadTranslator; + private readonly NodePipeClient _pipeClient; /// /// True if this side is gracefully disconnecting. @@ -73,11 +59,12 @@ internal sealed class MSBuildClientPacketPump : INodePacketHandler, INodePacketF /// private bool _isServerDisconnecting; - public MSBuildClientPacketPump(Stream stream) + public MSBuildClientPacketPump(NodePipeClient pipeClient) { - ErrorUtilities.VerifyThrowArgumentNull(stream); + ErrorUtilities.VerifyThrowArgumentNull(pipeClient); - _stream = stream; + _pipeClient = pipeClient; + _pipeClient.RegisterPacketFactory(this); _isServerDisconnecting = false; _packetFactory = new NodePacketFactory(); @@ -85,9 +72,6 @@ public MSBuildClientPacketPump(Stream stream) PacketReceivedEvent = new AutoResetEvent(false); PacketPumpCompleted = new ManualResetEvent(false); _packetPumpShutdownEvent = new ManualResetEvent(false); - - _readBufferMemoryStream = new MemoryStream(); - _binaryReadTranslator = BinaryTranslator.GetReadTranslator(_readBufferMemoryStream, InterningBinaryReader.CreateSharedBuffer()); } #region INodePacketFactory Members @@ -113,14 +97,13 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Deserializes and routes a packer to the appropriate handler. + /// Deserializes a packet. /// - /// The node from which the packet was received. /// The packet type. /// The translator to use as a source for packet data. - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { - _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); + return _packetFactory.DeserializePacket(packetType, translator); } /// @@ -182,21 +165,16 @@ public void Stop() /// private void PacketPumpProc() { - RunReadLoop(_stream, _packetPumpShutdownEvent); + RunReadLoop(_pipeClient, _packetPumpShutdownEvent); } - private void RunReadLoop(Stream localStream, ManualResetEvent localPacketPumpShutdownEvent) + private void RunReadLoop(NodePipeClient pipeClient, ManualResetEvent localPacketPumpShutdownEvent) { CommunicationsUtilities.Trace("Entering read loop."); try { - byte[] headerByte = new byte[5]; -#if FEATURE_APM - IAsyncResult result = localStream.BeginRead(headerByte, 0, headerByte.Length, null, null); -#else - Task readTask = CommunicationsUtilities.ReadAsync(localStream, headerByte, headerByte.Length).AsTask(); -#endif + Task readTask = pipeClient.ReadPacketAsync(); bool continueReading = true; do @@ -208,11 +186,7 @@ private void RunReadLoop(Stream localStream, ManualResetEvent localPacketPumpShu WaitHandle[] handles = [ localPacketPumpShutdownEvent, -#if FEATURE_APM - result.AsyncWaitHandle -#else ((IAsyncResult)readTask).AsyncWaitHandle -#endif ]; int waitId = WaitHandle.WaitAny(handles); switch (waitId) @@ -224,80 +198,27 @@ private void RunReadLoop(Stream localStream, ManualResetEvent localPacketPumpShu break; case 1: + INodePacket packet = readTask.GetAwaiter().GetResult(); + + if (packet.Type == NodePacketType.NodeShutdown) { - // Client recieved a packet header. Read the rest of it. - int headerBytesRead = 0; -#if FEATURE_APM - headerBytesRead = localStream.EndRead(result); -#else - headerBytesRead = readTask.Result; -#endif - - if ((headerBytesRead != headerByte.Length) && !localPacketPumpShutdownEvent.WaitOne(0)) + if (!_isServerDisconnecting) { - // Incomplete read. Abort. - if (headerBytesRead == 0) - { - if (_isServerDisconnecting) - { - continueReading = false; - break; - } - - ErrorUtilities.ThrowInternalError("Server disconnected abruptly"); - } - else - { - ErrorUtilities.ThrowInternalError("Incomplete header read. {0} of {1} bytes read", headerBytesRead, headerByte.Length); - } + ErrorUtilities.ThrowInternalError("Server disconnected abruptly."); } - NodePacketType packetType = (NodePacketType)Enum.ToObject(typeof(NodePacketType), headerByte[0]); - - int packetLength = BinaryPrimitives.ReadInt32LittleEndian(new Span(headerByte, 1, 4)); - int packetBytesRead = 0; - - _readBufferMemoryStream.Position = 0; - _readBufferMemoryStream.SetLength(packetLength); - byte[] packetData = _readBufferMemoryStream.GetBuffer(); - - while (packetBytesRead < packetLength) - { - int bytesRead = localStream.Read(packetData, packetBytesRead, packetLength - packetBytesRead); - if (bytesRead == 0) - { - // Incomplete read. Abort. - ErrorUtilities.ThrowInternalError("Incomplete packet read. {0} of {1} bytes read", packetBytesRead, packetLength); - } - - packetBytesRead += bytesRead; - } + continueReading = false; + break; + } - try - { - _packetFactory.DeserializeAndRoutePacket(0, packetType, _binaryReadTranslator); - } - catch - { - // Error while deserializing or handling packet. Logging additional info. - CommunicationsUtilities.Trace("Packet factory failed to receive package. Exception while deserializing packet {0}.", packetType); - throw; - } + _packetFactory.RoutePacket(0, packet); - if (packetType == NodePacketType.ServerNodeBuildResult) - { - continueReading = false; - } - else - { - // Start reading the next package header. -#if FEATURE_APM - result = localStream.BeginRead(headerByte, 0, headerByte.Length, null, null); -#else - readTask = CommunicationsUtilities.ReadAsync(localStream, headerByte, headerByte.Length).AsTask(); -#endif - } + continueReading = packet.Type != NodePacketType.ServerNodeBuildResult; + if (continueReading) + { + readTask = pipeClient.ReadPacketAsync(); } + break; default: diff --git a/src/Build/BackEnd/Components/Communications/NodeManager.cs b/src/Build/BackEnd/Components/Communications/NodeManager.cs index b0031746031..96cb184f1c2 100644 --- a/src/Build/BackEnd/Components/Communications/NodeManager.cs +++ b/src/Build/BackEnd/Components/Communications/NodeManager.cs @@ -240,19 +240,13 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. + /// Takes a serializer and deserializes the packet. /// - /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { - if (packetType == NodePacketType.NodeShutdown) - { - RemoveNodeFromMapping(nodeId); - } - - _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); + return _packetFactory.DeserializePacket(packetType, translator); } /// diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs index 61ff495b01f..a2709281af4 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs @@ -285,10 +285,11 @@ public void UnregisterPacketHandler(NodePacketType packetType) /// /// Deserializes and routes a packet. Not used in the in-proc node. /// - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { // Not used ErrorUtilities.ThrowInternalErrorUnreachable(); + return null; } /// diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index b1e380b5fa2..d346a6f3fe6 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -1,32 +1,22 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable disable + using System; -using System.Buffers.Binary; -using System.Collections.Generic; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Globalization; using System.IO; -using System.IO.Pipes; using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; -#if FEATURE_PIPE_SECURITY -using System.Security.Principal; -#endif - -#if FEATURE_APM -using Microsoft.Build.Eventing; -#endif using Microsoft.Build.Internal; using Microsoft.Build.Shared; -using Task = System.Threading.Tasks.Task; using Microsoft.Build.Framework; using Microsoft.Build.BackEnd.Logging; -#nullable disable - namespace Microsoft.Build.BackEnd { /// @@ -35,11 +25,6 @@ namespace Microsoft.Build.BackEnd /// internal abstract class NodeProviderOutOfProcBase { - /// - /// The maximum number of bytes to write - /// - private const int MaxPacketWriteSize = 1048576; - /// /// The number of times to retry creating an out-of-proc node. /// @@ -55,9 +40,6 @@ internal abstract class NodeProviderOutOfProcBase /// private const int TimeoutForWaitForExit = 30000; -#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY - private static readonly WindowsIdentity s_currentWindowsIdentity = WindowsIdentity.GetCurrent(); -#endif /// /// The build component host. /// @@ -163,21 +145,18 @@ protected void ShutdownAllNodes(bool nodeReuse, NodeContextTerminateDelegate ter int timeout = 30; // Attempt to connect to the process with the handshake without low priority. - Stream nodeStream = TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, false)); + NodePipeClient pipeClient = TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, false)); - if (nodeStream == null) - { - // If we couldn't connect attempt to connect to the process with the handshake including low priority. - nodeStream = TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, true)); - } + // If we couldn't connect attempt to connect to the process with the handshake including low priority. + pipeClient ??= TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, true)); - if (nodeStream != null) + if (pipeClient != null) { // If we're able to connect to such a process, send a packet requesting its termination CommunicationsUtilities.Trace("Shutting down node with pid = {0}", nodeProcess.Id); - NodeContext nodeContext = new NodeContext(0, nodeProcess, nodeStream, factory, terminateNode); + NodeContext nodeContext = new(0, nodeProcess, pipeClient, factory, terminateNode); nodeContext.SendData(new NodeBuildComplete(false /* no node reuse */)); - nodeStream.Dispose(); + pipeClient.Dispose(); } } } @@ -284,8 +263,8 @@ bool TryReuseAnyFromPossibleRunningNodes(int currentProcessId, int nodeId) _processesToIgnore.TryAdd(nodeLookupKey, default); // Attempt to connect to each process in turn. - Stream nodeStream = TryConnectToProcess(nodeToReuse.Id, 0 /* poll, don't wait for connections */, hostHandshake); - if (nodeStream != null) + NodePipeClient pipeClient = TryConnectToProcess(nodeToReuse.Id, 0 /* poll, don't wait for connections */, hostHandshake); + if (pipeClient != null) { // Connection successful, use this node. CommunicationsUtilities.Trace("Successfully connected to existed node {0} which is PID {1}", nodeId, nodeToReuse.Id); @@ -295,7 +274,7 @@ bool TryReuseAnyFromPossibleRunningNodes(int currentProcessId, int nodeId) BuildEventContext = new BuildEventContext(nodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId) }); - CreateNodeContext(nodeId, nodeToReuse, nodeStream); + CreateNodeContext(nodeId, nodeToReuse, pipeClient); return true; } } @@ -344,13 +323,13 @@ bool StartNewNode(int nodeId) // to the debugger process. Instead, use MSBUILDDEBUGONSTART=1 // Now try to connect to it. - Stream nodeStream = TryConnectToProcess(msbuildProcess.Id, TimeoutForNewNodeCreation, hostHandshake); - if (nodeStream != null) + NodePipeClient pipeClient = TryConnectToProcess(msbuildProcess.Id, TimeoutForNewNodeCreation, hostHandshake); + if (pipeClient != null) { // Connection successful, use this node. CommunicationsUtilities.Trace("Successfully connected to created node {0} which is PID {1}", nodeId, msbuildProcess.Id); - CreateNodeContext(nodeId, msbuildProcess, nodeStream); + CreateNodeContext(nodeId, msbuildProcess, pipeClient); return true; } @@ -379,9 +358,9 @@ bool StartNewNode(int nodeId) return false; } - void CreateNodeContext(int nodeId, Process nodeToReuse, Stream nodeStream) + void CreateNodeContext(int nodeId, Process nodeToReuse, NodePipeClient pipeClient) { - NodeContext nodeContext = new(nodeId, nodeToReuse, nodeStream, factory, terminateNode); + NodeContext nodeContext = new(nodeId, nodeToReuse, pipeClient, factory, terminateNode); nodeContexts.Enqueue(nodeContext); createNode(nodeContext); } @@ -423,52 +402,22 @@ private string GetProcessesToIgnoreKey(Handshake hostHandshake, int nodeProcessI #endif } -#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY - // This code needs to be in a separate method so that we don't try (and fail) to load the Windows-only APIs when JIT-ing the code - // on non-Windows operating systems - private static void ValidateRemotePipeSecurityOnWindows(NamedPipeClientStream nodeStream) - { - SecurityIdentifier identifier = s_currentWindowsIdentity.Owner; -#if FEATURE_PIPE_SECURITY - PipeSecurity remoteSecurity = nodeStream.GetAccessControl(); -#else - var remoteSecurity = new PipeSecurity(nodeStream.SafePipeHandle, System.Security.AccessControl.AccessControlSections.Access | - System.Security.AccessControl.AccessControlSections.Owner | System.Security.AccessControl.AccessControlSections.Group); -#endif - IdentityReference remoteOwner = remoteSecurity.GetOwner(typeof(SecurityIdentifier)); - if (remoteOwner != identifier) - { - CommunicationsUtilities.Trace("The remote pipe owner {0} does not match {1}", remoteOwner.Value, identifier.Value); - throw new UnauthorizedAccessException(); - } - } -#endif - /// /// Attempts to connect to the specified process. /// - private Stream TryConnectToProcess(int nodeProcessId, int timeout, Handshake handshake) + private NodePipeClient TryConnectToProcess(int nodeProcessId, int timeout, Handshake handshake) { // Try and connect to the process. string pipeName = NamedPipeUtil.GetPlatformSpecificPipeName(nodeProcessId); -#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - NamedPipeClientStream nodeStream = new NamedPipeClientStream( - serverName: ".", - pipeName, - PipeDirection.InOut, - PipeOptions.Asynchronous -#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY - | PipeOptions.CurrentUserOnly -#endif - ); -#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter - CommunicationsUtilities.Trace("Attempting connect to PID {0} with pipe {1} with timeout {2} ms", nodeProcessId, pipeName, timeout); + NodePipeClient pipeClient = new(pipeName, handshake); + + CommunicationsUtilities.Trace("Attempting connect to PID {0}", nodeProcessId); try { - ConnectToPipeStream(nodeStream, pipeName, handshake, timeout); - return nodeStream; + pipeClient.ConnectToServer(timeout); + return pipeClient; } catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { @@ -480,56 +429,12 @@ private Stream TryConnectToProcess(int nodeProcessId, int timeout, Handshake han CommunicationsUtilities.Trace("Failed to connect to pipe {0}. {1}", pipeName, e.Message.TrimEnd()); // If we don't close any stream, we might hang up the child - nodeStream?.Dispose(); + pipeClient?.Dispose(); } return null; } - /// - /// Connect to named pipe stream and ensure validate handshake and security. - /// - /// - /// Reused by MSBuild server client . - /// - internal static void ConnectToPipeStream(NamedPipeClientStream nodeStream, string pipeName, Handshake handshake, int timeout) - { - nodeStream.Connect(timeout); - -#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY - if (NativeMethodsShared.IsWindows) - { - // Verify that the owner of the pipe is us. This prevents a security hole where a remote node has - // been faked up with ACLs that would let us attach to it. It could then issue fake build requests back to - // us, potentially causing us to execute builds that do harmful or unexpected things. The pipe owner can - // only be set to the user's own SID by a normal, unprivileged process. The conditions where a faked up - // remote node could set the owner to something else would also let it change owners on other objects, so - // this would be a security flaw upstream of us. - ValidateRemotePipeSecurityOnWindows(nodeStream); - } -#endif - - int[] handshakeComponents = handshake.RetrieveHandshakeComponents(); - for (int i = 0; i < handshakeComponents.Length; i++) - { - CommunicationsUtilities.Trace("Writing handshake part {0} ({1}) to pipe {2}", i, handshakeComponents[i], pipeName); - nodeStream.WriteIntForHandshake(handshakeComponents[i]); - } - - // This indicates that we have finished all the parts of our handshake; hopefully the endpoint has as well. - nodeStream.WriteEndOfHandshakeSignal(); - - CommunicationsUtilities.Trace("Reading handshake from pipe {0}", pipeName); - -#if NETCOREAPP2_1_OR_GREATER - nodeStream.ReadEndOfHandshakeSignal(true, timeout); -#else - nodeStream.ReadEndOfHandshakeSignal(true); -#endif - // We got a connection. - CommunicationsUtilities.Trace("Successfully connected to pipe {0}...!", pipeName); - } - /// /// Class which wraps up the communications infrastructure for a given node. /// @@ -542,14 +447,13 @@ private enum ExitPacketState ExitPacketSent } - // The pipe(s) used to communicate with the node. - private Stream _clientToServerStream; - private Stream _serverToClientStream; + // The pipe client used to communicate with the node. + private readonly NodePipeClient _pipeClient; /// /// The factory used to create packets from data read off the pipe. /// - private INodePacketFactory _packetFactory; + private readonly INodePacketFactory _packetFactory; /// /// The node id assigned by the node provider. @@ -563,23 +467,6 @@ private enum ExitPacketState internal Process Process { get { return _process; } } - /// - /// An array used to store the header byte for each packet when read. - /// - private byte[] _headerByte; - - /// - /// A buffer typically big enough to handle a packet body. - /// We use this as a convenient way to manage and cache a byte[] that's resized - /// automatically to fit our payload. - /// - private MemoryStream _readBufferMemoryStream; - - /// - /// A reusable buffer for writing packets. - /// - private MemoryStream _writeBufferMemoryStream; - /// /// A queue used for enqueuing packets to write to the stream asynchronously. /// @@ -602,28 +489,19 @@ private enum ExitPacketState /// private ExitPacketState _exitPacketState; - /// - /// Per node read buffers - /// - private BinaryReaderFactory _binaryReaderFactory; - /// /// Constructor. /// public NodeContext(int nodeId, Process process, - Stream nodePipe, + NodePipeClient pipeClient, INodePacketFactory factory, NodeContextTerminateDelegate terminateDelegate) { _nodeId = nodeId; _process = process; - _clientToServerStream = nodePipe; - _serverToClientStream = nodePipe; + _pipeClient = pipeClient; + _pipeClient.RegisterPacketFactory(factory); _packetFactory = factory; - _headerByte = new byte[5]; // 1 for the packet type, 4 for the body length - _readBufferMemoryStream = new MemoryStream(); - _writeBufferMemoryStream = new MemoryStream(); _terminateDelegate = terminateDelegate; - _binaryReaderFactory = InterningBinaryReader.CreateSharedBuffer(); } /// @@ -636,73 +514,49 @@ public NodeContext(int nodeId, Process process, /// public void BeginAsyncPacketRead() { -#if FEATURE_APM - _clientToServerStream.BeginRead(_headerByte, 0, _headerByte.Length, HeaderReadComplete, this); -#else - ThreadPool.QueueUserWorkItem(delegate - { - var ignored = RunPacketReadLoopAsync(); - }); -#endif + _ = ThreadPool.QueueUserWorkItem(_ => _ = RunPacketReadLoopAsync()); } -#if !FEATURE_APM public async Task RunPacketReadLoopAsync() { - while (true) + INodePacket packet = null; + + while (packet?.Type != NodePacketType.NodeShutdown) { try { - int bytesRead = await CommunicationsUtilities.ReadAsync(_clientToServerStream, _headerByte, _headerByte.Length); - if (!ProcessHeaderBytesRead(bytesRead)) - { - return; - } + packet = await _pipeClient.ReadPacketAsync().ConfigureAwait(false); } catch (IOException e) { - CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in RunPacketReadLoopAsync: {0}", e); - _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); - Close(); - return; + CommunicationsUtilities.Trace(_nodeId, "COMMUNICATIONS ERROR (HRC) Node: {0} Process: {1} Exception: {2}", _nodeId, _process.Id, e.Message); + packet = new NodeShutdown(NodeShutdownReason.ConnectionFailed); } - NodePacketType packetType = (NodePacketType)_headerByte[0]; - int packetLength = BinaryPrimitives.ReadInt32LittleEndian(new Span(_headerByte, 1, 4)); - - _readBufferMemoryStream.SetLength(packetLength); - byte[] packetData = _readBufferMemoryStream.GetBuffer(); - - try + if (packet.Type == NodePacketType.NodeShutdown && (packet as NodeShutdown).Reason == NodeShutdownReason.ConnectionFailed) { - int bytesRead = await CommunicationsUtilities.ReadAsync(_clientToServerStream, packetData, packetLength); - if (!ProcessBodyBytesRead(bytesRead, packetLength, packetType)) + try { - return; + if (_process.HasExited) + { + CommunicationsUtilities.Trace(_nodeId, " Child Process {0} has exited.", _process.Id); + } + else + { + CommunicationsUtilities.Trace(_nodeId, " Child Process {0} is still running.", _process.Id); + } + } + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) + { + CommunicationsUtilities.Trace(_nodeId, "Unable to retrieve remote process information. {0}", e); } - } - catch (IOException e) - { - CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in RunPacketReadLoopAsync (Reading): {0}", e); - _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); - Close(); - return; - } - - // Read and route the packet. - if (!ReadAndRoutePacket(packetType, packetData, packetLength)) - { - return; } - if (packetType == NodePacketType.NodeShutdown) - { - Close(); - return; - } + _packetFactory.RoutePacket(_nodeId, packet); } + + Close(); } -#endif /// /// Sends the specified packet to this node asynchronously. @@ -748,37 +602,11 @@ private void DrainPacketQueue() static async Task SendDataCoreAsync(Task _, object state) { NodeContext context = (NodeContext)state; - while (context._packetWriteQueue.TryDequeue(out var packet)) + while (context._packetWriteQueue.TryDequeue(out INodePacket packet)) { - MemoryStream writeStream = context._writeBufferMemoryStream; - - // clear the buffer but keep the underlying capacity to avoid reallocations - writeStream.SetLength(0); - - ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(writeStream); try { - writeStream.WriteByte((byte)packet.Type); - - // Pad for the packet length - WriteInt32(writeStream, 0); - packet.Translate(writeTranslator); - - int writeStreamLength = (int)writeStream.Position; - - // Now plug in the real packet length - writeStream.Position = 1; - WriteInt32(writeStream, writeStreamLength - 5); - - byte[] writeStreamBuffer = writeStream.GetBuffer(); - - for (int i = 0; i < writeStreamLength; i += MaxPacketWriteSize) - { - int lengthToWrite = Math.Min(writeStreamLength - i, MaxPacketWriteSize); -#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - await context._serverToClientStream.WriteAsync(writeStreamBuffer, i, lengthToWrite, CancellationToken.None); -#pragma warning restore CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - } + await context._pipeClient.WritePacketAsync(packet).ConfigureAwait(false); if (IsExitPacket(packet)) { @@ -804,27 +632,12 @@ private static bool IsExitPacket(INodePacket packet) return packet is NodeBuildComplete buildCompletePacket && !buildCompletePacket.PrepareForReuse; } - /// - /// Avoid having a BinaryWriter just to write a 4-byte int - /// - private static void WriteInt32(MemoryStream stream, int value) - { - stream.WriteByte((byte)value); - stream.WriteByte((byte)(value >> 8)); - stream.WriteByte((byte)(value >> 16)); - stream.WriteByte((byte)(value >> 24)); - } - /// /// Closes the node's context, disconnecting it from the node. /// private void Close() { - _clientToServerStream.Dispose(); - if (!object.ReferenceEquals(_clientToServerStream, _serverToClientStream)) - { - _serverToClientStream.Dispose(); - } + _pipeClient.Dispose(); _terminateDelegate(_nodeId); } @@ -882,191 +695,6 @@ public async Task WaitForExitAsync(ILoggingService loggingService) _process.KillTree(timeoutMilliseconds: 5000); } - -#if FEATURE_APM - /// - /// Completes the asynchronous packet write to the node. - /// - private void PacketWriteComplete(IAsyncResult result) - { - try - { - _serverToClientStream.EndWrite(result); - } - catch (IOException) - { - // Do nothing here because any exception will be caught by the async read handler - } - } -#endif - - private bool ProcessHeaderBytesRead(int bytesRead) - { - if (bytesRead != _headerByte.Length) - { - CommunicationsUtilities.Trace(_nodeId, "COMMUNICATIONS ERROR (HRC) Node: {0} Process: {1} Bytes Read: {2} Expected: {3}", _nodeId, _process.Id, bytesRead, _headerByte.Length); - try - { - if (_process.HasExited) - { - CommunicationsUtilities.Trace(_nodeId, " Child Process {0} has exited.", _process.Id); - } - else - { - CommunicationsUtilities.Trace(_nodeId, " Child Process {0} is still running.", _process.Id); - } - } - catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) - { - CommunicationsUtilities.Trace(_nodeId, "Unable to retrieve remote process information. {0}", e); - } - - _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); - Close(); - return false; - } - - return true; - } - -#if FEATURE_APM - /// - /// Callback invoked by the completion of a read of a header byte on one of the named pipes. - /// - private void HeaderReadComplete(IAsyncResult result) - { - int bytesRead; - try - { - try - { - bytesRead = _clientToServerStream.EndRead(result); - } - - // Workaround for CLR stress bug; it sporadically calls us twice on the same async - // result, and EndRead will throw on the second one. Pretend the second one never happened. - catch (ArgumentException) - { - CommunicationsUtilities.Trace(_nodeId, "Hit CLR bug #825607: called back twice on same async result; ignoring"); - return; - } - - if (!ProcessHeaderBytesRead(bytesRead)) - { - return; - } - } - catch (IOException e) - { - CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in HeaderReadComplete: {0}", e); - _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); - Close(); - return; - } - - int packetLength = BinaryPrimitives.ReadInt32LittleEndian(new Span(_headerByte, 1, 4)); - MSBuildEventSource.Log.PacketReadSize(packetLength); - - // Ensures the buffer is at least this length. - // It avoids reallocations if the buffer is already large enough. - _readBufferMemoryStream.SetLength(packetLength); - byte[] packetData = _readBufferMemoryStream.GetBuffer(); - - _clientToServerStream.BeginRead(packetData, 0, packetLength, BodyReadComplete, new Tuple(packetData, packetLength)); - } -#endif - - private bool ProcessBodyBytesRead(int bytesRead, int packetLength, NodePacketType packetType) - { - if (bytesRead != packetLength) - { - CommunicationsUtilities.Trace(_nodeId, "Bad packet read for packet {0} - Expected {1} bytes, got {2}", packetType, packetLength, bytesRead); - _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); - Close(); - return false; - } - return true; - } - - private bool ReadAndRoutePacket(NodePacketType packetType, byte[] packetData, int packetLength) - { - try - { - // The buffer is publicly visible so that InterningBinaryReader doesn't have to copy to an intermediate buffer. - // Since the buffer is publicly visible dispose right away to discourage outsiders from holding a reference to it. - using (var packetStream = new MemoryStream(packetData, 0, packetLength, /*writeable*/ false, /*bufferIsPubliclyVisible*/ true)) - { - ITranslator readTranslator = BinaryTranslator.GetReadTranslator(packetStream, _binaryReaderFactory); - _packetFactory.DeserializeAndRoutePacket(_nodeId, packetType, readTranslator); - } - } - catch (IOException e) - { - CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in ReadAndRoutPacket: {0}", e); - _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); - Close(); - return false; - } - return true; - } - -#if FEATURE_APM - /// - /// Method called when the body of a packet has been read. - /// - private void BodyReadComplete(IAsyncResult result) - { - NodePacketType packetType = (NodePacketType)_headerByte[0]; - var state = (Tuple)result.AsyncState; - byte[] packetData = state.Item1; - int packetLength = state.Item2; - int bytesRead; - - try - { - try - { - bytesRead = _clientToServerStream.EndRead(result); - } - - // Workaround for CLR stress bug; it sporadically calls us twice on the same async - // result, and EndRead will throw on the second one. Pretend the second one never happened. - catch (ArgumentException) - { - CommunicationsUtilities.Trace(_nodeId, "Hit CLR bug #825607: called back twice on same async result; ignoring"); - return; - } - - if (!ProcessBodyBytesRead(bytesRead, packetLength, packetType)) - { - return; - } - } - catch (IOException e) - { - CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in BodyReadComplete (Reading): {0}", e); - _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); - Close(); - return; - } - - // Read and route the packet. - if (!ReadAndRoutePacket(packetType, packetData, packetLength)) - { - return; - } - - if (packetType != NodePacketType.NodeShutdown) - { - // Read the next packet. - BeginAsyncPacketRead(); - } - else - { - Close(); - } - } -#endif } } } diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs index 1d0f0f525d3..d7885edb750 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs @@ -270,21 +270,13 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. + /// Takes a serializer and deserializes the packet. /// - /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { - if (_nodeIdToPacketFactory.TryGetValue(nodeId, out INodePacketFactory nodePacketFactory)) - { - nodePacketFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); - } - else - { - _localPacketFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); - } + return _localPacketFactory.DeserializePacket(packetType, translator); } /// diff --git a/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs b/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs index e7e66d6b886..cafc95a4f22 100644 --- a/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs +++ b/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs @@ -141,10 +141,9 @@ public void UnregisterPacketHandler(NodePacketType packetType) /// /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. /// - /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { throw new NotSupportedException("not used"); } diff --git a/src/Build/BackEnd/Node/InProcNode.cs b/src/Build/BackEnd/Node/InProcNode.cs index 7b4049f8905..6ac34aabdd4 100644 --- a/src/Build/BackEnd/Node/InProcNode.cs +++ b/src/Build/BackEnd/Node/InProcNode.cs @@ -216,10 +216,11 @@ public void UnregisterPacketHandler(NodePacketType packetType) /// /// Not necessary for in-proc node - we don't serialize. /// - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { // The in-proc endpoint shouldn't be serializing, just routing. ErrorUtilities.ThrowInternalError("Unexpected call to DeserializeAndRoutePacket on the in-proc node."); + return null; } /// diff --git a/src/Build/BackEnd/Node/OutOfProcNode.cs b/src/Build/BackEnd/Node/OutOfProcNode.cs index f092add506b..bcd97d15400 100644 --- a/src/Build/BackEnd/Node/OutOfProcNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcNode.cs @@ -331,14 +331,13 @@ void INodePacketFactory.UnregisterPacketHandler(NodePacketType packetType) } /// - /// Deserializes and routes a packer to the appropriate handler. + /// Deserializes a packet. /// - /// The node from which the packet was received. /// The packet type. /// The translator to use as a source for packet data. - void INodePacketFactory.DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + INodePacket INodePacketFactory.DeserializePacket(NodePacketType packetType, ITranslator translator) { - _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); + return _packetFactory.DeserializePacket(packetType, translator); } /// diff --git a/src/Build/BackEnd/Node/OutOfProcServerNode.cs b/src/Build/BackEnd/Node/OutOfProcServerNode.cs index bda79d588cd..6af7462d159 100644 --- a/src/Build/BackEnd/Node/OutOfProcServerNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcServerNode.cs @@ -202,14 +202,13 @@ void INodePacketFactory.UnregisterPacketHandler(NodePacketType packetType) } /// - /// Deserializes and routes a packer to the appropriate handler. + /// Deserializes a packet. /// - /// The node from which the packet was received. /// The packet type. /// The translator to use as a source for packet data. - void INodePacketFactory.DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + INodePacket INodePacketFactory.DeserializePacket(NodePacketType packetType, ITranslator translator) { - _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); + return _packetFactory.DeserializePacket(packetType, translator); } /// diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index 784b67b200c..f0afd81d0a8 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -362,14 +362,13 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. + /// Takes a serializer and deserializes the packet. /// - /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { - _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); + return _packetFactory.DeserializePacket(packetType, translator); } /// diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index b28ac113cd2..6ccd378e62f 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -95,7 +95,6 @@ Collections\ReadOnlyEmptyCollection.cs - @@ -105,6 +104,9 @@ + + + diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj index 2edca8c339b..a7dc889b270 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -88,7 +88,6 @@ - @@ -103,6 +102,9 @@ + + + diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs index f862ae2adca..8aafef70b62 100644 --- a/src/MSBuild/OutOfProcTaskHostNode.cs +++ b/src/MSBuild/OutOfProcTaskHostNode.cs @@ -582,14 +582,13 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. + /// Takes a serializer and deserializes the packet. /// - /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { - _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); + return _packetFactory.DeserializePacket(packetType, translator); } /// diff --git a/src/MSBuildTaskHost/MSBuildTaskHost.csproj b/src/MSBuildTaskHost/MSBuildTaskHost.csproj index a189f58567a..d0ad4122b8d 100644 --- a/src/MSBuildTaskHost/MSBuildTaskHost.csproj +++ b/src/MSBuildTaskHost/MSBuildTaskHost.csproj @@ -64,7 +64,6 @@ - CopyOnWriteDictionary.cs @@ -140,6 +139,15 @@ NodeBuildComplete.cs + + NodeComponentBase.cs + + + NodeComponentBase.cs + + + NodeComponentBase.cs + NodeEndpointOutOfProcBase.cs diff --git a/src/Shared/BufferedReadStream.cs b/src/Shared/BufferedReadStream.cs deleted file mode 100644 index 55bba5986f8..00000000000 --- a/src/Shared/BufferedReadStream.cs +++ /dev/null @@ -1,210 +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; -using System.IO; -using System.IO.Pipes; -using System.Threading; - -#if NET451_OR_GREATER || NETCOREAPP -using System.Threading.Tasks; -#endif - -#nullable disable - -namespace Microsoft.Build.BackEnd -{ - internal class BufferedReadStream : Stream - { - private const int BUFFER_SIZE = 1024; - private NamedPipeServerStream _innerStream; - private byte[] _buffer; - - // The number of bytes in the buffer that have been read from the underlying stream but not read by consumers of this stream - private int _currentlyBufferedByteCount; - private int _currentIndexInBuffer; - - public BufferedReadStream(NamedPipeServerStream innerStream) - { - _innerStream = innerStream; - _buffer = new byte[BUFFER_SIZE]; - - _currentlyBufferedByteCount = 0; - } - - public override bool CanRead { get { return _innerStream.CanRead; } } - - public override bool CanSeek { get { return false; } } - - public override bool CanWrite { get { return _innerStream.CanWrite; } } - - public override long Length { get { return _innerStream.Length; } } - - public override long Position - { - get { throw new NotSupportedException(); } - set { throw new NotSupportedException(); } - } - - public override void Flush() - { - _innerStream.Flush(); - } - - public override int ReadByte() - { - if (_currentlyBufferedByteCount > 0) - { - int ret = _buffer[_currentIndexInBuffer]; - _currentIndexInBuffer++; - _currentlyBufferedByteCount--; - return ret; - } - else - { - // Let the base class handle it, which will end up calling the Read() method - return base.ReadByte(); - } - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (count > BUFFER_SIZE) - { - // Trying to read more data than the buffer can hold - int alreadyCopied = 0; - if (_currentlyBufferedByteCount > 0) - { - Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, _currentlyBufferedByteCount); - alreadyCopied = _currentlyBufferedByteCount; - _currentIndexInBuffer = 0; - _currentlyBufferedByteCount = 0; - } - int innerReadCount = _innerStream.Read(buffer, offset + alreadyCopied, count - alreadyCopied); - return innerReadCount + alreadyCopied; - } - else if (count <= _currentlyBufferedByteCount) - { - // Enough data buffered to satisfy read request - Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, count); - _currentIndexInBuffer += count; - _currentlyBufferedByteCount -= count; - return count; - } - else - { - // Need to read more data - int alreadyCopied = 0; - if (_currentlyBufferedByteCount > 0) - { - Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, _currentlyBufferedByteCount); - alreadyCopied = _currentlyBufferedByteCount; - _currentIndexInBuffer = 0; - _currentlyBufferedByteCount = 0; - } - - int innerReadCount = _innerStream.Read(_buffer, 0, BUFFER_SIZE); - _currentIndexInBuffer = 0; - _currentlyBufferedByteCount = innerReadCount; - - int remainingCopyCount; - - if (alreadyCopied + innerReadCount >= count) - { - remainingCopyCount = count - alreadyCopied; - } - else - { - remainingCopyCount = innerReadCount; - } - - Array.Copy(_buffer, 0, buffer, offset + alreadyCopied, remainingCopyCount); - _currentIndexInBuffer += remainingCopyCount; - _currentlyBufferedByteCount -= remainingCopyCount; - - return alreadyCopied + remainingCopyCount; - } - } - -#if NET451_OR_GREATER || NETCOREAPP - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (count > BUFFER_SIZE) - { - // Trying to read more data than the buffer can hold - int alreadyCopied = CopyToBuffer(buffer, offset); - -#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - int innerReadCount = await _innerStream.ReadAsync(buffer, offset + alreadyCopied, count - alreadyCopied, cancellationToken); -#pragma warning restore CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - return innerReadCount + alreadyCopied; - } - else if (count <= _currentlyBufferedByteCount) - { - // Enough data buffered to satisfy read request - Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, count); - _currentIndexInBuffer += count; - _currentlyBufferedByteCount -= count; - return count; - } - else - { - // Need to read more data - int alreadyCopied = CopyToBuffer(buffer, offset); - -#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - int innerReadCount = await _innerStream.ReadAsync(_buffer, 0, BUFFER_SIZE, cancellationToken); -#pragma warning restore CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - _currentIndexInBuffer = 0; - _currentlyBufferedByteCount = innerReadCount; - - int remainingCopyCount = alreadyCopied + innerReadCount >= count ? count - alreadyCopied : innerReadCount; - Array.Copy(_buffer, 0, buffer, offset + alreadyCopied, remainingCopyCount); - _currentIndexInBuffer += remainingCopyCount; - _currentlyBufferedByteCount -= remainingCopyCount; - - return alreadyCopied + remainingCopyCount; - } - - int CopyToBuffer(byte[] buffer, int offset) - { - int alreadyCopied = 0; - if (_currentlyBufferedByteCount > 0) - { - Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, _currentlyBufferedByteCount); - alreadyCopied = _currentlyBufferedByteCount; - _currentIndexInBuffer = 0; - _currentlyBufferedByteCount = 0; - } - - return alreadyCopied; - } - } -#endif - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - _innerStream.Write(buffer, offset, count); - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - _innerStream.Dispose(); - } - - base.Dispose(disposing); - } - } -} diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index cb1177fce58..4b48317081c 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -22,9 +22,6 @@ #if !CLR2COMPATIBILITY using Microsoft.Build.Shared.Debugging; #endif -#if !FEATURE_APM -using System.Threading.Tasks; -#endif #nullable disable @@ -472,25 +469,9 @@ internal static void WriteIntForHandshake(this PipeStream stream, int value) stream.Write(bytes, 0, bytes.Length); } -#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - internal static void ReadEndOfHandshakeSignal( - this PipeStream stream, - bool isProvider -#if NETCOREAPP2_1_OR_GREATER - , int timeout -#endif - ) -#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter + internal static void ReadEndOfHandshakeSignal(this PipeStream stream, bool isProvider, int timeout) { - // Accept only the first byte of the EndOfHandshakeSignal -#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - int valueRead = stream.ReadIntForHandshake( - byteToAccept: null -#if NETCOREAPP2_1_OR_GREATER - , timeout -#endif - ); -#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter + int valueRead = stream.ReadIntForHandshake(byteToAccept: null, timeout); if (valueRead != EndOfHandshakeSignal) { @@ -506,17 +487,11 @@ bool isProvider } } -#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter /// /// Extension method to read a series of bytes from a stream. /// If specified, leading byte matches one in the supplied array if any, returns rejection byte and throws IOException. /// - internal static int ReadIntForHandshake(this PipeStream stream, byte? byteToAccept -#if NETCOREAPP2_1_OR_GREATER - , int timeout -#endif - ) -#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter + internal static int ReadIntForHandshake(this PipeStream stream, byte? byteToAccept, int timeout) { byte[] bytes = new byte[4]; @@ -586,23 +561,6 @@ internal static int ReadIntForHandshake(this PipeStream stream, byte? byteToAcce } #nullable disable -#if !FEATURE_APM - internal static async ValueTask ReadAsync(Stream stream, byte[] buffer, int bytesToRead) - { - int totalBytesRead = 0; - while (totalBytesRead < bytesToRead) - { - int bytesRead = await stream.ReadAsync(buffer.AsMemory(totalBytesRead, bytesToRead - totalBytesRead), CancellationToken.None); - if (bytesRead == 0) - { - return totalBytesRead; - } - totalBytesRead += bytesRead; - } - return totalBytesRead; - } -#endif - /// /// Given the appropriate information, return the equivalent HandshakeOptions. /// diff --git a/src/Shared/INodePacketFactory.cs b/src/Shared/INodePacketFactory.cs index c972e0408b5..b0fd06bbbca 100644 --- a/src/Shared/INodePacketFactory.cs +++ b/src/Shared/INodePacketFactory.cs @@ -35,12 +35,11 @@ internal interface INodePacketFactory void UnregisterPacketHandler(NodePacketType packetType); /// - /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. + /// Takes a serializer and deserializes the packet. /// - /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator); + INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator); /// /// Routes the specified packet diff --git a/src/Shared/NodeEndpointOutOfProcBase.cs b/src/Shared/NodeEndpointOutOfProcBase.cs index 846f7716ec4..5a5d1db3eb5 100644 --- a/src/Shared/NodeEndpointOutOfProcBase.cs +++ b/src/Shared/NodeEndpointOutOfProcBase.cs @@ -3,25 +3,16 @@ using System; using System.Diagnostics.CodeAnalysis; -#if CLR2COMPATIBILITY +#if TASKHOST using Microsoft.Build.Shared.Concurrent; #else using System.Collections.Concurrent; +using System.Threading.Tasks; #endif using System.IO; -using System.IO.Pipes; using System.Threading; using Microsoft.Build.Internal; using Microsoft.Build.Shared; -#if FEATURE_SECURITY_PERMISSIONS || FEATURE_PIPE_SECURITY -using System.Security.AccessControl; -#endif -#if FEATURE_PIPE_SECURITY && FEATURE_NAMED_PIPE_SECURITY_CONSTRUCTOR -using System.Security.Principal; -#endif -#if NET451_OR_GREATER || NETCOREAPP -using System.Threading.Tasks; -#endif #nullable disable @@ -35,18 +26,6 @@ internal abstract class NodeEndpointOutOfProcBase : INodeEndpoint { #region Private Data -#if NETCOREAPP2_1_OR_GREATER - /// - /// The amount of time to wait for the client to connect to the host. - /// - private const int ClientConnectTimeout = 60000; -#endif // NETCOREAPP2_1 - - /// - /// The size of the buffers to use for named pipes - /// - private const int PipeBufferSize = 131072; - /// /// The current communication status of the node. /// @@ -55,7 +34,7 @@ internal abstract class NodeEndpointOutOfProcBase : INodeEndpoint /// /// The pipe client used by the nodes. /// - private NamedPipeServerStream _pipeServer; + private NodePipeServer _pipeServer; // The following private data fields are used only when the endpoint is in ASYNCHRONOUS mode. @@ -100,21 +79,6 @@ internal abstract class NodeEndpointOutOfProcBase : INodeEndpoint /// private ConcurrentQueue _packetQueue; - /// - /// Per-node shared read buffer. - /// - private BinaryReaderFactory _sharedReadBuffer; - - /// - /// A way to cache a byte array when writing out packets - /// - private MemoryStream _packetStream; - - /// - /// A binary writer to help write into - /// - private BinaryWriter _binaryWriter; - #endregion #region INodeEndpoint Events @@ -153,6 +117,7 @@ public void Listen(INodePacketFactory factory) ErrorUtilities.VerifyThrow(_status == LinkStatus.Inactive, "Link not inactive. Status is {0}", _status); ErrorUtilities.VerifyThrowArgumentNull(factory, nameof(factory)); _packetFactory = factory; + _pipeServer.RegisterPacketFactory(factory); InitializeAsyncPacketThread(); } @@ -206,54 +171,9 @@ internal void InternalConstruct(string pipeName = null) { _status = LinkStatus.Inactive; _asyncDataMonitor = new object(); - _sharedReadBuffer = InterningBinaryReader.CreateSharedBuffer(); - - _packetStream = new MemoryStream(); - _binaryWriter = new BinaryWriter(_packetStream); pipeName ??= NamedPipeUtil.GetPlatformSpecificPipeName(); - -#if FEATURE_PIPE_SECURITY && FEATURE_NAMED_PIPE_SECURITY_CONSTRUCTOR - SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner; - PipeSecurity security = new PipeSecurity(); - - // Restrict access to just this account. We set the owner specifically here, and on the - // pipe client side they will check the owner against this one - they must have identical - // SIDs or the client will reject this server. This is used to avoid attacks where a - // hacked server creates a less restricted pipe in an attempt to lure us into using it and - // then sending build requests to the real pipe client (which is the MSBuild Build Manager.) - PipeAccessRule rule = new PipeAccessRule(identifier, PipeAccessRights.ReadWrite, AccessControlType.Allow); - security.AddAccessRule(rule); - security.SetOwner(identifier); - - _pipeServer = new NamedPipeServerStream( - pipeName, - PipeDirection.InOut, - 1, // Only allow one connection at a time. - PipeTransmissionMode.Byte, - PipeOptions.Asynchronous | PipeOptions.WriteThrough -#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY - | PipeOptions.CurrentUserOnly -#endif - , - PipeBufferSize, // Default input buffer - PipeBufferSize, // Default output buffer - security, - HandleInheritability.None); -#else - _pipeServer = new NamedPipeServerStream( - pipeName, - PipeDirection.InOut, - 1, // Only allow one connection at a time. - PipeTransmissionMode.Byte, - PipeOptions.Asynchronous | PipeOptions.WriteThrough -#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY - | PipeOptions.CurrentUserOnly -#endif - , - PipeBufferSize, // Default input buffer - PipeBufferSize); // Default output buffer -#endif + _pipeServer = new NodePipeServer(pipeName, GetHandshake()); } #endregion @@ -295,7 +215,7 @@ private void InternalDisconnect() ErrorUtilities.VerifyThrow(_packetPump.ManagedThreadId != Thread.CurrentThread.ManagedThreadId, "Can't join on the same thread."); _terminatePacketPump.Set(); _packetPump.Join(); -#if CLR2COMPATIBILITY +#if TASKHOST _terminatePacketPump.Close(); #else _terminatePacketPump.Dispose(); @@ -345,172 +265,25 @@ private void InitializeAsyncPacketThread() /// private void PacketPumpProc() { - NamedPipeServerStream localPipeServer = _pipeServer; + NodePipeServer localPipeServer = _pipeServer; AutoResetEvent localPacketAvailable = _packetAvailable; AutoResetEvent localTerminatePacketPump = _terminatePacketPump; ConcurrentQueue localPacketQueue = _packetQueue; - DateTime originalWaitStartTime = DateTime.UtcNow; - bool gotValidConnection = false; - while (!gotValidConnection) + ChangeLinkStatus(localPipeServer.WaitForConnection()); + if (_status != LinkStatus.Active) { - gotValidConnection = true; - DateTime restartWaitTime = DateTime.UtcNow; - - // We only wait to wait the difference between now and the last original start time, in case we have multiple hosts attempting - // to attach. This prevents each attempt from resetting the timer. - TimeSpan usedWaitTime = restartWaitTime - originalWaitStartTime; - int waitTimeRemaining = Math.Max(0, CommunicationsUtilities.NodeConnectionTimeout - (int)usedWaitTime.TotalMilliseconds); - - try - { - // Wait for a connection -#if FEATURE_APM - IAsyncResult resultForConnection = localPipeServer.BeginWaitForConnection(null, null); - CommunicationsUtilities.Trace("Waiting for connection {0} ms...", waitTimeRemaining); - bool connected = resultForConnection.AsyncWaitHandle.WaitOne(waitTimeRemaining, false); -#else - Task connectionTask = localPipeServer.WaitForConnectionAsync(); - CommunicationsUtilities.Trace("Waiting for connection {0} ms...", waitTimeRemaining); - bool connected = connectionTask.Wait(waitTimeRemaining); -#endif - if (!connected) - { - CommunicationsUtilities.Trace("Connection timed out waiting a host to contact us. Exiting comm thread."); - ChangeLinkStatus(LinkStatus.ConnectionFailed); - return; - } - - CommunicationsUtilities.Trace("Parent started connecting. Reading handshake from parent"); -#if FEATURE_APM - localPipeServer.EndWaitForConnection(resultForConnection); -#endif - - // The handshake protocol is a series of int exchanges. The host sends us a each component, and we - // verify it. Afterwards, the host sends an "End of Handshake" signal, to which we respond in kind. - // Once the handshake is complete, both sides can be assured the other is ready to accept data. - Handshake handshake = GetHandshake(); - try - { - int[] handshakeComponents = handshake.RetrieveHandshakeComponents(); - for (int i = 0; i < handshakeComponents.Length; i++) - { -#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - int handshakePart = _pipeServer.ReadIntForHandshake( - byteToAccept: i == 0 ? (byte?)CommunicationsUtilities.handshakeVersion : null /* this will disconnect a < 16.8 host; it expects leading 00 or F5 or 06. 0x00 is a wildcard */ -#if NETCOREAPP2_1_OR_GREATER - , ClientConnectTimeout /* wait a long time for the handshake from this side */ -#endif - ); -#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter - - if (handshakePart != handshakeComponents[i]) - { - CommunicationsUtilities.Trace("Handshake failed. Received {0} from host not {1}. Probably the host is a different MSBuild build.", handshakePart, handshakeComponents[i]); - _pipeServer.WriteIntForHandshake(i + 1); - gotValidConnection = false; - break; - } - } - - if (gotValidConnection) - { - // To ensure that our handshake and theirs have the same number of bytes, receive and send a magic number indicating EOS. -#if NETCOREAPP2_1_OR_GREATER - _pipeServer.ReadEndOfHandshakeSignal(false, ClientConnectTimeout); /* wait a long time for the handshake from this side */ -#else - _pipeServer.ReadEndOfHandshakeSignal(false); -#endif - CommunicationsUtilities.Trace("Successfully connected to parent."); - _pipeServer.WriteEndOfHandshakeSignal(); - -#if FEATURE_SECURITY_PERMISSIONS - // We will only talk to a host that was started by the same user as us. Even though the pipe access is set to only allow this user, we want to ensure they - // haven't attempted to change those permissions out from under us. This ensures that the only way they can truly gain access is to be impersonating the - // user we were started by. - WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent(); - WindowsIdentity clientIdentity = null; - localPipeServer.RunAsClient(delegate () { clientIdentity = WindowsIdentity.GetCurrent(true); }); - - if (clientIdentity == null || !String.Equals(clientIdentity.Name, currentIdentity.Name, StringComparison.OrdinalIgnoreCase)) - { - CommunicationsUtilities.Trace("Handshake failed. Host user is {0} but we were created by {1}.", (clientIdentity == null) ? "" : clientIdentity.Name, currentIdentity.Name); - gotValidConnection = false; - continue; - } -#endif - } - } - catch (IOException e) - { - // We will get here when: - // 1. The host (OOP main node) connects to us, it immediately checks for user privileges - // and if they don't match it disconnects immediately leaving us still trying to read the blank handshake - // 2. The host is too old sending us bits we automatically reject in the handshake - // 3. We expected to read the EndOfHandshake signal, but we received something else - CommunicationsUtilities.Trace("Client connection failed but we will wait for another connection. Exception: {0}", e.Message); - - gotValidConnection = false; - } - catch (InvalidOperationException) - { - gotValidConnection = false; - } - - if (!gotValidConnection) - { - if (localPipeServer.IsConnected) - { - localPipeServer.Disconnect(); - } - continue; - } - - ChangeLinkStatus(LinkStatus.Active); - } - catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) - { - CommunicationsUtilities.Trace("Client connection failed. Exiting comm thread. {0}", e); - if (localPipeServer.IsConnected) - { - localPipeServer.Disconnect(); - } - - ExceptionHandling.DumpExceptionToFile(e); - ChangeLinkStatus(LinkStatus.Failed); - return; - } + return; } - RunReadLoop( - new BufferedReadStream(_pipeServer), - _pipeServer, - localPacketQueue, localPacketAvailable, localTerminatePacketPump); + RunReadLoop(localPipeServer, localPacketQueue, localPacketAvailable, localTerminatePacketPump); CommunicationsUtilities.Trace("Ending read loop"); - - try - { - if (localPipeServer.IsConnected) - { -#if NET // OperatingSystem.IsWindows() is new in .NET 5.0 - if (OperatingSystem.IsWindows()) -#endif - { - localPipeServer.WaitForPipeDrain(); - } - - localPipeServer.Disconnect(); - } - } - catch (Exception) - { - // We don't really care if Disconnect somehow fails, but it gives us a chance to do the right thing. - } + localPipeServer.Disconnect(); } - private void RunReadLoop(BufferedReadStream localReadPipe, NamedPipeServerStream localWritePipe, + private void RunReadLoop(NodePipeServer localPipeServer, ConcurrentQueue localPacketQueue, AutoResetEvent localPacketAvailable, AutoResetEvent localTerminatePacketPump) { // Ordering of the wait handles is important. The first signalled wait handle in the array @@ -518,13 +291,11 @@ private void RunReadLoop(BufferedReadStream localReadPipe, NamedPipeServerStream // terminate event triggered so that we cannot get into a situation where packets are being // spammed to the endpoint and it never gets an opportunity to shutdown. CommunicationsUtilities.Trace("Entering read loop."); - byte[] headerByte = new byte[5]; -#if NET451_OR_GREATER - Task readTask = localReadPipe.ReadAsync(headerByte, 0, headerByte.Length, CancellationToken.None); -#elif NETCOREAPP - Task readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length).AsTask(); +#if TASKHOST + Func readPacketFunc = localPipeServer.ReadPacket; + IAsyncResult result = readPacketFunc.BeginInvoke(null, null); #else - IAsyncResult result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); + Task readTask = localPipeServer.ReadPacketAsync(); #endif // Ordering is important. We want packetAvailable to supercede terminate otherwise we will not properly wait for all @@ -548,36 +319,25 @@ private void RunReadLoop(BufferedReadStream localReadPipe, NamedPipeServerStream { case 0: { - int bytesRead = 0; + INodePacket packet = null; + try { -#if NET451_OR_GREATER || NETCOREAPP - bytesRead = readTask.Result; +#if TASKHOST + packet = readPacketFunc.EndInvoke(result); #else - bytesRead = localReadPipe.EndRead(result); + packet = readTask.GetAwaiter().GetResult(); #endif - } - catch (Exception e) - { - // Lost communications. Abort (but allow node reuse) - CommunicationsUtilities.Trace("Exception reading from server. {0}", e); - ExceptionHandling.DumpExceptionToFile(e); - ChangeLinkStatus(LinkStatus.Inactive); - exitLoop = true; - break; - } - - if (bytesRead != headerByte.Length) - { - // Incomplete read. Abort. - if (bytesRead == 0) + if (packet.Type == NodePacketType.NodeShutdown) { if (_isClientDisconnecting) { - CommunicationsUtilities.Trace("Parent disconnected gracefully."); + // Lost communications. Abort (but allow node reuse). // Do not change link status to failed as this could make node think connection has failed // and recycle node, while this is perfectly expected and handled race condition // (both client and node is about to close pipe and client can be faster). + CommunicationsUtilities.Trace("Parent disconnected gracefully."); + ChangeLinkStatus(LinkStatus.Inactive); } else { @@ -587,43 +347,35 @@ private void RunReadLoop(BufferedReadStream localReadPipe, NamedPipeServerStream } else { - CommunicationsUtilities.Trace("Incomplete header read from server. {0} of {1} bytes read", bytesRead, headerByte.Length); - ChangeLinkStatus(LinkStatus.Failed); + _packetFactory.RoutePacket(0, packet); } - - exitLoop = true; - break; - } - - NodePacketType packetType = (NodePacketType)headerByte[0]; - - try - { - _packetFactory.DeserializeAndRoutePacket(0, packetType, BinaryTranslator.GetReadTranslator(localReadPipe, _sharedReadBuffer)); } catch (Exception e) { - // Error while deserializing or handling packet. Abort. - CommunicationsUtilities.Trace("Exception while deserializing packet {0}: {1}", packetType, e); + if (packet == null) + { + CommunicationsUtilities.Trace("Exception while reading packet from server: {0}", e); + } + else + { + CommunicationsUtilities.Trace("Exception while deserializing or handling packet {0}: {1}", packet.Type, e); + } + ExceptionHandling.DumpExceptionToFile(e); ChangeLinkStatus(LinkStatus.Failed); - exitLoop = true; - break; } -#if NET451_OR_GREATER - readTask = localReadPipe.ReadAsync(headerByte, 0, headerByte.Length, CancellationToken.None); -#elif NETCOREAPP - readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length).AsTask(); -#else - result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); -#endif - -#if NET451_OR_GREATER || NETCOREAPP - handles[0] = ((IAsyncResult)readTask).AsyncWaitHandle; + exitLoop = _status != LinkStatus.Active; + if (!exitLoop) + { +#if TASKHOST + result = readPacketFunc.BeginInvoke(null, null); + handles[0] = result.AsyncWaitHandle; #else - handles[0] = result.AsyncWaitHandle; + readTask = localPipeServer.ReadPacketAsync(); + handles[0] = ((IAsyncResult)readTask).AsyncWaitHandle; #endif + } } break; @@ -633,29 +385,9 @@ private void RunReadLoop(BufferedReadStream localReadPipe, NamedPipeServerStream try { // Write out all the queued packets. - INodePacket packet; - while (localPacketQueue.TryDequeue(out packet)) + while (localPacketQueue.TryDequeue(out INodePacket packet)) { - var packetStream = _packetStream; - packetStream.SetLength(0); - - ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(packetStream); - - packetStream.WriteByte((byte)packet.Type); - - // Pad for packet length - _binaryWriter.Write(0); - - // Reset the position in the write buffer. - packet.Translate(writeTranslator); - - int packetStreamLength = (int)packetStream.Position; - - // Now write in the actual packet length - packetStream.Position = 1; - _binaryWriter.Write(packetStreamLength - 5); - - localWritePipe.Write(packetStream.GetBuffer(), 0, packetStreamLength); + localPipeServer.WritePacket(packet); } } catch (Exception e) @@ -685,8 +417,8 @@ private void RunReadLoop(BufferedReadStream localReadPipe, NamedPipeServerStream while (!exitLoop); } -#endregion + #endregion -#endregion + #endregion } } diff --git a/src/Shared/NodePacketFactory.cs b/src/Shared/NodePacketFactory.cs index 214ddfa20f9..51cbee08655 100644 --- a/src/Shared/NodePacketFactory.cs +++ b/src/Shared/NodePacketFactory.cs @@ -45,9 +45,9 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Creates and routes a packet with data from a binary stream. + /// Creates a packet with data from a binary stream. /// - public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) { // PERF: Not using VerifyThrow to avoid boxing of packetType in the non-error case if (!_packetFactories.TryGetValue(packetType, out PacketFactoryRecord record)) @@ -55,7 +55,7 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr ErrorUtilities.ThrowInternalError("No packet handler for type {0}", packetType); } - record.DeserializeAndRoutePacket(nodeId, translator); + return record.DeserializePacket(translator); } /// @@ -63,7 +63,12 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr /// public void RoutePacket(int nodeId, INodePacket packet) { - PacketFactoryRecord record = _packetFactories[packet.Type]; + // PERF: Not using VerifyThrow to avoid boxing of packetType in the non-error case + if (!_packetFactories.TryGetValue(packet.Type, out PacketFactoryRecord record)) + { + ErrorUtilities.ThrowInternalError("No packet handler for type {0}", packet.Type); + } + record.RoutePacket(nodeId, packet); } @@ -94,13 +99,9 @@ public PacketFactoryRecord(INodePacketHandler handler, NodePacketFactoryMethod f } /// - /// Creates a packet from a binary stream and sends it to the registered handler. + /// Creates a packet from a binary stream. /// - public void DeserializeAndRoutePacket(int nodeId, ITranslator translator) - { - INodePacket packet = _factoryMethod(translator); - RoutePacket(nodeId, packet); - } + public INodePacket DeserializePacket(ITranslator translator) => _factoryMethod(translator); /// /// Routes the packet to the correct destination. diff --git a/src/Shared/NodePipeBase.cs b/src/Shared/NodePipeBase.cs new file mode 100644 index 00000000000..a9c9692a880 --- /dev/null +++ b/src/Shared/NodePipeBase.cs @@ -0,0 +1,272 @@ +// 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.IO; +using System.IO.Pipes; +using System.Threading; +using Microsoft.Build.BackEnd; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; + +#if !TASKHOST +using System.Buffers.Binary; +using System.Threading.Tasks; +using Microsoft.Build.Eventing; +#endif + +namespace Microsoft.Build.Internal +{ + internal abstract class NodePipeBase : IDisposable + { + /// + /// A packet header consists of 1 byte (enum) for the packet type + 4 bytes (int32) for the packet length. + /// + private const int HeaderLength = 5; + + /// + /// The size of the intermediate in-memory buffers. + /// + private const int InitialBufferSize = 131_072; + + /// + /// The maximum number of bytes to write in a single operation. + /// + private const int MaxPacketWriteSize = 104_8576; + + /// + /// A reusable buffer for reading the packet header. + /// + private readonly byte[] _headerData = new byte[HeaderLength]; + + /// + /// A buffer typically big enough to handle a packet body. + /// We use this as a convenient way to manage and cache a byte[] that's resized + /// automatically to fit our payload. + /// + private readonly MemoryStream _readBuffer = new(InitialBufferSize); + + /// + /// A buffer typically big enough to handle a packet body. + /// We use this as a convenient way to manage and cache a byte[] that's resized + /// automatically to fit our payload. + /// + private readonly MemoryStream _writeBuffer = new(InitialBufferSize); + + private readonly ITranslator _readTranslator; + + private readonly ITranslator _writeTranslator; + + /// + /// The packet factory to be used for deserialization, as packet types may have custom factory logic. + /// + private INodePacketFactory? _packetFactory; + + protected NodePipeBase(string pipeName, Handshake handshake) + { + PipeName = pipeName; + HandshakeComponents = handshake.RetrieveHandshakeComponents(); + _readTranslator = BinaryTranslator.GetReadTranslator(_readBuffer, InterningBinaryReader.CreateSharedBuffer()); + _writeTranslator = BinaryTranslator.GetWriteTranslator(_writeBuffer); + } + + protected abstract PipeStream NodeStream { get; } + + protected string PipeName { get; } + + protected int[] HandshakeComponents { get; } + + public void Dispose() + { + _readBuffer.Dispose(); + _writeBuffer.Dispose(); + _readTranslator.Dispose(); + _writeTranslator.Dispose(); + NodeStream.Dispose(); + } + + internal void RegisterPacketFactory(INodePacketFactory packetFactory) => _packetFactory = packetFactory; + + internal void WritePacket(INodePacket packet) + { + int messageLength = WritePacketToBuffer(packet); + byte[] buffer = _writeBuffer.GetBuffer(); + + for (int i = 0; i < messageLength; i += MaxPacketWriteSize) + { + int lengthToWrite = Math.Min(messageLength - i, MaxPacketWriteSize); + NodeStream.Write(buffer, i, lengthToWrite); + } + } + + internal INodePacket ReadPacket() + { + // Read the header. + int headerBytesRead = Read(_headerData, HeaderLength); + + // When an active connection is broken, any pending read will return 0 bytes before the pipe transitions to + // the broken state. As this is expected behavior, don't throw an exception if no packet is pending, A node + // may disconnect without waiting on the other end to gracefully cancel, and the caller can decide whether + // this was intentional. + if (headerBytesRead == 0) + { + return new NodeShutdown(NodeShutdownReason.ConnectionFailed); + } + else if (headerBytesRead != HeaderLength) + { + throw new IOException($"Incomplete header read. {headerBytesRead} of {HeaderLength} bytes read."); + } + +#if TASKHOST + int packetLength = BitConverter.ToInt32(_headerData, 1); +#else + int packetLength = BinaryPrimitives.ReadInt32LittleEndian(new Span(_headerData, 1, 4)); + MSBuildEventSource.Log.PacketReadSize(packetLength); +#endif + + // Read the packet. Set the buffer length now to avoid additional resizing during the read. + _readBuffer.Position = 0; + _readBuffer.SetLength(packetLength); + int packetBytesRead = Read(_readBuffer.GetBuffer(), packetLength); + + if (packetBytesRead < packetLength) + { + throw new IOException($"Incomplete packet read. {packetBytesRead} of {packetLength} bytes read."); + } + + return DeserializePacket(); + } + +#if !TASKHOST + internal async Task WritePacketAsync(INodePacket packet, CancellationToken cancellationToken = default) + { + int messageLength = WritePacketToBuffer(packet); + byte[] buffer = _writeBuffer.GetBuffer(); + + for (int i = 0; i < messageLength; i += MaxPacketWriteSize) + { + int lengthToWrite = Math.Min(messageLength - i, MaxPacketWriteSize); +#if NET + await NodeStream.WriteAsync(buffer.AsMemory(i, lengthToWrite), cancellationToken).ConfigureAwait(false); +#else + await NodeStream.WriteAsync(buffer, i, lengthToWrite, cancellationToken).ConfigureAwait(false); +#endif + } + } + + internal async Task ReadPacketAsync(CancellationToken cancellationToken = default) + { + // Read the header. + int headerBytesRead = await ReadAsync(_headerData, HeaderLength, cancellationToken).ConfigureAwait(false); + + // When an active connection is broken, any pending read will return 0 bytes before the pipe transitions to + // the broken state. As this is expected behavior, don't throw an exception if no packet is pending, A node + // may disconnect without waiting on the other end to gracefully cancel, and the caller can decide whether + // this was intentional. + if (headerBytesRead == 0) + { + return new NodeShutdown(NodeShutdownReason.ConnectionFailed); + } + else if (headerBytesRead != HeaderLength) + { + throw new IOException($"Incomplete header read. {headerBytesRead} of {HeaderLength} bytes read."); + } + + int packetLength = BinaryPrimitives.ReadInt32LittleEndian(new Span(_headerData, 1, 4)); + MSBuildEventSource.Log.PacketReadSize(packetLength); + + // Read the packet. Set the buffer length now to avoid additional resizing during the read. + _readBuffer.Position = 0; + _readBuffer.SetLength(packetLength); + int packetBytesRead = await ReadAsync(_readBuffer.GetBuffer(), packetLength, cancellationToken).ConfigureAwait(false); + + if (packetBytesRead < packetLength) + { + throw new IOException($"Incomplete packet read. {packetBytesRead} of {packetLength} bytes read."); + } + + return DeserializePacket(); + } +#endif + + private int WritePacketToBuffer(INodePacket packet) + { + // Clear the buffer but keep the underlying capacity to avoid reallocations. + _writeBuffer.SetLength(HeaderLength); + _writeBuffer.Position = HeaderLength; + + // Serialize and write the packet to the buffer. + packet.Translate(_writeTranslator); + + // Write the header to the buffer. + _writeBuffer.Position = 0; + _writeBuffer.WriteByte((byte)packet.Type); + int messageLength = (int)_writeBuffer.Length; + _writeTranslator.Writer.Write(messageLength - HeaderLength); + + return messageLength; + } + + private int Read(byte[] buffer, int bytesToRead) + { + int totalBytesRead = 0; + while (totalBytesRead < bytesToRead) + { + int bytesRead = NodeStream.Read(buffer, totalBytesRead, bytesToRead - totalBytesRead); + + // 0 byte read will occur if the pipe disconnects. + if (bytesRead == 0) + { + break; + } + + totalBytesRead += bytesRead; + } + + return totalBytesRead; + } + +#if !TASKHOST + private async ValueTask ReadAsync(byte[] buffer, int bytesToRead, CancellationToken cancellationToken) + { + int totalBytesRead = 0; + while (totalBytesRead < bytesToRead) + { +#if NET + int bytesRead = await NodeStream.ReadAsync(buffer.AsMemory(totalBytesRead, bytesToRead - totalBytesRead), cancellationToken).ConfigureAwait(false); +#else + int bytesRead = await NodeStream.ReadAsync(buffer, totalBytesRead, bytesToRead - totalBytesRead, cancellationToken).ConfigureAwait(false); +#endif + + // 0 byte read will occur if the pipe disconnects. + if (bytesRead == 0) + { + break; + } + + totalBytesRead += bytesRead; + } + + return totalBytesRead; + } +#endif + + private INodePacket DeserializePacket() + { + if (_packetFactory == null) + { + throw new InternalErrorException("No packet factory is registered for deserialization."); + } + + NodePacketType packetType = (NodePacketType)_headerData[0]; + try + { + return _packetFactory.DeserializePacket(packetType, _readTranslator); + } + catch (Exception e) when (e is not InternalErrorException) + { + throw new InternalErrorException($"Exception while deserializing packet {packetType}: {e}"); + } + } + } +} diff --git a/src/Shared/NodePipeClient.cs b/src/Shared/NodePipeClient.cs new file mode 100644 index 00000000000..6be1e0e422b --- /dev/null +++ b/src/Shared/NodePipeClient.cs @@ -0,0 +1,90 @@ +// 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.IO; +using System.IO.Pipes; +using System.Security.Principal; +using Microsoft.Build.BackEnd; + +namespace Microsoft.Build.Internal +{ + internal sealed class NodePipeClient : NodePipeBase + { + /// + /// If true, sets a timeout for the handshake. This is only used on Unix-like socket implementations, because the + /// timeout on the PipeStream connection is ignore. + /// + private static readonly bool s_useHandhakeTimeout = !NativeMethodsShared.IsWindows; + + private readonly NamedPipeClientStream _pipeClient; + + internal NodePipeClient(string pipeName, Handshake handshake) + : base(pipeName, handshake) => +#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter + _pipeClient = new( + serverName: ".", + pipeName, + PipeDirection.InOut, + PipeOptions.Asynchronous +#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY + | PipeOptions.CurrentUserOnly +#endif + ); +#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter + + protected override PipeStream NodeStream => _pipeClient; + + internal void ConnectToServer(int timeout) + { + CommunicationsUtilities.Trace("Attempting connect to pipe {0} with timeout {1} ms", PipeName, timeout); + _pipeClient.Connect(timeout); +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY + // Verify that the owner of the pipe is us. This prevents a security hole where a remote node has + // been faked up with ACLs that would let us attach to it. It could then issue fake build requests back to + // us, potentially causing us to execute builds that do harmful or unexpected things. The pipe owner can + // only be set to the user's own SID by a normal, unprivileged process. The conditions where a faked up + // remote node could set the owner to something else would also let it change owners on other objects, so + // this would be a security flaw upstream of us. + ValidateRemotePipeOwner(); +#endif + PerformHandshake(s_useHandhakeTimeout ? timeout : 0); + CommunicationsUtilities.Trace("Successfully connected to pipe {0}...!", PipeName); + } + +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY + // This code needs to be in a separate method so that we don't try (and fail) to load the Windows-only APIs when JIT-ing the code + // on non-Windows operating systems + private void ValidateRemotePipeOwner() + { + SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner; + PipeSecurity remoteSecurity = _pipeClient.GetAccessControl(); + IdentityReference remoteOwner = remoteSecurity.GetOwner(typeof(SecurityIdentifier)); + + if (remoteOwner != identifier) + { + CommunicationsUtilities.Trace("The remote pipe owner {0} does not match {1}", remoteOwner.Value, identifier.Value); + throw new UnauthorizedAccessException(); + } + } +#endif + + /// + /// Connect to named pipe stream and ensure validate handshake and security. + /// + private void PerformHandshake(int timeout) + { + for (int i = 0; i < HandshakeComponents.Length; i++) + { + CommunicationsUtilities.Trace("Writing handshake part {0} ({1}) to pipe {2}", i, HandshakeComponents[i], PipeName); + _pipeClient.WriteIntForHandshake(HandshakeComponents[i]); + } + + // This indicates that we have finished all the parts of our handshake; hopefully the endpoint has as well. + _pipeClient.WriteEndOfHandshakeSignal(); + + CommunicationsUtilities.Trace("Reading handshake from pipe {0}", PipeName); + _pipeClient.ReadEndOfHandshakeSignal(true, timeout); + } + } +} diff --git a/src/Shared/NodePipeServer.cs b/src/Shared/NodePipeServer.cs new file mode 100644 index 00000000000..91fba144c52 --- /dev/null +++ b/src/Shared/NodePipeServer.cs @@ -0,0 +1,220 @@ +// 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.IO; +using System.IO.Pipes; +using System.Security.AccessControl; +using System.Security.Principal; +using Microsoft.Build.BackEnd; +using Microsoft.Build.Shared; + +#if !TASKHOST +using System.Threading.Tasks; +#endif + +namespace Microsoft.Build.Internal +{ + internal sealed class NodePipeServer : NodePipeBase + { + /// + /// The size of kernel-level buffers used by the named pipe. If the total size of pending reads or write requests exceed + /// this amount (known as the quota), IO will block until either pending operations complete, or the OS increases the quota. + /// + private const int PipeBufferSize = 131_072; + + /// + /// A timeout for the handshake. This is only used on Unix-like socket implementations, because the + /// timeout on the PipeStream connection is ignore. + /// + private static readonly int s_handshakeTimeout = NativeMethodsShared.IsWindows ? 0 : 60_000; + + private readonly NamedPipeServerStream _pipeServer; + + internal NodePipeServer(string pipeName, Handshake handshake, int maxNumberOfServerInstances = 1) + : base(pipeName, handshake) + { + PipeOptions pipeOptions = PipeOptions.Asynchronous; +#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY + pipeOptions |= PipeOptions.CurrentUserOnly; +#else + // Restrict access to just this account. We set the owner specifically here, and on the + // pipe client side they will check the owner against this one - they must have identical + // SIDs or the client will reject this server. This is used to avoid attacks where a + // hacked server creates a less restricted pipe in an attempt to lure us into using it and + // then sending build requests to the real pipe client (which is the MSBuild Build Manager.) + PipeAccessRule rule = new(WindowsIdentity.GetCurrent().Owner, PipeAccessRights.ReadWrite, AccessControlType.Allow); + PipeSecurity security = new(); + security.AddAccessRule(rule); + security.SetOwner(rule.IdentityReference); +#endif + + _pipeServer = new NamedPipeServerStream( + pipeName, + PipeDirection.InOut, + maxNumberOfServerInstances, + PipeTransmissionMode.Byte, + pipeOptions, + inBufferSize: PipeBufferSize, + outBufferSize: PipeBufferSize +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY + , security, + HandleInheritability.None +#endif +#pragma warning disable SA1111 // Closing parenthesis should be on line of last parameter + ); +#pragma warning restore SA1111 // Closing parenthesis should be on line of last parameter + } + + protected override PipeStream NodeStream => _pipeServer; + + internal LinkStatus WaitForConnection() + { + DateTime originalWaitStartTime = DateTime.UtcNow; + bool gotValidConnection = false; + + while (!gotValidConnection) + { + gotValidConnection = true; + DateTime restartWaitTime = DateTime.UtcNow; + + // We only wait to wait the difference between now and the last original start time, in case we have multiple hosts attempting + // to attach. This prevents each attempt from resetting the timer. + TimeSpan usedWaitTime = restartWaitTime - originalWaitStartTime; + int waitTimeRemaining = Math.Max(0, CommunicationsUtilities.NodeConnectionTimeout - (int)usedWaitTime.TotalMilliseconds); + + try + { + // Wait for a connection +#if TASKHOST + IAsyncResult resultForConnection = _pipeServer.BeginWaitForConnection(null, null); + CommunicationsUtilities.Trace("Waiting for connection {0} ms...", waitTimeRemaining); + bool connected = resultForConnection.AsyncWaitHandle.WaitOne(waitTimeRemaining, false); + _pipeServer.EndWaitForConnection(resultForConnection); +#else + Task connectionTask = _pipeServer.WaitForConnectionAsync(); + CommunicationsUtilities.Trace("Waiting for connection {0} ms...", waitTimeRemaining); + bool connected = connectionTask.Wait(waitTimeRemaining); +#endif + if (!connected) + { + CommunicationsUtilities.Trace("Connection timed out waiting a host to contact us. Exiting comm thread."); + return LinkStatus.ConnectionFailed; + } + + CommunicationsUtilities.Trace("Parent started connecting. Reading handshake from parent"); + + // The handshake protocol is a series of int exchanges. The host sends us a each component, and we + // verify it. Afterwards, the host sends an "End of Handshake" signal, to which we respond in kind. + // Once the handshake is complete, both sides can be assured the other is ready to accept data. + try + { + gotValidConnection = ValidateHandshake(); +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY + gotValidConnection &= ValidateClientIdentity(); +#endif + } + catch (IOException e) + { + // We will get here when: + // 1. The host (OOP main node) connects to us, it immediately checks for user privileges + // and if they don't match it disconnects immediately leaving us still trying to read the blank handshake + // 2. The host is too old sending us bits we automatically reject in the handshake + // 3. We expected to read the EndOfHandshake signal, but we received something else + CommunicationsUtilities.Trace("Client connection failed but we will wait for another connection. Exception: {0}", e.Message); + gotValidConnection = false; + } + catch (InvalidOperationException) + { + gotValidConnection = false; + } + + if (!gotValidConnection && _pipeServer.IsConnected) + { + _pipeServer.Disconnect(); + } + } + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) + { + CommunicationsUtilities.Trace("Client connection failed. Exiting comm thread. {0}", e); + if (_pipeServer.IsConnected) + { + _pipeServer.Disconnect(); + } + + ExceptionHandling.DumpExceptionToFile(e); + return LinkStatus.Failed; + } + } + + return LinkStatus.Active; + } + + internal void Disconnect() + { + try + { + if (_pipeServer.IsConnected) + { +#if NET // OperatingSystem.IsWindows() is new in .NET 5.0 + if (OperatingSystem.IsWindows()) +#endif + { + _pipeServer.WaitForPipeDrain(); + } + + _pipeServer.Disconnect(); + } + } + catch (Exception) + { + // We don't really care if Disconnect somehow fails, but it gives us a chance to do the right thing. + } + } + + private bool ValidateHandshake() + { + for (int i = 0; i < HandshakeComponents.Length; i++) + { + // This will disconnect a < 16.8 host; it expects leading 00 or F5 or 06. 0x00 is a wildcard. + int handshakePart = _pipeServer.ReadIntForHandshake(byteToAccept: i == 0 ? CommunicationsUtilities.handshakeVersion : null, s_handshakeTimeout); + + if (handshakePart != HandshakeComponents[i]) + { + CommunicationsUtilities.Trace("Handshake failed. Received {0} from host not {1}. Probably the host is a different MSBuild build.", handshakePart, HandshakeComponents[i]); + _pipeServer.WriteIntForHandshake(i + 1); + return false; + } + } + + // To ensure that our handshake and theirs have the same number of bytes, receive and send a magic number indicating EOS. + _pipeServer.ReadEndOfHandshakeSignal(false, s_handshakeTimeout); + + CommunicationsUtilities.Trace("Successfully connected to parent."); + _pipeServer.WriteEndOfHandshakeSignal(); + + return true; + } + +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY + private bool ValidateClientIdentity() + { + // We will only talk to a host that was started by the same user as us. Even though the pipe access is set to only allow this user, we want to ensure they + // haven't attempted to change those permissions out from under us. This ensures that the only way they can truly gain access is to be impersonating the + // user we were started by. + WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent(); + WindowsIdentity? clientIdentity = null; + _pipeServer.RunAsClient(() => { clientIdentity = WindowsIdentity.GetCurrent(true); }); + + if (clientIdentity == null || !string.Equals(clientIdentity.Name, currentIdentity.Name, StringComparison.OrdinalIgnoreCase)) + { + CommunicationsUtilities.Trace("Handshake failed. Host user is {0} but we were created by {1}.", (clientIdentity == null) ? "" : clientIdentity.Name, currentIdentity.Name); + return false; + } + + return true; + } +#endif + + } +} From 4df808e9fd646aeae9cd68b055f39315fa819f53 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 22:35:09 +0100 Subject: [PATCH 040/106] [main] Update dependencies from dotnet/source-build-reference-packages (#11592) * Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20250313.3 Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 9.0.0-alpha.1.25081.6 -> To Version 9.0.0-alpha.1.25163.3 * Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20250320.3 Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 9.0.0-alpha.1.25081.6 -> To Version 9.0.0-alpha.1.25170.3 --------- Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 549504dec70..8c3f7367455 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -2,9 +2,9 @@ - + https://github.com/dotnet/source-build-reference-packages - 1cec3b4a8fb07138136a1ca1e04763bfcf7841db + 6968f7059f4418e985febe704a3b1320f9e5887d From f25f7fd6de04dc290d4341c791b46e31a559901c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Mon, 24 Mar 2025 18:04:49 -0700 Subject: [PATCH 041/106] Update maintenance-packages versions (#11457) --- eng/Versions.props | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index cc5d3cf10a1..5a9eaae5001 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -25,8 +25,9 @@ already consuming the newest version of these same dependencies. --> - 4.6.0 - 6.1.0 + 4.6.2 + 6.1.1 + 6.1.2 @@ -37,10 +38,10 @@ --> 4.5.5 6.0.0 + 6.0.1 - 6.0.1 0.2.104-beta 5.0.0 From 6aeb262fe5570316ede42dc69788908b548972c5 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 15:19:48 +0100 Subject: [PATCH 042/106] Update dependencies from https://github.com/dotnet/roslyn build 20250321.27 (#11615) Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.Net.Compilers.Toolset From Version 4.14.0-3.25164.10 -> To Version 4.14.0-3.25171.27 Co-authored-by: dotnet-maestro[bot] Co-authored-by: Jenny Bai --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8c3f7367455..1f86224d271 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -141,13 +141,13 @@ https://github.com/nuget/nuget.client 181b65dad9f440c7a31fe673abc59c258f224ada - + https://github.com/dotnet/roslyn - 517e95f9430d387e0e387a23fa2c8351a0863c4a + 1def752c5d33903795069ccddb78599ba6da39d3 - + https://github.com/dotnet/roslyn - 517e95f9430d387e0e387a23fa2c8351a0863c4a + 1def752c5d33903795069ccddb78599ba6da39d3 diff --git a/eng/Versions.props b/eng/Versions.props index 5a9eaae5001..f531453c4b4 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -79,7 +79,7 @@ $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) 4.2.0-1.22102.8 9.0.0-beta.25164.2 - 4.14.0-3.25164.10 + 4.14.0-3.25171.27 6.14.0-preview.1.66 From 9e51a07c6f1b23cb28b958d63c1dff1de704108d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 26 Mar 2025 10:29:32 +0100 Subject: [PATCH 043/106] Update MicrosoftBuildVersion to 17.15.0 (#11590) Co-authored-by: github-actions --- .../Microsoft.CheckTemplate/.template.config/template.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template_feed/content/Microsoft.CheckTemplate/.template.config/template.json b/template_feed/content/Microsoft.CheckTemplate/.template.config/template.json index 91b192b37a9..5753a79bada 100644 --- a/template_feed/content/Microsoft.CheckTemplate/.template.config/template.json +++ b/template_feed/content/Microsoft.CheckTemplate/.template.config/template.json @@ -27,7 +27,7 @@ "type": "parameter", "description": "Overrides the default Microsoft.Build version where check's interfaces are placed", "datatype": "text", - "defaultValue": "17.14.0", + "defaultValue": "17.15.0", "replaces": "1.0.0-MicrosoftBuildPackageVersion", "displayName": "Microsoft.Build default package version override" } From c2c7d24082c477c33da328213bf59d4b5cb5cfa6 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Wed, 26 Mar 2025 11:42:40 +0100 Subject: [PATCH 044/106] Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11225573 (#11608) --- src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf | 26 +++++++++---------- src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf | 18 ++++++------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf index 24c3f4dcb45..0968a8a75fb 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf @@ -841,9 +841,9 @@ WarningsOnly -- 仅显示警告。 NoItemAndPropertyList -- 在开始生成每个项目时不显示 项和属性的列表。 - ShowCommandLine -- 显示 TaskCommandLineEvent 消息 + ShowCommandLine -- 显示 TaskCommandLineEvent 消息 ShowTimestamp -- 将时间戳作为所有消息的前缀 - 显示。 + 显示。 ShowEventId -- 显示已开始事件、已完成事件和消息 的事件 ID。 ForceNoAlign -- 不将文本与控制台缓冲区的大小 @@ -900,10 +900,10 @@ Example: -validate:MyExtendedBuildSchema.xsd - -validate 依据默认架构验证项目。(缩写: + -validate 依据默认架构验证项目。(缩写: -val) - -validate:<schema> 依据指定的架构验证项目。(缩写: + -validate:<schema> 依据指定的架构验证项目。(缩写: -val) 示例: -validate:MyExtendedBuildSchema.xsd @@ -1081,7 +1081,7 @@ -toolsversion:<version> 要在生成过程中使用的 MSBuild 工具集 (任务、目标等)的版本。此版本将重写 - 各个项目指定的版本。(缩写: + 各个项目指定的版本。(缩写: -tv) 示例: -toolsversion:3.5 @@ -1137,17 +1137,17 @@ template and append the node id to this fileName to create a log file for each node. - -distributedFileLogger + -distributedFileLogger 将生成输出记录到多个日志文件,每个 MSBuild 节点 一个日志文件。这些文件的初始位置为 当前目录。默认情况下,这些文件名为 “MSBuild<nodeid>.log”。可通过添加 - “-fileLoggerParameters”开关来指定 + “-fileLoggerParameters”开关来指定 这些文件的位置和 fileLogger 的其他参数。 如果日志文件名是通过 fileLoggerParameters 开关设置的,分布式记录器将使用 fileName 作为 - 模板并将节点 ID 附加到此 fileName + 模板并将节点 ID 附加到此 fileName 以便为每个节点创建一个日志文件。 @@ -1189,12 +1189,12 @@ -flp1:warningsonly;logfile=msbuild.wrn -flp2:errorsonly;logfile=msbuild.err - -fileloggerparameters[n]:<parameters> + -fileloggerparameters[n]:<parameters> 为文件记录器提供任何额外的参数。 存在此开关意味着 存在对应的 -filelogger[n] 开关。 “n”(如果存在)可以为 1-9 的数字。 - 任何分布式文件记录器也可以使用 + 任何分布式文件记录器也可以使用 -fileloggerparameters,具体可参阅 -distributedFileLogger 的说明。 (缩写: -flp[n]) 为控制台记录器列出的相同参数 @@ -1214,8 +1214,8 @@ -fileLoggerParameters:LogFile=MyLog.log;Append; Verbosity=diagnostic;Encoding=UTF-8 - -flp:Summary;Verbosity=minimal;LogFile=msbuild.sum - -flp1:warningsonly;logfile=msbuild.wrn + -flp:Summary;Verbosity=minimal;LogFile=msbuild.sum + -flp1:warningsonly;logfile=msbuild.wrn -flp2:errorsonly;logfile=msbuild.err @@ -2200,4 +2200,4 @@ - + \ No newline at end of file diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf index 58b406ea531..b2b8fb45067 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf @@ -214,7 +214,7 @@ 終端機記錄器的參數。(簡短形式: -tlp) 可用的參數。 default -- 指定終端機記錄器的預設值。 - 其需要下列其中一值: + 其需要下列其中一值: 。 - 'on'、'true' 會強制使用 TerminalLogger,即使 其之後可能會停用。 @@ -227,7 +227,7 @@ -verbosity showCommandLine -- 顯示 TaskCommandLineEvent 訊息 - 範例: + 範例: -tlp:default=auto;verbosity=diag;shownCommandLine @@ -246,7 +246,7 @@ -getResultOutputFile:file 將輸出從 get* 重新導向至檔案。 - 範例: + 範例: -getProperty:Bar -getResultOutputFile:Biz.txt 這會將屬性列的值寫入 Biz.txt。 @@ -263,7 +263,7 @@ -check 在建置期間啟用 BuildChecks。 - BuildCheck 會啟用評估規則以確保組建的 + BuildCheck 會啟用評估規則以確保組建的 屬性。如需詳細資訊,請參閱 aka.ms/buildcheck @@ -446,8 +446,8 @@ -isolateProjects[:True|MessageUponIsolationViolation|False] 導致 MSBuild 在隔離中建置每個專案。 - 設定為 "MessageUponIsolationViolation" - (或其簡短形式 "Message") 時,如果提供 + 設定為 "MessageUponIsolationViolation" + (或其簡短形式 "Message") 時,如果提供 -outputResultsCache 切換,則只會序列化來自 頂層目標的結果。這是為了降低相依性專案上, 由於其相依性位於快取目標上 (其副作用 @@ -1081,8 +1081,8 @@ -toolsversion:<版本> 建置期間所使用的 MSBuild 工具組 (工作、目標等) - 版本。此版本將會覆寫 - 個別專案所指定的版本。(簡短形式: + 版本。此版本將會覆寫 + 個別專案所指定的版本。(簡短形式: -tv) 範例: -toolsVersion:3.5 @@ -2201,4 +2201,4 @@ - + \ No newline at end of file From 8777dcac503904cbe0d796a0da20d3360a75d2d0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Thu, 27 Mar 2025 07:08:49 -0700 Subject: [PATCH 045/106] Don't force shipping versions to be used in VMR builds (#11625) Don't build MSBuild with shipping versions in the VMR unless the VMR passes an official build ID. When MSBuild is being built in the VMR, sometimes later-built packages can end up with different versions than earlier-built packages. That's not good, but it doesn't cause failures. However, these later-built packages can have different (higher) versions of dependency projects that had already been built with the lower versions. This causes downgrade failures like the following intermittently for any VMR jobs triggered around the end of the PST workday: > Detected package downgrade: Microsoft.Build.Tasks.Core from 17.15.0-ci-25175-01 to centrally defined 17.15.0-ci-25174-01. Update the centrally managed package version to a higher version. > Msbuild.Tests.Utilities -> Microsoft.Build.Runtime 17.15.0-ci-25174-01 -> Microsoft.Build.Tasks.Core (>= 17.15.0-ci-25175-01) > Msbuild.Tests.Utilities -> Microsoft.Build.Tasks.Core (>= 17.15.0-ci-25174-01) --- eng/Versions.props | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index f531453c4b4..3093e9241f6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -6,7 +6,12 @@ 17.14.0-preview-25161-14 15.1.0.0 preview - true + + true true 1.1.87 From 21a35212ae90eb7587be30964cf384ac75d5a353 Mon Sep 17 00:00:00 2001 From: MaceWindu Date: Thu, 27 Mar 2025 15:23:57 +0100 Subject: [PATCH 046/106] Fix url formatting in BuildCheck/Codes.md (#11631) --- documentation/specs/BuildCheck/Codes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/specs/BuildCheck/Codes.md b/documentation/specs/BuildCheck/Codes.md index 3557170f825..e9a4c04a20f 100644 --- a/documentation/specs/BuildCheck/Codes.md +++ b/documentation/specs/BuildCheck/Codes.md @@ -21,7 +21,7 @@ Report codes are chosen to conform to suggested guidelines. Those guidelines are Notes: * What does the 'N/A' scope mean? The scope of checks are only applicable and configurable in cases where evaluation-time data are being used and the source of the data is determinable and available. Otherwise the scope of whole build is always checked. * How can you alter the default configuration? [Please check the Configuration section of the BuildCheck documentation](./BuildCheck.md#sample-configuration) - * To enable verbose logging in order to troubleshoot issue(s), enable [binary logging](https://github.com/dotnet/msbuild/blob/main/documentation/wiki/Binary-Log.md#msbuild-binary-log-overview + * To enable verbose logging in order to troubleshoot issue(s), enable [binary logging](https://github.com/dotnet/msbuild/blob/main/documentation/wiki/Binary-Log.md#msbuild-binary-log-overview) _Cmd:_ ```cmd dotnet build -bl -check From 1888b02a8fb36bea403b09b7863e735b183f72d2 Mon Sep 17 00:00:00 2001 From: Benjamin Brienen Date: Thu, 27 Mar 2025 19:59:57 +0100 Subject: [PATCH 047/106] markdown in documentation/specs formatting/linting/cleanup (#11611) Co-authored-by: Benjamin Brienen --- documentation/specs/event-source.md | 3 +- documentation/specs/low-priority-switch.md | 2 +- documentation/specs/project-cache.md | 73 +++++++++++-------- documentation/specs/question.md | 11 ++- documentation/specs/rar-core-scenarios.md | 2 + documentation/specs/remote-host-object.md | 7 +- .../specs/sdk-resolvers-algorithm.md | 33 +++++---- .../specs/single-project-isolated-builds.md | 14 ++-- documentation/specs/static-graph.md | 49 ++++++++++--- .../specs/task-isolation-and-dependencies.md | 33 ++++++--- documentation/specs/test-target.md | 67 ++++++++++------- 11 files changed, 187 insertions(+), 107 deletions(-) diff --git a/documentation/specs/event-source.md b/documentation/specs/event-source.md index 198791bb356..dcaab9c9cba 100644 --- a/documentation/specs/event-source.md +++ b/documentation/specs/event-source.md @@ -3,6 +3,7 @@ [EventSource](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.tracing.eventsource?view=netframework-4.8) is the tool that allows Event Tracing for Windows (ETW) used in MSBuild. Among its useful features, functions with names ending in "start" and "stop" correlate between calls such that it can automatically record how long the event between the two calls took. It also provides an easy way to cheaply opt in or out, log auxiliary messages in addition to time, and add progress updates in the middle of an event as needed. ## EventSource in MSBuild + EventSource is primarily used to profile code. For MSBuild specifically, a major goal is to reduce the time it takes to run, as measured (among other metrics) by the Regression Prevention System (RPS), i.e., running specific scenarios. To find which code segments were likely candidates for improvement, EventSources were added around a mix of code segments. Larger segments that encompass several steps within a build occur nearly every time MSBuild is run and take a long time. They generally run relatively few times. Smaller methods with well-defined purposes may occur numerous times. Profiling both types of events provides both broad strokes to identify large code segments that underperform and, more specifically, which parts of them. Profiled functions include: | Event | Description | @@ -20,7 +21,7 @@ EventSource is primarily used to profile code. For MSBuild specifically, a major | ExecuteTaskYield | Requests to yield the node, often while the task completes other work. | | ExpandGlob | Identifies a list of files that correspond to an item, potentially with a wildcard. | | GenerateResourceOverall | Uses resource APIs to transform resource files into strongly-typed resource classes. | -| LoadDocument | Loads an XMLDocumentWithLocation from a path. +| LoadDocument | Loads an XMLDocumentWithLocation from a path. | | MSBuildExe | Executes MSBuild from the command line. | | MSBuildServerBuild | Executes a build from the MSBuildServer node. | | PacketReadSize | Reports the size of a packet sent between nodes. Note that this does not include time information. | diff --git a/documentation/specs/low-priority-switch.md b/documentation/specs/low-priority-switch.md index ba33826a89e..bec5a72fc74 100644 --- a/documentation/specs/low-priority-switch.md +++ b/documentation/specs/low-priority-switch.md @@ -17,7 +17,6 @@ Visual Studio, on the other hand, should always run at normal priority. This ens 4. Any reused nodes are at the priority they themselves specify. Normal priority nodes are actually at normal priority, and low priority nodes are actually at BelowNormal priority. 5. All nodes are at the priority they should be when being used to build even if a normal priority process had connected to them as normal priority worker nodes, and they are now executing a low priority build. - ## Non-goals Perfect parity between windows and mac or linux. Windows permits processes to raise their own priority or that of another process, whereas other operating systems do not. This is very efficient, so we should use it. As we expect this feature to be used in Visual Studio, we anticipate it being less used on mac and linux, hence not being as high priority to make it just as efficient. @@ -27,6 +26,7 @@ Perfect parity between windows and mac or linux. Windows permits processes to ra Each node (including worker nodes) initially takes its priority from its parent process. Since we now need the priority to align with what it is passed instead of its parent, attempt to adjust priority afterwards if necessary as part of node startup. BuildManager.cs remembers the priority of the previous build it had executed. If that was set to a value that differs from the priority of the current build: + 1. On windows or when decreasing the priority: lowers the priority of all connected nodes 2. On linux and mac when increasing the priority: disconnects from all nodes. diff --git a/documentation/specs/project-cache.md b/documentation/specs/project-cache.md index b0ce961313d..90d19466fdc 100644 --- a/documentation/specs/project-cache.md +++ b/documentation/specs/project-cache.md @@ -1,8 +1,10 @@ -# Summary +# Project Cache + +## Summary Project cache is a new assembly-based plugin extension point in MSBuild which determines whether a build request (a project) can be skipped during build. The main expected benefit is reduced build times via [caching and/or distribution](static-graph.md#weakness-of-the-old-model-caching-and-distributability). -# Motivation +## Motivation As the introduction to [static graph](static-graph.md#what-is-static-graph-for) suggests, large and complex repos expose the weaknesses in MSBuild's scheduling and incrementality models as build times elongate. This project cache plugin lets MSBuild natively communicate with existing tools that enable build caching and/or distribution, enabling true scalability. @@ -10,31 +12,33 @@ Visual Studio is one beneficiary. This plugin inverts dependencies among build s This change also simplifies and unifies user experiences. MSBuild works the same from Visual Studio or the command line without dramatically changing how it works. -# Plugin requirements +## Plugin requirements - The plugin should tell MSBuild whether a build request needs building. If a project is skipped, then the plugin needs to ensure that: - it makes the filesystem look as if the project built - it returns sufficient information back to MSBuild such that MSBuild can construct a valid [`BuildResult`](/src/Build/BackEnd/Shared/BuildResult.cs#L30-L33) for its internal scheduling logic, such that future requests to build a skipped project are served directly from MSBuild's internal caches. -# High-level design +## High-level design Conceptually, there are two parts of caching: "cache get" and "cache add". "Cache get" is MSBuild asking the plugin if it wants to handle a build request, ie by fetching from some cache. "Cache add" is, upon cache miss, MSBuild providing enough information to the plugin during the build of the build request for the plugin to add the results to its cache and safely be able to retrieve it for some future build. The "cache get" functionality was introduced in 16.9, while "cache add" was added in 17.8. -## Plugin discovery +### Plugin discovery - Plugin dlls are discovered by MSBuild via a new special purpose `ProjectCachePlugin` [items](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-items). - These items can get injected into a project's import graph by package managers via the [PackageReference](https://docs.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files) item. - MSBuild will discover the plugin by searching project evaluations for `ProjectCachePlugin` items. -```xml - - - -``` + + ```xml + + + + ``` + - Programmatic usage of `BuildManager` can also set `BuildParameters.ProjectCacheDescriptor` to apply a plugin to all requests. -## Plugin lifetime +### Plugin lifetime - Plugin instances reside only in the `BuildManager` node. Having it otherwise (plugin instances residing in all nodes) means forcing the plugins to either deal with distributed state or implement a long lived service. We consider this high complexity cost to not be worth it. We also want to avoid serializing the `ProjectInstance` between nodes, which is expensive. - `BuildManager.BeginBuild` calls `ProjectCacheBase.BeginBuildAsync` on all discovered plugins. This allows plugins to start any required initialization work. It does not wait for the plugins to fully initialize, ie it is a "fire-and-forget" call at this point. The first query on the plugin will wait for plugin initialization. @@ -43,10 +47,10 @@ The "cache get" functionality was introduced in 16.9, while "cache add" was adde - The plugin instance will get called in reverse topological sort order (from referenced projects up towards referencing projects). This happens when performing a graph build (`/graph`), Visual Studio solution builds, and commonly in higher build engines. - Only the top-level build requests are checked against the cache. Build requests issued recursively from the top-level requests, for example a project building its dependencies, are not checked against the cache. However, because the build requests are assumed to be issued in reverse topological sort order, those requests should have already been built and present in MSBuild's internal result cache, provided either by the project cache plugin or real builds. A consequence of this is that projects which are not well-described in the graph (e.g. using `` tasks directly) will not benefit from the cache. -## Cache get scenario +### Cache get scenario - For each [`BuildRequestData`](/src/Build/BackEnd/BuildManager/BuildRequestData.cs#L83) ([`ProjectInstance`](/src/Build/Instance/ProjectInstance.cs#L71), Global Properties, Targets) submitted to the [`BuildManager`](/src/Build/BackEnd/BuildManager/BuildManager.cs#L38), MSBuild asks the plugin whether to build the request or not. - + - If the `BuildRequestData` is based on a project path instead of a `ProjectInstance`, the project is evaluated by the `BuildManager`. - If the plugin decides to build, then MSBuild proceeds building the project as usual. - If the plugin decides to skip the build, it needs to return back to MSBuild the target results that the build request would have produced. It can either provide the results directly, or instruct MSBuild to run a set of less expensive targets on the projects with the same effect as the expensive targets ("proxy targets"). @@ -62,7 +66,7 @@ The "cache get" functionality was introduced in 16.9, while "cache add" was adde - Best: A real `BuildResult` from a previous build is provided. This can either be done by serializing the `HandleProjectFinishedAsync`, or when the plugin's infrastructure (e.g. CloudBuild or AnyBuild builder nodes) runs and caches the build, it can tell MSBuild to serialize the BuildResult to a file via [BuildParameters.OutputResultsCacheFile](/src/Build/BackEnd/BuildManager/BuildParameters.cs#L767) or the `/outputResultsCache` command line argument. Then, on cache hits, the plugins deserialize the `BuildResult` and send it back to MSBuild. This is the most correct option, as it requires neither guessing nor proxy targets. Whatever a previous build did, that's exactly what's returned. - Potential Issue: serialization format may change between writing and reading the `BuildResult`, especially if binary serialization is used. -## Cache add scenario +### Cache add scenario - Upon a cache miss, MSBuild will generally handle a request as normal, ie by building it. - MSBuild uses [Detours](https://github.com/microsoft/Detours) to observe file accesses of the worker nodes. To facilitate the plugin being able to handle future builds, it forwards this information as well as the build result to the plugin for it to use as desired, for example to add to a cache. @@ -72,9 +76,10 @@ The "cache get" functionality was introduced in 16.9, while "cache add" was adde - Due to the experimental nature of the feature, `/ReportFileAccesses` is only available with MSBuild.exe (ie. the Visual Studio install; not `dotnet`), only for the x64 flavor (not x86 or arm64), and only from the command-line. The Visual Studio IDE does not set `BuildParameters.ReportFileAccesses`. - As described above, it is recommended to serialize the `BuildResult` from `HandleProjectFinishedAsync` for later replay. -# APIs and calling patterns +## APIs and calling patterns + +### Plugin API -## Plugin API [ProjectCachePluginBase](/src/Build/BackEnd/Components/ProjectCache/ProjectCachePluginBase.cs) is an abstract class which plugin implementors will subclass. See the [Plugin implementation guidance and simple example design](#plugin-implementation-guidance-and-simple-example-design) section for guidance for plugin implementations. @@ -97,14 +102,14 @@ This can then be accessed by the plugin in `BeginBuildAsync` as a dictionary via Note: As it is likely that plugins will be distributed through NuGet packages and those packages would define the `ProjectCachePlugin` item in a props or targets file in the package, it's recommended for plugin authors to have settings backed by MSBuild properties as in the example above. This allows the user to easily configure a plugin simply by setting the properties and including the `PackageReference`. -## Enabling from command line +### Enabling from command line - Requires `/graph` to light up cache get scenarios. - Requires `/reportfileaccesses` to light up cache add scenarios. - The static graph has all the project instances in the same process, making it easy to find and keep plugin instances in one process. - MSBuild constructs the static graph and build bottom up, so by the time a project is considered, all of its references and their build results are already present in the Scheduler. -## Enabling from Visual Studio, a temporary workaround +### Enabling from Visual Studio, a temporary workaround - Ideally, Visual Studio would provide a `ProjectGraph` instance. Until that happens, a workaround is needed. - The workaround logic activates only when MSBuild detects that it's running under VS. @@ -113,29 +118,30 @@ Note: As it is likely that plugins will be distributed through NuGet packages an - Plugins will be given the graph entry points instead of the entire graph in this scenario. - There is currently no way to enable cache add scenarios in Visual Studio. -# Detours (cache add scenario) +## Detours (cache add scenario) In order for MSBuild to observe the file accesses as part of the build, it uses Detours on the worker nodes. In this way the Scheduler node will emit events for all file accesses done by the worker nodes. As the Scheduler knows what build request a worker node is working on at any given moment, it is able to properly associate the file access with a build request and dispatch these augmented events to plugins via the plugins' `HandleFileAccess` and `HandleProcess` implementations. Note that the Scheduler node cannot use Detours on itself, so the in-proc node is disabled when repoting file accesses. Additionally task yielding is disabled since it would leave to improperly associated file accesses. -## Pipe synchronization +### Pipe synchronization Because the Detours implementation being used communicates over a pipe, and nodes communicate over a pipe as well, and pipes are async, there is some coordination required to ensure that file accesses are associated with the proper build request. For example, if a "project finished" signal comes through the node communication pipe, but the detours pipe still has a queue of file accesses which have not been processed yet, those file accesses might be processed after the worker node has moved onto some other project. To address this problem, when a worker node finishes a project it will emit a dummy file access with a specific format known to MSBuild. When the scheduler node receives as "project finished" event over the node communication pipe, it will wait to determine that the project is actually finished until it also receives the dummy file access. This ensures that the all file accesses associated with the project have fully flushed from the pipe before the scheduler determines the project is finished and schedules new work to the worker node (which would trigger new file accesses). -# Plugin implementation guidance and simple example design +## Plugin implementation guidance and simple example design The following will describe a very basic (and not very correct) plugin implementation. In practice, plugins will have to choose the specific level of correctness they're willing to trade off for the ability to get cache hits. Any machine state *could* impact build results, and the plugin implementation will need to determine what state matters and what doesn't. An obvious example to consider would be the content of the project file. An example which has trade-offs would be the processes' environment variables. Even the current time could possibly impact the build ("if Tuesday copy this file"), but if considered caching would be quite infeasible. -## Fingerprinting +### Fingerprinting A "fingerprint" describes each unique input which went into the building a build request. The more granular the fingerprint, the more "correct" the caching is, as described above. In this example, we will only consider the following as inputs, and thus part of the fingerprint: + - The global properties of the build request (eg `Configuration=Debug`, `Platform=AnyCPU`) - The content hash of the project file - The content hash of files defined in specific items we know contribute to the build, like `` and `` @@ -147,13 +153,13 @@ It can make sense for a fingerprint to be a hash of its inputs, so effectively i At the beginning of the build, the plugin's `BeginBuildAsync` method will be called. As part of the `CacheContext`, the plugin is either given the graph or the entry points to the graph for which it can create a graph from. The plugin can use this graph to do some initial processing, like predicting various inputs which a project is likely to use. This information can then be stored to help construct a fingerprint for a build request later. -## Cache storage +### Cache storage Any storage mechanism can be used as a cache implementation, for example [Azure Blob Storage](https://azure.microsoft.com/products/storage/blobs/), or even just the local filesystem. At least in this example the only real requirement is that it can be used effectively as a key-value store. In many cases it can be useful for content to be keyed by its hash, and for the metadata file to be keyed by the fingerprint. In particular when content is keyed by hash, it is effectively deduplicated across multiple copies of the same file, which is common in builds. For illustration purposes, consider our cache implementation is based on a simple filesystem with a separate metadata and content directory inside it. Under the metadata dir, each file is a metadata file where the filename matches the fingerprint it's describing. Under the content dir, each file is a content file where the filename matches the hash of the content itself. -## First build (cache population) +### First build (cache population) In the very first build there will be no cache hits so the "cache add" scenario will be most relevant here. @@ -168,22 +174,24 @@ In our example, we can use the read files to construct a fingerprint for the bui The plugin would then create some metadata describing the outputs (eg. the paths and hashes) and the serialized `BuildResult`, and associate it with the fingerprint and put that assocation in the cache. To illustrate this, consider a project with fingerprint `F` which wrote a single file `O` with hash `H` and had `BuildResult R`. The plugin could create a metadata file `M` which describes the outputs of the build (the path and hash of `O`) as well as the serialized `R`. Using the cache implementation described above, the plugin would write the following two files to the cache: - - `metadata/F -> M:"{outputs: [{path: 'path/to/O', hash: H}], result: R}"` - - `content/H -> O` + +- `metadata/F -> M:"{outputs: [{path: 'path/to/O', hash: H}], result: R}"` +- `content/H -> O` This can then be used for future builds. - ## Second Build (cache hits) - - In the second build we have a populated cache and so it could be possible to get cache hits. +### Second Build (cache hits) - For a given project, `GetCacheResultAsync` will be invoked. The plugin can fingerprint the request and use that fingerprint to look up in its cache. If the cache entry exists, it can declare a cache hit. +In the second build we have a populated cache and so it could be possible to get cache hits. + +For a given project, `GetCacheResultAsync` will be invoked. The plugin can fingerprint the request and use that fingerprint to look up in its cache. If the cache entry exists, it can declare a cache hit. In the example above, if all inputs are the same as in the first build, we should end up with a fingerprint `F`. We look up in the metadata part of the cache (file `metadata/F`) and find that it exists. This means we have a cache hit. We can fetch that metadata `M` from the cache and find that it describes the output with path `O` and hash `H`. The plugin would then copy `content/H` to `O` and return the deserialized `BuildResult R` contained in `M` to MSBuild. If the inputs were not the same as in the first build, for example if a `Compile` item (a .cs file) changed, the fingerprint would be something else besides `F` and so would not have corresponding cache entries for it, indicating a cache miss. This will then go through the "cache add" scenario described above to populate the cache with the new fingerprint. -# Caveats +## Caveats + - Without the "cache add" scenario enabled, the content which powers "cache get" must be populated by some external entity, for example some higher-order build engine. - Absolute paths circulating through the saved build results - Absolute paths will likely break the build, since they'd be captured on the machine that writes to the cache. @@ -193,6 +201,7 @@ If the inputs were not the same as in the first build, for example if a `Compile - Msbuild /graph requires that the [target inference protocol](static-graph.md#inferring-which-targets-to-run-for-a-project-within-the-graph) is good enough. - Small repos will probably be slower with plugin implementations that access the network. Remote distribution and caching will only be worth it for repos that are large enough. -# Potential future work of dubious value +## Potential future work of dubious value + - Enable plugins to work with the just-in-time top down msbuild traversal that msbuild natively does when it's not using `/graph`. - Extend the project cache API to allow skipping individual targets or tasks instead of entire projects. This would allow for smaller specialized plugins, like plugins that only know to distribute, cache, and skip CSC.exe calls. diff --git a/documentation/specs/question.md b/documentation/specs/question.md index 84fac8ed9f3..f46aa910af8 100644 --- a/documentation/specs/question.md +++ b/documentation/specs/question.md @@ -8,15 +8,19 @@ Question switch ask if the next build is up-to-date. It will start a build, but [Fast Up-To-Date Check](https://github.com/dotnet/project-system/blob/cd275918ef9f181f6efab96715a91db7aabec832/docs/up-to-date-check.md) is a system that is implemented by the Project System, that decides, if it needs to run MSBuild. MSBuild takes a non-trival amount of time to load, evaluate, and run through each target and task. Fast Up-To-Date is faster, but can be less accurate, suitable for an IDE and a human interface. It is not accurate enough for a CI. ## Usage + Question mode is designed to be used on the command line. Run your normal build, then run again with /question. -``` + +```cmd msbuild /p:Configuration=Debug Project1.csproj /bl:build.binlog msbuild /p:Configuration=Debug Project1.csproj /bl:incremental.binlog /question ``` + If there are no errors, then your build is up-to-date. If there are errors, then investigate the error. See common errors below. Keep both logs to help with your investigation. ## Custom Tasks + Task author can implement the optional `IIncrementalTask` interface that will expose `FailIfNotIncremental`. `FailIfNotIncremental` is true when /question switch is used. The custom task will need to decide how it want to handle their behavior. For example. If there is already a message describing why the task cannot be skipped, then simply convert the message to a error. Remember to return false to stop the build. For the best reproducibility, do not modify any files on disk. ```C# @@ -32,6 +36,7 @@ else ``` ## Shipping Tasks + When question switch is used, it will modify the shipping task with these behavior. Note: this is still experimental and can change. `Exec` @@ -73,11 +78,11 @@ Error when SkipUnchangedFiles is true. `ZipDirectory` Error if the destination zip file doesn't exists. - ## Common Error + - **Typographical error**. Spelling, casing, or incorrect path. Check if the target inputs and outputs real files. - Inputs and Outputs are sometimes used for Cross Product. Try to move all to Outputs. If not possible, use Returns instead of Inputs. - **Double Checks**. Since target and task could be incremental, if both are implemented, then it can lead task skipping but not the task. For example, a Target has inputs A and outputs B. If A is newer, than B, then the target will start. If the task compares the content of A and B and deems nothing has changed, then B is not updated. If such case, this leads to target rerunning. - **Exec Task** are not Skipable, thus they should be wrapped with Target Inputs and Outputs or other systems. For backwards compatibility, Question will not issue an error. - **FileWritten**. The common clean system will remove files that aren't in the FileWritten itemgroup. Sometimes task output won't be add to FileWritten itemgroup. -- **Build, then Build**. Sometimes, a 2nd build will break up to date. Question after the 2nd build. \ No newline at end of file +- **Build, then Build**. Sometimes, a 2nd build will break up to date. Question after the 2nd build. diff --git a/documentation/specs/rar-core-scenarios.md b/documentation/specs/rar-core-scenarios.md index 3fb19ad7846..48af2458d6d 100644 --- a/documentation/specs/rar-core-scenarios.md +++ b/documentation/specs/rar-core-scenarios.md @@ -139,6 +139,7 @@ effect. Be it eliminating allocations, simplifying tight loops, reordering cases address the elephant in the room: the file I/O resulting from scanning of assemblies, checking their timestamps, and reading/writing on-disk caches. For regular project references the system works as about as efficient as possible. + - In a cold scenario, where there is no state in memory or on disk, the referenced assembly file has to be scanned for its name and dependencies. - In a warm scenario, where there is no state in memory but a disk cache exists, the assembly name and dependencies are read from the cache, together with the corresponding timestamp which is compared to the current timestamp of the assembly file. If they match the cached data is used. @@ -188,6 +189,7 @@ enlistment - the system may prime by building the full solution and then the dev cache and get sub-optimal first-time build performance. Saving of the per-project disk cache may be further optimized by + - Keeping the timestamp of the cache file in memory and skipping the save if the relevant cache items haven't become dirty (i.e. the dependencies have not changed) *and* the timestamp of the cache file hasn't changed since the last save. In hot inner loop scenarios this would reduce the save to a timestamp check. - Saving the file asynchronously, i.e. not blocking the build on completing the save operation. diff --git a/documentation/specs/remote-host-object.md b/documentation/specs/remote-host-object.md index 536535637d2..a64a8836ab4 100644 --- a/documentation/specs/remote-host-object.md +++ b/documentation/specs/remote-host-object.md @@ -4,8 +4,9 @@ A remote host object must be registered in the [Running Object Table (ROT)](http [The registration of interfaces](https://docs.microsoft.com/en-us/dotnet/framework/interop/how-to-register-primary-interop-assemblies) is the only thing interop with COM that need extra care. There are 3 interfaces involved in out-of-proc tasks work: `IVsMSBuildTaskFileManager`, `IPersistFileCheckSum` and `ITaskHost`. `IVsMSBuildTaskFileManager` and `IPersistFileCheckSum` are registered globally in Windows registry by VS existing setup. `ITaskHost` is also configured in VS using registration-free. So the only work is to configure it using registration-free in **MSBuild**. That results the change in msbuild.exe.manifest file and the change to generate tlb file for ITaskHost. -## Annotated additions to the msbuild.exe.manifest file. -``` +## Annotated additions to the msbuild.exe.manifest file + +```xml -- Location of the tlb, it should be in the same directory as msbuild.exe MySdkResolver.dll MySdk.* @@ -23,11 +29,12 @@ By default the resolvers are general. To make all the resolvers from some dll sp Note, that the manifest file, if exists, from ChangeWave 17.4 would have preference over the dll. The sdk discovery works according to the following algorithm: -- First try locate the manifest file and use it. -- If it is not found, we try to locate the dll in the resolver's folder. + +- First try locate the manifest file and use it. +- If it is not found, we try to locate the dll in the resolver's folder. Both xml and dll name should match the following name pattern `...\SdkResolvers\(ResolverName)\(ResolverName).(xml/dll)`. -### Failed SDK Resolution +## Failed SDK Resolution > 🚧 Note > @@ -35,15 +42,15 @@ Both xml and dll name should match the following name pattern `...\SdkResolvers\ SDK resolvers previously attempted to continue when one critically fails (throws an unhandled exception). This lead to misleading error messages such as: -``` +```text warning MSB4242: The SDK resolver "Microsoft.DotNet.MSBuildWorkloadSdkResolver" failed to run. 's' is an invalid start of a property name. Expected a '"'. LineNumber: 14 | BytePositionInLine: 8. error MSB4236: The SDK 'Microsoft.NET.SDK.WorkloadAutoImportPropsLocator' specified could not be found. [C:\foo\bar.csproj] ``` `MSB4236` is a red herring while `MSB4242` is the real error despite being logged as a warning. Because of this, SDK resolvers now fail the build _immediately_ upon unhandled exceptions. These exceptions are propogated as `SdkResolverException`s, and `MSB4242` has been promoted to an error code. The new error message appears like so: -``` -C:\src\temp\8-18>"C:\foo\dotnet-sdk-6.0.100-preview.7.21379.14-win-x64\dotnet.exe" build +```text +C:\src\temp\8-18>"C:\foo\dotnet-sdk-6.0.100-preview.7.21379.14-win-x64\dotnet.exe" build Microsoft (R) Build Engine version 17.0.0-dev-21420-01+5df152759 for .NET Copyright (C) Microsoft Corporation. All rights reserved. @@ -56,4 +63,4 @@ C:\foo\bar.csproj : error MSB4242: SDK Resolver Failure: "The SDK resolver "Micr 1 Error(s) Time Elapsed 00:00:00.15 -``` \ No newline at end of file +``` diff --git a/documentation/specs/single-project-isolated-builds.md b/documentation/specs/single-project-isolated-builds.md index 75b15fc5b82..8a8f9e68413 100644 --- a/documentation/specs/single-project-isolated-builds.md +++ b/documentation/specs/single-project-isolated-builds.md @@ -19,25 +19,27 @@ In a build, the input and output cache files have the same lifetime as the `Conf When loading input cache files, MSBuild merges incoming instances of `ConfigCache`s and `ResultsCache`s into one instance of each with the help of the [`CacheAggregator`](https://github.com/dotnet/msbuild/blob/51df47643a8ee2715ac67fab8d652b25be070cd2/src/Build/BackEnd/BuildManager/CacheAggregator.cs#L15), which enforces the following constraints: + - No duplicate cache entries - Bijection: - - `ConfigCache.Entries.Size == ResultsCache.Entries.Size` - - `BuildResult.ConfigurationId` == `BuildRequestConfiguration.ConfigurationId` + - `ConfigCache.Entries.Size == ResultsCache.Entries.Size` + - `BuildResult.ConfigurationId == BuildRequestConfiguration.ConfigurationId` Note that the output cache file contains a single `BuildResult` with the `TargetResult`s from the project specified to be built in the `BeginBuild` / `EndBuild` session, as any `BuildResult`s obtained through isolation exemption are excluded to prevent potential duplicate input cache entries; Entries from input caches are not transferred to the output cache. -Input cache entries are separated from output cache entries with the composite caches [`ConfigCacheWithOverride`](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ConfigCacheWithOverride.cs) and [`ResultsCacheWithOverride`](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ResultsCacheWithOverride.cs). Each composite cache contains two underlying caches: a cache where input caches files are loaded into (the override cache), and a cache where new results are written into (the current cache).* In the `ConfigCacheWithOverride`, these caches are instances of `ConfigCache`s and, in the `ResultsCacheWithOverride`, these caches are instances of `ResultsCache`s. A query for a cache entry is first attempted from the override cache and, if unsatisfied, a second attempt is made from the current cache. Writes are only written to the current cache, never into the override cache.* It is illegal for both the current cache and override cache to contain entries for the same project configuration, a constraint that is checked by the two override caches on each cache query. +Input cache entries are separated from output cache entries with the composite caches [`ConfigCacheWithOverride`](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ConfigCacheWithOverride.cs) and [`ResultsCacheWithOverride`](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ResultsCacheWithOverride.cs). Each composite cache contains two underlying caches: a cache where input caches files are loaded into (the override cache), and a cache where new results are written into (the current cache). *In the `ConfigCacheWithOverride`, these caches are instances of `ConfigCache`s and, in the `ResultsCacheWithOverride`, these caches are instances of `ResultsCache`s. A query for a cache entry is first attempted from the override cache and, if unsatisfied, a second attempt is made from the current cache. Writes are only written to the current cache, never into the override cache.* It is illegal for both the current cache and override cache to contain entries for the same project configuration, a constraint that is checked by the two override caches on each cache query. ## Isolation Implementation [Isolation constraints](static-graph.md##single-project-isolated-builds) are implemented in the `Scheduler` and `TaskBuilder`. [`TaskBuilder.ExecuteInstantiatedTask`](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs#L743) ensures that the `MSBuild` task is only called on projects declared in a `ProjectReference` item. [`Scheduler.CheckIfCacheMissOnReferencedProjectIsAllowedAndErrorIfNot`](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Components/Scheduler/Scheduler.cs#L1818) ensures that all `MSBuild` tasks are cache hits. ### Isolation Exemption + The `Scheduler` [skips isolation constraints](static-graph.md#exempting-references-from-isolation-constraints) on project references via the: -* `GraphIsolationExemptReference` item. The `RequestBuilder` sets the `SkipStaticGraphIsolationConstraints` property of a `BuildRequest` to `true` if the `RequestBuilder` matches it against a `GraphIsolationExemptReference` item defined in the calling project. Additionally, the `RequestBuilder` marks the `BuildRequest`'s corresponding `BuildRequestConfiguration` as exempt to allow the `TaskBuilder` to verify exemption from isolation constraints. +- `GraphIsolationExemptReference` item. The `RequestBuilder` sets the `SkipStaticGraphIsolationConstraints` property of a `BuildRequest` to `true` if the `RequestBuilder` matches it against a `GraphIsolationExemptReference` item defined in the calling project. Additionally, the `RequestBuilder` marks the `BuildRequest`'s corresponding `BuildRequestConfiguration` as exempt to allow the `TaskBuilder` to verify exemption from isolation constraints. -* `isolate:MessageUponIsolationViolation` switch. The `RequestBuilder` sets the `SkipStaticGraphIsolationConstraints` property of _every_ `BuildRequest` to `true`. The `TaskBuilder` verifies exemption from isolation constraints just by the switch value. +- `isolate:MessageUponIsolationViolation` switch. The `RequestBuilder` sets the `SkipStaticGraphIsolationConstraints` property of _every_ `BuildRequest` to `true`. The `TaskBuilder` verifies exemption from isolation constraints just by the switch value. -\* Except in the following scenario when a `ProjectReference` is exempted from isolation constraints: a dependency project A outputs a cache file F containing a `BuildResult` with `TargetResult`s Tcached for targets t1, t2, ..., tm and a dependent project B uses F as an input cache file but builds and obtains the `TargetResult`s Tnew for targets tm + 1, tm + 2, ..., tn such that 0 < m < n. In this case, Tnew will be placed into the `ResultsCache` containing Tcached to enforce no overlap between the override and current caches in the `ConfigCacheWithOverride`. \ No newline at end of file +\* Except in the following scenario when a `ProjectReference` is exempted from isolation constraints: a dependency project A outputs a cache file F containing a `BuildResult` with `TargetResult`s Tcached for targets t1, t2, ..., tm and a dependent project B uses F as an input cache file but builds and obtains the `TargetResult`s Tnew for targets tm + 1, tm + 2, ..., tn such that 0 < m < n. In this case, Tnew will be placed into the `ResultsCache` containing Tcached to enforce no overlap between the override and current caches in the `ConfigCacheWithOverride`. diff --git a/documentation/specs/static-graph.md b/documentation/specs/static-graph.md index 49acebe57fe..6112072349a 100644 --- a/documentation/specs/static-graph.md +++ b/documentation/specs/static-graph.md @@ -85,6 +85,7 @@ Static graph functionality can be used in three ways: ## Project Graph ### Constructing the project graph + Calculating the project graph will be very similar to the MS internal build engine's existing Traversal logic. For a given evaluated project, all project references will be identified and recursively evaluated (with deduping). Project references are identified via the `ProjectReference` item. @@ -112,14 +113,16 @@ Multitargeting refers to projects that specify multiple build dimensions applica Multitargeting is implemented by having a project reference itself multiple times, once for each combination of multitargeting global properties. This leads to multiple evaluations of the same project, with different global properties. These evaluations can be classified in two groups -1. Multiple inner builds. Each inner build is evaluated with one set of multitargeting global properties (e.g. the `TargetFramework=net472` inner build, or the `TargetFramework=netcoreapp2.2` inner build). -2. One outer build. This evaluation does not have any multitargeting global properties set. It can be viewed as a proxy for the inner builds. Other projects query the outer build in order to learn the set of valid multitargeting global properties (the set of valid inner builds). When the outer build is also the root of the project to project graph, the outer build multicasts the entry target (i.e. `Build`, `Clean`, etc) to all inner builds. + +1. Multiple inner builds. Each inner build is evaluated with one set of multitargeting global properties (e.g. the `TargetFramework=net472` inner build, or the `TargetFramework=netcoreapp2.2` inner build). +2. One outer build. This evaluation does not have any multitargeting global properties set. It can be viewed as a proxy for the inner builds. Other projects query the outer build in order to learn the set of valid multitargeting global properties (the set of valid inner builds). When the outer build is also the root of the project to project graph, the outer build multicasts the entry target (i.e. `Build`, `Clean`, etc) to all inner builds. In order for the graph to represent inner and outer builds as nodes, it imposes a contract on what multitargeting means, and requires the multitargeting supporting SDKs to implement this contract. Multitargeting supporting SDKs MUST implement the following properties and semantics: + - `InnerBuildProperty`. It contains the property name that defines the multitargeting build dimension. - `InnerBuildPropertyValues`. It contains the property name that holds the possible values for the `InnerBuildProperty`. - Project classification: @@ -136,6 +139,7 @@ These specific rules represent the minimal rules required to represent multitarg For example, `InnerBuildProperty` could become `InnerBuildProperties` for SDKs where there's multiple multitargeting global properties. For example, here is a trimmed down `Microsoft.Net.Sdk` multitargeting project: + ```xml @@ -152,14 +156,17 @@ For example, here is a trimmed down `Microsoft.Net.Sdk` multitargeting project: ``` To summarize, there are two main patterns for specifying build dimensions: + 1. Multitargeting based. A multitargeting project self describes supported build dimensions. In this case the SDK needs to specify the multitargeting build dimensions. The graph then extracts innerbuilds from a given outer build. For example, the `TargetFramework` build dimension gets specified this way. 2. Global Property based: A top level set of global properties get applied to the graph entrypoints and get propagated downward through the graph. For example, the `Configuration` and `Platform` build dimensions get specified this way. Why does an outerbuild need to generate speculative edges to all of its innerbuilds? Why can't it use nuget to prune the speculative edges down to the compatible set? + - One big design constraint we imposed on static graph was to keep it agnostic of SDK implementation details. So the graph must not know about particular details of one language's SDK. We wanted a generic design that all language SDKs can leverage. We considered that calling nuget to get the compatible TargetFramework values breaks this rule, as both the concept of "nuget" and the concept of "TargetFramework" are implementation details of the .net SDK. If someone were to write a Java SDK, would "calling nuget to get the compatible TargetFramework" still be relevant? A solution to this is to allow SDKs to configure the graph with an extension point on "how to collapse multiple speculative innerbuild edges into a smaller compatible set", but we didn't have the time to design it yet. - There is a conflicting need between build everything or just building a "TF slice" through the graph. Outer loop builds (CI builds) that publish binaries need to build all the packages for all the supported TFs, so they need the graph to express all possible combinations. Inner loop builds (dev-at-work) can be sliced down to only the TF that the dev is working on in order to reduce build times. Again, we didn't have time to design how to express these two things so we went with "express everything" because that allows both scenarios to work. ### Executing targets on a graph + When building a graph, project references should be built before the projects that reference them, as opposed to the existing msbuild scheduler which builds projects just in time. For example if project A depends on project B, then project B should build first, then project A. Existing msbuild scheduling would start building project A, reach an MSBuild task for project B, yield project A, build project B, then resume project A once unblocked. @@ -169,6 +176,7 @@ Building in this way should make better use of parallelism as all CPU cores can Note that graph cycles are disallowed, even if they're using disconnected targets. This is a breaking change, as today you can have two projects where each project depends on a target from the other project, but that target doesn't depend on the default target or anything in its target graph. #### Command line + `msbuild /graph` - msbuild will create a static graph from the entry point project and build it in topological order with the specified targets. Targets to call on each node are inferred via the rules in [this section](#inferring-which-targets-to-run-for-a-project-within-the-graph). #### APIs @@ -176,11 +184,12 @@ Note that graph cycles are disallowed, even if they're using disconnected target [BuildManager.PendBuildRequest(GraphBuildRequestData requestData)](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/BuildManager/BuildManager.cs#L676) ### Inferring which targets to run for a project within the graph + In the classic MSBuild build (i.e. execution of targets), the referencing project chooses which targets to call on the referenced projects and may call into a project multiple times with different target lists and global properties (examples in [project reference protocol](../ProjectReference-Protocol.md)). This is a top-down traversal of dependencies. These calls are made via the [MSBuild task](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-task?view=vs-2019). When building a graph, projects are built before the projects that reference them. This is a bottom-up traversal. Therefore the graph needs to determine the list of targets to execute on a specific project `B` **before** building the referencing projects that reference `B`. The static graph contains the structural information on which reference projects a referencing project depends on. But it does not contain information on what "depends" means. At build time "depends" means that a referencing evaluated project will call a subset of reference evaluations with some targets. Subset because the static graph is an inferred graph, therefore there are ambiguities during graph construction, and thus it needs to be conservative and represent a superset of the "runtime graph". The "runtime graph" is the actual graph that gets executed during a real build. We cannot know the runtime graph because that would require us to analyze msbuild xml code inside of targets in order to find the `MSBuild task` invocations. This means doing heavy program analysis, like symbolic execution. That would make things very complicated, slower, and would probably introduce even more ambiguity, so a larger superset conservative graph. So we kept it simple and only looked at evaluation time msbuild xml code (i.e. msbuild xml code outside of `` elements). To summarize, the static graph does not have insights into the `MSBuild task` callsites. It does not know callsite specific information such as the `Targets="Foo;Bar"` or `Properties="Foo=Bar"` `MSBuild task` attributes. -Since the graph does not have access to MSBuild task callsites, it does not know what targets will get called for a given graph edge. +Since the graph does not have access to MSBuild task callsites, it does not know what targets will get called for a given graph edge. To infer target information we use a flow analysis to propagate target information down the graph. The flow analysis uses the `ProjectReferenceTargets` protocol (described further down) to infer how one incoming target on a graph node (e.g. `Build`) generates multiple outgoing targets to its referenced nodes (e.g. `GetTargetFrameworks`, `GetNativeManifest`, `Build`). SDKs **must** explicitly describe the project-to-project calling patterns via the `ProjectReferenceTargets` protocol in such a way that a graph based build can correctly infer the entry targets for a graph node. @@ -189,7 +198,7 @@ Each project needs to specify the project reference protocol targets it supports For example, a simple recursive rule would be `A -> A`, which says that a project called with target `A` will call target `A` on its referenced projects. Here's an example execution with two nodes: -``` +```text Execute target A+-->Proj1 A->A + | @@ -202,11 +211,12 @@ Execute target A+-->Proj1 A->A Proj1 depends on Proj2, and we want to build the graph with target `A`. Proj1 gets inspected for the project reference protocol for target `A` (represented to the right of Proj1). The protocol says the referenced projects will be called with `A`. Therefore Proj2 gets called with target `A`. After Proj2 builds, Proj1 then also builds with `A` because Proj1 is an entry point and `A` is what was requested by the user. A project reference protocol may contain multiple targets, for example `A -> B, A`. This means that building `A` on the referencing project will lead to `B` and `A` getting called on the referenced projects. If all nodes in the graph repeat the same rule, then the rule is repeated recursively on all nodes. However, a project can choose to implement the protocol differently. In the following example, the entry targets are: + - Proj4 is called with targets `B, A, C, D`. On multiple references, the incoming targets get concatenated. The order of these target lists does not matter, as MSBuild has non-deterministic p2p ordering, however the order within the target lists does. IE. `B, A, C, D` and `C, D, B, A` are valid, while `A, B, C, D` is not. - Proj3 and Proj2 get called with `B, A`, as specified by the rule in Proj1. - Proj1 builds with `A`, because it's the root of the graph. -``` +```text A+-->Proj1 A->B, A / \ B, A / \ B, A @@ -238,6 +248,7 @@ Here are the rules for the common protocols: `Rebuild` actually calls `Clean` and `Build`, which in turn uses the concatenation of the `Clean` and `Build` mappings. `GetTargetFrameworks` is repeated so only the first call to it remains in the final target list. Restore is a composition of two rules: + - `Restore -> _IsProjectRestoreSupported, _GenerateRestoreProjectPathWalk, _GenerateRestoreGraphProjectEntry` - `_GenerateRestoreProjectPathWalk -> _IsProjectRestoreSupported, _GenerateRestoreProjectPathWalk, _GenerateRestoreGraphProjectEntry` @@ -258,22 +269,24 @@ We'll represent the project reference protocols as `ProjectReferenceTargets` ite #### Multitargeting details A multitargeting project can get called with different targets for the outer build and the inner builds. In this case, the `ProjectReferenceTargets` items containing targets for the outer build are marked with the `OuterBuild=true` metadata. Here are the rules for how targets from `ProjectReferenceTargets` get assigned to different project types: - - *Outer build*: targets with `OuterBuild=true` metadata - - *Dependent inner build*: targets without `OuterBuild=true` metadata - - *Standalone inner build*: the same as non multitargeting builds. - - *Non multitargeting build*: concatenation of targets with `OuterBuild=true` metadata and targets without `OuterBuild=true` metadata + +- *Outer build*: targets with `OuterBuild=true` metadata +- *Dependent inner build*: targets without `OuterBuild=true` metadata +- *Standalone inner build*: the same as non multitargeting builds. +- *Non multitargeting build*: concatenation of targets with `OuterBuild=true` metadata and targets without `OuterBuild=true` metadata **OPEN ISSUE:** Current implementation does not disambiguate between the two types of inner builds, leading to overbuilding certain targets by conservatively treating both inner build types as standalone inner builds. For example, consider the graph of `A (non multitargeting) -> B (multitargeting with 2 innerbuilds) -> C (standalone inner build)`, with the following target propagation rules: -``` + +```text A -> Ao when OuterBuild=true A -> Ai, A ``` According to the graph construction rules defined in the [multitargeting section](#multitargeting), we get the following graph, annotated with the target propagation for target `A`. -``` +```text A+-->ProjA / | \ / | \ @@ -294,6 +307,7 @@ According to the graph construction rules defined in the [multitargeting section ``` ### Underspecified graphs + The intention is that the project graph and the target lists for each node be exactly correct, however MSBuild is quite flexible and particular projects or project types may not adequately describe these for the project graph. If a project calls into another project which either isn't represented in the graph or with a target list which isn't represented by the graph, it will fall back to classical MSBuild behavior and execute that target on the project reference just-in-time. This has the consequence of still requiring all project state be kept in memory in case any arbitrary project wants to execute targets on any other arbitrary project. @@ -301,6 +315,7 @@ If a project calls into another project which either isn't represented in the gr To enable further optimizations (and strictness), graph builds can run [isolated](#isolated-builds) which enforces that the graph be entirely accurate. ### Public API + This is a proposal for what the public API for ProjectGraph may look like: ```csharp @@ -360,6 +375,7 @@ namespace Microsoft.Build.Experimental.Graph ``` ## Isolated builds + Building a project in isolation means enforcing the constraint that whenever a graph node is built, all the target calls that it does on its references **do not execute** because their results are already available. This means that any `BuildResult` objects for project references must be precomputed and somehow provided as inputs to the referencing project. If a project uses the MSBuild task, the build result must be in MSBuild's build result cache instead of just-in-time executing targets on that referenced project. If it is not in the build result cache, an error will be logged and the build will fail. If the project is calling into itself either via `CallTarget` or the MSBuild task with a different set of global properties, this will be allowed to support multitargeting and other build dimensions implemented in a similar way. @@ -367,6 +383,7 @@ If a project uses the MSBuild task, the build result must be in MSBuild's build Because referenced projects and their entry targets are guaranteed to be in the cache, they will not build again. Therefore we do not need to set `/p:BuildProjectReferences=false` or any other gesture that tells SDKs to not do recursive operations. ### Isolated graph builds + When building a graph in isolated mode, the graph is used to traverse and build the projects in the right order, but each individual project is built in isolation. The build result cache will just be in memory exactly as it is today, but on cache miss it will error. This enforces that both the graph and target mappings are complete and correct. Furthermore, running in this mode enforces that each `(project, global properties)` pair is executed only once and must execute all targets needed by all projects which reference that node. This gives it a concrete start and end time, which leads to some potential perf optimizations, like garbage collecting all project state (except the build results) once it finishes building. This can greatly reduce the memory overhead for large builds. @@ -374,9 +391,11 @@ Furthermore, running in this mode enforces that each `(project, global propertie This discrete start and end time also allows for easy integration with [I/O Tracking](#io-tracking) to observe all inputs and outputs for a project. Note however that I/O during target execution, particular target execution which may not normally happen as part of a project's individual build execution, would be attributed to the project reference project rather the project with the project reference. This differs from today's behavior, but seems like a desirable difference anyway. ### Single project isolated builds + When building a single project in isolation, all project references' build results must be provided to the project externally. Specifically, the results will need to be [deserialized](#deserialization) from files and loaded into the build result cache in memory. When MSBuild runs in isolation mode, it fails the build when it detects: + 1. `MSBuild` task calls which cannot be served from the cache. Cache misses are illegal. 2. `MSBuild` task calls to project files which were not defined in the `ProjectReference` item. @@ -389,16 +408,20 @@ These incremental builds could be extended to the entire graph by keeping a proj Details on how isolation and cache files are implemented in MSBuild can be found [here](./static-graph-implementation-details.md). #### APIs + Cache file information is provided via [`BuildParameters`](https://github.com/dotnet/msbuild/blob/2d4dc592a638b809944af10ad1e48e7169e40808/src/Build/BackEnd/BuildManager/BuildParameters.cs#L746-L764). Input caches are applied in `BuildManager.BeginBuild`. Output cache files are written in `BuildManager.EndBuild`. Thus, the scope of the caches are one `BuildManager` `BeginBuild`/`EndBuild` session. Isolation constraints are turned on via [`BuildParameters.IsolateProjects`](https://github.com/dotnet/msbuild/blob/b111470ae61eba02c6102374c2b7d62aebe45f5b/src/Build/BackEnd/BuildManager/BuildParameters.cs#L742). Isolation constraints are also automatically turned on if either input or output cache files are used, except when the `isolate:MessageUponIsolationViolation` switch is used. #### Command line + Caches are provided to MSBuild.exe via the multi value `/inputResultsCaches` and the single value `/outputResultsCache`. Isolation constraints are turned on via `/isolate` (they are also implicitly activated when either input or output caches are used). #### Exempting references from isolation constraints + In certain situations one may want to exempt a reference from isolation constraints. A few potential cases: + - debugging / onboarding to isolation constraints - exempting references whose project files are generated at build times with random names (for example, each WPF project, before the Build target, generates and builds a helper .csproj with a random file name) - relaxing constraints for MSBuild task calling patterns that static graph cannot express (for exemple, if a project is calculating references, or the targets to call on references, at runtime via an arbitrary algorithm) @@ -422,6 +445,7 @@ If multiple projects need to exempt the same reference, all of them need to add For now, self-builds (a project building itself with different global properties) are also exempt from isolation constraints, but this behaviour is of dubious value and might be changed in the future. ## I/O Tracking + To help facilitate caching of build outputs by a higher-order build engine, MSBuild needs to track all I/O that happens as part of a build. **OPEN ISSUE:** This isn't actually true in most scenarios. Today the MS internal build engine can wrap any arbitrary process to track the I/O that happens as part of its execution as well as its children. That's sufficient for all scenarios except compiler servers or an MSBuild server (see below). Additionally, if the MS internal build engine supports any other build type besides MSBuild (or older versions of MSBuild), it will still need to be able to detour the process itself anyway. @@ -429,6 +453,7 @@ To help facilitate caching of build outputs by a higher-order build engine, MSBu **NOTE**: Based on the complexity and challenges involved, the feature of I/O tracking in MSBuild is currently on hold and not scheduled to be implemented. This section intends to describe these challenges and be a dump of the current thinking on the subject. ### Detours + [Detours](https://github.com/microsoft/detours) will be used to intercept Windows API calls to track I/O. This is the same technology that [FileTracker](../../src/Utilities/TrackedDependencies/FileTracker.cs) and [FullTracking](../../src/Build/BackEnd/Components/RequestBuilder/FullTracking.cs) use as well as what the MS internal build engine ("BuildXL Tracker") uses to track I/O. Today FileTracker and FullTracking are currently a bit specific to generating tlogs, and do not collect all the I/O operations we would want to collect like directory enumerations and probes. Additionally, the BuildXL Tracker implementation does not currently have the ability to attach to the currently running process. @@ -438,11 +463,13 @@ Either existing implementation would require some work to fit this scenario. Bec Elsewhere in this spec the final Detours-based file tracking implementation will simply be referred to as "Tracker". ### Isolation requirement + I/O Tracking will only be available when running isolated builds, as the current implementation of project yielding in MSBuild makes it exceedingly difficult to attribute any observed I/O to the correct project. Isolated builds make this feasible since each MSBuild node will be building exactly one project configuration at any given moment and each project configuration has a concrete start and stop time. This allows us to turn on I/O tracking for the MSBuild process and start and stop tracking with the project start and stop. **OPEN ISSUE:** For graph-based isolated builds, project evaluation happens in parallel on the main node. Any I/O that happens as part of evaluation should be reported for that specific project, but there's no good way to do that here. ### Tool servers + Tool servers are long-lived processes which can be reused multiple times across builds. This causes problems for Tracker, as that long-lived process is not a child process of MSBuild, so many I/O operations would be missed. For example, when `SharedCompilation=true`, the Roslyn compiler (csc.exe) will launch in server mode. This causes the `Csc` task to connect to any existing csc.exe process and pass the compilation request over a named pipe. diff --git a/documentation/specs/task-isolation-and-dependencies.md b/documentation/specs/task-isolation-and-dependencies.md index 2ec96c8eb18..2994335dfc8 100644 --- a/documentation/specs/task-isolation-and-dependencies.md +++ b/documentation/specs/task-isolation-and-dependencies.md @@ -1,39 +1,50 @@ # Task isolation + ## Problem definition + Tasks in MSBuild are dynamically loaded assemblies with potentially separate and colliding dependency trees. Currently MSBuild on .NET Core has no isolation between tasks and as such only one version of any given assembly can be loaded. Prime example of this is Newtonsoft.Json which has multiple versions, but all the tasks must agree on it to work. This problem is also described in #1754. ## Solution + Use [`AssemblyLoadContext`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.loader.assemblyloadcontext?view=netcore-2.2) (ALC) to provide binding isolation for task assemblies. Each task assembly would be loaded into its own ALC instance. -* The ALC would resolve all dependencies of the task assemblies (see dependency resolution below) -* ALC would fallback to the Default for dependencies which the assembly doesn't carry with itself (frameworks and so on) -* ALC would probably have to forcefully fallback for MSBuild assemblies since it's possible that tasks will carry these, but the system requires for the MSBuild assemblies to be shared. + +- The ALC would resolve all dependencies of the task assemblies (see dependency resolution below) +- ALC would fallback to the Default for dependencies which the assembly doesn't carry with itself (frameworks and so on) +- ALC would probably have to forcefully fallback for MSBuild assemblies since it's possible that tasks will carry these, but the system requires for the MSBuild assemblies to be shared. We also want to load groups of tasks which belong together into the same ALC (for example based on their location on disk) to improve performance. This will need some care as there's no guarantee that two random tasks have compatible dependency trees. As implemented, each task assembly is loaded into its own ALC. ## Potential risks -* Has some small probability of causing breaks. Currently all assemblies from all tasks are loaded into the default context and thus are "visible" to everybody. Tasks with following properties might not work: - * Task has a dependency on an assembly, but it doesn't declare this dependency in its .deps.json and this dependency gets loaded through some other task. This is mostly fixable by implementing probing similar to today's behavior. - * Two tasks from different assemblies which somehow rely on sharing certain types. If the new system decides to load these in isolation they won't share types anymore and might not work. -* Performance - task isolation inherently (and by design) leads to loading certain assemblies multiple times. This increases memory pressure and causes additional JITing and other related work. + +- Has some small probability of causing breaks. Currently all assemblies from all tasks are loaded into the default context and thus are "visible" to everybody. Tasks with following properties might not work: + - Task has a dependency on an assembly, but it doesn't declare this dependency in its .deps.json and this dependency gets loaded through some other task. This is mostly fixable by implementing probing similar to today's behavior. + - Two tasks from different assemblies which somehow rely on sharing certain types. If the new system decides to load these in isolation they won't share types anymore and might not work. +- Performance - task isolation inherently (and by design) leads to loading certain assemblies multiple times. This increases memory pressure and causes additional JITing and other related work. ## Additional consideration -* None of these changes would have any effect on MSBuild on .NET Framework -* Task isolation alone could be achieved on existing MSBuild + +- None of these changes would have any effect on MSBuild on .NET Framework +- Task isolation alone could be achieved on existing MSBuild # Task dependency resolution + ## Problem definition + Tasks with complex and specifically platform specific dependencies don't work out of the box. For example if a task uses [`LibGit2Sharp`](https://www.nuget.org/packages/LibGit2Sharp) package it will not work as is. `LibGit2Sharp` has native dependencies which are platform specific. While the package carries all of them, there's no built in support for the task to load the right ones. For example [source link](https://github.com/dotnet/sourcelink/blob/29b3197e824c05d03427c05d56700e4c704233e4/src/Microsoft.Build.Tasks.Git/GitLoaderContext.cs) runs into this problem. ## Solution + .NET Core uses `.deps.json` files to describe dependencies of components. It would be natural to treat task assemblies as components and use associated .deps.json file to determine their dependencies. This would make the system work nicely end to end with the .NET Core CLI/SDK and VS integration. In .NET Core 3 there's a new type [`AssemblyDependencyResolver`](https://github.com/dotnet/coreclr/blob/master/src/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyDependencyResolver.cs) which implements parsing and processing of a `.deps.json` for a component (or assembly). The usage is to create an instance of the resolver pointing to the assembly (in MSBuild case the task assembly). The resolver parses the `.deps.json` and stores the information. It exposes two methods to resolve managed and native dependencies. It was designed to be used as the underlying piece to implement custom ALC. So it would work nicely with task isolation above. ## Potential risks -* Small probability of breaking tasks which have `.deps.json` with them and those are not correct. With this change the file would suddenly be used and could cause either load failures or different versions of assemblies to get loaded. + +- Small probability of breaking tasks which have `.deps.json` with them and those are not correct. With this change the file would suddenly be used and could cause either load failures or different versions of assemblies to get loaded. ## Additional consideration -* Task dependency resolution requires APIs which are only available in .NET Core 3.0 (no plan to backport), as such MSBuild will have to target netcoreapp3.0 to use these APIs. + +- Task dependency resolution requires APIs which are only available in .NET Core 3.0 (no plan to backport), as such MSBuild will have to target netcoreapp3.0 to use these APIs. We decided not to implement `AssemblyDependencyResolver` in the .NET Core 3.x timeframe because of the uncertain impact of the change. We should reconsider in the .NET 5 timeframe. diff --git a/documentation/specs/test-target.md b/documentation/specs/test-target.md index 7726f1f6971..e5818eb6c81 100644 --- a/documentation/specs/test-target.md +++ b/documentation/specs/test-target.md @@ -1,62 +1,77 @@ -## MSBuild Test Target and Task +# MSBuild Test Target and Task + See: [MSBuild Test Target](https://github.com/dotnet/msbuild/pull/9193) -### Motivation +## Motivation + The primary motivation of the MSBuild Test Target is to offer a convienent and standardardized way for executing tests within the msbuild environment. This is inspired by the simplicity of the `dotnet test` command. The proposed command for initiating test within MSBuild would be `msbuild /t:Test` Another significatnt benefit of integrating this target is to faciliatet the caching of test executions, using MSBuild project caching capabilities. This enhancement will optimize the testing process by reducing test runs which could significantly reduce time spent building and testing, as tests would only execute, (after the initial run) if there are changes to those tests. As an example running with [MSBuildCache](https://github.com/microsoft/MSBuildCache) we can cache both build and test executions. Functionally, this means skipping test executions that have been determined to have not changed. Example usage: `msbuild /graph /restore:false /m /nr:false /reportfileaccesses /t:"Build;Test"` -### Design Overview +## Design Overview + The 'Microsoft.Common.Test.targets' file contains a stub test target. -``` + +```xml ``` + This target serves a placeholder and entry point for test target implementations. -#### Conditional Import -* This stub target is conditionally imported, determined by a condition named +### Conditional Import + +- This stub target is conditionally imported, determined by a condition named `$(UseMSBuildTestInfrastructure)`. -* This condition allows for users to opt-in to this test target, which helps to prevent breaking changes, with respect the the target name, since there are likely 'Test' targets that exist in the wild already. +- This condition allows for users to opt-in to this test target, which helps to prevent breaking changes, with respect the the target name, since there are likely 'Test' targets that exist in the wild already. The 'Microsoft.Common.CurrentVersion.targets' file contains. -``` + +```xml false ``` -#### Extensibility for Test Runners -* Test runner implemenations can hook into the provided stub using the `AfterTargets` property. -* This approach enables different test runners to extend the basic funcionarlity of the test target. + +### Extensibility for Test Runners + +- Test runner implemenations can hook into the provided stub using the `AfterTargets` property. +- This approach enables different test runners to extend the basic funcionarlity of the test target. For instance, an implementation for running VSTest would look like: -``` + +```xml ``` -#### Usage Scenario -* Users who wish to utilize this target will set the `$(UseMSBuildTestInfrastructure)` condition in their project file, rsp or via the command line. -* By executing `msbuild /t:Test`, the MSBuild engine will envoke the `Test` taget, which in turn triggers any test runner targets defined to run after it. +### Usage Scenario + +- Users who wish to utilize this target will set the `$(UseMSBuildTestInfrastructure)` condition in their project file, rsp or via the command line. +- By executing `msbuild /t:Test`, the MSBuild engine will envoke the `Test` taget, which in turn triggers any test runner targets defined to run after it. + +## Default Task Implementation -### Default Task Implementation See: [MSBuild Test Task](https://github.com/microsoft/MSBuildSdks/pull/473) -#### Nuget package for default implementaion -* The default implementation will be provided through a nuget package. -* This package will contain an MSBuild Task deigned to execute `vstest.console.exe`. +### Nuget package for default implementaion + +- The default implementation will be provided through a nuget package. +- This package will contain an MSBuild Task deigned to execute `vstest.console.exe`. + +### MSBuild Task Functionality + +- The core of this implemenation is an MSBuild task that interfaces with `vstest.console.exe`. +- This task will accept arguments as properties and pass them directly into the command line test runner. -#### MSBuild Task Functionality -* The core of this implemenation is an MSBuild task that interfaces with `vstest.console.exe`. -* This task will accept arguments as properties and pass them directly into the command line test runner. +### Using The Default Implementation -#### Using The Default Implementation -* Users would install the provided Nuget Package to incorporate it into their projects. -* Add the package to their GlobalPackageReferences or specific projects. -* Once integrated, executing `msbuild /t:Test` would trigger the MSBuild Task, ultimately executing `vstest.console.exe`. +- Users would install the provided Nuget Package to incorporate it into their projects. +- Add the package to their GlobalPackageReferences or specific projects. +- Once integrated, executing `msbuild /t:Test` would trigger the MSBuild Task, ultimately executing `vstest.console.exe`. From 1c255f6e6a0aee7f204de818ad81e5cd22e83c5b Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Fri, 28 Mar 2025 11:01:29 +0100 Subject: [PATCH 048/106] Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11283091 (#11639) Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11285593 --- src/Tasks/Resources/xlf/Strings.it.xlf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tasks/Resources/xlf/Strings.it.xlf b/src/Tasks/Resources/xlf/Strings.it.xlf index 82941a83d6b..5614177333f 100644 --- a/src/Tasks/Resources/xlf/Strings.it.xlf +++ b/src/Tasks/Resources/xlf/Strings.it.xlf @@ -2839,7 +2839,7 @@ Could not infer the type of parameter "{0}" because the attribute type is unknown. The value will be treated as a string. - Non è stato possibile inferire il tipo di parametro "{0}" perché il tipo di attributo è sconosciuto. Il valore sarà trattto come una stringa. + Non è stato possibile inferire il tipo di parametro "{0}" perché il tipo di attributo è sconosciuto. Il valore sarà tratto come una stringa. From a5c9cc849fef449fc38b2b9664bb3c64a51e4aa9 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 28 Mar 2025 10:42:57 +0000 Subject: [PATCH 049/106] [main] Update dependencies from nuget/nuget.client (#11599) This pull request updates the following dependencies From https://github.com/nuget/nuget.client Subscription: 3fe128a9-5a85-4aba-f7ba-08da008becb5 Build: 6.14.0.89 Date Produced: March 21, 2025 10:00:37 PM UTC Commit: 13550619f90e73a1f8b4b5159c6d7f268c9756d0 Branch: dev Updates: NuGet.Build.Tasks: from 6.14.0-preview.1.66 to 6.14.0-preview.1.89 --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1f86224d271..92bf0ff1e27 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -137,9 +137,9 @@ https://github.com/dotnet/arcade 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee - + https://github.com/nuget/nuget.client - 181b65dad9f440c7a31fe673abc59c258f224ada + 13550619f90e73a1f8b4b5159c6d7f268c9756d0 https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index 3093e9241f6..ce75c963a5a 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -85,7 +85,7 @@ 4.2.0-1.22102.8 9.0.0-beta.25164.2 4.14.0-3.25171.27 - 6.14.0-preview.1.66 + 6.14.0-preview.1.89 9.0.200-preview.0.24603.3 From 248a0de139af4ce7a8fbae0a20dbd5f170df8431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Provazn=C3=ADk?= Date: Fri, 28 Mar 2025 16:46:37 +0100 Subject: [PATCH 050/106] add telemetry e2e test, address code quality issues (#11602) * typo, namespaces, remove flushing intermediate data, rename * address review comments pt1, implement e2e node telemetry test * rename telemetrytests file * descriptive names and docs * fix env setup in test * when parsing env variable force . as a decimal separator * address feedback --- .../BackEnd/NodePackets_Tests.cs | 1 + .../OpenTelemetryActivities_Tests.cs | 3 +- .../OpenTelemetryManager_Tests.cs | 60 ++--- .../Telemetry_Tests.cs} | 179 ++++++++++++--- .../BackEnd/BuildManager/BuildManager.cs | 4 +- .../Components/Logging/EventSourceSink.cs | 1 + .../InternalTelemetryConsumingLogger.cs | 27 +-- .../TelemetryInfra/TelemetryDataUtils.cs | 211 ++++++++++-------- .../TelemetryForwarderProvider.cs | 1 + .../WorkerNodeTelemetryEventArgs_Tests.cs | 1 + src/Framework/IEventSource5.cs | 1 + .../Telemetry/IWorkerNodeTelemetryData.cs | 2 +- .../InternalTelemetryForwardingLogger.cs | 4 +- src/Framework/Telemetry/TaskExecutionStats.cs | 31 ++- .../Telemetry/TaskOrTargetTelemetryKey.cs | 77 +++++-- src/Framework/Telemetry/TelemetryConstants.cs | 9 + .../Telemetry/WorkerNodeTelemetryData.cs | 6 +- .../Telemetry/WorkerNodeTelemetryEventArgs.cs | 8 +- src/Framework/Traits.cs | 9 +- src/Shared/LogMessagePacketBase.cs | 1 + 20 files changed, 406 insertions(+), 230 deletions(-) rename src/Build.UnitTests/{BackEnd => Telemetry}/OpenTelemetryActivities_Tests.cs (98%) rename src/Build.UnitTests/{BackEnd => Telemetry}/OpenTelemetryManager_Tests.cs (64%) rename src/Build.UnitTests/{TelemetryTests.cs => Telemetry/Telemetry_Tests.cs} (55%) diff --git a/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs b/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs index 74963e26794..8ca50416de7 100644 --- a/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs +++ b/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs @@ -10,6 +10,7 @@ using Microsoft.Build.Execution; using Microsoft.Build.Experimental.BuildCheck; using Microsoft.Build.Framework; +using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Shared; using Xunit; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; diff --git a/src/Build.UnitTests/BackEnd/OpenTelemetryActivities_Tests.cs b/src/Build.UnitTests/Telemetry/OpenTelemetryActivities_Tests.cs similarity index 98% rename from src/Build.UnitTests/BackEnd/OpenTelemetryActivities_Tests.cs rename to src/Build.UnitTests/Telemetry/OpenTelemetryActivities_Tests.cs index cd041632de2..5616f614530 100644 --- a/src/Build.UnitTests/BackEnd/OpenTelemetryActivities_Tests.cs +++ b/src/Build.UnitTests/Telemetry/OpenTelemetryActivities_Tests.cs @@ -8,8 +8,9 @@ using System.Text; using Xunit; using Shouldly; +using Microsoft.Build.Framework.Telemetry; -namespace Microsoft.Build.Framework.Telemetry.Tests +namespace Microsoft.Build.Engine.UnitTests.Telemetry { public class ActivityExtensionsTests { diff --git a/src/Build.UnitTests/BackEnd/OpenTelemetryManager_Tests.cs b/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs similarity index 64% rename from src/Build.UnitTests/BackEnd/OpenTelemetryManager_Tests.cs rename to src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs index a2ec5161797..58dbfa240ca 100644 --- a/src/Build.UnitTests/BackEnd/OpenTelemetryManager_Tests.cs +++ b/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs @@ -8,12 +8,11 @@ using Xunit.Abstractions; using Microsoft.Build.UnitTests.Shared; using Microsoft.Build.UnitTests; +using Microsoft.Build.Framework.Telemetry; -namespace Microsoft.Build.Framework.Telemetry.Tests +namespace Microsoft.Build.Engine.UnitTests.Telemetry { - /// - /// Ensures tests run serially so environment variables and the singleton do not interfere with parallel test runs. - /// + // Putting the tests to a collection ensures tests run serially by default, that's needed to isolate the manager singleton state and env vars in some telemetry tests. [Collection("OpenTelemetryManagerTests")] public class OpenTelemetryManagerTests : IDisposable { @@ -23,46 +22,13 @@ public class OpenTelemetryManagerTests : IDisposable private const string TelemetrySampleRateOverrideEnvVarName = "MSBUILD_TELEMETRY_SAMPLE_RATE"; private const string VS1714TelemetryOptInEnvVarName = "MSBUILD_TELEMETRY_OPTIN"; - private string? preTestFxOptout; - private string? preTestDotnetOptout; - private string? preTestSampleRate; - private string? preTestVS1714TelemetryOptIn; - public OpenTelemetryManagerTests() { - // control environment state before each test - SaveEnvVars(); ResetManagerState(); - ResetEnvVars(); - } - - private void SaveEnvVars() - { - preTestFxOptout = Environment.GetEnvironmentVariable(TelemetryFxOptoutEnvVarName); - preTestDotnetOptout = Environment.GetEnvironmentVariable(DotnetOptOut); - preTestSampleRate = Environment.GetEnvironmentVariable(TelemetrySampleRateOverrideEnvVarName); - preTestVS1714TelemetryOptIn = Environment.GetEnvironmentVariable(VS1714TelemetryOptInEnvVarName); - } - - private void RestoreEnvVars() - { - Environment.SetEnvironmentVariable(TelemetryFxOptoutEnvVarName, preTestFxOptout); - Environment.SetEnvironmentVariable(DotnetOptOut, preTestDotnetOptout); - Environment.SetEnvironmentVariable(TelemetrySampleRateOverrideEnvVarName, preTestSampleRate); - Environment.SetEnvironmentVariable(VS1714TelemetryOptInEnvVarName, preTestVS1714TelemetryOptIn); - } - - private void ResetEnvVars() - { - Environment.SetEnvironmentVariable(DotnetOptOut, null); - Environment.SetEnvironmentVariable(TelemetryFxOptoutEnvVarName, null); - Environment.SetEnvironmentVariable(TelemetrySampleRateOverrideEnvVarName, null); - Environment.SetEnvironmentVariable(VS1714TelemetryOptInEnvVarName, null); } public void Dispose() { - RestoreEnvVars(); } [Theory] @@ -73,7 +39,8 @@ public void Dispose() public void Initialize_ShouldSetStateToOptOut_WhenOptOutEnvVarIsTrue(string optoutVar, string value) { // Arrange - Environment.SetEnvironmentVariable(optoutVar, value); + using TestEnvironment environment = TestEnvironment.Create(); + environment.SetEnvironmentVariable(optoutVar, value); // Act OpenTelemetryManager.Instance.Initialize(isStandalone: false); @@ -82,11 +49,13 @@ public void Initialize_ShouldSetStateToOptOut_WhenOptOutEnvVarIsTrue(string opto OpenTelemetryManager.Instance.IsActive().ShouldBeFalse(); } -#if NET +#if NETCOREAPP [Fact] public void Initialize_ShouldSetStateToUnsampled_WhenNoOverrideOnNetCore() { - Environment.SetEnvironmentVariable(TelemetrySampleRateOverrideEnvVarName, null); + using TestEnvironment environment = TestEnvironment.Create(); + environment.SetEnvironmentVariable(TelemetrySampleRateOverrideEnvVarName, null); + environment.SetEnvironmentVariable(DotnetOptOut, null); OpenTelemetryManager.Instance.Initialize(isStandalone: false); @@ -101,8 +70,10 @@ public void Initialize_ShouldSetStateToUnsampled_WhenNoOverrideOnNetCore() public void Initialize_ShouldSetSampleRateOverride_AndCreateActivitySource_WhenRandomBelowOverride(bool standalone) { // Arrange - Environment.SetEnvironmentVariable(VS1714TelemetryOptInEnvVarName, "1"); - Environment.SetEnvironmentVariable(TelemetrySampleRateOverrideEnvVarName, "1.0"); + using TestEnvironment environment = TestEnvironment.Create(); + environment.SetEnvironmentVariable(VS1714TelemetryOptInEnvVarName, "1"); + environment.SetEnvironmentVariable(TelemetrySampleRateOverrideEnvVarName, "1.0"); + environment.SetEnvironmentVariable(DotnetOptOut, null); // Act OpenTelemetryManager.Instance.Initialize(isStandalone: standalone); @@ -115,11 +86,12 @@ public void Initialize_ShouldSetSampleRateOverride_AndCreateActivitySource_WhenR [Fact] public void Initialize_ShouldNoOp_WhenCalledMultipleTimes() { - Environment.SetEnvironmentVariable(DotnetOptOut, "true"); + using TestEnvironment environment = TestEnvironment.Create(); + environment.SetEnvironmentVariable(DotnetOptOut, "true"); OpenTelemetryManager.Instance.Initialize(isStandalone: true); var state1 = OpenTelemetryManager.Instance.IsActive(); - Environment.SetEnvironmentVariable(DotnetOptOut, null); + environment.SetEnvironmentVariable(DotnetOptOut, null); OpenTelemetryManager.Instance.Initialize(isStandalone: true); var state2 = OpenTelemetryManager.Instance.IsActive(); diff --git a/src/Build.UnitTests/TelemetryTests.cs b/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs similarity index 55% rename from src/Build.UnitTests/TelemetryTests.cs rename to src/Build.UnitTests/Telemetry/Telemetry_Tests.cs index d04353d7321..6939eda86e5 100644 --- a/src/Build.UnitTests/TelemetryTests.cs +++ b/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text.Json; using Microsoft.Build.Execution; @@ -16,11 +17,12 @@ namespace Microsoft.Build.Engine.UnitTests { - public class TelemetryTests + [Collection("OpenTelemetryManagerTests")] + public class Telemetry_Tests { private readonly ITestOutputHelper _output; - public TelemetryTests(ITestOutputHelper output) + public Telemetry_Tests(ITestOutputHelper output) { _output = output; } @@ -82,7 +84,7 @@ public void WorkerNodeTelemetryCollection_BasicTarget() ((int)workerNodeTelemetryData.TasksExecutionData[(TaskOrTargetTelemetryKey)"Microsoft.Build.Tasks.CreateItem"].ExecutionsCount).ShouldBe(1); workerNodeTelemetryData.TasksExecutionData[(TaskOrTargetTelemetryKey)"Microsoft.Build.Tasks.CreateItem"].CumulativeExecutionTime.ShouldBeGreaterThan(TimeSpan.Zero); - workerNodeTelemetryData.TasksExecutionData.Keys.ShouldAllBe(k => !k.IsCustom && !k.IsFromNugetCache); + workerNodeTelemetryData.TasksExecutionData.Keys.ShouldAllBe(k => !k.IsCustom && !k.IsNuget); workerNodeTelemetryData.TasksExecutionData.Values .Count(v => v.CumulativeExecutionTime > TimeSpan.Zero || v.ExecutionsCount > 0).ShouldBe(2); } @@ -106,7 +108,7 @@ public void WorkerNodeTelemetryCollection_CustomTargetsAndTasks() - + - + @@ -127,12 +129,12 @@ public void WorkerNodeTelemetryCollection_CustomTargetsAndTasks() - + - + @@ -166,32 +168,151 @@ public void WorkerNodeTelemetryCollection_CustomTargetsAndTasks() workerNodeTelemetryData.TasksExecutionData.Values .Count(v => v.CumulativeExecutionTime > TimeSpan.Zero || v.ExecutionsCount > 0).ShouldBe(3); - workerNodeTelemetryData.TasksExecutionData.Keys.ShouldAllBe(k => !k.IsFromNugetCache); + workerNodeTelemetryData.TasksExecutionData.Keys.ShouldAllBe(k => !k.IsNuget); } +#if NET + // test in .net core with opentelemetry opted in to avoid sending it but enable listening to it [Fact] - public void Foo() + public void NodeTelemetryE2E() { - WorkerNodeTelemetryData wd = new WorkerNodeTelemetryData( - new Dictionary() - { - { - new TaskOrTargetTelemetryKey("TaskA", false, true), - new TaskExecutionStats(TimeSpan.FromSeconds(2.1554548), 5, 545) - }, - { - new TaskOrTargetTelemetryKey("TaskA", true, false), - new TaskExecutionStats(TimeSpan.FromSeconds(254548), 6, 54545451) - }, - }, - new Dictionary() - { - { new TaskOrTargetTelemetryKey("TargetA", false, true, false), false }, - { new TaskOrTargetTelemetryKey("TargetA", true, true, false), false }, - { new TaskOrTargetTelemetryKey("TargetB", false, false, true), false } - }); - - var holder = TelemetryDataUtils.AsActivityDataHolder(wd, true, true); + using TestEnvironment env = TestEnvironment.Create(); + env.SetEnvironmentVariable("MSBUILD_TELEMETRY_OPTIN", "1"); + env.SetEnvironmentVariable("MSBUILD_TELEMETRY_SAMPLE_RATE", "1.0"); + env.SetEnvironmentVariable("MSBUILD_TELEMETRY_OPTOUT", null); + env.SetEnvironmentVariable("DOTNET_CLI_TELEMETRY_OPTOUT", null); + + // Reset the OpenTelemetryManager state to ensure clean test + var instance = OpenTelemetryManager.Instance; + typeof(OpenTelemetryManager) + .GetField("_telemetryState", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) + ?.SetValue(instance, OpenTelemetryManager.TelemetryState.Uninitialized); + + typeof(OpenTelemetryManager) + .GetProperty("DefaultActivitySource", + System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) + ?.SetValue(instance, null); + + // track activities through an ActivityListener + var capturedActivities = new List(); + using var listener = new ActivityListener + { + ShouldListenTo = source => source.Name.StartsWith(TelemetryConstants.DefaultActivitySourceNamespace), + Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllData, + ActivityStarted = capturedActivities.Add, + ActivityStopped = _ => { } + }; + ActivitySource.AddActivityListener(listener); + + var testProject = @" + + + + + + + + + + + + "; + + using var testEnv = TestEnvironment.Create(_output); + var projectFile = testEnv.CreateFile("test.proj", testProject).Path; + + // Set up loggers + var projectFinishedLogger = new ProjectFinishedCapturingLogger(); + var buildParameters = new BuildParameters + { + Loggers = new ILogger[] { projectFinishedLogger }, + IsTelemetryEnabled = true + }; + + // Act + using (var buildManager = new BuildManager()) + { + // Phase 1: Begin Build - This initializes telemetry infrastructure + buildManager.BeginBuild(buildParameters); + + // Phase 2: Execute build requests + var buildRequestData1 = new BuildRequestData( + projectFile, + new Dictionary(), + null, + new[] { "Build" }, + null); + + buildManager.BuildRequest(buildRequestData1); + + var buildRequestData2 = new BuildRequestData( + projectFile, + new Dictionary(), + null, + new[] { "Clean" }, + null); + + buildManager.BuildRequest(buildRequestData2); + + // Phase 3: End Build - This puts telemetry to an system.diagnostics activity + buildManager.EndBuild(); + + // Verify build activity were captured by the listener and contain task and target info + capturedActivities.ShouldNotBeEmpty(); + var activity = capturedActivities.FindLast(a => a.DisplayName == "VS/MSBuild/Build").ShouldNotBeNull(); + var tags = activity.Tags.ToDictionary(t => t.Key, t => t.Value); + tags.ShouldNotBeNull(); + + tags.ShouldContainKey("VS.MSBuild.BuildTarget"); + tags["VS.MSBuild.BuildTarget"].ShouldNotBeNullOrEmpty(); + + // Verify task data + tags.ShouldContainKey("VS.MSBuild.Tasks"); + var tasksJson = tags["VS.MSBuild.Tasks"]; + tasksJson.ShouldNotBeNullOrEmpty(); + tasksJson.ShouldContain("Microsoft.Build.Tasks.Message"); + tasksJson.ShouldContain("Microsoft.Build.Tasks.CreateItem"); + + // Parse tasks data for detailed assertions + var tasksData = JsonSerializer.Deserialize(tasksJson); + + // Verify Message task execution metrics - updated for object structure + tasksData.TryGetProperty("Microsoft.Build.Tasks.Message", out var messageTask).ShouldBe(true); + messageTask.GetProperty("ExecutionsCount").GetInt32().ShouldBe(3); + messageTask.GetProperty("TotalMilliseconds").GetDouble().ShouldBeGreaterThan(0); + messageTask.GetProperty("TotalMemoryBytes").GetInt64().ShouldBeGreaterThan(0); + messageTask.GetProperty(nameof(TaskOrTargetTelemetryKey.IsCustom)).GetBoolean().ShouldBe(false); + messageTask.GetProperty(nameof(TaskOrTargetTelemetryKey.IsCustom)).GetBoolean().ShouldBe(false); + + // Verify CreateItem task execution metrics - updated for object structure + tasksData.TryGetProperty("Microsoft.Build.Tasks.CreateItem", out var createItemTask).ShouldBe(true); + createItemTask.GetProperty("ExecutionsCount").GetInt32().ShouldBe(1); + createItemTask.GetProperty("TotalMilliseconds").GetDouble().ShouldBeGreaterThan(0); + createItemTask.GetProperty("TotalMemoryBytes").GetInt64().ShouldBeGreaterThan(0); + + // Verify Targets summary information + tags.ShouldContainKey("VS.MSBuild.TargetsSummary"); + var targetsSummaryJson = tags["VS.MSBuild.TargetsSummary"]; + targetsSummaryJson.ShouldNotBeNullOrEmpty(); + var targetsSummary = JsonSerializer.Deserialize(targetsSummaryJson); + + // Verify loaded and executed targets counts - match structure in TargetsSummaryConverter.Write + targetsSummary.GetProperty("Loaded").GetProperty("Total").GetInt32().ShouldBe(2); + targetsSummary.GetProperty("Executed").GetProperty("Total").GetInt32().ShouldBe(2); + + // Verify Tasks summary information + tags.ShouldContainKey("VS.MSBuild.TasksSummary"); + var tasksSummaryJson = tags["VS.MSBuild.TasksSummary"]; + tasksSummaryJson.ShouldNotBeNullOrEmpty(); + var tasksSummary = JsonSerializer.Deserialize(tasksSummaryJson); + + // Verify task execution summary metrics based on TasksSummaryConverter.Write structure + tasksSummary.GetProperty("Microsoft").GetProperty("Total").GetProperty("ExecutionsCount").GetInt32().ShouldBe(4); + tasksSummary.GetProperty("Microsoft").GetProperty("Total").GetProperty("TotalMilliseconds").GetDouble().ShouldBeGreaterThan(0); + tasksSummary.GetProperty("Microsoft").GetProperty("Total").GetProperty("TotalMemoryBytes").GetInt64().ShouldBeGreaterThan(0); + } } + +#endif } } diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 78f96a187df..a60bd4ed0d8 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -2989,8 +2989,8 @@ private ILoggingService CreateLoggingService( // We do want to dictate our own forwarding logger (otherwise CentralForwardingLogger with minimum transferred importance MessageImportance.Low is used) // In the future we might optimize for single, in-node build scenario - where forwarding logger is not needed (but it's just quick pass-through) LoggerDescription forwardingLoggerDescription = new LoggerDescription( - loggerClassName: typeof(InternalTelemeteryForwardingLogger).FullName, - loggerAssemblyName: typeof(InternalTelemeteryForwardingLogger).GetTypeInfo().Assembly.GetName().FullName, + loggerClassName: typeof(InternalTelemetryForwardingLogger).FullName, + loggerAssemblyName: typeof(InternalTelemetryForwardingLogger).GetTypeInfo().Assembly.GetName().FullName, loggerAssemblyFile: null, loggerSwitchParameters: null, verbosity: LoggerVerbosity.Quiet); diff --git a/src/Build/BackEnd/Components/Logging/EventSourceSink.cs b/src/Build/BackEnd/Components/Logging/EventSourceSink.cs index e61db2b91b2..995370003eb 100644 --- a/src/Build/BackEnd/Components/Logging/EventSourceSink.cs +++ b/src/Build/BackEnd/Components/Logging/EventSourceSink.cs @@ -5,6 +5,7 @@ using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Experimental.BuildCheck; using Microsoft.Build.Framework; +using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Shared; using InternalLoggerException = Microsoft.Build.Exceptions.InternalLoggerException; diff --git a/src/Build/TelemetryInfra/InternalTelemetryConsumingLogger.cs b/src/Build/TelemetryInfra/InternalTelemetryConsumingLogger.cs index 24cd5b9ed0e..8d4832cd374 100644 --- a/src/Build/TelemetryInfra/InternalTelemetryConsumingLogger.cs +++ b/src/Build/TelemetryInfra/InternalTelemetryConsumingLogger.cs @@ -14,7 +14,7 @@ internal sealed class InternalTelemetryConsumingLogger : ILogger { public LoggerVerbosity Verbosity { get; set; } public string? Parameters { get; set; } - internal static event Action? TestOnly_InternalTelemetryAggregted; + internal static event Action? TestOnly_InternalTelemetryAggregted; public void Initialize(IEventSource eventSource) { @@ -38,12 +38,11 @@ private void EventSourceOnBuildFinished(object sender, BuildFinishedEventArgs e) { TestOnly_InternalTelemetryAggregted?.Invoke(_workerNodeTelemetryData); FlushDataIntoConsoleIfRequested(); - FlushDataIntoJsonFileIfRequested(); } private void FlushDataIntoConsoleIfRequested() { - if (!Traits.IsEnvVarOneOrTrue("MSBUILDOUTPUTNODESTELEMETRY")) + if (!Traits.Instance.FlushNodesTelemetryIntoConsole) { return; } @@ -63,15 +62,15 @@ private void FlushDataIntoConsoleIfRequested() } Console.WriteLine("=========================================="); Console.WriteLine("Tasks by time:"); - foreach (var task in _workerNodeTelemetryData.TasksExecutionData.OrderByDescending(t => t.Value.CumulativeExecutionTime).Take(20)) + foreach (var task in _workerNodeTelemetryData.TasksExecutionData.OrderByDescending(t => t.Value.CumulativeExecutionTime)) { Console.WriteLine($"{task.Key} - {task.Value.CumulativeExecutionTime}"); } Console.WriteLine("=========================================="); Console.WriteLine("Tasks by memory consumption:"); - foreach (var task in _workerNodeTelemetryData.TasksExecutionData.OrderByDescending(t => t.Value.TotalMemoryConsumption).Take(20)) + foreach (var task in _workerNodeTelemetryData.TasksExecutionData.OrderByDescending(t => t.Value.TotalMemoryBytes)) { - Console.WriteLine($"{task.Key} - {task.Value.TotalMemoryConsumption / 1024.0:0.00}kB"); + Console.WriteLine($"{task.Key} - {task.Value.TotalMemoryBytes / 1024.0:0.00}kB"); } Console.WriteLine("=========================================="); Console.WriteLine("Tasks by Executions count:"); @@ -82,22 +81,6 @@ private void FlushDataIntoConsoleIfRequested() Console.WriteLine("=========================================="); } - private void FlushDataIntoJsonFileIfRequested() - { - const string jsonFileNameVariable = "MSBUILDNODETELEMETRYFILENAME"; - var jsonFilePath = Environment.GetEnvironmentVariable(jsonFileNameVariable); - if (string.IsNullOrEmpty(jsonFilePath)) - { - return; - } - - var telemetryTags = _workerNodeTelemetryData.AsActivityDataHolder(true, true)?.GetActivityProperties(); - - using var stream = File.OpenWrite(jsonFilePath); - stream.SetLength(0); - JsonSerializer.Serialize(stream, telemetryTags, new JsonSerializerOptions() { WriteIndented = true }); - } - public void Shutdown() { } } diff --git a/src/Build/TelemetryInfra/TelemetryDataUtils.cs b/src/Build/TelemetryInfra/TelemetryDataUtils.cs index ed91dcdbaa9..e2759bec030 100644 --- a/src/Build/TelemetryInfra/TelemetryDataUtils.cs +++ b/src/Build/TelemetryInfra/TelemetryDataUtils.cs @@ -5,10 +5,18 @@ using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; + namespace Microsoft.Build.Framework.Telemetry { internal static class TelemetryDataUtils { + /// + /// Transforms collected telemetry data to format recognized by the telemetry infrastructure. + /// + /// Data about tasks and target forwarded from nodes. + /// Controls whether Task details should attached to the telemetry. + /// Controls whether Target details should be attached to the telemetry. + /// Node Telemetry data wrapped in a list of properties that can be attached as tags to a . public static IActivityTelemetryDataHolder? AsActivityDataHolder(this IWorkerNodeTelemetryData? telemetryData, bool includeTasksDetails, bool includeTargetDetails) { if (telemetryData == null) @@ -20,24 +28,24 @@ internal static class TelemetryDataUtils if (includeTasksDetails) { - telemetryItems.Add(new TelemetryItem("Tasks", + telemetryItems.Add(new TelemetryItem(NodeTelemetryTags.Tasks, JsonSerializer.Serialize(telemetryData.TasksExecutionData, _serializerOptions), false)); } if (includeTargetDetails) { - telemetryItems.Add(new TelemetryItem("Targets", + telemetryItems.Add(new TelemetryItem(NodeTelemetryTags.Targets, JsonSerializer.Serialize(telemetryData.TargetsExecutionData, _serializerOptions), false)); } - TargetsSummary targetsSummary = new(); - targetsSummary.Initialize(telemetryData.TargetsExecutionData); - telemetryItems.Add(new TelemetryItem("TargetsSummary", + TargetsSummaryConverter targetsSummary = new(); + targetsSummary.Process(telemetryData.TargetsExecutionData); + telemetryItems.Add(new TelemetryItem(NodeTelemetryTags.TargetsSummary, JsonSerializer.Serialize(targetsSummary, _serializerOptions), false)); - TasksSummary tasksSummary = new(); - tasksSummary.Initialize(telemetryData.TasksExecutionData); - telemetryItems.Add(new TelemetryItem("TasksSummary", + TasksSummaryConverter tasksSummary = new(); + tasksSummary.Process(telemetryData.TasksExecutionData); + telemetryItems.Add(new TelemetryItem(NodeTelemetryTags.TasksSummary, JsonSerializer.Serialize(tasksSummary, _serializerOptions), false)); return new NodeTelemetry(telemetryItems); @@ -49,21 +57,19 @@ private static JsonSerializerOptions CreateSerializerOptions() { var opt = new JsonSerializerOptions { - // Add following if user-friendly indentation would be needed - // WriteIndented = true, Converters = { - new TargetDataConverter(), - new TaskDataConverter(), - new TargetsSummary(), - new TasksSummary(), + new TargetsDetailsConverter(), + new TasksDetailsConverter(), + new TargetsSummaryConverter(), + new TasksSummaryConverter(), }, }; return opt; } - private class TargetDataConverter : JsonConverter?> + private class TargetsDetailsConverter : JsonConverter?> { public override Dictionary? Read( ref Utf8JsonReader reader, @@ -83,24 +89,29 @@ public override void Write( } // Following needed - as System.Text.Json doesn't support indexing dictionary by composite types - - writer.WriteStartArray(); + writer.WriteStartObject(); foreach (KeyValuePair valuePair in value) { - writer.WriteStartObject(valuePair.Key.IsCustom || valuePair.Key.IsFromMetaProject ? ActivityExtensions.GetHashed(valuePair.Key.Name) : valuePair.Key.Name); + string keyName = ShouldHashKey(valuePair.Key) ? + ActivityExtensions.GetHashed(valuePair.Key.Name) : + valuePair.Key.Name; + + writer.WriteStartObject(keyName); writer.WriteBoolean("WasExecuted", valuePair.Value); - writer.WriteBoolean("IsCustom", valuePair.Key.IsCustom); - writer.WriteBoolean("IsFromNuget", valuePair.Key.IsFromNugetCache); - writer.WriteBoolean("IsMetaproj", valuePair.Key.IsFromMetaProject); + writer.WriteBoolean(nameof(valuePair.Key.IsCustom), valuePair.Key.IsCustom); + writer.WriteBoolean(nameof(valuePair.Key.IsNuget), valuePair.Key.IsNuget); + writer.WriteBoolean(nameof(valuePair.Key.IsMetaProj), valuePair.Key.IsMetaProj); writer.WriteEndObject(); } - writer.WriteEndArray(); + writer.WriteEndObject(); } + + private bool ShouldHashKey(TaskOrTargetTelemetryKey key) => key.IsCustom || key.IsMetaProj; } - private class TaskDataConverter : JsonConverter?> + private class TasksDetailsConverter : JsonConverter?> { public override Dictionary? Read( ref Utf8JsonReader reader, @@ -120,58 +131,67 @@ public override void Write( } // Following needed - as System.Text.Json doesn't support indexing dictionary by composite types - - writer.WriteStartArray(); + writer.WriteStartObject(); foreach (KeyValuePair valuePair in value) { - writer.WriteStartObject(valuePair.Key.IsCustom ? ActivityExtensions.GetHashed(valuePair.Key.Name) : valuePair.Key.Name); - // We do not want decimals - writer.WriteNumber("ExecTimeMs", valuePair.Value.CumulativeExecutionTime.TotalMilliseconds / 1); - writer.WriteNumber("ExecCnt", valuePair.Value.ExecutionsCount); - // We do not want decimals - writer.WriteNumber("MemKBs", valuePair.Value.TotalMemoryConsumption / 1024); - writer.WriteBoolean("IsCustom", valuePair.Key.IsCustom); - writer.WriteBoolean("IsFromNuget", valuePair.Key.IsFromNugetCache); + string keyName = valuePair.Key.IsCustom ? + ActivityExtensions.GetHashed(valuePair.Key.Name) : + valuePair.Key.Name; + writer.WriteStartObject(keyName); + writer.WriteNumber(nameof(valuePair.Value.CumulativeExecutionTime.TotalMilliseconds), valuePair.Value.CumulativeExecutionTime.TotalMilliseconds); + writer.WriteNumber(nameof(valuePair.Value.ExecutionsCount), valuePair.Value.ExecutionsCount); + writer.WriteNumber(nameof(valuePair.Value.TotalMemoryBytes), valuePair.Value.TotalMemoryBytes); + writer.WriteBoolean(nameof(valuePair.Key.IsCustom), valuePair.Key.IsCustom); + writer.WriteBoolean(nameof(valuePair.Key.IsNuget), valuePair.Key.IsNuget); writer.WriteEndObject(); } - writer.WriteEndArray(); + writer.WriteEndObject(); } } - private class TargetsSummary : JsonConverter + private class TargetsSummaryConverter : JsonConverter { - public void Initialize(Dictionary targetsExecutionData) + /// + /// Processes target execution data to compile summary statistics for both built-in and custom targets. + /// + /// Dictionary containing target execution data keyed by task identifiers. + public void Process(Dictionary targetsExecutionData) { - foreach (var targetInfo in targetsExecutionData) + foreach (KeyValuePair targetPair in targetsExecutionData) { - UpdateStatistics(LoadedBuiltinTargetInfo, LoadedCustomTargetInfo, targetInfo.Key); - if (targetInfo.Value) + TaskOrTargetTelemetryKey key = targetPair.Key; + bool wasExecuted = targetPair.Value; + + // Update loaded targets statistics (all targets are loaded) + UpdateTargetStatistics(key, isExecuted: false); + + // Update executed targets statistics (only targets that were actually executed) + if (wasExecuted) { - UpdateStatistics(ExecutedBuiltinTargetInfo, ExecutedCustomTargetInfo, targetInfo.Key); + UpdateTargetStatistics(key, isExecuted: true); } } + } - void UpdateStatistics( - TargetInfo builtinTargetInfo, - TargetInfo customTargetInfo, - TaskOrTargetTelemetryKey key) - { - UpdateSingleStatistics(key.IsCustom ? customTargetInfo : builtinTargetInfo, key); + private void UpdateTargetStatistics(TaskOrTargetTelemetryKey key, bool isExecuted) + { + // Select the appropriate target info collections based on execution state + TargetInfo builtinTargetInfo = isExecuted ? ExecutedBuiltinTargetInfo : LoadedBuiltinTargetInfo; + TargetInfo customTargetInfo = isExecuted ? ExecutedCustomTargetInfo : LoadedCustomTargetInfo; - void UpdateSingleStatistics(TargetInfo targetInfo, TaskOrTargetTelemetryKey kkey) - { - targetInfo.Total++; - if (kkey.IsFromNugetCache) - { - targetInfo.FromNuget++; - } - if (kkey.IsFromMetaProject) - { - targetInfo.FromMetaproj++; - } - } + // Update either custom or builtin target info based on target type + TargetInfo targetInfo = key.IsCustom ? customTargetInfo : builtinTargetInfo; + + targetInfo.Total++; + if (key.IsNuget) + { + targetInfo.FromNuget++; + } + if (key.IsMetaProj) + { + targetInfo.FromMetaproj++; } } @@ -187,7 +207,7 @@ private class TargetInfo public int FromMetaproj { get; internal set; } } - public override TargetsSummary? Read( + public override TargetsSummaryConverter? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => @@ -195,7 +215,7 @@ private class TargetInfo public override void Write( Utf8JsonWriter writer, - TargetsSummary value, + TargetsSummaryConverter value, JsonSerializerOptions options) { writer.WriteStartObject(); @@ -207,10 +227,9 @@ public override void Write( writer.WriteEndObject(); writer.WriteEndObject(); - - void WriteStat(Utf8JsonWriter writer, TargetInfo customTargetsInfo, TargetInfo builtinTargetsInfo) + void WriteStat(Utf8JsonWriter writer, TargetInfo builtinTargetsInfo, TargetInfo customTargetsInfo) { - writer.WriteNumber("Total", builtinTargetsInfo.Total + customTargetsInfo.Total); + writer.WriteNumber(nameof(builtinTargetsInfo.Total), builtinTargetsInfo.Total + customTargetsInfo.Total); WriteSingleStat(writer, builtinTargetsInfo, "Microsoft"); WriteSingleStat(writer, customTargetsInfo, "Custom"); } @@ -220,54 +239,56 @@ void WriteSingleStat(Utf8JsonWriter writer, TargetInfo targetInfo, string name) if (targetInfo.Total > 0) { writer.WriteStartObject(name); - writer.WriteNumber("Total", targetInfo.Total); - writer.WriteNumber("FromNuget", targetInfo.FromNuget); - writer.WriteNumber("FromMetaproj", targetInfo.FromMetaproj); + writer.WriteNumber(nameof(targetInfo.Total), targetInfo.Total); + writer.WriteNumber(nameof(targetInfo.FromNuget), targetInfo.FromNuget); + writer.WriteNumber(nameof(targetInfo.FromMetaproj), targetInfo.FromMetaproj); writer.WriteEndObject(); } } } } - - private class TasksSummary : JsonConverter + private class TasksSummaryConverter : JsonConverter { - public void Initialize(Dictionary tasksExecutionData) + /// + /// Processes task execution data to compile summary statistics for both built-in and custom tasks. + /// + /// Dictionary containing task execution data keyed by task identifiers. + public void Process(Dictionary tasksExecutionData) { - foreach (var taskInfo in tasksExecutionData) + foreach (KeyValuePair taskInfo in tasksExecutionData) { - UpdateStatistics(BuiltinTasksInfo, CustomTasksInfo, taskInfo.Key, taskInfo.Value); + UpdateTaskStatistics(BuiltinTasksInfo, CustomTasksInfo, taskInfo.Key, taskInfo.Value); } + } - void UpdateStatistics( - TasksInfo builtinTaskInfo, - TasksInfo customTaskInfo, - TaskOrTargetTelemetryKey key, - TaskExecutionStats taskExecutionStats) - { - UpdateSingleStatistics(key.IsCustom ? customTaskInfo : builtinTaskInfo, taskExecutionStats, key); + private void UpdateTaskStatistics( + TasksInfo builtinTaskInfo, + TasksInfo customTaskInfo, + TaskOrTargetTelemetryKey key, + TaskExecutionStats taskExecutionStats) + { + TasksInfo taskInfo = key.IsCustom ? customTaskInfo : builtinTaskInfo; + taskInfo.Total.Accumulate(taskExecutionStats); - void UpdateSingleStatistics(TasksInfo summarizedTaskInfo, TaskExecutionStats infoToAdd, TaskOrTargetTelemetryKey kkey) - { - summarizedTaskInfo.Total.AddAnother(infoToAdd); - if (kkey.IsFromNugetCache) - { - summarizedTaskInfo.FromNuget.AddAnother(infoToAdd); - } - } + if (key.IsNuget) + { + taskInfo.FromNuget.Accumulate(taskExecutionStats); } } private TasksInfo BuiltinTasksInfo { get; } = new TasksInfo(); + private TasksInfo CustomTasksInfo { get; } = new TasksInfo(); private class TasksInfo { public TaskExecutionStats Total { get; } = TaskExecutionStats.CreateEmpty(); + public TaskExecutionStats FromNuget { get; } = TaskExecutionStats.CreateEmpty(); } - public override TasksSummary? Read( + public override TasksSummaryConverter? Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => @@ -275,7 +296,7 @@ private class TasksInfo public override void Write( Utf8JsonWriter writer, - TasksSummary value, + TasksSummaryConverter value, JsonSerializerOptions options) { writer.WriteStartObject(); @@ -286,21 +307,19 @@ public override void Write( void WriteStat(Utf8JsonWriter writer, TasksInfo tasksInfo, string name) { writer.WriteStartObject(name); - WriteSingleStat(writer, tasksInfo.Total, "Total", true); - WriteSingleStat(writer, tasksInfo.FromNuget, "FromNuget", false); + WriteSingleStat(writer, tasksInfo.Total, nameof(tasksInfo.Total)); + WriteSingleStat(writer, tasksInfo.FromNuget, nameof(tasksInfo.FromNuget)); writer.WriteEndObject(); } - void WriteSingleStat(Utf8JsonWriter writer, TaskExecutionStats stats, string name, bool writeIfEmpty) + void WriteSingleStat(Utf8JsonWriter writer, TaskExecutionStats stats, string name) { if (stats.ExecutionsCount > 0) { writer.WriteStartObject(name); - writer.WriteNumber("TotalExecutionsCount", stats.ExecutionsCount); - // We do not want decimals - writer.WriteNumber("CumulativeExecutionTimeMs", (long)stats.CumulativeExecutionTime.TotalMilliseconds); - // We do not want decimals - writer.WriteNumber("CumulativeConsumedMemoryKB", stats.TotalMemoryConsumption / 1024); + writer.WriteNumber(nameof(stats.ExecutionsCount), stats.ExecutionsCount); + writer.WriteNumber(nameof(stats.CumulativeExecutionTime.TotalMilliseconds), stats.CumulativeExecutionTime.TotalMilliseconds); + writer.WriteNumber(nameof(stats.TotalMemoryBytes), stats.TotalMemoryBytes); writer.WriteEndObject(); } } diff --git a/src/Build/TelemetryInfra/TelemetryForwarderProvider.cs b/src/Build/TelemetryInfra/TelemetryForwarderProvider.cs index b3522dbf64a..58ea242088b 100644 --- a/src/Build/TelemetryInfra/TelemetryForwarderProvider.cs +++ b/src/Build/TelemetryInfra/TelemetryForwarderProvider.cs @@ -5,6 +5,7 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; +using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Shared; namespace Microsoft.Build.TelemetryInfra; diff --git a/src/Framework.UnitTests/WorkerNodeTelemetryEventArgs_Tests.cs b/src/Framework.UnitTests/WorkerNodeTelemetryEventArgs_Tests.cs index 57d822d7194..bf5303e2c09 100644 --- a/src/Framework.UnitTests/WorkerNodeTelemetryEventArgs_Tests.cs +++ b/src/Framework.UnitTests/WorkerNodeTelemetryEventArgs_Tests.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Shouldly; using Xunit; +using Microsoft.Build.Framework.Telemetry; namespace Microsoft.Build.Framework.UnitTests { diff --git a/src/Framework/IEventSource5.cs b/src/Framework/IEventSource5.cs index 6ce4f300c30..cd56c63fb40 100644 --- a/src/Framework/IEventSource5.cs +++ b/src/Framework/IEventSource5.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Build.Framework.Telemetry; namespace Microsoft.Build.Framework { diff --git a/src/Framework/Telemetry/IWorkerNodeTelemetryData.cs b/src/Framework/Telemetry/IWorkerNodeTelemetryData.cs index 7f439252482..a0303e4a4e2 100644 --- a/src/Framework/Telemetry/IWorkerNodeTelemetryData.cs +++ b/src/Framework/Telemetry/IWorkerNodeTelemetryData.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; -namespace Microsoft.Build.Framework; +namespace Microsoft.Build.Framework.Telemetry; internal interface IWorkerNodeTelemetryData { diff --git a/src/Framework/Telemetry/InternalTelemetryForwardingLogger.cs b/src/Framework/Telemetry/InternalTelemetryForwardingLogger.cs index 95e38567587..2208f4f2f5b 100644 --- a/src/Framework/Telemetry/InternalTelemetryForwardingLogger.cs +++ b/src/Framework/Telemetry/InternalTelemetryForwardingLogger.cs @@ -1,12 +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.Build.Framework; +namespace Microsoft.Build.Framework.Telemetry; /// /// Ensure that events filtering is in sync with InternalTelemetryConsumingLogger. /// -internal class InternalTelemeteryForwardingLogger : IForwardingLogger +internal class InternalTelemetryForwardingLogger : IForwardingLogger { public IEventRedirector? BuildEventRedirector { get; set; } diff --git a/src/Framework/Telemetry/TaskExecutionStats.cs b/src/Framework/Telemetry/TaskExecutionStats.cs index b3f1564d615..533599734fd 100644 --- a/src/Framework/Telemetry/TaskExecutionStats.cs +++ b/src/Framework/Telemetry/TaskExecutionStats.cs @@ -3,14 +3,21 @@ using System; -namespace Microsoft.Build.Framework; +namespace Microsoft.Build.Framework.Telemetry; +/// +/// Represents the execution statistics of tasks executed on a node. +/// internal class TaskExecutionStats(TimeSpan cumulativeExecutionTime, int executionsCount, long totalMemoryConsumption) { private TaskExecutionStats() : this(TimeSpan.Zero, 0, 0) { } + /// + /// Creates an instance of initialized to zero values. + /// + /// Empty task execution statistics. internal static TaskExecutionStats CreateEmpty() => new(); @@ -22,21 +29,25 @@ internal static TaskExecutionStats CreateEmpty() /// /// Total memory consumption (across all executions) in bytes. /// - public long TotalMemoryConsumption { get; set; } = totalMemoryConsumption; + public long TotalMemoryBytes { get; set; } = totalMemoryConsumption; /// - /// Total number of execution of the tasks in all nodes for all projects. + /// Total number of executions of the task. /// public int ExecutionsCount { get; set; } = executionsCount; - internal void AddAnother(TaskExecutionStats another) + /// + /// Accumulates statistics from another instance into this one. + /// + /// Statistics to add to this instance. + internal void Accumulate(TaskExecutionStats other) { - this.CumulativeExecutionTime += another.CumulativeExecutionTime; - this.TotalMemoryConsumption += another.TotalMemoryConsumption; - this.ExecutionsCount += another.ExecutionsCount; + this.CumulativeExecutionTime += other.CumulativeExecutionTime; + this.TotalMemoryBytes += other.TotalMemoryBytes; + this.ExecutionsCount += other.ExecutionsCount; } - // We need custom Equals for easier assertations in tests + // We need custom Equals for easier assertions in tests public override bool Equals(object? obj) { if (obj is TaskExecutionStats other) @@ -48,7 +59,7 @@ public override bool Equals(object? obj) protected bool Equals(TaskExecutionStats other) => CumulativeExecutionTime.Equals(other.CumulativeExecutionTime) && - TotalMemoryConsumption == other.TotalMemoryConsumption && + TotalMemoryBytes == other.TotalMemoryBytes && ExecutionsCount == other.ExecutionsCount; // Needed since we override Equals @@ -57,7 +68,7 @@ public override int GetHashCode() unchecked { var hashCode = CumulativeExecutionTime.GetHashCode(); - hashCode = (hashCode * 397) ^ TotalMemoryConsumption.GetHashCode(); + hashCode = (hashCode * 397) ^ TotalMemoryBytes.GetHashCode(); hashCode = (hashCode * 397) ^ ExecutionsCount.GetHashCode(); return hashCode; } diff --git a/src/Framework/Telemetry/TaskOrTargetTelemetryKey.cs b/src/Framework/Telemetry/TaskOrTargetTelemetryKey.cs index 864ce31e7a9..5647f2fdadd 100644 --- a/src/Framework/Telemetry/TaskOrTargetTelemetryKey.cs +++ b/src/Framework/Telemetry/TaskOrTargetTelemetryKey.cs @@ -3,36 +3,83 @@ using System; -namespace Microsoft.Build.Framework; +namespace Microsoft.Build.Framework.Telemetry; +/// +/// Represents a unique key for task or target telemetry data. +/// +/// +/// Used as a dictionary key for tracking execution metrics of tasks and targets. +/// internal struct TaskOrTargetTelemetryKey : IEquatable { + /// + /// Initializes a new instance of the struct with all properties. + /// + /// The name of the task or target. + /// Indicates whether the task/target is custom. + /// Indicates whether the task/target is from NuGet cache. + /// Indicates whether the task/target is from a meta project. public TaskOrTargetTelemetryKey(string name, bool isCustom, bool isFromNugetCache, bool isFromMetaProject) { Name = name; IsCustom = isCustom; - IsFromNugetCache = isFromNugetCache; - IsFromMetaProject = isFromMetaProject; + IsNuget = isFromNugetCache; + IsMetaProj = isFromMetaProject; } + /// + /// Initializes a new instance of the struct without meta project flag. + /// + /// The name of the task or target. + /// Indicates whether the task/target is custom. + /// Indicates whether the task/target is from NuGet cache. public TaskOrTargetTelemetryKey(string name, bool isCustom, bool isFromNugetCache) { Name = name; IsCustom = isCustom; - IsFromNugetCache = isFromNugetCache; + IsNuget = isFromNugetCache; + IsMetaProj = false; } - public TaskOrTargetTelemetryKey(string name) => Name = name; + /// + /// Initializes a new instance of the struct with name only. + /// + /// The name of the task or target. + public TaskOrTargetTelemetryKey(string name) : this(name, false, false, false) { } + /// + /// Enables explicit casting from string to . + /// + /// The string name to convert to a telemetry key. + /// A telemetry key with the given name. public static explicit operator TaskOrTargetTelemetryKey(string key) => new(key); + /// + /// Gets the name of the task or target. + /// + /// + /// This name is used as the primary key in serialized JSON data. + /// It is hashed when the task/target is custom or from a meta project. + /// public string Name { get; } - // Indicate custom targets/task - those must be hashed. + + /// + /// Indicates whether the task/target is custom. + /// public bool IsCustom { get; } - // Indicate targets/tasks sourced from nuget cache - those can be custom or MSFT provided ones. - public bool IsFromNugetCache { get; } - // Indicate targets/tasks generated during build - those must be hashed (as they contain paths). - public bool IsFromMetaProject { get; } + + /// + /// Indicates whether the task/target is from NuGet cache. + /// + /// Those can be custom or MSFT provided ones. + public bool IsNuget { get; } + + /// + /// Indicates whether the task/target is generated during build from a metaproject. + /// + /// Those must be hashed (as they contain paths). + public bool IsMetaProj { get; } public override bool Equals(object? obj) { @@ -46,8 +93,8 @@ public override bool Equals(object? obj) public bool Equals(TaskOrTargetTelemetryKey other) => string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase) && IsCustom == other.IsCustom && - IsFromNugetCache == other.IsFromNugetCache && - IsFromMetaProject == other.IsFromMetaProject; + IsNuget == other.IsNuget && + IsMetaProj == other.IsMetaProj; // We need hash code and equals - so that we can stuff data into dictionaries public override int GetHashCode() @@ -56,11 +103,11 @@ public override int GetHashCode() { var hashCode = Name.GetHashCode(); hashCode = (hashCode * 397) ^ IsCustom.GetHashCode(); - hashCode = (hashCode * 397) ^ IsFromNugetCache.GetHashCode(); - hashCode = (hashCode * 397) ^ IsFromMetaProject.GetHashCode(); + hashCode = (hashCode * 397) ^ IsNuget.GetHashCode(); + hashCode = (hashCode * 397) ^ IsMetaProj.GetHashCode(); return hashCode; } } - public override string ToString() => $"{Name},Custom:{IsCustom},IsFromNugetCache:{IsFromNugetCache},IsFromMetaProject:{IsFromMetaProject}"; + public override string ToString() => $"{Name},Custom:{IsCustom},IsFromNugetCache:{IsNuget},IsFromMetaProject:{IsMetaProj}"; } diff --git a/src/Framework/Telemetry/TelemetryConstants.cs b/src/Framework/Telemetry/TelemetryConstants.cs index 87df7c68e1c..f373760de7c 100644 --- a/src/Framework/Telemetry/TelemetryConstants.cs +++ b/src/Framework/Telemetry/TelemetryConstants.cs @@ -48,3 +48,12 @@ internal static class TelemetryConstants /// public const string InnerBuildDurationPropertyName = "InnerBuildDurationInMilliseconds"; } + +internal static class NodeTelemetryTags +{ + // These properties can't use nameof since they're not tied to a specific class property + public const string Tasks = "Tasks"; + public const string Targets = "Targets"; + public const string TargetsSummary = "TargetsSummary"; + public const string TasksSummary = "TasksSummary"; +} diff --git a/src/Framework/Telemetry/WorkerNodeTelemetryData.cs b/src/Framework/Telemetry/WorkerNodeTelemetryData.cs index 4b5afad229f..d643045ffe6 100644 --- a/src/Framework/Telemetry/WorkerNodeTelemetryData.cs +++ b/src/Framework/Telemetry/WorkerNodeTelemetryData.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; -namespace Microsoft.Build.Framework; +namespace Microsoft.Build.Framework.Telemetry; internal class WorkerNodeTelemetryData : IWorkerNodeTelemetryData { @@ -18,7 +18,7 @@ public void Add(IWorkerNodeTelemetryData other) { foreach (var task in other.TasksExecutionData) { - AddTask(task.Key, task.Value.CumulativeExecutionTime, task.Value.ExecutionsCount, task.Value.TotalMemoryConsumption); + AddTask(task.Key, task.Value.CumulativeExecutionTime, task.Value.ExecutionsCount, task.Value.TotalMemoryBytes); } foreach (var target in other.TargetsExecutionData) @@ -39,7 +39,7 @@ public void AddTask(TaskOrTargetTelemetryKey task, TimeSpan cumulativeExectionTi { taskExecutionStats.CumulativeExecutionTime += cumulativeExectionTime; taskExecutionStats.ExecutionsCount += executionsCount; - taskExecutionStats.TotalMemoryConsumption += totalMemoryConsumption; + taskExecutionStats.TotalMemoryBytes += totalMemoryConsumption; } } diff --git a/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs b/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs index a416f4245ad..a2a11f16e82 100644 --- a/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs +++ b/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs @@ -6,7 +6,7 @@ using System.IO; using Microsoft.Build.Shared; -namespace Microsoft.Build.Framework; +namespace Microsoft.Build.Framework.Telemetry; internal sealed class WorkerNodeTelemetryEventArgs(IWorkerNodeTelemetryData workerNodeTelemetryData) : BuildEventArgs { @@ -24,7 +24,7 @@ internal override void WriteToStream(BinaryWriter writer) WriteToStream(writer, entry.Key); writer.Write(entry.Value.CumulativeExecutionTime.Ticks); writer.Write(entry.Value.ExecutionsCount); - writer.Write(entry.Value.TotalMemoryConsumption); + writer.Write(entry.Value.TotalMemoryBytes); } writer.Write7BitEncodedInt(WorkerNodeTelemetryData.TargetsExecutionData.Count); @@ -62,8 +62,8 @@ private static void WriteToStream(BinaryWriter writer, TaskOrTargetTelemetryKey { writer.Write(key.Name); writer.Write(key.IsCustom); - writer.Write(key.IsFromNugetCache); - writer.Write(key.IsFromMetaProject); + writer.Write(key.IsNuget); + writer.Write(key.IsMetaProj); } private static TaskOrTargetTelemetryKey ReadFromStream(BinaryReader reader) diff --git a/src/Framework/Traits.cs b/src/Framework/Traits.cs index bcdc4ac195c..f2447e47031 100644 --- a/src/Framework/Traits.cs +++ b/src/Framework/Traits.cs @@ -142,6 +142,7 @@ public Traits() public bool FrameworkTelemetryOptOut = IsEnvVarOneOrTrue("MSBUILD_TELEMETRY_OPTOUT"); public double? TelemetrySampleRateOverride = ParseDoubleFromEnvironmentVariable("MSBUILD_TELEMETRY_SAMPLE_RATE"); public bool ExcludeTasksDetailsFromTelemetry = IsEnvVarOneOrTrue("MSBUILDTELEMETRYEXCLUDETASKSDETAILS"); + public bool FlushNodesTelemetryIntoConsole = IsEnvVarOneOrTrue("MSBUILDFLUSHNODESTELEMETRYINTOCONSOLE"); // for VS17.14 public readonly bool TelemetryOptIn = IsEnvVarOneOrTrue("MSBUILD_TELEMETRY_OPTIN"); @@ -163,9 +164,15 @@ private static int ParseIntFromEnvironmentVariableOrDefault(string environmentVa : defaultValue; } + /// + /// Parse a double from an environment variable with invariant culture. + /// private static double? ParseDoubleFromEnvironmentVariable(string environmentVariable) { - return double.TryParse(Environment.GetEnvironmentVariable(environmentVariable), out double result) + return double.TryParse(Environment.GetEnvironmentVariable(environmentVariable), + NumberStyles.Float, + CultureInfo.InvariantCulture, + out double result) ? result : null; } diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 3c617c54014..65283a2591b 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -10,6 +10,7 @@ using Microsoft.Build.Framework; #if !TASKHOST +using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Experimental.BuildCheck; #endif From 42e4cc71aebaf6cf01461b4b1d07aea4ed747dad Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Fri, 28 Mar 2025 17:10:54 +0100 Subject: [PATCH 051/106] Remove MSBuildRuntimeType conditions (#11641) The VMR builds the msbuild repo with dotnet build. Therefore remove some of the MSBuildRuntimeType conditions which seem to not be needed anymore. --- .../Microsoft.Build.UnGAC/Microsoft.Build.UnGAC.csproj | 2 +- src/StringTools.UnitTests/StringTools.UnitTests.net35.csproj | 4 ---- src/StringTools/StringTools.csproj | 3 +-- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Package/Microsoft.Build.UnGAC/Microsoft.Build.UnGAC.csproj b/src/Package/Microsoft.Build.UnGAC/Microsoft.Build.UnGAC.csproj index 6282c3a2134..0d02925d4e9 100644 --- a/src/Package/Microsoft.Build.UnGAC/Microsoft.Build.UnGAC.csproj +++ b/src/Package/Microsoft.Build.UnGAC/Microsoft.Build.UnGAC.csproj @@ -24,6 +24,6 @@ - + \ No newline at end of file diff --git a/src/StringTools.UnitTests/StringTools.UnitTests.net35.csproj b/src/StringTools.UnitTests/StringTools.UnitTests.net35.csproj index b63fa1e4e9e..d45c242ed70 100644 --- a/src/StringTools.UnitTests/StringTools.UnitTests.net35.csproj +++ b/src/StringTools.UnitTests/StringTools.UnitTests.net35.csproj @@ -1,9 +1,5 @@ - - - $(FullFrameworkTFM) $(RuntimeOutputPlatformTarget) diff --git a/src/StringTools/StringTools.csproj b/src/StringTools/StringTools.csproj index 4809373c67c..5015904acba 100644 --- a/src/StringTools/StringTools.csproj +++ b/src/StringTools/StringTools.csproj @@ -1,7 +1,6 @@ - $(LibraryTargetFrameworks) - $(LibraryTargetFrameworks);net35 + $(LibraryTargetFrameworks);net35 AnyCPU true true From a4ef175ef7015e3a072fa84ca37c1af7d54e34a7 Mon Sep 17 00:00:00 2001 From: Jenny Bai Date: Mon, 31 Mar 2025 11:46:39 +0000 Subject: [PATCH 052/106] Set IDE0005 (Remove unnecessary using directives) severity to Warning (#11643) --- .editorconfig | 2 +- .../Construction/ConstructionEditing_Tests.cs | 1 - .../Construction/ProjectFormatting_Tests.cs | 1 - .../ProjectItemGroupElement_tests.cs | 3 --- .../ProjectPropertyGroupElement_Tests.cs | 2 -- .../Construction/ProjectRootElement_Tests.cs | 4 ---- .../ProjectTargetElement_Tests.cs | 1 - .../Construction/ProjectTaskElement_Tests.cs | 2 -- .../Construction/SolutionFile_Tests.cs | 4 ---- .../Definition/DefinitionEditing_Tests.cs | 2 -- ...gElementsReferencedByOrReferences_Tests.cs | 2 -- .../Definition/ProjectItemDefinition_Tests.cs | 1 - .../Definition/ProjectItem_Tests.cs | 2 -- .../Definition/ProjectMetadata_Tests.cs | 1 - .../Definition/ProjectProperty_Tests.cs | 2 -- .../Definition/ProtectImports_Tests.cs | 1 - .../Instance/ProjectInstance_Tests.cs | 1 - .../Instance/ProjectItemInstance_Tests.cs | 6 ------ .../Instance/ProjectOnErrorInstance_Tests.cs | 2 -- .../Instance/ProjectTargetInstance_Tests.cs | 1 - .../Instance/ProjectTaskInstance_Tests.cs | 2 -- .../ProjectTaskOutputItemInstance_Tests.cs | 2 -- ...ProjectTaskOutputPropertyInstance_Tests.cs | 2 -- src/Build.OM.UnitTests/NugetRestoreTests.cs | 3 +-- .../ExporterMock.cs | 2 -- .../BackEnd/BinaryTranslator_Tests.cs | 1 - .../BuildEventArgTransportSink_Tests.cs | 2 -- .../BackEnd/BuildManager_Logging_Tests.cs | 1 - .../BackEnd/BuildManager_Tests.cs | 1 - .../BackEnd/BuildRequestEngine_Tests.cs | 1 - .../BackEnd/BuildRequestEntry_Tests.cs | 1 - .../BackEnd/BuildRequest_Tests.cs | 1 - .../BackEnd/BuildResult_Tests.cs | 1 - .../BackEnd/CacheAggregator_Tests.cs | 2 -- .../BackEnd/CentralForwardingLogger_Tests.cs | 1 - .../BackEnd/ConfigurationMetadata_Tests.cs | 2 -- .../BackEnd/DebugUtils_tests.cs | 1 - .../BackEnd/EventRedirectorToSink_Tests.cs | 1 - .../BackEnd/EventSourceTestHelper.cs | 4 ---- .../BackEnd/IntrinsicTask_Tests.cs | 1 - .../LoggingConfigurationTelemetry_Tests.cs | 2 -- .../BackEnd/LoggingContext_Tests.cs | 1 - .../BackEnd/LoggingService_Tests.cs | 1 - .../BackEnd/LoggingServicesLogMethod_Tests.cs | 1 - src/Build.UnitTests/BackEnd/Lookup_Tests.cs | 1 - src/Build.UnitTests/BackEnd/MockHost.cs | 2 +- .../BackEnd/NodeConfiguration_Tests.cs | 3 --- .../BackEnd/NodeEndpointInProc_Tests.cs | 3 +-- .../BackEnd/RedirectConsoleWriter_Tests.cs | 1 - .../BackEnd/RequestedProjectState_Tests.cs | 2 -- .../BackEnd/ResultsCache_Tests.cs | 3 --- .../BackEnd/Scheduler_Tests.cs | 1 - .../BackEnd/SdkResolverService_Tests.cs | 2 -- .../BackEnd/SdkResultItemComparison_Tests.cs | 1 - .../BackEnd/TargetBuilder_Tests.cs | 1 - .../BackEnd/TargetEntry_Tests.cs | 1 - .../BackEnd/TargetUpToDateChecker_Tests.cs | 1 - .../BackEnd/TaskBuilderTestTask.cs | 2 -- .../BackEnd/TaskBuilder_Tests.cs | 1 - .../BackEnd/TaskExecutionHost_Tests.cs | 1 - .../BackEnd/TaskHostConfiguration_Tests.cs | 2 -- .../BackEnd/TaskHostFactory_Tests.cs | 1 - .../BackEnd/TaskHostTaskComplete_Tests.cs | 2 +- src/Build.UnitTests/BackEnd/TaskThatThrows.cs | 4 ---- .../BackEnd/TranslationHelpers.cs | 1 - src/Build.UnitTests/BinaryLogger_Tests.cs | 1 - .../BuildEnvironmentHelper_Tests.cs | 1 - .../BuildEventArgsDataEnumeration.cs | 1 - src/Build.UnitTests/ChangeWaves_Tests.cs | 1 - .../MSBuildNameIgnoreCaseComparer_Tests.cs | 1 - .../Collections/OMcollections_tests.cs | 1 - .../SolutionFile_NewParser_Tests.cs | 3 --- .../Construction/SolutionFilter_Tests.cs | 2 +- .../Definition/ProjectHelpers.cs | 2 -- .../Definition/ProjectItem_Tests.cs | 2 -- .../Definition/Project_Internal_Tests.cs | 1 - .../Definition/ToolsVersion_Tests.cs | 2 -- .../ToolsetConfigurationReader_Tests.cs | 1 - .../Definition/ToolsetReader_Tests.cs | 1 - .../Definition/Toolset_Tests.cs | 2 -- .../Evaluation/ExpanderFunction_Tests.cs | 3 --- .../ImportFromMSBuildExtensionsPath_Tests.cs | 1 - .../IntrinsicFunctionOverload_Tests.cs | 5 ----- .../ProjectRootElementCache_Tests.cs | 1 - .../SimpleProjectRootElementCache_Tests.cs | 1 - .../ToolsetConfigurationNet5_Tests.cs | 5 ++--- .../EvaluationProfiler_Tests.cs | 1 - src/Build.UnitTests/FixPathOnUnix_Tests.cs | 1 - .../Globbing/MSBuildGlob_Tests.cs | 1 - .../Graph/GetCompatiblePlatformGraph_Tests.cs | 11 ---------- .../Graph/GraphLoadedFromSolution_tests.cs | 1 - .../Graph/IsolateProjects_Tests.cs | 1 - .../Graph/ResultCacheBasedBuilds_Tests.cs | 1 - .../Instance/HostServices_Tests.cs | 1 - .../ProjectInstance_Internal_Tests.cs | 1 - .../Instance/TaskItem_Tests.cs | 2 -- .../InvalidProjectFileException_Tests.cs | 1 - .../NodeStatus_SizeChange_Tests.cs | 5 ----- .../NodeStatus_Transition_Tests.cs | 4 ---- .../ProjectCache/ProjectCacheTests.cs | 2 -- src/Build.UnitTests/Scanner_Tests.cs | 3 --- src/Build.UnitTests/StaticStopwatch.cs | 1 - .../OpenTelemetryActivities_Tests.cs | 10 ++++----- .../Telemetry/OpenTelemetryManager_Tests.cs | 8 +++---- .../Telemetry/Telemetry_Tests.cs | 2 ++ src/Build.UnitTests/TerminalLogger_Tests.cs | 4 ---- src/Build.UnitTests/TestLoggingContext.cs | 5 ----- .../BackEnd/BuildManager/BuildParameters.cs | 2 -- .../BackEnd/BuildManager/BuildRequestData.cs | 7 ------- .../BuildManager/BuildSubmissionBase.cs | 4 ---- .../BackEnd/BuildManager/CacheAggregator.cs | 1 - .../BuildManager/GlobalPropertiesLookup.cs | 3 --- .../BackEnd/Client/MSBuildClientPacketPump.cs | 2 +- .../BuildComponentFactoryCollection.cs | 2 ++ .../BuildRequestEngine/BuildRequestEngine.cs | 3 +-- .../BuildRequestEngine/BuildRequestEntry.cs | 2 +- .../Caching/ResultsCacheResponse.cs | 3 --- .../Components/Communications/CurrentHost.cs | 1 - .../Communications/DetouredNodeLauncher.cs | 2 -- .../Communications/NodeProviderInProc.cs | 3 ++- .../NodeProviderOutOfProcBase.cs | 6 +++--- .../Components/Logging/EventSourceSink.cs | 1 - .../Logging/ProjectLoggingContext.cs | 1 - .../Logging/TargetLoggingContext.cs | 1 - .../Components/ProjectCache/CacheContext.cs | 1 - .../ProjectCache/ProjectCacheService.cs | 2 ++ .../IntrinsicTasks/ItemGroupLoggingHelper.cs | 1 - .../RequestBuilder/IntrinsicTasks/MSBuild.cs | 1 - .../Components/RequestBuilder/ItemBucket.cs | 1 - .../Components/RequestBuilder/Lookup.cs | 1 - .../RequestBuilder/RequestBuilder.cs | 1 - .../Components/RequestBuilder/TaskHost.cs | 7 ++++--- .../Components/Scheduler/ScheduleResponse.cs | 2 -- .../BackEnd/Components/Scheduler/Scheduler.cs | 1 - .../SdkResolution/SdkResolverLoader.cs | 1 - src/Build/BackEnd/Node/NodeConfiguration.cs | 1 - src/Build/BackEnd/Node/OutOfProcNode.cs | 4 +++- src/Build/BackEnd/Node/OutOfProcServerNode.cs | 2 -- src/Build/BackEnd/Shared/BuildResult.cs | 4 ++-- .../BackEnd/Shared/EventsCreatorHelper.cs | 4 ---- .../Checks/EmbeddedResourceCheck.cs | 2 +- .../Checks/SharedOutputPathCheck.cs | 2 +- .../Infrastructure/ConfigurationProvider.cs | 4 ++-- .../BuildCheck/OM/BuildCheckDataContext.cs | 2 +- src/Build/BuildCheck/OM/PropertyReadData.cs | 2 +- .../ICopyOnWritePropertyDictionary.cs | 4 ---- src/Build/Collections/IItemDictionary.cs | 5 ----- src/Build/Collections/IMultiDictionary.cs | 4 ---- .../IRetrievableValuedEntryHashSet.cs | 3 --- .../RetrievableValuedEntryHashSet.cs | 1 - .../Solution/ProjectInSolution.cs | 12 +++++++---- .../Construction/Solution/SolutionFile.cs | 1 - src/Build/Definition/ProjectItemDefinition.cs | 1 - src/Build/Definition/Toolset.cs | 3 ++- src/Build/Definition/ToolsetLocalReader.cs | 1 - src/Build/Definition/ToolsetRegistryReader.cs | 1 - .../InvalidToolsetDefinitionException.cs | 6 +++--- src/Build/Evaluation/ConditionEvaluator.cs | 1 - .../Conditionals/AndExpressionNode.cs | 1 - .../FunctionCallExpressionNode.cs | 1 - .../Conditionals/GenericExpressionNode.cs | 1 - .../MultipleComparisonExpressionNode.cs | 1 - .../Conditionals/NotExpressionNode.cs | 1 - .../NumericComparisonExpressionNode.cs | 1 - .../Conditionals/OperatorExpressionNode.cs | 2 -- .../Conditionals/OrExpressionNode.cs | 1 - .../Conditionals/StringExpressionNode.cs | 1 - src/Build/Evaluation/Expander.cs | 3 +-- .../Evaluation/Expander/ArgumentParser.cs | 5 ++--- .../Evaluation/Expander/WellKnownFunctions.cs | 5 ----- src/Build/Evaluation/IItemFactory.cs | 1 - src/Build/Evaluation/IntrinsicFunctions.cs | 8 +++---- src/Build/Evaluation/ItemSpec.cs | 1 - .../LazyItemEvaluator.ItemFactoryWrapper.cs | 1 - .../Evaluation/ProjectRootElementCache.cs | 1 - .../DirectoryCacheFileSystemWrapper.cs | 4 ++-- src/Build/Graph/GraphBuildRequestData.cs | 3 ++- src/Build/Graph/GraphBuildSubmission.cs | 2 -- src/Build/Graph/ProjectGraphNode.cs | 1 - ...ableGlobalPropertiesCollectionConverter.cs | 4 ---- .../ImmutableItemDefinitionsListConverter.cs | 3 --- .../ImmutableItemDictionary.cs | 3 --- ...ImmutableLinkedMultiDictionaryConverter.cs | 4 ---- .../Instance/ProjectItemDefinitionInstance.cs | 1 - .../Instance/TaskFactories/TaskHostTask.cs | 6 ++++-- src/Build/Instance/TaskRegistry.cs | 2 -- .../Logging/BinaryLogger/BinaryLogger.cs | 1 - .../Postprocessing/ArchiveFile.cs | 2 -- .../Postprocessing/StreamExtensions.cs | 2 -- src/Build/Logging/ConsoleLogger.cs | 4 ---- src/Build/Logging/SimpleErrorLogger.cs | 1 - .../Logging/TerminalLogger/TerminalLogger.cs | 2 -- .../TerminalLogger/TerminalNodeStatus.cs | 4 ++-- .../TerminalLogger/TerminalProjectInfo.cs | 1 - .../InternalTelemetryConsumingLogger.cs | 2 -- .../TelemetryForwarderProvider.cs | 3 +-- src/Build/Utilities/NuGetFrameworkWrapper.cs | 3 ++- src/Build/Utilities/Utilities.cs | 3 --- src/Build/Xml/ProjectXmlUtilities.cs | 1 - .../CheckConfigurationEffectiveTests.cs | 6 +++--- .../CheckConfiguration_Test.cs | 2 +- .../ConfigurationProvider_Tests.cs | 2 +- .../DoubleWritesAnalyzer_Tests.cs | 3 --- src/BuildCheck.UnitTests/EndToEndTests.cs | 1 - .../ErrorOnRegisteredAction.cs | 1 - .../InvalidCustomCheck/InvalidCheck.cs | 2 -- src/Directory.BeforeCommon.targets | 1 + .../BuildCanceledEventArgs_Tests.cs | 4 ---- .../BuildCheckTracingEventArgs_Tests.cs | 3 --- .../BuildSubmissionStartedEventArgs_Tests.cs | 3 --- src/Framework.UnitTests/EventArgs_Tests.cs | 2 -- .../FileClassifier_Tests.cs | 1 - .../GeneratedFileUsedEventArgs_Tests.cs | 4 ---- .../OperatingSystem_Tests.cs | 1 - .../ProjectStartedEventArgs_Tests.cs | 1 - .../WorkerNodeTelemetryEventArgs_Tests.cs | 7 ++----- src/Framework/BinaryTranslator.cs | 1 - src/Framework/BuildCanceledEventArgs.cs | 1 - .../BuildCheck/BuildCheckTracingData.cs | 2 -- .../BuildCheck/EnumerableExtensions.cs | 2 ++ src/Framework/BuildErrorEventArgs.cs | 2 ++ src/Framework/BuildMessageEventArgs.cs | 2 ++ src/Framework/BuildRequestDataFlags.cs | 4 ---- .../BuildSubmissionStartedEventArgs.cs | 1 - src/Framework/BuildWarningEventArgs.cs | 2 ++ src/Framework/ChangeWaves.cs | 2 ++ .../CriticalBuildMessageEventArgs.cs | 2 ++ src/Framework/CriticalTaskException.cs | 4 ---- src/Framework/CustomBuildEventArgs.cs | 2 ++ .../EnvironmentVariableReadEventArgs.cs | 2 -- src/Framework/ExtendedBuildErrorEventArgs.cs | 2 ++ .../ExtendedBuildWarningEventArgs.cs | 2 ++ src/Framework/Features.cs | 4 ---- src/Framework/FileClassifier.cs | 1 - src/Framework/GeneratedFileUsedEventArgs.cs | 1 - src/Framework/LazyFormattedBuildEventArgs.cs | 2 ++ src/Framework/Logging/AnsiDetector.cs | 1 - .../Logging/LoggerParametersHelper.cs | 3 --- .../MetaProjectGeneratedEventArgs.cs | 2 +- src/Framework/NativeMethods.cs | 5 ++++- .../Profiler/EvaluationIdProvider.cs | 1 - src/Framework/ProjectImportedEventArgs.cs | 2 ++ src/Framework/TargetSkippedEventArgs.cs | 2 ++ src/Framework/Telemetry/ActivityExtensions.cs | 4 ++-- .../InternalTelemetryForwardingLogger.cs | 2 +- .../LoggingConfigurationTelemetry.cs | 1 - .../Telemetry/OpenTelemetryManager.cs | 4 +--- .../Telemetry/WorkerNodeTelemetryEventArgs.cs | 2 ++ .../CommandLineSwitches_Tests.cs | 2 -- src/MSBuild.UnitTests/MSBuildServer_Tests.cs | 1 - .../ValidateAssemblyLoadContext.cs | 2 +- src/MSBuild/AutomaticEncodingRestorer.cs | 1 - src/MSBuild/CommandLineSwitches.cs | 1 - src/MSBuild/OutOfProcTaskHostNode.cs | 1 - src/MSBuild/PerformanceLogEventListener.cs | 1 - src/Shared/BuildEnvironmentHelper.cs | 1 - src/Shared/CanonicalError.cs | 2 ++ src/Shared/CommunicationsUtilities.cs | 2 ++ src/Shared/ConversionUtilities.cs | 2 ++ src/Shared/ExceptionHandling.cs | 1 - src/Shared/IElementLocation.cs | 1 - src/Shared/LogMessagePacketBase.cs | 21 +++++++------------ src/Shared/MSBuildLoadContext.cs | 1 - src/Shared/NamedPipeUtil.cs | 1 - src/Shared/NodeEndpointOutOfProcBase.cs | 1 - src/Shared/NodePipeBase.cs | 1 - src/Shared/NodePipeClient.cs | 6 +++--- src/Shared/NodePipeServer.cs | 2 ++ src/Shared/ProjectWriter.cs | 1 - src/Shared/QuotingUtilities.cs | 1 - src/Shared/TaskHostConfiguration.cs | 1 - src/Shared/TaskHostTaskComplete.cs | 2 +- src/Shared/TaskLoggingHelper.cs | 2 ++ src/Shared/TaskParameter.cs | 2 -- src/Shared/Tracing.cs | 2 +- src/Shared/UnitTests/AssemblyNameEx_Tests.cs | 1 - .../UnitTests/CommunicationUtilities_Tests.cs | 1 - .../UnitTests/CopyOnWriteDictionary_Tests.cs | 2 -- src/Shared/UnitTests/ErrorUtilities_Tests.cs | 2 -- src/Shared/UnitTests/FileMatcher_Tests.cs | 1 - src/Shared/UnitTests/FileUtilities_Tests.cs | 1 - .../UnitTests/NativeMethodsShared_Tests.cs | 1 - src/Shared/UnitTests/XmakeAttributes_Tests.cs | 1 - src/Shared/XMakeElements.cs | 3 ++- src/StringTools/FowlerNollVo1aHash.cs | 2 -- .../AssemblyDependency/Miscellaneous.cs | 1 - .../ResolveAssemblyReferenceTestFixture.cs | 1 - .../SpecificVersionPrimary.cs | 1 - .../AssemblyDependency/SuggestedRedirects.cs | 1 - src/Tasks.UnitTests/AssignCulture_Tests.cs | 2 -- .../AssignLinkMetadata_Tests.cs | 1 - src/Tasks.UnitTests/AssignTargetPath_Tests.cs | 1 - src/Tasks.UnitTests/AxImp_Tests.cs | 2 -- src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs | 1 - ...skFactoryEmbeddedFileInBinlogTestHelper.cs | 1 - src/Tasks.UnitTests/ComReference_Tests.cs | 1 - src/Tasks.UnitTests/CombinePath_Tests.cs | 2 -- ...bineTargetFrameworkInfoProperties_Tests.cs | 1 - src/Tasks.UnitTests/CreateItem_Tests.cs | 4 ---- src/Tasks.UnitTests/Delete_Tests.cs | 1 - src/Tasks.UnitTests/Exec_Tests.cs | 1 - src/Tasks.UnitTests/FileStateTests.cs | 1 - src/Tasks.UnitTests/FindUnderPath_Tests.cs | 1 - src/Tasks.UnitTests/FormatUrl_Tests.cs | 1 - .../GetAssembliesMetadata_Tests.cs | 4 ++-- .../GetInstalledSDKLocations_Tests.cs | 1 - src/Tasks.UnitTests/GetSDKReference_Tests.cs | 2 -- src/Tasks.UnitTests/HintPathResolver_Tests.cs | 1 - src/Tasks.UnitTests/MakeDir_Tests.cs | 1 - src/Tasks.UnitTests/Move_Tests.cs | 1 - src/Tasks.UnitTests/OutputPathTests.cs | 1 - src/Tasks.UnitTests/PortableTasks_Tests.cs | 1 - src/Tasks.UnitTests/RegressionTests.cs | 1 - .../ResolveComReference_Tests.cs | 2 -- .../ResolveSDKReference_Tests.cs | 1 - .../GenerateResourceOutOfProc_Tests.cs | 2 -- .../GenerateResource_Tests.cs | 2 -- .../RoslynCodeTaskFactory_Tests.cs | 1 - src/Tasks.UnitTests/SGen_Tests.cs | 1 - .../SdkToolsPathUtility_Tests.cs | 1 - src/Tasks.UnitTests/SecurityUtil_Tests.cs | 6 ------ .../Projects/Custom_COM/Custom_COM/Class1.cs | 1 - .../Custom_COM/Properties/AssemblyInfo.cs | 3 +-- src/Tasks.UnitTests/Unzip_Tests.cs | 2 -- .../WriteCodeFragment_Tests.cs | 1 - src/Tasks.UnitTests/XmlPeek_Tests.cs | 1 - .../AssemblyDependency/AssemblyAttributes.cs | 6 ------ .../AssemblyDependency/AssemblyInformation.cs | 3 --- .../GenerateBindingRedirects.cs | 4 ++-- .../AssemblyDependency/InstalledAssemblies.cs | 1 - .../ResolveAssemblyReference.cs | 4 +++- src/Tasks/Copy.cs | 3 --- src/Tasks/CreateProperty.cs | 1 - src/Tasks/CultureInfoCache.cs | 1 - src/Tasks/Error.cs | 2 ++ src/Tasks/Exec.cs | 2 ++ src/Tasks/FileIO/VerifyFileHash.cs | 2 +- src/Tasks/GenerateApplicationManifest.cs | 1 - src/Tasks/GenerateManifestBase.cs | 1 - src/Tasks/GenerateResource.cs | 4 ++-- src/Tasks/GetAssembliesMetadata.cs | 9 -------- src/Tasks/GetAssemblyIdentity.cs | 6 ++++-- src/Tasks/ManifestUtil/ApplicationManifest.cs | 1 - .../ManifestUtil/EmbeddedManifestReader.cs | 1 - src/Tasks/ManifestUtil/Manifest.cs | 1 - src/Tasks/ManifestUtil/ManifestFormatter.cs | 1 - src/Tasks/ManifestUtil/ManifestWriter.cs | 1 - src/Tasks/ManifestUtil/PathUtil.cs | 3 +++ src/Tasks/ManifestUtil/TrustInfo.cs | 1 - src/Tasks/ManifestUtil/XmlUtil.cs | 1 - src/Tasks/NativeMethods.cs | 2 ++ src/Tasks/ResolveSDKReference.cs | 1 - .../RoslynCodeTaskFactoryCompilers.cs | 6 ++++-- src/Tasks/TaskRequiresFramework.cs | 3 ++- src/Tasks/Warning.cs | 2 ++ src/Tasks/XamlTaskFactory/RelationsParser.cs | 2 -- src/UnitTests.Shared/DriveMapping.cs | 2 -- src/UnitTests.Shared/DummyMappedDrive.cs | 1 - src/UnitTests.Shared/DummyMappedDriveUtils.cs | 1 - src/UnitTests.Shared/ObjectModelHelpers.cs | 2 -- src/UnitTests.Shared/RunnerUtilities.cs | 3 +-- .../EncodingUtilities_Tests.cs | 4 ---- src/Utilities.UnitTests/MuxLogger_Tests.cs | 2 -- .../ProcessorArchitecture_Tests.cs | 1 - src/Utilities.UnitTests/TaskItem_Tests.cs | 1 - .../TaskLoggingHelper_Tests.cs | 1 - .../ToolLocationHelper_Tests.cs | 1 - src/Utilities/LockCheck.cs | 1 - src/Utilities/TargetPlatformSDK.cs | 1 - src/Utilities/ToolLocationHelper.cs | 1 - 370 files changed, 179 insertions(+), 620 deletions(-) diff --git a/.editorconfig b/.editorconfig index e64a0519df7..2927cad5369 100644 --- a/.editorconfig +++ b/.editorconfig @@ -216,7 +216,7 @@ dotnet_analyzer_diagnostic.category-Style.severity = warning dotnet_diagnostic.IDE0004.severity = suggestion # IDE0005: Remove unnecessary usings/imports -dotnet_diagnostic.IDE0005.severity = none +dotnet_diagnostic.IDE0005.severity = warning # Use explicit type instead of 'var' dotnet_diagnostic.IDE0008.severity = suggestion diff --git a/src/Build.OM.UnitTests/Construction/ConstructionEditing_Tests.cs b/src/Build.OM.UnitTests/Construction/ConstructionEditing_Tests.cs index 6cd24d2c366..aaa2c454210 100644 --- a/src/Build.OM.UnitTests/Construction/ConstructionEditing_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ConstructionEditing_Tests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Text; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Shared; diff --git a/src/Build.OM.UnitTests/Construction/ProjectFormatting_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectFormatting_Tests.cs index f7567f061ac..cc0562f9752 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectFormatting_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectFormatting_Tests.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Text; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Shared; diff --git a/src/Build.OM.UnitTests/Construction/ProjectItemGroupElement_tests.cs b/src/Build.OM.UnitTests/Construction/ProjectItemGroupElement_tests.cs index 5be0d1fe5dc..706fb7d2b79 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectItemGroupElement_tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectItemGroupElement_tests.cs @@ -1,9 +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.IO; -using System.Xml; - using Microsoft.Build.Construction; using Xunit; diff --git a/src/Build.OM.UnitTests/Construction/ProjectPropertyGroupElement_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectPropertyGroupElement_Tests.cs index 10b21f0a8ad..35cad10fae7 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectPropertyGroupElement_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectPropertyGroupElement_Tests.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.IO; -using System.Xml; using Microsoft.Build.Construction; using Xunit; diff --git a/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs index 271bbd11e8e..95256f674d9 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs @@ -9,7 +9,6 @@ using System.Security.AccessControl; using System.Security.Principal; #endif -using System.Reflection; using System.Text; using System.Threading; using System.Xml; @@ -19,10 +18,7 @@ using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; using ProjectCollection = Microsoft.Build.Evaluation.ProjectCollection; -using Shouldly; using Xunit; -using Microsoft.Build.Framework; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Build.OM.UnitTests/Construction/ProjectTargetElement_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectTargetElement_Tests.cs index 2cb88649b35..41f835cf229 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectTargetElement_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectTargetElement_Tests.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Shouldly; using Xunit; diff --git a/src/Build.OM.UnitTests/Construction/ProjectTaskElement_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectTaskElement_Tests.cs index 353d2031a3d..679d410d8de 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectTaskElement_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectTaskElement_Tests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; -using System.Xml; using Microsoft.Build.Construction; using Xunit; using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; diff --git a/src/Build.OM.UnitTests/Construction/SolutionFile_Tests.cs b/src/Build.OM.UnitTests/Construction/SolutionFile_Tests.cs index cfd643aea0e..836d884c5f1 100644 --- a/src/Build.OM.UnitTests/Construction/SolutionFile_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/SolutionFile_Tests.cs @@ -1,12 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Text; using System.Threading; using Microsoft.Build.Construction; using Microsoft.Build.Exceptions; diff --git a/src/Build.OM.UnitTests/Definition/DefinitionEditing_Tests.cs b/src/Build.OM.UnitTests/Definition/DefinitionEditing_Tests.cs index 3e85948675a..6708096bd7e 100644 --- a/src/Build.OM.UnitTests/Definition/DefinitionEditing_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/DefinitionEditing_Tests.cs @@ -5,12 +5,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; -using Microsoft.Build.Shared; using Xunit; #nullable disable diff --git a/src/Build.OM.UnitTests/Definition/EditingElementsReferencedByOrReferences_Tests.cs b/src/Build.OM.UnitTests/Definition/EditingElementsReferencedByOrReferences_Tests.cs index 018dc9dca91..3c7e530d9d3 100644 --- a/src/Build.OM.UnitTests/Definition/EditingElementsReferencedByOrReferences_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/EditingElementsReferencedByOrReferences_Tests.cs @@ -3,9 +3,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.Evaluation; using Xunit; diff --git a/src/Build.OM.UnitTests/Definition/ProjectItemDefinition_Tests.cs b/src/Build.OM.UnitTests/Definition/ProjectItemDefinition_Tests.cs index 9d795ecaa6f..dcce4d61853 100644 --- a/src/Build.OM.UnitTests/Definition/ProjectItemDefinition_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProjectItemDefinition_Tests.cs @@ -10,7 +10,6 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Xunit; using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; diff --git a/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs b/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs index 490cc3cce47..75b2e82319d 100644 --- a/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Definition; using Microsoft.Build.Engine.UnitTests.Globbing; @@ -16,7 +15,6 @@ using Microsoft.Build.UnitTests.Shared; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; #nullable disable diff --git a/src/Build.OM.UnitTests/Definition/ProjectMetadata_Tests.cs b/src/Build.OM.UnitTests/Definition/ProjectMetadata_Tests.cs index c0807e37e68..f7bae907db1 100644 --- a/src/Build.OM.UnitTests/Definition/ProjectMetadata_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProjectMetadata_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Shared; diff --git a/src/Build.OM.UnitTests/Definition/ProjectProperty_Tests.cs b/src/Build.OM.UnitTests/Definition/ProjectProperty_Tests.cs index 3ecc456a3cb..c028fd294cd 100644 --- a/src/Build.OM.UnitTests/Definition/ProjectProperty_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProjectProperty_Tests.cs @@ -4,11 +4,9 @@ using System; using System.Collections.Generic; using System.IO; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; -using Microsoft.Build.Shared; using Xunit; #nullable disable diff --git a/src/Build.OM.UnitTests/Definition/ProtectImports_Tests.cs b/src/Build.OM.UnitTests/Definition/ProtectImports_Tests.cs index b22c8bf0ab9..6a337e3bbc9 100644 --- a/src/Build.OM.UnitTests/Definition/ProtectImports_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProtectImports_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.Evaluation; using Xunit; diff --git a/src/Build.OM.UnitTests/Instance/ProjectInstance_Tests.cs b/src/Build.OM.UnitTests/Instance/ProjectInstance_Tests.cs index b8845cd6244..c7278144dae 100644 --- a/src/Build.OM.UnitTests/Instance/ProjectInstance_Tests.cs +++ b/src/Build.OM.UnitTests/Instance/ProjectInstance_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; diff --git a/src/Build.OM.UnitTests/Instance/ProjectItemInstance_Tests.cs b/src/Build.OM.UnitTests/Instance/ProjectItemInstance_Tests.cs index e8259bd3120..0e060a450d7 100644 --- a/src/Build.OM.UnitTests/Instance/ProjectItemInstance_Tests.cs +++ b/src/Build.OM.UnitTests/Instance/ProjectItemInstance_Tests.cs @@ -3,19 +3,13 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.Construction; -using Microsoft.Build.Definition; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.UnitTests.Shared; -using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; #nullable disable diff --git a/src/Build.OM.UnitTests/Instance/ProjectOnErrorInstance_Tests.cs b/src/Build.OM.UnitTests/Instance/ProjectOnErrorInstance_Tests.cs index 14346060f12..457e4bb27fb 100644 --- a/src/Build.OM.UnitTests/Instance/ProjectOnErrorInstance_Tests.cs +++ b/src/Build.OM.UnitTests/Instance/ProjectOnErrorInstance_Tests.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.IO; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; diff --git a/src/Build.OM.UnitTests/Instance/ProjectTargetInstance_Tests.cs b/src/Build.OM.UnitTests/Instance/ProjectTargetInstance_Tests.cs index 3e9ab10b3df..fb41c9da8a1 100644 --- a/src/Build.OM.UnitTests/Instance/ProjectTargetInstance_Tests.cs +++ b/src/Build.OM.UnitTests/Instance/ProjectTargetInstance_Tests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; diff --git a/src/Build.OM.UnitTests/Instance/ProjectTaskInstance_Tests.cs b/src/Build.OM.UnitTests/Instance/ProjectTaskInstance_Tests.cs index 2acc85a6995..c2e0a60ae10 100644 --- a/src/Build.OM.UnitTests/Instance/ProjectTaskInstance_Tests.cs +++ b/src/Build.OM.UnitTests/Instance/ProjectTaskInstance_Tests.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.IO; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; diff --git a/src/Build.OM.UnitTests/Instance/ProjectTaskOutputItemInstance_Tests.cs b/src/Build.OM.UnitTests/Instance/ProjectTaskOutputItemInstance_Tests.cs index 716c675982d..31d85a9e608 100644 --- a/src/Build.OM.UnitTests/Instance/ProjectTaskOutputItemInstance_Tests.cs +++ b/src/Build.OM.UnitTests/Instance/ProjectTaskOutputItemInstance_Tests.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.IO; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; diff --git a/src/Build.OM.UnitTests/Instance/ProjectTaskOutputPropertyInstance_Tests.cs b/src/Build.OM.UnitTests/Instance/ProjectTaskOutputPropertyInstance_Tests.cs index 8c9e4770842..fb17c3b113b 100644 --- a/src/Build.OM.UnitTests/Instance/ProjectTaskOutputPropertyInstance_Tests.cs +++ b/src/Build.OM.UnitTests/Instance/ProjectTaskOutputPropertyInstance_Tests.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.IO; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; diff --git a/src/Build.OM.UnitTests/NugetRestoreTests.cs b/src/Build.OM.UnitTests/NugetRestoreTests.cs index ee35a83c93a..d5b4b8bbf38 100644 --- a/src/Build.OM.UnitTests/NugetRestoreTests.cs +++ b/src/Build.OM.UnitTests/NugetRestoreTests.cs @@ -1,13 +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.IO; using Microsoft.Build.UnitTests; using Microsoft.Build.UnitTests.Shared; using Shouldly; -using System.IO; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; namespace Microsoft.Build.Engine.OM.UnitTests { diff --git a/src/Build.OM.UnitTests/ObjectModelRemoting/RemoteProjectsProviderMock/ExporterMock.cs b/src/Build.OM.UnitTests/ObjectModelRemoting/RemoteProjectsProviderMock/ExporterMock.cs index 05112a6ed62..d21ca7a50d6 100644 --- a/src/Build.OM.UnitTests/ObjectModelRemoting/RemoteProjectsProviderMock/ExporterMock.cs +++ b/src/Build.OM.UnitTests/ObjectModelRemoting/RemoteProjectsProviderMock/ExporterMock.cs @@ -7,9 +7,7 @@ namespace Microsoft.Build.UnitTests.OM.ObjectModelRemoting { using System; using System.Collections.Generic; - using System.IO; using System.Threading; - using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.ObjectModelRemoting; diff --git a/src/Build.UnitTests/BackEnd/BinaryTranslator_Tests.cs b/src/Build.UnitTests/BackEnd/BinaryTranslator_Tests.cs index 55834d7d650..9a0bb05138a 100644 --- a/src/Build.UnitTests/BackEnd/BinaryTranslator_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BinaryTranslator_Tests.cs @@ -8,7 +8,6 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.CompilerServices; using Microsoft.Build.BackEnd; using Microsoft.Build.Exceptions; using Microsoft.Build.Framework; diff --git a/src/Build.UnitTests/BackEnd/BuildEventArgTransportSink_Tests.cs b/src/Build.UnitTests/BackEnd/BuildEventArgTransportSink_Tests.cs index 2a2e8822ba1..d36e9d716a3 100644 --- a/src/Build.UnitTests/BackEnd/BuildEventArgTransportSink_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildEventArgTransportSink_Tests.cs @@ -5,9 +5,7 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Build.UnitTests/BackEnd/BuildManager_Logging_Tests.cs b/src/Build.UnitTests/BackEnd/BuildManager_Logging_Tests.cs index 99f6603f61c..d588c980ab4 100644 --- a/src/Build.UnitTests/BackEnd/BuildManager_Logging_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildManager_Logging_Tests.cs @@ -15,7 +15,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; using static Microsoft.Build.UnitTests.ObjectModelHelpers; #nullable disable diff --git a/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs b/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs index 2111a5ee369..35bf1f4cc3a 100644 --- a/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs @@ -10,7 +10,6 @@ using System.Linq; using System.Reflection; using System.Threading; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.Collections; diff --git a/src/Build.UnitTests/BackEnd/BuildRequestEngine_Tests.cs b/src/Build.UnitTests/BackEnd/BuildRequestEngine_Tests.cs index b4efa7ea860..b7ea019500a 100644 --- a/src/Build.UnitTests/BackEnd/BuildRequestEngine_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildRequestEngine_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Threading; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.Evaluation; using Microsoft.Build.Exceptions; diff --git a/src/Build.UnitTests/BackEnd/BuildRequestEntry_Tests.cs b/src/Build.UnitTests/BackEnd/BuildRequestEntry_Tests.cs index b994f4ceb2c..9e84a022a90 100644 --- a/src/Build.UnitTests/BackEnd/BuildRequestEntry_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildRequestEntry_Tests.cs @@ -7,7 +7,6 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Unittest; using Xunit; diff --git a/src/Build.UnitTests/BackEnd/BuildRequest_Tests.cs b/src/Build.UnitTests/BackEnd/BuildRequest_Tests.cs index 2090f4c4807..5e103b5ef66 100644 --- a/src/Build.UnitTests/BackEnd/BuildRequest_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildRequest_Tests.cs @@ -9,7 +9,6 @@ using Microsoft.Build.Framework; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Build.UnitTests/BackEnd/BuildResult_Tests.cs b/src/Build.UnitTests/BackEnd/BuildResult_Tests.cs index dec50951300..1b94ac3357f 100644 --- a/src/Build.UnitTests/BackEnd/BuildResult_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildResult_Tests.cs @@ -9,7 +9,6 @@ using Microsoft.Build.Exceptions; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Unittest; using Xunit; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; diff --git a/src/Build.UnitTests/BackEnd/CacheAggregator_Tests.cs b/src/Build.UnitTests/BackEnd/CacheAggregator_Tests.cs index 488f5f8bfd9..db1dcd72cb4 100644 --- a/src/Build.UnitTests/BackEnd/CacheAggregator_Tests.cs +++ b/src/Build.UnitTests/BackEnd/CacheAggregator_Tests.cs @@ -6,8 +6,6 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Internal; -using Microsoft.Build.Shared; using Microsoft.Build.Unittest; using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/BackEnd/CentralForwardingLogger_Tests.cs b/src/Build.UnitTests/BackEnd/CentralForwardingLogger_Tests.cs index da450fb9228..64f34e78d8f 100644 --- a/src/Build.UnitTests/BackEnd/CentralForwardingLogger_Tests.cs +++ b/src/Build.UnitTests/BackEnd/CentralForwardingLogger_Tests.cs @@ -3,7 +3,6 @@ using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Xunit; #nullable disable diff --git a/src/Build.UnitTests/BackEnd/ConfigurationMetadata_Tests.cs b/src/Build.UnitTests/BackEnd/ConfigurationMetadata_Tests.cs index 7900e9564cb..859e5507f68 100644 --- a/src/Build.UnitTests/BackEnd/ConfigurationMetadata_Tests.cs +++ b/src/Build.UnitTests/BackEnd/ConfigurationMetadata_Tests.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; diff --git a/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs b/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs index 6f5fd31c934..2a48fe4d81a 100644 --- a/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs +++ b/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Shared; using Shouldly; using Xunit; -using Xunit.Abstractions; #nullable disable diff --git a/src/Build.UnitTests/BackEnd/EventRedirectorToSink_Tests.cs b/src/Build.UnitTests/BackEnd/EventRedirectorToSink_Tests.cs index d36c7ee0667..1971cca0763 100644 --- a/src/Build.UnitTests/BackEnd/EventRedirectorToSink_Tests.cs +++ b/src/Build.UnitTests/BackEnd/EventRedirectorToSink_Tests.cs @@ -3,7 +3,6 @@ using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Xunit; #nullable disable diff --git a/src/Build.UnitTests/BackEnd/EventSourceTestHelper.cs b/src/Build.UnitTests/BackEnd/EventSourceTestHelper.cs index 8d3929afaba..e2ed8378c07 100644 --- a/src/Build.UnitTests/BackEnd/EventSourceTestHelper.cs +++ b/src/Build.UnitTests/BackEnd/EventSourceTestHelper.cs @@ -1,12 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using System.Collections.Generic; using System.Diagnostics.Tracing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.Build.Engine.UnitTests.BackEnd { diff --git a/src/Build.UnitTests/BackEnd/IntrinsicTask_Tests.cs b/src/Build.UnitTests/BackEnd/IntrinsicTask_Tests.cs index 7f0ccecd569..3d65794eb1f 100644 --- a/src/Build.UnitTests/BackEnd/IntrinsicTask_Tests.cs +++ b/src/Build.UnitTests/BackEnd/IntrinsicTask_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.Collections; using Microsoft.Build.Construction; diff --git a/src/Build.UnitTests/BackEnd/LoggingConfigurationTelemetry_Tests.cs b/src/Build.UnitTests/BackEnd/LoggingConfigurationTelemetry_Tests.cs index d6e66cc6ecd..56ec028ddf1 100644 --- a/src/Build.UnitTests/BackEnd/LoggingConfigurationTelemetry_Tests.cs +++ b/src/Build.UnitTests/BackEnd/LoggingConfigurationTelemetry_Tests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. #nullable disable -using System; -using System.Globalization; using System.Linq; using Microsoft.Build.Framework.Telemetry; using Shouldly; diff --git a/src/Build.UnitTests/BackEnd/LoggingContext_Tests.cs b/src/Build.UnitTests/BackEnd/LoggingContext_Tests.cs index 7ccbcc227b7..020d527ab3f 100644 --- a/src/Build.UnitTests/BackEnd/LoggingContext_Tests.cs +++ b/src/Build.UnitTests/BackEnd/LoggingContext_Tests.cs @@ -3,7 +3,6 @@ using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Shouldly; using Xunit; using Xunit.Abstractions; diff --git a/src/Build.UnitTests/BackEnd/LoggingService_Tests.cs b/src/Build.UnitTests/BackEnd/LoggingService_Tests.cs index eea8d075466..4d42f596bcb 100644 --- a/src/Build.UnitTests/BackEnd/LoggingService_Tests.cs +++ b/src/Build.UnitTests/BackEnd/LoggingService_Tests.cs @@ -14,7 +14,6 @@ using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Logging; -using Microsoft.Build.Shared; using Microsoft.Build.UnitTests.BackEnd; using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/BackEnd/LoggingServicesLogMethod_Tests.cs b/src/Build.UnitTests/BackEnd/LoggingServicesLogMethod_Tests.cs index fb932fb70d5..3fe5fcd8e6e 100644 --- a/src/Build.UnitTests/BackEnd/LoggingServicesLogMethod_Tests.cs +++ b/src/Build.UnitTests/BackEnd/LoggingServicesLogMethod_Tests.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Construction; diff --git a/src/Build.UnitTests/BackEnd/Lookup_Tests.cs b/src/Build.UnitTests/BackEnd/Lookup_Tests.cs index 9ee0264517d..90c0c170a0d 100644 --- a/src/Build.UnitTests/BackEnd/Lookup_Tests.cs +++ b/src/Build.UnitTests/BackEnd/Lookup_Tests.cs @@ -8,7 +8,6 @@ using Microsoft.Build.Collections; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Xunit; #nullable disable diff --git a/src/Build.UnitTests/BackEnd/MockHost.cs b/src/Build.UnitTests/BackEnd/MockHost.cs index 1f08b92bf79..3326ddfd49e 100644 --- a/src/Build.UnitTests/BackEnd/MockHost.cs +++ b/src/Build.UnitTests/BackEnd/MockHost.cs @@ -5,10 +5,10 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.BackEnd.SdkResolution; -using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Engine.UnitTests.BackEnd; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; +using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.TelemetryInfra; using LegacyThreadingData = Microsoft.Build.Execution.LegacyThreadingData; diff --git a/src/Build.UnitTests/BackEnd/NodeConfiguration_Tests.cs b/src/Build.UnitTests/BackEnd/NodeConfiguration_Tests.cs index bb0c19a4415..bd3b9855abf 100644 --- a/src/Build.UnitTests/BackEnd/NodeConfiguration_Tests.cs +++ b/src/Build.UnitTests/BackEnd/NodeConfiguration_Tests.cs @@ -2,10 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.BackEnd; using Microsoft.Build.Execution; using Microsoft.Build.Logging; diff --git a/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs b/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs index fd8178dcadd..4b922bde816 100644 --- a/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs +++ b/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs @@ -8,9 +8,8 @@ using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; -using LegacyThreadingData = Microsoft.Build.Execution.LegacyThreadingData; using Xunit; +using LegacyThreadingData = Microsoft.Build.Execution.LegacyThreadingData; #nullable disable diff --git a/src/Build.UnitTests/BackEnd/RedirectConsoleWriter_Tests.cs b/src/Build.UnitTests/BackEnd/RedirectConsoleWriter_Tests.cs index 81caa63af47..b8adb10c2a2 100644 --- a/src/Build.UnitTests/BackEnd/RedirectConsoleWriter_Tests.cs +++ b/src/Build.UnitTests/BackEnd/RedirectConsoleWriter_Tests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; using System.Text; using System.Threading.Tasks; using Microsoft.Build.Experimental; diff --git a/src/Build.UnitTests/BackEnd/RequestedProjectState_Tests.cs b/src/Build.UnitTests/BackEnd/RequestedProjectState_Tests.cs index 034b3d1594a..166fd5750bc 100644 --- a/src/Build.UnitTests/BackEnd/RequestedProjectState_Tests.cs +++ b/src/Build.UnitTests/BackEnd/RequestedProjectState_Tests.cs @@ -4,9 +4,7 @@ using System.Collections.Generic; using FluentAssertions; using Microsoft.Build.Execution; -using Shouldly; using Xunit; -using Xunit.Abstractions; namespace Microsoft.Build.UnitTests.BackEnd { diff --git a/src/Build.UnitTests/BackEnd/ResultsCache_Tests.cs b/src/Build.UnitTests/BackEnd/ResultsCache_Tests.cs index eceb5123067..77f6dc04be6 100644 --- a/src/Build.UnitTests/BackEnd/ResultsCache_Tests.cs +++ b/src/Build.UnitTests/BackEnd/ResultsCache_Tests.cs @@ -8,11 +8,8 @@ using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.Construction; -using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Internal; -using Microsoft.Build.Shared; using Microsoft.Build.Unittest; using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs b/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs index 7b0cd8745c6..e470f6be153 100644 --- a/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs +++ b/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; diff --git a/src/Build.UnitTests/BackEnd/SdkResolverService_Tests.cs b/src/Build.UnitTests/BackEnd/SdkResolverService_Tests.cs index 4a891408cc9..10a1c5dcd6c 100644 --- a/src/Build.UnitTests/BackEnd/SdkResolverService_Tests.cs +++ b/src/Build.UnitTests/BackEnd/SdkResolverService_Tests.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; -using System.Configuration; -using System.Diagnostics.Tracing; using System.Linq; using System.Text.RegularExpressions; using System.Threading; diff --git a/src/Build.UnitTests/BackEnd/SdkResultItemComparison_Tests.cs b/src/Build.UnitTests/BackEnd/SdkResultItemComparison_Tests.cs index 6af6e6616b5..a1f051339fa 100644 --- a/src/Build.UnitTests/BackEnd/SdkResultItemComparison_Tests.cs +++ b/src/Build.UnitTests/BackEnd/SdkResultItemComparison_Tests.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.Collections.Generic; using Microsoft.Build.Framework; using Shouldly; diff --git a/src/Build.UnitTests/BackEnd/TargetBuilder_Tests.cs b/src/Build.UnitTests/BackEnd/TargetBuilder_Tests.cs index 11d5207ec9b..b39a91f56cb 100644 --- a/src/Build.UnitTests/BackEnd/TargetBuilder_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TargetBuilder_Tests.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.Collections; diff --git a/src/Build.UnitTests/BackEnd/TargetEntry_Tests.cs b/src/Build.UnitTests/BackEnd/TargetEntry_Tests.cs index b2cb7cd17bd..259b3f530dc 100644 --- a/src/Build.UnitTests/BackEnd/TargetEntry_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TargetEntry_Tests.cs @@ -7,7 +7,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.BackEnd.SdkResolution; diff --git a/src/Build.UnitTests/BackEnd/TargetUpToDateChecker_Tests.cs b/src/Build.UnitTests/BackEnd/TargetUpToDateChecker_Tests.cs index 743e165ca0a..d54a77ac90a 100644 --- a/src/Build.UnitTests/BackEnd/TargetUpToDateChecker_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TargetUpToDateChecker_Tests.cs @@ -7,7 +7,6 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; diff --git a/src/Build.UnitTests/BackEnd/TaskBuilderTestTask.cs b/src/Build.UnitTests/BackEnd/TaskBuilderTestTask.cs index 64ffc8fba5c..c66854b0206 100644 --- a/src/Build.UnitTests/BackEnd/TaskBuilderTestTask.cs +++ b/src/Build.UnitTests/BackEnd/TaskBuilderTestTask.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Reflection; using Microsoft.Build.Framework; diff --git a/src/Build.UnitTests/BackEnd/TaskBuilder_Tests.cs b/src/Build.UnitTests/BackEnd/TaskBuilder_Tests.cs index 68a8dea7eb0..f0155787682 100644 --- a/src/Build.UnitTests/BackEnd/TaskBuilder_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskBuilder_Tests.cs @@ -7,7 +7,6 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.Collections; diff --git a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs index 61b1551576c..b1a2a334e8d 100644 --- a/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskExecutionHost_Tests.cs @@ -8,7 +8,6 @@ using System.Linq; using System.Reflection; using System.Threading; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Collections; diff --git a/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs b/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs index dcf1f45727d..959142d4a67 100644 --- a/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs @@ -6,13 +6,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Text; using System.Threading; using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Utilities; using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs b/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs index ebb24ca82e6..401b0f9ea49 100644 --- a/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskHostFactory_Tests.cs @@ -6,7 +6,6 @@ using System.Globalization; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; using Microsoft.Build.UnitTests.BackEnd; diff --git a/src/Build.UnitTests/BackEnd/TaskHostTaskComplete_Tests.cs b/src/Build.UnitTests/BackEnd/TaskHostTaskComplete_Tests.cs index b6b6025441b..c475da6dcac 100644 --- a/src/Build.UnitTests/BackEnd/TaskHostTaskComplete_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskHostTaskComplete_Tests.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using Microsoft.Build.BackEnd; -using Microsoft.Build.Framework; using Microsoft.Build.Experimental.FileAccess; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Utilities; using Xunit; diff --git a/src/Build.UnitTests/BackEnd/TaskThatThrows.cs b/src/Build.UnitTests/BackEnd/TaskThatThrows.cs index b837e225740..f62541256cc 100644 --- a/src/Build.UnitTests/BackEnd/TaskThatThrows.cs +++ b/src/Build.UnitTests/BackEnd/TaskThatThrows.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Linq; using System.Reflection; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.Build.Engine.UnitTests; diff --git a/src/Build.UnitTests/BackEnd/TranslationHelpers.cs b/src/Build.UnitTests/BackEnd/TranslationHelpers.cs index 7d4736837ce..bdcad9d36bb 100644 --- a/src/Build.UnitTests/BackEnd/TranslationHelpers.cs +++ b/src/Build.UnitTests/BackEnd/TranslationHelpers.cs @@ -9,7 +9,6 @@ using System.Text; using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; -using Xunit; #nullable disable diff --git a/src/Build.UnitTests/BinaryLogger_Tests.cs b/src/Build.UnitTests/BinaryLogger_Tests.cs index 3ff827e24aa..9334bcf5314 100644 --- a/src/Build.UnitTests/BinaryLogger_Tests.cs +++ b/src/Build.UnitTests/BinaryLogger_Tests.cs @@ -6,7 +6,6 @@ using System.IO; using System.IO.Compression; using System.Linq; -using System.Reflection; using System.Text; using FakeItEasy; using FluentAssertions; diff --git a/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs b/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs index d987efa0fab..f500c937ab9 100644 --- a/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs +++ b/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Shared; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Build.UnitTests/BuildEventArgsDataEnumeration.cs b/src/Build.UnitTests/BuildEventArgsDataEnumeration.cs index 0f760d002c5..83042fcf5f5 100644 --- a/src/Build.UnitTests/BuildEventArgsDataEnumeration.cs +++ b/src/Build.UnitTests/BuildEventArgsDataEnumeration.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.Collections; using System.Collections.Generic; using System.Linq; diff --git a/src/Build.UnitTests/ChangeWaves_Tests.cs b/src/Build.UnitTests/ChangeWaves_Tests.cs index 9bfa098ee0d..d81e765c3ab 100644 --- a/src/Build.UnitTests/ChangeWaves_Tests.cs +++ b/src/Build.UnitTests/ChangeWaves_Tests.cs @@ -6,7 +6,6 @@ using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; -using Microsoft.Build.Utilities; using Shouldly; using Xunit; using Xunit.Abstractions; diff --git a/src/Build.UnitTests/Collections/MSBuildNameIgnoreCaseComparer_Tests.cs b/src/Build.UnitTests/Collections/MSBuildNameIgnoreCaseComparer_Tests.cs index be97777e5e3..dfc4482a09f 100644 --- a/src/Build.UnitTests/Collections/MSBuildNameIgnoreCaseComparer_Tests.cs +++ b/src/Build.UnitTests/Collections/MSBuildNameIgnoreCaseComparer_Tests.cs @@ -5,7 +5,6 @@ using Microsoft.Build.Collections; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Xunit; #nullable disable diff --git a/src/Build.UnitTests/Collections/OMcollections_tests.cs b/src/Build.UnitTests/Collections/OMcollections_tests.cs index 354a6bc9464..56273244f51 100644 --- a/src/Build.UnitTests/Collections/OMcollections_tests.cs +++ b/src/Build.UnitTests/Collections/OMcollections_tests.cs @@ -10,7 +10,6 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.UnitTests.BackEnd; using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/Construction/SolutionFile_NewParser_Tests.cs b/src/Build.UnitTests/Construction/SolutionFile_NewParser_Tests.cs index 014b8521524..a15fffd391d 100644 --- a/src/Build.UnitTests/Construction/SolutionFile_NewParser_Tests.cs +++ b/src/Build.UnitTests/Construction/SolutionFile_NewParser_Tests.cs @@ -1,13 +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; using System.Collections; using System.Collections.Generic; -using System.IO; using System.Threading; using Microsoft.Build.Construction; -using Microsoft.Build.Exceptions; using Microsoft.Build.Shared; using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; diff --git a/src/Build.UnitTests/Construction/SolutionFilter_Tests.cs b/src/Build.UnitTests/Construction/SolutionFilter_Tests.cs index 71a769aed3b..facbeff5386 100644 --- a/src/Build.UnitTests/Construction/SolutionFilter_Tests.cs +++ b/src/Build.UnitTests/Construction/SolutionFilter_Tests.cs @@ -15,9 +15,9 @@ using Microsoft.Build.Framework; using Microsoft.Build.Graph; using Microsoft.Build.UnitTests; +using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; using Microsoft.VisualStudio.SolutionPersistence.Serializer; -using Microsoft.VisualStudio.SolutionPersistence; using Shouldly; using Xunit; using Xunit.Abstractions; diff --git a/src/Build.UnitTests/Definition/ProjectHelpers.cs b/src/Build.UnitTests/Definition/ProjectHelpers.cs index b3be8049606..2f0ccf65fe7 100644 --- a/src/Build.UnitTests/Definition/ProjectHelpers.cs +++ b/src/Build.UnitTests/Definition/ProjectHelpers.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.IO; -using System.Xml; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; diff --git a/src/Build.UnitTests/Definition/ProjectItem_Tests.cs b/src/Build.UnitTests/Definition/ProjectItem_Tests.cs index ce86f880e14..bb33b9a9733 100644 --- a/src/Build.UnitTests/Definition/ProjectItem_Tests.cs +++ b/src/Build.UnitTests/Definition/ProjectItem_Tests.cs @@ -3,9 +3,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Xunit; diff --git a/src/Build.UnitTests/Definition/Project_Internal_Tests.cs b/src/Build.UnitTests/Definition/Project_Internal_Tests.cs index 8255424b6a7..fbdc08a9dee 100644 --- a/src/Build.UnitTests/Definition/Project_Internal_Tests.cs +++ b/src/Build.UnitTests/Definition/Project_Internal_Tests.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Xml; using Microsoft.Build.Construction; using Microsoft.Build.Definition; diff --git a/src/Build.UnitTests/Definition/ToolsVersion_Tests.cs b/src/Build.UnitTests/Definition/ToolsVersion_Tests.cs index 7ca7b24910b..115f418e3c3 100644 --- a/src/Build.UnitTests/Definition/ToolsVersion_Tests.cs +++ b/src/Build.UnitTests/Definition/ToolsVersion_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Text.RegularExpressions; -using System.Xml; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Collections; using Microsoft.Build.Construction; @@ -15,7 +14,6 @@ using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Xunit; -using Xunit.NetCore.Extensions; using InternalUtilities = Microsoft.Build.Internal.Utilities; using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; using LoggerMode = Microsoft.Build.BackEnd.Logging.LoggerMode; diff --git a/src/Build.UnitTests/Definition/ToolsetConfigurationReader_Tests.cs b/src/Build.UnitTests/Definition/ToolsetConfigurationReader_Tests.cs index 50aadc89522..f894d193999 100644 --- a/src/Build.UnitTests/Definition/ToolsetConfigurationReader_Tests.cs +++ b/src/Build.UnitTests/Definition/ToolsetConfigurationReader_Tests.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; -using Microsoft.Build.Shared; using Xunit; using ToolsetConfigurationSection = Microsoft.Build.Evaluation.ToolsetConfigurationSection; diff --git a/src/Build.UnitTests/Definition/ToolsetReader_Tests.cs b/src/Build.UnitTests/Definition/ToolsetReader_Tests.cs index 833ac2561a1..846be328f53 100644 --- a/src/Build.UnitTests/Definition/ToolsetReader_Tests.cs +++ b/src/Build.UnitTests/Definition/ToolsetReader_Tests.cs @@ -20,7 +20,6 @@ using InvalidToolsetDefinitionException = Microsoft.Build.Exceptions.InvalidToolsetDefinitionException; using InternalUtilities = Microsoft.Build.Internal.Utilities; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Build.UnitTests/Definition/Toolset_Tests.cs b/src/Build.UnitTests/Definition/Toolset_Tests.cs index 9c76a26954e..929513584ac 100644 --- a/src/Build.UnitTests/Definition/Toolset_Tests.cs +++ b/src/Build.UnitTests/Definition/Toolset_Tests.cs @@ -9,10 +9,8 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Internal; -using Microsoft.Build.Shared; using Microsoft.Build.UnitTests.BackEnd; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Build.UnitTests/Evaluation/ExpanderFunction_Tests.cs b/src/Build.UnitTests/Evaluation/ExpanderFunction_Tests.cs index 5cc7e4309cc..fe0acc1db0d 100644 --- a/src/Build.UnitTests/Evaluation/ExpanderFunction_Tests.cs +++ b/src/Build.UnitTests/Evaluation/ExpanderFunction_Tests.cs @@ -2,11 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Globalization; -using System.Runtime.InteropServices; using System.Threading; -using Microsoft.Build.Evaluation; - using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/Evaluation/ImportFromMSBuildExtensionsPath_Tests.cs b/src/Build.UnitTests/Evaluation/ImportFromMSBuildExtensionsPath_Tests.cs index c354a5b26e0..53ca81fcccb 100644 --- a/src/Build.UnitTests/Evaluation/ImportFromMSBuildExtensionsPath_Tests.cs +++ b/src/Build.UnitTests/Evaluation/ImportFromMSBuildExtensionsPath_Tests.cs @@ -10,7 +10,6 @@ using Microsoft.Build.Execution; using Microsoft.Build.Shared; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Build.UnitTests/Evaluation/IntrinsicFunctionOverload_Tests.cs b/src/Build.UnitTests/Evaluation/IntrinsicFunctionOverload_Tests.cs index c34473dd335..4501f10688c 100644 --- a/src/Build.UnitTests/Evaluation/IntrinsicFunctionOverload_Tests.cs +++ b/src/Build.UnitTests/Evaluation/IntrinsicFunctionOverload_Tests.cs @@ -1,13 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.IO; -using System.Xml; - using Microsoft.Build.Evaluation; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; using Shouldly; diff --git a/src/Build.UnitTests/Evaluation/ProjectRootElementCache_Tests.cs b/src/Build.UnitTests/Evaluation/ProjectRootElementCache_Tests.cs index 915e38582d5..0709ee7be41 100644 --- a/src/Build.UnitTests/Evaluation/ProjectRootElementCache_Tests.cs +++ b/src/Build.UnitTests/Evaluation/ProjectRootElementCache_Tests.cs @@ -8,7 +8,6 @@ using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Xunit; -using Xunit.NetCore.Extensions; diff --git a/src/Build.UnitTests/Evaluation/SimpleProjectRootElementCache_Tests.cs b/src/Build.UnitTests/Evaluation/SimpleProjectRootElementCache_Tests.cs index e082039d337..947d1dc22c6 100644 --- a/src/Build.UnitTests/Evaluation/SimpleProjectRootElementCache_Tests.cs +++ b/src/Build.UnitTests/Evaluation/SimpleProjectRootElementCache_Tests.cs @@ -5,7 +5,6 @@ using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/Evaluation/ToolsetConfigurationNet5_Tests.cs b/src/Build.UnitTests/Evaluation/ToolsetConfigurationNet5_Tests.cs index 85abd3297a2..6a3ad2f17bd 100644 --- a/src/Build.UnitTests/Evaluation/ToolsetConfigurationNet5_Tests.cs +++ b/src/Build.UnitTests/Evaluation/ToolsetConfigurationNet5_Tests.cs @@ -5,12 +5,11 @@ /* This test is designed especially to test Configuration parsing in net5.0 * which means it WON'T work in net472 and thus we don't run it in net472 */ +using System.Collections.Generic; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; - -using Xunit; -using System.Collections.Generic; using Shouldly; +using Xunit; #nullable disable diff --git a/src/Build.UnitTests/EvaluationProfiler_Tests.cs b/src/Build.UnitTests/EvaluationProfiler_Tests.cs index ff450b8a77b..ba4945a0bde 100644 --- a/src/Build.UnitTests/EvaluationProfiler_Tests.cs +++ b/src/Build.UnitTests/EvaluationProfiler_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; diff --git a/src/Build.UnitTests/FixPathOnUnix_Tests.cs b/src/Build.UnitTests/FixPathOnUnix_Tests.cs index 757c2ccde2c..bd5ac2b4224 100644 --- a/src/Build.UnitTests/FixPathOnUnix_Tests.cs +++ b/src/Build.UnitTests/FixPathOnUnix_Tests.cs @@ -6,7 +6,6 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs b/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs index 8156ed4fb52..d6295254e32 100644 --- a/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs +++ b/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/Build.UnitTests/Graph/GetCompatiblePlatformGraph_Tests.cs b/src/Build.UnitTests/Graph/GetCompatiblePlatformGraph_Tests.cs index d6344240f1a..d2e63cd06f8 100644 --- a/src/Build.UnitTests/Graph/GetCompatiblePlatformGraph_Tests.cs +++ b/src/Build.UnitTests/Graph/GetCompatiblePlatformGraph_Tests.cs @@ -1,24 +1,13 @@ // 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.Collections.Immutable; -using System.Diagnostics; using System.IO; -using System.Linq; -using System.Text.RegularExpressions; using Microsoft.Build.Evaluation; -using Microsoft.Build.Exceptions; -using Microsoft.Build.Execution; -using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; using Shouldly; using Xunit; -using Xunit.Abstractions; using static Microsoft.Build.Graph.UnitTests.GraphTestingUtilities; -using static Microsoft.Build.Graph.UnitTests.ProjectGraphTests; #nullable disable diff --git a/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs b/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs index 6d535479b1e..bcf72ce632a 100644 --- a/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs +++ b/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Build.BackEnd; -using Microsoft.Build.Collections; using Microsoft.Build.Construction; using Microsoft.Build.Engine.UnitTests; using Microsoft.Build.Exceptions; diff --git a/src/Build.UnitTests/Graph/IsolateProjects_Tests.cs b/src/Build.UnitTests/Graph/IsolateProjects_Tests.cs index d799c4267b1..13bb4275c65 100644 --- a/src/Build.UnitTests/Graph/IsolateProjects_Tests.cs +++ b/src/Build.UnitTests/Graph/IsolateProjects_Tests.cs @@ -7,7 +7,6 @@ using System.Linq; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Internal; using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; using Shouldly; diff --git a/src/Build.UnitTests/Graph/ResultCacheBasedBuilds_Tests.cs b/src/Build.UnitTests/Graph/ResultCacheBasedBuilds_Tests.cs index 8ff5f52352f..f9dd9bf05d4 100644 --- a/src/Build.UnitTests/Graph/ResultCacheBasedBuilds_Tests.cs +++ b/src/Build.UnitTests/Graph/ResultCacheBasedBuilds_Tests.cs @@ -11,7 +11,6 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Internal; using Microsoft.Build.UnitTests; using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/Instance/HostServices_Tests.cs b/src/Build.UnitTests/Instance/HostServices_Tests.cs index ed18f318540..e05239f9528 100644 --- a/src/Build.UnitTests/Instance/HostServices_Tests.cs +++ b/src/Build.UnitTests/Instance/HostServices_Tests.cs @@ -13,7 +13,6 @@ using Microsoft.Build.UnitTests.BackEnd; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Build.UnitTests/Instance/ProjectInstance_Internal_Tests.cs b/src/Build.UnitTests/Instance/ProjectInstance_Internal_Tests.cs index cb3ceade820..49fc0f9bfad 100644 --- a/src/Build.UnitTests/Instance/ProjectInstance_Internal_Tests.cs +++ b/src/Build.UnitTests/Instance/ProjectInstance_Internal_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.Construction; using Microsoft.Build.Definition; diff --git a/src/Build.UnitTests/Instance/TaskItem_Tests.cs b/src/Build.UnitTests/Instance/TaskItem_Tests.cs index 7699a66b623..af1e84b2397 100644 --- a/src/Build.UnitTests/Instance/TaskItem_Tests.cs +++ b/src/Build.UnitTests/Instance/TaskItem_Tests.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Xml; using Microsoft.Build.BackEnd; using Microsoft.Build.Collections; using Microsoft.Build.Construction; diff --git a/src/Build.UnitTests/InvalidProjectFileException_Tests.cs b/src/Build.UnitTests/InvalidProjectFileException_Tests.cs index 0987c3e9d6a..7a1d62aab19 100644 --- a/src/Build.UnitTests/InvalidProjectFileException_Tests.cs +++ b/src/Build.UnitTests/InvalidProjectFileException_Tests.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Runtime.Serialization.Formatters.Binary; using Microsoft.Build.Exceptions; using Xunit; diff --git a/src/Build.UnitTests/NodeStatus_SizeChange_Tests.cs b/src/Build.UnitTests/NodeStatus_SizeChange_Tests.cs index 458eb4edb27..bbe391ce360 100644 --- a/src/Build.UnitTests/NodeStatus_SizeChange_Tests.cs +++ b/src/Build.UnitTests/NodeStatus_SizeChange_Tests.cs @@ -2,15 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.Build.Logging; - -using VerifyTests; using VerifyXunit; using Xunit; diff --git a/src/Build.UnitTests/NodeStatus_Transition_Tests.cs b/src/Build.UnitTests/NodeStatus_Transition_Tests.cs index d811c2e4988..62cc3557215 100644 --- a/src/Build.UnitTests/NodeStatus_Transition_Tests.cs +++ b/src/Build.UnitTests/NodeStatus_Transition_Tests.cs @@ -2,16 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; -using System.Text.RegularExpressions; using System.Threading.Tasks; using Microsoft.Build.Framework.Logging; using Microsoft.Build.Logging; using Shouldly; -using VerifyTests; using VerifyXunit; using Xunit; diff --git a/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs b/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs index 449bdf1401d..e5ec5d49edd 100644 --- a/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs +++ b/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs @@ -10,7 +10,6 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using Microsoft.Build.CommandLine; using Microsoft.Build.Construction; using Microsoft.Build.Execution; using Microsoft.Build.Experimental.ProjectCache; @@ -24,7 +23,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.Sdk; using Task = System.Threading.Tasks.Task; namespace Microsoft.Build.Engine.UnitTests.ProjectCache diff --git a/src/Build.UnitTests/Scanner_Tests.cs b/src/Build.UnitTests/Scanner_Tests.cs index b2b705d2c22..da75fd75436 100644 --- a/src/Build.UnitTests/Scanner_Tests.cs +++ b/src/Build.UnitTests/Scanner_Tests.cs @@ -4,9 +4,6 @@ using System; using Microsoft.Build.Evaluation; using Microsoft.Build.Exceptions; -using Microsoft.Build.Framework; -using Microsoft.Build.Shared; -using Microsoft.Build.Utilities; using Shouldly; using Xunit; diff --git a/src/Build.UnitTests/StaticStopwatch.cs b/src/Build.UnitTests/StaticStopwatch.cs index 63d1bf7bcbd..8846e440b20 100644 --- a/src/Build.UnitTests/StaticStopwatch.cs +++ b/src/Build.UnitTests/StaticStopwatch.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.Net.Http.Headers; using Microsoft.Build.Logging; namespace Microsoft.Build.CommandLine.UnitTests; diff --git a/src/Build.UnitTests/Telemetry/OpenTelemetryActivities_Tests.cs b/src/Build.UnitTests/Telemetry/OpenTelemetryActivities_Tests.cs index 5616f614530..7a567e79495 100644 --- a/src/Build.UnitTests/Telemetry/OpenTelemetryActivities_Tests.cs +++ b/src/Build.UnitTests/Telemetry/OpenTelemetryActivities_Tests.cs @@ -4,11 +4,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Text; -using Xunit; -using Shouldly; using Microsoft.Build.Framework.Telemetry; +using Shouldly; +using Xunit; namespace Microsoft.Build.Engine.UnitTests.Telemetry { @@ -18,7 +16,7 @@ public class ActivityExtensionsTests public void WithTag_ShouldSetUnhashedValue() { var activity = new Activity("TestActivity"); - activity.Start(); + activity.Start(); var telemetryItem = new TelemetryItem( Name: "TestItem", @@ -121,7 +119,7 @@ public void WithStartTime_NullDateTime_ShouldNotSetStartTime() } /// - /// A simple mock for testing IActivityTelemetryDataHolder. + /// A simple mock for testing IActivityTelemetryDataHolder. /// Returns two items: one hashed, one not hashed. /// internal sealed class MockTelemetryDataHolder : IActivityTelemetryDataHolder diff --git a/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs b/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs index 58dbfa240ca..323326401c6 100644 --- a/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs +++ b/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs @@ -3,12 +3,10 @@ using System; using System.Reflection; -using Xunit; -using Shouldly; -using Xunit.Abstractions; -using Microsoft.Build.UnitTests.Shared; -using Microsoft.Build.UnitTests; using Microsoft.Build.Framework.Telemetry; +using Microsoft.Build.UnitTests; +using Shouldly; +using Xunit; namespace Microsoft.Build.Engine.UnitTests.Telemetry { diff --git a/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs b/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs index 6939eda86e5..6154403ab83 100644 --- a/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs +++ b/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs @@ -5,7 +5,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +#if NET using System.Text.Json; +#endif using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Framework.Telemetry; diff --git a/src/Build.UnitTests/TerminalLogger_Tests.cs b/src/Build.UnitTests/TerminalLogger_Tests.cs index 2c04241a604..f63f35e386f 100644 --- a/src/Build.UnitTests/TerminalLogger_Tests.cs +++ b/src/Build.UnitTests/TerminalLogger_Tests.cs @@ -6,13 +6,9 @@ using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Text; -using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.CommandLine.UnitTests; -using Microsoft.Build.Evaluation; using Microsoft.Build.Framework; using Microsoft.Build.Logging; using Microsoft.Build.UnitTests.Shared; diff --git a/src/Build.UnitTests/TestLoggingContext.cs b/src/Build.UnitTests/TestLoggingContext.cs index 756f61b8284..cc532c44d08 100644 --- a/src/Build.UnitTests/TestLoggingContext.cs +++ b/src/Build.UnitTests/TestLoggingContext.cs @@ -1,11 +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.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; diff --git a/src/Build/BackEnd/BuildManager/BuildParameters.cs b/src/Build/BackEnd/BuildManager/BuildParameters.cs index 38eca66834b..1bfab1b0e52 100644 --- a/src/Build/BackEnd/BuildManager/BuildParameters.cs +++ b/src/Build/BackEnd/BuildManager/BuildParameters.cs @@ -10,8 +10,6 @@ using Microsoft.Build.BackEnd; using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; -using Microsoft.Build.Experimental; -using Microsoft.Build.Experimental.BuildCheck; using Microsoft.Build.Experimental.ProjectCache; using Microsoft.Build.Framework; using Microsoft.Build.Graph; diff --git a/src/Build/BackEnd/BuildManager/BuildRequestData.cs b/src/Build/BackEnd/BuildManager/BuildRequestData.cs index 11f54ffbade..ab64b422770 100644 --- a/src/Build/BackEnd/BuildManager/BuildRequestData.cs +++ b/src/Build/BackEnd/BuildManager/BuildRequestData.cs @@ -1,16 +1,9 @@ // 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; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; -using System.Runtime.CompilerServices; using Microsoft.Build.Collections; -using Microsoft.Build.Evaluation; using Microsoft.Build.Experimental.BuildCheck; -using Microsoft.Build.Framework; using Microsoft.Build.Shared; namespace Microsoft.Build.Execution diff --git a/src/Build/BackEnd/BuildManager/BuildSubmissionBase.cs b/src/Build/BackEnd/BuildManager/BuildSubmissionBase.cs index 719625f43d9..8195c79aa0e 100644 --- a/src/Build/BackEnd/BuildManager/BuildSubmissionBase.cs +++ b/src/Build/BackEnd/BuildManager/BuildSubmissionBase.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; using Microsoft.Build.Shared; namespace Microsoft.Build.Execution diff --git a/src/Build/BackEnd/BuildManager/CacheAggregator.cs b/src/Build/BackEnd/BuildManager/CacheAggregator.cs index ddbe4524e06..694e9ef4af1 100644 --- a/src/Build/BackEnd/BuildManager/CacheAggregator.cs +++ b/src/Build/BackEnd/BuildManager/CacheAggregator.cs @@ -6,7 +6,6 @@ using System.Linq; using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; -using Microsoft.Build.Internal; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/BackEnd/BuildManager/GlobalPropertiesLookup.cs b/src/Build/BackEnd/BuildManager/GlobalPropertiesLookup.cs index 1cf11aa2068..a440de531f9 100644 --- a/src/Build/BackEnd/BuildManager/GlobalPropertiesLookup.cs +++ b/src/Build/BackEnd/BuildManager/GlobalPropertiesLookup.cs @@ -1,13 +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; using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Collections; namespace Microsoft.Build.Execution diff --git a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs index 34ec9d28df1..17a39536c22 100644 --- a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs +++ b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs @@ -4,9 +4,9 @@ using System; using System.Collections.Concurrent; using System.Threading; +using System.Threading.Tasks; using Microsoft.Build.Internal; using Microsoft.Build.Shared; -using System.Threading.Tasks; namespace Microsoft.Build.BackEnd.Client { diff --git a/src/Build/BackEnd/Components/BuildComponentFactoryCollection.cs b/src/Build/BackEnd/Components/BuildComponentFactoryCollection.cs index c48b89ef797..5e255466d52 100644 --- a/src/Build/BackEnd/Components/BuildComponentFactoryCollection.cs +++ b/src/Build/BackEnd/Components/BuildComponentFactoryCollection.cs @@ -5,7 +5,9 @@ using Microsoft.Build.BackEnd.Components.Caching; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.Experimental.BuildCheck.Infrastructure; +#if FEATURE_REPORTFILEACCESSES using Microsoft.Build.FileAccesses; +#endif using Microsoft.Build.Shared; using Microsoft.Build.TelemetryInfra; diff --git a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs index a87175b7936..d892518e9ea 100644 --- a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs +++ b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs @@ -3,15 +3,14 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks.Dataflow; using Microsoft.Build.BackEnd.Logging; -using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Execution; +using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.Debugging; diff --git a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEntry.cs b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEntry.cs index 2450c2debfd..f3e860cb6c7 100644 --- a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEntry.cs +++ b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEntry.cs @@ -6,8 +6,8 @@ using System.Diagnostics; using System.IO; using System.Linq; -using Microsoft.Build.Shared; using Microsoft.Build.Execution; +using Microsoft.Build.Shared; using BuildAbortedException = Microsoft.Build.Exceptions.BuildAbortedException; #nullable disable diff --git a/src/Build/BackEnd/Components/Caching/ResultsCacheResponse.cs b/src/Build/BackEnd/Components/Caching/ResultsCacheResponse.cs index 03e123f9141..012ae7af269 100644 --- a/src/Build/BackEnd/Components/Caching/ResultsCacheResponse.cs +++ b/src/Build/BackEnd/Components/Caching/ResultsCacheResponse.cs @@ -1,9 +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.Collections.Generic; - using BuildResult = Microsoft.Build.Execution.BuildResult; #nullable disable diff --git a/src/Build/BackEnd/Components/Communications/CurrentHost.cs b/src/Build/BackEnd/Components/Communications/CurrentHost.cs index a9c8336b837..6e08f93e955 100644 --- a/src/Build/BackEnd/Components/Communications/CurrentHost.cs +++ b/src/Build/BackEnd/Components/Communications/CurrentHost.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. #if RUNTIME_TYPE_NETCORE -using System.Diagnostics; using System.IO; using Microsoft.Build.Shared; #endif diff --git a/src/Build/BackEnd/Components/Communications/DetouredNodeLauncher.cs b/src/Build/BackEnd/Components/Communications/DetouredNodeLauncher.cs index 3defd87986c..7df245c49a5 100644 --- a/src/Build/BackEnd/Components/Communications/DetouredNodeLauncher.cs +++ b/src/Build/BackEnd/Components/Communications/DetouredNodeLauncher.cs @@ -6,11 +6,9 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using BuildXL.Processes; using BuildXL.Utilities.Core; using Microsoft.Build.Exceptions; -using Microsoft.Build.Experimental.FileAccess; using Microsoft.Build.FileAccesses; using Microsoft.Build.Internal; using Microsoft.Build.Shared; diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs index a2709281af4..ff0347b6f13 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs @@ -4,13 +4,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Globalization; using System.Threading; using Microsoft.Build.Internal; using Microsoft.Build.Shared; #if FEATURE_THREAD_CULTURE using BuildParameters = Microsoft.Build.Execution.BuildParameters; +#else +using System.Globalization; #endif using NodeEngineShutdownReason = Microsoft.Build.Execution.NodeEngineShutdownReason; diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index d346a6f3fe6..d15ee32c4e6 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -6,16 +6,16 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Build.BackEnd.Logging; +using Microsoft.Build.Framework; using Microsoft.Build.Internal; using Microsoft.Build.Shared; -using Microsoft.Build.Framework; -using Microsoft.Build.BackEnd.Logging; namespace Microsoft.Build.BackEnd { diff --git a/src/Build/BackEnd/Components/Logging/EventSourceSink.cs b/src/Build/BackEnd/Components/Logging/EventSourceSink.cs index 995370003eb..bda1135b030 100644 --- a/src/Build/BackEnd/Components/Logging/EventSourceSink.cs +++ b/src/Build/BackEnd/Components/Logging/EventSourceSink.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Experimental.BuildCheck; using Microsoft.Build.Framework; using Microsoft.Build.Framework.Telemetry; diff --git a/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs b/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs index 22687557135..2bda5a2e93f 100644 --- a/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs +++ b/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs @@ -3,7 +3,6 @@ using System.Collections; using System.Collections.Generic; -using System.Configuration; using System.Linq; using Microsoft.Build.Collections; using Microsoft.Build.Execution; diff --git a/src/Build/BackEnd/Components/Logging/TargetLoggingContext.cs b/src/Build/BackEnd/Components/Logging/TargetLoggingContext.cs index d68f04e2bfa..d79a052c5d9 100644 --- a/src/Build/BackEnd/Components/Logging/TargetLoggingContext.cs +++ b/src/Build/BackEnd/Components/Logging/TargetLoggingContext.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; #nullable disable diff --git a/src/Build/BackEnd/Components/ProjectCache/CacheContext.cs b/src/Build/BackEnd/Components/ProjectCache/CacheContext.cs index 5f3ff599c99..6a5eb2daaa9 100644 --- a/src/Build/BackEnd/Components/ProjectCache/CacheContext.cs +++ b/src/Build/BackEnd/Components/ProjectCache/CacheContext.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.Collections.Generic; using Microsoft.Build.FileSystem; using Microsoft.Build.Graph; diff --git a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs index 8906f10ba5a..e89f7d9fed8 100644 --- a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs +++ b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs @@ -16,7 +16,9 @@ using Microsoft.Build.Construction; using Microsoft.Build.Eventing; using Microsoft.Build.Execution; +#if FEATURE_REPORTFILEACCESSES using Microsoft.Build.FileAccesses; +#endif using Microsoft.Build.FileSystem; using Microsoft.Build.Framework; using Microsoft.Build.Graph; diff --git a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupLoggingHelper.cs b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupLoggingHelper.cs index deae62102f0..23f3d96c1f6 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupLoggingHelper.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupLoggingHelper.cs @@ -10,7 +10,6 @@ #endif using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Collections; -using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Shared; diff --git a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs index 9c2271ff635..ac3e5aab535 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/MSBuild.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Threading.Tasks; using Microsoft.Build.Framework; diff --git a/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs b/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs index 950b2848a68..7f5ebcbf243 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/ItemBucket.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Diagnostics; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Collections; diff --git a/src/Build/BackEnd/Components/RequestBuilder/Lookup.cs b/src/Build/BackEnd/Components/RequestBuilder/Lookup.cs index c2b180dd2b9..596555819ca 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/Lookup.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/Lookup.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Threading; using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; diff --git a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs index 1adad068292..4be12ea2854 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; diff --git a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs index 0c4bb721766..a262028f9f9 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs @@ -11,19 +11,20 @@ #endif using System.Diagnostics; using System.Reflection; -using System.Threading; using System.Threading.Tasks; using Microsoft.Build.BackEnd.Components.Caching; using Microsoft.Build.Collections; using Microsoft.Build.Eventing; using Microsoft.Build.Execution; -using Microsoft.Build.FileAccesses; using Microsoft.Build.Framework; -using Microsoft.Build.Experimental.FileAccess; using Microsoft.Build.Shared; using ElementLocation = Microsoft.Build.Construction.ElementLocation; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; using TaskLoggingContext = Microsoft.Build.BackEnd.Logging.TaskLoggingContext; +#if FEATURE_REPORTFILEACCESSES +using Microsoft.Build.Experimental.FileAccess; +using Microsoft.Build.FileAccesses; +#endif #nullable disable diff --git a/src/Build/BackEnd/Components/Scheduler/ScheduleResponse.cs b/src/Build/BackEnd/Components/Scheduler/ScheduleResponse.cs index c335c2cebf1..a7b6ddd743e 100644 --- a/src/Build/BackEnd/Components/Scheduler/ScheduleResponse.cs +++ b/src/Build/BackEnd/Components/Scheduler/ScheduleResponse.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Globalization; using Microsoft.Build.Execution; #nullable disable diff --git a/src/Build/BackEnd/Components/Scheduler/Scheduler.cs b/src/Build/BackEnd/Components/Scheduler/Scheduler.cs index 5911b984a72..e0390e978f0 100644 --- a/src/Build/BackEnd/Components/Scheduler/Scheduler.cs +++ b/src/Build/BackEnd/Components/Scheduler/Scheduler.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; diff --git a/src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs b/src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs index 2d5341cf215..c9876d8a9bc 100644 --- a/src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs +++ b/src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Reflection; using System.Xml; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Construction; using Microsoft.Build.Eventing; using Microsoft.Build.Framework; diff --git a/src/Build/BackEnd/Node/NodeConfiguration.cs b/src/Build/BackEnd/Node/NodeConfiguration.cs index 99ab53e2e70..cd57d5b74df 100644 --- a/src/Build/BackEnd/Node/NodeConfiguration.cs +++ b/src/Build/BackEnd/Node/NodeConfiguration.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using Microsoft.Build.Execution; -using Microsoft.Build.Framework; using Microsoft.Build.Logging; #nullable disable diff --git a/src/Build/BackEnd/Node/OutOfProcNode.cs b/src/Build/BackEnd/Node/OutOfProcNode.cs index bcd97d15400..c26c657f9a7 100644 --- a/src/Build/BackEnd/Node/OutOfProcNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcNode.cs @@ -14,10 +14,12 @@ using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.BackEnd.SdkResolution; using Microsoft.Build.Evaluation; -using Microsoft.Build.FileAccesses; using Microsoft.Build.Framework; using Microsoft.Build.Internal; using Microsoft.Build.Shared; +#if FEATURE_REPORTFILEACCESSES +using Microsoft.Build.FileAccesses; +#endif using SdkResult = Microsoft.Build.BackEnd.SdkResolution.SdkResult; #nullable disable diff --git a/src/Build/BackEnd/Node/OutOfProcServerNode.cs b/src/Build/BackEnd/Node/OutOfProcServerNode.cs index 6af7462d159..5131fe962e6 100644 --- a/src/Build/BackEnd/Node/OutOfProcServerNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcServerNode.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Concurrent; using System.IO; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; diff --git a/src/Build/BackEnd/Shared/BuildResult.cs b/src/Build/BackEnd/Shared/BuildResult.cs index f433ffc25ad..1ef8455bdf1 100644 --- a/src/Build/BackEnd/Shared/BuildResult.cs +++ b/src/Build/BackEnd/Shared/BuildResult.cs @@ -5,12 +5,12 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.Build.BackEnd; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; -using Microsoft.Build.Framework; -using System.Diagnostics.CodeAnalysis; namespace Microsoft.Build.Execution { diff --git a/src/Build/BackEnd/Shared/EventsCreatorHelper.cs b/src/Build/BackEnd/Shared/EventsCreatorHelper.cs index 1dad22fc1cf..6b6500c467a 100644 --- a/src/Build/BackEnd/Shared/EventsCreatorHelper.cs +++ b/src/Build/BackEnd/Shared/EventsCreatorHelper.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Framework; using Microsoft.Build.Shared; diff --git a/src/Build/BuildCheck/Checks/EmbeddedResourceCheck.cs b/src/Build/BuildCheck/Checks/EmbeddedResourceCheck.cs index aaeda56eaa6..6feb8874470 100644 --- a/src/Build/BuildCheck/Checks/EmbeddedResourceCheck.cs +++ b/src/Build/BuildCheck/Checks/EmbeddedResourceCheck.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.IO; using System.Collections.Generic; +using System.IO; using Microsoft.Build.Collections; using Microsoft.Build.Construction; using Microsoft.Build.Framework; diff --git a/src/Build/BuildCheck/Checks/SharedOutputPathCheck.cs b/src/Build/BuildCheck/Checks/SharedOutputPathCheck.cs index fe80a4ded80..635fc4ff1a6 100644 --- a/src/Build/BuildCheck/Checks/SharedOutputPathCheck.cs +++ b/src/Build/BuildCheck/Checks/SharedOutputPathCheck.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.IO; +using Microsoft.Build.Collections; using Microsoft.Build.Construction; using Microsoft.Build.Shared; -using Microsoft.Build.Collections; namespace Microsoft.Build.Experimental.BuildCheck.Checks; diff --git a/src/Build/BuildCheck/Infrastructure/ConfigurationProvider.cs b/src/Build/BuildCheck/Infrastructure/ConfigurationProvider.cs index e0179b29c5d..0030a1574cc 100644 --- a/src/Build/BuildCheck/Infrastructure/ConfigurationProvider.cs +++ b/src/Build/BuildCheck/Infrastructure/ConfigurationProvider.cs @@ -2,12 +2,12 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using Microsoft.Build.BuildCheck.Infrastructure; using Microsoft.Build.Experimental.BuildCheck.Infrastructure.EditorConfig; -using System.Collections.Concurrent; using Microsoft.Build.Experimental.BuildCheck.Utilities; -using Microsoft.Build.BuildCheck.Infrastructure; namespace Microsoft.Build.Experimental.BuildCheck.Infrastructure; diff --git a/src/Build/BuildCheck/OM/BuildCheckDataContext.cs b/src/Build/BuildCheck/OM/BuildCheckDataContext.cs index b4ead9bb81b..6f77454eaaf 100644 --- a/src/Build/BuildCheck/OM/BuildCheckDataContext.cs +++ b/src/Build/BuildCheck/OM/BuildCheckDataContext.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using System.IO; +using Microsoft.Build.Experimental.BuildCheck.Infrastructure; namespace Microsoft.Build.Experimental.BuildCheck; diff --git a/src/Build/BuildCheck/OM/PropertyReadData.cs b/src/Build/BuildCheck/OM/PropertyReadData.cs index 531f9fdc4a4..d4198c8385f 100644 --- a/src/Build/BuildCheck/OM/PropertyReadData.cs +++ b/src/Build/BuildCheck/OM/PropertyReadData.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Evaluation; +using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Shared; namespace Microsoft.Build.Experimental.BuildCheck; diff --git a/src/Build/Collections/ICopyOnWritePropertyDictionary.cs b/src/Build/Collections/ICopyOnWritePropertyDictionary.cs index 310476fa83b..f7c0589e7f5 100644 --- a/src/Build/Collections/ICopyOnWritePropertyDictionary.cs +++ b/src/Build/Collections/ICopyOnWritePropertyDictionary.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; -using System.Collections.Immutable; -using System.Diagnostics; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Collections/IItemDictionary.cs b/src/Build/Collections/IItemDictionary.cs index 1555c1de814..cca36bf088d 100644 --- a/src/Build/Collections/IItemDictionary.cs +++ b/src/Build/Collections/IItemDictionary.cs @@ -2,13 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Evaluation; -using Microsoft.Build.Shared; namespace Microsoft.Build.Collections { diff --git a/src/Build/Collections/IMultiDictionary.cs b/src/Build/Collections/IMultiDictionary.cs index 1fece50dc23..f78a2cf768e 100644 --- a/src/Build/Collections/IMultiDictionary.cs +++ b/src/Build/Collections/IMultiDictionary.cs @@ -1,11 +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; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.Build.Collections { diff --git a/src/Build/Collections/RetrievableEntryHashSet/IRetrievableValuedEntryHashSet.cs b/src/Build/Collections/RetrievableEntryHashSet/IRetrievableValuedEntryHashSet.cs index e3b10556772..798512d22ad 100644 --- a/src/Build/Collections/RetrievableEntryHashSet/IRetrievableValuedEntryHashSet.cs +++ b/src/Build/Collections/RetrievableEntryHashSet/IRetrievableValuedEntryHashSet.cs @@ -1,9 +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.Generic; -using System.Runtime.Serialization; - #nullable disable namespace Microsoft.Build.Collections diff --git a/src/Build/Collections/RetrievableEntryHashSet/RetrievableValuedEntryHashSet.cs b/src/Build/Collections/RetrievableEntryHashSet/RetrievableValuedEntryHashSet.cs index c45da4e12b6..9b3ffdefa0f 100644 --- a/src/Build/Collections/RetrievableEntryHashSet/RetrievableValuedEntryHashSet.cs +++ b/src/Build/Collections/RetrievableEntryHashSet/RetrievableValuedEntryHashSet.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.Collections.Generic; using System.Diagnostics; diff --git a/src/Build/Construction/Solution/ProjectInSolution.cs b/src/Build/Construction/Solution/ProjectInSolution.cs index 4eb3b5e82bd..106b21221ac 100644 --- a/src/Build/Construction/Solution/ProjectInSolution.cs +++ b/src/Build/Construction/Solution/ProjectInSolution.cs @@ -2,16 +2,18 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Buffers; + using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Security; -using System.Text; using System.Xml; -#if !NETFRAMEWORK +#if NETFRAMEWORK +using System.Text; +#else +using System.Buffers; using Microsoft.Build.Shared; #endif @@ -20,6 +22,8 @@ using BuildEventFileInfo = Microsoft.Build.Shared.BuildEventFileInfo; using ErrorUtilities = Microsoft.Build.Shared.ErrorUtilities; + + #nullable disable namespace Microsoft.Build.Construction @@ -581,7 +585,7 @@ private static bool ElementContainsInvalidNamespaceDefitions(XmlElement mainProj return false; } -#endregion + #endregion #region Constants diff --git a/src/Build/Construction/Solution/SolutionFile.cs b/src/Build/Construction/Solution/SolutionFile.cs index 01ece85d306..a424caca2b6 100644 --- a/src/Build/Construction/Solution/SolutionFile.cs +++ b/src/Build/Construction/Solution/SolutionFile.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Globalization; using System.IO; using System.Linq; using System.Runtime.InteropServices; diff --git a/src/Build/Definition/ProjectItemDefinition.cs b/src/Build/Definition/ProjectItemDefinition.cs index 5e79cf18d51..8fa69153f7e 100644 --- a/src/Build/Definition/ProjectItemDefinition.cs +++ b/src/Build/Definition/ProjectItemDefinition.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Microsoft.Build.Collections; using Microsoft.Build.Construction; using Microsoft.Build.ObjectModelRemoting; diff --git a/src/Build/Definition/Toolset.cs b/src/Build/Definition/Toolset.cs index bd84fed6708..9e2444e5bc1 100644 --- a/src/Build/Definition/Toolset.cs +++ b/src/Build/Definition/Toolset.cs @@ -13,14 +13,15 @@ using Microsoft.Build.Collections; using Microsoft.Build.Construction; using Microsoft.Build.Execution; +#if NET using Microsoft.Build.Framework; +#endif using Microsoft.Build.Internal; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; #if FEATURE_WIN32_REGISTRY using Microsoft.Win32; #endif -using ILoggingService = Microsoft.Build.BackEnd.Logging.ILoggingService; using ObjectModel = System.Collections.ObjectModel; using ReservedPropertyNames = Microsoft.Build.Internal.ReservedPropertyNames; diff --git a/src/Build/Definition/ToolsetLocalReader.cs b/src/Build/Definition/ToolsetLocalReader.cs index dacbbb6e8b0..68fcb2a5d51 100644 --- a/src/Build/Definition/ToolsetLocalReader.cs +++ b/src/Build/Definition/ToolsetLocalReader.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using System.Linq; using Microsoft.Build.Collections; using Microsoft.Build.Construction; using Microsoft.Build.Execution; diff --git a/src/Build/Definition/ToolsetRegistryReader.cs b/src/Build/Definition/ToolsetRegistryReader.cs index 8e272113322..33ec20b8e26 100644 --- a/src/Build/Definition/ToolsetRegistryReader.cs +++ b/src/Build/Definition/ToolsetRegistryReader.cs @@ -3,7 +3,6 @@ #if FEATURE_WIN32_REGISTRY -using System; using System.Collections.Generic; using Microsoft.Build.Collections; using Microsoft.Build.Construction; diff --git a/src/Build/Errors/InvalidToolsetDefinitionException.cs b/src/Build/Errors/InvalidToolsetDefinitionException.cs index 8dbae22aa4a..b5c1d724240 100644 --- a/src/Build/Errors/InvalidToolsetDefinitionException.cs +++ b/src/Build/Errors/InvalidToolsetDefinitionException.cs @@ -1,11 +1,11 @@ // 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.Framework.BuildException; -using Microsoft.Build.Shared; using System; -using System.Runtime.Serialization; using System.Collections.Generic; +using System.Runtime.Serialization; +using Microsoft.Build.Framework.BuildException; +using Microsoft.Build.Shared; #if FEATURE_SECURITY_PERMISSIONS using System.Security.Permissions; #endif diff --git a/src/Build/Evaluation/ConditionEvaluator.cs b/src/Build/Evaluation/ConditionEvaluator.cs index f6a5f70330f..cbe29768a60 100644 --- a/src/Build/Evaluation/ConditionEvaluator.cs +++ b/src/Build/Evaluation/ConditionEvaluator.cs @@ -11,7 +11,6 @@ using Microsoft.Build.Shared.FileSystem; using BuildEventContext = Microsoft.Build.Framework.BuildEventContext; using ElementLocation = Microsoft.Build.Construction.ElementLocation; -using ILoggingService = Microsoft.Build.BackEnd.Logging.ILoggingService; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; namespace Microsoft.Build.Evaluation diff --git a/src/Build/Evaluation/Conditionals/AndExpressionNode.cs b/src/Build/Evaluation/Conditionals/AndExpressionNode.cs index 1303731a2f6..f460c40142f 100644 --- a/src/Build/Evaluation/Conditionals/AndExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/AndExpressionNode.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/Conditionals/FunctionCallExpressionNode.cs b/src/Build/Evaluation/Conditionals/FunctionCallExpressionNode.cs index 6d7b27eb243..61769eb3da9 100644 --- a/src/Build/Evaluation/Conditionals/FunctionCallExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/FunctionCallExpressionNode.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Shared; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; diff --git a/src/Build/Evaluation/Conditionals/GenericExpressionNode.cs b/src/Build/Evaluation/Conditionals/GenericExpressionNode.cs index e8a7858d415..a701cade16d 100644 --- a/src/Build/Evaluation/Conditionals/GenericExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/GenericExpressionNode.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/Conditionals/MultipleComparisonExpressionNode.cs b/src/Build/Evaluation/Conditionals/MultipleComparisonExpressionNode.cs index f398e8d740a..57685a74ba9 100644 --- a/src/Build/Evaluation/Conditionals/MultipleComparisonExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/MultipleComparisonExpressionNode.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/Conditionals/NotExpressionNode.cs b/src/Build/Evaluation/Conditionals/NotExpressionNode.cs index 66bfc64c1c2..093599928cd 100644 --- a/src/Build/Evaluation/Conditionals/NotExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/NotExpressionNode.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/Conditionals/NumericComparisonExpressionNode.cs b/src/Build/Evaluation/Conditionals/NumericComparisonExpressionNode.cs index fb6cc3b3a71..ecc9cb4995c 100644 --- a/src/Build/Evaluation/Conditionals/NumericComparisonExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/NumericComparisonExpressionNode.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/Conditionals/OperatorExpressionNode.cs b/src/Build/Evaluation/Conditionals/OperatorExpressionNode.cs index 55cf768c60b..9d3e1ee1193 100644 --- a/src/Build/Evaluation/Conditionals/OperatorExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/OperatorExpressionNode.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.Build.BackEnd.Logging; -using Microsoft.Build.Collections; #nullable disable diff --git a/src/Build/Evaluation/Conditionals/OrExpressionNode.cs b/src/Build/Evaluation/Conditionals/OrExpressionNode.cs index dae691252e2..03852261ebe 100644 --- a/src/Build/Evaluation/Conditionals/OrExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/OrExpressionNode.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/Conditionals/StringExpressionNode.cs b/src/Build/Evaluation/Conditionals/StringExpressionNode.cs index 9e528acc5f4..9e4264af604 100644 --- a/src/Build/Evaluation/Conditionals/StringExpressionNode.cs +++ b/src/Build/Evaluation/Conditionals/StringExpressionNode.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index 7397dee024f..8e8f26a1233 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -24,11 +24,10 @@ using Microsoft.NET.StringTools; using Microsoft.Win32; using AvailableStaticMethods = Microsoft.Build.Internal.AvailableStaticMethods; -using ReservedPropertyNames = Microsoft.Build.Internal.ReservedPropertyNames; using ParseArgs = Microsoft.Build.Evaluation.Expander.ArgumentParser; +using ReservedPropertyNames = Microsoft.Build.Internal.ReservedPropertyNames; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; using TaskItemFactory = Microsoft.Build.Execution.ProjectItemInstance.TaskItem.TaskItemFactory; -using System.Buffers; #nullable disable diff --git a/src/Build/Evaluation/Expander/ArgumentParser.cs b/src/Build/Evaluation/Expander/ArgumentParser.cs index ca4039aae5d..c2dd113d5ff 100644 --- a/src/Build/Evaluation/Expander/ArgumentParser.cs +++ b/src/Build/Evaluation/Expander/ArgumentParser.cs @@ -2,11 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.Globalization; +#if NETFRAMEWORK using System.Linq; -using System.Text; -using System.Threading.Tasks; +#endif namespace Microsoft.Build.Evaluation.Expander { diff --git a/src/Build/Evaluation/Expander/WellKnownFunctions.cs b/src/Build/Evaluation/Expander/WellKnownFunctions.cs index bc7a74e0a74..d6bd9de96d3 100644 --- a/src/Build/Evaluation/Expander/WellKnownFunctions.cs +++ b/src/Build/Evaluation/Expander/WellKnownFunctions.cs @@ -2,16 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; -using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Microsoft.Build.BackEnd.Logging; -using Microsoft.Build.Evaluation; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; diff --git a/src/Build/Evaluation/IItemFactory.cs b/src/Build/Evaluation/IItemFactory.cs index 9624d90a562..d0f26ffd63b 100644 --- a/src/Build/Evaluation/IItemFactory.cs +++ b/src/Build/Evaluation/IItemFactory.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Microsoft.Build.Construction; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/IntrinsicFunctions.cs b/src/Build/Evaluation/IntrinsicFunctions.cs index 7d6b051cf99..fc86d54e613 100644 --- a/src/Build/Evaluation/IntrinsicFunctions.cs +++ b/src/Build/Evaluation/IntrinsicFunctions.cs @@ -4,9 +4,13 @@ using System; using System.Collections.Generic; using System.IO; +#if NETFRAMEWORK +using System.Linq; +#endif using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; +using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using Microsoft.Build.BackEnd.Logging; @@ -18,12 +22,8 @@ using Microsoft.Build.Utilities; using Microsoft.NET.StringTools; using Microsoft.Win32; -using System.Linq; - // Needed for DoesTaskHostExistForParameters using NodeProviderOutOfProcTaskHost = Microsoft.Build.BackEnd.NodeProviderOutOfProcTaskHost; -using System.Security.Cryptography; -using System.Buffers.Text; #nullable disable diff --git a/src/Build/Evaluation/ItemSpec.cs b/src/Build/Evaluation/ItemSpec.cs index 30400c410f6..26138e50b92 100644 --- a/src/Build/Evaluation/ItemSpec.cs +++ b/src/Build/Evaluation/ItemSpec.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Globbing; using Microsoft.Build.Internal; using Microsoft.Build.Shared; diff --git a/src/Build/Evaluation/LazyItemEvaluator.ItemFactoryWrapper.cs b/src/Build/Evaluation/LazyItemEvaluator.ItemFactoryWrapper.cs index 99c7166189b..f83ef9635c8 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.ItemFactoryWrapper.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.ItemFactoryWrapper.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using Microsoft.Build.Construction; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Evaluation/ProjectRootElementCache.cs b/src/Build/Evaluation/ProjectRootElementCache.cs index 30e364ae5bd..c97ebad4bff 100644 --- a/src/Build/Evaluation/ProjectRootElementCache.cs +++ b/src/Build/Evaluation/ProjectRootElementCache.cs @@ -7,7 +7,6 @@ using System.Diagnostics; using System.Globalization; using System.IO; -using System.Linq; using System.Xml; using Microsoft.Build.Collections; using Microsoft.Build.Construction; diff --git a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs index 9d259bda8c6..992e89967f0 100644 --- a/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs +++ b/src/Build/FileSystem/DirectoryCacheFileSystemWrapper.cs @@ -1,12 +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 Microsoft.Build.Shared; -using Microsoft.Build.Shared.FileSystem; using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.Build.Shared; +using Microsoft.Build.Shared.FileSystem; #if FEATURE_MSIOREDIST using Path = Microsoft.IO.Path; diff --git a/src/Build/Graph/GraphBuildRequestData.cs b/src/Build/Graph/GraphBuildRequestData.cs index 4d95ec0afe7..3d67ecf9eb2 100644 --- a/src/Build/Graph/GraphBuildRequestData.cs +++ b/src/Build/Graph/GraphBuildRequestData.cs @@ -5,8 +5,9 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.Build.Execution; +#if NETFRAMEWORK using Microsoft.Build.Experimental.BuildCheck; -using Microsoft.Build.Framework; +#endif using Microsoft.Build.Shared; namespace Microsoft.Build.Graph diff --git a/src/Build/Graph/GraphBuildSubmission.cs b/src/Build/Graph/GraphBuildSubmission.cs index 164cfc8e377..a7280646e08 100644 --- a/src/Build/Graph/GraphBuildSubmission.cs +++ b/src/Build/Graph/GraphBuildSubmission.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Globalization; using System.Threading; -using Microsoft.Build.BackEnd; using Microsoft.Build.Execution; using Microsoft.Build.Shared; diff --git a/src/Build/Graph/ProjectGraphNode.cs b/src/Build/Graph/ProjectGraphNode.cs index dcc2a2a24b1..4c43b1e1061 100644 --- a/src/Build/Graph/ProjectGraphNode.cs +++ b/src/Build/Graph/ProjectGraphNode.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.Collections.Generic; using System.Diagnostics; using Microsoft.Build.BackEnd; diff --git a/src/Build/Instance/ImmutableProjectCollections/ImmutableGlobalPropertiesCollectionConverter.cs b/src/Build/Instance/ImmutableProjectCollections/ImmutableGlobalPropertiesCollectionConverter.cs index b5579adf088..47e51a66e3f 100644 --- a/src/Build/Instance/ImmutableProjectCollections/ImmutableGlobalPropertiesCollectionConverter.cs +++ b/src/Build/Instance/ImmutableProjectCollections/ImmutableGlobalPropertiesCollectionConverter.cs @@ -2,13 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.CodeDom; using System.Collections; using System.Collections.Generic; -using System.Linq; using System.Runtime.Serialization; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Collections; using Microsoft.Build.Execution; using Microsoft.Build.Shared; diff --git a/src/Build/Instance/ImmutableProjectCollections/ImmutableItemDefinitionsListConverter.cs b/src/Build/Instance/ImmutableProjectCollections/ImmutableItemDefinitionsListConverter.cs index 245d42583b5..67243d66e62 100644 --- a/src/Build/Instance/ImmutableProjectCollections/ImmutableItemDefinitionsListConverter.cs +++ b/src/Build/Instance/ImmutableProjectCollections/ImmutableItemDefinitionsListConverter.cs @@ -4,9 +4,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Collections; using Microsoft.Build.Shared; diff --git a/src/Build/Instance/ImmutableProjectCollections/ImmutableItemDictionary.cs b/src/Build/Instance/ImmutableProjectCollections/ImmutableItemDictionary.cs index de2d06522ae..b92e762eba9 100644 --- a/src/Build/Instance/ImmutableProjectCollections/ImmutableItemDictionary.cs +++ b/src/Build/Instance/ImmutableProjectCollections/ImmutableItemDictionary.cs @@ -5,11 +5,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; -using Microsoft.Build.Execution; using Microsoft.Build.Shared; namespace Microsoft.Build.Instance diff --git a/src/Build/Instance/ImmutableProjectCollections/ImmutableLinkedMultiDictionaryConverter.cs b/src/Build/Instance/ImmutableProjectCollections/ImmutableLinkedMultiDictionaryConverter.cs index 1b11db2341a..ecf078d2567 100644 --- a/src/Build/Instance/ImmutableProjectCollections/ImmutableLinkedMultiDictionaryConverter.cs +++ b/src/Build/Instance/ImmutableProjectCollections/ImmutableLinkedMultiDictionaryConverter.cs @@ -3,11 +3,7 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Collections; -using Microsoft.Build.Execution; namespace Microsoft.Build.Instance.ImmutableProjectCollections { diff --git a/src/Build/Instance/ProjectItemDefinitionInstance.cs b/src/Build/Instance/ProjectItemDefinitionInstance.cs index 4e886bd134a..2bfb6750f50 100644 --- a/src/Build/Instance/ProjectItemDefinitionInstance.cs +++ b/src/Build/Instance/ProjectItemDefinitionInstance.cs @@ -10,7 +10,6 @@ using Microsoft.Build.Collections; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; -using Microsoft.Build.Instance; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index f0afd81d0a8..753b9add55b 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -9,11 +9,13 @@ using System.Threading; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Exceptions; -using Microsoft.Build.FileAccesses; using Microsoft.Build.Framework; -using Microsoft.Build.Experimental.FileAccess; using Microsoft.Build.Internal; using Microsoft.Build.Shared; +#if FEATURE_REPORTFILEACCESSES +using Microsoft.Build.Experimental.FileAccess; +using Microsoft.Build.FileAccesses; +#endif #nullable disable diff --git a/src/Build/Instance/TaskRegistry.cs b/src/Build/Instance/TaskRegistry.cs index 12d084f9940..508cb7483b6 100644 --- a/src/Build/Instance/TaskRegistry.cs +++ b/src/Build/Instance/TaskRegistry.cs @@ -11,7 +11,6 @@ using System.Reflection; using System.Threading; using Microsoft.Build.BackEnd; -using Microsoft.Build.BackEnd.Components.RequestBuilder; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Collections; using Microsoft.Build.Construction; @@ -20,7 +19,6 @@ using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Microsoft.NET.StringTools; -using ILoggingService = Microsoft.Build.BackEnd.Logging.ILoggingService; using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; using ProjectXmlUtilities = Microsoft.Build.Internal.ProjectXmlUtilities; using TargetLoggingContext = Microsoft.Build.BackEnd.Logging.TargetLoggingContext; diff --git a/src/Build/Logging/BinaryLogger/BinaryLogger.cs b/src/Build/Logging/BinaryLogger/BinaryLogger.cs index 9c3fda8fec3..10286b512eb 100644 --- a/src/Build/Logging/BinaryLogger/BinaryLogger.cs +++ b/src/Build/Logging/BinaryLogger/BinaryLogger.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Framework; using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Shared; -using Microsoft.Build.Shared.FileSystem; #nullable disable diff --git a/src/Build/Logging/BinaryLogger/Postprocessing/ArchiveFile.cs b/src/Build/Logging/BinaryLogger/Postprocessing/ArchiveFile.cs index ec8ba12c8b1..6239d90f453 100644 --- a/src/Build/Logging/BinaryLogger/Postprocessing/ArchiveFile.cs +++ b/src/Build/Logging/BinaryLogger/Postprocessing/ArchiveFile.cs @@ -4,8 +4,6 @@ using System; using System.IO; using System.IO.Compression; -using System.Text; -using Microsoft.Build.Shared; namespace Microsoft.Build.Logging { diff --git a/src/Build/Logging/BinaryLogger/Postprocessing/StreamExtensions.cs b/src/Build/Logging/BinaryLogger/Postprocessing/StreamExtensions.cs index 8a0cc2ed489..3e85d2bb02b 100644 --- a/src/Build/Logging/BinaryLogger/Postprocessing/StreamExtensions.cs +++ b/src/Build/Logging/BinaryLogger/Postprocessing/StreamExtensions.cs @@ -3,9 +3,7 @@ using System; using System.Buffers; -using System.Diagnostics; using System.IO; -using System.Text; using Microsoft.Build.Shared; namespace Microsoft.Build.Logging diff --git a/src/Build/Logging/ConsoleLogger.cs b/src/Build/Logging/ConsoleLogger.cs index dc365d45387..41bfaf94f50 100644 --- a/src/Build/Logging/ConsoleLogger.cs +++ b/src/Build/Logging/ConsoleLogger.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Framework; using Microsoft.Build.Framework.Logging; diff --git a/src/Build/Logging/SimpleErrorLogger.cs b/src/Build/Logging/SimpleErrorLogger.cs index 5b248afd5e7..fafc01e340d 100644 --- a/src/Build/Logging/SimpleErrorLogger.cs +++ b/src/Build/Logging/SimpleErrorLogger.cs @@ -4,7 +4,6 @@ using System; using Microsoft.Build.Framework; using Microsoft.Build.Framework.Logging; -using Microsoft.Build.Logging; using Microsoft.Build.Shared; namespace Microsoft.Build.Logging.SimpleErrorLogger diff --git a/src/Build/Logging/TerminalLogger/TerminalLogger.cs b/src/Build/Logging/TerminalLogger/TerminalLogger.cs index 754165ab923..eca3e0749a3 100644 --- a/src/Build/Logging/TerminalLogger/TerminalLogger.cs +++ b/src/Build/Logging/TerminalLogger/TerminalLogger.cs @@ -13,9 +13,7 @@ using Microsoft.Build.Shared; #if NET -using System.Diagnostics.CodeAnalysis; using System.Buffers; - #endif #if NETFRAMEWORK diff --git a/src/Build/Logging/TerminalLogger/TerminalNodeStatus.cs b/src/Build/Logging/TerminalLogger/TerminalNodeStatus.cs index 4661130b4c8..22eb0157257 100644 --- a/src/Build/Logging/TerminalLogger/TerminalNodeStatus.cs +++ b/src/Build/Logging/TerminalLogger/TerminalNodeStatus.cs @@ -1,10 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#if DEBUG using System; -using System.Diagnostics; +#endif using Microsoft.Build.Framework.Logging; -using Microsoft.Build.Shared; namespace Microsoft.Build.Logging; diff --git a/src/Build/Logging/TerminalLogger/TerminalProjectInfo.cs b/src/Build/Logging/TerminalLogger/TerminalProjectInfo.cs index 8e4f98fe688..8c64b0978f5 100644 --- a/src/Build/Logging/TerminalLogger/TerminalProjectInfo.cs +++ b/src/Build/Logging/TerminalLogger/TerminalProjectInfo.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; namespace Microsoft.Build.Logging; diff --git a/src/Build/TelemetryInfra/InternalTelemetryConsumingLogger.cs b/src/Build/TelemetryInfra/InternalTelemetryConsumingLogger.cs index 8d4832cd374..b028dd4b7fa 100644 --- a/src/Build/TelemetryInfra/InternalTelemetryConsumingLogger.cs +++ b/src/Build/TelemetryInfra/InternalTelemetryConsumingLogger.cs @@ -3,10 +3,8 @@ using System; using System.Linq; -using System.Text.Json; using Microsoft.Build.Framework; using Microsoft.Build.Framework.Telemetry; -using System.IO; namespace Microsoft.Build.TelemetryInfra; diff --git a/src/Build/TelemetryInfra/TelemetryForwarderProvider.cs b/src/Build/TelemetryInfra/TelemetryForwarderProvider.cs index 58ea242088b..717846204eb 100644 --- a/src/Build/TelemetryInfra/TelemetryForwarderProvider.cs +++ b/src/Build/TelemetryInfra/TelemetryForwarderProvider.cs @@ -4,7 +4,6 @@ using System; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; -using Microsoft.Build.Framework; using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Shared; @@ -75,7 +74,7 @@ private static TaskOrTargetTelemetryKey GetKey(string name, bool isCustom, bool public void FinalizeProcessing(LoggingContext loggingContext) { WorkerNodeTelemetryEventArgs telemetryArgs = new(_workerNodeTelemetryData) - { BuildEventContext = loggingContext.BuildEventContext }; + { BuildEventContext = loggingContext.BuildEventContext }; loggingContext.LogBuildEvent(telemetryArgs); } } diff --git a/src/Build/Utilities/NuGetFrameworkWrapper.cs b/src/Build/Utilities/NuGetFrameworkWrapper.cs index 4e1f865bbae..0332e0f7b61 100644 --- a/src/Build/Utilities/NuGetFrameworkWrapper.cs +++ b/src/Build/Utilities/NuGetFrameworkWrapper.cs @@ -3,12 +3,13 @@ using System; using System.Collections.Generic; +#if FEATURE_APPDOMAIN using System.Globalization; +#endif using System.IO; using System.Linq; using System.Reflection; using System.Text; - using Microsoft.Build.Framework; using Microsoft.Build.Shared; diff --git a/src/Build/Utilities/Utilities.cs b/src/Build/Utilities/Utilities.cs index 79d23f8415b..cf5b6ae383e 100644 --- a/src/Build/Utilities/Utilities.cs +++ b/src/Build/Utilities/Utilities.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -11,12 +10,10 @@ using System.Text; using System.Text.RegularExpressions; using System.Xml; -using Microsoft.Build.BackEnd; using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Logging; using Microsoft.Build.Shared; using Toolset = Microsoft.Build.Evaluation.Toolset; using XmlElementWithLocation = Microsoft.Build.Construction.XmlElementWithLocation; diff --git a/src/Build/Xml/ProjectXmlUtilities.cs b/src/Build/Xml/ProjectXmlUtilities.cs index e555da0aae3..43672a07329 100644 --- a/src/Build/Xml/ProjectXmlUtilities.cs +++ b/src/Build/Xml/ProjectXmlUtilities.cs @@ -6,7 +6,6 @@ using System.IO; using System.Xml; using Microsoft.Build.Construction; -using Microsoft.Build.Framework; using Microsoft.Build.Framework.BuildException; using Microsoft.Build.Shared; diff --git a/src/BuildCheck.UnitTests/CheckConfigurationEffectiveTests.cs b/src/BuildCheck.UnitTests/CheckConfigurationEffectiveTests.cs index a02eb1847c7..8315825e862 100644 --- a/src/BuildCheck.UnitTests/CheckConfigurationEffectiveTests.cs +++ b/src/BuildCheck.UnitTests/CheckConfigurationEffectiveTests.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Xunit; -using Microsoft.Build.Experimental.BuildCheck.Infrastructure; +using System; using Microsoft.Build.Experimental.BuildCheck; +using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Shouldly; -using System; +using Xunit; namespace Microsoft.Build.BuildCheck.UnitTests; diff --git a/src/BuildCheck.UnitTests/CheckConfiguration_Test.cs b/src/BuildCheck.UnitTests/CheckConfiguration_Test.cs index 8033a91064b..6de3c3f6432 100644 --- a/src/BuildCheck.UnitTests/CheckConfiguration_Test.cs +++ b/src/BuildCheck.UnitTests/CheckConfiguration_Test.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; -using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Experimental.BuildCheck; +using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Shouldly; using Xunit; diff --git a/src/BuildCheck.UnitTests/ConfigurationProvider_Tests.cs b/src/BuildCheck.UnitTests/ConfigurationProvider_Tests.cs index 53b7ba1b7b3..776109be948 100644 --- a/src/BuildCheck.UnitTests/ConfigurationProvider_Tests.cs +++ b/src/BuildCheck.UnitTests/ConfigurationProvider_Tests.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.Experimental.BuildCheck; +using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Microsoft.Build.UnitTests; using Shouldly; using Xunit; diff --git a/src/BuildCheck.UnitTests/DoubleWritesAnalyzer_Tests.cs b/src/BuildCheck.UnitTests/DoubleWritesAnalyzer_Tests.cs index 7e7953b6006..29880b5a7f2 100644 --- a/src/BuildCheck.UnitTests/DoubleWritesAnalyzer_Tests.cs +++ b/src/BuildCheck.UnitTests/DoubleWritesAnalyzer_Tests.cs @@ -1,13 +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; using System.Collections.Generic; using System.IO; -using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Experimental.BuildCheck; using Microsoft.Build.Experimental.BuildCheck.Checks; -using Microsoft.Build.Experimental.BuildCheck.Infrastructure; using Shouldly; using Xunit; diff --git a/src/BuildCheck.UnitTests/EndToEndTests.cs b/src/BuildCheck.UnitTests/EndToEndTests.cs index 4b5a516e649..08e3fccb43f 100644 --- a/src/BuildCheck.UnitTests/EndToEndTests.cs +++ b/src/BuildCheck.UnitTests/EndToEndTests.cs @@ -12,7 +12,6 @@ using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; using Microsoft.Build.UnitTests.Shared; -using Microsoft.VisualStudio.TestPlatform.Utilities; using Shouldly; using Xunit; using Xunit.Abstractions; diff --git a/src/BuildCheck.UnitTests/TestAssets/ErrorCustomCheck/ErrorOnRegisteredAction.cs b/src/BuildCheck.UnitTests/TestAssets/ErrorCustomCheck/ErrorOnRegisteredAction.cs index 3dc02bb7cca..ad031e74bfd 100644 --- a/src/BuildCheck.UnitTests/TestAssets/ErrorCustomCheck/ErrorOnRegisteredAction.cs +++ b/src/BuildCheck.UnitTests/TestAssets/ErrorCustomCheck/ErrorOnRegisteredAction.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using Microsoft.Build.Construction; using Microsoft.Build.Experimental.BuildCheck; namespace ErrorCustomCheck diff --git a/src/BuildCheck.UnitTests/TestAssets/InvalidCustomCheck/InvalidCheck.cs b/src/BuildCheck.UnitTests/TestAssets/InvalidCustomCheck/InvalidCheck.cs index e2bcfb57a88..1cd1db13df9 100644 --- a/src/BuildCheck.UnitTests/TestAssets/InvalidCustomCheck/InvalidCheck.cs +++ b/src/BuildCheck.UnitTests/TestAssets/InvalidCustomCheck/InvalidCheck.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; - namespace InvalidCustomCheck { public sealed class InvalidCheck diff --git a/src/Directory.BeforeCommon.targets b/src/Directory.BeforeCommon.targets index 10f4321b686..73b91fe6c96 100644 --- a/src/Directory.BeforeCommon.targets +++ b/src/Directory.BeforeCommon.targets @@ -98,6 +98,7 @@ + $(NoWarn);IDE0005 false diff --git a/src/Framework.UnitTests/BuildCanceledEventArgs_Tests.cs b/src/Framework.UnitTests/BuildCanceledEventArgs_Tests.cs index 9448bfbb743..4fa0d615019 100644 --- a/src/Framework.UnitTests/BuildCanceledEventArgs_Tests.cs +++ b/src/Framework.UnitTests/BuildCanceledEventArgs_Tests.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Shouldly; using Xunit; diff --git a/src/Framework.UnitTests/BuildCheckTracingEventArgs_Tests.cs b/src/Framework.UnitTests/BuildCheckTracingEventArgs_Tests.cs index b180fe6f631..9e59a4b575e 100644 --- a/src/Framework.UnitTests/BuildCheckTracingEventArgs_Tests.cs +++ b/src/Framework.UnitTests/BuildCheckTracingEventArgs_Tests.cs @@ -4,9 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Experimental.BuildCheck; using Shouldly; using Xunit; diff --git a/src/Framework.UnitTests/BuildSubmissionStartedEventArgs_Tests.cs b/src/Framework.UnitTests/BuildSubmissionStartedEventArgs_Tests.cs index 4e9c9dea78e..8efe355f891 100644 --- a/src/Framework.UnitTests/BuildSubmissionStartedEventArgs_Tests.cs +++ b/src/Framework.UnitTests/BuildSubmissionStartedEventArgs_Tests.cs @@ -4,9 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Shouldly; using Xunit; diff --git a/src/Framework.UnitTests/EventArgs_Tests.cs b/src/Framework.UnitTests/EventArgs_Tests.cs index 04a1c9e61ea..451ec654a16 100644 --- a/src/Framework.UnitTests/EventArgs_Tests.cs +++ b/src/Framework.UnitTests/EventArgs_Tests.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; using Microsoft.Build.Framework; using Shouldly; using Xunit; diff --git a/src/Framework.UnitTests/FileClassifier_Tests.cs b/src/Framework.UnitTests/FileClassifier_Tests.cs index b0441062a44..492655cae93 100644 --- a/src/Framework.UnitTests/FileClassifier_Tests.cs +++ b/src/Framework.UnitTests/FileClassifier_Tests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.IO; -using Microsoft.Build.Shared; using Shouldly; using Xunit; diff --git a/src/Framework.UnitTests/GeneratedFileUsedEventArgs_Tests.cs b/src/Framework.UnitTests/GeneratedFileUsedEventArgs_Tests.cs index 576c37e265f..5cf9cda8110 100644 --- a/src/Framework.UnitTests/GeneratedFileUsedEventArgs_Tests.cs +++ b/src/Framework.UnitTests/GeneratedFileUsedEventArgs_Tests.cs @@ -2,11 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Shouldly; using Xunit; diff --git a/src/Framework.UnitTests/OperatingSystem_Tests.cs b/src/Framework.UnitTests/OperatingSystem_Tests.cs index 4fbaf7af615..d001351b14b 100644 --- a/src/Framework.UnitTests/OperatingSystem_Tests.cs +++ b/src/Framework.UnitTests/OperatingSystem_Tests.cs @@ -4,7 +4,6 @@ using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; namespace Microsoft.Build.Framework.UnitTests { diff --git a/src/Framework.UnitTests/ProjectStartedEventArgs_Tests.cs b/src/Framework.UnitTests/ProjectStartedEventArgs_Tests.cs index 7eb7895b2df..4968243235a 100644 --- a/src/Framework.UnitTests/ProjectStartedEventArgs_Tests.cs +++ b/src/Framework.UnitTests/ProjectStartedEventArgs_Tests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using Microsoft.Build.Framework; using Shouldly; diff --git a/src/Framework.UnitTests/WorkerNodeTelemetryEventArgs_Tests.cs b/src/Framework.UnitTests/WorkerNodeTelemetryEventArgs_Tests.cs index bf5303e2c09..930ce27b496 100644 --- a/src/Framework.UnitTests/WorkerNodeTelemetryEventArgs_Tests.cs +++ b/src/Framework.UnitTests/WorkerNodeTelemetryEventArgs_Tests.cs @@ -1,15 +1,12 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.Build.Framework.Telemetry; using Shouldly; using Xunit; -using Microsoft.Build.Framework.Telemetry; namespace Microsoft.Build.Framework.UnitTests { diff --git a/src/Framework/BinaryTranslator.cs b/src/Framework/BinaryTranslator.cs index c302b537038..ecf16907307 100644 --- a/src/Framework/BinaryTranslator.cs +++ b/src/Framework/BinaryTranslator.cs @@ -6,7 +6,6 @@ using System.Diagnostics; using System.Globalization; using System.IO; -using System.Runtime.Serialization.Formatters.Binary; using Microsoft.Build.Framework; using Microsoft.Build.Framework.BuildException; diff --git a/src/Framework/BuildCanceledEventArgs.cs b/src/Framework/BuildCanceledEventArgs.cs index 6209b398156..89ea2f79ccf 100644 --- a/src/Framework/BuildCanceledEventArgs.cs +++ b/src/Framework/BuildCanceledEventArgs.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; namespace Microsoft.Build.Framework { diff --git a/src/Framework/BuildCheck/BuildCheckTracingData.cs b/src/Framework/BuildCheck/BuildCheckTracingData.cs index dc3ef3cb227..27716da0960 100644 --- a/src/Framework/BuildCheck/BuildCheckTracingData.cs +++ b/src/Framework/BuildCheck/BuildCheckTracingData.cs @@ -4,8 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.Build.Experimental.BuildCheck; diff --git a/src/Framework/BuildCheck/EnumerableExtensions.cs b/src/Framework/BuildCheck/EnumerableExtensions.cs index d74136269d9..025c2edfb98 100644 --- a/src/Framework/BuildCheck/EnumerableExtensions.cs +++ b/src/Framework/BuildCheck/EnumerableExtensions.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +#if !NET using System.Collections.ObjectModel; +#endif namespace Microsoft.Build.Experimental.BuildCheck; diff --git a/src/Framework/BuildErrorEventArgs.cs b/src/Framework/BuildErrorEventArgs.cs index 97cb5b1f1df..052d602f63d 100644 --- a/src/Framework/BuildErrorEventArgs.cs +++ b/src/Framework/BuildErrorEventArgs.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.IO; using Microsoft.Build.Shared; diff --git a/src/Framework/BuildMessageEventArgs.cs b/src/Framework/BuildMessageEventArgs.cs index 20a1898bea2..cab1d21892b 100644 --- a/src/Framework/BuildMessageEventArgs.cs +++ b/src/Framework/BuildMessageEventArgs.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.IO; using System.Runtime.Serialization; using Microsoft.Build.Shared; diff --git a/src/Framework/BuildRequestDataFlags.cs b/src/Framework/BuildRequestDataFlags.cs index 8a0c1f585f2..f125dc5f10b 100644 --- a/src/Framework/BuildRequestDataFlags.cs +++ b/src/Framework/BuildRequestDataFlags.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; // Note: Namespace is within Build.Execution for type forwarding to work correctly namespace Microsoft.Build.Execution diff --git a/src/Framework/BuildSubmissionStartedEventArgs.cs b/src/Framework/BuildSubmissionStartedEventArgs.cs index 385d3d342c5..6a1712abf83 100644 --- a/src/Framework/BuildSubmissionStartedEventArgs.cs +++ b/src/Framework/BuildSubmissionStartedEventArgs.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/Framework/BuildWarningEventArgs.cs b/src/Framework/BuildWarningEventArgs.cs index 543281e8c26..5e4d11acd39 100644 --- a/src/Framework/BuildWarningEventArgs.cs +++ b/src/Framework/BuildWarningEventArgs.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.IO; using Microsoft.Build.Shared; diff --git a/src/Framework/ChangeWaves.cs b/src/Framework/ChangeWaves.cs index 8e58f93835c..3bbcfe6f6f2 100644 --- a/src/Framework/ChangeWaves.cs +++ b/src/Framework/ChangeWaves.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if DEBUG using System.Diagnostics; +#endif using System.Linq; #nullable disable diff --git a/src/Framework/CriticalBuildMessageEventArgs.cs b/src/Framework/CriticalBuildMessageEventArgs.cs index 613240347c4..07488b90833 100644 --- a/src/Framework/CriticalBuildMessageEventArgs.cs +++ b/src/Framework/CriticalBuildMessageEventArgs.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET using System.Diagnostics.CodeAnalysis; +#endif #nullable disable diff --git a/src/Framework/CriticalTaskException.cs b/src/Framework/CriticalTaskException.cs index 6bd36d576f6..e9509888655 100644 --- a/src/Framework/CriticalTaskException.cs +++ b/src/Framework/CriticalTaskException.cs @@ -2,10 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Framework.BuildException; namespace Microsoft.Build.Framework diff --git a/src/Framework/CustomBuildEventArgs.cs b/src/Framework/CustomBuildEventArgs.cs index 85d59389d3f..19f3f844776 100644 --- a/src/Framework/CustomBuildEventArgs.cs +++ b/src/Framework/CustomBuildEventArgs.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET using System.Diagnostics.CodeAnalysis; +#endif #nullable disable diff --git a/src/Framework/EnvironmentVariableReadEventArgs.cs b/src/Framework/EnvironmentVariableReadEventArgs.cs index ef5bb651404..d0bdf9f6250 100644 --- a/src/Framework/EnvironmentVariableReadEventArgs.cs +++ b/src/Framework/EnvironmentVariableReadEventArgs.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Diagnostics; using System.IO; using Microsoft.Build.Shared; diff --git a/src/Framework/ExtendedBuildErrorEventArgs.cs b/src/Framework/ExtendedBuildErrorEventArgs.cs index 54f558432b1..2ce76e0bf53 100644 --- a/src/Framework/ExtendedBuildErrorEventArgs.cs +++ b/src/Framework/ExtendedBuildErrorEventArgs.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.IO; using Microsoft.Build.Shared; diff --git a/src/Framework/ExtendedBuildWarningEventArgs.cs b/src/Framework/ExtendedBuildWarningEventArgs.cs index 2d9a163eb15..598526c40a8 100644 --- a/src/Framework/ExtendedBuildWarningEventArgs.cs +++ b/src/Framework/ExtendedBuildWarningEventArgs.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.IO; using Microsoft.Build.Shared; diff --git a/src/Framework/Features.cs b/src/Framework/Features.cs index ec1183c94f5..3e7518b473d 100644 --- a/src/Framework/Features.cs +++ b/src/Framework/Features.cs @@ -1,11 +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; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace Microsoft.Build.Framework { diff --git a/src/Framework/FileClassifier.cs b/src/Framework/FileClassifier.cs index 648605cc33e..f6f79baafb1 100644 --- a/src/Framework/FileClassifier.cs +++ b/src/Framework/FileClassifier.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; using Microsoft.Build.Shared; #if !RUNTIME_TYPE_NETCORE -using System.Diagnostics; using System.Text.RegularExpressions; #endif diff --git a/src/Framework/GeneratedFileUsedEventArgs.cs b/src/Framework/GeneratedFileUsedEventArgs.cs index f64ba9521ff..9cc327831e2 100644 --- a/src/Framework/GeneratedFileUsedEventArgs.cs +++ b/src/Framework/GeneratedFileUsedEventArgs.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.IO; namespace Microsoft.Build.Framework diff --git a/src/Framework/LazyFormattedBuildEventArgs.cs b/src/Framework/LazyFormattedBuildEventArgs.cs index 5cb00f9a8b2..28598b2f1b6 100644 --- a/src/Framework/LazyFormattedBuildEventArgs.cs +++ b/src/Framework/LazyFormattedBuildEventArgs.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.Globalization; using System.IO; diff --git a/src/Framework/Logging/AnsiDetector.cs b/src/Framework/Logging/AnsiDetector.cs index 2b0c0e8b38a..6c2b09bd7a0 100644 --- a/src/Framework/Logging/AnsiDetector.cs +++ b/src/Framework/Logging/AnsiDetector.cs @@ -6,7 +6,6 @@ // and from the supports-ansi project by Qingrong Ke // https://github.com/keqingrong/supports-ansi/blob/master/index.js -using System; using System.Linq; using System.Text.RegularExpressions; diff --git a/src/Framework/Logging/LoggerParametersHelper.cs b/src/Framework/Logging/LoggerParametersHelper.cs index b4f7a843d4f..fdc615f19d4 100644 --- a/src/Framework/Logging/LoggerParametersHelper.cs +++ b/src/Framework/Logging/LoggerParametersHelper.cs @@ -4,9 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Shared; namespace Microsoft.Build.Framework.Logging diff --git a/src/Framework/MetaProjectGeneratedEventArgs.cs b/src/Framework/MetaProjectGeneratedEventArgs.cs index 1a529ed7171..79e0a68ad09 100644 --- a/src/Framework/MetaProjectGeneratedEventArgs.cs +++ b/src/Framework/MetaProjectGeneratedEventArgs.cs @@ -2,8 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.Build.Shared; using System.IO; +using Microsoft.Build.Shared; #nullable disable diff --git a/src/Framework/NativeMethods.cs b/src/Framework/NativeMethods.cs index ebd415b9e3c..352723a6e53 100644 --- a/src/Framework/NativeMethods.cs +++ b/src/Framework/NativeMethods.cs @@ -10,11 +10,14 @@ using System.Reflection; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using Microsoft.Build.Framework.Logging; using Microsoft.Build.Shared; using Microsoft.Win32; using Microsoft.Win32.SafeHandles; +#if !CLR2COMPATIBILITY +using Microsoft.Build.Framework.Logging; +#endif + using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; #nullable disable diff --git a/src/Framework/Profiler/EvaluationIdProvider.cs b/src/Framework/Profiler/EvaluationIdProvider.cs index 573204e1e15..76fc1b0266e 100644 --- a/src/Framework/Profiler/EvaluationIdProvider.cs +++ b/src/Framework/Profiler/EvaluationIdProvider.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.Diagnostics; using System.Threading; using Microsoft.Build.Shared; diff --git a/src/Framework/ProjectImportedEventArgs.cs b/src/Framework/ProjectImportedEventArgs.cs index 4884d1bcf23..2df59de35f3 100644 --- a/src/Framework/ProjectImportedEventArgs.cs +++ b/src/Framework/ProjectImportedEventArgs.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.IO; using Microsoft.Build.Shared; diff --git a/src/Framework/TargetSkippedEventArgs.cs b/src/Framework/TargetSkippedEventArgs.cs index 8536d2bec8a..6e636b4f61b 100644 --- a/src/Framework/TargetSkippedEventArgs.cs +++ b/src/Framework/TargetSkippedEventArgs.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.IO; using Microsoft.Build.Shared; diff --git a/src/Framework/Telemetry/ActivityExtensions.cs b/src/Framework/Telemetry/ActivityExtensions.cs index aa10b1a6615..9b4e05f7c02 100644 --- a/src/Framework/Telemetry/ActivityExtensions.cs +++ b/src/Framework/Telemetry/ActivityExtensions.cs @@ -1,11 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Security.Cryptography; -using System.Text; using System; using System.Collections.Generic; using System.Diagnostics; +using System.Security.Cryptography; +using System.Text; namespace Microsoft.Build.Framework.Telemetry { diff --git a/src/Framework/Telemetry/InternalTelemetryForwardingLogger.cs b/src/Framework/Telemetry/InternalTelemetryForwardingLogger.cs index 2208f4f2f5b..c0645634435 100644 --- a/src/Framework/Telemetry/InternalTelemetryForwardingLogger.cs +++ b/src/Framework/Telemetry/InternalTelemetryForwardingLogger.cs @@ -22,7 +22,7 @@ public void Initialize(IEventSource eventSource) { if (BuildEventRedirector != null && eventSource is IEventSource5 eventSource5) { - eventSource5.WorkerNodeTelemetryLogged += (o,e) => BuildEventRedirector.ForwardEvent(e); + eventSource5.WorkerNodeTelemetryLogged += (o, e) => BuildEventRedirector.ForwardEvent(e); } } diff --git a/src/Framework/Telemetry/LoggingConfigurationTelemetry.cs b/src/Framework/Telemetry/LoggingConfigurationTelemetry.cs index 493a945a526..9c721301747 100644 --- a/src/Framework/Telemetry/LoggingConfigurationTelemetry.cs +++ b/src/Framework/Telemetry/LoggingConfigurationTelemetry.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.Collections.Generic; using System.Globalization; diff --git a/src/Framework/Telemetry/OpenTelemetryManager.cs b/src/Framework/Telemetry/OpenTelemetryManager.cs index f392e1c24e3..a659cb9a932 100644 --- a/src/Framework/Telemetry/OpenTelemetryManager.cs +++ b/src/Framework/Telemetry/OpenTelemetryManager.cs @@ -9,10 +9,8 @@ using OpenTelemetry.Trace; #endif using System; -using System.Diagnostics; -using System.Threading; -using System.Globalization; using System.Runtime.CompilerServices; +using System.Threading; namespace Microsoft.Build.Framework.Telemetry { diff --git a/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs b/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs index a2a11f16e82..4eef343b196 100644 --- a/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs +++ b/src/Framework/Telemetry/WorkerNodeTelemetryEventArgs.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.IO; +#if NETFRAMEWORK using Microsoft.Build.Shared; +#endif namespace Microsoft.Build.Framework.Telemetry; diff --git a/src/MSBuild.UnitTests/CommandLineSwitches_Tests.cs b/src/MSBuild.UnitTests/CommandLineSwitches_Tests.cs index 47e9361022d..e233f7fc509 100644 --- a/src/MSBuild.UnitTests/CommandLineSwitches_Tests.cs +++ b/src/MSBuild.UnitTests/CommandLineSwitches_Tests.cs @@ -13,11 +13,9 @@ using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Graph; -using Microsoft.Build.Logging; using Microsoft.Build.Shared; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/MSBuild.UnitTests/MSBuildServer_Tests.cs b/src/MSBuild.UnitTests/MSBuildServer_Tests.cs index a749bd7145f..adc194cf98e 100644 --- a/src/MSBuild.UnitTests/MSBuildServer_Tests.cs +++ b/src/MSBuild.UnitTests/MSBuildServer_Tests.cs @@ -11,7 +11,6 @@ using Microsoft.Build.Experimental; using Microsoft.Build.Framework; using Microsoft.Build.Shared; -using Microsoft.Build.Shared.Debugging; using Microsoft.Build.UnitTests; using Microsoft.Build.UnitTests.Shared; #if NETFRAMEWORK diff --git a/src/MSBuild.UnitTests/ValidateAssemblyLoadContext.cs b/src/MSBuild.UnitTests/ValidateAssemblyLoadContext.cs index 7a802d191e4..ea5394f0be4 100644 --- a/src/MSBuild.UnitTests/ValidateAssemblyLoadContext.cs +++ b/src/MSBuild.UnitTests/ValidateAssemblyLoadContext.cs @@ -3,10 +3,10 @@ #if FEATURE_ASSEMBLYLOADCONTEXT +using System.Runtime.Loader; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Utilities; -using System.Runtime.Loader; #nullable disable diff --git a/src/MSBuild/AutomaticEncodingRestorer.cs b/src/MSBuild/AutomaticEncodingRestorer.cs index 034a0366786..b9a9c046453 100644 --- a/src/MSBuild/AutomaticEncodingRestorer.cs +++ b/src/MSBuild/AutomaticEncodingRestorer.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Runtime.InteropServices; using System.Security; using System.Text; diff --git a/src/MSBuild/CommandLineSwitches.cs b/src/MSBuild/CommandLineSwitches.cs index 982999ec30f..154f1920aa5 100644 --- a/src/MSBuild/CommandLineSwitches.cs +++ b/src/MSBuild/CommandLineSwitches.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Shared; diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs index 8aafef70b62..0f1b21b3664 100644 --- a/src/MSBuild/OutOfProcTaskHostNode.cs +++ b/src/MSBuild/OutOfProcTaskHostNode.cs @@ -4,7 +4,6 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; diff --git a/src/MSBuild/PerformanceLogEventListener.cs b/src/MSBuild/PerformanceLogEventListener.cs index 2f0a0d31595..a42f05194bc 100644 --- a/src/MSBuild/PerformanceLogEventListener.cs +++ b/src/MSBuild/PerformanceLogEventListener.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Diagnostics; using System.Diagnostics.Tracing; using System.IO; using System.Text; diff --git a/src/Shared/BuildEnvironmentHelper.cs b/src/Shared/BuildEnvironmentHelper.cs index c3615e4acf6..f93696c6aba 100644 --- a/src/Shared/BuildEnvironmentHelper.cs +++ b/src/Shared/BuildEnvironmentHelper.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; diff --git a/src/Shared/CanonicalError.cs b/src/Shared/CanonicalError.cs index 4c4a8ea96cf..4da40e7054f 100644 --- a/src/Shared/CanonicalError.cs +++ b/src/Shared/CanonicalError.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +#if NET using System.Buffers; +#endif using System.Globalization; using System.Text.RegularExpressions; diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index 4b48317081c..b65967a750a 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -7,7 +7,9 @@ using System.Globalization; using System.IO; using System.IO.Pipes; +#if NETFRAMEWORK using System.Runtime.InteropServices; +#endif #if FEATURE_SECURITY_PRINCIPAL_WINDOWS using System.Security.Principal; #endif diff --git a/src/Shared/ConversionUtilities.cs b/src/Shared/ConversionUtilities.cs index 9f1e756baeb..d04a52db675 100644 --- a/src/Shared/ConversionUtilities.cs +++ b/src/Shared/ConversionUtilities.cs @@ -3,7 +3,9 @@ using System; using System.Globalization; +#if !NET using System.Text; +#endif using Error = Microsoft.Build.Shared.ErrorUtilities; #nullable disable diff --git a/src/Shared/ExceptionHandling.cs b/src/Shared/ExceptionHandling.cs index 16a19dcadc2..a802379a8e4 100644 --- a/src/Shared/ExceptionHandling.cs +++ b/src/Shared/ExceptionHandling.cs @@ -8,7 +8,6 @@ namespace Microsoft.Build.AppxPackage.Shared #else using System; using System.Collections.Generic; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; diff --git a/src/Shared/IElementLocation.cs b/src/Shared/IElementLocation.cs index 4824e758d86..57fb254271c 100644 --- a/src/Shared/IElementLocation.cs +++ b/src/Shared/IElementLocation.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Build.BackEnd; -using Microsoft.Build.Framework; #nullable disable diff --git a/src/Shared/LogMessagePacketBase.cs b/src/Shared/LogMessagePacketBase.cs index 65283a2591b..cb7123038f0 100644 --- a/src/Shared/LogMessagePacketBase.cs +++ b/src/Shared/LogMessagePacketBase.cs @@ -18,11 +18,6 @@ using Microsoft.Build.Collections; using Microsoft.Build.Framework.Profiler; using System.Collections; -using System.Linq; -#endif - -#if FEATURE_APPDOMAIN -using TaskEngineAssemblyResolver = Microsoft.Build.BackEnd.Logging.TaskEngineAssemblyResolver; #endif #nullable disable @@ -433,14 +428,14 @@ internal void WriteToStream(ITranslator translator) bool eventCanSerializeItself = methodInfo != null; #if !TASKHOST && !MSBUILDENTRYPOINTEXE - if (_buildEvent is ProjectEvaluationStartedEventArgs - or ProjectEvaluationFinishedEventArgs - or ResponseFileUsedEventArgs) - { - // switch to serialization methods that we provide in this file - // and don't use the WriteToStream inherited from LazyFormattedBuildEventArgs - eventCanSerializeItself = false; - } + if (_buildEvent is ProjectEvaluationStartedEventArgs + or ProjectEvaluationFinishedEventArgs + or ResponseFileUsedEventArgs) + { + // switch to serialization methods that we provide in this file + // and don't use the WriteToStream inherited from LazyFormattedBuildEventArgs + eventCanSerializeItself = false; + } #endif translator.Translate(ref eventCanSerializeItself); diff --git a/src/Shared/MSBuildLoadContext.cs b/src/Shared/MSBuildLoadContext.cs index 7427c5ed735..b5396133c08 100644 --- a/src/Shared/MSBuildLoadContext.cs +++ b/src/Shared/MSBuildLoadContext.cs @@ -6,7 +6,6 @@ using System.IO; using System.Reflection; using System.Runtime.Loader; -using Microsoft.Build.Framework; using Microsoft.Build.Shared.FileSystem; namespace Microsoft.Build.Shared diff --git a/src/Shared/NamedPipeUtil.cs b/src/Shared/NamedPipeUtil.cs index bf31c0193f6..25094e59035 100644 --- a/src/Shared/NamedPipeUtil.cs +++ b/src/Shared/NamedPipeUtil.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.Diagnostics; using System.IO; namespace Microsoft.Build.Shared diff --git a/src/Shared/NodeEndpointOutOfProcBase.cs b/src/Shared/NodeEndpointOutOfProcBase.cs index 5a5d1db3eb5..0c9acac23d3 100644 --- a/src/Shared/NodeEndpointOutOfProcBase.cs +++ b/src/Shared/NodeEndpointOutOfProcBase.cs @@ -9,7 +9,6 @@ using System.Collections.Concurrent; using System.Threading.Tasks; #endif -using System.IO; using System.Threading; using Microsoft.Build.Internal; using Microsoft.Build.Shared; diff --git a/src/Shared/NodePipeBase.cs b/src/Shared/NodePipeBase.cs index a9c9692a880..fd1d08efe9c 100644 --- a/src/Shared/NodePipeBase.cs +++ b/src/Shared/NodePipeBase.cs @@ -7,7 +7,6 @@ using System.Threading; using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; #if !TASKHOST using System.Buffers.Binary; diff --git a/src/Shared/NodePipeClient.cs b/src/Shared/NodePipeClient.cs index 6be1e0e422b..6585a2558e7 100644 --- a/src/Shared/NodePipeClient.cs +++ b/src/Shared/NodePipeClient.cs @@ -1,11 +1,11 @@ // 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.IO; using System.IO.Pipes; +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY +using System; using System.Security.Principal; -using Microsoft.Build.BackEnd; +#endif namespace Microsoft.Build.Internal { diff --git a/src/Shared/NodePipeServer.cs b/src/Shared/NodePipeServer.cs index 91fba144c52..dde8e2ee371 100644 --- a/src/Shared/NodePipeServer.cs +++ b/src/Shared/NodePipeServer.cs @@ -4,8 +4,10 @@ using System; using System.IO; using System.IO.Pipes; +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY using System.Security.AccessControl; using System.Security.Principal; +#endif using Microsoft.Build.BackEnd; using Microsoft.Build.Shared; diff --git a/src/Shared/ProjectWriter.cs b/src/Shared/ProjectWriter.cs index d5a5a00003f..d0b57944440 100644 --- a/src/Shared/ProjectWriter.cs +++ b/src/Shared/ProjectWriter.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.IO; using System.Text; using System.Text.RegularExpressions; diff --git a/src/Shared/QuotingUtilities.cs b/src/Shared/QuotingUtilities.cs index 17ca8a419db..5d42939b505 100644 --- a/src/Shared/QuotingUtilities.cs +++ b/src/Shared/QuotingUtilities.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; using System.Collections.Generic; using System.Diagnostics; using System.Text; diff --git a/src/Shared/TaskHostConfiguration.cs b/src/Shared/TaskHostConfiguration.cs index 723a4ba240b..df56c4efc53 100644 --- a/src/Shared/TaskHostConfiguration.cs +++ b/src/Shared/TaskHostConfiguration.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; -using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Shared/TaskHostTaskComplete.cs b/src/Shared/TaskHostTaskComplete.cs index 862341eaa8f..6bded722522 100644 --- a/src/Shared/TaskHostTaskComplete.cs +++ b/src/Shared/TaskHostTaskComplete.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -#if !CLR2COMPATIBILITY +#if !CLR2COMPATIBILITY && FEATURE_REPORTFILEACCESSES using Microsoft.Build.Experimental.FileAccess; #endif using Microsoft.Build.Shared; diff --git a/src/Shared/TaskLoggingHelper.cs b/src/Shared/TaskLoggingHelper.cs index 979319e5d36..b79bca33d16 100644 --- a/src/Shared/TaskLoggingHelper.cs +++ b/src/Shared/TaskLoggingHelper.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.Globalization; using System.IO; using System.Resources; diff --git a/src/Shared/TaskParameter.cs b/src/Shared/TaskParameter.cs index 3e436da8c38..3b6265ca4d9 100644 --- a/src/Shared/TaskParameter.cs +++ b/src/Shared/TaskParameter.cs @@ -4,9 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; -using System.Linq; using System.Reflection; #if FEATURE_APPDOMAIN using System.Security; diff --git a/src/Shared/Tracing.cs b/src/Shared/Tracing.cs index 154c6ff6f05..70b8d4e0792 100644 --- a/src/Shared/Tracing.cs +++ b/src/Shared/Tracing.cs @@ -4,8 +4,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; #if DEBUG +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; #endif diff --git a/src/Shared/UnitTests/AssemblyNameEx_Tests.cs b/src/Shared/UnitTests/AssemblyNameEx_Tests.cs index ec407e54d29..fbeba59cf92 100644 --- a/src/Shared/UnitTests/AssemblyNameEx_Tests.cs +++ b/src/Shared/UnitTests/AssemblyNameEx_Tests.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Serialization.Formatters.Binary; using Microsoft.Build.BackEnd; using Microsoft.Build.Shared; using Shouldly; diff --git a/src/Shared/UnitTests/CommunicationUtilities_Tests.cs b/src/Shared/UnitTests/CommunicationUtilities_Tests.cs index 84cfcd034b8..63e1e2a7197 100644 --- a/src/Shared/UnitTests/CommunicationUtilities_Tests.cs +++ b/src/Shared/UnitTests/CommunicationUtilities_Tests.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using Shouldly; using Xunit; -using Xunit.Abstractions; using CommunicationsUtilities = Microsoft.Build.Internal.CommunicationsUtilities; namespace Microsoft.Build.UnitTests diff --git a/src/Shared/UnitTests/CopyOnWriteDictionary_Tests.cs b/src/Shared/UnitTests/CopyOnWriteDictionary_Tests.cs index 32101d5d60e..689edb1e36b 100644 --- a/src/Shared/UnitTests/CopyOnWriteDictionary_Tests.cs +++ b/src/Shared/UnitTests/CopyOnWriteDictionary_Tests.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; -using System.Runtime.Serialization.Formatters.Binary; using Microsoft.Build.Collections; using Shouldly; using Xunit; diff --git a/src/Shared/UnitTests/ErrorUtilities_Tests.cs b/src/Shared/UnitTests/ErrorUtilities_Tests.cs index 6adbc583bd9..4bd7a10e35d 100644 --- a/src/Shared/UnitTests/ErrorUtilities_Tests.cs +++ b/src/Shared/UnitTests/ErrorUtilities_Tests.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; - using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Xunit; diff --git a/src/Shared/UnitTests/FileMatcher_Tests.cs b/src/Shared/UnitTests/FileMatcher_Tests.cs index 6d43904420f..35235a7d0f6 100644 --- a/src/Shared/UnitTests/FileMatcher_Tests.cs +++ b/src/Shared/UnitTests/FileMatcher_Tests.cs @@ -14,7 +14,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Shared/UnitTests/FileUtilities_Tests.cs b/src/Shared/UnitTests/FileUtilities_Tests.cs index a99d79be223..8f2a750f675 100644 --- a/src/Shared/UnitTests/FileUtilities_Tests.cs +++ b/src/Shared/UnitTests/FileUtilities_Tests.cs @@ -9,7 +9,6 @@ using Microsoft.Build.Shared; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Shared/UnitTests/NativeMethodsShared_Tests.cs b/src/Shared/UnitTests/NativeMethodsShared_Tests.cs index b96ee0700d4..fd5e9a82ed8 100644 --- a/src/Shared/UnitTests/NativeMethodsShared_Tests.cs +++ b/src/Shared/UnitTests/NativeMethodsShared_Tests.cs @@ -8,7 +8,6 @@ using System.Runtime.Versioning; using Microsoft.Build.Shared; using Xunit; -using Xunit.NetCore.Extensions; diff --git a/src/Shared/UnitTests/XmakeAttributes_Tests.cs b/src/Shared/UnitTests/XmakeAttributes_Tests.cs index 0ccd5682302..da7375f123a 100644 --- a/src/Shared/UnitTests/XmakeAttributes_Tests.cs +++ b/src/Shared/UnitTests/XmakeAttributes_Tests.cs @@ -6,7 +6,6 @@ using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Shared/XMakeElements.cs b/src/Shared/XMakeElements.cs index 40d22e96234..69528ab540a 100644 --- a/src/Shared/XMakeElements.cs +++ b/src/Shared/XMakeElements.cs @@ -1,7 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - +#if NET using System.Buffers; +#endif using System.Collections.Generic; #nullable disable diff --git a/src/StringTools/FowlerNollVo1aHash.cs b/src/StringTools/FowlerNollVo1aHash.cs index 5a9a876e4c0..56c2ca80891 100644 --- a/src/StringTools/FowlerNollVo1aHash.cs +++ b/src/StringTools/FowlerNollVo1aHash.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Runtime.InteropServices; -using System; namespace Microsoft.NET.StringTools { diff --git a/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs b/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs index 164f91774e0..576145d5cd5 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs @@ -18,7 +18,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; using FrameworkNameVersioning = System.Runtime.Versioning.FrameworkName; using SystemProcessorArchitecture = System.Reflection.ProcessorArchitecture; diff --git a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs index 774d8f3d735..ddfb1bd9c4f 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.Versioning; using Microsoft.Build.Framework; diff --git a/src/Tasks.UnitTests/AssemblyDependency/SpecificVersionPrimary.cs b/src/Tasks.UnitTests/AssemblyDependency/SpecificVersionPrimary.cs index a27434df16c..c11fc53cfcc 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/SpecificVersionPrimary.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/SpecificVersionPrimary.cs @@ -9,7 +9,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/AssemblyDependency/SuggestedRedirects.cs b/src/Tasks.UnitTests/AssemblyDependency/SuggestedRedirects.cs index 9ab178efbb5..2dc93f2fe56 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/SuggestedRedirects.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/SuggestedRedirects.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Reflection; -using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; diff --git a/src/Tasks.UnitTests/AssignCulture_Tests.cs b/src/Tasks.UnitTests/AssignCulture_Tests.cs index 47ceeb5d7b0..fd2d3e0995b 100644 --- a/src/Tasks.UnitTests/AssignCulture_Tests.cs +++ b/src/Tasks.UnitTests/AssignCulture_Tests.cs @@ -2,12 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/AssignLinkMetadata_Tests.cs b/src/Tasks.UnitTests/AssignLinkMetadata_Tests.cs index 9e70ea4d20a..a0eb459595a 100644 --- a/src/Tasks.UnitTests/AssignLinkMetadata_Tests.cs +++ b/src/Tasks.UnitTests/AssignLinkMetadata_Tests.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; using Xunit; diff --git a/src/Tasks.UnitTests/AssignTargetPath_Tests.cs b/src/Tasks.UnitTests/AssignTargetPath_Tests.cs index 2289f896eaf..f2c08c59c6d 100644 --- a/src/Tasks.UnitTests/AssignTargetPath_Tests.cs +++ b/src/Tasks.UnitTests/AssignTargetPath_Tests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; using Shouldly; diff --git a/src/Tasks.UnitTests/AxImp_Tests.cs b/src/Tasks.UnitTests/AxImp_Tests.cs index 52d433c32f9..6525c1656ea 100644 --- a/src/Tasks.UnitTests/AxImp_Tests.cs +++ b/src/Tasks.UnitTests/AxImp_Tests.cs @@ -1,11 +1,9 @@ // 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.Shared; using Microsoft.Build.Tasks; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs b/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs index ce9d7feadcb..99df3fbf783 100644 --- a/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs +++ b/src/Tasks.UnitTests/AxTlbBaseTask_Tests.cs @@ -8,7 +8,6 @@ using Microsoft.Build.Utilities; using Microsoft.Runtime.Hosting; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/CodeTaskFactoryEmbeddedFileInBinlogTestHelper.cs b/src/Tasks.UnitTests/CodeTaskFactoryEmbeddedFileInBinlogTestHelper.cs index 20542cd8ce8..20c5c443a17 100644 --- a/src/Tasks.UnitTests/CodeTaskFactoryEmbeddedFileInBinlogTestHelper.cs +++ b/src/Tasks.UnitTests/CodeTaskFactoryEmbeddedFileInBinlogTestHelper.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.IO; using System.IO.Compression; using Microsoft.Build.Logging; diff --git a/src/Tasks.UnitTests/ComReference_Tests.cs b/src/Tasks.UnitTests/ComReference_Tests.cs index 741cd74479e..5f2b31c1010 100644 --- a/src/Tasks.UnitTests/ComReference_Tests.cs +++ b/src/Tasks.UnitTests/ComReference_Tests.cs @@ -6,7 +6,6 @@ using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/CombinePath_Tests.cs b/src/Tasks.UnitTests/CombinePath_Tests.cs index a719717e07e..080cf10f288 100644 --- a/src/Tasks.UnitTests/CombinePath_Tests.cs +++ b/src/Tasks.UnitTests/CombinePath_Tests.cs @@ -4,11 +4,9 @@ using System.IO; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/CombineTargetFrameworkInfoProperties_Tests.cs b/src/Tasks.UnitTests/CombineTargetFrameworkInfoProperties_Tests.cs index e22a715f8a4..4bf3eba2529 100644 --- a/src/Tasks.UnitTests/CombineTargetFrameworkInfoProperties_Tests.cs +++ b/src/Tasks.UnitTests/CombineTargetFrameworkInfoProperties_Tests.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 Microsoft.Build.Framework; using Microsoft.Build.Tasks; using Shouldly; diff --git a/src/Tasks.UnitTests/CreateItem_Tests.cs b/src/Tasks.UnitTests/CreateItem_Tests.cs index de09dcbc85e..90a57722f30 100644 --- a/src/Tasks.UnitTests/CreateItem_Tests.cs +++ b/src/Tasks.UnitTests/CreateItem_Tests.cs @@ -4,18 +4,14 @@ using System; using System.Collections.Generic; using System.IO; -using Microsoft.Build.Definition; -using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Microsoft.Build.UnitTests.Shared; using Microsoft.Build.Utilities; using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/Delete_Tests.cs b/src/Tasks.UnitTests/Delete_Tests.cs index d404c63246f..e523c13816c 100644 --- a/src/Tasks.UnitTests/Delete_Tests.cs +++ b/src/Tasks.UnitTests/Delete_Tests.cs @@ -8,7 +8,6 @@ using Microsoft.Build.Utilities; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/Exec_Tests.cs b/src/Tasks.UnitTests/Exec_Tests.cs index 50732b70703..2f0f9e1ace3 100644 --- a/src/Tasks.UnitTests/Exec_Tests.cs +++ b/src/Tasks.UnitTests/Exec_Tests.cs @@ -16,7 +16,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/FileStateTests.cs b/src/Tasks.UnitTests/FileStateTests.cs index 1048fc95358..1b23ffaba7d 100644 --- a/src/Tasks.UnitTests/FileStateTests.cs +++ b/src/Tasks.UnitTests/FileStateTests.cs @@ -6,7 +6,6 @@ using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/FindUnderPath_Tests.cs b/src/Tasks.UnitTests/FindUnderPath_Tests.cs index ce0be31a183..5b38b678b58 100644 --- a/src/Tasks.UnitTests/FindUnderPath_Tests.cs +++ b/src/Tasks.UnitTests/FindUnderPath_Tests.cs @@ -8,7 +8,6 @@ using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; using Xunit; -using Xunit.NetCore.Extensions; diff --git a/src/Tasks.UnitTests/FormatUrl_Tests.cs b/src/Tasks.UnitTests/FormatUrl_Tests.cs index 26fabaf9db5..8cc77f6358a 100644 --- a/src/Tasks.UnitTests/FormatUrl_Tests.cs +++ b/src/Tasks.UnitTests/FormatUrl_Tests.cs @@ -7,7 +7,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; namespace Microsoft.Build.UnitTests { diff --git a/src/Tasks.UnitTests/GetAssembliesMetadata_Tests.cs b/src/Tasks.UnitTests/GetAssembliesMetadata_Tests.cs index 8d50571656d..1c8bcd0ccf3 100644 --- a/src/Tasks.UnitTests/GetAssembliesMetadata_Tests.cs +++ b/src/Tasks.UnitTests/GetAssembliesMetadata_Tests.cs @@ -10,11 +10,11 @@ using System.Text; using System.Threading.Tasks; using Microsoft.Build.UnitTests; +using Microsoft.Build.UnitTests.Shared; using Shouldly; using Xunit; -using Xunit.Sdk; -using Microsoft.Build.UnitTests.Shared; using Xunit.Abstractions; +using Xunit.Sdk; namespace Microsoft.Build.Tasks.UnitTests { diff --git a/src/Tasks.UnitTests/GetInstalledSDKLocations_Tests.cs b/src/Tasks.UnitTests/GetInstalledSDKLocations_Tests.cs index 040199f9fb9..e5ad7426db7 100644 --- a/src/Tasks.UnitTests/GetInstalledSDKLocations_Tests.cs +++ b/src/Tasks.UnitTests/GetInstalledSDKLocations_Tests.cs @@ -10,7 +10,6 @@ using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/GetSDKReference_Tests.cs b/src/Tasks.UnitTests/GetSDKReference_Tests.cs index db7e1f155f1..f5ff062f87b 100644 --- a/src/Tasks.UnitTests/GetSDKReference_Tests.cs +++ b/src/Tasks.UnitTests/GetSDKReference_Tests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; using Microsoft.Build.Framework; using Microsoft.Build.Shared; @@ -13,7 +12,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/HintPathResolver_Tests.cs b/src/Tasks.UnitTests/HintPathResolver_Tests.cs index 599a5750961..9548e32f421 100644 --- a/src/Tasks.UnitTests/HintPathResolver_Tests.cs +++ b/src/Tasks.UnitTests/HintPathResolver_Tests.cs @@ -8,7 +8,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/MakeDir_Tests.cs b/src/Tasks.UnitTests/MakeDir_Tests.cs index 8a50acbd4c3..7b29328f258 100644 --- a/src/Tasks.UnitTests/MakeDir_Tests.cs +++ b/src/Tasks.UnitTests/MakeDir_Tests.cs @@ -8,7 +8,6 @@ using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/Move_Tests.cs b/src/Tasks.UnitTests/Move_Tests.cs index ca8bee21b50..a951d75d216 100644 --- a/src/Tasks.UnitTests/Move_Tests.cs +++ b/src/Tasks.UnitTests/Move_Tests.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/OutputPathTests.cs b/src/Tasks.UnitTests/OutputPathTests.cs index af1d3902804..1bf85cccef8 100644 --- a/src/Tasks.UnitTests/OutputPathTests.cs +++ b/src/Tasks.UnitTests/OutputPathTests.cs @@ -12,7 +12,6 @@ using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/PortableTasks_Tests.cs b/src/Tasks.UnitTests/PortableTasks_Tests.cs index e409d1f61bd..cd051c5a90e 100644 --- a/src/Tasks.UnitTests/PortableTasks_Tests.cs +++ b/src/Tasks.UnitTests/PortableTasks_Tests.cs @@ -9,7 +9,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/RegressionTests.cs b/src/Tasks.UnitTests/RegressionTests.cs index ef1b944e160..924a0adb84b 100644 --- a/src/Tasks.UnitTests/RegressionTests.cs +++ b/src/Tasks.UnitTests/RegressionTests.cs @@ -8,7 +8,6 @@ using Microsoft.Build.UnitTests; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/ResolveComReference_Tests.cs b/src/Tasks.UnitTests/ResolveComReference_Tests.cs index 47fb7e3c853..9dc7b95f2c2 100644 --- a/src/Tasks.UnitTests/ResolveComReference_Tests.cs +++ b/src/Tasks.UnitTests/ResolveComReference_Tests.cs @@ -5,8 +5,6 @@ using System; using System.Collections.Generic; -using System.IO; -using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; diff --git a/src/Tasks.UnitTests/ResolveSDKReference_Tests.cs b/src/Tasks.UnitTests/ResolveSDKReference_Tests.cs index 775098727b3..26ee71150e0 100644 --- a/src/Tasks.UnitTests/ResolveSDKReference_Tests.cs +++ b/src/Tasks.UnitTests/ResolveSDKReference_Tests.cs @@ -12,7 +12,6 @@ using Microsoft.Build.Utilities; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; using SDKReference = Microsoft.Build.Tasks.ResolveSDKReference.SDKReference; #nullable disable diff --git a/src/Tasks.UnitTests/ResourceHandling/GenerateResourceOutOfProc_Tests.cs b/src/Tasks.UnitTests/ResourceHandling/GenerateResourceOutOfProc_Tests.cs index 472d732a973..b9d1c4eae42 100644 --- a/src/Tasks.UnitTests/ResourceHandling/GenerateResourceOutOfProc_Tests.cs +++ b/src/Tasks.UnitTests/ResourceHandling/GenerateResourceOutOfProc_Tests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Threading; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; @@ -12,7 +11,6 @@ using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs b/src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs index 458fc147f78..772dfd5c743 100644 --- a/src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs +++ b/src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs @@ -11,12 +11,10 @@ using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; -using Microsoft.Build.UnitTests.Shared; using Microsoft.Build.Utilities; using Shouldly; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/RoslynCodeTaskFactory_Tests.cs b/src/Tasks.UnitTests/RoslynCodeTaskFactory_Tests.cs index a77f9d5759e..e1867c0129a 100644 --- a/src/Tasks.UnitTests/RoslynCodeTaskFactory_Tests.cs +++ b/src/Tasks.UnitTests/RoslynCodeTaskFactory_Tests.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text.RegularExpressions; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; diff --git a/src/Tasks.UnitTests/SGen_Tests.cs b/src/Tasks.UnitTests/SGen_Tests.cs index 75b712a53a7..2755cc179aa 100644 --- a/src/Tasks.UnitTests/SGen_Tests.cs +++ b/src/Tasks.UnitTests/SGen_Tests.cs @@ -4,7 +4,6 @@ using System; using System.IO; using System.Linq; -using Microsoft.Build.Shared; using Microsoft.Build.Tasks; using Shouldly; using Xunit; diff --git a/src/Tasks.UnitTests/SdkToolsPathUtility_Tests.cs b/src/Tasks.UnitTests/SdkToolsPathUtility_Tests.cs index f0cd952108b..a784e141ede 100644 --- a/src/Tasks.UnitTests/SdkToolsPathUtility_Tests.cs +++ b/src/Tasks.UnitTests/SdkToolsPathUtility_Tests.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/SecurityUtil_Tests.cs b/src/Tasks.UnitTests/SecurityUtil_Tests.cs index 696e5ef1470..7ae12543968 100644 --- a/src/Tasks.UnitTests/SecurityUtil_Tests.cs +++ b/src/Tasks.UnitTests/SecurityUtil_Tests.cs @@ -2,15 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Runtime.ConstrainedExecution; using System.Runtime.Versioning; -using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading.Tasks; using Microsoft.Build.Tasks.Deployment.ManifestUtilities; using Shouldly; using Xunit; diff --git a/src/Tasks.UnitTests/TestResources/Projects/Custom_COM/Custom_COM/Class1.cs b/src/Tasks.UnitTests/TestResources/Projects/Custom_COM/Custom_COM/Class1.cs index 891e68b690d..d36d6c2c0d4 100644 --- a/src/Tasks.UnitTests/TestResources/Projects/Custom_COM/Custom_COM/Class1.cs +++ b/src/Tasks.UnitTests/TestResources/Projects/Custom_COM/Custom_COM/Class1.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Reflection; using System.Runtime.InteropServices; namespace Custom_COM diff --git a/src/Tasks.UnitTests/TestResources/Projects/Custom_COM/Custom_COM/Properties/AssemblyInfo.cs b/src/Tasks.UnitTests/TestResources/Projects/Custom_COM/Custom_COM/Properties/AssemblyInfo.cs index 233af2b505c..71939e001bb 100644 --- a/src/Tasks.UnitTests/TestResources/Projects/Custom_COM/Custom_COM/Properties/AssemblyInfo.cs +++ b/src/Tasks.UnitTests/TestResources/Projects/Custom_COM/Custom_COM/Properties/AssemblyInfo.cs @@ -1,9 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Resources; using System.Reflection; -using System.Runtime.CompilerServices; +using System.Resources; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/src/Tasks.UnitTests/Unzip_Tests.cs b/src/Tasks.UnitTests/Unzip_Tests.cs index bcfe0b54460..e2e1494ee52 100644 --- a/src/Tasks.UnitTests/Unzip_Tests.cs +++ b/src/Tasks.UnitTests/Unzip_Tests.cs @@ -5,13 +5,11 @@ using System.Diagnostics; using System.IO; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.UnitTests; using Microsoft.Build.UnitTests.Shared; using Microsoft.Build.Utilities; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/WriteCodeFragment_Tests.cs b/src/Tasks.UnitTests/WriteCodeFragment_Tests.cs index 1c8dffcff37..d077c4f8ec7 100644 --- a/src/Tasks.UnitTests/WriteCodeFragment_Tests.cs +++ b/src/Tasks.UnitTests/WriteCodeFragment_Tests.cs @@ -10,7 +10,6 @@ using Microsoft.Build.Utilities; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Tasks.UnitTests/XmlPeek_Tests.cs b/src/Tasks.UnitTests/XmlPeek_Tests.cs index 63ce7c1be53..81b0a7ddd89 100644 --- a/src/Tasks.UnitTests/XmlPeek_Tests.cs +++ b/src/Tasks.UnitTests/XmlPeek_Tests.cs @@ -6,7 +6,6 @@ using System.Linq; using Microsoft.Build.Evaluation; -using Microsoft.Build.Framework; using Microsoft.Build.Tasks; using Microsoft.Build.Utilities; diff --git a/src/Tasks/AssemblyDependency/AssemblyAttributes.cs b/src/Tasks/AssemblyDependency/AssemblyAttributes.cs index 4e6fd111a27..56d51302e29 100644 --- a/src/Tasks/AssemblyDependency/AssemblyAttributes.cs +++ b/src/Tasks/AssemblyDependency/AssemblyAttributes.cs @@ -1,12 +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.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace Microsoft.Build.Tasks.AssemblyDependency { /// diff --git a/src/Tasks/AssemblyDependency/AssemblyInformation.cs b/src/Tasks/AssemblyDependency/AssemblyInformation.cs index 7f6fa93139c..2a3c9c6aef3 100644 --- a/src/Tasks/AssemblyDependency/AssemblyInformation.cs +++ b/src/Tasks/AssemblyDependency/AssemblyInformation.cs @@ -12,12 +12,9 @@ #endif using System.Reflection; using System.Runtime.Versioning; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; using System.Text; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; -using static Microsoft.Build.Shared.FileSystem.WindowsNative; #if FEATURE_ASSEMBLYLOADCONTEXT using System.Reflection.PortableExecutable; using System.Reflection.Metadata; diff --git a/src/Tasks/AssemblyDependency/GenerateBindingRedirects.cs b/src/Tasks/AssemblyDependency/GenerateBindingRedirects.cs index 99307761f8f..ad9503b4121 100644 --- a/src/Tasks/AssemblyDependency/GenerateBindingRedirects.cs +++ b/src/Tasks/AssemblyDependency/GenerateBindingRedirects.cs @@ -3,14 +3,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; +using System.Xml; using System.Xml.Linq; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; -using System.IO; -using System.Xml; #nullable disable diff --git a/src/Tasks/AssemblyDependency/InstalledAssemblies.cs b/src/Tasks/AssemblyDependency/InstalledAssemblies.cs index b7352e6b255..3c6fbf3ba98 100644 --- a/src/Tasks/AssemblyDependency/InstalledAssemblies.cs +++ b/src/Tasks/AssemblyDependency/InstalledAssemblies.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index b331aeb86be..a5a3ae00813 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -5,7 +5,9 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +#if !NET using System.Globalization; +#endif using System.IO; using System.Linq; using System.Reflection; @@ -2045,7 +2047,7 @@ private void LogConflict(Reference reference, string fusionName, StringBuilder l break; } } -#endregion + #endregion #region StateFile /// diff --git a/src/Tasks/Copy.cs b/src/Tasks/Copy.cs index 3cf26dc3eb4..96a0e17089b 100644 --- a/src/Tasks/Copy.cs +++ b/src/Tasks/Copy.cs @@ -7,7 +7,6 @@ using System.IO; using System.Runtime.InteropServices; using System.Threading; -using System.Threading.Tasks.Dataflow; using Microsoft.Build.Eventing; using Microsoft.Build.Framework; @@ -15,8 +14,6 @@ using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Utilities; -using TPLTask = System.Threading.Tasks.Task; - #nullable disable namespace Microsoft.Build.Tasks diff --git a/src/Tasks/CreateProperty.cs b/src/Tasks/CreateProperty.cs index d077411d58c..7d7d131b149 100644 --- a/src/Tasks/CreateProperty.cs +++ b/src/Tasks/CreateProperty.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 Microsoft.Build.Framework; #nullable disable diff --git a/src/Tasks/CultureInfoCache.cs b/src/Tasks/CultureInfoCache.cs index 53448eb1ba3..baca3658583 100644 --- a/src/Tasks/CultureInfoCache.cs +++ b/src/Tasks/CultureInfoCache.cs @@ -6,7 +6,6 @@ using System.Globalization; #if NET using System.Linq; -using Microsoft.Build.Framework; #endif using Microsoft.Build.Shared; diff --git a/src/Tasks/Error.cs b/src/Tasks/Error.cs index 9d5ad2ab386..b507c65e066 100644 --- a/src/Tasks/Error.cs +++ b/src/Tasks/Error.cs @@ -3,7 +3,9 @@ #nullable disable +#if NET using System.Diagnostics.CodeAnalysis; +#endif namespace Microsoft.Build.Tasks { diff --git a/src/Tasks/Exec.cs b/src/Tasks/Exec.cs index cf87ebedec1..4daa47cf647 100644 --- a/src/Tasks/Exec.cs +++ b/src/Tasks/Exec.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +#if NET using System.Diagnostics.CodeAnalysis; +#endif using System.IO; using System.Text; using System.Text.RegularExpressions; diff --git a/src/Tasks/FileIO/VerifyFileHash.cs b/src/Tasks/FileIO/VerifyFileHash.cs index 9c3eca7556b..58bd4ce7aa8 100644 --- a/src/Tasks/FileIO/VerifyFileHash.cs +++ b/src/Tasks/FileIO/VerifyFileHash.cs @@ -2,9 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Threading; using Microsoft.Build.Framework; using Microsoft.Build.Shared.FileSystem; -using System.Threading; #nullable disable diff --git a/src/Tasks/GenerateApplicationManifest.cs b/src/Tasks/GenerateApplicationManifest.cs index c91044032b0..cbd90c75ed4 100644 --- a/src/Tasks/GenerateApplicationManifest.cs +++ b/src/Tasks/GenerateApplicationManifest.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; -using System.Globalization; using System.IO; using System.Runtime.Versioning; using System.Xml; diff --git a/src/Tasks/GenerateManifestBase.cs b/src/Tasks/GenerateManifestBase.cs index 96ae300d52a..54362377d72 100644 --- a/src/Tasks/GenerateManifestBase.cs +++ b/src/Tasks/GenerateManifestBase.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Globalization; using System.IO; using Microsoft.Build.Framework; using Microsoft.Build.Shared; diff --git a/src/Tasks/GenerateResource.cs b/src/Tasks/GenerateResource.cs index b7e981bea5f..d761f90b332 100644 --- a/src/Tasks/GenerateResource.cs +++ b/src/Tasks/GenerateResource.cs @@ -16,7 +16,6 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; -using System.Linq; using System.Resources; using System.Resources.Extensions; using System.Reflection; @@ -42,6 +41,7 @@ using Microsoft.Build.Utilities; #if FEATURE_RESXREADER_LIVEDESERIALIZATION using Microsoft.Win32; +using System.Linq; #endif #nullable disable @@ -4014,7 +4014,7 @@ internal int LinePosition get { return column; } } } -#endregion // Code from ResGen.EXE + #endregion // Code from ResGen.EXE } #if !FEATURE_ASSEMBLYLOADCONTEXT diff --git a/src/Tasks/GetAssembliesMetadata.cs b/src/Tasks/GetAssembliesMetadata.cs index 73a3877001f..c3186034f57 100644 --- a/src/Tasks/GetAssembliesMetadata.cs +++ b/src/Tasks/GetAssembliesMetadata.cs @@ -2,20 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.IO; -using System.Linq; -using System.Reflection; using System.Runtime.Versioning; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; using Microsoft.Build.Shared; -using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Tasks.AssemblyDependency; using Microsoft.Build.Utilities; diff --git a/src/Tasks/GetAssemblyIdentity.cs b/src/Tasks/GetAssemblyIdentity.cs index f818366bc9f..375e0d2d430 100644 --- a/src/Tasks/GetAssemblyIdentity.cs +++ b/src/Tasks/GetAssemblyIdentity.cs @@ -3,12 +3,14 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.Reflection; -using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Utilities; +#if !NET +using System.Globalization; +using System.Text; +#endif #nullable disable diff --git a/src/Tasks/ManifestUtil/ApplicationManifest.cs b/src/Tasks/ManifestUtil/ApplicationManifest.cs index 555734fde12..22564f94bc6 100644 --- a/src/Tasks/ManifestUtil/ApplicationManifest.cs +++ b/src/Tasks/ManifestUtil/ApplicationManifest.cs @@ -9,7 +9,6 @@ using System.Runtime.InteropServices; using System.Xml; using System.Xml.Serialization; -using Microsoft.Build.Shared; #nullable disable diff --git a/src/Tasks/ManifestUtil/EmbeddedManifestReader.cs b/src/Tasks/ManifestUtil/EmbeddedManifestReader.cs index ff62bb0b948..dd5d26ca355 100644 --- a/src/Tasks/ManifestUtil/EmbeddedManifestReader.cs +++ b/src/Tasks/ManifestUtil/EmbeddedManifestReader.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Globalization; using System.IO; using System.Runtime.InteropServices; diff --git a/src/Tasks/ManifestUtil/Manifest.cs b/src/Tasks/ManifestUtil/Manifest.cs index 63b307cd6df..60b55b7cb1e 100644 --- a/src/Tasks/ManifestUtil/Manifest.cs +++ b/src/Tasks/ManifestUtil/Manifest.cs @@ -10,7 +10,6 @@ using System.Runtime.InteropServices; using System.Xml; using System.Xml.Serialization; -using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; #nullable disable diff --git a/src/Tasks/ManifestUtil/ManifestFormatter.cs b/src/Tasks/ManifestUtil/ManifestFormatter.cs index 91405ecaac7..6300dba4d0b 100644 --- a/src/Tasks/ManifestUtil/ManifestFormatter.cs +++ b/src/Tasks/ManifestUtil/ManifestFormatter.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Globalization; using System.IO; using System.Text; using System.Xml; diff --git a/src/Tasks/ManifestUtil/ManifestWriter.cs b/src/Tasks/ManifestUtil/ManifestWriter.cs index c5aff0aeae1..71d3a1478b6 100644 --- a/src/Tasks/ManifestUtil/ManifestWriter.cs +++ b/src/Tasks/ManifestUtil/ManifestWriter.cs @@ -3,7 +3,6 @@ using System; using System.Collections; -using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Xml.Serialization; diff --git a/src/Tasks/ManifestUtil/PathUtil.cs b/src/Tasks/ManifestUtil/PathUtil.cs index 705f2a79d3b..93ec71a20a8 100644 --- a/src/Tasks/ManifestUtil/PathUtil.cs +++ b/src/Tasks/ManifestUtil/PathUtil.cs @@ -3,8 +3,11 @@ using System; using System.IO; +#if !NET using System.Linq; +#else using System.Text; +#endif using Microsoft.Build.Shared; #nullable disable diff --git a/src/Tasks/ManifestUtil/TrustInfo.cs b/src/Tasks/ManifestUtil/TrustInfo.cs index 07eaa9e03d2..5583c56c349 100644 --- a/src/Tasks/ManifestUtil/TrustInfo.cs +++ b/src/Tasks/ManifestUtil/TrustInfo.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.IO; using System.Runtime.InteropServices; #if !RUNTIME_TYPE_NETCORE diff --git a/src/Tasks/ManifestUtil/XmlUtil.cs b/src/Tasks/ManifestUtil/XmlUtil.cs index 39c110eebb0..524ee02ff3d 100644 --- a/src/Tasks/ManifestUtil/XmlUtil.cs +++ b/src/Tasks/ManifestUtil/XmlUtil.cs @@ -5,7 +5,6 @@ using System.Collections; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.IO; using System.Reflection; using System.Text; diff --git a/src/Tasks/NativeMethods.cs b/src/Tasks/NativeMethods.cs index 8c761db013d..5f321996db9 100644 --- a/src/Tasks/NativeMethods.cs +++ b/src/Tasks/NativeMethods.cs @@ -12,7 +12,9 @@ using System.Collections.Generic; using System.Collections; using System.Globalization; +#if !NET using System.Linq; +#endif #if FEATURE_HANDLEPROCESSCORRUPTEDSTATEEXCEPTIONS using System.Runtime.ExceptionServices; #endif diff --git a/src/Tasks/ResolveSDKReference.cs b/src/Tasks/ResolveSDKReference.cs index 6a1a82c1ced..9d2c288a93e 100644 --- a/src/Tasks/ResolveSDKReference.cs +++ b/src/Tasks/ResolveSDKReference.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; diff --git a/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactoryCompilers.cs b/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactoryCompilers.cs index 50a26f7f5f2..21021e60adf 100644 --- a/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactoryCompilers.cs +++ b/src/Tasks/RoslynCodeTaskFactory/RoslynCodeTaskFactoryCompilers.cs @@ -4,10 +4,12 @@ using System; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using Microsoft.Build.Framework; -using Microsoft.Build.Shared; using Microsoft.Build.Utilities; +#if RUNTIME_TYPE_NETCORE +using System.Runtime.InteropServices; +using Microsoft.Build.Shared; +#endif #nullable disable diff --git a/src/Tasks/TaskRequiresFramework.cs b/src/Tasks/TaskRequiresFramework.cs index e681eb903b5..cbc3caf0b2f 100644 --- a/src/Tasks/TaskRequiresFramework.cs +++ b/src/Tasks/TaskRequiresFramework.cs @@ -1,8 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. - +#if NETFRAMEWORK using System; +#endif namespace Microsoft.Build.Tasks { diff --git a/src/Tasks/Warning.cs b/src/Tasks/Warning.cs index 2f46b591c2a..b1db0edd418 100644 --- a/src/Tasks/Warning.cs +++ b/src/Tasks/Warning.cs @@ -3,7 +3,9 @@ #nullable disable +#if NET using System.Diagnostics.CodeAnalysis; +#endif namespace Microsoft.Build.Tasks { diff --git a/src/Tasks/XamlTaskFactory/RelationsParser.cs b/src/Tasks/XamlTaskFactory/RelationsParser.cs index 6a119d563f2..415c3174a9f 100644 --- a/src/Tasks/XamlTaskFactory/RelationsParser.cs +++ b/src/Tasks/XamlTaskFactory/RelationsParser.cs @@ -6,8 +6,6 @@ using System.IO; using System.Xml; using Microsoft.Build.Shared; -using Microsoft.Build.Tasks.Deployment.ManifestUtilities; -using Microsoft.IO; using File = System.IO.File; #nullable disable diff --git a/src/UnitTests.Shared/DriveMapping.cs b/src/UnitTests.Shared/DriveMapping.cs index 81324086548..c9034b6902a 100644 --- a/src/UnitTests.Shared/DriveMapping.cs +++ b/src/UnitTests.Shared/DriveMapping.cs @@ -2,10 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. #nullable enable -using System; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using System.Text; namespace Microsoft.Build.UnitTests.Shared; diff --git a/src/UnitTests.Shared/DummyMappedDrive.cs b/src/UnitTests.Shared/DummyMappedDrive.cs index ec7df37a00d..064ec22d4b6 100644 --- a/src/UnitTests.Shared/DummyMappedDrive.cs +++ b/src/UnitTests.Shared/DummyMappedDrive.cs @@ -5,7 +5,6 @@ using System; using System.Diagnostics; using System.IO; -using System.Runtime.Versioning; namespace Microsoft.Build.UnitTests.Shared; diff --git a/src/UnitTests.Shared/DummyMappedDriveUtils.cs b/src/UnitTests.Shared/DummyMappedDriveUtils.cs index 90f596858d3..5e8412c956d 100644 --- a/src/UnitTests.Shared/DummyMappedDriveUtils.cs +++ b/src/UnitTests.Shared/DummyMappedDriveUtils.cs @@ -4,7 +4,6 @@ #nullable enable using System; using Microsoft.Build.Framework; -using Microsoft.Build.UnitTests.Shared; namespace Microsoft.Build.UnitTests.Shared; diff --git a/src/UnitTests.Shared/ObjectModelHelpers.cs b/src/UnitTests.Shared/ObjectModelHelpers.cs index 375c84d6779..a075aaad7e4 100644 --- a/src/UnitTests.Shared/ObjectModelHelpers.cs +++ b/src/UnitTests.Shared/ObjectModelHelpers.cs @@ -23,11 +23,9 @@ using Microsoft.Build.Logging; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; -using Microsoft.Build.Utilities; using Shouldly; using Xunit; using Xunit.Abstractions; -using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; #nullable disable diff --git a/src/UnitTests.Shared/RunnerUtilities.cs b/src/UnitTests.Shared/RunnerUtilities.cs index e0879c00028..038c1fc2b9b 100644 --- a/src/UnitTests.Shared/RunnerUtilities.cs +++ b/src/UnitTests.Shared/RunnerUtilities.cs @@ -3,12 +3,11 @@ using System; using System.Diagnostics; -using Microsoft.Build.Shared; using System.IO; using System.Reflection; using Microsoft.Build.Framework; +using Microsoft.Build.Shared; using Xunit.Abstractions; -using System.Linq; #nullable disable diff --git a/src/Utilities.UnitTests/EncodingUtilities_Tests.cs b/src/Utilities.UnitTests/EncodingUtilities_Tests.cs index a56ee25c4db..591ba25fd9c 100644 --- a/src/Utilities.UnitTests/EncodingUtilities_Tests.cs +++ b/src/Utilities.UnitTests/EncodingUtilities_Tests.cs @@ -1,11 +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; using System.Globalization; -using System.Runtime.InteropServices; -using System.Threading; -using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Shouldly; using Xunit; diff --git a/src/Utilities.UnitTests/MuxLogger_Tests.cs b/src/Utilities.UnitTests/MuxLogger_Tests.cs index db440177756..b4228b11909 100644 --- a/src/Utilities.UnitTests/MuxLogger_Tests.cs +++ b/src/Utilities.UnitTests/MuxLogger_Tests.cs @@ -2,9 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.IO; using System.Threading; -using System.Xml; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; diff --git a/src/Utilities.UnitTests/ProcessorArchitecture_Tests.cs b/src/Utilities.UnitTests/ProcessorArchitecture_Tests.cs index ff9c8243fea..d4d52e05d0f 100644 --- a/src/Utilities.UnitTests/ProcessorArchitecture_Tests.cs +++ b/src/Utilities.UnitTests/ProcessorArchitecture_Tests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using Microsoft.Build.Shared; using Microsoft.Build.Utilities; using Shouldly; using Xunit; diff --git a/src/Utilities.UnitTests/TaskItem_Tests.cs b/src/Utilities.UnitTests/TaskItem_Tests.cs index 04a54e3e511..a7db155e2ce 100644 --- a/src/Utilities.UnitTests/TaskItem_Tests.cs +++ b/src/Utilities.UnitTests/TaskItem_Tests.cs @@ -11,7 +11,6 @@ using Microsoft.Build.Utilities; using Shouldly; using Xunit; -using Xunit.NetCore.Extensions; #pragma warning disable 0219 diff --git a/src/Utilities.UnitTests/TaskLoggingHelper_Tests.cs b/src/Utilities.UnitTests/TaskLoggingHelper_Tests.cs index 9a3a96ef330..04e54d27c00 100644 --- a/src/Utilities.UnitTests/TaskLoggingHelper_Tests.cs +++ b/src/Utilities.UnitTests/TaskLoggingHelper_Tests.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using Microsoft.Build.Exceptions; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Utilities; diff --git a/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs b/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs index 7fbd0431575..a06401e8191 100644 --- a/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs +++ b/src/Utilities.UnitTests/ToolLocationHelper_Tests.cs @@ -23,7 +23,6 @@ using SharedDotNetFrameworkArchitecture = Microsoft.Build.Shared.DotNetFrameworkArchitecture; using Xunit; using Xunit.Abstractions; -using Xunit.NetCore.Extensions; #nullable disable diff --git a/src/Utilities/LockCheck.cs b/src/Utilities/LockCheck.cs index f9ec08b314a..187d3d59f8e 100644 --- a/src/Utilities/LockCheck.cs +++ b/src/Utilities/LockCheck.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Runtime.InteropServices; using System.Runtime.Versioning; -using Microsoft.Build.Framework; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Utilities/TargetPlatformSDK.cs b/src/Utilities/TargetPlatformSDK.cs index f0c7992c74b..64ef39b0ed5 100644 --- a/src/Utilities/TargetPlatformSDK.cs +++ b/src/Utilities/TargetPlatformSDK.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Globalization; using Microsoft.Build.Shared; #nullable disable diff --git a/src/Utilities/ToolLocationHelper.cs b/src/Utilities/ToolLocationHelper.cs index 598b9539c43..25ae27e63d0 100644 --- a/src/Utilities/ToolLocationHelper.cs +++ b/src/Utilities/ToolLocationHelper.cs @@ -9,7 +9,6 @@ using System.Runtime.Versioning; using System.Text; using System.Xml; -using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Tasks.AssemblyFoldersFromConfig; From 96ca754734f42eea40b99c50cdc67102e4abf15d Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 13:46:56 +0200 Subject: [PATCH 053/106] Update dependencies from https://github.com/dotnet/roslyn build 20250329.1 (#11646) --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 92bf0ff1e27..d9ade9bf3bb 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -141,13 +141,13 @@ https://github.com/nuget/nuget.client 13550619f90e73a1f8b4b5159c6d7f268c9756d0 - + https://github.com/dotnet/roslyn - 1def752c5d33903795069ccddb78599ba6da39d3 + 304768b76e90f5d224b745e3a03cfc5e9509baf6 - + https://github.com/dotnet/roslyn - 1def752c5d33903795069ccddb78599ba6da39d3 + 304768b76e90f5d224b745e3a03cfc5e9509baf6 diff --git a/eng/Versions.props b/eng/Versions.props index ce75c963a5a..b2510945229 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -84,7 +84,7 @@ $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) 4.2.0-1.22102.8 9.0.0-beta.25164.2 - 4.14.0-3.25171.27 + 4.14.0-3.25179.1 6.14.0-preview.1.89 From 37eb2be9d966d951248e0e04195890831187d028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6plinger?= Date: Mon, 31 Mar 2025 15:03:14 +0200 Subject: [PATCH 054/106] Remove RichCodeNavIndexer from .vsts-dotnet-ci.yml (#11647) The service is not coming back --- .vsts-dotnet-ci.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/.vsts-dotnet-ci.yml b/.vsts-dotnet-ci.yml index 8b1834e9b25..3ea490a61af 100644 --- a/.vsts-dotnet-ci.yml +++ b/.vsts-dotnet-ci.yml @@ -276,23 +276,6 @@ jobs: continueOnError: true condition: eq(variables.onlyDocChanged, 0) -# Unavailable in dnceng-public as of 9/1/2022; should be restored soon. -# - job: RichCodeNavIndex -# displayName: "Windows Code Indexing" -# pool: -# vmImage: 'windows-2022' -# steps: -# - task: BatchScript@1 -# displayName: build.cmd -# inputs: -# filename: 'build.cmd' -# - task: RichCodeNavIndexer@0 -# displayName: RichCodeNav Upload -# inputs: -# languages: 'csharp' -# continueOnError: true -# condition: succeeded() - - job: CoreBootstrappedOnLinux displayName: "Linux Core" dependsOn: IfOnlyDocumentionChanged From 06d1cb4401fa6a7e948e582c708987cb91238414 Mon Sep 17 00:00:00 2001 From: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com> Date: Mon, 31 Mar 2025 17:46:27 +0200 Subject: [PATCH 055/106] [REVERT] 11546 refactor common pipe code (#11648) --- .../BackEnd/NodeEndpointInProc_Tests.cs | 2 +- src/Build/BackEnd/Client/MSBuildClient.cs | 60 ++- .../BackEnd/Client/MSBuildClientPacketPump.cs | 133 ++++- .../Components/Communications/NodeManager.cs | 12 +- .../Communications/NodeProviderInProc.cs | 3 +- .../NodeProviderOutOfProcBase.cs | 482 ++++++++++++++++-- .../NodeProviderOutOfProcTaskHost.cs | 14 +- .../Communications/TaskHostNodeManager.cs | 3 +- src/Build/BackEnd/Node/InProcNode.cs | 3 +- src/Build/BackEnd/Node/OutOfProcNode.cs | 7 +- src/Build/BackEnd/Node/OutOfProcServerNode.cs | 7 +- .../Instance/TaskFactories/TaskHostTask.cs | 7 +- src/Build/Microsoft.Build.csproj | 4 +- src/MSBuild/MSBuild.csproj | 4 +- src/MSBuild/OutOfProcTaskHostNode.cs | 7 +- src/MSBuildTaskHost/MSBuildTaskHost.csproj | 10 +- src/Shared/BufferedReadStream.cs | 210 ++++++++ src/Shared/CommunicationsUtilities.cs | 48 +- src/Shared/INodePacketFactory.cs | 5 +- src/Shared/NodeEndpointOutOfProcBase.cs | 369 ++++++++++++-- src/Shared/NodePacketFactory.cs | 21 +- 21 files changed, 1219 insertions(+), 192 deletions(-) create mode 100644 src/Shared/BufferedReadStream.cs diff --git a/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs b/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs index 4b922bde816..5d0ae210a56 100644 --- a/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs +++ b/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs @@ -100,7 +100,7 @@ public void UnregisterPacketHandler(NodePacketType packetType) throw new NotImplementedException(); } - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { throw new NotImplementedException(); } diff --git a/src/Build/BackEnd/Client/MSBuildClient.cs b/src/Build/BackEnd/Client/MSBuildClient.cs index 5cceeb22d66..0abf86a2aa2 100644 --- a/src/Build/BackEnd/Client/MSBuildClient.cs +++ b/src/Build/BackEnd/Client/MSBuildClient.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Globalization; using System.IO; +using System.IO.Pipes; using System.Threading; using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Client; @@ -74,9 +75,19 @@ public sealed class MSBuildClient private readonly string _pipeName; /// - /// The named pipe client for client-server communication. + /// The named pipe stream for client-server communication. /// - private NodePipeClient _pipeClient = null!; + private NamedPipeClientStream _nodeStream = null!; + + /// + /// A way to cache a byte array when writing out packets + /// + private readonly MemoryStream _packetMemoryStream; + + /// + /// A binary writer to help write into + /// + private readonly BinaryWriter _binaryWriter; /// /// Used to estimate the size of the build with an ETW trace. @@ -119,14 +130,26 @@ public MSBuildClient( // Client <-> Server communication stream _handshake = GetHandshake(); _pipeName = OutOfProcServerNode.GetPipeName(_handshake); + _packetMemoryStream = new MemoryStream(); + _binaryWriter = new BinaryWriter(_packetMemoryStream); CreateNodePipeStream(); } private void CreateNodePipeStream() { - _pipeClient = new NodePipeClient(_pipeName, _handshake); - _packetPump = new MSBuildClientPacketPump(_pipeClient); +#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter + _nodeStream = new NamedPipeClientStream( + serverName: ".", + _pipeName, + PipeDirection.InOut, + PipeOptions.Asynchronous +#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY + | PipeOptions.CurrentUserOnly +#endif + ); +#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter + _packetPump = new MSBuildClientPacketPump(_nodeStream); } /// @@ -400,7 +423,7 @@ private bool TrySendPacket(Func packetResolver) try { packet = packetResolver(); - _pipeClient.WritePacket(packet); + WritePacket(_nodeStream, packet); CommunicationsUtilities.Trace("Command packet of type '{0}' sent...", packet.Type); } catch (Exception ex) @@ -598,7 +621,7 @@ private bool TryConnectToServer(int timeoutMilliseconds) tryAgain = false; try { - _pipeClient.ConnectToServer(Math.Max(1, timeoutMilliseconds - (int)sw.ElapsedMilliseconds)); + NodeProviderOutOfProcBase.ConnectToPipeStream(_nodeStream, _pipeName, _handshake, Math.Max(1, timeoutMilliseconds - (int)sw.ElapsedMilliseconds)); } catch (Exception ex) { @@ -621,5 +644,30 @@ private bool TryConnectToServer(int timeoutMilliseconds) return true; } + + private void WritePacket(Stream nodeStream, INodePacket packet) + { + MemoryStream memoryStream = _packetMemoryStream; + memoryStream.SetLength(0); + + ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(memoryStream); + + // Write header + memoryStream.WriteByte((byte)packet.Type); + + // Pad for packet length + _binaryWriter.Write(0); + + // Reset the position in the write buffer. + packet.Translate(writeTranslator); + + int packetStreamLength = (int)memoryStream.Position; + + // Now write in the actual packet length + memoryStream.Position = 1; + _binaryWriter.Write(packetStreamLength - 5); + + nodeStream.Write(memoryStream.GetBuffer(), 0, packetStreamLength); + } } } diff --git a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs index 17a39536c22..0219982e43e 100644 --- a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs +++ b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs @@ -2,9 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers.Binary; using System.Collections.Concurrent; +using System.IO; using System.Threading; + +#if NET using System.Threading.Tasks; +#endif + using Microsoft.Build.Internal; using Microsoft.Build.Shared; @@ -42,15 +48,25 @@ internal sealed class MSBuildClientPacketPump : INodePacketHandler, INodePacketF /// private readonly NodePacketFactory _packetFactory; + /// + /// The memory stream for a read buffer. + /// + private readonly MemoryStream _readBufferMemoryStream; + /// /// The thread which runs the asynchronous packet pump /// private Thread? _packetPumpThread; /// - /// The pipe client from where to read packets. + /// The stream from where to read packets. /// - private readonly NodePipeClient _pipeClient; + private readonly Stream _stream; + + /// + /// The binary translator for reading packets. + /// + private readonly ITranslator _binaryReadTranslator; /// /// True if this side is gracefully disconnecting. @@ -59,12 +75,11 @@ internal sealed class MSBuildClientPacketPump : INodePacketHandler, INodePacketF /// private bool _isServerDisconnecting; - public MSBuildClientPacketPump(NodePipeClient pipeClient) + public MSBuildClientPacketPump(Stream stream) { - ErrorUtilities.VerifyThrowArgumentNull(pipeClient); + ErrorUtilities.VerifyThrowArgumentNull(stream); - _pipeClient = pipeClient; - _pipeClient.RegisterPacketFactory(this); + _stream = stream; _isServerDisconnecting = false; _packetFactory = new NodePacketFactory(); @@ -72,6 +87,9 @@ public MSBuildClientPacketPump(NodePipeClient pipeClient) PacketReceivedEvent = new AutoResetEvent(false); PacketPumpCompleted = new ManualResetEvent(false); _packetPumpShutdownEvent = new ManualResetEvent(false); + + _readBufferMemoryStream = new MemoryStream(); + _binaryReadTranslator = BinaryTranslator.GetReadTranslator(_readBufferMemoryStream, InterningBinaryReader.CreateSharedBuffer()); } #region INodePacketFactory Members @@ -97,13 +115,14 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Deserializes a packet. + /// Deserializes and routes a packer to the appropriate handler. /// + /// The node from which the packet was received. /// The packet type. /// The translator to use as a source for packet data. - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { - return _packetFactory.DeserializePacket(packetType, translator); + _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } /// @@ -165,16 +184,21 @@ public void Stop() /// private void PacketPumpProc() { - RunReadLoop(_pipeClient, _packetPumpShutdownEvent); + RunReadLoop(_stream, _packetPumpShutdownEvent); } - private void RunReadLoop(NodePipeClient pipeClient, ManualResetEvent localPacketPumpShutdownEvent) + private void RunReadLoop(Stream localStream, ManualResetEvent localPacketPumpShutdownEvent) { CommunicationsUtilities.Trace("Entering read loop."); try { - Task readTask = pipeClient.ReadPacketAsync(); + byte[] headerByte = new byte[5]; +#if FEATURE_APM + IAsyncResult result = localStream.BeginRead(headerByte, 0, headerByte.Length, null, null); +#else + Task readTask = CommunicationsUtilities.ReadAsync(localStream, headerByte, headerByte.Length).AsTask(); +#endif bool continueReading = true; do @@ -186,7 +210,11 @@ private void RunReadLoop(NodePipeClient pipeClient, ManualResetEvent localPacket WaitHandle[] handles = [ localPacketPumpShutdownEvent, +#if FEATURE_APM + result.AsyncWaitHandle +#else ((IAsyncResult)readTask).AsyncWaitHandle +#endif ]; int waitId = WaitHandle.WaitAny(handles); switch (waitId) @@ -198,27 +226,80 @@ private void RunReadLoop(NodePipeClient pipeClient, ManualResetEvent localPacket break; case 1: - INodePacket packet = readTask.GetAwaiter().GetResult(); - - if (packet.Type == NodePacketType.NodeShutdown) { - if (!_isServerDisconnecting) + // Client recieved a packet header. Read the rest of it. + int headerBytesRead = 0; +#if FEATURE_APM + headerBytesRead = localStream.EndRead(result); +#else + headerBytesRead = readTask.Result; +#endif + + if ((headerBytesRead != headerByte.Length) && !localPacketPumpShutdownEvent.WaitOne(0)) { - ErrorUtilities.ThrowInternalError("Server disconnected abruptly."); + // Incomplete read. Abort. + if (headerBytesRead == 0) + { + if (_isServerDisconnecting) + { + continueReading = false; + break; + } + + ErrorUtilities.ThrowInternalError("Server disconnected abruptly"); + } + else + { + ErrorUtilities.ThrowInternalError("Incomplete header read. {0} of {1} bytes read", headerBytesRead, headerByte.Length); + } } - continueReading = false; - break; - } + NodePacketType packetType = (NodePacketType)Enum.ToObject(typeof(NodePacketType), headerByte[0]); - _packetFactory.RoutePacket(0, packet); + int packetLength = BinaryPrimitives.ReadInt32LittleEndian(new Span(headerByte, 1, 4)); + int packetBytesRead = 0; - continueReading = packet.Type != NodePacketType.ServerNodeBuildResult; - if (continueReading) - { - readTask = pipeClient.ReadPacketAsync(); - } + _readBufferMemoryStream.Position = 0; + _readBufferMemoryStream.SetLength(packetLength); + byte[] packetData = _readBufferMemoryStream.GetBuffer(); + while (packetBytesRead < packetLength) + { + int bytesRead = localStream.Read(packetData, packetBytesRead, packetLength - packetBytesRead); + if (bytesRead == 0) + { + // Incomplete read. Abort. + ErrorUtilities.ThrowInternalError("Incomplete packet read. {0} of {1} bytes read", packetBytesRead, packetLength); + } + + packetBytesRead += bytesRead; + } + + try + { + _packetFactory.DeserializeAndRoutePacket(0, packetType, _binaryReadTranslator); + } + catch + { + // Error while deserializing or handling packet. Logging additional info. + CommunicationsUtilities.Trace("Packet factory failed to receive package. Exception while deserializing packet {0}.", packetType); + throw; + } + + if (packetType == NodePacketType.ServerNodeBuildResult) + { + continueReading = false; + } + else + { + // Start reading the next package header. +#if FEATURE_APM + result = localStream.BeginRead(headerByte, 0, headerByte.Length, null, null); +#else + readTask = CommunicationsUtilities.ReadAsync(localStream, headerByte, headerByte.Length).AsTask(); +#endif + } + } break; default: diff --git a/src/Build/BackEnd/Components/Communications/NodeManager.cs b/src/Build/BackEnd/Components/Communications/NodeManager.cs index 96cb184f1c2..b0031746031 100644 --- a/src/Build/BackEnd/Components/Communications/NodeManager.cs +++ b/src/Build/BackEnd/Components/Communications/NodeManager.cs @@ -240,13 +240,19 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Takes a serializer and deserializes the packet. + /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. /// + /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { - return _packetFactory.DeserializePacket(packetType, translator); + if (packetType == NodePacketType.NodeShutdown) + { + RemoveNodeFromMapping(nodeId); + } + + _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } /// diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs index ff0347b6f13..45334ce6752 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs @@ -286,11 +286,10 @@ public void UnregisterPacketHandler(NodePacketType packetType) /// /// Deserializes and routes a packet. Not used in the in-proc node. /// - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { // Not used ErrorUtilities.ThrowInternalErrorUnreachable(); - return null; } /// diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index d15ee32c4e6..4e6a7b77970 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -1,22 +1,30 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System; +using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; +using System.IO.Pipes; using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Build.BackEnd.Logging; + +#if NETFRAMEWORK +using Microsoft.Build.Eventing; +using System.Security.Principal; +#endif + using Microsoft.Build.Framework; using Microsoft.Build.Internal; using Microsoft.Build.Shared; +#nullable disable + namespace Microsoft.Build.BackEnd { /// @@ -25,6 +33,11 @@ namespace Microsoft.Build.BackEnd /// internal abstract class NodeProviderOutOfProcBase { + /// + /// The maximum number of bytes to write + /// + private const int MaxPacketWriteSize = 1048576; + /// /// The number of times to retry creating an out-of-proc node. /// @@ -40,6 +53,9 @@ internal abstract class NodeProviderOutOfProcBase /// private const int TimeoutForWaitForExit = 30000; +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY + private static readonly WindowsIdentity s_currentWindowsIdentity = WindowsIdentity.GetCurrent(); +#endif /// /// The build component host. /// @@ -145,18 +161,21 @@ protected void ShutdownAllNodes(bool nodeReuse, NodeContextTerminateDelegate ter int timeout = 30; // Attempt to connect to the process with the handshake without low priority. - NodePipeClient pipeClient = TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, false)); + Stream nodeStream = TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, false)); - // If we couldn't connect attempt to connect to the process with the handshake including low priority. - pipeClient ??= TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, true)); + if (nodeStream == null) + { + // If we couldn't connect attempt to connect to the process with the handshake including low priority. + nodeStream = TryConnectToProcess(nodeProcess.Id, timeout, NodeProviderOutOfProc.GetHandshake(nodeReuse, true)); + } - if (pipeClient != null) + if (nodeStream != null) { // If we're able to connect to such a process, send a packet requesting its termination CommunicationsUtilities.Trace("Shutting down node with pid = {0}", nodeProcess.Id); - NodeContext nodeContext = new(0, nodeProcess, pipeClient, factory, terminateNode); + NodeContext nodeContext = new NodeContext(0, nodeProcess, nodeStream, factory, terminateNode); nodeContext.SendData(new NodeBuildComplete(false /* no node reuse */)); - pipeClient.Dispose(); + nodeStream.Dispose(); } } } @@ -263,8 +282,8 @@ bool TryReuseAnyFromPossibleRunningNodes(int currentProcessId, int nodeId) _processesToIgnore.TryAdd(nodeLookupKey, default); // Attempt to connect to each process in turn. - NodePipeClient pipeClient = TryConnectToProcess(nodeToReuse.Id, 0 /* poll, don't wait for connections */, hostHandshake); - if (pipeClient != null) + Stream nodeStream = TryConnectToProcess(nodeToReuse.Id, 0 /* poll, don't wait for connections */, hostHandshake); + if (nodeStream != null) { // Connection successful, use this node. CommunicationsUtilities.Trace("Successfully connected to existed node {0} which is PID {1}", nodeId, nodeToReuse.Id); @@ -274,7 +293,7 @@ bool TryReuseAnyFromPossibleRunningNodes(int currentProcessId, int nodeId) BuildEventContext = new BuildEventContext(nodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId) }); - CreateNodeContext(nodeId, nodeToReuse, pipeClient); + CreateNodeContext(nodeId, nodeToReuse, nodeStream); return true; } } @@ -323,13 +342,13 @@ bool StartNewNode(int nodeId) // to the debugger process. Instead, use MSBUILDDEBUGONSTART=1 // Now try to connect to it. - NodePipeClient pipeClient = TryConnectToProcess(msbuildProcess.Id, TimeoutForNewNodeCreation, hostHandshake); - if (pipeClient != null) + Stream nodeStream = TryConnectToProcess(msbuildProcess.Id, TimeoutForNewNodeCreation, hostHandshake); + if (nodeStream != null) { // Connection successful, use this node. CommunicationsUtilities.Trace("Successfully connected to created node {0} which is PID {1}", nodeId, msbuildProcess.Id); - CreateNodeContext(nodeId, msbuildProcess, pipeClient); + CreateNodeContext(nodeId, msbuildProcess, nodeStream); return true; } @@ -358,9 +377,9 @@ bool StartNewNode(int nodeId) return false; } - void CreateNodeContext(int nodeId, Process nodeToReuse, NodePipeClient pipeClient) + void CreateNodeContext(int nodeId, Process nodeToReuse, Stream nodeStream) { - NodeContext nodeContext = new(nodeId, nodeToReuse, pipeClient, factory, terminateNode); + NodeContext nodeContext = new(nodeId, nodeToReuse, nodeStream, factory, terminateNode); nodeContexts.Enqueue(nodeContext); createNode(nodeContext); } @@ -402,22 +421,52 @@ private string GetProcessesToIgnoreKey(Handshake hostHandshake, int nodeProcessI #endif } +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY + // This code needs to be in a separate method so that we don't try (and fail) to load the Windows-only APIs when JIT-ing the code + // on non-Windows operating systems + private static void ValidateRemotePipeSecurityOnWindows(NamedPipeClientStream nodeStream) + { + SecurityIdentifier identifier = s_currentWindowsIdentity.Owner; +#if FEATURE_PIPE_SECURITY + PipeSecurity remoteSecurity = nodeStream.GetAccessControl(); +#else + var remoteSecurity = new PipeSecurity(nodeStream.SafePipeHandle, System.Security.AccessControl.AccessControlSections.Access | + System.Security.AccessControl.AccessControlSections.Owner | System.Security.AccessControl.AccessControlSections.Group); +#endif + IdentityReference remoteOwner = remoteSecurity.GetOwner(typeof(SecurityIdentifier)); + if (remoteOwner != identifier) + { + CommunicationsUtilities.Trace("The remote pipe owner {0} does not match {1}", remoteOwner.Value, identifier.Value); + throw new UnauthorizedAccessException(); + } + } +#endif + /// /// Attempts to connect to the specified process. /// - private NodePipeClient TryConnectToProcess(int nodeProcessId, int timeout, Handshake handshake) + private Stream TryConnectToProcess(int nodeProcessId, int timeout, Handshake handshake) { // Try and connect to the process. string pipeName = NamedPipeUtil.GetPlatformSpecificPipeName(nodeProcessId); - NodePipeClient pipeClient = new(pipeName, handshake); - - CommunicationsUtilities.Trace("Attempting connect to PID {0}", nodeProcessId); +#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter + NamedPipeClientStream nodeStream = new NamedPipeClientStream( + serverName: ".", + pipeName, + PipeDirection.InOut, + PipeOptions.Asynchronous +#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY + | PipeOptions.CurrentUserOnly +#endif + ); +#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter + CommunicationsUtilities.Trace("Attempting connect to PID {0} with pipe {1} with timeout {2} ms", nodeProcessId, pipeName, timeout); try { - pipeClient.ConnectToServer(timeout); - return pipeClient; + ConnectToPipeStream(nodeStream, pipeName, handshake, timeout); + return nodeStream; } catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { @@ -429,12 +478,56 @@ private NodePipeClient TryConnectToProcess(int nodeProcessId, int timeout, Hands CommunicationsUtilities.Trace("Failed to connect to pipe {0}. {1}", pipeName, e.Message.TrimEnd()); // If we don't close any stream, we might hang up the child - pipeClient?.Dispose(); + nodeStream?.Dispose(); } return null; } + /// + /// Connect to named pipe stream and ensure validate handshake and security. + /// + /// + /// Reused by MSBuild server client . + /// + internal static void ConnectToPipeStream(NamedPipeClientStream nodeStream, string pipeName, Handshake handshake, int timeout) + { + nodeStream.Connect(timeout); + +#if !FEATURE_PIPEOPTIONS_CURRENTUSERONLY + if (NativeMethodsShared.IsWindows) + { + // Verify that the owner of the pipe is us. This prevents a security hole where a remote node has + // been faked up with ACLs that would let us attach to it. It could then issue fake build requests back to + // us, potentially causing us to execute builds that do harmful or unexpected things. The pipe owner can + // only be set to the user's own SID by a normal, unprivileged process. The conditions where a faked up + // remote node could set the owner to something else would also let it change owners on other objects, so + // this would be a security flaw upstream of us. + ValidateRemotePipeSecurityOnWindows(nodeStream); + } +#endif + + int[] handshakeComponents = handshake.RetrieveHandshakeComponents(); + for (int i = 0; i < handshakeComponents.Length; i++) + { + CommunicationsUtilities.Trace("Writing handshake part {0} ({1}) to pipe {2}", i, handshakeComponents[i], pipeName); + nodeStream.WriteIntForHandshake(handshakeComponents[i]); + } + + // This indicates that we have finished all the parts of our handshake; hopefully the endpoint has as well. + nodeStream.WriteEndOfHandshakeSignal(); + + CommunicationsUtilities.Trace("Reading handshake from pipe {0}", pipeName); + +#if NETCOREAPP2_1_OR_GREATER + nodeStream.ReadEndOfHandshakeSignal(true, timeout); +#else + nodeStream.ReadEndOfHandshakeSignal(true); +#endif + // We got a connection. + CommunicationsUtilities.Trace("Successfully connected to pipe {0}...!", pipeName); + } + /// /// Class which wraps up the communications infrastructure for a given node. /// @@ -447,13 +540,14 @@ private enum ExitPacketState ExitPacketSent } - // The pipe client used to communicate with the node. - private readonly NodePipeClient _pipeClient; + // The pipe(s) used to communicate with the node. + private Stream _clientToServerStream; + private Stream _serverToClientStream; /// /// The factory used to create packets from data read off the pipe. /// - private readonly INodePacketFactory _packetFactory; + private INodePacketFactory _packetFactory; /// /// The node id assigned by the node provider. @@ -467,6 +561,23 @@ private enum ExitPacketState internal Process Process { get { return _process; } } + /// + /// An array used to store the header byte for each packet when read. + /// + private byte[] _headerByte; + + /// + /// A buffer typically big enough to handle a packet body. + /// We use this as a convenient way to manage and cache a byte[] that's resized + /// automatically to fit our payload. + /// + private MemoryStream _readBufferMemoryStream; + + /// + /// A reusable buffer for writing packets. + /// + private MemoryStream _writeBufferMemoryStream; + /// /// A queue used for enqueuing packets to write to the stream asynchronously. /// @@ -489,19 +600,28 @@ private enum ExitPacketState /// private ExitPacketState _exitPacketState; + /// + /// Per node read buffers + /// + private BinaryReaderFactory _binaryReaderFactory; + /// /// Constructor. /// public NodeContext(int nodeId, Process process, - NodePipeClient pipeClient, + Stream nodePipe, INodePacketFactory factory, NodeContextTerminateDelegate terminateDelegate) { _nodeId = nodeId; _process = process; - _pipeClient = pipeClient; - _pipeClient.RegisterPacketFactory(factory); + _clientToServerStream = nodePipe; + _serverToClientStream = nodePipe; _packetFactory = factory; + _headerByte = new byte[5]; // 1 for the packet type, 4 for the body length + _readBufferMemoryStream = new MemoryStream(); + _writeBufferMemoryStream = new MemoryStream(); _terminateDelegate = terminateDelegate; + _binaryReaderFactory = InterningBinaryReader.CreateSharedBuffer(); } /// @@ -514,49 +634,73 @@ public NodeContext(int nodeId, Process process, /// public void BeginAsyncPacketRead() { - _ = ThreadPool.QueueUserWorkItem(_ => _ = RunPacketReadLoopAsync()); +#if FEATURE_APM + _clientToServerStream.BeginRead(_headerByte, 0, _headerByte.Length, HeaderReadComplete, this); +#else + ThreadPool.QueueUserWorkItem(delegate + { + var ignored = RunPacketReadLoopAsync(); + }); +#endif } +#if !FEATURE_APM public async Task RunPacketReadLoopAsync() { - INodePacket packet = null; - - while (packet?.Type != NodePacketType.NodeShutdown) + while (true) { try { - packet = await _pipeClient.ReadPacketAsync().ConfigureAwait(false); + int bytesRead = await CommunicationsUtilities.ReadAsync(_clientToServerStream, _headerByte, _headerByte.Length); + if (!ProcessHeaderBytesRead(bytesRead)) + { + return; + } } catch (IOException e) { - CommunicationsUtilities.Trace(_nodeId, "COMMUNICATIONS ERROR (HRC) Node: {0} Process: {1} Exception: {2}", _nodeId, _process.Id, e.Message); - packet = new NodeShutdown(NodeShutdownReason.ConnectionFailed); + CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in RunPacketReadLoopAsync: {0}", e); + _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); + Close(); + return; } - if (packet.Type == NodePacketType.NodeShutdown && (packet as NodeShutdown).Reason == NodeShutdownReason.ConnectionFailed) + NodePacketType packetType = (NodePacketType)_headerByte[0]; + int packetLength = BinaryPrimitives.ReadInt32LittleEndian(new Span(_headerByte, 1, 4)); + + _readBufferMemoryStream.SetLength(packetLength); + byte[] packetData = _readBufferMemoryStream.GetBuffer(); + + try { - try - { - if (_process.HasExited) - { - CommunicationsUtilities.Trace(_nodeId, " Child Process {0} has exited.", _process.Id); - } - else - { - CommunicationsUtilities.Trace(_nodeId, " Child Process {0} is still running.", _process.Id); - } - } - catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) + int bytesRead = await CommunicationsUtilities.ReadAsync(_clientToServerStream, packetData, packetLength); + if (!ProcessBodyBytesRead(bytesRead, packetLength, packetType)) { - CommunicationsUtilities.Trace(_nodeId, "Unable to retrieve remote process information. {0}", e); + return; } } + catch (IOException e) + { + CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in RunPacketReadLoopAsync (Reading): {0}", e); + _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); + Close(); + return; + } - _packetFactory.RoutePacket(_nodeId, packet); - } + // Read and route the packet. + if (!ReadAndRoutePacket(packetType, packetData, packetLength)) + { + return; + } - Close(); + if (packetType == NodePacketType.NodeShutdown) + { + Close(); + return; + } + } } +#endif /// /// Sends the specified packet to this node asynchronously. @@ -602,11 +746,37 @@ private void DrainPacketQueue() static async Task SendDataCoreAsync(Task _, object state) { NodeContext context = (NodeContext)state; - while (context._packetWriteQueue.TryDequeue(out INodePacket packet)) + while (context._packetWriteQueue.TryDequeue(out var packet)) { + MemoryStream writeStream = context._writeBufferMemoryStream; + + // clear the buffer but keep the underlying capacity to avoid reallocations + writeStream.SetLength(0); + + ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(writeStream); try { - await context._pipeClient.WritePacketAsync(packet).ConfigureAwait(false); + writeStream.WriteByte((byte)packet.Type); + + // Pad for the packet length + WriteInt32(writeStream, 0); + packet.Translate(writeTranslator); + + int writeStreamLength = (int)writeStream.Position; + + // Now plug in the real packet length + writeStream.Position = 1; + WriteInt32(writeStream, writeStreamLength - 5); + + byte[] writeStreamBuffer = writeStream.GetBuffer(); + + for (int i = 0; i < writeStreamLength; i += MaxPacketWriteSize) + { + int lengthToWrite = Math.Min(writeStreamLength - i, MaxPacketWriteSize); +#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' + await context._serverToClientStream.WriteAsync(writeStreamBuffer, i, lengthToWrite, CancellationToken.None); +#pragma warning restore CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' + } if (IsExitPacket(packet)) { @@ -632,12 +802,27 @@ private static bool IsExitPacket(INodePacket packet) return packet is NodeBuildComplete buildCompletePacket && !buildCompletePacket.PrepareForReuse; } + /// + /// Avoid having a BinaryWriter just to write a 4-byte int + /// + private static void WriteInt32(MemoryStream stream, int value) + { + stream.WriteByte((byte)value); + stream.WriteByte((byte)(value >> 8)); + stream.WriteByte((byte)(value >> 16)); + stream.WriteByte((byte)(value >> 24)); + } + /// /// Closes the node's context, disconnecting it from the node. /// private void Close() { - _pipeClient.Dispose(); + _clientToServerStream.Dispose(); + if (!object.ReferenceEquals(_clientToServerStream, _serverToClientStream)) + { + _serverToClientStream.Dispose(); + } _terminateDelegate(_nodeId); } @@ -695,6 +880,191 @@ public async Task WaitForExitAsync(ILoggingService loggingService) _process.KillTree(timeoutMilliseconds: 5000); } + +#if FEATURE_APM + /// + /// Completes the asynchronous packet write to the node. + /// + private void PacketWriteComplete(IAsyncResult result) + { + try + { + _serverToClientStream.EndWrite(result); + } + catch (IOException) + { + // Do nothing here because any exception will be caught by the async read handler + } + } +#endif + + private bool ProcessHeaderBytesRead(int bytesRead) + { + if (bytesRead != _headerByte.Length) + { + CommunicationsUtilities.Trace(_nodeId, "COMMUNICATIONS ERROR (HRC) Node: {0} Process: {1} Bytes Read: {2} Expected: {3}", _nodeId, _process.Id, bytesRead, _headerByte.Length); + try + { + if (_process.HasExited) + { + CommunicationsUtilities.Trace(_nodeId, " Child Process {0} has exited.", _process.Id); + } + else + { + CommunicationsUtilities.Trace(_nodeId, " Child Process {0} is still running.", _process.Id); + } + } + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) + { + CommunicationsUtilities.Trace(_nodeId, "Unable to retrieve remote process information. {0}", e); + } + + _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); + Close(); + return false; + } + + return true; + } + +#if FEATURE_APM + /// + /// Callback invoked by the completion of a read of a header byte on one of the named pipes. + /// + private void HeaderReadComplete(IAsyncResult result) + { + int bytesRead; + try + { + try + { + bytesRead = _clientToServerStream.EndRead(result); + } + + // Workaround for CLR stress bug; it sporadically calls us twice on the same async + // result, and EndRead will throw on the second one. Pretend the second one never happened. + catch (ArgumentException) + { + CommunicationsUtilities.Trace(_nodeId, "Hit CLR bug #825607: called back twice on same async result; ignoring"); + return; + } + + if (!ProcessHeaderBytesRead(bytesRead)) + { + return; + } + } + catch (IOException e) + { + CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in HeaderReadComplete: {0}", e); + _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); + Close(); + return; + } + + int packetLength = BinaryPrimitives.ReadInt32LittleEndian(new Span(_headerByte, 1, 4)); + MSBuildEventSource.Log.PacketReadSize(packetLength); + + // Ensures the buffer is at least this length. + // It avoids reallocations if the buffer is already large enough. + _readBufferMemoryStream.SetLength(packetLength); + byte[] packetData = _readBufferMemoryStream.GetBuffer(); + + _clientToServerStream.BeginRead(packetData, 0, packetLength, BodyReadComplete, new Tuple(packetData, packetLength)); + } +#endif + + private bool ProcessBodyBytesRead(int bytesRead, int packetLength, NodePacketType packetType) + { + if (bytesRead != packetLength) + { + CommunicationsUtilities.Trace(_nodeId, "Bad packet read for packet {0} - Expected {1} bytes, got {2}", packetType, packetLength, bytesRead); + _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); + Close(); + return false; + } + return true; + } + + private bool ReadAndRoutePacket(NodePacketType packetType, byte[] packetData, int packetLength) + { + try + { + // The buffer is publicly visible so that InterningBinaryReader doesn't have to copy to an intermediate buffer. + // Since the buffer is publicly visible dispose right away to discourage outsiders from holding a reference to it. + using (var packetStream = new MemoryStream(packetData, 0, packetLength, /*writeable*/ false, /*bufferIsPubliclyVisible*/ true)) + { + ITranslator readTranslator = BinaryTranslator.GetReadTranslator(packetStream, _binaryReaderFactory); + _packetFactory.DeserializeAndRoutePacket(_nodeId, packetType, readTranslator); + } + } + catch (IOException e) + { + CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in ReadAndRoutPacket: {0}", e); + _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); + Close(); + return false; + } + return true; + } + +#if FEATURE_APM + /// + /// Method called when the body of a packet has been read. + /// + private void BodyReadComplete(IAsyncResult result) + { + NodePacketType packetType = (NodePacketType)_headerByte[0]; + var state = (Tuple)result.AsyncState; + byte[] packetData = state.Item1; + int packetLength = state.Item2; + int bytesRead; + + try + { + try + { + bytesRead = _clientToServerStream.EndRead(result); + } + + // Workaround for CLR stress bug; it sporadically calls us twice on the same async + // result, and EndRead will throw on the second one. Pretend the second one never happened. + catch (ArgumentException) + { + CommunicationsUtilities.Trace(_nodeId, "Hit CLR bug #825607: called back twice on same async result; ignoring"); + return; + } + + if (!ProcessBodyBytesRead(bytesRead, packetLength, packetType)) + { + return; + } + } + catch (IOException e) + { + CommunicationsUtilities.Trace(_nodeId, "EXCEPTION in BodyReadComplete (Reading): {0}", e); + _packetFactory.RoutePacket(_nodeId, new NodeShutdown(NodeShutdownReason.ConnectionFailed)); + Close(); + return; + } + + // Read and route the packet. + if (!ReadAndRoutePacket(packetType, packetData, packetLength)) + { + return; + } + + if (packetType != NodePacketType.NodeShutdown) + { + // Read the next packet. + BeginAsyncPacketRead(); + } + else + { + Close(); + } + } +#endif } } } diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs index d7885edb750..1d0f0f525d3 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs @@ -270,13 +270,21 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Takes a serializer and deserializes the packet. + /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. /// + /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { - return _localPacketFactory.DeserializePacket(packetType, translator); + if (_nodeIdToPacketFactory.TryGetValue(nodeId, out INodePacketFactory nodePacketFactory)) + { + nodePacketFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); + } + else + { + _localPacketFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); + } } /// diff --git a/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs b/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs index cafc95a4f22..e7e66d6b886 100644 --- a/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs +++ b/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs @@ -141,9 +141,10 @@ public void UnregisterPacketHandler(NodePacketType packetType) /// /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. /// + /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { throw new NotSupportedException("not used"); } diff --git a/src/Build/BackEnd/Node/InProcNode.cs b/src/Build/BackEnd/Node/InProcNode.cs index 6ac34aabdd4..7b4049f8905 100644 --- a/src/Build/BackEnd/Node/InProcNode.cs +++ b/src/Build/BackEnd/Node/InProcNode.cs @@ -216,11 +216,10 @@ public void UnregisterPacketHandler(NodePacketType packetType) /// /// Not necessary for in-proc node - we don't serialize. /// - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { // The in-proc endpoint shouldn't be serializing, just routing. ErrorUtilities.ThrowInternalError("Unexpected call to DeserializeAndRoutePacket on the in-proc node."); - return null; } /// diff --git a/src/Build/BackEnd/Node/OutOfProcNode.cs b/src/Build/BackEnd/Node/OutOfProcNode.cs index c26c657f9a7..0adad41674c 100644 --- a/src/Build/BackEnd/Node/OutOfProcNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcNode.cs @@ -333,13 +333,14 @@ void INodePacketFactory.UnregisterPacketHandler(NodePacketType packetType) } /// - /// Deserializes a packet. + /// Deserializes and routes a packer to the appropriate handler. /// + /// The node from which the packet was received. /// The packet type. /// The translator to use as a source for packet data. - INodePacket INodePacketFactory.DeserializePacket(NodePacketType packetType, ITranslator translator) + void INodePacketFactory.DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { - return _packetFactory.DeserializePacket(packetType, translator); + _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } /// diff --git a/src/Build/BackEnd/Node/OutOfProcServerNode.cs b/src/Build/BackEnd/Node/OutOfProcServerNode.cs index 5131fe962e6..aefae5aceab 100644 --- a/src/Build/BackEnd/Node/OutOfProcServerNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcServerNode.cs @@ -200,13 +200,14 @@ void INodePacketFactory.UnregisterPacketHandler(NodePacketType packetType) } /// - /// Deserializes a packet. + /// Deserializes and routes a packer to the appropriate handler. /// + /// The node from which the packet was received. /// The packet type. /// The translator to use as a source for packet data. - INodePacket INodePacketFactory.DeserializePacket(NodePacketType packetType, ITranslator translator) + void INodePacketFactory.DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { - return _packetFactory.DeserializePacket(packetType, translator); + _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } /// diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index 753b9add55b..fd169167a9c 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -364,13 +364,14 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Takes a serializer and deserializes the packet. + /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. /// + /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { - return _packetFactory.DeserializePacket(packetType, translator); + _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } /// diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index 6ccd378e62f..b28ac113cd2 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -95,6 +95,7 @@ Collections\ReadOnlyEmptyCollection.cs + @@ -104,9 +105,6 @@ - - - diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj index a7dc889b270..2edca8c339b 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -88,6 +88,7 @@ + @@ -102,9 +103,6 @@ - - - diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs index 0f1b21b3664..fbd97f6e083 100644 --- a/src/MSBuild/OutOfProcTaskHostNode.cs +++ b/src/MSBuild/OutOfProcTaskHostNode.cs @@ -581,13 +581,14 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Takes a serializer and deserializes the packet. + /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. /// + /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { - return _packetFactory.DeserializePacket(packetType, translator); + _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } /// diff --git a/src/MSBuildTaskHost/MSBuildTaskHost.csproj b/src/MSBuildTaskHost/MSBuildTaskHost.csproj index d0ad4122b8d..a189f58567a 100644 --- a/src/MSBuildTaskHost/MSBuildTaskHost.csproj +++ b/src/MSBuildTaskHost/MSBuildTaskHost.csproj @@ -64,6 +64,7 @@ + CopyOnWriteDictionary.cs @@ -139,15 +140,6 @@ NodeBuildComplete.cs - - NodeComponentBase.cs - - - NodeComponentBase.cs - - - NodeComponentBase.cs - NodeEndpointOutOfProcBase.cs diff --git a/src/Shared/BufferedReadStream.cs b/src/Shared/BufferedReadStream.cs new file mode 100644 index 00000000000..55bba5986f8 --- /dev/null +++ b/src/Shared/BufferedReadStream.cs @@ -0,0 +1,210 @@ +// 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.IO; +using System.IO.Pipes; +using System.Threading; + +#if NET451_OR_GREATER || NETCOREAPP +using System.Threading.Tasks; +#endif + +#nullable disable + +namespace Microsoft.Build.BackEnd +{ + internal class BufferedReadStream : Stream + { + private const int BUFFER_SIZE = 1024; + private NamedPipeServerStream _innerStream; + private byte[] _buffer; + + // The number of bytes in the buffer that have been read from the underlying stream but not read by consumers of this stream + private int _currentlyBufferedByteCount; + private int _currentIndexInBuffer; + + public BufferedReadStream(NamedPipeServerStream innerStream) + { + _innerStream = innerStream; + _buffer = new byte[BUFFER_SIZE]; + + _currentlyBufferedByteCount = 0; + } + + public override bool CanRead { get { return _innerStream.CanRead; } } + + public override bool CanSeek { get { return false; } } + + public override bool CanWrite { get { return _innerStream.CanWrite; } } + + public override long Length { get { return _innerStream.Length; } } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + public override void Flush() + { + _innerStream.Flush(); + } + + public override int ReadByte() + { + if (_currentlyBufferedByteCount > 0) + { + int ret = _buffer[_currentIndexInBuffer]; + _currentIndexInBuffer++; + _currentlyBufferedByteCount--; + return ret; + } + else + { + // Let the base class handle it, which will end up calling the Read() method + return base.ReadByte(); + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count > BUFFER_SIZE) + { + // Trying to read more data than the buffer can hold + int alreadyCopied = 0; + if (_currentlyBufferedByteCount > 0) + { + Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, _currentlyBufferedByteCount); + alreadyCopied = _currentlyBufferedByteCount; + _currentIndexInBuffer = 0; + _currentlyBufferedByteCount = 0; + } + int innerReadCount = _innerStream.Read(buffer, offset + alreadyCopied, count - alreadyCopied); + return innerReadCount + alreadyCopied; + } + else if (count <= _currentlyBufferedByteCount) + { + // Enough data buffered to satisfy read request + Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, count); + _currentIndexInBuffer += count; + _currentlyBufferedByteCount -= count; + return count; + } + else + { + // Need to read more data + int alreadyCopied = 0; + if (_currentlyBufferedByteCount > 0) + { + Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, _currentlyBufferedByteCount); + alreadyCopied = _currentlyBufferedByteCount; + _currentIndexInBuffer = 0; + _currentlyBufferedByteCount = 0; + } + + int innerReadCount = _innerStream.Read(_buffer, 0, BUFFER_SIZE); + _currentIndexInBuffer = 0; + _currentlyBufferedByteCount = innerReadCount; + + int remainingCopyCount; + + if (alreadyCopied + innerReadCount >= count) + { + remainingCopyCount = count - alreadyCopied; + } + else + { + remainingCopyCount = innerReadCount; + } + + Array.Copy(_buffer, 0, buffer, offset + alreadyCopied, remainingCopyCount); + _currentIndexInBuffer += remainingCopyCount; + _currentlyBufferedByteCount -= remainingCopyCount; + + return alreadyCopied + remainingCopyCount; + } + } + +#if NET451_OR_GREATER || NETCOREAPP + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + if (count > BUFFER_SIZE) + { + // Trying to read more data than the buffer can hold + int alreadyCopied = CopyToBuffer(buffer, offset); + +#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' + int innerReadCount = await _innerStream.ReadAsync(buffer, offset + alreadyCopied, count - alreadyCopied, cancellationToken); +#pragma warning restore CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' + return innerReadCount + alreadyCopied; + } + else if (count <= _currentlyBufferedByteCount) + { + // Enough data buffered to satisfy read request + Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, count); + _currentIndexInBuffer += count; + _currentlyBufferedByteCount -= count; + return count; + } + else + { + // Need to read more data + int alreadyCopied = CopyToBuffer(buffer, offset); + +#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' + int innerReadCount = await _innerStream.ReadAsync(_buffer, 0, BUFFER_SIZE, cancellationToken); +#pragma warning restore CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' + _currentIndexInBuffer = 0; + _currentlyBufferedByteCount = innerReadCount; + + int remainingCopyCount = alreadyCopied + innerReadCount >= count ? count - alreadyCopied : innerReadCount; + Array.Copy(_buffer, 0, buffer, offset + alreadyCopied, remainingCopyCount); + _currentIndexInBuffer += remainingCopyCount; + _currentlyBufferedByteCount -= remainingCopyCount; + + return alreadyCopied + remainingCopyCount; + } + + int CopyToBuffer(byte[] buffer, int offset) + { + int alreadyCopied = 0; + if (_currentlyBufferedByteCount > 0) + { + Array.Copy(_buffer, _currentIndexInBuffer, buffer, offset, _currentlyBufferedByteCount); + alreadyCopied = _currentlyBufferedByteCount; + _currentIndexInBuffer = 0; + _currentlyBufferedByteCount = 0; + } + + return alreadyCopied; + } + } +#endif + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + _innerStream.Write(buffer, offset, count); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _innerStream.Dispose(); + } + + base.Dispose(disposing); + } + } +} diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index b65967a750a..fe93672dee4 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -24,6 +24,9 @@ #if !CLR2COMPATIBILITY using Microsoft.Build.Shared.Debugging; #endif +#if !FEATURE_APM +using System.Threading.Tasks; +#endif #nullable disable @@ -471,9 +474,25 @@ internal static void WriteIntForHandshake(this PipeStream stream, int value) stream.Write(bytes, 0, bytes.Length); } - internal static void ReadEndOfHandshakeSignal(this PipeStream stream, bool isProvider, int timeout) +#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter + internal static void ReadEndOfHandshakeSignal( + this PipeStream stream, + bool isProvider +#if NETCOREAPP2_1_OR_GREATER + , int timeout +#endif + ) +#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter { - int valueRead = stream.ReadIntForHandshake(byteToAccept: null, timeout); + // Accept only the first byte of the EndOfHandshakeSignal +#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter + int valueRead = stream.ReadIntForHandshake( + byteToAccept: null +#if NETCOREAPP2_1_OR_GREATER + , timeout +#endif + ); +#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter if (valueRead != EndOfHandshakeSignal) { @@ -489,11 +508,17 @@ internal static void ReadEndOfHandshakeSignal(this PipeStream stream, bool isPro } } +#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter /// /// Extension method to read a series of bytes from a stream. /// If specified, leading byte matches one in the supplied array if any, returns rejection byte and throws IOException. /// - internal static int ReadIntForHandshake(this PipeStream stream, byte? byteToAccept, int timeout) + internal static int ReadIntForHandshake(this PipeStream stream, byte? byteToAccept +#if NETCOREAPP2_1_OR_GREATER + , int timeout +#endif + ) +#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter { byte[] bytes = new byte[4]; @@ -563,6 +588,23 @@ internal static int ReadIntForHandshake(this PipeStream stream, byte? byteToAcce } #nullable disable +#if !FEATURE_APM + internal static async ValueTask ReadAsync(Stream stream, byte[] buffer, int bytesToRead) + { + int totalBytesRead = 0; + while (totalBytesRead < bytesToRead) + { + int bytesRead = await stream.ReadAsync(buffer.AsMemory(totalBytesRead, bytesToRead - totalBytesRead), CancellationToken.None); + if (bytesRead == 0) + { + return totalBytesRead; + } + totalBytesRead += bytesRead; + } + return totalBytesRead; + } +#endif + /// /// Given the appropriate information, return the equivalent HandshakeOptions. /// diff --git a/src/Shared/INodePacketFactory.cs b/src/Shared/INodePacketFactory.cs index b0fd06bbbca..c972e0408b5 100644 --- a/src/Shared/INodePacketFactory.cs +++ b/src/Shared/INodePacketFactory.cs @@ -35,11 +35,12 @@ internal interface INodePacketFactory void UnregisterPacketHandler(NodePacketType packetType); /// - /// Takes a serializer and deserializes the packet. + /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. /// + /// The node from which the packet was received. /// The packet type. /// The translator containing the data from which the packet should be reconstructed. - INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator); + void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator); /// /// Routes the specified packet diff --git a/src/Shared/NodeEndpointOutOfProcBase.cs b/src/Shared/NodeEndpointOutOfProcBase.cs index 0c9acac23d3..fbaeb4dbde2 100644 --- a/src/Shared/NodeEndpointOutOfProcBase.cs +++ b/src/Shared/NodeEndpointOutOfProcBase.cs @@ -3,15 +3,27 @@ using System; using System.Diagnostics.CodeAnalysis; -#if TASKHOST +#if CLR2COMPATIBILITY using Microsoft.Build.Shared.Concurrent; #else using System.Collections.Concurrent; -using System.Threading.Tasks; #endif using System.Threading; using Microsoft.Build.Internal; using Microsoft.Build.Shared; +using System.IO.Pipes; +using System.IO; + +#if FEATURE_SECURITY_PERMISSIONS || FEATURE_PIPE_SECURITY +using System.Security.AccessControl; +#endif +#if FEATURE_PIPE_SECURITY && FEATURE_NAMED_PIPE_SECURITY_CONSTRUCTOR +using System.Security.Principal; + +#endif +#if NET451_OR_GREATER || NETCOREAPP +using System.Threading.Tasks; +#endif #nullable disable @@ -25,6 +37,18 @@ internal abstract class NodeEndpointOutOfProcBase : INodeEndpoint { #region Private Data +#if NETCOREAPP2_1_OR_GREATER + /// + /// The amount of time to wait for the client to connect to the host. + /// + private const int ClientConnectTimeout = 60000; +#endif // NETCOREAPP2_1 + + /// + /// The size of the buffers to use for named pipes + /// + private const int PipeBufferSize = 131072; + /// /// The current communication status of the node. /// @@ -33,7 +57,7 @@ internal abstract class NodeEndpointOutOfProcBase : INodeEndpoint /// /// The pipe client used by the nodes. /// - private NodePipeServer _pipeServer; + private NamedPipeServerStream _pipeServer; // The following private data fields are used only when the endpoint is in ASYNCHRONOUS mode. @@ -78,6 +102,21 @@ internal abstract class NodeEndpointOutOfProcBase : INodeEndpoint /// private ConcurrentQueue _packetQueue; + /// + /// Per-node shared read buffer. + /// + private BinaryReaderFactory _sharedReadBuffer; + + /// + /// A way to cache a byte array when writing out packets + /// + private MemoryStream _packetStream; + + /// + /// A binary writer to help write into + /// + private BinaryWriter _binaryWriter; + #endregion #region INodeEndpoint Events @@ -116,7 +155,6 @@ public void Listen(INodePacketFactory factory) ErrorUtilities.VerifyThrow(_status == LinkStatus.Inactive, "Link not inactive. Status is {0}", _status); ErrorUtilities.VerifyThrowArgumentNull(factory, nameof(factory)); _packetFactory = factory; - _pipeServer.RegisterPacketFactory(factory); InitializeAsyncPacketThread(); } @@ -170,9 +208,54 @@ internal void InternalConstruct(string pipeName = null) { _status = LinkStatus.Inactive; _asyncDataMonitor = new object(); + _sharedReadBuffer = InterningBinaryReader.CreateSharedBuffer(); + + _packetStream = new MemoryStream(); + _binaryWriter = new BinaryWriter(_packetStream); pipeName ??= NamedPipeUtil.GetPlatformSpecificPipeName(); - _pipeServer = new NodePipeServer(pipeName, GetHandshake()); + +#if FEATURE_PIPE_SECURITY && FEATURE_NAMED_PIPE_SECURITY_CONSTRUCTOR + SecurityIdentifier identifier = WindowsIdentity.GetCurrent().Owner; + PipeSecurity security = new PipeSecurity(); + + // Restrict access to just this account. We set the owner specifically here, and on the + // pipe client side they will check the owner against this one - they must have identical + // SIDs or the client will reject this server. This is used to avoid attacks where a + // hacked server creates a less restricted pipe in an attempt to lure us into using it and + // then sending build requests to the real pipe client (which is the MSBuild Build Manager.) + PipeAccessRule rule = new PipeAccessRule(identifier, PipeAccessRights.ReadWrite, AccessControlType.Allow); + security.AddAccessRule(rule); + security.SetOwner(identifier); + + _pipeServer = new NamedPipeServerStream( + pipeName, + PipeDirection.InOut, + 1, // Only allow one connection at a time. + PipeTransmissionMode.Byte, + PipeOptions.Asynchronous | PipeOptions.WriteThrough +#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY + | PipeOptions.CurrentUserOnly +#endif + , + PipeBufferSize, // Default input buffer + PipeBufferSize, // Default output buffer + security, + HandleInheritability.None); +#else + _pipeServer = new NamedPipeServerStream( + pipeName, + PipeDirection.InOut, + 1, // Only allow one connection at a time. + PipeTransmissionMode.Byte, + PipeOptions.Asynchronous | PipeOptions.WriteThrough +#if FEATURE_PIPEOPTIONS_CURRENTUSERONLY + | PipeOptions.CurrentUserOnly +#endif + , + PipeBufferSize, // Default input buffer + PipeBufferSize); // Default output buffer +#endif } #endregion @@ -214,7 +297,7 @@ private void InternalDisconnect() ErrorUtilities.VerifyThrow(_packetPump.ManagedThreadId != Thread.CurrentThread.ManagedThreadId, "Can't join on the same thread."); _terminatePacketPump.Set(); _packetPump.Join(); -#if TASKHOST +#if CLR2COMPATIBILITY _terminatePacketPump.Close(); #else _terminatePacketPump.Dispose(); @@ -264,25 +347,172 @@ private void InitializeAsyncPacketThread() /// private void PacketPumpProc() { - NodePipeServer localPipeServer = _pipeServer; + NamedPipeServerStream localPipeServer = _pipeServer; AutoResetEvent localPacketAvailable = _packetAvailable; AutoResetEvent localTerminatePacketPump = _terminatePacketPump; ConcurrentQueue localPacketQueue = _packetQueue; - ChangeLinkStatus(localPipeServer.WaitForConnection()); - if (_status != LinkStatus.Active) + DateTime originalWaitStartTime = DateTime.UtcNow; + bool gotValidConnection = false; + while (!gotValidConnection) { - return; + gotValidConnection = true; + DateTime restartWaitTime = DateTime.UtcNow; + + // We only wait to wait the difference between now and the last original start time, in case we have multiple hosts attempting + // to attach. This prevents each attempt from resetting the timer. + TimeSpan usedWaitTime = restartWaitTime - originalWaitStartTime; + int waitTimeRemaining = Math.Max(0, CommunicationsUtilities.NodeConnectionTimeout - (int)usedWaitTime.TotalMilliseconds); + + try + { + // Wait for a connection +#if FEATURE_APM + IAsyncResult resultForConnection = localPipeServer.BeginWaitForConnection(null, null); + CommunicationsUtilities.Trace("Waiting for connection {0} ms...", waitTimeRemaining); + bool connected = resultForConnection.AsyncWaitHandle.WaitOne(waitTimeRemaining, false); +#else + Task connectionTask = localPipeServer.WaitForConnectionAsync(); + CommunicationsUtilities.Trace("Waiting for connection {0} ms...", waitTimeRemaining); + bool connected = connectionTask.Wait(waitTimeRemaining); +#endif + if (!connected) + { + CommunicationsUtilities.Trace("Connection timed out waiting a host to contact us. Exiting comm thread."); + ChangeLinkStatus(LinkStatus.ConnectionFailed); + return; + } + + CommunicationsUtilities.Trace("Parent started connecting. Reading handshake from parent"); +#if FEATURE_APM + localPipeServer.EndWaitForConnection(resultForConnection); +#endif + + // The handshake protocol is a series of int exchanges. The host sends us a each component, and we + // verify it. Afterwards, the host sends an "End of Handshake" signal, to which we respond in kind. + // Once the handshake is complete, both sides can be assured the other is ready to accept data. + Handshake handshake = GetHandshake(); + try + { + int[] handshakeComponents = handshake.RetrieveHandshakeComponents(); + for (int i = 0; i < handshakeComponents.Length; i++) + { +#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter + int handshakePart = _pipeServer.ReadIntForHandshake( + byteToAccept: i == 0 ? (byte?)CommunicationsUtilities.handshakeVersion : null /* this will disconnect a < 16.8 host; it expects leading 00 or F5 or 06. 0x00 is a wildcard */ +#if NETCOREAPP2_1_OR_GREATER + , ClientConnectTimeout /* wait a long time for the handshake from this side */ +#endif + ); +#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter + + if (handshakePart != handshakeComponents[i]) + { + CommunicationsUtilities.Trace("Handshake failed. Received {0} from host not {1}. Probably the host is a different MSBuild build.", handshakePart, handshakeComponents[i]); + _pipeServer.WriteIntForHandshake(i + 1); + gotValidConnection = false; + break; + } + } + + if (gotValidConnection) + { + // To ensure that our handshake and theirs have the same number of bytes, receive and send a magic number indicating EOS. +#if NETCOREAPP2_1_OR_GREATER + _pipeServer.ReadEndOfHandshakeSignal(false, ClientConnectTimeout); /* wait a long time for the handshake from this side */ +#else + _pipeServer.ReadEndOfHandshakeSignal(false); +#endif + CommunicationsUtilities.Trace("Successfully connected to parent."); + _pipeServer.WriteEndOfHandshakeSignal(); + +#if FEATURE_SECURITY_PERMISSIONS + // We will only talk to a host that was started by the same user as us. Even though the pipe access is set to only allow this user, we want to ensure they + // haven't attempted to change those permissions out from under us. This ensures that the only way they can truly gain access is to be impersonating the + // user we were started by. + WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent(); + WindowsIdentity clientIdentity = null; + localPipeServer.RunAsClient(delegate () { clientIdentity = WindowsIdentity.GetCurrent(true); }); + + if (clientIdentity == null || !String.Equals(clientIdentity.Name, currentIdentity.Name, StringComparison.OrdinalIgnoreCase)) + { + CommunicationsUtilities.Trace("Handshake failed. Host user is {0} but we were created by {1}.", (clientIdentity == null) ? "" : clientIdentity.Name, currentIdentity.Name); + gotValidConnection = false; + continue; + } +#endif + } + } + catch (IOException e) + { + // We will get here when: + // 1. The host (OOP main node) connects to us, it immediately checks for user privileges + // and if they don't match it disconnects immediately leaving us still trying to read the blank handshake + // 2. The host is too old sending us bits we automatically reject in the handshake + // 3. We expected to read the EndOfHandshake signal, but we received something else + CommunicationsUtilities.Trace("Client connection failed but we will wait for another connection. Exception: {0}", e.Message); + + gotValidConnection = false; + } + catch (InvalidOperationException) + { + gotValidConnection = false; + } + + if (!gotValidConnection) + { + if (localPipeServer.IsConnected) + { + localPipeServer.Disconnect(); + } + continue; + } + + ChangeLinkStatus(LinkStatus.Active); + } + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) + { + CommunicationsUtilities.Trace("Client connection failed. Exiting comm thread. {0}", e); + if (localPipeServer.IsConnected) + { + localPipeServer.Disconnect(); + } + + ExceptionHandling.DumpExceptionToFile(e); + ChangeLinkStatus(LinkStatus.Failed); + return; + } } - RunReadLoop(localPipeServer, localPacketQueue, localPacketAvailable, localTerminatePacketPump); + RunReadLoop( + new BufferedReadStream(_pipeServer), + _pipeServer, + localPacketQueue, localPacketAvailable, localTerminatePacketPump); CommunicationsUtilities.Trace("Ending read loop"); - localPipeServer.Disconnect(); + + try + { + if (localPipeServer.IsConnected) + { +#if NET // OperatingSystem.IsWindows() is new in .NET 5.0 + if (OperatingSystem.IsWindows()) +#endif + { + localPipeServer.WaitForPipeDrain(); + } + + localPipeServer.Disconnect(); + } + } + catch (Exception) + { + // We don't really care if Disconnect somehow fails, but it gives us a chance to do the right thing. + } } - private void RunReadLoop(NodePipeServer localPipeServer, + private void RunReadLoop(BufferedReadStream localReadPipe, NamedPipeServerStream localWritePipe, ConcurrentQueue localPacketQueue, AutoResetEvent localPacketAvailable, AutoResetEvent localTerminatePacketPump) { // Ordering of the wait handles is important. The first signalled wait handle in the array @@ -290,11 +520,13 @@ private void RunReadLoop(NodePipeServer localPipeServer, // terminate event triggered so that we cannot get into a situation where packets are being // spammed to the endpoint and it never gets an opportunity to shutdown. CommunicationsUtilities.Trace("Entering read loop."); -#if TASKHOST - Func readPacketFunc = localPipeServer.ReadPacket; - IAsyncResult result = readPacketFunc.BeginInvoke(null, null); + byte[] headerByte = new byte[5]; +#if NET451_OR_GREATER + Task readTask = localReadPipe.ReadAsync(headerByte, 0, headerByte.Length, CancellationToken.None); +#elif NETCOREAPP + Task readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length).AsTask(); #else - Task readTask = localPipeServer.ReadPacketAsync(); + IAsyncResult result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); #endif // Ordering is important. We want packetAvailable to supercede terminate otherwise we will not properly wait for all @@ -318,25 +550,36 @@ private void RunReadLoop(NodePipeServer localPipeServer, { case 0: { - INodePacket packet = null; - + int bytesRead = 0; try { -#if TASKHOST - packet = readPacketFunc.EndInvoke(result); +#if NET451_OR_GREATER || NETCOREAPP + bytesRead = readTask.Result; #else - packet = readTask.GetAwaiter().GetResult(); + bytesRead = localReadPipe.EndRead(result); #endif - if (packet.Type == NodePacketType.NodeShutdown) + } + catch (Exception e) + { + // Lost communications. Abort (but allow node reuse) + CommunicationsUtilities.Trace("Exception reading from server. {0}", e); + ExceptionHandling.DumpExceptionToFile(e); + ChangeLinkStatus(LinkStatus.Inactive); + exitLoop = true; + break; + } + + if (bytesRead != headerByte.Length) + { + // Incomplete read. Abort. + if (bytesRead == 0) { if (_isClientDisconnecting) { - // Lost communications. Abort (but allow node reuse). + CommunicationsUtilities.Trace("Parent disconnected gracefully."); // Do not change link status to failed as this could make node think connection has failed // and recycle node, while this is perfectly expected and handled race condition // (both client and node is about to close pipe and client can be faster). - CommunicationsUtilities.Trace("Parent disconnected gracefully."); - ChangeLinkStatus(LinkStatus.Inactive); } else { @@ -346,35 +589,43 @@ private void RunReadLoop(NodePipeServer localPipeServer, } else { - _packetFactory.RoutePacket(0, packet); + CommunicationsUtilities.Trace("Incomplete header read from server. {0} of {1} bytes read", bytesRead, headerByte.Length); + ChangeLinkStatus(LinkStatus.Failed); } + + exitLoop = true; + break; + } + + NodePacketType packetType = (NodePacketType)headerByte[0]; + + try + { + _packetFactory.DeserializeAndRoutePacket(0, packetType, BinaryTranslator.GetReadTranslator(localReadPipe, _sharedReadBuffer)); } catch (Exception e) { - if (packet == null) - { - CommunicationsUtilities.Trace("Exception while reading packet from server: {0}", e); - } - else - { - CommunicationsUtilities.Trace("Exception while deserializing or handling packet {0}: {1}", packet.Type, e); - } - + // Error while deserializing or handling packet. Abort. + CommunicationsUtilities.Trace("Exception while deserializing packet {0}: {1}", packetType, e); ExceptionHandling.DumpExceptionToFile(e); ChangeLinkStatus(LinkStatus.Failed); + exitLoop = true; + break; } - exitLoop = _status != LinkStatus.Active; - if (!exitLoop) - { -#if TASKHOST - result = readPacketFunc.BeginInvoke(null, null); - handles[0] = result.AsyncWaitHandle; +#if NET451_OR_GREATER + readTask = localReadPipe.ReadAsync(headerByte, 0, headerByte.Length, CancellationToken.None); +#elif NETCOREAPP + readTask = CommunicationsUtilities.ReadAsync(localReadPipe, headerByte, headerByte.Length).AsTask(); #else - readTask = localPipeServer.ReadPacketAsync(); - handles[0] = ((IAsyncResult)readTask).AsyncWaitHandle; + result = localReadPipe.BeginRead(headerByte, 0, headerByte.Length, null, null); +#endif + +#if NET451_OR_GREATER || NETCOREAPP + handles[0] = ((IAsyncResult)readTask).AsyncWaitHandle; +#else + handles[0] = result.AsyncWaitHandle; #endif - } } break; @@ -384,9 +635,29 @@ private void RunReadLoop(NodePipeServer localPipeServer, try { // Write out all the queued packets. - while (localPacketQueue.TryDequeue(out INodePacket packet)) + INodePacket packet; + while (localPacketQueue.TryDequeue(out packet)) { - localPipeServer.WritePacket(packet); + var packetStream = _packetStream; + packetStream.SetLength(0); + + ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(packetStream); + + packetStream.WriteByte((byte)packet.Type); + + // Pad for packet length + _binaryWriter.Write(0); + + // Reset the position in the write buffer. + packet.Translate(writeTranslator); + + int packetStreamLength = (int)packetStream.Position; + + // Now write in the actual packet length + packetStream.Position = 1; + _binaryWriter.Write(packetStreamLength - 5); + + localWritePipe.Write(packetStream.GetBuffer(), 0, packetStreamLength); } } catch (Exception e) @@ -416,8 +687,8 @@ private void RunReadLoop(NodePipeServer localPipeServer, while (!exitLoop); } - #endregion +#endregion - #endregion +#endregion } } diff --git a/src/Shared/NodePacketFactory.cs b/src/Shared/NodePacketFactory.cs index 51cbee08655..214ddfa20f9 100644 --- a/src/Shared/NodePacketFactory.cs +++ b/src/Shared/NodePacketFactory.cs @@ -45,9 +45,9 @@ public void UnregisterPacketHandler(NodePacketType packetType) } /// - /// Creates a packet with data from a binary stream. + /// Creates and routes a packet with data from a binary stream. /// - public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator) { // PERF: Not using VerifyThrow to avoid boxing of packetType in the non-error case if (!_packetFactories.TryGetValue(packetType, out PacketFactoryRecord record)) @@ -55,7 +55,7 @@ public INodePacket DeserializePacket(NodePacketType packetType, ITranslator tran ErrorUtilities.ThrowInternalError("No packet handler for type {0}", packetType); } - return record.DeserializePacket(translator); + record.DeserializeAndRoutePacket(nodeId, translator); } /// @@ -63,12 +63,7 @@ public INodePacket DeserializePacket(NodePacketType packetType, ITranslator tran /// public void RoutePacket(int nodeId, INodePacket packet) { - // PERF: Not using VerifyThrow to avoid boxing of packetType in the non-error case - if (!_packetFactories.TryGetValue(packet.Type, out PacketFactoryRecord record)) - { - ErrorUtilities.ThrowInternalError("No packet handler for type {0}", packet.Type); - } - + PacketFactoryRecord record = _packetFactories[packet.Type]; record.RoutePacket(nodeId, packet); } @@ -99,9 +94,13 @@ public PacketFactoryRecord(INodePacketHandler handler, NodePacketFactoryMethod f } /// - /// Creates a packet from a binary stream. + /// Creates a packet from a binary stream and sends it to the registered handler. /// - public INodePacket DeserializePacket(ITranslator translator) => _factoryMethod(translator); + public void DeserializeAndRoutePacket(int nodeId, ITranslator translator) + { + INodePacket packet = _factoryMethod(translator); + RoutePacket(nodeId, packet); + } /// /// Routes the packet to the correct destination. From f1cb73f73ae123dfb18f35bd8e6df3adef7c9269 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:58:20 +0200 Subject: [PATCH 056/106] Update dependencies from https://github.com/nuget/nuget.client build 6.14.0.97 (#11645) NuGet.Build.Tasks From Version 6.14.0-preview.1.89 -> To Version 6.14.0-preview.1.97 Co-authored-by: dotnet-maestro[bot] Co-authored-by: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com> --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index d9ade9bf3bb..6e1e643d391 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -137,9 +137,9 @@ https://github.com/dotnet/arcade 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee - + https://github.com/nuget/nuget.client - 13550619f90e73a1f8b4b5159c6d7f268c9756d0 + 8f6362aea4972acab1454de411cfe835619e4e41 https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index 4ee8c37b3e0..1e4ef0a7a5f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -74,7 +74,7 @@ 4.2.0-1.22102.8 9.0.0-beta.25164.2 4.14.0-3.25179.1 - 6.14.0-preview.1.89 + 6.14.0-preview.1.97 9.0.200-preview.0.24603.3 From 10993ad25e431e6138600a2ae74c89fc1237bbd8 Mon Sep 17 00:00:00 2001 From: Ilia Shuliatikov Date: Tue, 1 Apr 2025 14:06:49 +0300 Subject: [PATCH 057/106] Implement ExecCliBuild build check to warn if the Exec task is used to build a project (#11523) --- documentation/specs/BuildCheck/Codes.md | 8 + .../BuildCheck/Checks/ExecCliBuildCheck.cs | 150 ++++++++++++++++++ .../BuildCheckManagerProvider.cs | 1 + src/Build/Resources/Strings.resx | 6 + src/Build/Resources/xlf/Strings.cs.xlf | 10 ++ src/Build/Resources/xlf/Strings.de.xlf | 10 ++ src/Build/Resources/xlf/Strings.es.xlf | 10 ++ src/Build/Resources/xlf/Strings.fr.xlf | 10 ++ src/Build/Resources/xlf/Strings.it.xlf | 10 ++ src/Build/Resources/xlf/Strings.ja.xlf | 10 ++ src/Build/Resources/xlf/Strings.ko.xlf | 10 ++ src/Build/Resources/xlf/Strings.pl.xlf | 10 ++ src/Build/Resources/xlf/Strings.pt-BR.xlf | 10 ++ src/Build/Resources/xlf/Strings.ru.xlf | 10 ++ src/Build/Resources/xlf/Strings.tr.xlf | 10 ++ src/Build/Resources/xlf/Strings.zh-Hans.xlf | 10 ++ src/Build/Resources/xlf/Strings.zh-Hant.xlf | 10 ++ .../ExecCliBuildCheck_Tests.cs | 104 ++++++++++++ 18 files changed, 399 insertions(+) create mode 100644 src/Build/BuildCheck/Checks/ExecCliBuildCheck.cs create mode 100644 src/BuildCheck.UnitTests/ExecCliBuildCheck_Tests.cs diff --git a/documentation/specs/BuildCheck/Codes.md b/documentation/specs/BuildCheck/Codes.md index e9a4c04a20f..892e17a178a 100644 --- a/documentation/specs/BuildCheck/Codes.md +++ b/documentation/specs/BuildCheck/Codes.md @@ -12,6 +12,7 @@ Report codes are chosen to conform to suggested guidelines. Those guidelines are | [BC0106](#bc0106---copytooutputdirectoryalways-should-be-avoided) | Warning | N/A | 9.0.200 | CopyToOutputDirectory='Always' should be avoided. | | [BC0107](#bc0107---targetframework-and-targetframeworks-specified-together) | Warning | N/A | 9.0.200 | TargetFramework and TargetFrameworks specified together. | | [BC0108](#bc0108---targetframework-or-targetframeworks-specified-in-non-sdk-style-project) | Warning | N/A | 9.0.300 | TargetFramework or TargetFrameworks specified in non-SDK style project. | +| [BC0109](#bc0109---building-using-the-exec-task) | Warning | N/A | 9.0.300 | Building using the Exec task. | | [BC0201](#bc0201---usage-of-undefined-property) | Warning | Project | 9.0.100 | Usage of undefined property. | | [BC0202](#bc0202---property-first-declared-after-it-was-used) | Warning | Project | 9.0.100 | Property first declared after it was used. | | [BC0203](#bc0203----property-declared-but-never-used) | None | Project | 9.0.100 | Property declared but never used. | @@ -137,6 +138,13 @@ dotnet build my-multi-target.csproj /p:TargetFramework=net9.0 Make sure the Target Framework is specified appropriately for your project. + +## BC0109 - Building using the Exec task. + +"The 'Exec' task should not be used to build projects." + +Building projects using the dotnet/msbuild/nuget CLI in the `Exec` task is not recommended, as it spawns a separate build process that the MSBuild engine cannot track. Please use the [MSBuild task](https://learn.microsoft.com/visualstudio/msbuild/msbuild-task) instead. + ## BC0201 - Usage of undefined property. diff --git a/src/Build/BuildCheck/Checks/ExecCliBuildCheck.cs b/src/Build/BuildCheck/Checks/ExecCliBuildCheck.cs new file mode 100644 index 00000000000..868827b24cc --- /dev/null +++ b/src/Build/BuildCheck/Checks/ExecCliBuildCheck.cs @@ -0,0 +1,150 @@ +// 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; +#if !FEATURE_MSIOREDIST +using System.IO; +#endif +using Microsoft.Build.Shared; + +#if FEATURE_MSIOREDIST +using Path = Microsoft.IO.Path; +#endif + +namespace Microsoft.Build.Experimental.BuildCheck.Checks; + +internal sealed class ExecCliBuildCheck : Check +{ + public static CheckRule SupportedRule = new CheckRule( + "BC0109", + "ExecCliBuild", + ResourceUtilities.GetResourceString("BuildCheck_BC0109_Title")!, + ResourceUtilities.GetResourceString("BuildCheck_BC0109_MessageFmt")!, + new CheckConfiguration() { Severity = CheckResultSeverity.Warning }); + + private const string ExecTaskName = "Exec"; + private const string CommandParameterName = "Command"; + + private static readonly char[] s_knownCommandSeparators = ['&', ';', '|']; + + private static readonly string[] s_knownBuildCommands = + [ + "dotnet build", + "dotnet clean", + "dotnet msbuild", + "dotnet restore", + "dotnet publish", + "dotnet pack", + "dotnet vstest", + "nuget restore", + "msbuild", + "dotnet test", + "dotnet run", + ]; + + public override string FriendlyName => "MSBuild.ExecCliBuildCheck"; + + internal override bool IsBuiltIn => true; + + public override IReadOnlyList SupportedRules { get; } = [SupportedRule]; + + public override void Initialize(ConfigurationContext configurationContext) + { + /* This is it - no custom configuration */ + } + + public override void RegisterActions(IBuildCheckRegistrationContext registrationContext) + { + registrationContext.RegisterTaskInvocationAction(TaskInvocationAction); + } + + private static void TaskInvocationAction(BuildCheckDataContext context) + { + if (context.Data.TaskName == ExecTaskName + && context.Data.Parameters.TryGetValue(CommandParameterName, out TaskInvocationCheckData.TaskParameter? commandArgument)) + { + var execCommandValue = commandArgument.Value?.ToString() ?? string.Empty; + + var commandSpan = execCommandValue.AsSpan(); + int start = 0; + + while (start < commandSpan.Length) + { + var nextSeparatorIndex = commandSpan.Slice(start, commandSpan.Length - start).IndexOfAny(s_knownCommandSeparators); + + if (nextSeparatorIndex == -1) + { + if (TryGetMatchingKnownBuildCommand(commandSpan.Slice(start), out var knownBuildCommand)) + { + context.ReportResult(BuildCheckResult.CreateBuiltIn( + SupportedRule, + context.Data.TaskInvocationLocation, + context.Data.TaskName, + Path.GetFileName(context.Data.ProjectFilePath), + GetToolName(knownBuildCommand))); + } + + break; + } + else + { + var command = commandSpan.Slice(start, nextSeparatorIndex); + + if (TryGetMatchingKnownBuildCommand(command, out var knownBuildCommand)) + { + context.ReportResult(BuildCheckResult.CreateBuiltIn( + SupportedRule, + context.Data.TaskInvocationLocation, + context.Data.TaskName, + Path.GetFileName(context.Data.ProjectFilePath), + GetToolName(knownBuildCommand))); + + break; + } + + start += nextSeparatorIndex + 1; + } + } + } + } + + private static bool TryGetMatchingKnownBuildCommand(ReadOnlySpan command, out string knownBuildCommand) + { + const int maxStackLimit = 1024; + + Span normalizedBuildCommand = command.Length <= maxStackLimit ? stackalloc char[command.Length] : new char[command.Length]; + int normalizedCommandIndex = 0; + + foreach (var c in command) + { + if (char.IsWhiteSpace(c) && (normalizedCommandIndex == 0 || char.IsWhiteSpace(normalizedBuildCommand[normalizedCommandIndex - 1]))) + { + continue; + } + + normalizedBuildCommand[normalizedCommandIndex++] = c; + } + + foreach (var buildCommand in s_knownBuildCommands) + { + if (normalizedBuildCommand.StartsWith(buildCommand.AsSpan())) + { + knownBuildCommand = buildCommand; + return true; + } + } + + knownBuildCommand = string.Empty; + return false; + } + + private static string GetToolName(string knownBuildCommand) + { + int nextSpaceIndex = knownBuildCommand.IndexOf(' '); + + return nextSpaceIndex == -1 + ? knownBuildCommand + : knownBuildCommand.AsSpan().Slice(0, nextSpaceIndex).ToString(); + } +} diff --git a/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs b/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs index 7100095f1b4..89998bad255 100644 --- a/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs +++ b/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs @@ -149,6 +149,7 @@ internal readonly record struct BuiltInCheckFactory( new BuiltInCheckFactory([PreferProjectReferenceCheck.SupportedRule.Id], PreferProjectReferenceCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), new BuiltInCheckFactory([CopyAlwaysCheck.SupportedRule.Id], CopyAlwaysCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), new BuiltInCheckFactory([DoubleWritesCheck.SupportedRule.Id], DoubleWritesCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), + new BuiltInCheckFactory([ExecCliBuildCheck.SupportedRule.Id], ExecCliBuildCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), new BuiltInCheckFactory([NoEnvironmentVariablePropertyCheck.SupportedRule.Id], NoEnvironmentVariablePropertyCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), new BuiltInCheckFactory([EmbeddedResourceCheck.SupportedRule.Id], EmbeddedResourceCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), new BuiltInCheckFactory([TargetFrameworkConfusionCheck.SupportedRule.Id], TargetFrameworkConfusionCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), diff --git a/src/Build/Resources/Strings.resx b/src/Build/Resources/Strings.resx index 128cf7283c0..f36cdbcde75 100644 --- a/src/Build/Resources/Strings.resx +++ b/src/Build/Resources/Strings.resx @@ -2206,6 +2206,12 @@ Utilization: {0} Average Utilization: {1:###.0} Project {0} specifies 'TargetFramework(s)' property '{1}', which does not use the .NET SDK. Those properties are not understood by projects that import C# targets directly. Terms in quotes are not to be translated. + + The 'Exec' task should not be used to build a project. + + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + A property that is accessed should be declared first. diff --git a/src/Build/Resources/xlf/Strings.cs.xlf b/src/Build/Resources/xlf/Strings.cs.xlf index 5d95d9b15e5..a4ed911d08e 100644 --- a/src/Build/Resources/xlf/Strings.cs.xlf +++ b/src/Build/Resources/xlf/Strings.cs.xlf @@ -221,6 +221,16 @@ Vlastnosti TargetFramework a TargetFrameworks se nedodržují a neměly by se zadává v projektech, které nepoužívají sadu .NET SDK. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. K vlastnosti: {0} bylo přistupováno, ale nebyla nikdy inicializována. diff --git a/src/Build/Resources/xlf/Strings.de.xlf b/src/Build/Resources/xlf/Strings.de.xlf index 2cc67095e98..7fe8564cd54 100644 --- a/src/Build/Resources/xlf/Strings.de.xlf +++ b/src/Build/Resources/xlf/Strings.de.xlf @@ -221,6 +221,16 @@ Die Eigenschaften "TargetFramework" und "TargetFrameworks" werden nicht berücksichtigt und sollten nicht in Projekten angegeben werden, die nicht das .NET SDK verwenden. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. Auf die Eigenschaft „{0}“ wurde zugegriffen, sie wurde jedoch nie initialisiert. diff --git a/src/Build/Resources/xlf/Strings.es.xlf b/src/Build/Resources/xlf/Strings.es.xlf index 7a8f4854d66..d6b69ebe981 100644 --- a/src/Build/Resources/xlf/Strings.es.xlf +++ b/src/Build/Resources/xlf/Strings.es.xlf @@ -221,6 +221,16 @@ Las propiedades "TargetFramework" y "TargetFrameworks" no se respetan y no deben especificarse en proyectos que no usen el SDK de .NET. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. Propiedad: se obtuvo acceso a "{0}", pero nunca se inicializó. diff --git a/src/Build/Resources/xlf/Strings.fr.xlf b/src/Build/Resources/xlf/Strings.fr.xlf index aafdf22e15f..476f81982a5 100644 --- a/src/Build/Resources/xlf/Strings.fr.xlf +++ b/src/Build/Resources/xlf/Strings.fr.xlf @@ -221,6 +221,16 @@ Les propriétés 'TargetFramework' et 'TargetFrameworks' ne sont pas respectées et ne doivent pas être spécifiées dans les projets qui n’utilisent pas le SDK .NET. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. Propriété : « {0} » a été consultée, mais elle n'a jamais été initialisée. diff --git a/src/Build/Resources/xlf/Strings.it.xlf b/src/Build/Resources/xlf/Strings.it.xlf index 5e38412a12f..88de2f29361 100644 --- a/src/Build/Resources/xlf/Strings.it.xlf +++ b/src/Build/Resources/xlf/Strings.it.xlf @@ -221,6 +221,16 @@ Le proprietà 'TargetFramework' e 'TargetFrameworks' non vengono rispettate e non devono essere specificate nei progetti che non usano .NET SDK. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. È stato eseguito l'accesso alla proprietà '{0}', ma non è mai stata inizializzata. diff --git a/src/Build/Resources/xlf/Strings.ja.xlf b/src/Build/Resources/xlf/Strings.ja.xlf index dda0d13a014..ac24fa3a8cc 100644 --- a/src/Build/Resources/xlf/Strings.ja.xlf +++ b/src/Build/Resources/xlf/Strings.ja.xlf @@ -221,6 +221,16 @@ 'TargetFramework' プロパティと 'TargetFrameworks' プロパティは優先されないため、.NET SDK を使用しないプロジェクトでは指定しないでください。 Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. プロパティ: '{0}' にアクセスしましたが、初期化されませんでした。 diff --git a/src/Build/Resources/xlf/Strings.ko.xlf b/src/Build/Resources/xlf/Strings.ko.xlf index 03f95d108ff..604e05765c3 100644 --- a/src/Build/Resources/xlf/Strings.ko.xlf +++ b/src/Build/Resources/xlf/Strings.ko.xlf @@ -221,6 +221,16 @@ 'TargetFramework' 및 'TargetFrameworks' 속성은 사용되지 않으며 .NET SDK를 사용하지 않는 프로젝트에서 지정해서는 안 됩니다. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. 속성: '{0}'에 액세스했지만 초기화되지 않았습니다. diff --git a/src/Build/Resources/xlf/Strings.pl.xlf b/src/Build/Resources/xlf/Strings.pl.xlf index b41218bfd21..c4b1e4e7950 100644 --- a/src/Build/Resources/xlf/Strings.pl.xlf +++ b/src/Build/Resources/xlf/Strings.pl.xlf @@ -221,6 +221,16 @@ Właściwości "TargetFramework" i "TargetFrameworks" nie są respektowane i nie należy ich określać w projektach, w których nie jest używany zestaw .NET SDK. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. Właściwość: uzyskano dostęp do „{0}”, ale nigdy nie dokonano inicjacji. diff --git a/src/Build/Resources/xlf/Strings.pt-BR.xlf b/src/Build/Resources/xlf/Strings.pt-BR.xlf index dd2bf9c6cf7..dac69426c0d 100644 --- a/src/Build/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Build/Resources/xlf/Strings.pt-BR.xlf @@ -221,6 +221,16 @@ As propriedades 'TargetFramework' e 'TargetFrameworks' não são respeitadas e não devem ser especificadas em projetos que não usam o SDK do .NET. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. Propriedade: "{0}" foi acessada, mas nunca foi inicializada. diff --git a/src/Build/Resources/xlf/Strings.ru.xlf b/src/Build/Resources/xlf/Strings.ru.xlf index 7d7883cf5c6..7761ba40014 100644 --- a/src/Build/Resources/xlf/Strings.ru.xlf +++ b/src/Build/Resources/xlf/Strings.ru.xlf @@ -221,6 +221,16 @@ Свойства TargetFramework и TargetFrameworks не учитываются и не должны указываться в проектах, не использующих пакет SDK для .NET. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. Свойство: к "{0}" получен доступ, но он не инициализирован. diff --git a/src/Build/Resources/xlf/Strings.tr.xlf b/src/Build/Resources/xlf/Strings.tr.xlf index 8bc03e8a1bc..2e698dd6995 100644 --- a/src/Build/Resources/xlf/Strings.tr.xlf +++ b/src/Build/Resources/xlf/Strings.tr.xlf @@ -221,6 +221,16 @@ 'TargetFramework' ve 'TargetFrameworks' özellikleri dikkate alınmaz ve .NET SDK kullanmayan projelerde belirtilmeli. Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. '{0}' özelliğine erişildi, ancak hiç başlatılmadı. diff --git a/src/Build/Resources/xlf/Strings.zh-Hans.xlf b/src/Build/Resources/xlf/Strings.zh-Hans.xlf index dcc11e8f51b..f67cf71b15f 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hans.xlf @@ -221,6 +221,16 @@ 不考虑 “TargetFramework” 和 “TargetFrameworks” 属性,不应在不使用 .NET SDK 的项目中指定这些属性。 Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. 已访问属性“{0}”,但从未将其初始化过。 diff --git a/src/Build/Resources/xlf/Strings.zh-Hant.xlf b/src/Build/Resources/xlf/Strings.zh-Hant.xlf index beb75eae504..74f2f895537 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hant.xlf @@ -221,6 +221,16 @@ 未遵守 『TargetFramework』 和 『TargetFrameworks』 屬性,且不應在未使用 .NET SDK 的專案中指定。 Terms in quotes are not to be translated. + + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + + + + The 'Exec' task should not be used to build a project. + The 'Exec' task should not be used to build a project. + + Property: '{0}' was accessed, but it was never initialized. 已存取屬性: '{0}',但從未初始化。 diff --git a/src/BuildCheck.UnitTests/ExecCliBuildCheck_Tests.cs b/src/BuildCheck.UnitTests/ExecCliBuildCheck_Tests.cs new file mode 100644 index 00000000000..edbb0e6bdc9 --- /dev/null +++ b/src/BuildCheck.UnitTests/ExecCliBuildCheck_Tests.cs @@ -0,0 +1,104 @@ +// 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.Generic; +using System.Runtime.InteropServices; +using Microsoft.Build.Experimental.BuildCheck; +using Microsoft.Build.Experimental.BuildCheck.Checks; +using Shouldly; +using Xunit; + +namespace Microsoft.Build.BuildCheck.UnitTests +{ + public sealed class ExecCliBuildCheck_Tests + { + private const int MaxStackSizeWindows = 1024 * 1024; // 1 MB + private const int MaxStackSizeLinux = 1024 * 1024 * 8; // 8 MB + + private readonly ExecCliBuildCheck _check; + + private readonly MockBuildCheckRegistrationContext _registrationContext; + + public static TheoryData BuildCommandTestData => new TheoryData( + "dotnet build", + "dotnet build&dotnet build", + "dotnet build", + "dotnet clean", + "dotnet msbuild", + "dotnet restore", + "dotnet publish", + "dotnet pack", + "dotnet test", + "dotnet vstest", + "dotnet build -p:Configuration=Release", + "dotnet build /t:Restore;Clean", + "dotnet build&some command", + "some command&dotnet build&some other command", + "some command&dotnet build", + "some command&dotnet build&some other command", + "msbuild", + "msbuild /t:Build", + "msbuild --t:Restore;Clean", + "nuget restore", + "dotnet run --project project.SLN", + "dotnet run project.csproj", + "dotnet run project.proj", + "dotnet run", + string.Join(";", new string('a', 1025), "dotnet build", new string('a', 1025)), + string.Join(";", new string('a', RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? MaxStackSizeWindows * 2 : MaxStackSizeLinux * 2), "dotnet build")); + + public static TheoryData NonBuildCommandTestData => new TheoryData( + "dotnet help", + "where dotnet", + "where msbuild", + "where nuget", + "dotnet bin/net472/project.dll", + string.Empty, + null, + new string('a', RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? MaxStackSizeWindows * 2 : MaxStackSizeLinux * 2)); + + public ExecCliBuildCheck_Tests() + { + _check = new ExecCliBuildCheck(); + _registrationContext = new MockBuildCheckRegistrationContext(); + _check.RegisterActions(_registrationContext); + } + + [Theory] + [MemberData(nameof(BuildCommandTestData))] + public void ExecTask_WithCommandExecutingBuild_ShouldShowWarning(string? command) + { + _registrationContext.TriggerTaskInvocationAction(MakeTaskInvocationData("Exec", new Dictionary + { + { "Command", new TaskInvocationCheckData.TaskParameter(command, IsOutput: false) }, + })); + + _registrationContext.Results.Count.ShouldBe(1); + _registrationContext.Results[0].CheckRule.Id.ShouldBe("BC0109"); + } + + [Theory] + [MemberData(nameof(NonBuildCommandTestData))] + public void ExecTask_WithCommandNotExecutingBuild_ShouldNotShowWarning(string? command) + { + _registrationContext.TriggerTaskInvocationAction(MakeTaskInvocationData("Exec", new Dictionary + { + { "Command", new TaskInvocationCheckData.TaskParameter(command, IsOutput: false) }, + })); + + _registrationContext.Results.Count.ShouldBe(0); + } + + private TaskInvocationCheckData MakeTaskInvocationData(string taskName, Dictionary parameters) + { + string projectFile = Framework.NativeMethods.IsWindows ? @"C:\fake\project.proj" : "/fake/project.proj"; + return new TaskInvocationCheckData( + projectFile, + null, + Construction.ElementLocation.EmptyLocation, + taskName, + projectFile, + parameters); + } + } +} From 0bf0b50e8ec65593ea164175c41f308f7f549398 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Tue, 1 Apr 2025 15:08:12 +0200 Subject: [PATCH 058/106] Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11312841 (#11653) --- src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf | 26 +++++++++---------- src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf | 18 ++++++------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf index 24c3f4dcb45..0968a8a75fb 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf @@ -841,9 +841,9 @@ WarningsOnly -- 仅显示警告。 NoItemAndPropertyList -- 在开始生成每个项目时不显示 项和属性的列表。 - ShowCommandLine -- 显示 TaskCommandLineEvent 消息 + ShowCommandLine -- 显示 TaskCommandLineEvent 消息 ShowTimestamp -- 将时间戳作为所有消息的前缀 - 显示。 + 显示。 ShowEventId -- 显示已开始事件、已完成事件和消息 的事件 ID。 ForceNoAlign -- 不将文本与控制台缓冲区的大小 @@ -900,10 +900,10 @@ Example: -validate:MyExtendedBuildSchema.xsd - -validate 依据默认架构验证项目。(缩写: + -validate 依据默认架构验证项目。(缩写: -val) - -validate:<schema> 依据指定的架构验证项目。(缩写: + -validate:<schema> 依据指定的架构验证项目。(缩写: -val) 示例: -validate:MyExtendedBuildSchema.xsd @@ -1081,7 +1081,7 @@ -toolsversion:<version> 要在生成过程中使用的 MSBuild 工具集 (任务、目标等)的版本。此版本将重写 - 各个项目指定的版本。(缩写: + 各个项目指定的版本。(缩写: -tv) 示例: -toolsversion:3.5 @@ -1137,17 +1137,17 @@ template and append the node id to this fileName to create a log file for each node. - -distributedFileLogger + -distributedFileLogger 将生成输出记录到多个日志文件,每个 MSBuild 节点 一个日志文件。这些文件的初始位置为 当前目录。默认情况下,这些文件名为 “MSBuild<nodeid>.log”。可通过添加 - “-fileLoggerParameters”开关来指定 + “-fileLoggerParameters”开关来指定 这些文件的位置和 fileLogger 的其他参数。 如果日志文件名是通过 fileLoggerParameters 开关设置的,分布式记录器将使用 fileName 作为 - 模板并将节点 ID 附加到此 fileName + 模板并将节点 ID 附加到此 fileName 以便为每个节点创建一个日志文件。 @@ -1189,12 +1189,12 @@ -flp1:warningsonly;logfile=msbuild.wrn -flp2:errorsonly;logfile=msbuild.err - -fileloggerparameters[n]:<parameters> + -fileloggerparameters[n]:<parameters> 为文件记录器提供任何额外的参数。 存在此开关意味着 存在对应的 -filelogger[n] 开关。 “n”(如果存在)可以为 1-9 的数字。 - 任何分布式文件记录器也可以使用 + 任何分布式文件记录器也可以使用 -fileloggerparameters,具体可参阅 -distributedFileLogger 的说明。 (缩写: -flp[n]) 为控制台记录器列出的相同参数 @@ -1214,8 +1214,8 @@ -fileLoggerParameters:LogFile=MyLog.log;Append; Verbosity=diagnostic;Encoding=UTF-8 - -flp:Summary;Verbosity=minimal;LogFile=msbuild.sum - -flp1:warningsonly;logfile=msbuild.wrn + -flp:Summary;Verbosity=minimal;LogFile=msbuild.sum + -flp1:warningsonly;logfile=msbuild.wrn -flp2:errorsonly;logfile=msbuild.err @@ -2200,4 +2200,4 @@ - + \ No newline at end of file diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf index 58b406ea531..b2b8fb45067 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf @@ -214,7 +214,7 @@ 終端機記錄器的參數。(簡短形式: -tlp) 可用的參數。 default -- 指定終端機記錄器的預設值。 - 其需要下列其中一值: + 其需要下列其中一值: 。 - 'on'、'true' 會強制使用 TerminalLogger,即使 其之後可能會停用。 @@ -227,7 +227,7 @@ -verbosity showCommandLine -- 顯示 TaskCommandLineEvent 訊息 - 範例: + 範例: -tlp:default=auto;verbosity=diag;shownCommandLine @@ -246,7 +246,7 @@ -getResultOutputFile:file 將輸出從 get* 重新導向至檔案。 - 範例: + 範例: -getProperty:Bar -getResultOutputFile:Biz.txt 這會將屬性列的值寫入 Biz.txt。 @@ -263,7 +263,7 @@ -check 在建置期間啟用 BuildChecks。 - BuildCheck 會啟用評估規則以確保組建的 + BuildCheck 會啟用評估規則以確保組建的 屬性。如需詳細資訊,請參閱 aka.ms/buildcheck @@ -446,8 +446,8 @@ -isolateProjects[:True|MessageUponIsolationViolation|False] 導致 MSBuild 在隔離中建置每個專案。 - 設定為 "MessageUponIsolationViolation" - (或其簡短形式 "Message") 時,如果提供 + 設定為 "MessageUponIsolationViolation" + (或其簡短形式 "Message") 時,如果提供 -outputResultsCache 切換,則只會序列化來自 頂層目標的結果。這是為了降低相依性專案上, 由於其相依性位於快取目標上 (其副作用 @@ -1081,8 +1081,8 @@ -toolsversion:<版本> 建置期間所使用的 MSBuild 工具組 (工作、目標等) - 版本。此版本將會覆寫 - 個別專案所指定的版本。(簡短形式: + 版本。此版本將會覆寫 + 個別專案所指定的版本。(簡短形式: -tv) 範例: -toolsVersion:3.5 @@ -2201,4 +2201,4 @@ - + \ No newline at end of file From def23c98acb87bf46da45c29f0d88b81c833f100 Mon Sep 17 00:00:00 2001 From: Christian Castaneda Date: Mon, 31 Mar 2025 22:48:01 -0700 Subject: [PATCH 059/106] Add separate DeserializePacket() to INodePacketFactory --- .../BackEnd/NodeEndpointInProc_Tests.cs | 5 +++ .../BackEnd/Client/MSBuildClientPacketPump.cs | 10 +++++ .../Components/Communications/NodeManager.cs | 10 +++++ .../Communications/NodeProviderInProc.cs | 10 +++++ .../NodeProviderOutOfProcTaskHost.cs | 10 +++++ .../Communications/TaskHostNodeManager.cs | 10 +++++ src/Build/BackEnd/Node/InProcNode.cs | 10 +++++ src/Build/BackEnd/Node/OutOfProcNode.cs | 10 +++++ src/Build/BackEnd/Node/OutOfProcServerNode.cs | 10 +++++ .../Instance/TaskFactories/TaskHostTask.cs | 10 +++++ src/MSBuild/OutOfProcTaskHostNode.cs | 10 +++++ src/Shared/INodePacketFactory.cs | 9 +++- src/Shared/NodePacketFactory.cs | 41 ++++++++++++------- src/Shared/NodePipeBase.cs | 2 +- src/Shared/NodePipeClient.cs | 4 ++ src/Shared/NodePipeServer.cs | 10 +++++ 16 files changed, 155 insertions(+), 16 deletions(-) diff --git a/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs b/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs index 5d0ae210a56..0c22214c0ee 100644 --- a/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs +++ b/src/Build.UnitTests/BackEnd/NodeEndpointInProc_Tests.cs @@ -105,6 +105,11 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr throw new NotImplementedException(); } + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + throw new NotImplementedException(); + } + public void RoutePacket(int nodeId, INodePacket packet) { _dataReceivedContext = new DataReceivedContext(Thread.CurrentThread, packet); diff --git a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs index 0219982e43e..b78001c5f3d 100644 --- a/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs +++ b/src/Build/BackEnd/Client/MSBuildClientPacketPump.cs @@ -125,6 +125,16 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } + /// + /// Deserializes a packet. + /// + /// The packet type. + /// The translator to use as a source for packet data. + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + return _packetFactory.DeserializePacket(packetType, translator); + } + /// /// Routes a packet to the appropriate handler. /// diff --git a/src/Build/BackEnd/Components/Communications/NodeManager.cs b/src/Build/BackEnd/Components/Communications/NodeManager.cs index b0031746031..31ebde5a1d6 100644 --- a/src/Build/BackEnd/Components/Communications/NodeManager.cs +++ b/src/Build/BackEnd/Components/Communications/NodeManager.cs @@ -255,6 +255,16 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } + /// + /// Takes a serializer and deserializes the packet. + /// + /// The packet type. + /// The translator containing the data from which the packet should be reconstructed. + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + return _packetFactory.DeserializePacket(packetType, translator); + } + /// /// Routes the specified packet. This is called by the Inproc node directly since it does not have to do any deserialization /// diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs index 45334ce6752..54f5205c3f3 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderInProc.cs @@ -292,6 +292,16 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr ErrorUtilities.ThrowInternalErrorUnreachable(); } + /// + /// Deserializes and routes a packet. Not used in the in-proc node. + /// + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + // Not used + ErrorUtilities.ThrowInternalErrorUnreachable(); + return null; + } + /// /// Routes a packet. /// diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs index 1d0f0f525d3..95df655f7c9 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcTaskHost.cs @@ -287,6 +287,16 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr } } + /// + /// Takes a serializer and deserializes the packet. + /// + /// The packet type. + /// The translator containing the data from which the packet should be reconstructed. + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + return _localPacketFactory.DeserializePacket(packetType, translator); + } + /// /// Routes the specified packet /// diff --git a/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs b/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs index e7e66d6b886..66c881052b8 100644 --- a/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs +++ b/src/Build/BackEnd/Components/Communications/TaskHostNodeManager.cs @@ -149,6 +149,16 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr throw new NotSupportedException("not used"); } + /// + /// Takes a serializer, deserializes the packet and routes it to the appropriate handler. + /// + /// The packet type. + /// The translator containing the data from which the packet should be reconstructed. + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + throw new NotSupportedException("not used"); + } + /// /// Routes the specified packet. This is called by the Inproc node directly since it does not have to do any deserialization /// diff --git a/src/Build/BackEnd/Node/InProcNode.cs b/src/Build/BackEnd/Node/InProcNode.cs index 7b4049f8905..33d5fb6359d 100644 --- a/src/Build/BackEnd/Node/InProcNode.cs +++ b/src/Build/BackEnd/Node/InProcNode.cs @@ -222,6 +222,16 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr ErrorUtilities.ThrowInternalError("Unexpected call to DeserializeAndRoutePacket on the in-proc node."); } + /// + /// Not necessary for in-proc node - we don't serialize. + /// + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + // The in-proc endpoint shouldn't be serializing, just routing. + ErrorUtilities.ThrowInternalError("Unexpected call to DeserializePacket on the in-proc node."); + return null; + } + /// /// Routes the packet to the appropriate handler. /// diff --git a/src/Build/BackEnd/Node/OutOfProcNode.cs b/src/Build/BackEnd/Node/OutOfProcNode.cs index 0adad41674c..d56acbd0db0 100644 --- a/src/Build/BackEnd/Node/OutOfProcNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcNode.cs @@ -343,6 +343,16 @@ void INodePacketFactory.DeserializeAndRoutePacket(int nodeId, NodePacketType pac _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } + /// + /// Deserializes a packet. + /// + /// The packet type. + /// The translator to use as a source for packet data. + INodePacket INodePacketFactory.DeserializePacket(NodePacketType packetType, ITranslator translator) + { + return _packetFactory.DeserializePacket(packetType, translator); + } + /// /// Routes a packet to the appropriate handler. /// diff --git a/src/Build/BackEnd/Node/OutOfProcServerNode.cs b/src/Build/BackEnd/Node/OutOfProcServerNode.cs index aefae5aceab..89591cd0f4c 100644 --- a/src/Build/BackEnd/Node/OutOfProcServerNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcServerNode.cs @@ -210,6 +210,16 @@ void INodePacketFactory.DeserializeAndRoutePacket(int nodeId, NodePacketType pac _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } + /// + /// Deserializes a packet. + /// + /// The packet type. + /// The translator to use as a source for packet data. + INodePacket INodePacketFactory.DeserializePacket(NodePacketType packetType, ITranslator translator) + { + return _packetFactory.DeserializePacket(packetType, translator); + } + /// /// Routes a packet to the appropriate handler. /// diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index fd169167a9c..fdb9dc2373a 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -374,6 +374,16 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } + /// + /// Takes a serializer and deserializes the packet. + /// + /// The packet type. + /// The translator containing the data from which the packet should be reconstructed. + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + return _packetFactory.DeserializePacket(packetType, translator); + } + /// /// Routes the specified packet /// diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs index fbd97f6e083..9b670a086d4 100644 --- a/src/MSBuild/OutOfProcTaskHostNode.cs +++ b/src/MSBuild/OutOfProcTaskHostNode.cs @@ -591,6 +591,16 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr _packetFactory.DeserializeAndRoutePacket(nodeId, packetType, translator); } + /// + /// Takes a serializer and deserializes the packet. + /// + /// The packet type. + /// The translator containing the data from which the packet should be reconstructed. + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + return _packetFactory.DeserializePacket(packetType, translator); + } + /// /// Routes the specified packet /// diff --git a/src/Shared/INodePacketFactory.cs b/src/Shared/INodePacketFactory.cs index c972e0408b5..63d469eb021 100644 --- a/src/Shared/INodePacketFactory.cs +++ b/src/Shared/INodePacketFactory.cs @@ -43,7 +43,14 @@ internal interface INodePacketFactory void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITranslator translator); /// - /// Routes the specified packet + /// Takes a serializer and deserializes the packet. + /// + /// The packet type. + /// The translator containing the data from which the packet should be reconstructed. + INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator); + + /// + /// Routes the specified packet. /// /// The node from which the packet was received. /// The packet to route. diff --git a/src/Shared/NodePacketFactory.cs b/src/Shared/NodePacketFactory.cs index 214ddfa20f9..478c88310eb 100644 --- a/src/Shared/NodePacketFactory.cs +++ b/src/Shared/NodePacketFactory.cs @@ -55,7 +55,22 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr ErrorUtilities.ThrowInternalError("No packet handler for type {0}", packetType); } - record.DeserializeAndRoutePacket(nodeId, translator); + INodePacket packet = record.DeserializePacket(translator); + record.RoutePacket(nodeId, packet); + } + + /// + /// Creates a packet with data from a binary stream. + /// + public INodePacket DeserializePacket(NodePacketType packetType, ITranslator translator) + { + // PERF: Not using VerifyThrow to avoid boxing of packetType in the non-error case + if (!_packetFactories.TryGetValue(packetType, out PacketFactoryRecord record)) + { + ErrorUtilities.ThrowInternalError("No packet handler for type {0}", packetType); + } + + return record.DeserializePacket(translator); } /// @@ -63,7 +78,12 @@ public void DeserializeAndRoutePacket(int nodeId, NodePacketType packetType, ITr /// public void RoutePacket(int nodeId, INodePacket packet) { - PacketFactoryRecord record = _packetFactories[packet.Type]; + // PERF: Not using VerifyThrow to avoid boxing of packetType in the non-error case + if (!_packetFactories.TryGetValue(packet.Type, out PacketFactoryRecord record)) + { + ErrorUtilities.ThrowInternalError("No packet handler for type {0}", packet.Type); + } + record.RoutePacket(nodeId, packet); } @@ -77,12 +97,12 @@ private class PacketFactoryRecord /// /// The handler to invoke when the packet is deserialized. /// - private INodePacketHandler _handler; + private readonly INodePacketHandler _handler; /// /// The method used to construct a packet from a translator stream. /// - private NodePacketFactoryMethod _factoryMethod; + private readonly NodePacketFactoryMethod _factoryMethod; /// /// Constructor. @@ -94,21 +114,14 @@ public PacketFactoryRecord(INodePacketHandler handler, NodePacketFactoryMethod f } /// - /// Creates a packet from a binary stream and sends it to the registered handler. + /// Creates a packet from a binary stream. /// - public void DeserializeAndRoutePacket(int nodeId, ITranslator translator) - { - INodePacket packet = _factoryMethod(translator); - RoutePacket(nodeId, packet); - } + public INodePacket DeserializePacket(ITranslator translator) => _factoryMethod(translator); /// /// Routes the packet to the correct destination. /// - public void RoutePacket(int nodeId, INodePacket packet) - { - _handler.PacketReceived(nodeId, packet); - } + public void RoutePacket(int nodeId, INodePacket packet) => _handler.PacketReceived(nodeId, packet); } } } diff --git a/src/Shared/NodePipeBase.cs b/src/Shared/NodePipeBase.cs index fd1d08efe9c..a824bc0aca3 100644 --- a/src/Shared/NodePipeBase.cs +++ b/src/Shared/NodePipeBase.cs @@ -226,7 +226,7 @@ private int Read(byte[] buffer, int bytesToRead) } #if !TASKHOST - private async ValueTask ReadAsync(byte[] buffer, int bytesToRead, CancellationToken cancellationToken) + private async Task ReadAsync(byte[] buffer, int bytesToRead, CancellationToken cancellationToken) { int totalBytesRead = 0; while (totalBytesRead < bytesToRead) diff --git a/src/Shared/NodePipeClient.cs b/src/Shared/NodePipeClient.cs index 6585a2558e7..a521f4f34d2 100644 --- a/src/Shared/NodePipeClient.cs +++ b/src/Shared/NodePipeClient.cs @@ -84,7 +84,11 @@ private void PerformHandshake(int timeout) _pipeClient.WriteEndOfHandshakeSignal(); CommunicationsUtilities.Trace("Reading handshake from pipe {0}", PipeName); +#if NET _pipeClient.ReadEndOfHandshakeSignal(true, timeout); +#else + _pipeClient.ReadEndOfHandshakeSignal(true); +#endif } } } diff --git a/src/Shared/NodePipeServer.cs b/src/Shared/NodePipeServer.cs index dde8e2ee371..eb932d973aa 100644 --- a/src/Shared/NodePipeServer.cs +++ b/src/Shared/NodePipeServer.cs @@ -25,11 +25,13 @@ internal sealed class NodePipeServer : NodePipeBase /// private const int PipeBufferSize = 131_072; +#if NET /// /// A timeout for the handshake. This is only used on Unix-like socket implementations, because the /// timeout on the PipeStream connection is ignore. /// private static readonly int s_handshakeTimeout = NativeMethodsShared.IsWindows ? 0 : 60_000; +#endif private readonly NamedPipeServerStream _pipeServer; @@ -179,7 +181,11 @@ private bool ValidateHandshake() for (int i = 0; i < HandshakeComponents.Length; i++) { // This will disconnect a < 16.8 host; it expects leading 00 or F5 or 06. 0x00 is a wildcard. +#if NET int handshakePart = _pipeServer.ReadIntForHandshake(byteToAccept: i == 0 ? CommunicationsUtilities.handshakeVersion : null, s_handshakeTimeout); +#else + int handshakePart = _pipeServer.ReadIntForHandshake(byteToAccept: i == 0 ? CommunicationsUtilities.handshakeVersion : null); +#endif if (handshakePart != HandshakeComponents[i]) { @@ -190,7 +196,11 @@ private bool ValidateHandshake() } // To ensure that our handshake and theirs have the same number of bytes, receive and send a magic number indicating EOS. +#if NET _pipeServer.ReadEndOfHandshakeSignal(false, s_handshakeTimeout); +#else + _pipeServer.ReadEndOfHandshakeSignal(false); +#endif CommunicationsUtilities.Trace("Successfully connected to parent."); _pipeServer.WriteEndOfHandshakeSignal(); From d00b111697a772427c3183cf65e9b42388bdfc31 Mon Sep 17 00:00:00 2001 From: stan-sz <37585349+stan-sz@users.noreply.github.com> Date: Wed, 2 Apr 2025 09:46:47 +0200 Subject: [PATCH 060/106] Improve TargetFrameworkConfusionCheck (#11656) --- src/Build/BuildCheck/Checks/TargetFrameworkConfusionCheck.cs | 2 ++ src/BuildCheck.UnitTests/EndToEndTests.cs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/Build/BuildCheck/Checks/TargetFrameworkConfusionCheck.cs b/src/Build/BuildCheck/Checks/TargetFrameworkConfusionCheck.cs index 2c3db3512ad..7d117fe456f 100644 --- a/src/Build/BuildCheck/Checks/TargetFrameworkConfusionCheck.cs +++ b/src/Build/BuildCheck/Checks/TargetFrameworkConfusionCheck.cs @@ -46,7 +46,9 @@ private void EvaluatedPropertiesAction(BuildCheckDataContextnet9.0""", "", false)] [InlineData("""net9.0;net472""", "", false)] [InlineData("""net9.0;net472""", " /p:TargetFramework=net9.0", false)] + [InlineData("""net9.0;net472""", "", false)] + [InlineData("""net9.0;net472""", "", false)] + [InlineData("""net9.0""", "", false)] + [InlineData("""net9.0""", "", false)] [InlineData("""net9.0net9.0;net472""", "", true)] public void TFMConfusionCheckTest(string tfmString, string cliSuffix, bool shouldTriggerCheck) { From a48a28320f8e35cbe317f21d14a7c3be7e8cc047 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:25:55 +0200 Subject: [PATCH 061/106] [automated] Merge branch 'vs17.14' => 'main' (#11664) --- eng/Packages.props | 1 - eng/Versions.props | 22 ++++++++++++++++------ src/MSBuild/app.amd64.config | 24 ++++++++++++------------ src/MSBuild/app.config | 12 ++++++------ 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/eng/Packages.props b/eng/Packages.props index 456f037819e..6afcad00799 100644 --- a/eng/Packages.props +++ b/eng/Packages.props @@ -34,7 +34,6 @@ - diff --git a/eng/Versions.props b/eng/Versions.props index 1e4ef0a7a5f..7a862bae10f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -25,18 +25,28 @@ true - - + + + + 4.6.0 + 6.1.0 + + + - 4.6.0 - 4.6.0 - 6.1.0 + 4.5.5 + 6.0.0 + + + + 6.0.1 0.2.104-beta - 6.1.0 5.0.0 diff --git a/src/MSBuild/app.amd64.config b/src/MSBuild/app.amd64.config index dea6d8dbc9c..ca919c51e3f 100644 --- a/src/MSBuild/app.amd64.config +++ b/src/MSBuild/app.amd64.config @@ -39,8 +39,8 @@ - - + + @@ -94,8 +94,8 @@ - - + + @@ -190,13 +190,13 @@ - - + + - - + + @@ -215,8 +215,8 @@ - - + + @@ -240,8 +240,8 @@ - - + + diff --git a/src/MSBuild/app.config b/src/MSBuild/app.config index cd0059bd3db..f1bb2ea9f69 100644 --- a/src/MSBuild/app.config +++ b/src/MSBuild/app.config @@ -45,7 +45,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -78,11 +78,11 @@ - + - + @@ -98,7 +98,7 @@ - + @@ -114,7 +114,7 @@ - + From 62532f66022ee6f1fe7db036fcc2fdc37ed7ab36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Provazn=C3=ADk?= Date: Wed, 2 Apr 2025 15:21:17 +0200 Subject: [PATCH 062/106] document differences between plans and implementation --- .../specs/proposed/VS-OpenTelemetry.md | 67 ++++++++++++------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/documentation/specs/proposed/VS-OpenTelemetry.md b/documentation/specs/proposed/VS-OpenTelemetry.md index fbb5f701d9a..59d1f6e5d17 100644 --- a/documentation/specs/proposed/VS-OpenTelemetry.md +++ b/documentation/specs/proposed/VS-OpenTelemetry.md @@ -3,6 +3,8 @@ VS OTel provide packages compatible with ingesting data to their backend if we instrument it via OpenTelemetry traces (System.Diagnostics.Activity). VS OTel packages are not open source so we need to conditionally include them in our build only for VS and MSBuild.exe +> this formatting is a comment describing how the implementation turned out in 17.14 when our original goals were different + [Onepager](https://github.com/dotnet/msbuild/blob/main/documentation/specs/proposed/telemetry-onepager.md) ## Concepts @@ -12,10 +14,10 @@ It's a bit confusing how things are named in OpenTelemetry and .NET and VS Telem | OTel concept | .NET/VS | Description | | --- | --- | --- | | Span/Trace | System.Diagnostics.Activity | Trace is a tree of Spans. Activities can be nested.| -| Tracer | System.Diagnostics.ActivitySource | Creates and listens to activites. | +| Tracer | System.Diagnostics.ActivitySource | Creates activites. | | Processor/Exporter | VS OTel provided default config | filters and saves telemetry as files in a desired format | -| TracerProvider | OTel SDK TracerProvider | Singleton that is aware of processors, exporters and Tracers (in .NET a bit looser relationship because it does not create Tracers just hooks to them) | -| Collector | VS OTel Collector | Sends to VS backend, expensive to initialize and finalize | +| TracerProvider | OTel SDK TracerProvider | Singleton that is aware of processors, exporters and Tracers and listens (in .NET a bit looser relationship because it does not create Tracers just hooks to them) | +| Collector | VS OTel Collector | Sends to VS backend | ## Requirements @@ -26,6 +28,8 @@ It's a bit confusing how things are named in OpenTelemetry and .NET and VS Telem - Has to have no impact on Core without opting into tracing, small impact on Framework - No regression in VS perf ddrit scenarios. +> there is an allocation regression when sampled, one of the reasons why it's not enabled by default + ### Privacy - Hashing data points that could identify customers (e.g. names of targets) @@ -37,6 +41,8 @@ It's a bit confusing how things are named in OpenTelemetry and .NET and VS Telem - If custom hooking solution will be used - document the security implications of hooking custom telemetry Exporters/Collectors in Framework - other security requirements (transportation, rate limiting, sanitization, data access) are implemented by VS Telemetry library or the backend +> hooking in Framework not implemented + ### Data handling - Implement head [Sampling](https://opentelemetry.io/docs/concepts/sampling/) with the granularity of a MSBuild.exe invocation/VS instance. @@ -61,30 +67,37 @@ The data sent via VS OpenTelemetry is neither a subset neither a superset of wha ##### Features - BuildCheck enabled -- Custom tasks and targets counts and durations +- Tasks runtimes and memory usage +- Tasks summary - whether they come from Nuget or are custom +- Targets summary - how many loaded and executed, how many come from nuget, how many come from metaproject -The design allows for easy instrumentation of additional data points. +The design should allow for easy instrumentation of additional data points. +> current implementation has only one datapoint and that is the whole build `vs/msbuild/build`, the instrumentaiton of additional datapoints is gated by first checking that telemetry is running and using `Activity` classes only in helper methods gated by `[MethodImpl(MethodImplOptions.NoInlining)]` to avoid System.Diagnostics.DiagnosticSource dll load. ## Core `dotnet build` scenario - Telemetry should not be collected via VS OpenTelemetry mechanism because it's already collected in sdk. - opt in to initialize the ActivitySource to avoid degrading performance. -- [baronfel/otel-startup-hook: A .NET CLR Startup Hook that exports OpenTelemetry metrics via the OTLP Exporter to an OpenTelemetry Collector](https://github.com/baronfel/otel-startup-hook/) and similar enable collecting telemetry data locally by listening to the ActivitySource name defined in MSBuild. +- [baronfel/otel-startup-hook: A .NET CLR Startup Hook that exports OpenTelemetry metrics via the OTLP Exporter to an OpenTelemetry Collector](https://github.com/baronfel/otel-startup-hook/) and similar enable collecting telemetry data locally by listening to the ActivitySource prefix defined in MSBuild. + +> this hook can be used when the customer specifies that they want to listen to the prefix `Microsoft.VisualStudio.OpenTelemetry.MSBuild`, opt in by setting environment variables `MSBUILD_TELEMETRY_OPTIN=1`,`MSBUILD_TELEMETRY_SAMPLE_RATE=1.0` ## Standalone MSBuild.exe scenario - Initialize and finalize in Xmake.cs - - ActivitySource, TracerProvider, VS Collector - - overhead of starting VS collector is nonzero - - head sampling should avoid initializing if not sampled + ActivitySource, TracerProvider, VS Collector +- overhead of starting VS collector is nonzero +- head sampling should avoid initializing if not sampled ## VS in proc (devenv) scenario - VS can call `BuildManager` in a thread unsafe way the telemetry implementation has to be mindful of [BuildManager instances acquire its own BuildTelemetry instance by rokonec · Pull Request #8444 · dotnet/msbuild](https://github.com/dotnet/msbuild/pull/8444) - - ensure no race conditions in initialization - - only 1 TracerProvider with VS defined processing should exist + - ensure no race conditions in initialization + - only 1 TracerProvider with VS defined processing should exist - Visual Studio should be responsible for having a running collector, we don't want this overhead in MSBuild and eventually many will use it +> this was not achieved in 17.14 so we start collector every time + ## Implementation and MSBuild developer experience ### ActivitySource names @@ -103,15 +116,17 @@ For proportion estimation (of fairly common occurence in the builds), with not v - Enables opt-in and opt-out for guaranteed sample or not sampled. - nullable ActivitySource, using `?` when working with them, we can be initialized but not sampled -> it will not reinitialize but not collect telemetry. -- for Dev17 we can't use the new OTel assemblies and their dependencies, so everything has to be opt in. -- for Dev18 OpenTelemetry will be available and usable by default +- for 17.14 we can't use the new OTel assemblies and their dependencies, so everything has to be opt in. +- eventually OpenTelemetry will be available and usable by default - We can use experiments in VS to pass the environment variable to initialize +> Targeted notification can be set that samples 100% of customers to which it is sent + ### Initialization at entrypoints - There are 2 entrypoints: - - for VS in BuildManager.BeginBuild - - for standalone in Xmake.cs Main + - for VS in BuildManager.BeginBuild + - for standalone in Xmake.cs Main ### Exiting @@ -147,6 +162,8 @@ IActivityTelemetryDataHolder data = new SomeData(); myActivity?.WithTags(data); ``` +> currently this should be gated in a separate method to avoid System.DiagnosticDiagnosticsource dll load. + #### Default Build activity in EndBuild - this activity would always be created at the same point when sdk telemetry is sent in Core @@ -165,13 +182,17 @@ We potentially want apart from the Default ActivitySource: - Create a way of using a "HighPrioActivitySource" which would override sampling and initialize Collector in MSBuild.exe scenario/tracerprovider in VS. - this would enable us to catch rare events +> not implemented + +### Implementation details + +- `OpenTelemetryManager` - singleton that manages lifetime of OpenTelemetry objects listening to `Activity`ies, start by initializing in `Xmake` or `BuildManager`. +- Task and Target data is forwarded from worker nodes via `TelemetryForwarder` and `InternalTelemetryForwardingLogger` and then aggregated to stats and serialized in `TelemetryDataUtils` and attached to the default `vs/msbuild/build` event. -## Uncertainties +## Future work when/if we decide to invest in telemetry again -- Configuring tail sampling in VS telemetry server side infrastructure. -- Sampling rare events details. -- In standalone we could start the collector async without waiting which would potentially miss some earlier traces (unlikely to miss the important end of build trace though) but would degrade performance less than waiting for it's startup. The performance and utility tradeoff is not clear. -- Can collector startup/shutdown be faster? -- Exceptions in Collector code paths -- Do we want to send antivirus state? Figuring it out is expensive: https://github.com/dotnet/msbuild/compare/main...proto/get-av ~100ms -- ability to configure the overall and per-namespace sampling from server side (e.g. storing it in the .msbuild folder in user profile if different then default values set from server side - this would obviously have a delay of the default sample rate # of executions) +- avoid initializing/finalizing collector in VS when there is one running +- multiple levels of sampling for different types of events +- running by default with head sampling (simplifies instrumentation with `Activity`ies) +- implement anonymization consistently in an OTel processor and not ad hoc in each usage +- add datapoints helping perf optimization decisions/ reliability investigations From 0aa8fb935eaf4a7f3adb32e55e7ff3caf7cfb738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Provazn=C3=ADk?= Date: Wed, 2 Apr 2025 15:22:41 +0200 Subject: [PATCH 063/106] move out of 'proposed' specs --- documentation/specs/{proposed => }/VS-OpenTelemetry.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename documentation/specs/{proposed => }/VS-OpenTelemetry.md (100%) diff --git a/documentation/specs/proposed/VS-OpenTelemetry.md b/documentation/specs/VS-OpenTelemetry.md similarity index 100% rename from documentation/specs/proposed/VS-OpenTelemetry.md rename to documentation/specs/VS-OpenTelemetry.md From 06179d08bf90f1cdc6cc283b865adcdac457ea3c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:58:59 +0200 Subject: [PATCH 064/106] [automated] Merge branch 'vs17.14' => 'main' (#11667) --- .vsts-dotnet-ci.yml | 70 +++++++-------- documentation/specs/custom-cultures.md | 42 +++++++++ documentation/wiki/ChangeWaves.md | 2 +- eng/BootStrapMsBuild.targets | 15 +++- .../EntryProject/EntryProject.csproj | 1 + .../ReferencedProject.csproj | 1 + src/Framework/Traits.cs | 4 +- .../Tasks/LocateVisualStudioTask.cs | 62 +++++++++++++ .../MSBuild/Microsoft.Build.CommonTypes.xsd | 1 + .../AssemblyDependency/Miscellaneous.cs | 43 +-------- .../Microsoft.Build.Tasks.UnitTests.csproj | 6 ++ ...lveAssemblyReference_CustomCultureTests.cs | 89 +++++++++++++++++++ .../CustomCulture/ProjectA.csproj | 25 ++++++ .../CustomCulture/ProjectB.csproj | 19 ++++ .../TestResources/CustomCulture/Test.euy.resx | 58 ++++++++++++ .../TestResources/CustomCulture/Test.resx | 58 ++++++++++++ .../TestResources/CustomCulture/Test.yue.resx | 58 ++++++++++++ .../AssemblyDependency/ReferenceTable.cs | 33 +++++-- .../ResolveAssemblyReference.cs | 32 ++++++- src/Tasks/CreateCSharpManifestResourceName.cs | 14 +-- src/Tasks/CreateManifestResourceName.cs | 11 +++ .../CreateVisualBasicManifestResourceName.cs | 14 +-- .../Microsoft.CSharp.CurrentVersion.targets | 1 + .../Microsoft.Common.CurrentVersion.targets | 17 +++- ...crosoft.VisualBasic.CurrentVersion.targets | 1 + 25 files changed, 575 insertions(+), 102 deletions(-) create mode 100644 documentation/specs/custom-cultures.md create mode 100644 src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs create mode 100644 src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs create mode 100644 src/Tasks.UnitTests/TestResources/CustomCulture/ProjectA.csproj create mode 100644 src/Tasks.UnitTests/TestResources/CustomCulture/ProjectB.csproj create mode 100644 src/Tasks.UnitTests/TestResources/CustomCulture/Test.euy.resx create mode 100644 src/Tasks.UnitTests/TestResources/CustomCulture/Test.resx create mode 100644 src/Tasks.UnitTests/TestResources/CustomCulture/Test.yue.resx diff --git a/.vsts-dotnet-ci.yml b/.vsts-dotnet-ci.yml index 3ea490a61af..d62083aecab 100644 --- a/.vsts-dotnet-ci.yml +++ b/.vsts-dotnet-ci.yml @@ -14,40 +14,40 @@ variables: value: none jobs: -# - job: CheckVersionBumpOnReleaseBranches -# displayName: "Check Version Bump On Release Branches" -# steps: -# - powershell: | -# $versionsFile = "eng/Versions.props" -# $changedFiles = git diff --name-only HEAD HEAD~1 -# $changedVersionsFile = $changedFiles | Where-Object { $_ -eq $versionsFile } -# $isInitialCommit = $false -# $isVersionBumped = $false -# if ($changedVersionsFile -ne $null) { -# $difference = git diff HEAD~1 $versionsFile -# $changedContent = $difference -join "%" -# # 'DotNetFinalVersionKind' is expected to be added only during the initial setup of the release branch -# $initialCommitPattern = '-\s*\d+\.\d+\.\d+<\/VersionPrefix>%.*\+\s*\d+\.\d+\.\d+<\/VersionPrefix>release<\/DotNetFinalVersionKind>' -# $isInitialCommit = $changedContent -match $initialCommitPattern -# $pattern = '-\s*\d+\.\d+\.(?\d+)<\/VersionPrefix>.*%\+\s*\d+\.\d+\.(?\d+)<\/VersionPrefix>' -# if (!($isInitialCommit) -and ($changedContent -match $pattern)) { -# try { -# $previousPatch = [Convert]::ToInt32($Matches.previous) -# $currentPatch = [Convert]::ToInt32($Matches.current) -# if ($currentPatch -gt $previousPatch) { -# $isVersionBumped = $true -# } -# } catch { -# Write-Host "An error occurred during conversion: $_" -# } -# } -# } +- job: CheckVersionBumpOnReleaseBranches + displayName: "Check Version Bump On Release Branches" + condition: startsWith(variables['System.PullRequest.TargetBranch'], 'vs') + steps: + - powershell: | + $versionsFile = "eng/Versions.props" + $changedFiles = git diff --name-only HEAD HEAD~1 + $changedVersionsFile = $changedFiles | Where-Object { $_ -eq $versionsFile } + $isInitialCommit = $false + $isVersionBumped = $false + if ($changedVersionsFile -ne $null) { + $difference = git diff HEAD~1 $versionsFile + $changedContent = $difference -join "%" + # 'DotNetFinalVersionKind' is expected to be added only during the initial setup of the release branch + $initialCommitPattern = '-\s*\d+\.\d+\.\d+<\/VersionPrefix>%.*\+\s*\d+\.\d+\.\d+<\/VersionPrefix>release<\/DotNetFinalVersionKind>' + $isInitialCommit = $changedContent -match $initialCommitPattern + $pattern = '-\s*\d+\.\d+\.(?\d+)<\/VersionPrefix>.*%\+\s*\d+\.\d+\.(?\d+)<\/VersionPrefix>' + if (!($isInitialCommit) -and ($changedContent -match $pattern)) { + try { + $previousPatch = [Convert]::ToInt32($Matches.previous) + $currentPatch = [Convert]::ToInt32($Matches.current) + if ($currentPatch -gt $previousPatch) { + $isVersionBumped = $true + } + } catch { + Write-Host "An error occurred during conversion: $_" + } + } + } -# if (!($isInitialCommit -or $isVersionBumped)) { -# throw "Hello! I noticed that you're targeting one of our servicing branches. You need to increase the revision version number (the last part) of 'VersionPrefix' in eng/Versions.props." -# } -# condition: startsWith(variables['System.PullRequest.TargetBranch'], 'vs') -# displayName: "Check if patch version is bumped up" + if (!($isInitialCommit -or $isVersionBumped)) { + throw "Hello! I noticed that you're targeting one of our servicing branches. You need to increase the revision version number (the last part) of 'VersionPrefix' in eng/Versions.props." + } + displayName: "Check if patch version is bumped up" - job: IfOnlyDocumentionChanged displayName: "Check whether Test Results need to be executed" @@ -294,7 +294,7 @@ jobs: Token: $(dn-bot-dnceng-artifact-feeds-rw) - bash: . 'eng/cibuild_bootstrapped_msbuild.sh' --onlyDocChanged $(onlyDocChanged) displayName: CI Build - env: + env: MSBUILDUSESERVER: "1" - task: PublishTestResults@2 displayName: Publish .NET Test Results @@ -361,7 +361,7 @@ jobs: Token: $(dn-bot-dnceng-artifact-feeds-rw) - bash: . 'eng/cibuild_bootstrapped_msbuild.sh' --onlyDocChanged $(onlyDocChanged) displayName: CI Build - env: + env: MSBUILDUSESERVER: "1" - task: PublishTestResults@2 displayName: Publish .NET Test Results diff --git a/documentation/specs/custom-cultures.md b/documentation/specs/custom-cultures.md new file mode 100644 index 00000000000..a3e683c25e4 --- /dev/null +++ b/documentation/specs/custom-cultures.md @@ -0,0 +1,42 @@ +# MSBuild Custom Cultures Support + +## Overview + +The `EnableCustomCulture` property provides an opt-in mechanism for handling custom culture-specific resources in MSBuild projects. This feature allows for greater control over which directories are treated as culture-specific resources during the build process. + +## Purpose + +In some projects, directory names that match culture name patterns might not actually be culture resources. This can cause issues with resource compilation and deployment. This feature flag enables: + +1. Control over whether custom culture detection is enabled +2. Fine-grained configuration of which directories should be excluded from culture-specific resource processing + +## Usage + +### Enabling the Feature + +To enable the custom cultures feature, set the `EnableCustomCulture` property `true`. + +```xml + + true + +``` + +### Excluding Specific Directories + +When the feature is enabled, you can specify directories that should not be treated as culture-specific resources using the `NonCultureResourceDirectories` property: + +```xml + + long;hash;temp + +``` + +In this example, directories named "long", "hash", or "temp" will not be processed as culture-specific resources and the assemblied inside of them will be skipped, even if their names match culture naming patterns. Globbing is not supported. + +## Additional Notes + +- This feature does not affect the standard resource handling for well-known cultures. +- The feature is designed to be backward compatible - existing projects without the feature flag will behave the same as before. +- Performance impact is minimal, as the exclusion check happens only during the resource discovery phase of the build. \ No newline at end of file diff --git a/documentation/wiki/ChangeWaves.md b/documentation/wiki/ChangeWaves.md index 6b88ed66097..833299ed296 100644 --- a/documentation/wiki/ChangeWaves.md +++ b/documentation/wiki/ChangeWaves.md @@ -26,7 +26,7 @@ A wave of features is set to "rotate out" (i.e. become standard functionality) t ### 17.14 - ~[.SLNX support - use the new parser for .sln and .slnx](https://github.com/dotnet/msbuild/pull/10836)~ reverted after compat problems discovered -- [Support custom culture in RAR](https://github.com/dotnet/msbuild/pull/11000) +- ~~[Support custom culture in RAR](https://github.com/dotnet/msbuild/pull/11000)~~ - see [11607](https://github.com/dotnet/msbuild/pull/11607) for details - [VS Telemetry](https://github.com/dotnet/msbuild/pull/11255) ### 17.12 diff --git a/eng/BootStrapMsBuild.targets b/eng/BootStrapMsBuild.targets index d4330ba658d..4789ffcec85 100644 --- a/eng/BootStrapMsBuild.targets +++ b/eng/BootStrapMsBuild.targets @@ -63,14 +63,27 @@ + + + + + + + + + + + - + diff --git a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj index 1ac36d043de..228409c378c 100644 --- a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj +++ b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj @@ -4,6 +4,7 @@ net9.0 enable enable + true diff --git a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj index 4208181be80..c03ac65c696 100644 --- a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj +++ b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj @@ -5,6 +5,7 @@ net8.0 enable enable + true diff --git a/src/Framework/Traits.cs b/src/Framework/Traits.cs index f2447e47031..e355761d9fd 100644 --- a/src/Framework/Traits.cs +++ b/src/Framework/Traits.cs @@ -7,11 +7,12 @@ namespace Microsoft.Build.Framework { /// - /// Represents toggleable features of the MSBuild engine + /// Represents toggleable features of the MSBuild engine. /// internal class Traits { private static Traits _instance = new Traits(); + public static Traits Instance { get @@ -132,7 +133,6 @@ public Traits() public readonly bool InProcNodeDisabled = Environment.GetEnvironmentVariable("MSBUILDNOINPROCNODE") == "1"; - /// /// Variables controlling opt out at the level of not initializing telemetry infrastructure. Set to "1" or "true" to opt out. /// mirroring diff --git a/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs b/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs new file mode 100644 index 00000000000..91cf7608e36 --- /dev/null +++ b/src/MSBuild.Bootstrap.Utils/Tasks/LocateVisualStudioTask.cs @@ -0,0 +1,62 @@ +// 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.IO; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace MSBuild.Bootstrap.Utils.Tasks +{ + public class LocateVisualStudioTask : ToolTask + { + private readonly StringBuilder _standardOutput = new(); + + [Output] + public string VsInstallPath { get; set; } + + protected override string ToolName => "vswhere.exe"; + + protected override string GenerateFullPathToTool() + { + string programFilesX86 = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86); + string vsWherePath = Path.Combine(programFilesX86, "Microsoft Visual Studio", "Installer", ToolName); + + + return vsWherePath; + } + + protected override string GenerateCommandLineCommands() => "-latest -prerelease -property installationPath"; + + public override bool Execute() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Log.LogMessage(MessageImportance.High, "Not running on Windows. Skipping Visual Studio detection."); + return true; + } + + _ = ExecuteTool(GenerateFullPathToTool(), string.Empty, GenerateCommandLineCommands()); + + if (!Log.HasLoggedErrors) + { + VsInstallPath = _standardOutput.ToString().Trim(); + } + + return true; + } + + // Override to capture standard output + protected override void LogEventsFromTextOutput(string singleLine, MessageImportance messageImportance) + { + if (!string.IsNullOrWhiteSpace(singleLine)) + { + _ = _standardOutput.AppendLine(singleLine); + } + + base.LogEventsFromTextOutput(singleLine, messageImportance); + } + } +} diff --git a/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd b/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd index 05365f4f62a..56c53a3af4d 100644 --- a/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd +++ b/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd @@ -1893,6 +1893,7 @@ elementFormDefault="qualified"> + boolean diff --git a/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs b/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs index 576145d5cd5..cdfb19a59bf 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/Miscellaneous.cs @@ -3268,41 +3268,6 @@ public void ParentAssemblyResolvedFromAForGac() Assert.Equal(reference2.ResolvedSearchPath, parentReferenceFolders[0].Directory); } - /// - /// Generate a fake reference which has been resolved from the gac. We will use it to verify the creation of the exclusion list. - /// - /// - private ReferenceTable GenerateTableWithAssemblyFromTheGlobalLocation(string location) - { - ReferenceTable referenceTable = new ReferenceTable(null, false, false, false, false, Array.Empty(), null, null, null, null, null, null, SystemProcessorArchitecture.None, fileExists, null, null, null, null, -#if FEATURE_WIN32_REGISTRY - null, null, null, -#endif - null, null, new Version("4.0"), null, null, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null); - - AssemblyNameExtension assemblyNameExtension = new AssemblyNameExtension(new AssemblyName("Microsoft.VisualStudio.Interopt, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")); - TaskItem taskItem = new TaskItem("Microsoft.VisualStudio.Interopt, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); - - Reference reference = new Reference(isWinMDFile, fileExists, getRuntimeVersion); - reference.MakePrimaryAssemblyReference(taskItem, false, ".dll"); - // "Resolve the assembly from the gac" - reference.FullPath = "c:\\Microsoft.VisualStudio.Interopt.dll"; - reference.ResolvedSearchPath = location; - referenceTable.AddReference(assemblyNameExtension, reference); - - assemblyNameExtension = new AssemblyNameExtension(new AssemblyName("Team.System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")); - taskItem = new TaskItem("Team, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); - - reference = new Reference(isWinMDFile, fileExists, getRuntimeVersion); - reference.MakePrimaryAssemblyReference(taskItem, false, ".dll"); - - // "Resolve the assembly from the gac" - reference.FullPath = "c:\\Team.System.dll"; - reference.ResolvedSearchPath = location; - referenceTable.AddReference(assemblyNameExtension, reference); - return referenceTable; - } - /// /// Given a reference that resolves to a bad image, we should get a warning and /// no reference. We don't want an exception. @@ -6734,11 +6699,11 @@ public void ReferenceTableDependentItemsInDenyList3() [Fact] public void ReferenceTableDependentItemsInDenyList4() { - ReferenceTable referenceTable = new ReferenceTable(null, false, false, false, false, Array.Empty(), null, null, null, null, null, null, SystemProcessorArchitecture.None, fileExists, null, null, null, + ReferenceTable referenceTable = new ReferenceTable(null, false, false, false, false, false, Array.Empty(), null, null, null, null, null, null, SystemProcessorArchitecture.None, fileExists, null, null, null, #if FEATURE_WIN32_REGISTRY null, null, null, #endif - null, null, null, new Version("4.0"), null, null, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null); + null, null, null, new Version("4.0"), null, null, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null, Array.Empty()); MockEngine mockEngine; ResolveAssemblyReference rar; Dictionary denyList; @@ -6912,11 +6877,11 @@ public void ReferenceTableDependentItemsInDenyListPrimaryWithSpecificVersion() private static ReferenceTable MakeEmptyReferenceTable(TaskLoggingHelper log) { - ReferenceTable referenceTable = new ReferenceTable(null, false, false, false, false, Array.Empty(), null, null, null, null, null, null, SystemProcessorArchitecture.None, fileExists, null, null, null, null, + ReferenceTable referenceTable = new ReferenceTable(null, false, false, false, false, false, Array.Empty(), null, null, null, null, null, null, SystemProcessorArchitecture.None, fileExists, null, null, null, null, #if FEATURE_WIN32_REGISTRY null, null, null, #endif - null, null, new Version("4.0"), null, log, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null); + null, null, new Version("4.0"), null, log, null, true, false, null, null, false, null, WarnOrErrorOnTargetArchitectureMismatchBehavior.None, false, false, null, Array.Empty()); return referenceTable; } diff --git a/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj b/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj index 81b5048f0f7..2ff059b2833 100644 --- a/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj +++ b/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj @@ -64,6 +64,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -155,6 +158,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs new file mode 100644 index 00000000000..1a7d7ed0562 --- /dev/null +++ b/src/Tasks.UnitTests/ResolveAssemblyReference_CustomCultureTests.cs @@ -0,0 +1,89 @@ +// 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.IO; +using Microsoft.Build.UnitTests; +using Microsoft.Build.UnitTests.Shared; +using Shouldly; +using Xunit; + +namespace Microsoft.Build.Tasks.UnitTests +{ + /// + /// Unit tests for the ResolveAssemblyReference task. + /// + public class ResolveAssemblyReference_CustomCultureTests + { + private static string TestAssetsRootPath { get; } = Path.Combine( + Path.GetDirectoryName(typeof(AddToWin32Manifest_Tests).Assembly.Location) ?? AppContext.BaseDirectory, + "TestResources", + "CustomCulture"); + + [WindowsOnlyTheory] + [InlineData(true, "", true, true)] + [InlineData(false)] + [InlineData(true, "yue", false, true)] + [InlineData(false, "yue", false, true)] + [InlineData(true, "euy", true)] + [InlineData(true, "yue;euy")] + [InlineData(true, "euy;yue")] + public void E2EScenarioTests(bool enableCustomCulture, string customCultureExclusions = "", bool isYueCultureExpected = false, bool isEuyCultureExpected = false) + { + using (TestEnvironment env = TestEnvironment.Create()) + { + // Set up project paths + var testAssetsPath = TestAssetsRootPath; + var solutionFolder = env.CreateFolder(); + var solutionPath = solutionFolder.Path; + + // Create and configure ProjectB + var projectBName = "ProjectB.csproj"; + var projBOutputPath = env.CreateFolder().Path; + var projectBFolder = Path.Combine(solutionPath, projectBName); + Directory.CreateDirectory(projectBFolder); + var projBContent = File.ReadAllText(Path.Combine(testAssetsPath, projectBName)) + .Replace("OutputPathPlaceholder", projBOutputPath) + .Replace("NonCultureResourceDirectoriesPlaceholder", customCultureExclusions) + .Replace("EnableCustomCulturePlaceholder", enableCustomCulture.ToString()); + env.CreateFile(Path.Combine(projectBFolder, projectBName), projBContent); + + // Copy ProjectA files to test solution folder + CopyTestAsset(testAssetsPath, "ProjectA.csproj", solutionPath); + CopyTestAsset(testAssetsPath, "Test.resx", solutionPath); + CopyTestAsset(testAssetsPath, "Test.yue.resx", solutionPath); + CopyTestAsset(testAssetsPath, "Test.euy.resx", solutionPath); + + env.SetCurrentDirectory(projectBFolder); + var output = RunnerUtilities.ExecBootstrapedMSBuild("-restore", out bool buildSucceeded); + + buildSucceeded.ShouldBeTrue($"MSBuild should complete successfully. Build output: {output}"); + + var yueCultureResourceDll = Path.Combine(projBOutputPath, "yue", "ProjectA.resources.dll"); + AssertCustomCulture(isYueCultureExpected, "yue", yueCultureResourceDll); + + var euyCultureResourceDll = Path.Combine(projBOutputPath, "euy", "ProjectA.resources.dll"); + AssertCustomCulture(isEuyCultureExpected, "euy", euyCultureResourceDll); + } + + void AssertCustomCulture(bool isCultureExpectedToExist, string customCultureName, string cultureResourcePath) + { + if (enableCustomCulture && isCultureExpectedToExist) + { + File.Exists(cultureResourcePath).ShouldBeTrue($"Expected '{customCultureName}' resource DLL not found at: {cultureResourcePath}"); + } + else + { + File.Exists(cultureResourcePath).ShouldBeFalse($"Unexpected '{customCultureName}' culture DLL was found at: {cultureResourcePath}"); + } + } + } + + private void CopyTestAsset(string sourceFolder, string fileName, string destinationFolder) + { + var sourcePath = Path.Combine(sourceFolder, fileName); + + File.Copy(sourcePath, Path.Combine(destinationFolder, fileName)); + } + } +} diff --git a/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectA.csproj b/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectA.csproj new file mode 100644 index 00000000000..aa6d648f1b1 --- /dev/null +++ b/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectA.csproj @@ -0,0 +1,25 @@ + + + + Library + net472 + + + + True + + + + ResXFileCodeGenerator + + + yue + Test.yue.resources + + + euy + Test.euy.resources + + + + diff --git a/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectB.csproj b/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectB.csproj new file mode 100644 index 00000000000..1daa05a8bc7 --- /dev/null +++ b/src/Tasks.UnitTests/TestResources/CustomCulture/ProjectB.csproj @@ -0,0 +1,19 @@ + + + + net472 + false + Library + OutputPathPlaceholder + EnableCustomCulturePlaceholder + + + + NonCultureResourceDirectoriesPlaceholder + + + + + + + diff --git a/src/Tasks.UnitTests/TestResources/CustomCulture/Test.euy.resx b/src/Tasks.UnitTests/TestResources/CustomCulture/Test.euy.resx new file mode 100644 index 00000000000..e6136e281c2 --- /dev/null +++ b/src/Tasks.UnitTests/TestResources/CustomCulture/Test.euy.resx @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/Tasks.UnitTests/TestResources/CustomCulture/Test.resx b/src/Tasks.UnitTests/TestResources/CustomCulture/Test.resx new file mode 100644 index 00000000000..e6136e281c2 --- /dev/null +++ b/src/Tasks.UnitTests/TestResources/CustomCulture/Test.resx @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/Tasks.UnitTests/TestResources/CustomCulture/Test.yue.resx b/src/Tasks.UnitTests/TestResources/CustomCulture/Test.yue.resx new file mode 100644 index 00000000000..e6136e281c2 --- /dev/null +++ b/src/Tasks.UnitTests/TestResources/CustomCulture/Test.yue.resx @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/src/Tasks/AssemblyDependency/ReferenceTable.cs b/src/Tasks/AssemblyDependency/ReferenceTable.cs index 9e3b0c07d36..5c611481085 100644 --- a/src/Tasks/AssemblyDependency/ReferenceTable.cs +++ b/src/Tasks/AssemblyDependency/ReferenceTable.cs @@ -135,7 +135,7 @@ internal sealed class ReferenceTable private readonly bool _doNotCopyLocalIfInGac; /// - /// Shoould the framework attribute version mismatch be ignored. + /// Should the framework attribute version mismatch be ignored. /// private readonly bool _ignoreFrameworkAttributeVersionMismatch; @@ -145,7 +145,17 @@ internal sealed class ReferenceTable private readonly GetAssemblyPathInGac _getAssemblyPathInGac; /// - /// Should a warning or error be emitted on architecture mismatch + /// Contains the list of directories that should NOT be considered as custom culture directories. + /// + private readonly string[] _nonCultureResourceDirectories = []; + + /// + /// Is true, custom culture processing is enabled. + /// + private readonly bool _enableCustomCulture = false; + + /// + /// Should a warning or error be emitted on architecture mismatch. /// private readonly WarnOrErrorOnTargetArchitectureMismatchBehavior _warnOrErrorOnTargetArchitectureMismatch = WarnOrErrorOnTargetArchitectureMismatchBehavior.Warning; @@ -174,6 +184,7 @@ internal sealed class ReferenceTable /// If true, then search for satellite files. /// If true, then search for serialization assembly files. /// If true, then search for related files. + /// If true, custom culture processing is enabled. /// Paths to search for dependent assemblies on. /// /// List of literal assembly file names to be considered when SearchPaths has {CandidateAssemblyFiles}. @@ -206,6 +217,7 @@ internal sealed class ReferenceTable /// /// /// + /// #else /// /// Construct. @@ -215,13 +227,14 @@ internal sealed class ReferenceTable /// If true, then search for satellite files. /// If true, then search for serialization assembly files. /// If true, then search for related files. + /// If true, custom culture processing is enabled. /// Paths to search for dependent assemblies on. /// /// List of literal assembly file names to be considered when SearchPaths has {CandidateAssemblyFiles}. /// Resolved sdk items /// Path to the FX. /// Installed assembly XML tables. - /// Like x86 or IA64\AMD64, the processor architecture being targetted. + /// Like x86 or IA64\AMD64, the processor architecture being targeted. /// Delegate used for checking for the existence of a file. /// Delegate used for files. /// Delegate used for getting directories. @@ -234,7 +247,7 @@ internal sealed class ReferenceTable /// Version of the runtime to target. /// Version of the framework targeted by the project. /// Target framework moniker we are targeting. - /// Logging helper to allow the logging of meessages from the Reference Table. + /// Logging helper to allow the logging of messages from the Reference Table. /// /// /// @@ -244,6 +257,7 @@ internal sealed class ReferenceTable /// /// /// + /// #endif internal ReferenceTable( IBuildEngine buildEngine, @@ -251,6 +265,7 @@ internal ReferenceTable( bool findSatellites, bool findSerializationAssemblies, bool findRelatedFiles, + bool enableCustomCulture, string[] searchPaths, string[] allowedAssemblyExtensions, string[] relatedFileExtensions, @@ -284,7 +299,8 @@ internal ReferenceTable( WarnOrErrorOnTargetArchitectureMismatchBehavior warnOrErrorOnTargetArchitectureMismatch, bool ignoreFrameworkAttributeVersionMismatch, bool unresolveFrameworkAssembliesFromHigherFrameworks, - ConcurrentDictionary assemblyMetadataCache) + ConcurrentDictionary assemblyMetadataCache, + string[] nonCultureResourceDirectories) { _log = log; _findDependencies = findDependencies; @@ -317,6 +333,8 @@ internal ReferenceTable( _warnOrErrorOnTargetArchitectureMismatch = warnOrErrorOnTargetArchitectureMismatch; _ignoreFrameworkAttributeVersionMismatch = ignoreFrameworkAttributeVersionMismatch; _assemblyMetadataCache = assemblyMetadataCache; + _nonCultureResourceDirectories = nonCultureResourceDirectories; + _enableCustomCulture = enableCustomCulture; // Set condition for when to check assembly version against the target framework version _checkAssemblyVersionAgainstTargetFrameworkVersion = unresolveFrameworkAssembliesFromHigherFrameworks || ((_projectTargetFramework ?? ReferenceTable.s_targetFrameworkVersion_40) <= ReferenceTable.s_targetFrameworkVersion_40); @@ -970,8 +988,9 @@ private void FindSatellites( // Is there a candidate satellite in that folder? string cultureName = Path.GetFileName(subDirectory); - // Custom or unknown cultures can be met as well - if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_14) || CultureInfoCache.IsValidCultureString(cultureName)) + // Custom or unknown cultures can be met only if the feature is enabled and the directory was not added to the exclusion list. + if ((_enableCustomCulture && !_nonCultureResourceDirectories.Contains(cultureName)) + || CultureInfoCache.IsValidCultureString(cultureName)) { string satelliteAssembly = Path.Combine(subDirectory, satelliteFilename); if (_fileExists(satelliteAssembly)) diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index a5a3ae00813..2feae38d440 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -177,8 +177,10 @@ internal static void Initialize(TaskLoggingHelper log) private ITaskItem[] _resolvedSDKReferences = Array.Empty(); private bool _ignoreDefaultInstalledAssemblyTables = false; private bool _ignoreDefaultInstalledAssemblySubsetTables = false; + private bool _enableCustomCulture = false; private string[] _candidateAssemblyFiles = []; private string[] _targetFrameworkDirectories = []; + private string[] _nonCultureResourceDirectories = []; private string[] _searchPaths = []; private string[] _allowedAssemblyExtensions = [".winmd", ".dll", ".exe"]; private string[] _relatedFileExtensions = [".pdb", ".xml", ".pri"]; @@ -422,6 +424,24 @@ public string[] TargetFrameworkDirectories set { _targetFrameworkDirectories = value; } } + /// + /// Contains list of directories that point to custom culture resources that has to be ignored by MSBuild. + /// + public string[] NonCultureResourceDirectories + { + get { return _nonCultureResourceDirectories; } + set { _nonCultureResourceDirectories = value; } + } + + /// + /// Contains the information if custom culture is enabled. + /// + public bool EnableCustomCulture + { + get { return _enableCustomCulture; } + set { _enableCustomCulture = value; } + } + /// /// A list of XML files that contain assemblies that are expected to be installed on the target machine. /// @@ -1511,7 +1531,10 @@ private void LogInputs() } Log.LogMessage(importance, property, "TargetFrameworkDirectories"); - Log.LogMessage(importance, indent + String.Join(",", TargetFrameworkDirectories)); + Log.LogMessage(importance, indent + string.Join(",", TargetFrameworkDirectories)); + + Log.LogMessage(importance, property, "NonCultureResourceDirectories"); + Log.LogMessage(importance, indent + string.Join(",", NonCultureResourceDirectories)); Log.LogMessage(importance, property, "InstalledAssemblyTables"); foreach (ITaskItem installedAssemblyTable in InstalledAssemblyTables) @@ -1547,6 +1570,9 @@ private void LogInputs() Log.LogMessage(importance, property, "AutoUnify"); Log.LogMessage(importance, $"{indent}{AutoUnify}"); + Log.LogMessage(importance, property, "EnableCustomCulture"); + Log.LogMessage(importance, $"{indent}{EnableCustomCulture}"); + Log.LogMessage(importance, property, "CopyLocalDependenciesWhenParentReferenceInGac"); Log.LogMessage(importance, $"{indent}{_copyLocalDependenciesWhenParentReferenceInGac}"); @@ -2386,6 +2412,7 @@ internal bool Execute( _findSatellites, _findSerializationAssemblies, _findRelatedFiles, + _enableCustomCulture, _searchPaths, _allowedAssemblyExtensions, _relatedFileExtensions, @@ -2419,7 +2446,8 @@ internal bool Execute( _warnOrErrorOnTargetArchitectureMismatch, _ignoreTargetFrameworkAttributeVersionMismatch, _unresolveFrameworkAssembliesFromHigherFrameworks, - assemblyMetadataCache); + assemblyMetadataCache, + _nonCultureResourceDirectories); dependencyTable.FindDependenciesOfExternallyResolvedReferences = FindDependenciesOfExternallyResolvedReferences; diff --git a/src/Tasks/CreateCSharpManifestResourceName.cs b/src/Tasks/CreateCSharpManifestResourceName.cs index 6f3b6b5a06d..c7f838b16ef 100644 --- a/src/Tasks/CreateCSharpManifestResourceName.cs +++ b/src/Tasks/CreateCSharpManifestResourceName.cs @@ -51,7 +51,7 @@ protected override string CreateManifestName( Actual implementation is in a static method called CreateManifestNameImpl. The reason is that CreateManifestName can't be static because it is an override of a method declared in the base class, but its convenient - to expose a static version anyway for unittesting purposes. + to expose a static version anyway for unit testing purposes. */ return CreateManifestNameImpl( fileName, @@ -62,7 +62,8 @@ The reason is that CreateManifestName can't be static because it is an culture, binaryStream, Log, - treatAsCultureNeutral); + treatAsCultureNeutral, + EnableCustomCulture); } /// @@ -81,6 +82,7 @@ The reason is that CreateManifestName can't be static because it is an /// File contents binary stream, may be null /// Task's TaskLoggingHelper, for logging warnings or errors /// Whether to treat the current file as 'culture-neutral' and retain the culture in the name. + /// Whether custom culture handling is expected. /// Returns the manifest name internal static string CreateManifestNameImpl( string fileName, @@ -91,7 +93,8 @@ internal static string CreateManifestNameImpl( string culture, // may be null Stream binaryStream, // File contents binary stream, may be null TaskLoggingHelper log, - bool treatAsCultureNeutral = false) + bool treatAsCultureNeutral = false, + bool enableCustomCulture = false) { // Use the link file name if there is one, otherwise, fall back to file name. string embeddedFileName = FileUtilities.FixFilePath(linkFileName); @@ -103,13 +106,12 @@ internal static string CreateManifestNameImpl( dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; - if (!string.IsNullOrEmpty(culture) && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_14)) + if (!string.IsNullOrEmpty(culture) && enableCustomCulture) { info = new Culture.ItemCultureInfo() { culture = culture, - cultureNeutralFilename = - embeddedFileName.RemoveLastInstanceOf("." + culture, StringComparison.OrdinalIgnoreCase), + cultureNeutralFilename = embeddedFileName.RemoveLastInstanceOf("." + culture, StringComparison.OrdinalIgnoreCase), }; } else diff --git a/src/Tasks/CreateManifestResourceName.cs b/src/Tasks/CreateManifestResourceName.cs index 934d67c6a68..d974b1a8d1c 100644 --- a/src/Tasks/CreateManifestResourceName.cs +++ b/src/Tasks/CreateManifestResourceName.cs @@ -28,6 +28,8 @@ public abstract class CreateManifestResourceName : TaskExtension private ITaskItem[] _resourceFiles; + private bool _enableCustomCulture; + [SuppressMessage("Microsoft.Design", "CA1051:DoNotDeclareVisibleInstanceFields", Justification = "Shipped this way in Dev11 Beta (go-live)")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Taskitem", Justification = "Shipped this way in Dev11 Beta (go-live)")] protected Dictionary itemSpecToTaskitem = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -56,6 +58,15 @@ public ITaskItem[] ResourceFiles set => _resourceFiles = value; } + /// + /// Contains the information if custom culture is enabled. + /// + public bool EnableCustomCulture + { + get { return _enableCustomCulture; } + set { _enableCustomCulture = value; } + } + /// /// Rootnamespace to use for naming. /// diff --git a/src/Tasks/CreateVisualBasicManifestResourceName.cs b/src/Tasks/CreateVisualBasicManifestResourceName.cs index 0115685336f..d2cf7f405ef 100644 --- a/src/Tasks/CreateVisualBasicManifestResourceName.cs +++ b/src/Tasks/CreateVisualBasicManifestResourceName.cs @@ -50,7 +50,7 @@ protected override string CreateManifestName( Actual implementation is in a static method called CreateManifestNameImpl. The reason is that CreateManifestName can't be static because it is an override of a method declared in the base class, but its convenient - to expose a static version anyway for unittesting purposes. + to expose a static version anyway for unit testing purposes. */ return CreateManifestNameImpl( fileName, @@ -61,7 +61,8 @@ The reason is that CreateManifestName can't be static because it is an culture, binaryStream, Log, - treatAsCultureNeutral); + treatAsCultureNeutral, + EnableCustomCulture); } /// @@ -80,6 +81,7 @@ The reason is that CreateManifestName can't be static because it is an /// File contents binary stream, may be null /// Task's TaskLoggingHelper, for logging warnings or errors /// Whether to treat the current file as 'culture-neutral' and retain the culture in the name. + /// Whether custom culture handling is expected. /// Returns the manifest name internal static string CreateManifestNameImpl( string fileName, @@ -90,7 +92,8 @@ internal static string CreateManifestNameImpl( string culture, Stream binaryStream, // File contents binary stream, may be null TaskLoggingHelper log, - bool treatAsCultureNeutral = false) + bool treatAsCultureNeutral = false, + bool enableCustomCulture = false) { // Use the link file name if there is one, otherwise, fall back to file name. string embeddedFileName = linkFileName; @@ -102,13 +105,12 @@ internal static string CreateManifestNameImpl( dependentUponFileName = FileUtilities.FixFilePath(dependentUponFileName); Culture.ItemCultureInfo info; - if (!string.IsNullOrEmpty(culture) && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_14)) + if (!string.IsNullOrEmpty(culture) && enableCustomCulture) { info = new Culture.ItemCultureInfo() { culture = culture, - cultureNeutralFilename = - embeddedFileName.RemoveLastInstanceOf("." + culture, StringComparison.OrdinalIgnoreCase) + cultureNeutralFilename = embeddedFileName.RemoveLastInstanceOf("." + culture, StringComparison.OrdinalIgnoreCase), }; } else diff --git a/src/Tasks/Microsoft.CSharp.CurrentVersion.targets b/src/Tasks/Microsoft.CSharp.CurrentVersion.targets index 60045885791..0280966ef15 100644 --- a/src/Tasks/Microsoft.CSharp.CurrentVersion.targets +++ b/src/Tasks/Microsoft.CSharp.CurrentVersion.targets @@ -100,6 +100,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 48b9de51827..0c4ce55ad13 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -2412,6 +2412,15 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + + + + + + false + + - + false - + - + - + https://github.com/dotnet/source-build-reference-packages - 6968f7059f4418e985febe704a3b1320f9e5887d + 643689c88b1d5a0f1561383972c4189a0c673abe From 6b3f65a09778ed715491bdcfcfb924fc5e56f3d0 Mon Sep 17 00:00:00 2001 From: Christian Castaneda Date: Fri, 4 Apr 2025 17:33:49 -0700 Subject: [PATCH 067/106] Add Microsoft.Bcl.AsyncInterfaces to pre-builts --- eng/SourceBuildPrebuiltBaseline.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml index 6bf7dfcbf3a..a3ccc3b79c9 100644 --- a/eng/SourceBuildPrebuiltBaseline.xml +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -24,6 +24,8 @@ + + From 60e11f72321a7e0a90c97e02a3bb82ece4f9a4a9 Mon Sep 17 00:00:00 2001 From: Christian Castaneda Date: Fri, 4 Apr 2025 17:37:35 -0700 Subject: [PATCH 068/106] Restore ValueTask --- src/Shared/NodePipeBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Shared/NodePipeBase.cs b/src/Shared/NodePipeBase.cs index a824bc0aca3..fd1d08efe9c 100644 --- a/src/Shared/NodePipeBase.cs +++ b/src/Shared/NodePipeBase.cs @@ -226,7 +226,7 @@ private int Read(byte[] buffer, int bytesToRead) } #if !TASKHOST - private async Task ReadAsync(byte[] buffer, int bytesToRead, CancellationToken cancellationToken) + private async ValueTask ReadAsync(byte[] buffer, int bytesToRead, CancellationToken cancellationToken) { int totalBytesRead = 0; while (totalBytesRead < bytesToRead) From 9c85f564934808334c1c439ed87ed0b84212b38f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:25:44 +0200 Subject: [PATCH 069/106] Update dependencies from https://github.com/dotnet/roslyn build 20250402.5 (#11695) --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 8b7069ed8aa..2645da122e9 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -141,13 +141,13 @@ https://github.com/nuget/nuget.client 8f6362aea4972acab1454de411cfe835619e4e41 - + https://github.com/dotnet/roslyn - 304768b76e90f5d224b745e3a03cfc5e9509baf6 + f76d6ab7fa6310b6cda343419aa7bf9ee2df8e8e - + https://github.com/dotnet/roslyn - 304768b76e90f5d224b745e3a03cfc5e9509baf6 + f76d6ab7fa6310b6cda343419aa7bf9ee2df8e8e diff --git a/eng/Versions.props b/eng/Versions.props index f3f7238a2bc..08ce7af0140 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -74,7 +74,7 @@ $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) 4.2.0-1.22102.8 9.0.0-beta.25164.2 - 4.14.0-3.25179.1 + 4.14.0-3.25202.5 6.14.0-preview.1.97 From 96f09e23c2945c9004789df072aa0a0e9732f46e Mon Sep 17 00:00:00 2001 From: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:51:19 +0200 Subject: [PATCH 070/106] update BuildCheck code BC0109 -> BC0302 (#11696) --- documentation/specs/BuildCheck/Codes.md | 18 ++++++++---------- .../BuildCheck/Checks/ExecCliBuildCheck.cs | 6 +++--- src/Build/Resources/Strings.resx | 4 ++-- src/Build/Resources/xlf/Strings.cs.xlf | 4 ++-- src/Build/Resources/xlf/Strings.de.xlf | 4 ++-- src/Build/Resources/xlf/Strings.es.xlf | 4 ++-- src/Build/Resources/xlf/Strings.fr.xlf | 4 ++-- src/Build/Resources/xlf/Strings.it.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ja.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ko.xlf | 4 ++-- src/Build/Resources/xlf/Strings.pl.xlf | 4 ++-- src/Build/Resources/xlf/Strings.pt-BR.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ru.xlf | 4 ++-- src/Build/Resources/xlf/Strings.tr.xlf | 6 +++--- src/Build/Resources/xlf/Strings.zh-Hans.xlf | 4 ++-- src/Build/Resources/xlf/Strings.zh-Hant.xlf | 4 ++-- .../ExecCliBuildCheck_Tests.cs | 2 +- 17 files changed, 41 insertions(+), 43 deletions(-) diff --git a/documentation/specs/BuildCheck/Codes.md b/documentation/specs/BuildCheck/Codes.md index 892e17a178a..2f0d197c2b1 100644 --- a/documentation/specs/BuildCheck/Codes.md +++ b/documentation/specs/BuildCheck/Codes.md @@ -12,12 +12,11 @@ Report codes are chosen to conform to suggested guidelines. Those guidelines are | [BC0106](#bc0106---copytooutputdirectoryalways-should-be-avoided) | Warning | N/A | 9.0.200 | CopyToOutputDirectory='Always' should be avoided. | | [BC0107](#bc0107---targetframework-and-targetframeworks-specified-together) | Warning | N/A | 9.0.200 | TargetFramework and TargetFrameworks specified together. | | [BC0108](#bc0108---targetframework-or-targetframeworks-specified-in-non-sdk-style-project) | Warning | N/A | 9.0.300 | TargetFramework or TargetFrameworks specified in non-SDK style project. | -| [BC0109](#bc0109---building-using-the-exec-task) | Warning | N/A | 9.0.300 | Building using the Exec task. | | [BC0201](#bc0201---usage-of-undefined-property) | Warning | Project | 9.0.100 | Usage of undefined property. | | [BC0202](#bc0202---property-first-declared-after-it-was-used) | Warning | Project | 9.0.100 | Property first declared after it was used. | | [BC0203](#bc0203----property-declared-but-never-used) | None | Project | 9.0.100 | Property declared but never used. | | [BC0301](#bc0301---building-from-downloads-folder) | None | Project | 9.0.300 | Building from Downloads folder. | - +| [BC0302](#bc0302---building-using-the-exec-task) | Warning | N/A | 9.0.300 | Building using the Exec task. | Notes: * What does the 'N/A' scope mean? The scope of checks are only applicable and configurable in cases where evaluation-time data are being used and the source of the data is determinable and available. Otherwise the scope of whole build is always checked. @@ -138,14 +137,6 @@ dotnet build my-multi-target.csproj /p:TargetFramework=net9.0 Make sure the Target Framework is specified appropriately for your project. - -## BC0109 - Building using the Exec task. - -"The 'Exec' task should not be used to build projects." - -Building projects using the dotnet/msbuild/nuget CLI in the `Exec` task is not recommended, as it spawns a separate build process that the MSBuild engine cannot track. Please use the [MSBuild task](https://learn.microsoft.com/visualstudio/msbuild/msbuild-task) instead. - - ## BC0201 - Usage of undefined property. @@ -205,6 +196,13 @@ Placing project files into Downloads folder (or any other folder that cannot be Place your projects into trusted locations - including cases when you intend to only open the project in IDE. + +## BC0302 - Building using the Exec task. + +"The 'Exec' task should not be used to build projects." + +Building projects using the dotnet/msbuild/nuget CLI in the `Exec` task is not recommended, as it spawns a separate build process that the MSBuild engine cannot track. Please use the [MSBuild task](https://learn.microsoft.com/visualstudio/msbuild/msbuild-task) instead. +


diff --git a/src/Build/BuildCheck/Checks/ExecCliBuildCheck.cs b/src/Build/BuildCheck/Checks/ExecCliBuildCheck.cs index 868827b24cc..252159162b2 100644 --- a/src/Build/BuildCheck/Checks/ExecCliBuildCheck.cs +++ b/src/Build/BuildCheck/Checks/ExecCliBuildCheck.cs @@ -17,10 +17,10 @@ namespace Microsoft.Build.Experimental.BuildCheck.Checks; internal sealed class ExecCliBuildCheck : Check { public static CheckRule SupportedRule = new CheckRule( - "BC0109", + "BC0302", "ExecCliBuild", - ResourceUtilities.GetResourceString("BuildCheck_BC0109_Title")!, - ResourceUtilities.GetResourceString("BuildCheck_BC0109_MessageFmt")!, + ResourceUtilities.GetResourceString("BuildCheck_BC0302_Title")!, + ResourceUtilities.GetResourceString("BuildCheck_BC0302_MessageFmt")!, new CheckConfiguration() { Severity = CheckResultSeverity.Warning }); private const string ExecTaskName = "Exec"; diff --git a/src/Build/Resources/Strings.resx b/src/Build/Resources/Strings.resx index f36cdbcde75..314b1b6a53c 100644 --- a/src/Build/Resources/Strings.resx +++ b/src/Build/Resources/Strings.resx @@ -2206,10 +2206,10 @@ Utilization: {0} Average Utilization: {1:###.0} Project {0} specifies 'TargetFramework(s)' property '{1}', which does not use the .NET SDK. Those properties are not understood by projects that import C# targets directly. Terms in quotes are not to be translated. - + The 'Exec' task should not be used to build a project. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. diff --git a/src/Build/Resources/xlf/Strings.cs.xlf b/src/Build/Resources/xlf/Strings.cs.xlf index 6c5929e74ff..2c2d11588ac 100644 --- a/src/Build/Resources/xlf/Strings.cs.xlf +++ b/src/Build/Resources/xlf/Strings.cs.xlf @@ -221,12 +221,12 @@ Vlastnosti TargetFramework a TargetFrameworks se nedodržují a neměly by se zadává v projektech, které nepoužívají sadu .NET SDK. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. Úloha {0} z projektu {1} sestaví projekt pomocí rozhraní příkazového řádku {2}. Místo toho by se měla použít úloha MSBuild. - + The 'Exec' task should not be used to build a project. Úloha Exec by se neměla používat k sestavení projektu. diff --git a/src/Build/Resources/xlf/Strings.de.xlf b/src/Build/Resources/xlf/Strings.de.xlf index 9951ccc9819..034f434df51 100644 --- a/src/Build/Resources/xlf/Strings.de.xlf +++ b/src/Build/Resources/xlf/Strings.de.xlf @@ -221,12 +221,12 @@ Die Eigenschaften "TargetFramework" und "TargetFrameworks" werden nicht berücksichtigt und sollten nicht in Projekten angegeben werden, die nicht das .NET SDK verwenden. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. Die {0}-Aufgabe aus dem Projekt {1} erstellt ein Projekt mithilfe der {2} CLI. Stattdessen sollte die MSBuild-Aufgabe verwendet werden. - + The 'Exec' task should not be used to build a project. Die "Ausführen"-Aufgabe sollte nicht zum Erstellen eines Projekts verwendet werden. diff --git a/src/Build/Resources/xlf/Strings.es.xlf b/src/Build/Resources/xlf/Strings.es.xlf index 461284cccbf..c6cbcee8297 100644 --- a/src/Build/Resources/xlf/Strings.es.xlf +++ b/src/Build/Resources/xlf/Strings.es.xlf @@ -221,12 +221,12 @@ Las propiedades "TargetFramework" y "TargetFrameworks" no se respetan y no deben especificarse en proyectos que no usen el SDK de .NET. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. La tarea {0} del proyecto {1} compila un proyecto mediante la CLI {2}. En su lugar, se debe usar la tarea MSBuild. - + The 'Exec' task should not be used to build a project. La tarea "Exec" no debe usarse para compilar un proyecto. diff --git a/src/Build/Resources/xlf/Strings.fr.xlf b/src/Build/Resources/xlf/Strings.fr.xlf index 99ec50ff39a..572dfbb0d28 100644 --- a/src/Build/Resources/xlf/Strings.fr.xlf +++ b/src/Build/Resources/xlf/Strings.fr.xlf @@ -221,12 +221,12 @@ Les propriétés 'TargetFramework' et 'TargetFrameworks' ne sont pas respectées et ne doivent pas être spécifiées dans les projets qui n’utilisent pas le SDK .NET. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. La tâche {0} du projet {1} crée un projet en utilisant l’interface CLI {2}. Vous devez utiliser la tâche MSBuild à la place. - + The 'Exec' task should not be used to build a project. Vous ne devez pas utiliser la tâche « Exec¯ pour construire un projet. diff --git a/src/Build/Resources/xlf/Strings.it.xlf b/src/Build/Resources/xlf/Strings.it.xlf index 106c3e30903..abb7d0613fd 100644 --- a/src/Build/Resources/xlf/Strings.it.xlf +++ b/src/Build/Resources/xlf/Strings.it.xlf @@ -221,12 +221,12 @@ Le proprietà 'TargetFramework' e 'TargetFrameworks' non vengono rispettate e non devono essere specificate nei progetti che non usano .NET SDK. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. L'attività {0} del progetto{1} compila un progetto utilizzando la CLI {2}. È necessario utilizzare invece l'attività MSBuild. - + The 'Exec' task should not be used to build a project. L'attività 'Esecuzione' non deve essere utilizzata per compilare un progetto. diff --git a/src/Build/Resources/xlf/Strings.ja.xlf b/src/Build/Resources/xlf/Strings.ja.xlf index 6d9ae65fc13..337cf87e8c8 100644 --- a/src/Build/Resources/xlf/Strings.ja.xlf +++ b/src/Build/Resources/xlf/Strings.ja.xlf @@ -221,12 +221,12 @@ 'TargetFramework' プロパティと 'TargetFrameworks' プロパティは優先されないため、.NET SDK を使用しないプロジェクトでは指定しないでください。 Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. プロジェクト {1} のタスク {0} は、{2} CLI を使用してプロジェクトをビルドします。代わりに MSBuild タスクを使用する必要があります。 - + The 'Exec' task should not be used to build a project. 'Exec' タスクはプロジェクトのビルドには使用しないでください。 diff --git a/src/Build/Resources/xlf/Strings.ko.xlf b/src/Build/Resources/xlf/Strings.ko.xlf index b4a2bf31864..ab55a29636d 100644 --- a/src/Build/Resources/xlf/Strings.ko.xlf +++ b/src/Build/Resources/xlf/Strings.ko.xlf @@ -221,12 +221,12 @@ 'TargetFramework' 및 'TargetFrameworks' 속성은 사용되지 않으며 .NET SDK를 사용하지 않는 프로젝트에서 지정해서는 안 됩니다. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. 프로젝트 {1}의 작업 {0}(은)는 {2} CLI를 사용하여 프로젝트를 빌드합니다. 대신 MSBuild 작업을 사용해야 합니다. - + The 'Exec' task should not be used to build a project. 'Exec' 작업은 프로젝트를 빌드하는 데 사용하면 안 됩니다. diff --git a/src/Build/Resources/xlf/Strings.pl.xlf b/src/Build/Resources/xlf/Strings.pl.xlf index da05fb5c78c..31b1ff023ce 100644 --- a/src/Build/Resources/xlf/Strings.pl.xlf +++ b/src/Build/Resources/xlf/Strings.pl.xlf @@ -221,12 +221,12 @@ Właściwości "TargetFramework" i "TargetFrameworks" nie są respektowane i nie należy ich określać w projektach, w których nie jest używany zestaw .NET SDK. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. Zadanie {0} z projektu {1} tworzy projekt przy użyciu interfejsu wiersza polecenia {2}. Zamiast tego należy użyć zadania MSBuild. - + The 'Exec' task should not be used to build a project. Zadanie „Exec” nie powinno być używane do kompilowania projektu. diff --git a/src/Build/Resources/xlf/Strings.pt-BR.xlf b/src/Build/Resources/xlf/Strings.pt-BR.xlf index 655ef691ea7..6d361e55d57 100644 --- a/src/Build/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Build/Resources/xlf/Strings.pt-BR.xlf @@ -221,12 +221,12 @@ As propriedades 'TargetFramework' e 'TargetFrameworks' não são respeitadas e não devem ser especificadas em projetos que não usam o SDK do .NET. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. A tarefa {0} do projeto {1} cria um projeto usando a CLI {2}. Em vez disso, a tarefa do MSBuild deve ser usada. - + The 'Exec' task should not be used to build a project. A tarefa "Exec" não deve ser usada para criar um projeto. diff --git a/src/Build/Resources/xlf/Strings.ru.xlf b/src/Build/Resources/xlf/Strings.ru.xlf index 09e2130e169..c6064233811 100644 --- a/src/Build/Resources/xlf/Strings.ru.xlf +++ b/src/Build/Resources/xlf/Strings.ru.xlf @@ -221,12 +221,12 @@ Свойства TargetFramework и TargetFrameworks не учитываются и не должны указываться в проектах, не использующих пакет SDK для .NET. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. Задача {0} из проекта {1} создает проект с помощью CLI {2}. Вместо этого следует использовать задачу MSBuild. - + The 'Exec' task should not be used to build a project. Задачу "Exec" не следует использовать для создания проекта. diff --git a/src/Build/Resources/xlf/Strings.tr.xlf b/src/Build/Resources/xlf/Strings.tr.xlf index 97d99cfdc09..cd05ebfcae1 100644 --- a/src/Build/Resources/xlf/Strings.tr.xlf +++ b/src/Build/Resources/xlf/Strings.tr.xlf @@ -221,12 +221,12 @@ 'TargetFramework' ve 'TargetFrameworks' özellikleri dikkate alınmaz ve .NET SDK kullanmayan projelerde belirtilmeli. Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. {1} projesindeki {0} görevi {2} CLI kullanılan bir proje oluşturur. Bunun yerine MSBuild görevi kullanılmalıdır. - + The 'Exec' task should not be used to build a project. 'Exec' görevi, bir projeyi oluşturmak için kullanılmamalıdır. @@ -357,7 +357,7 @@ MSB4252: Genel özelliklerle sahip "{0}" projesi ({1}) - ({4}) hedefle + ({4}) hedefle ({3}) genel özelliklere sahip "{2}" projesini oluşturuyor ancak oluşturulan projeye yönelik derleme sonucu, altyapı önbelleğinde değil. Bu, yalıtılmış derlemelerde aşağıdakilerden biri anlamına gelebilir: - Başvuru, "{0}" projesindeki ProjectReferenceTargets öğesinde belirtilmeyen bir hedefle çağrıldı diff --git a/src/Build/Resources/xlf/Strings.zh-Hans.xlf b/src/Build/Resources/xlf/Strings.zh-Hans.xlf index dee94c2daab..ab54e811299 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hans.xlf @@ -221,12 +221,12 @@ 不考虑 “TargetFramework” 和 “TargetFrameworks” 属性,不应在不使用 .NET SDK 的项目中指定这些属性。 Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. 项目 {1} 中的任务 {0} 使用 {2} CLI 生成项目。应改用 MSBuild 任务。 - + The 'Exec' task should not be used to build a project. 不应使用 "Exec" 任务来生成项目。 diff --git a/src/Build/Resources/xlf/Strings.zh-Hant.xlf b/src/Build/Resources/xlf/Strings.zh-Hant.xlf index 547ade36856..98a8f314606 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hant.xlf @@ -221,12 +221,12 @@ 未遵守 『TargetFramework』 和 『TargetFrameworks』 屬性,且不應在未使用 .NET SDK 的專案中指定。 Terms in quotes are not to be translated. - + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. 來自專案 {1} 的工作 {0} 使用 {2} CLI 建置專案。建議改用 MSBuild 工作。 - + The 'Exec' task should not be used to build a project. 不應該使用 'Exec' 工作來建置專案。 diff --git a/src/BuildCheck.UnitTests/ExecCliBuildCheck_Tests.cs b/src/BuildCheck.UnitTests/ExecCliBuildCheck_Tests.cs index edbb0e6bdc9..105ba97f9e1 100644 --- a/src/BuildCheck.UnitTests/ExecCliBuildCheck_Tests.cs +++ b/src/BuildCheck.UnitTests/ExecCliBuildCheck_Tests.cs @@ -74,7 +74,7 @@ public void ExecTask_WithCommandExecutingBuild_ShouldShowWarning(string? command })); _registrationContext.Results.Count.ShouldBe(1); - _registrationContext.Results[0].CheckRule.Id.ShouldBe("BC0109"); + _registrationContext.Results[0].CheckRule.Id.ShouldBe("BC0302"); } [Theory] From 85cb0073ac351e2740b514b3f999659ab1f73500 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:04:46 +0000 Subject: [PATCH 071/106] Update dependencies from https://github.com/nuget/nuget.client build 6.14.0.102 (#11689) NuGet.Build.Tasks From Version 6.14.0-preview.1.97 -> To Version 6.14.0-preview.1.102 Co-authored-by: dotnet-maestro[bot] Co-authored-by: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com> --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2645da122e9..e7d02905bdc 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -137,9 +137,9 @@ https://github.com/dotnet/arcade 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee
- + https://github.com/nuget/nuget.client - 8f6362aea4972acab1454de411cfe835619e4e41 + 7f50923823cb8fe4dab9b6565ece9516407de498 https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index 08ce7af0140..5e11bd7a2df 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -75,7 +75,7 @@ 4.2.0-1.22102.8 9.0.0-beta.25164.2 4.14.0-3.25202.5 - 6.14.0-preview.1.97 + 6.14.0-preview.1.102 9.0.200-preview.0.24603.3 From e135d3cffb5acbf8692ae871c165216576030189 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 10:09:34 +0000 Subject: [PATCH 072/106] Update dependencies from https://github.com/dotnet/arcade build 20250404.5 (#11690) Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.25164.2 -> To Version 9.0.0-beta.25204.5 Co-authored-by: dotnet-maestro[bot] Co-authored-by: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com> --- eng/Version.Details.xml | 16 ++++++++-------- eng/Versions.props | 2 +- global.json | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index e7d02905bdc..e58d82be9ba 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -123,19 +123,19 @@ - + https://github.com/dotnet/arcade - 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee + 97cbc7361ff28b2948c8182720c166a744049f55 - + https://github.com/dotnet/arcade - 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee + 97cbc7361ff28b2948c8182720c166a744049f55 - + https://github.com/dotnet/arcade - 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee + 97cbc7361ff28b2948c8182720c166a744049f55 https://github.com/nuget/nuget.client @@ -150,9 +150,9 @@ f76d6ab7fa6310b6cda343419aa7bf9ee2df8e8e - + https://github.com/dotnet/arcade - 5ba9ca776c1d0bb72b2791591e54cf51fc52dfee + 97cbc7361ff28b2948c8182720c166a744049f55 diff --git a/eng/Versions.props b/eng/Versions.props index 5e11bd7a2df..2677a23f518 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -73,7 +73,7 @@ Otherwise, this version of dotnet will not be installed and the build will error out. --> $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) 4.2.0-1.22102.8 - 9.0.0-beta.25164.2 + 9.0.0-beta.25204.5 4.14.0-3.25202.5 6.14.0-preview.1.102 diff --git a/global.json b/global.json index e2e3c22ae34..0ccd947b32d 100644 --- a/global.json +++ b/global.json @@ -10,6 +10,6 @@ "xcopy-msbuild": "17.12.0" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25164.2" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25204.5" } } From c2bc2836d5718fd647917dd202665725cf059fd6 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Mon, 7 Apr 2025 04:09:44 -0700 Subject: [PATCH 073/106] Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11350274 (#11700) --- src/Build/Resources/xlf/Strings.cs.xlf | 4 ++-- src/Build/Resources/xlf/Strings.de.xlf | 4 ++-- src/Build/Resources/xlf/Strings.es.xlf | 4 ++-- src/Build/Resources/xlf/Strings.fr.xlf | 4 ++-- src/Build/Resources/xlf/Strings.it.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ja.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ko.xlf | 4 ++-- src/Build/Resources/xlf/Strings.pl.xlf | 4 ++-- src/Build/Resources/xlf/Strings.pt-BR.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ru.xlf | 4 ++-- src/Build/Resources/xlf/Strings.tr.xlf | 6 +++--- src/Build/Resources/xlf/Strings.zh-Hans.xlf | 4 ++-- src/Build/Resources/xlf/Strings.zh-Hant.xlf | 4 ++-- 13 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/Build/Resources/xlf/Strings.cs.xlf b/src/Build/Resources/xlf/Strings.cs.xlf index 2c2d11588ac..d65294cfc54 100644 --- a/src/Build/Resources/xlf/Strings.cs.xlf +++ b/src/Build/Resources/xlf/Strings.cs.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Úloha {0} z projektu {1} sestaví projekt pomocí rozhraní příkazového řádku {2}. Místo toho by se měla použít úloha MSBuild. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - Úloha Exec by se neměla používat k sestavení projektu. + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.de.xlf b/src/Build/Resources/xlf/Strings.de.xlf index 034f434df51..b45278e6555 100644 --- a/src/Build/Resources/xlf/Strings.de.xlf +++ b/src/Build/Resources/xlf/Strings.de.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Die {0}-Aufgabe aus dem Projekt {1} erstellt ein Projekt mithilfe der {2} CLI. Stattdessen sollte die MSBuild-Aufgabe verwendet werden. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - Die "Ausführen"-Aufgabe sollte nicht zum Erstellen eines Projekts verwendet werden. + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.es.xlf b/src/Build/Resources/xlf/Strings.es.xlf index c6cbcee8297..f02802a5777 100644 --- a/src/Build/Resources/xlf/Strings.es.xlf +++ b/src/Build/Resources/xlf/Strings.es.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - La tarea {0} del proyecto {1} compila un proyecto mediante la CLI {2}. En su lugar, se debe usar la tarea MSBuild. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - La tarea "Exec" no debe usarse para compilar un proyecto. + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.fr.xlf b/src/Build/Resources/xlf/Strings.fr.xlf index 572dfbb0d28..cd50b6c190d 100644 --- a/src/Build/Resources/xlf/Strings.fr.xlf +++ b/src/Build/Resources/xlf/Strings.fr.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - La tâche {0} du projet {1} crée un projet en utilisant l’interface CLI {2}. Vous devez utiliser la tâche MSBuild à la place. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - Vous ne devez pas utiliser la tâche « Exec¯ pour construire un projet. + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.it.xlf b/src/Build/Resources/xlf/Strings.it.xlf index abb7d0613fd..f387aa31050 100644 --- a/src/Build/Resources/xlf/Strings.it.xlf +++ b/src/Build/Resources/xlf/Strings.it.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - L'attività {0} del progetto{1} compila un progetto utilizzando la CLI {2}. È necessario utilizzare invece l'attività MSBuild. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - L'attività 'Esecuzione' non deve essere utilizzata per compilare un progetto. + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.ja.xlf b/src/Build/Resources/xlf/Strings.ja.xlf index 337cf87e8c8..18fc679e249 100644 --- a/src/Build/Resources/xlf/Strings.ja.xlf +++ b/src/Build/Resources/xlf/Strings.ja.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - プロジェクト {1} のタスク {0} は、{2} CLI を使用してプロジェクトをビルドします。代わりに MSBuild タスクを使用する必要があります。 + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - 'Exec' タスクはプロジェクトのビルドには使用しないでください。 + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.ko.xlf b/src/Build/Resources/xlf/Strings.ko.xlf index ab55a29636d..495d5ef06de 100644 --- a/src/Build/Resources/xlf/Strings.ko.xlf +++ b/src/Build/Resources/xlf/Strings.ko.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - 프로젝트 {1}의 작업 {0}(은)는 {2} CLI를 사용하여 프로젝트를 빌드합니다. 대신 MSBuild 작업을 사용해야 합니다. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - 'Exec' 작업은 프로젝트를 빌드하는 데 사용하면 안 됩니다. + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.pl.xlf b/src/Build/Resources/xlf/Strings.pl.xlf index 31b1ff023ce..b2055495b08 100644 --- a/src/Build/Resources/xlf/Strings.pl.xlf +++ b/src/Build/Resources/xlf/Strings.pl.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Zadanie {0} z projektu {1} tworzy projekt przy użyciu interfejsu wiersza polecenia {2}. Zamiast tego należy użyć zadania MSBuild. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - Zadanie „Exec” nie powinno być używane do kompilowania projektu. + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.pt-BR.xlf b/src/Build/Resources/xlf/Strings.pt-BR.xlf index 6d361e55d57..2ead1a82e33 100644 --- a/src/Build/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Build/Resources/xlf/Strings.pt-BR.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - A tarefa {0} do projeto {1} cria um projeto usando a CLI {2}. Em vez disso, a tarefa do MSBuild deve ser usada. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - A tarefa "Exec" não deve ser usada para criar um projeto. + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.ru.xlf b/src/Build/Resources/xlf/Strings.ru.xlf index c6064233811..28f22bedb3b 100644 --- a/src/Build/Resources/xlf/Strings.ru.xlf +++ b/src/Build/Resources/xlf/Strings.ru.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Задача {0} из проекта {1} создает проект с помощью CLI {2}. Вместо этого следует использовать задачу MSBuild. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - Задачу "Exec" не следует использовать для создания проекта. + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.tr.xlf b/src/Build/Resources/xlf/Strings.tr.xlf index cd05ebfcae1..352a31c2244 100644 --- a/src/Build/Resources/xlf/Strings.tr.xlf +++ b/src/Build/Resources/xlf/Strings.tr.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - {1} projesindeki {0} görevi {2} CLI kullanılan bir proje oluşturur. Bunun yerine MSBuild görevi kullanılmalıdır. + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - 'Exec' görevi, bir projeyi oluşturmak için kullanılmamalıdır. + The 'Exec' task should not be used to build a project. @@ -357,7 +357,7 @@ MSB4252: Genel özelliklerle sahip "{0}" projesi ({1}) - ({4}) hedefle + ({4}) hedefle ({3}) genel özelliklere sahip "{2}" projesini oluşturuyor ancak oluşturulan projeye yönelik derleme sonucu, altyapı önbelleğinde değil. Bu, yalıtılmış derlemelerde aşağıdakilerden biri anlamına gelebilir: - Başvuru, "{0}" projesindeki ProjectReferenceTargets öğesinde belirtilmeyen bir hedefle çağrıldı diff --git a/src/Build/Resources/xlf/Strings.zh-Hans.xlf b/src/Build/Resources/xlf/Strings.zh-Hans.xlf index ab54e811299..543660c0c22 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hans.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - 项目 {1} 中的任务 {0} 使用 {2} CLI 生成项目。应改用 MSBuild 任务。 + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - 不应使用 "Exec" 任务来生成项目。 + The 'Exec' task should not be used to build a project. diff --git a/src/Build/Resources/xlf/Strings.zh-Hant.xlf b/src/Build/Resources/xlf/Strings.zh-Hant.xlf index 98a8f314606..ca5f2985c11 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hant.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - 來自專案 {1} 的工作 {0} 使用 {2} CLI 建置專案。建議改用 MSBuild 工作。 + Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. The 'Exec' task should not be used to build a project. - 不應該使用 'Exec' 工作來建置專案。 + The 'Exec' task should not be used to build a project. From cb83bcecc1714302107ae594498a98c230641486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Mon, 7 Apr 2025 05:43:26 -0700 Subject: [PATCH 074/106] Update maintenance-packages versions (#11684) * Update maintenance-packages versions * Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20250404.3 Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 9.0.0-alpha.1.25170.3 -> To Version 9.0.0-alpha.1.25204.3 --------- Co-authored-by: dotnet-maestro[bot] Co-authored-by: Viktor Hofer --- eng/Versions.props | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 2677a23f518..2d137451a71 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -25,8 +25,17 @@ true - - + + + 6.1.3 + 4.6.3 + 6.1.2 + 4.6.3 + + + 6.1.0 4.6.0 - 4.6.0 - 6.1.0 - 0.2.104-beta 6.1.0 - 5.0.0 + 4.6.0 @@ -52,12 +58,14 @@ 9.0.0 9.0.0 9.0.0 + 5.0.0 9.0.0 9.0.0 9.0.0 9.0.0 + 0.2.104-beta - + https://github.com/dotnet/source-build-reference-packages - 643689c88b1d5a0f1561383972c4189a0c673abe + 7dbf5deea5bdccf513df73cba179c4c0ad106010 From f183e7cdc332e45bd4b62e921acd17c03c3b438c Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Thu, 10 Apr 2025 01:13:04 -0700 Subject: [PATCH 078/106] Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11366194 (#11709) --- src/Build/Resources/xlf/Strings.cs.xlf | 4 ++-- src/Build/Resources/xlf/Strings.de.xlf | 4 ++-- src/Build/Resources/xlf/Strings.es.xlf | 4 ++-- src/Build/Resources/xlf/Strings.fr.xlf | 4 ++-- src/Build/Resources/xlf/Strings.it.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ja.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ko.xlf | 4 ++-- src/Build/Resources/xlf/Strings.pl.xlf | 4 ++-- src/Build/Resources/xlf/Strings.pt-BR.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ru.xlf | 4 ++-- src/Build/Resources/xlf/Strings.tr.xlf | 4 ++-- src/Build/Resources/xlf/Strings.zh-Hans.xlf | 4 ++-- src/Build/Resources/xlf/Strings.zh-Hant.xlf | 4 ++-- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Build/Resources/xlf/Strings.cs.xlf b/src/Build/Resources/xlf/Strings.cs.xlf index d65294cfc54..2c2d11588ac 100644 --- a/src/Build/Resources/xlf/Strings.cs.xlf +++ b/src/Build/Resources/xlf/Strings.cs.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Úloha {0} z projektu {1} sestaví projekt pomocí rozhraní příkazového řádku {2}. Místo toho by se měla použít úloha MSBuild. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + Úloha Exec by se neměla používat k sestavení projektu. diff --git a/src/Build/Resources/xlf/Strings.de.xlf b/src/Build/Resources/xlf/Strings.de.xlf index b45278e6555..034f434df51 100644 --- a/src/Build/Resources/xlf/Strings.de.xlf +++ b/src/Build/Resources/xlf/Strings.de.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Die {0}-Aufgabe aus dem Projekt {1} erstellt ein Projekt mithilfe der {2} CLI. Stattdessen sollte die MSBuild-Aufgabe verwendet werden. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + Die "Ausführen"-Aufgabe sollte nicht zum Erstellen eines Projekts verwendet werden. diff --git a/src/Build/Resources/xlf/Strings.es.xlf b/src/Build/Resources/xlf/Strings.es.xlf index f02802a5777..c6cbcee8297 100644 --- a/src/Build/Resources/xlf/Strings.es.xlf +++ b/src/Build/Resources/xlf/Strings.es.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + La tarea {0} del proyecto {1} compila un proyecto mediante la CLI {2}. En su lugar, se debe usar la tarea MSBuild. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + La tarea "Exec" no debe usarse para compilar un proyecto. diff --git a/src/Build/Resources/xlf/Strings.fr.xlf b/src/Build/Resources/xlf/Strings.fr.xlf index cd50b6c190d..572dfbb0d28 100644 --- a/src/Build/Resources/xlf/Strings.fr.xlf +++ b/src/Build/Resources/xlf/Strings.fr.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + La tâche {0} du projet {1} crée un projet en utilisant l’interface CLI {2}. Vous devez utiliser la tâche MSBuild à la place. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + Vous ne devez pas utiliser la tâche « Exec¯ pour construire un projet. diff --git a/src/Build/Resources/xlf/Strings.it.xlf b/src/Build/Resources/xlf/Strings.it.xlf index f387aa31050..abb7d0613fd 100644 --- a/src/Build/Resources/xlf/Strings.it.xlf +++ b/src/Build/Resources/xlf/Strings.it.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + L'attività {0} del progetto{1} compila un progetto utilizzando la CLI {2}. È necessario utilizzare invece l'attività MSBuild. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + L'attività 'Esecuzione' non deve essere utilizzata per compilare un progetto. diff --git a/src/Build/Resources/xlf/Strings.ja.xlf b/src/Build/Resources/xlf/Strings.ja.xlf index 18fc679e249..337cf87e8c8 100644 --- a/src/Build/Resources/xlf/Strings.ja.xlf +++ b/src/Build/Resources/xlf/Strings.ja.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + プロジェクト {1} のタスク {0} は、{2} CLI を使用してプロジェクトをビルドします。代わりに MSBuild タスクを使用する必要があります。 The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + 'Exec' タスクはプロジェクトのビルドには使用しないでください。 diff --git a/src/Build/Resources/xlf/Strings.ko.xlf b/src/Build/Resources/xlf/Strings.ko.xlf index 495d5ef06de..ab55a29636d 100644 --- a/src/Build/Resources/xlf/Strings.ko.xlf +++ b/src/Build/Resources/xlf/Strings.ko.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + 프로젝트 {1}의 작업 {0}(은)는 {2} CLI를 사용하여 프로젝트를 빌드합니다. 대신 MSBuild 작업을 사용해야 합니다. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + 'Exec' 작업은 프로젝트를 빌드하는 데 사용하면 안 됩니다. diff --git a/src/Build/Resources/xlf/Strings.pl.xlf b/src/Build/Resources/xlf/Strings.pl.xlf index b2055495b08..31b1ff023ce 100644 --- a/src/Build/Resources/xlf/Strings.pl.xlf +++ b/src/Build/Resources/xlf/Strings.pl.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Zadanie {0} z projektu {1} tworzy projekt przy użyciu interfejsu wiersza polecenia {2}. Zamiast tego należy użyć zadania MSBuild. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + Zadanie „Exec” nie powinno być używane do kompilowania projektu. diff --git a/src/Build/Resources/xlf/Strings.pt-BR.xlf b/src/Build/Resources/xlf/Strings.pt-BR.xlf index 2ead1a82e33..6d361e55d57 100644 --- a/src/Build/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Build/Resources/xlf/Strings.pt-BR.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + A tarefa {0} do projeto {1} cria um projeto usando a CLI {2}. Em vez disso, a tarefa do MSBuild deve ser usada. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + A tarefa "Exec" não deve ser usada para criar um projeto. diff --git a/src/Build/Resources/xlf/Strings.ru.xlf b/src/Build/Resources/xlf/Strings.ru.xlf index 28f22bedb3b..c6064233811 100644 --- a/src/Build/Resources/xlf/Strings.ru.xlf +++ b/src/Build/Resources/xlf/Strings.ru.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + Задача {0} из проекта {1} создает проект с помощью CLI {2}. Вместо этого следует использовать задачу MSBuild. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + Задачу "Exec" не следует использовать для создания проекта. diff --git a/src/Build/Resources/xlf/Strings.tr.xlf b/src/Build/Resources/xlf/Strings.tr.xlf index 352a31c2244..066ba02cfde 100644 --- a/src/Build/Resources/xlf/Strings.tr.xlf +++ b/src/Build/Resources/xlf/Strings.tr.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + {1} projesindeki {0} görevi {2} CLI kullanılan bir proje oluşturur. Bunun yerine MSBuild görevi kullanılmalıdır. The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + 'Exec' görevi, bir projeyi oluşturmak için kullanılmamalıdır. diff --git a/src/Build/Resources/xlf/Strings.zh-Hans.xlf b/src/Build/Resources/xlf/Strings.zh-Hans.xlf index 543660c0c22..ab54e811299 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hans.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + 项目 {1} 中的任务 {0} 使用 {2} CLI 生成项目。应改用 MSBuild 任务。 The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + 不应使用 "Exec" 任务来生成项目。 diff --git a/src/Build/Resources/xlf/Strings.zh-Hant.xlf b/src/Build/Resources/xlf/Strings.zh-Hant.xlf index ca5f2985c11..98a8f314606 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hant.xlf @@ -223,12 +223,12 @@ Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. - Task {0} from project {1} builds a project using the {2} CLI. The MSBuild task should be used instead. + 來自專案 {1} 的工作 {0} 使用 {2} CLI 建置專案。建議改用 MSBuild 工作。 The 'Exec' task should not be used to build a project. - The 'Exec' task should not be used to build a project. + 不應該使用 'Exec' 工作來建置專案。 From 466055af51676fb24ef3458a1978d9ec73b3758f Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 10:13:31 +0200 Subject: [PATCH 079/106] Update dependencies from https://github.com/dotnet/arcade build 20250408.6 (#11714) Microsoft.SourceBuild.Intermediate.arcade , Microsoft.DotNet.Arcade.Sdk , Microsoft.DotNet.XliffTasks , Microsoft.DotNet.XUnitExtensions From Version 9.0.0-beta.25204.5 -> To Version 9.0.0-beta.25208.6 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 16 ++++++++-------- eng/Versions.props | 2 +- global.json | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 0ae70bffd22..621523d5751 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -123,19 +123,19 @@ - + https://github.com/dotnet/arcade - 97cbc7361ff28b2948c8182720c166a744049f55 + aa61e8c20a869bcc994f8b29eb07d927d2bec6f4 - + https://github.com/dotnet/arcade - 97cbc7361ff28b2948c8182720c166a744049f55 + aa61e8c20a869bcc994f8b29eb07d927d2bec6f4 - + https://github.com/dotnet/arcade - 97cbc7361ff28b2948c8182720c166a744049f55 + aa61e8c20a869bcc994f8b29eb07d927d2bec6f4 https://github.com/nuget/nuget.client @@ -150,9 +150,9 @@ f76d6ab7fa6310b6cda343419aa7bf9ee2df8e8e - + https://github.com/dotnet/arcade - 97cbc7361ff28b2948c8182720c166a744049f55 + aa61e8c20a869bcc994f8b29eb07d927d2bec6f4 diff --git a/eng/Versions.props b/eng/Versions.props index 2d137451a71..b333a776fbe 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -81,7 +81,7 @@ Otherwise, this version of dotnet will not be installed and the build will error out. --> $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) 4.2.0-1.22102.8 - 9.0.0-beta.25204.5 + 9.0.0-beta.25208.6 4.14.0-3.25202.5 6.14.0-preview.1.102 diff --git a/global.json b/global.json index 0ccd947b32d..cdd0598fc19 100644 --- a/global.json +++ b/global.json @@ -3,13 +3,13 @@ "allowPrerelease": true }, "tools": { - "dotnet": "9.0.104", + "dotnet": "9.0.105", "vs": { "version": "17.12.0" }, "xcopy-msbuild": "17.12.0" }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25204.5" + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.25208.6" } } From b96560aa033132c61c19d7c6b16d17c26fe3dbaf Mon Sep 17 00:00:00 2001 From: Jenny Bai Date: Fri, 11 Apr 2025 01:12:36 +0000 Subject: [PATCH 080/106] Embed .editorconfig in binary log (#11670) Fixes #10866 Context Binary log (.binlog file) contains embedded files. For improved diagnostics, we should also embed BuildCheck .editorconfig file to the binary log. Changes Made Use the static property to store all the .editorconfig file and call that in the BinaryLogger.cs Shutdown method. --- .../EditorConfig/EditorConfigParser.cs | 18 ++++++++++++++---- src/Build/Logging/BinaryLogger/BinaryLogger.cs | 8 ++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs b/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs index 393a9f7612c..9b72644bdb8 100644 --- a/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs +++ b/src/Build/BuildCheck/Infrastructure/EditorConfig/EditorConfigParser.cs @@ -12,6 +12,10 @@ namespace Microsoft.Build.Experimental.BuildCheck.Infrastructure.EditorConfig; internal sealed class EditorConfigParser { + // static property for embedding resolved `.editorconfig`s in binlog + private static ConcurrentBag editorConfigFilePaths = new ConcurrentBag(); + public static IEnumerable EditorConfigFilePaths => editorConfigFilePaths; + private const string EditorconfigFile = ".editorconfig"; /// @@ -25,6 +29,13 @@ internal Dictionary Parse(string filePath) return MergeEditorConfigFiles(editorConfigs, filePath); } + /// + /// Clears the editorConfigFilePaths collection after embedding in the binlog. + /// + public static void ClearEditorConfigFilePaths() + { + editorConfigFilePaths = new ConcurrentBag(); + } /// /// Fetches the list of EditorconfigFile ordered from the nearest to the filePath. /// @@ -34,16 +45,15 @@ internal List DiscoverEditorConfigFiles(string filePath) var editorConfigDataFromFilesList = new List(); var directoryOfTheProject = Path.GetDirectoryName(filePath); - // The method will look for the file in parent directory if not found in current until found or the directory is root. + // The method will look for the file in parent directory if not found in current until found or the directory is root. var editorConfigFilePath = FileUtilities.GetPathOfFileAbove(EditorconfigFile, directoryOfTheProject); - while (editorConfigFilePath != string.Empty) { var editorConfig = _editorConfigFileCache.GetOrAdd(editorConfigFilePath, (key) => { return EditorConfigFile.Parse(File.ReadAllText(editorConfigFilePath)); }); - + editorConfigFilePaths.Add(editorConfigFilePath); editorConfigDataFromFilesList.Add(editorConfig); if (editorConfig.IsRoot) @@ -61,7 +71,7 @@ internal List DiscoverEditorConfigFiles(string filePath) } /// - /// Retrieves the config dictionary from the sections that matched the filePath. + /// Retrieves the config dictionary from the sections that matched the filePath. /// /// /// diff --git a/src/Build/Logging/BinaryLogger/BinaryLogger.cs b/src/Build/Logging/BinaryLogger/BinaryLogger.cs index 10286b512eb..e5947b6bf33 100644 --- a/src/Build/Logging/BinaryLogger/BinaryLogger.cs +++ b/src/Build/Logging/BinaryLogger/BinaryLogger.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.IO.Compression; +using Microsoft.Build.Experimental.BuildCheck.Infrastructure.EditorConfig; using Microsoft.Build.Framework; using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.Shared; @@ -321,6 +322,12 @@ public void Shutdown() if (projectImportsCollector != null) { + // Write the build check editorconfig file paths to the log + foreach (var filePath in EditorConfigParser.EditorConfigFilePaths) + { + projectImportsCollector.AddFile(filePath); + } + EditorConfigParser.ClearEditorConfigFilePaths(); projectImportsCollector.Close(); if (CollectProjectImports == ProjectImportsCollectionMode.Embed) @@ -336,6 +343,7 @@ public void Shutdown() projectImportsCollector = null; } + if (stream != null) { // It's hard to determine whether we're at the end of decoding GZipStream From 46b5fa75b0f9a5525846981359daa6850d21ea0f Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Fri, 11 Apr 2025 10:15:07 +0200 Subject: [PATCH 081/106] Move DotNetBuild properties into the repo (#11720) --- Directory.Build.targets | 5 +++++ eng/Build.props | 9 +++++---- eng/DotNetBuild.props | 12 ------------ 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Directory.Build.targets b/Directory.Build.targets index f2c71b74a0e..3538e50a581 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -7,6 +7,11 @@ $(TargetFileName) + + + false + + diff --git a/eng/Build.props b/eng/Build.props index 591a4d41340..fec5d08db7d 100644 --- a/eng/Build.props +++ b/eng/Build.props @@ -6,11 +6,12 @@ - + - + + diff --git a/eng/DotNetBuild.props b/eng/DotNetBuild.props index 778419d070a..997ad524331 100644 --- a/eng/DotNetBuild.props +++ b/eng/DotNetBuild.props @@ -1,5 +1,4 @@ - @@ -7,15 +6,4 @@ true - - - - $(InnerBuildArgs) /p:Projects="$(InnerSourceBuildRepoRoot)MSBuild.SourceBuild.slnf" - - - $(InnerBuildArgs) /p:EnablePackageValidation=false - - - From a10ca4529c177605addf622c572a1c563c7a819c Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:27:48 +0200 Subject: [PATCH 082/106] Update dependencies from https://github.com/dotnet/roslyn build 20250410.2 (#11722) Microsoft.SourceBuild.Intermediate.roslyn , Microsoft.Net.Compilers.Toolset From Version 4.14.0-3.25202.5 -> To Version 4.14.0-3.25210.2 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 8 ++++---- eng/Versions.props | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 621523d5751..b2d855f8a00 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -141,13 +141,13 @@ https://github.com/nuget/nuget.client 7f50923823cb8fe4dab9b6565ece9516407de498 - + https://github.com/dotnet/roslyn - f76d6ab7fa6310b6cda343419aa7bf9ee2df8e8e + 575cfa2b4bbeaa7a5084529bf985389ed2925977 - + https://github.com/dotnet/roslyn - f76d6ab7fa6310b6cda343419aa7bf9ee2df8e8e + 575cfa2b4bbeaa7a5084529bf985389ed2925977 diff --git a/eng/Versions.props b/eng/Versions.props index b333a776fbe..7a05ec36aea 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -82,7 +82,7 @@ $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) 4.2.0-1.22102.8 9.0.0-beta.25208.6 - 4.14.0-3.25202.5 + 4.14.0-3.25210.2 6.14.0-preview.1.102 From e0de5c8e18e43466327131b27e8d0886d8ce2286 Mon Sep 17 00:00:00 2001 From: Jenny Bai Date: Mon, 14 Apr 2025 13:41:43 +0000 Subject: [PATCH 083/106] Fix parallelism in test case EndToEndMinimumMessageImportance (#11697) --- src/MSBuild.UnitTests/XMake_Tests.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/MSBuild.UnitTests/XMake_Tests.cs b/src/MSBuild.UnitTests/XMake_Tests.cs index bc93f3ea4aa..b18eb45c346 100644 --- a/src/MSBuild.UnitTests/XMake_Tests.cs +++ b/src/MSBuild.UnitTests/XMake_Tests.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using System.Text.RegularExpressions; using System.Threading; using System.Xml.Linq; using Microsoft.Build.CommandLine; @@ -2711,6 +2712,16 @@ public void EndToEndMinimumMessageImportance(string arguments, MessageImportance TransientTestProjectWithFiles testProject = testEnvironment.CreateTestProjectWithFiles(projectContents); + // If /bl is specified, set a path for the binlog that is defined by the test environment + string pattern = @"/v:(\w+)\s/b"; ; + Regex.Match(arguments, pattern); + Match match = Regex.Match(arguments, pattern); + if (match.Success) + { + string binlogPath = Path.Combine(testProject.TestRoot, match.Groups[1] + ".binlog"); + arguments = arguments.Replace("/bl", $"/bl:{binlogPath}"); + } + // Build in-proc. RunnerUtilities.ExecMSBuild($"{arguments} \"{testProject.ProjectFile}\"", out bool success, _output); success.ShouldBeTrue(); From 314b669887a7d8ca9857b7287ffb796d669ba508 Mon Sep 17 00:00:00 2001 From: Viktor Hofer Date: Tue, 15 Apr 2025 10:47:53 +0200 Subject: [PATCH 084/106] Fix warnings shown in VMR with recent SDK (#11728) --- .../Microsoft.Build.BuildCheck.UnitTests.csproj | 7 +++++-- src/Framework/BinaryTranslator.cs | 12 ------------ src/Framework/BuildEventArgs.cs | 2 +- src/Framework/IBuildEngine7.cs | 2 +- src/Framework/IBuildEngine8.cs | 2 +- src/Framework/InternalErrorException.cs | 2 ++ src/MSBuild/MSBuild.csproj | 2 +- .../ProjectCachePlugin/ProjectCachePlugin.csproj | 12 ++++++++---- src/Tasks/Microsoft.Build.Tasks.csproj | 2 +- src/Utilities/Microsoft.Build.Utilities.csproj | 2 +- 10 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/BuildCheck.UnitTests/Microsoft.Build.BuildCheck.UnitTests.csproj b/src/BuildCheck.UnitTests/Microsoft.Build.BuildCheck.UnitTests.csproj index 440a60cc86e..76031c5b599 100644 --- a/src/BuildCheck.UnitTests/Microsoft.Build.BuildCheck.UnitTests.csproj +++ b/src/BuildCheck.UnitTests/Microsoft.Build.BuildCheck.UnitTests.csproj @@ -24,10 +24,13 @@ - + + + + - + diff --git a/src/Framework/BinaryTranslator.cs b/src/Framework/BinaryTranslator.cs index ecf16907307..762292169eb 100644 --- a/src/Framework/BinaryTranslator.cs +++ b/src/Framework/BinaryTranslator.cs @@ -55,11 +55,6 @@ internal static ITranslator GetWriteTranslator(Stream stream) /// private class BinaryReadTranslator : ITranslator { - /// - /// The stream used as a source or destination for data. - /// - private Stream _packetStream; - /// /// The binary reader used in read mode. /// @@ -71,7 +66,6 @@ private class BinaryReadTranslator : ITranslator ///
public BinaryReadTranslator(Stream packetStream, BinaryReaderFactory buffer) { - _packetStream = packetStream; _reader = buffer.Create(packetStream); } #nullable disable @@ -795,11 +789,6 @@ public bool TranslateNullable(T value) ///
private class BinaryWriteTranslator : ITranslator { - /// - /// The stream used as a source or destination for data. - /// - private Stream _packetStream; - /// /// The binary writer used in write mode. /// @@ -811,7 +800,6 @@ private class BinaryWriteTranslator : ITranslator /// The stream serving as the source or destination of data. public BinaryWriteTranslator(Stream packetStream) { - _packetStream = packetStream; _writer = new BinaryWriter(packetStream); } diff --git a/src/Framework/BuildEventArgs.cs b/src/Framework/BuildEventArgs.cs index 1e1264246ec..a26e7b26948 100644 --- a/src/Framework/BuildEventArgs.cs +++ b/src/Framework/BuildEventArgs.cs @@ -284,7 +284,7 @@ private void SetBuildEventContextDefaultAfterSerialization(StreamingContext sc) /// This is used by the Message property overrides to reconstruct the /// message lazily on demand. ///
- internal static Func ResourceStringFormatter = (string resourceName, string?[] arguments) => + internal static Func ResourceStringFormatter = (resourceName, arguments) => { var sb = new StringBuilder(); sb.Append(resourceName); diff --git a/src/Framework/IBuildEngine7.cs b/src/Framework/IBuildEngine7.cs index c5fcd4c18a4..040e6ac338e 100644 --- a/src/Framework/IBuildEngine7.cs +++ b/src/Framework/IBuildEngine7.cs @@ -11,6 +11,6 @@ namespace Microsoft.Build.Framework ///
public interface IBuildEngine7 : IBuildEngine6 { - public bool AllowFailureWithoutError { get; set; } + bool AllowFailureWithoutError { get; set; } } } diff --git a/src/Framework/IBuildEngine8.cs b/src/Framework/IBuildEngine8.cs index 7d08daa0c4d..d31e09ed246 100644 --- a/src/Framework/IBuildEngine8.cs +++ b/src/Framework/IBuildEngine8.cs @@ -17,6 +17,6 @@ public interface IBuildEngine8 : IBuildEngine7 ///
/// The warning code to check. /// A boolean to determine whether the warning should be treated as an error. - public bool ShouldTreatWarningAsError(string warningCode); + bool ShouldTreatWarningAsError(string warningCode); } } diff --git a/src/Framework/InternalErrorException.cs b/src/Framework/InternalErrorException.cs index 73180f14fb8..8b1096c5eef 100644 --- a/src/Framework/InternalErrorException.cs +++ b/src/Framework/InternalErrorException.cs @@ -148,6 +148,8 @@ private static void LaunchDebugger(string message, string innerMessage) } #endregion +#if DEBUG private static bool RunningTests() => BuildEnvironmentState.s_runningTests; +#endif } } diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj index 2edca8c339b..d169a6ecfe0 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -179,7 +179,6 @@ - @@ -194,6 +193,7 @@ + diff --git a/src/Samples/ProjectCachePlugin/ProjectCachePlugin.csproj b/src/Samples/ProjectCachePlugin/ProjectCachePlugin.csproj index 684e80213ae..ec425f3f969 100644 --- a/src/Samples/ProjectCachePlugin/ProjectCachePlugin.csproj +++ b/src/Samples/ProjectCachePlugin/ProjectCachePlugin.csproj @@ -1,4 +1,5 @@ + true false @@ -7,17 +8,20 @@ $(LatestDotNetCoreForMSBuild) $(FullFrameworkTFM);$(LatestDotNetCoreForMSBuild) + + - - - - + + + + + diff --git a/src/Tasks/Microsoft.Build.Tasks.csproj b/src/Tasks/Microsoft.Build.Tasks.csproj index 37d7cece260..7184f04adb7 100644 --- a/src/Tasks/Microsoft.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.Build.Tasks.csproj @@ -663,7 +663,6 @@ - @@ -682,6 +681,7 @@ + diff --git a/src/Utilities/Microsoft.Build.Utilities.csproj b/src/Utilities/Microsoft.Build.Utilities.csproj index e5a0a89b595..86e032a484b 100644 --- a/src/Utilities/Microsoft.Build.Utilities.csproj +++ b/src/Utilities/Microsoft.Build.Utilities.csproj @@ -23,7 +23,7 @@ - + From b57af8f00b52e854ec7d82c4ff11a3cfa08a9bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Provazn=C3=ADk?= Date: Tue, 15 Apr 2025 15:16:06 +0200 Subject: [PATCH 085/106] perfstar branch creation gh action (#11730) --- .github/workflows/perfstar-branch.yml | 55 +++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/perfstar-branch.yml diff --git a/.github/workflows/perfstar-branch.yml b/.github/workflows/perfstar-branch.yml new file mode 100644 index 00000000000..f7f52492417 --- /dev/null +++ b/.github/workflows/perfstar-branch.yml @@ -0,0 +1,55 @@ +name: Create Perf Branch on /perfstar comment + +on: + issue_comment: + types: [created] + +permissions: + contents: write + pull-requests: read + +jobs: + create_perf_branch: + if: | + github.event.issue.pull_request && + github.event.comment.body == '/perfstar' && + contains(fromJSON('["COLLABORATOR", "MEMBER", "OWNER"]'), github.event.comment.author_association) + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Get PR information + id: pr_info + run: | + PR_DATA=$(gh pr view ${{ github.event.issue.number }} --json headRefName,headRefOid,headRepository) + HEAD_REF=$(echo $PR_DATA | jq -r '.headRefName') + HEAD_SHA=$(echo $PR_DATA | jq -r '.headRefOid') + HEAD_REPO=$(echo $PR_DATA | jq -r '.headRepository.nameWithOwner') + + echo "pr_head_branch=${HEAD_REF}" >> $GITHUB_OUTPUT + echo "pr_head_sha=${HEAD_SHA}" >> $GITHUB_OUTPUT + echo "pr_head_repo=${HEAD_REPO}" >> $GITHUB_OUTPUT + echo "new_branch_name=perf/${HEAD_REF}" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Create and push perf branch + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Ensure we can access the PR's commits (especially important for forks) + git fetch origin pull/${{ github.event.issue.number }}/head:pr-${{ github.event.issue.number }}-head + + # Create branch from PR head + git checkout -b ${{ steps.pr_info.outputs.new_branch_name }} ${{ steps.pr_info.outputs.pr_head_sha }} + + # Merge main branch + git fetch origin main + git merge origin/main --no-ff --no-edit -m "Merge main into ${{ steps.pr_info.outputs.new_branch_name }} for perf testing" + + # Push branch + git push origin ${{ steps.pr_info.outputs.new_branch_name }} From 7bbc2e5175d66a781abc18526bf7a10c4da113a3 Mon Sep 17 00:00:00 2001 From: Wenwen <53243232+Winniexu01@users.noreply.github.com> Date: Tue, 15 Apr 2025 13:53:36 +0000 Subject: [PATCH 086/106] Remove unnecessary CA2022 suppressions (#11626) --- src/Shared/FileUtilities.cs | 39 ++++++++++++++----- src/Tasks/ManifestUtil/ManifestReader.cs | 8 +--- src/Tasks/ManifestUtil/PathUtil.cs | 4 +- src/Tasks/StrongNameUtils.cs | 4 +- .../TrackedDependencies/FileTrackerTests.cs | 4 +- 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/Shared/FileUtilities.cs b/src/Shared/FileUtilities.cs index 911439a8bb0..cb67ca26bec 100644 --- a/src/Shared/FileUtilities.cs +++ b/src/Shared/FileUtilities.cs @@ -1565,21 +1565,42 @@ internal static void ClearFileExistenceCache() internal static void ReadFromStream(this Stream stream, byte[] content, int startIndex, int length) { -#if NET stream.ReadExactly(content, startIndex, length); -#else - int bytesRead = 0; - while (bytesRead < length) + } + } +} + +#if !NET +namespace System.IO +{ + internal static class StreamExtensions + { + internal static void ReadExactly(this Stream stream, byte[] buffer, int offset, int count) + { + if (buffer == null) { - int read = stream.Read(content, startIndex + bytesRead, length - bytesRead); - if (read == 0) + throw new ArgumentNullException(nameof(buffer)); + } + if (offset < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset)); + } + if ((uint)count > buffer.Length - offset) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + while (count > 0) + { + int read = stream.Read(buffer, offset, count); + if (read <= 0) { throw new EndOfStreamException(); } - - bytesRead += read; + offset +=read; + count -= read; } -#endif } } } +#endif \ No newline at end of file diff --git a/src/Tasks/ManifestUtil/ManifestReader.cs b/src/Tasks/ManifestUtil/ManifestReader.cs index 1bd48abe2db..87e7e3d452d 100644 --- a/src/Tasks/ManifestUtil/ManifestReader.cs +++ b/src/Tasks/ManifestUtil/ManifestReader.cs @@ -54,9 +54,7 @@ private static XmlDocument GetXmlDocument(string path) using (Stream s = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { byte[] buffer = new byte[2]; -#pragma warning disable CA2022 // Avoid inexact read with 'Stream.Read' The check of bytes happens later in the code. In case of invalid documents the code will throw an exception during xml loading. - s.Read(buffer, 0, 2); -#pragma warning restore CA2022 // Avoid inexact read with 'Stream.Read' + s.ReadExactly(buffer, 0, 2); s.Position = 0; var document = new XmlDocument(); var xrSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }; @@ -140,9 +138,7 @@ public static Manifest ReadManifest(string manifestType, string path, bool prese using (Stream s = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { byte[] buffer = new byte[2]; -#pragma warning disable CA2022 // Avoid inexact read with 'Stream.Read' The check of bytes happens later in the code. In case of invalid document the exception is expected later - s.Read(buffer, 0, 2); -#pragma warning restore CA2022 // Avoid inexact read with 'Stream.Read' + s.ReadExactly(buffer, 0, 2); s.Position = 0; // if first two bytes are "MZ" then we're looking at an .exe or a .dll not a .manifest if ((buffer[0] == 0x4D) && (buffer[1] == 0x5A)) diff --git a/src/Tasks/ManifestUtil/PathUtil.cs b/src/Tasks/ManifestUtil/PathUtil.cs index 93ec71a20a8..83184f8f618 100644 --- a/src/Tasks/ManifestUtil/PathUtil.cs +++ b/src/Tasks/ManifestUtil/PathUtil.cs @@ -166,9 +166,7 @@ public static bool IsPEFile(string path) byte[] buffer = new byte[2]; using (Stream s = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { -#pragma warning disable CA2022 // Avoid inexact read with 'Stream.Read' - s.Read(buffer, 0, 2); -#pragma warning restore CA2022 // Avoid inexact read with 'Stream.Read' + s.ReadExactly(buffer, 0, 2); } // if first two bytes are "MZ" then we're looking at an .exe or a .dll not a .manifest diff --git a/src/Tasks/StrongNameUtils.cs b/src/Tasks/StrongNameUtils.cs index 26fce852479..f1aef67e8c0 100644 --- a/src/Tasks/StrongNameUtils.cs +++ b/src/Tasks/StrongNameUtils.cs @@ -49,10 +49,8 @@ internal static void ReadKeyFile(TaskLoggingHelper log, string keyFile, out Stro int fileLength = (int)fs.Length; keyFileContents = new byte[fileLength]; -#pragma warning disable CA2022 // Avoid inexact read with 'Stream.Read' // TODO: Read the count of read bytes and check if it matches the expected length, if not raise an exception - fs.Read(keyFileContents, 0, fileLength); -#pragma warning restore CA2022 // Avoid inexact read with 'Stream.Read' + fs.ReadExactly(keyFileContents, 0, fileLength); } } catch (ArgumentException e) diff --git a/src/Utilities.UnitTests/TrackedDependencies/FileTrackerTests.cs b/src/Utilities.UnitTests/TrackedDependencies/FileTrackerTests.cs index 1bcad909332..f99b7af9a60 100644 --- a/src/Utilities.UnitTests/TrackedDependencies/FileTrackerTests.cs +++ b/src/Utilities.UnitTests/TrackedDependencies/FileTrackerTests.cs @@ -1735,9 +1735,7 @@ public void CreateFileDoesntRecordWriteIfNotWrittenTo() var buffer = new byte[10]; using (FileStream fs = File.Open(readFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { -#pragma warning disable CA2022 - fs.Read(buffer, 0, 10); -#pragma warning restore CA2022 + fs.ReadExactly(buffer, 0, 10); } FileTracker.WriteContextTLogs(testDir, tlogRootName); From 95ecbd29a31a44fa39745045118468be21e982d7 Mon Sep 17 00:00:00 2001 From: Eric Arndt <60519722+Erarndt@users.noreply.github.com> Date: Tue, 15 Apr 2025 08:19:03 -0700 Subject: [PATCH 087/106] Avoid some allocations and CPU FileIsUnderPath (#11663) Introduce FileIsUnderNormalizedPath to allow some more streamlined code in the case where we can guarantee that we meet the requirements, like we do in the `FileIsExcludedFromDependencies` calls. --------- Co-authored-by: Rainer Sigwald --- .../TrackedDependencies/FileTracker.cs | 52 ++++++++++++++----- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/Utilities/TrackedDependencies/FileTracker.cs b/src/Utilities/TrackedDependencies/FileTracker.cs index ef175ad916b..c3ac96f1f15 100644 --- a/src/Utilities/TrackedDependencies/FileTracker.cs +++ b/src/Utilities/TrackedDependencies/FileTracker.cs @@ -9,7 +9,6 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; -using System.Linq; using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Shared; @@ -80,7 +79,7 @@ public static class FileTracker /// This must be the base system-wide temp path because we use it to filter out I/O of tools outside of our control. /// Tools running under the tracker may put temp files in the temp base or in a sub-directory of their choosing. /// - private static readonly string s_tempPath = Path.GetTempPath(); + private static readonly string s_tempPath = FileUtilities.EnsureTrailingSlash(Path.GetTempPath()); // The short path to temp private static readonly string s_tempShortPath = FileUtilities.EnsureTrailingSlash(NativeMethodsShared.GetShortFilePath(s_tempPath).ToUpperInvariant()); @@ -247,12 +246,25 @@ public static bool FileIsExcludedFromDependencies(string fileName) // 5. Files under the common ("All Users") Application Data location -- C:\Documents and Settings\All Users\Application Data // on XP and either C:\Users\All Users\Application Data or C:\ProgramData on Vista+ - return FileIsUnderPath(fileName, s_applicationDataPath) || - FileIsUnderPath(fileName, s_localApplicationDataPath) || - FileIsUnderPath(fileName, s_localLowApplicationDataPath) || - FileIsUnderPath(fileName, s_tempShortPath) || - FileIsUnderPath(fileName, s_tempLongPath) || - s_commonApplicationDataPaths.Any(p => FileIsUnderPath(fileName, p)); + if (FileIsUnderNormalizedPath(fileName, s_applicationDataPath) || + FileIsUnderNormalizedPath(fileName, s_localApplicationDataPath) || + FileIsUnderNormalizedPath(fileName, s_localLowApplicationDataPath) || + FileIsUnderNormalizedPath(fileName, s_tempShortPath) || + FileIsUnderNormalizedPath(fileName, s_tempLongPath)) + { + return true; + } + + // PERF: Avoid LINQ in this path. + foreach (string p in s_commonApplicationDataPaths) + { + if (FileIsUnderNormalizedPath(fileName, p)) + { + return true; + } + } + + return false; } /// @@ -266,15 +278,31 @@ public static bool FileIsExcludedFromDependencies(string fileName) /// public static bool FileIsUnderPath(string fileName, string path) { + // Ensure that the path has a trailing slash that we are checking under + // By default the paths that we check for most often will have, so this will + // return fast and not allocate memory in the process + return FileIsUnderNormalizedPath(fileName, FileUtilities.EnsureTrailingSlash(path)); + } + + internal static bool FileIsUnderNormalizedPath(string fileName, string path) + { + int pathLength = path.Length; + + Debug.Assert(path[pathLength - 1] == Path.DirectorySeparatorChar); + // UNDONE: Get the long file path for the entry // This is an incredibly expensive operation. The tracking log // as written by CL etc. does not contain short paths // fileDirectory = NativeMethods.GetFullLongFilePath(fileDirectory); - // Ensure that the path has a trailing slash that we are checking under - // By default the paths that we check for most often will have, so this will - // return fast and not allocate memory in the process - path = FileUtilities.EnsureTrailingSlash(path); + // quick checks to return early. If our given filename is less than the length of the path, it can't be under it. + // Similarly, if the file name doesn't have a path separator + // at the index of the last separator in the path, it doesn't match. + // Because we have a normalized path below we can check against normal-slash directly. + if (fileName.Length < pathLength || fileName[pathLength - 1] != Path.DirectorySeparatorChar) + { + return false; + } // Is the fileName under the filePath? return string.Compare(fileName, 0, path, 0, path.Length, StringComparison.OrdinalIgnoreCase) == 0; From 63bc80ddd7bbf2a47883629ac98fcb3dc04b5833 Mon Sep 17 00:00:00 2001 From: Jenny Bai Date: Wed, 16 Apr 2025 09:24:25 +0000 Subject: [PATCH 088/106] Enable nullable analysis in the LoadedType.cs (#11566) * Add DisallowNull for LoadType property * Enable Nullable Reference Types * Fix issues after enable nullable * Fix System.IndexOutOfRangeException: Index was outside the bounds of the array * Enable nullable for dile TaskLoader.cs * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Shared/LoadedType.cs | 13 ++++++------- src/Shared/TaskLoader.cs | 22 ++++++++++++---------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/Shared/LoadedType.cs b/src/Shared/LoadedType.cs index 6b2f8aed5c8..6be0b228a75 100644 --- a/src/Shared/LoadedType.cs +++ b/src/Shared/LoadedType.cs @@ -7,7 +7,6 @@ using Microsoft.Build.Execution; using Microsoft.Build.Framework; -#nullable disable namespace Microsoft.Build.Shared { @@ -47,7 +46,7 @@ internal LoadedType(Type type, AssemblyLoadInfo assemblyLoadInfo, Assembly loade // properties and reflect over them without needing them to be fully loaded, so it also isn't need for TaskHosts. // MetadataLoadContext-loaded Type objects don't support testing for inherited attributes, so we manually walk the BaseType chain. - Type t = type; + Type? t = type; while (t is not null) { if (CustomAttributeData.GetCustomAttributes(t).Any(attr => attr.AttributeType.Name.Equals(nameof(LoadInSeparateAppDomainAttribute)))) @@ -92,7 +91,7 @@ internal LoadedType(Type type, AssemblyLoadInfo assemblyLoadInfo, Assembly loade } // Check whether it's assignable to ITaskItem or ITaskItem[]. Simplify to just checking for ITaskItem. - Type pt = props[i].PropertyType; + Type? pt = props[i].PropertyType; if (pt.IsArray) { pt = pt.GetElementType(); @@ -101,9 +100,9 @@ internal LoadedType(Type type, AssemblyLoadInfo assemblyLoadInfo, Assembly loade bool isAssignableToITask = iTaskItemType.IsAssignableFrom(pt); Properties[i] = new ReflectableTaskPropertyInfo(props[i], outputAttribute, requiredAttribute, isAssignableToITask); - if (loadedViaMetadataLoadContext) + if (loadedViaMetadataLoadContext && PropertyAssemblyQualifiedNames != null) { - PropertyAssemblyQualifiedNames[i] = Properties[i].PropertyType.AssemblyQualifiedName; + PropertyAssemblyQualifiedNames[i] = Properties[i]?.PropertyType?.AssemblyQualifiedName?? string.Empty; } } #else @@ -143,7 +142,7 @@ private bool CheckForHardcodedSTARequirement() { AssemblyName assemblyName = Type.GetTypeInfo().Assembly.GetName(); Version lastVersionToForce = new Version(3, 5); - if (assemblyName.Version.CompareTo(lastVersionToForce) > 0) + if (assemblyName.Version?.CompareTo(lastVersionToForce) > 0) { if (String.Equals(assemblyName.Name, "PresentationBuildTasks", StringComparison.OrdinalIgnoreCase)) { @@ -180,7 +179,7 @@ private bool CheckForHardcodedSTARequirement() /// /// Assembly-qualified names for properties. Only has a value if this type was loaded using MetadataLoadContext. /// - internal string[] PropertyAssemblyQualifiedNames { get; private set; } + internal string[]? PropertyAssemblyQualifiedNames { get; private set; } /// /// Gets the assembly the type was loaded from. diff --git a/src/Shared/TaskLoader.cs b/src/Shared/TaskLoader.cs index ea170a16a08..602a36871ed 100644 --- a/src/Shared/TaskLoader.cs +++ b/src/Shared/TaskLoader.cs @@ -5,8 +5,6 @@ using System.Reflection; using Microsoft.Build.Framework; -#nullable disable - namespace Microsoft.Build.Shared { /// @@ -19,7 +17,7 @@ internal static class TaskLoader /// For saving the assembly that was loaded by the TypeLoader /// We only use this when the assembly failed to load properly into the appdomain /// - private static LoadedType s_resolverLoadedType; + private static LoadedType? s_resolverLoadedType; #endif /// @@ -42,7 +40,7 @@ internal static bool IsTaskClass(Type type, object unused) /// Creates an ITask instance and returns it. /// #pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - internal static ITask CreateTask( + internal static ITask? CreateTask( LoadedType loadedType, string taskName, string taskLocation, @@ -55,7 +53,7 @@ internal static ITask CreateTask( #endif bool isOutOfProc #if FEATURE_APPDOMAIN - , out AppDomain taskAppDomain + , out AppDomain? taskAppDomain #endif ) #pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter @@ -64,7 +62,7 @@ bool isOutOfProc bool separateAppDomain = loadedType.HasLoadInSeparateAppDomainAttribute; s_resolverLoadedType = null; taskAppDomain = null; - ITask taskInstanceInOtherAppDomain = null; + ITask? taskInstanceInOtherAppDomain = null; #endif try @@ -126,7 +124,7 @@ bool isOutOfProc { // perf improvement for the same appdomain case - we already have the type object // and don't want to go through reflection to recreate it from the name. - return (ITask)Activator.CreateInstance(loadedType.Type); + return (ITask?)Activator.CreateInstance(loadedType.Type); } #if FEATURE_APPDOMAIN @@ -158,7 +156,7 @@ bool isOutOfProc taskInstanceInOtherAppDomain = (ITask)taskAppDomain.CreateInstanceAndUnwrap(loadedType.Type.GetTypeInfo().Assembly.FullName, loadedType.Type.FullName); } - return taskInstanceInOtherAppDomain; + return taskInstanceInOtherAppDomain; #endif } finally @@ -179,10 +177,14 @@ bool isOutOfProc /// This is a resolver to help created AppDomains when they are unable to load an assembly into their domain we will help /// them succeed by providing the already loaded one in the currentdomain so that they can derive AssemblyName info from it /// - internal static Assembly AssemblyResolver(object sender, ResolveEventArgs args) + internal static Assembly? AssemblyResolver(object sender, ResolveEventArgs args) { - if (args.Name.Equals(s_resolverLoadedType.LoadedAssemblyName.FullName, StringComparison.OrdinalIgnoreCase)) + if (args.Name.Equals(s_resolverLoadedType?.LoadedAssemblyName?.FullName, StringComparison.OrdinalIgnoreCase)) { + if (s_resolverLoadedType == null || s_resolverLoadedType.Path == null) + { + return null; + } return s_resolverLoadedType.LoadedAssembly ?? Assembly.Load(s_resolverLoadedType.Path); } From 78a1d3eb76b61819e7a1b88c3f6d11a51662a694 Mon Sep 17 00:00:00 2001 From: Mariana Dematte Date: Wed, 16 Apr 2025 11:51:19 +0200 Subject: [PATCH 089/106] Drop major version prop files (#11591) * deleted file import * deleted unused props files * bring back import, just unconditional * removed props files references from more files * another props removal --- src/MSBuild/MSBuild.csproj | 3 --- src/Package/MSBuild.VSSetup/files.swr | 1 - src/Tasks/Microsoft.Build.Tasks.csproj | 18 ------------------ src/Tasks/Microsoft.Common.props | 2 +- ...osoft.VisualStudioVersion.v11.Common.props | 19 ------------------- ...osoft.VisualStudioVersion.v12.Common.props | 19 ------------------- ...osoft.VisualStudioVersion.v14.Common.props | 19 ------------------- ...osoft.VisualStudioVersion.v15.Common.props | 19 ------------------- ...osoft.VisualStudioVersion.v16.Common.props | 19 ------------------- ...osoft.VisualStudioVersion.v17.Common.props | 19 ------------------- 10 files changed, 1 insertion(+), 137 deletions(-) delete mode 100644 src/Tasks/Microsoft.VisualStudioVersion.v11.Common.props delete mode 100644 src/Tasks/Microsoft.VisualStudioVersion.v12.Common.props delete mode 100644 src/Tasks/Microsoft.VisualStudioVersion.v14.Common.props delete mode 100644 src/Tasks/Microsoft.VisualStudioVersion.v15.Common.props delete mode 100644 src/Tasks/Microsoft.VisualStudioVersion.v16.Common.props delete mode 100644 src/Tasks/Microsoft.VisualStudioVersion.v17.Common.props diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj index d169a6ecfe0..686e78276ab 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -274,9 +274,6 @@ <_OurFiles Include="$(OutputPath)%(_TargetFrameworks.Identity)\Microsoft.VisualBasic.CrossTargeting.targets" TargetFramework="%(_TargetFrameworks.Identity)" /> <_OurFiles Include="$(OutputPath)%(_TargetFrameworks.Identity)\Microsoft.VisualBasic.CurrentVersion.targets" TargetFramework="%(_TargetFrameworks.Identity)" /> <_OurFiles Include="$(OutputPath)%(_TargetFrameworks.Identity)\Microsoft.VisualBasic.targets" TargetFramework="%(_TargetFrameworks.Identity)" /> - <_OurFiles Include="$(OutputPath)%(_TargetFrameworks.Identity)\Microsoft.VisualStudioVersion.v11.Common.props" TargetFramework="%(_TargetFrameworks.Identity)" /> - <_OurFiles Include="$(OutputPath)%(_TargetFrameworks.Identity)\Microsoft.VisualStudioVersion.v12.Common.props" TargetFramework="%(_TargetFrameworks.Identity)" /> - <_OurFiles Include="$(OutputPath)%(_TargetFrameworks.Identity)\Microsoft.VisualStudioVersion.v14.Common.props" TargetFramework="%(_TargetFrameworks.Identity)" /> <_OurFiles Include="$(OutputPath)%(_TargetFrameworks.Identity)\ref\**" TargetFramework="%(_TargetFrameworks.Identity)" Subdirectory="ref\" /> diff --git a/src/Package/MSBuild.VSSetup/files.swr b/src/Package/MSBuild.VSSetup/files.swr index ee8123a3994..a9e28eea646 100644 --- a/src/Package/MSBuild.VSSetup/files.swr +++ b/src/Package/MSBuild.VSSetup/files.swr @@ -22,7 +22,6 @@ vs.relatedProcessFiles folder InstallDir:\MSBuild\Current file source=$(X86BinPath)Microsoft.Common.props - file source=$(X86BinPath)Microsoft.VisualStudioVersion.v17.Common.props file source=$(ThirdPartyNotice) folder InstallDir:\MSBuild\Current\Bin diff --git a/src/Tasks/Microsoft.Build.Tasks.csproj b/src/Tasks/Microsoft.Build.Tasks.csproj index 7184f04adb7..a518f22fe8b 100644 --- a/src/Tasks/Microsoft.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.Build.Tasks.csproj @@ -447,24 +447,6 @@ PreserveNewest - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - $(AssemblyName).Strings.resources diff --git a/src/Tasks/Microsoft.Common.props b/src/Tasks/Microsoft.Common.props index e322c4a2f1e..be186f628bd 100644 --- a/src/Tasks/Microsoft.Common.props +++ b/src/Tasks/Microsoft.Common.props @@ -108,7 +108,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. JavaScript - + diff --git a/src/Tasks/Microsoft.VisualStudioVersion.v11.Common.props b/src/Tasks/Microsoft.VisualStudioVersion.v11.Common.props deleted file mode 100644 index 9ef50e92c14..00000000000 --- a/src/Tasks/Microsoft.VisualStudioVersion.v11.Common.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - 11.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - \ No newline at end of file diff --git a/src/Tasks/Microsoft.VisualStudioVersion.v12.Common.props b/src/Tasks/Microsoft.VisualStudioVersion.v12.Common.props deleted file mode 100644 index 428a5c1d571..00000000000 --- a/src/Tasks/Microsoft.VisualStudioVersion.v12.Common.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - 12.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - \ No newline at end of file diff --git a/src/Tasks/Microsoft.VisualStudioVersion.v14.Common.props b/src/Tasks/Microsoft.VisualStudioVersion.v14.Common.props deleted file mode 100644 index c2e2af3ad8e..00000000000 --- a/src/Tasks/Microsoft.VisualStudioVersion.v14.Common.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - 14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - \ No newline at end of file diff --git a/src/Tasks/Microsoft.VisualStudioVersion.v15.Common.props b/src/Tasks/Microsoft.VisualStudioVersion.v15.Common.props deleted file mode 100644 index 9d9e0f62d71..00000000000 --- a/src/Tasks/Microsoft.VisualStudioVersion.v15.Common.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - \ No newline at end of file diff --git a/src/Tasks/Microsoft.VisualStudioVersion.v16.Common.props b/src/Tasks/Microsoft.VisualStudioVersion.v16.Common.props deleted file mode 100644 index 1843227e760..00000000000 --- a/src/Tasks/Microsoft.VisualStudioVersion.v16.Common.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - 16.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - \ No newline at end of file diff --git a/src/Tasks/Microsoft.VisualStudioVersion.v17.Common.props b/src/Tasks/Microsoft.VisualStudioVersion.v17.Common.props deleted file mode 100644 index 94031c33888..00000000000 --- a/src/Tasks/Microsoft.VisualStudioVersion.v17.Common.props +++ /dev/null @@ -1,19 +0,0 @@ - - - - - 17.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - \ No newline at end of file From c6468bdb2c3014005e6fb3bf0c1a33a2b3f02c39 Mon Sep 17 00:00:00 2001 From: YuliiaKovalova <95473390+YuliiaKovalova@users.noreply.github.com> Date: Wed, 16 Apr 2025 13:40:53 +0200 Subject: [PATCH 090/106] and breaking-change bot (#11729) --- .github/policies/resourceManagement.yml | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/.github/policies/resourceManagement.yml b/.github/policies/resourceManagement.yml index effb189ad06..c65e138b268 100644 --- a/.github/policies/resourceManagement.yml +++ b/.github/policies/resourceManagement.yml @@ -168,5 +168,36 @@ configuration: - addReply: reply: Hello @${issueAuthor}, I noticed that you’re changing an *.swr file or any file under src/Package/MSBuild.VSSetup.*. Please make sure to validate this change by an experimental VS insertion. This is accomplished by pushing to an exp/* branch, which requires write permissions to this repo. description: Remind to run VS Perf DDRITs when deployed assemblies change + - if: + - payloadType: Issues + - labelAdded: + label: breaking-change + then: + - addReply: + reply: >- + Refer to the [.NET SDK breaking change guidelines](https://github.com/dotnet/sdk/blob/main/documentation/project-docs/breaking-change-guidelines.md#required-process-for-all-net-sdk-breaking-changes) + description: Add breaking change doc instructions to issue + - if: + - payloadType: Pull_Request + - labelAdded: + label: breaking-change + then: + - addLabel: + label: needs-breaking-change-doc-created + - addReply: + reply: >- + Added `needs-breaking-change-doc-created` label because this PR has the `breaking-change` label. + + + When you commit this breaking change: + + + 1. [ ] Create and link to this PR and the issue a matching issue in the dotnet/docs repo using the [breaking change documentation template](https://aka.ms/dotnet/docs/new-breaking-change-issue), then remove this `needs-breaking-change-doc-created` label. + + 2. [ ] Ask a committer to mail the `.NET SDK Breaking Change Notification` email list. + + + You can refer to the [.NET SDK breaking change guidelines](https://github.com/dotnet/sdk/blob/main/documentation/project-docs/breaking-change-guidelines.md) + description: Add breaking change instructions to PR. onFailure: onSuccess: From 168e61b7dfc91ff4513790beb40912cb573b1faf Mon Sep 17 00:00:00 2001 From: JanThomas118 <35668921+JanThomas118@users.noreply.github.com> Date: Thu, 17 Apr 2025 10:09:18 +0200 Subject: [PATCH 091/106] Use correct separator character when parsing buildplan file (#11734) --- src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs b/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs index 19a63a6eb5f..50b5e32b58d 100644 --- a/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs +++ b/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs @@ -400,7 +400,7 @@ private void ReadTimes(StreamReader file) return; } - string[] values = line.Split(MSBuildConstants.SemicolonChar); + string[] values = line.Split(MSBuildConstants.SpaceChar); if (values.Length < 3) { throw new InvalidDataException("Too few values in build plan."); From 812db57193a40e5f594fb92510f62bd4e7fd4802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Provazn=C3=ADk?= Date: Thu, 17 Apr 2025 10:26:43 +0200 Subject: [PATCH 092/106] onboard labeler-workflows (#11738) --- .github/workflows/labeler-build-predictor.yml | 17 +++++ .github/workflows/labeler-cache-retention.yml | 13 ++++ .github/workflows/labeler-predict-issues.yml | 33 ++++++++++ .github/workflows/labeler-predict-pulls.yml | 44 +++++++++++++ .github/workflows/labeler-promote.yml | 42 +++++++++++++ .github/workflows/labeler-train.yml | 63 +++++++++++++++++++ 6 files changed, 212 insertions(+) create mode 100644 .github/workflows/labeler-build-predictor.yml create mode 100644 .github/workflows/labeler-cache-retention.yml create mode 100644 .github/workflows/labeler-predict-issues.yml create mode 100644 .github/workflows/labeler-predict-pulls.yml create mode 100644 .github/workflows/labeler-promote.yml create mode 100644 .github/workflows/labeler-train.yml diff --git a/.github/workflows/labeler-build-predictor.yml b/.github/workflows/labeler-build-predictor.yml new file mode 100644 index 00000000000..8a12b312db0 --- /dev/null +++ b/.github/workflows/labeler-build-predictor.yml @@ -0,0 +1,17 @@ +name: "Labeler: Build Predictor App" + +on: + # Allow dispatching the workflow via the Actions UI + workflow_dispatch: + inputs: + rebuild: + description: "Force a rebuild of the app" + type: boolean + +jobs: + build-predictor: + permissions: + actions: write + uses: dotnet/issue-labeler/.github/workflows/build-predictor.yml@f0c098669828a134c0313adf3f58c1909e555d86 # v1.0.1 + with: + rebuild: ${{ inputs.rebuild }} diff --git a/.github/workflows/labeler-cache-retention.yml b/.github/workflows/labeler-cache-retention.yml new file mode 100644 index 00000000000..26a09ee7244 --- /dev/null +++ b/.github/workflows/labeler-cache-retention.yml @@ -0,0 +1,13 @@ +name: "Labeler: Cache Retention" + +on: + schedule: + - cron: "10 3 * * *" # 3:10 every day (arbitrary time daily, modified to different values in each repository) + + workflow_dispatch: + +jobs: + cache-retention: + # Do not run the workflow on forks outside the 'dotnet' org + if: ${{ github.repository_owner == 'dotnet' }} + uses: dotnet/issue-labeler/.github/workflows/cache-retention.yml@f0c098669828a134c0313adf3f58c1909e555d86 # v1.0.1 diff --git a/.github/workflows/labeler-predict-issues.yml b/.github/workflows/labeler-predict-issues.yml new file mode 100644 index 00000000000..e560988577d --- /dev/null +++ b/.github/workflows/labeler-predict-issues.yml @@ -0,0 +1,33 @@ +name: "Labeler: Predict Issue Labels" + +on: + # Only automatically predict area labels when issues are originally opened + issues: + types: opened + + # Allow dispatching the workflow via the Actions UI, specifying ranges of numbers + workflow_dispatch: + inputs: + issue_numbers: + description: "Issue Numbers (comma-separated list of ranges)" + type: string + model_cache_key: + description: "The cache key suffix to use for loading the model" + type: string + required: true + default: "LIVE" + +jobs: + predict-issues: + # Do not run the workflow on forks outside the 'dotnet' org + if: ${{ github.repository_owner == 'dotnet' && (inputs.issue_numbers || github.event.issue.number) }} + permissions: + issues: write + uses: dotnet/issue-labeler/.github/workflows/predict-issues.yml@f0c098669828a134c0313adf3f58c1909e555d86 # v1.0.1 + with: + model_cache_key: ${{ inputs.model_cache_key }} + issue_numbers: ${{ inputs.issue_numbers || github.event.issue.number }} + label_prefix: "Area: " + threshold: 0.40 + # default_label: "needs-area-label" + diff --git a/.github/workflows/labeler-predict-pulls.yml b/.github/workflows/labeler-predict-pulls.yml new file mode 100644 index 00000000000..fba01a5d324 --- /dev/null +++ b/.github/workflows/labeler-predict-pulls.yml @@ -0,0 +1,44 @@ +name: "Labeler: Predict Pull Labels" + +on: + # Per to the following documentation: + # https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target + # + # The `pull_request_target` event runs in the context of the base of the pull request, rather + # than in the context of the merge commit, as the `pull_request` event does. This prevents + # execution of unsafe code from the head of the pull request that could alter the repository + # or steal any secrets you use in your workflow. This event allows your workflow to do things + # like label or comment on pull requests from forks. + # + # Only automatically predict area labels when pull requests are first opened + pull_request_target: + types: opened + branches: + - 'main' + - 'vs*' + + # Allow dispatching the workflow via the Actions UI, specifying ranges of numbers + workflow_dispatch: + inputs: + pull_numbers: + description: "Pull Numbers (comma-separated list of ranges)" + type: string + model_cache_key: + description: "The cache key suffix to use for loading the model" + type: string + required: true + default: "LIVE" + +jobs: + predict-pulls: + # Do not run the workflow on forks outside the 'dotnet' org + if: ${{ github.repository_owner == 'dotnet' && (inputs.pull_numbers || github.event.number) }} + permissions: + pull-requests: write + uses: dotnet/issue-labeler/.github/workflows/predict-pulls.yml@f0c098669828a134c0313adf3f58c1909e555d86 # v1.0.1 + with: + model_cache_key: ${{ inputs.model_cache_key }} + pull_numbers: ${{ inputs.pull_numbers || github.event.number }} + label_prefix: "Area: " + threshold: 0.40 + # default_label: "needs-area-label" diff --git a/.github/workflows/labeler-promote.yml b/.github/workflows/labeler-promote.yml new file mode 100644 index 00000000000..97f40afa8f1 --- /dev/null +++ b/.github/workflows/labeler-promote.yml @@ -0,0 +1,42 @@ +name: "Labeler: Promote Models" + +on: + # Dispatched via the Actions UI, promotes the staged models from + # a staging slot into the prediction environment + workflow_dispatch: + inputs: + promote_issues: + description: "Issues: Promote Model" + type: boolean + required: true + promote_pulls: + description: "Pulls: Promote Model" + type: boolean + required: true + model_cache_key: + description: "The cache key suffix to promote into the 'LIVE' cache" + type: string + required: true + default: "staging" + backup_cache_key: + description: "The cache key suffix to use for backing up the currently promoted model" + type: string + default: "backup" + +permissions: + actions: write + +jobs: + labeler-promote-issues: + if: ${{ inputs.promote_issues }} + uses: dotnet/issue-labeler/.github/workflows/promote-issues.yml@f0c098669828a134c0313adf3f58c1909e555d86 # v1.0.1 + with: + model_cache_key: ${{ inputs.model_cache_key }} + backup_cache_key: ${{ inputs.backup_cache_key }} + + labeler-promote-pulls: + if: ${{ inputs.promote_pulls }} + uses: dotnet/issue-labeler/.github/workflows/promote-pulls.yml@f0c098669828a134c0313adf3f58c1909e555d86 # v1.0.1 + with: + model_cache_key: ${{ inputs.model_cache_key }} + backup_cache_key: ${{ inputs.backup_cache_key }} diff --git a/.github/workflows/labeler-train.yml b/.github/workflows/labeler-train.yml new file mode 100644 index 00000000000..90095eb88ba --- /dev/null +++ b/.github/workflows/labeler-train.yml @@ -0,0 +1,63 @@ +name: "Labeler: Train Models" + +on: + # Dispatched via the Actions UI, stages new models for promotion consideration + # Each step of the workflow can be run independently: Download, Train, and Test + workflow_dispatch: + inputs: + download_issues: + description: "Issues: Download Data" + type: boolean + default: true + train_issues: + description: "Issues: Train Model" + type: boolean + default: true + test_issues: + description: "Issues: Test Model" + type: boolean + default: true + download_pulls: + description: "Pulls: Download Data" + type: boolean + default: true + train_pulls: + description: "Pulls: Train Model" + type: boolean + default: true + test_pulls: + description: "Pulls: Test Model" + type: boolean + default: true + repository: + description: "Repository to train the models from" + + data_limit: + description: "Max number of items to include in the model" + type: number + + cache_key_suffix: + description: "The cache key suffix to use for staging data/models (use 'LIVE' to bypass staging)" + type: string + required: true + default: "staging" + +jobs: + labeler-train: + permissions: + issues: read + pull-requests: read + actions: write + uses: dotnet/issue-labeler/.github/workflows/train.yml@f0c098669828a134c0313adf3f58c1909e555d86 # v1.0.1 + with: + download_issues: ${{ inputs.download_issues }} + train_issues: ${{ inputs.train_issues }} + test_issues: ${{ inputs.test_issues }} + download_pulls: ${{ inputs.download_pulls }} + train_pulls: ${{ inputs.train_pulls }} + test_pulls: ${{ inputs.test_pulls }} + data_limit: ${{ inputs.data_limit && fromJSON(inputs.data_limit) || 0 }} + cache_key_suffix: ${{ inputs.cache_key_suffix }} + repository: ${{ inputs.repository }} + label_prefix: "Area: " + threshold: 0.40 From f71a7518aeffb85e9563a6597955eb6b4d13e3e2 Mon Sep 17 00:00:00 2001 From: Gang Wang Date: Thu, 17 Apr 2025 17:24:12 +0800 Subject: [PATCH 093/106] Suppress CodeQL warning dangerous deserialization that only happens in debug build (#11739) --- .../RetrievableEntryHashSet/RetrievableEntryHashSet.cs | 2 +- src/Build/Errors/InvalidToolsetDefinitionException.cs | 2 +- src/MSBuild/CommandLineSwitchException.cs | 2 +- src/MSBuild/InitializationException.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Build/Collections/RetrievableEntryHashSet/RetrievableEntryHashSet.cs b/src/Build/Collections/RetrievableEntryHashSet/RetrievableEntryHashSet.cs index dc8d96f9f7c..5db99b8ab70 100644 --- a/src/Build/Collections/RetrievableEntryHashSet/RetrievableEntryHashSet.cs +++ b/src/Build/Collections/RetrievableEntryHashSet/RetrievableEntryHashSet.cs @@ -82,7 +82,7 @@ namespace Microsoft.Build.Collections #if FEATURE_SECURITY_PERMISSIONS [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)] #endif - internal class RetrievableEntryHashSet : IRetrievableEntryHashSet + internal class RetrievableEntryHashSet : IRetrievableEntryHashSet // CodeQL [SM02227] The dangerous method is called only in debug build. It's safe for release build. where T : class, IKeyed { // store lower 31 bits of hash code diff --git a/src/Build/Errors/InvalidToolsetDefinitionException.cs b/src/Build/Errors/InvalidToolsetDefinitionException.cs index b5c1d724240..3176644c32d 100644 --- a/src/Build/Errors/InvalidToolsetDefinitionException.cs +++ b/src/Build/Errors/InvalidToolsetDefinitionException.cs @@ -18,7 +18,7 @@ namespace Microsoft.Build.Exceptions /// Exception subclass that ToolsetReaders should throw. /// [Serializable] - public class InvalidToolsetDefinitionException : BuildExceptionBase + public class InvalidToolsetDefinitionException : BuildExceptionBase // CodeQL [SM02227] The dangerous method is called only in debug build. It's safe for release build. { /// /// The MSBuild error code corresponding with this exception. diff --git a/src/MSBuild/CommandLineSwitchException.cs b/src/MSBuild/CommandLineSwitchException.cs index fdfd2b36762..e8ce5dd036d 100644 --- a/src/MSBuild/CommandLineSwitchException.cs +++ b/src/MSBuild/CommandLineSwitchException.cs @@ -17,7 +17,7 @@ namespace Microsoft.Build.CommandLine /// This exception is used to flag (syntax) errors in command line switches passed to the application. /// [Serializable] - internal sealed class CommandLineSwitchException : Exception + internal sealed class CommandLineSwitchException : Exception // CodeQL [SM02227] The dangerous method is called only in debug build. It's safe for release build. { /// /// This constructor initializes the exception message. diff --git a/src/MSBuild/InitializationException.cs b/src/MSBuild/InitializationException.cs index 4607ec549af..2d1153029b7 100644 --- a/src/MSBuild/InitializationException.cs +++ b/src/MSBuild/InitializationException.cs @@ -22,7 +22,7 @@ namespace Microsoft.Build.CommandLine /// Unlike the CommandLineSwitchException, this exception is NOT thrown for syntax errors in switches. /// [Serializable] - internal sealed class InitializationException : Exception + internal sealed class InitializationException : Exception // CodeQL [SM02227] The dangerous method is called only in debug build. It's safe for release build. { /// /// This constructor initializes the exception message. From 56af14726886f18fb6acd3212532a034eac32c47 Mon Sep 17 00:00:00 2001 From: Mariana Dematte Date: Thu, 17 Apr 2025 13:48:59 +0200 Subject: [PATCH 094/106] Revert "[automated] Merge branch 'vs17.14' => 'main' (#11562)" (#11737) This reverts commit dd2f7ad5ff4dfaf425b4e6e6b1a1467d15bd25d6, reversing changes made to af0a20f32a79ea405ee79a6218182a510624a01f. --- azure-pipelines/vs-insertion-experimental.yml | 10 ---------- azure-pipelines/vs-insertion.yml | 16 ++++++++-------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/azure-pipelines/vs-insertion-experimental.yml b/azure-pipelines/vs-insertion-experimental.yml index 965ceb7d478..ab2ce364131 100644 --- a/azure-pipelines/vs-insertion-experimental.yml +++ b/azure-pipelines/vs-insertion-experimental.yml @@ -2,16 +2,6 @@ trigger: none name: $(Date:yyyyMMdd).$(Rev:r) -# Since our release branch is the one flowing into main -# we will keep our main experimental insertions to make sure everything is alright -schedules: - - cron: '0 3 * * 1,3,5' # Runs every Monday, Wednesday and Friday at 3AM UTC - displayName: Experimental VS insertion main - branches: - include: - - main - always: false # Don't run if there are no code changes - resources: pipelines: - pipeline: 'MSBuild' diff --git a/azure-pipelines/vs-insertion.yml b/azure-pipelines/vs-insertion.yml index 2f2046c4687..2f8d8732f04 100644 --- a/azure-pipelines/vs-insertion.yml +++ b/azure-pipelines/vs-insertion.yml @@ -12,13 +12,13 @@ trigger: none pr: none name: $(Date:yyyyMMdd).$(Rev:r) -# schedules: -# - cron: '0 3 * * 1-5' # Runs every weekday at 3AM UTC -# displayName: Daily VS insertion main -# branches: -# include: -# - main -# always: false # Don't run if there are no code changes +schedules: + - cron: '0 3 * * 1-5' # Runs every weekday at 3AM UTC + displayName: Daily VS insertion main + branches: + include: + - main + always: false # Don't run if there are no code changes resources: pipelines: @@ -66,7 +66,7 @@ variables: # `auto` should work every time and selecting a branch in parameters is likely to fail due to incompatible versions in MSBuild and VS - name: AutoInsertTargetBranch ${{ if eq(variables['Build.SourceBranchName'], 'vs17.14') }}: - value: 'main' + value: 'rel/d17.14' ${{ elseif eq(variables['Build.SourceBranchName'], 'vs17.13') }}: value: 'rel/d17.13' ${{ elseif eq(variables['Build.SourceBranchName'], 'vs17.12') }}: From a9d68ab58eab3c25b6378a90be8060dd1429a6ef Mon Sep 17 00:00:00 2001 From: Tanya Solyanik Date: Thu, 17 Apr 2025 14:26:06 -0700 Subject: [PATCH 095/106] removed dead code that had been removed in NETFX (#11742) And some minor clean-up changes: * added readonly to a field * added parameter names to function parameters --- src/Tasks/ManifestUtil/SecurityUtil.cs | 9 +- src/Tasks/ManifestUtil/mansign2.cs | 144 ++++++++----------------- 2 files changed, 48 insertions(+), 105 deletions(-) diff --git a/src/Tasks/ManifestUtil/SecurityUtil.cs b/src/Tasks/ManifestUtil/SecurityUtil.cs index a737ed59012..0013fbde3e2 100644 --- a/src/Tasks/ManifestUtil/SecurityUtil.cs +++ b/src/Tasks/ManifestUtil/SecurityUtil.cs @@ -502,7 +502,7 @@ public static PermissionSet XmlToPermissionSet(XmlElement element) [SupportedOSPlatform("windows")] public static void SignFile(string certThumbprint, Uri timestampUrl, string path) { - SignFile(certThumbprint, timestampUrl, path, null, null); + SignFile(certThumbprint, timestampUrl, path, targetFrameworkVersion: null, targetFrameworkIdentifier: null); } /// @@ -518,7 +518,7 @@ public static void SignFile(string certThumbprint, string path, string targetFrameworkVersion) { - SignFile(certThumbprint, timestampUrl, path, targetFrameworkVersion, null); + SignFile(certThumbprint, timestampUrl, path, targetFrameworkVersion, targetFrameworkIdentifier: null); } /// @@ -536,7 +536,7 @@ public static void SignFile(string certThumbprint, string targetFrameworkVersion, string targetFrameworkIdentifier) { - SignFile(certThumbprint, timestampUrl, path, targetFrameworkVersion, targetFrameworkIdentifier, false); + SignFile(certThumbprint, timestampUrl, path, targetFrameworkVersion, targetFrameworkIdentifier, disallowMansignTimestampFallback: false); } /// @@ -637,7 +637,7 @@ public static void SignFile(X509Certificate2 cert, Uri timestampUrl, string path { // setup resources System.Resources.ResourceManager resources = new System.Resources.ResourceManager("Microsoft.Build.Tasks.Core.Strings.ManifestUtilities", typeof(SecurityUtilities).Module.Assembly); - SignFileInternal(cert, timestampUrl, path, true, resources); + SignFileInternal(cert, timestampUrl, path, targetFrameworkSupportsSha256: true, resources); } [SupportedOSPlatform("windows")] @@ -701,6 +701,7 @@ private static void SignFileInternal(X509Certificate2 cert, { doc.Load(xr); } + var manifest = new SignedCmiManifest2(doc, useSha256); CmiManifestSigner2 signer; if (useSha256 && rsa is RSACryptoServiceProvider rsacsp) diff --git a/src/Tasks/ManifestUtil/mansign2.cs b/src/Tasks/ManifestUtil/mansign2.cs index d23e63c0870..522a2173045 100644 --- a/src/Tasks/ManifestUtil/mansign2.cs +++ b/src/Tasks/ManifestUtil/mansign2.cs @@ -550,126 +550,68 @@ private static void ReplacePublicKeyToken(XmlDocument manifestDom, AsymmetricAlg } } + [SuppressMessage("Security", "CA5350:Do Not Use Weak Cryptographic Algorithms", Justification = "SHA1 is retained for compatibility reasons as an option in VisualStudio signing page and consequently in the trust manager, default is SHA2.")] private static byte[] ComputeHashFromManifest(XmlDocument manifestDom, bool useSha256) { -#if (true) // BUGBUG: Remove before RTM when old format support is no longer needed. - return ComputeHashFromManifest(manifestDom, false, useSha256); - } + // Since the DOM given to us is not guaranteed to be normalized, + // we need to normalize it ourselves. Also, we always preserve + // white space as Fusion XML engine always preserve white space. + XmlDocument normalizedDom = new XmlDocument(); + normalizedDom.PreserveWhitespace = true; + + // Normalize the document + using (TextReader stringReader = new StringReader(manifestDom.OuterXml)) + { + XmlReaderSettings settings = new XmlReaderSettings(); + settings.DtdProcessing = DtdProcessing.Parse; + using (XmlReader reader = XmlReader.Create(stringReader, settings, manifestDom.BaseURI)) + { + normalizedDom.Load(reader); + } + } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Security", "CA5350:Do Not Use Weak Cryptographic Algorithms", Justification = "SHA1 is retained for compatibility reasons as an option in VisualStudio signing page and consequently in the trust manager, default is SHA2.")] - private static byte[] ComputeHashFromManifest(XmlDocument manifestDom, bool oldFormat, bool useSha256) - { - if (oldFormat) - { - XmlDsigExcC14NTransform exc = new XmlDsigExcC14NTransform(); - exc.LoadInput(manifestDom); + XmlDsigExcC14NTransform exc = new XmlDsigExcC14NTransform(); + exc.LoadInput(normalizedDom); - if (useSha256) - { + if (useSha256) + { #pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - using (SHA256 sha2 = SHA256.Create( + using (SHA256 sha2 = SHA256.Create( #if FEATURE_CRYPTOGRAPHIC_FACTORY_ALGORITHM_NAMES - "System.Security.Cryptography.SHA256CryptoServiceProvider" + "System.Security.Cryptography.SHA256CryptoServiceProvider" #endif - )) + )) #pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter - { - byte[] hash = sha2.ComputeHash(exc.GetOutput() as MemoryStream); - if (hash == null) - { - throw new CryptographicException(Win32.TRUST_E_BAD_DIGEST); - } - - return hash; - } - } - else { -#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - // codeql[cs/weak-crypto] SHA1 is retained for compatibility reasons as an option in VisualStudio signing page and consequently in the trust manager, default is SHA2. https://devdiv.visualstudio.com/DevDiv/_workitems/edit/139025 - using (SHA1 sha1 = SHA1.Create( -#if FEATURE_CRYPTOGRAPHIC_FACTORY_ALGORITHM_NAMES - "System.Security.Cryptography.SHA1CryptoServiceProvider" -#endif - )) -#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter + byte[] hash = sha2.ComputeHash(exc.GetOutput() as MemoryStream); + if (hash == null) { - byte[] hash = sha1.ComputeHash(exc.GetOutput() as MemoryStream); - if (hash == null) - { - throw new CryptographicException(Win32.TRUST_E_BAD_DIGEST); - } - - return hash; + throw new CryptographicException(Win32.TRUST_E_BAD_DIGEST); } + + return hash; } } else { -#endif - // Since the DOM given to us is not guaranteed to be normalized, - // we need to normalize it ourselves. Also, we always preserve - // white space as Fusion XML engine always preserve white space. - XmlDocument normalizedDom = new XmlDocument(); - normalizedDom.PreserveWhitespace = true; - - // Normalize the document - using (TextReader stringReader = new StringReader(manifestDom.OuterXml)) - { - XmlReaderSettings settings = new XmlReaderSettings(); - settings.DtdProcessing = DtdProcessing.Parse; - using (XmlReader reader = XmlReader.Create(stringReader, settings, manifestDom.BaseURI)) - { - normalizedDom.Load(reader); - } - } - - XmlDsigExcC14NTransform exc = new XmlDsigExcC14NTransform(); - exc.LoadInput(normalizedDom); - - if (useSha256) - { #pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - using (SHA256 sha2 = SHA256.Create( + // codeql[cs/weak-crypto] SHA1 is retained for compatibility reasons as an option in VisualStudio signing page and consequently in the trust manager, default is SHA2. https://devdiv.visualstudio.com/DevDiv/_workitems/edit/139025 + using (SHA1 sha1 = SHA1.Create( #if FEATURE_CRYPTOGRAPHIC_FACTORY_ALGORITHM_NAMES - "System.Security.Cryptography.SHA256CryptoServiceProvider" + "System.Security.Cryptography.SHA1CryptoServiceProvider" #endif - )) + )) #pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter - { - byte[] hash = sha2.ComputeHash(exc.GetOutput() as MemoryStream); - if (hash == null) - { - throw new CryptographicException(Win32.TRUST_E_BAD_DIGEST); - } - - return hash; - } - } - else { -#pragma warning disable SA1111, SA1009 // Closing parenthesis should be on line of last parameter - // codeql[cs/weak-crypto] SHA1 is retained for compatibility reasons as an option in VisualStudio signing page and consequently in the trust manager, default is SHA2. https://devdiv.visualstudio.com/DevDiv/_workitems/edit/139025 - using (SHA1 sha1 = SHA1.Create( -#if FEATURE_CRYPTOGRAPHIC_FACTORY_ALGORITHM_NAMES - "System.Security.Cryptography.SHA1CryptoServiceProvider" -#endif - )) -#pragma warning restore SA1111, SA1009 // Closing parenthesis should be on line of last parameter + byte[] hash = sha1.ComputeHash(exc.GetOutput() as MemoryStream); + if (hash == null) { - byte[] hash = sha1.ComputeHash(exc.GetOutput() as MemoryStream); - if (hash == null) - { - throw new CryptographicException(Win32.TRUST_E_BAD_DIGEST); - } - - return hash; + throw new CryptographicException(Win32.TRUST_E_BAD_DIGEST); } - } -#if (true) // BUGBUG: Remove before RTM when old format support is no longer needed. + return hash; + } } -#endif } private const string AssemblyNamespaceUri = "urn:schemas-microsoft-com:asm.v1"; @@ -739,8 +681,8 @@ private static void AuthenticodeSignLicenseDom(XmlDocument licenseDom, CmiManife signedXml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigExcC14NTransformUrl; if (signer.UseSha256) { - signedXml.SignedInfo.SignatureMethod = Sha256SignatureMethodUri; - } + signedXml.SignedInfo.SignatureMethod = Sha256SignatureMethodUri; + } else { signedXml.SignedInfo.SignatureMethod = Sha1SignatureMethodUri; @@ -1108,12 +1050,12 @@ internal class CmiManifestSigner2 private X509Certificate2Collection _certificates; private X509IncludeOption _includeOption; private CmiManifestSignerFlag _signerFlag; - private bool _useSha256; + private readonly bool _useSha256; private CmiManifestSigner2() { } internal CmiManifestSigner2(AsymmetricAlgorithm strongNameKey) : - this(strongNameKey, null, false) + this(strongNameKey, certificate: null, useSha256: false) { } internal CmiManifestSigner2(AsymmetricAlgorithm strongNameKey, X509Certificate2 certificate, bool useSha256) @@ -1311,7 +1253,7 @@ internal CmiAuthenticodeSignerInfo(int errorCode) } internal CmiAuthenticodeSignerInfo(Win32.AXL_SIGNER_INFO signerInfo, - Win32.AXL_TIMESTAMPER_INFO timestamperInfo) + Win32.AXL_TIMESTAMPER_INFO timestamperInfo) { _error = (int)signerInfo.dwError; if (signerInfo.pChainContext != IntPtr.Zero) From b99f36ba1c3c0971c044a7739ef12c8be30af4ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Provazn=C3=ADk?= Date: Tue, 22 Apr 2025 10:35:43 +0200 Subject: [PATCH 096/106] log telemetry load failures (#11613) * log telemetry load failure * fix buildmanager state in test * fix usings broken by merge * fix race condition --- .../Telemetry/OpenTelemetryManager_Tests.cs | 20 ++++++++++++++++ .../Telemetry/Telemetry_Tests.cs | 2 -- .../BackEnd/BuildManager/BuildManager.cs | 23 ++++++++++++++++++- src/Build/Resources/Strings.resx | 3 +++ src/Build/Resources/xlf/Strings.cs.xlf | 5 ++++ src/Build/Resources/xlf/Strings.de.xlf | 5 ++++ src/Build/Resources/xlf/Strings.es.xlf | 5 ++++ src/Build/Resources/xlf/Strings.fr.xlf | 5 ++++ src/Build/Resources/xlf/Strings.it.xlf | 5 ++++ src/Build/Resources/xlf/Strings.ja.xlf | 5 ++++ src/Build/Resources/xlf/Strings.ko.xlf | 5 ++++ src/Build/Resources/xlf/Strings.pl.xlf | 5 ++++ src/Build/Resources/xlf/Strings.pt-BR.xlf | 5 ++++ src/Build/Resources/xlf/Strings.ru.xlf | 5 ++++ src/Build/Resources/xlf/Strings.tr.xlf | 5 ++++ src/Build/Resources/xlf/Strings.zh-Hans.xlf | 5 ++++ src/Build/Resources/xlf/Strings.zh-Hant.xlf | 5 ++++ .../Telemetry/OpenTelemetryManager.cs | 3 +++ 18 files changed, 113 insertions(+), 3 deletions(-) diff --git a/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs b/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs index 323326401c6..b10cf9465d4 100644 --- a/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs +++ b/src/Build.UnitTests/Telemetry/OpenTelemetryManager_Tests.cs @@ -2,7 +2,10 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; +using System.Linq; using System.Reflection; +using Microsoft.Build.Execution; using Microsoft.Build.Framework.Telemetry; using Microsoft.Build.UnitTests; using Shouldly; @@ -98,6 +101,23 @@ public void Initialize_ShouldNoOp_WhenCalledMultipleTimes() state2.ShouldBe(false); } + [Fact] + public void TelemetryLoadFailureIsLoggedOnce() + { + OpenTelemetryManager.Instance.LoadFailureExceptionMessage = new System.IO.FileNotFoundException().ToString(); + using BuildManager bm = new BuildManager(); + var deferredMessages = new List(); + bm.BeginBuild(new BuildParameters(), deferredMessages); + deferredMessages.ShouldContain(x => x.Text.Contains("FileNotFound")); + bm.EndBuild(); + bm.BeginBuild(new BuildParameters()); + bm.EndBuild(); + + // should not add message twice + int count = deferredMessages.Count(x => x.Text.Contains("FileNotFound")); + count.ShouldBe(1); + } + /* Helper methods */ /// diff --git a/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs b/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs index 6154403ab83..6939eda86e5 100644 --- a/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs +++ b/src/Build.UnitTests/Telemetry/Telemetry_Tests.cs @@ -5,9 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; -#if NET using System.Text.Json; -#endif using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Framework.Telemetry; diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index a60bd4ed0d8..ce330e93bdd 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -459,7 +459,8 @@ private void UpdatePriority(Process p, ProcessPriorityClass priority) /// Thrown if a build is already in progress. public void BeginBuild(BuildParameters parameters) { - OpenTelemetryManager.Instance.Initialize(isStandalone: false); + InitializeTelemetry(); + if (_previousLowPriority != null) { if (parameters.LowPriority != _previousLowPriority) @@ -723,6 +724,26 @@ void InitializeCaches() } } + private void InitializeTelemetry() + { + OpenTelemetryManager.Instance.Initialize(isStandalone: false); + string? failureMessage = OpenTelemetryManager.Instance.LoadFailureExceptionMessage; + if (_deferredBuildMessages != null && + failureMessage != null && + _deferredBuildMessages is ICollection deferredBuildMessagesCollection) + { + deferredBuildMessagesCollection.Add( + new DeferredBuildMessage( + ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword( + "OpenTelemetryLoadFailed", + failureMessage), + MessageImportance.Low)); + + // clean up the message from OpenTelemetryManager to avoid double logging it + OpenTelemetryManager.Instance.LoadFailureExceptionMessage = null; + } + } + #if FEATURE_REPORTFILEACCESSES /// /// Configure the build to use I/O tracking for nodes. diff --git a/src/Build/Resources/Strings.resx b/src/Build/Resources/Strings.resx index 314b1b6a53c..f6b9b047fea 100644 --- a/src/Build/Resources/Strings.resx +++ b/src/Build/Resources/Strings.resx @@ -2392,6 +2392,9 @@ Utilization: {0} Average Utilization: {1:###.0} succeeded: {0} {0} whole number + + Loading telemetry libraries failed with exception: {0}. + + + + + + From a034dbdd3fb899811b31eab12d944ee6548e8cb1 Mon Sep 17 00:00:00 2001 From: Jenny Bai Date: Thu, 24 Apr 2025 10:01:15 +0000 Subject: [PATCH 100/106] Add more info to ETL traces (#11743) Add these info to etl traces: Add a cancellation started event to better track the timeline of cancellation (when a customer attempts to cancel a build) Add Target List to build project start event ((now it's only on build stop event) --- src/Build/BackEnd/BuildManager/BuildManager.cs | 1 + .../Components/RequestBuilder/RequestBuilder.cs | 12 +++++++----- src/Framework/MSBuildEventSource.cs | 13 ++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 1a3de410eff..667e0ba6616 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -800,6 +800,7 @@ private static void AttachDebugger() /// public void CancelAllSubmissions() { + MSBuildEventSource.Log.CancelSubmissionsStart(); CancelAllSubmissions(true); } diff --git a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs index 2359a42d686..3f08179f500 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs @@ -1106,8 +1106,6 @@ private async Task BuildProject() // logged with the node logging context _projectLoggingContext = null; - MSBuildEventSource.Log.BuildProjectStart(_requestEntry.RequestConfiguration.ProjectFullPath); - try { // Load the project @@ -1145,6 +1143,13 @@ private async Task BuildProject() try { + // Determine the set of targets we need to build + (string name, TargetBuiltReason reason)[] allTargets = _requestEntry.RequestConfiguration + .GetTargetsUsedToBuildRequest(_requestEntry.Request).ToArray(); + if (MSBuildEventSource.Log.IsEnabled()) + { + MSBuildEventSource.Log.BuildProjectStart(_requestEntry.RequestConfiguration.ProjectFullPath, string.Join(", ", allTargets)); + } HandleProjectStarted(buildCheckManager); // Make sure to extract known immutable folders from properties and register them for fast up-to-date check @@ -1162,9 +1167,6 @@ private async Task BuildProject() _requestEntry.Request.BuildEventContext = _projectLoggingContext.BuildEventContext; - // Determine the set of targets we need to build - (string name, TargetBuiltReason reason)[] allTargets = _requestEntry.RequestConfiguration - .GetTargetsUsedToBuildRequest(_requestEntry.Request).ToArray(); ProjectErrorUtilities.VerifyThrowInvalidProject(allTargets.Length > 0, _requestEntry.RequestConfiguration.Project.ProjectFileLocation, "NoTargetSpecified"); diff --git a/src/Framework/MSBuildEventSource.cs b/src/Framework/MSBuildEventSource.cs index 4b403ebdf1d..5ed8c01507d 100644 --- a/src/Framework/MSBuildEventSource.cs +++ b/src/Framework/MSBuildEventSource.cs @@ -79,11 +79,12 @@ public void BuildStop() /// /// Call this method to notify listeners of information of how a project file built. /// Filename of the project being built. + /// Names of the targets that built. /// - [Event(5, Keywords = Keywords.All | Keywords.PerformanceLog)] - public void BuildProjectStart(string projectPath) + [Event(5, Keywords = Keywords.All | Keywords.PerformanceLog, Version = 1)] + public void BuildProjectStart(string projectPath, string targets) { - WriteEvent(5, projectPath); + WriteEvent(5, projectPath, targets); } /// Filename of the project being built. @@ -672,6 +673,12 @@ public void ProjectCacheHandleBuildResultStop(string pluginTypeName, string proj { WriteEvent(92, pluginTypeName, projectPath, targets); } + + [Event(93, Keywords = Keywords.All)] + public void CancelSubmissionsStart() + { + WriteEvent(93); + } #endregion } } From 34ccf7f74382b73388ed97c3ff1d678b037a0647 Mon Sep 17 00:00:00 2001 From: Djuradj Kurepa <91743470+dkurepa@users.noreply.github.com> Date: Thu, 24 Apr 2025 12:16:20 +0200 Subject: [PATCH 101/106] Update Version.Details.xml (#11760) --- eng/Version.Details.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2b8dfaead48..54efc62ca37 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,5 +1,6 @@ + From d40ed5884d80b92a5fc13adadb5ab3a893243b52 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Thu, 24 Apr 2025 18:37:26 -0700 Subject: [PATCH 102/106] Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11466882 (#11761) Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11466940 --- src/Build/Resources/xlf/Strings.cs.xlf | 2 +- src/Build/Resources/xlf/Strings.es.xlf | 2 +- src/Build/Resources/xlf/Strings.fr.xlf | 2 +- src/Build/Resources/xlf/Strings.it.xlf | 2 +- src/Build/Resources/xlf/Strings.ja.xlf | 2 +- src/Build/Resources/xlf/Strings.pt-BR.xlf | 2 +- src/Build/Resources/xlf/Strings.ru.xlf | 2 +- src/Build/Resources/xlf/Strings.zh-Hans.xlf | 2 +- src/Build/Resources/xlf/Strings.zh-Hant.xlf | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Build/Resources/xlf/Strings.cs.xlf b/src/Build/Resources/xlf/Strings.cs.xlf index 79f24a9c962..57283f86965 100644 --- a/src/Build/Resources/xlf/Strings.cs.xlf +++ b/src/Build/Resources/xlf/Strings.cs.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + Načítání knihoven telemetrie se nezdařilo s výjimkou: {0}. diff --git a/src/Build/Resources/xlf/Strings.es.xlf b/src/Build/Resources/xlf/Strings.es.xlf index 000d1aadc1e..ecb3a3815d6 100644 --- a/src/Build/Resources/xlf/Strings.es.xlf +++ b/src/Build/Resources/xlf/Strings.es.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + Error al cargar las bibliotecas de telemetría con la excepción: {0}. diff --git a/src/Build/Resources/xlf/Strings.fr.xlf b/src/Build/Resources/xlf/Strings.fr.xlf index 44f4c3de7e7..de96b1472c8 100644 --- a/src/Build/Resources/xlf/Strings.fr.xlf +++ b/src/Build/Resources/xlf/Strings.fr.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + Nous n’avons pas pu charger les bibliothèques de télémétrie avec l’exception : {0}. diff --git a/src/Build/Resources/xlf/Strings.it.xlf b/src/Build/Resources/xlf/Strings.it.xlf index 6e64c1a3725..d4acfbaa817 100644 --- a/src/Build/Resources/xlf/Strings.it.xlf +++ b/src/Build/Resources/xlf/Strings.it.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + Caricamento delle librerie di telemetria non riuscito con eccezione: {0}. diff --git a/src/Build/Resources/xlf/Strings.ja.xlf b/src/Build/Resources/xlf/Strings.ja.xlf index 2aa67912b6e..8de15be3a29 100644 --- a/src/Build/Resources/xlf/Strings.ja.xlf +++ b/src/Build/Resources/xlf/Strings.ja.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + テレメトリ ライブラリの読み込みが次の例外で失敗しました: {0}。 diff --git a/src/Build/Resources/xlf/Strings.pt-BR.xlf b/src/Build/Resources/xlf/Strings.pt-BR.xlf index a887cb14856..f975fac9acb 100644 --- a/src/Build/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Build/Resources/xlf/Strings.pt-BR.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + Falha ao carregar as bibliotecas de telemetria com a exceção: {0}. diff --git a/src/Build/Resources/xlf/Strings.ru.xlf b/src/Build/Resources/xlf/Strings.ru.xlf index 2ae28d66f1d..83bbbb6e1d1 100644 --- a/src/Build/Resources/xlf/Strings.ru.xlf +++ b/src/Build/Resources/xlf/Strings.ru.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + Не удалось загрузить библиотеки телеметрии с исключением: {0}. diff --git a/src/Build/Resources/xlf/Strings.zh-Hans.xlf b/src/Build/Resources/xlf/Strings.zh-Hans.xlf index 32f048e6f18..46aef2401a8 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hans.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + 加载遥测库失败,出现异常: {0}。 diff --git a/src/Build/Resources/xlf/Strings.zh-Hant.xlf b/src/Build/Resources/xlf/Strings.zh-Hant.xlf index 9462e35e3fa..1b5d8c86b82 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hant.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + 載入遙測程式庫時發生例外狀況: {0}。 From 18c34c03dc3db4d5f06c4bae4a7fceed0c91bb4d Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Fri, 25 Apr 2025 07:53:44 -0700 Subject: [PATCH 103/106] Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 11473012 (#11764) --- src/Build/Resources/xlf/Strings.de.xlf | 2 +- src/Build/Resources/xlf/Strings.ko.xlf | 2 +- src/Build/Resources/xlf/Strings.pl.xlf | 2 +- src/Build/Resources/xlf/Strings.tr.xlf | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Build/Resources/xlf/Strings.de.xlf b/src/Build/Resources/xlf/Strings.de.xlf index 2fd74f8aaef..985615208fb 100644 --- a/src/Build/Resources/xlf/Strings.de.xlf +++ b/src/Build/Resources/xlf/Strings.de.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + Fehler beim Laden von Telemetriebibliotheken. Ausnahme:{0}. diff --git a/src/Build/Resources/xlf/Strings.ko.xlf b/src/Build/Resources/xlf/Strings.ko.xlf index 6bfb40996dc..03bc234c5ef 100644 --- a/src/Build/Resources/xlf/Strings.ko.xlf +++ b/src/Build/Resources/xlf/Strings.ko.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + 예외 {0}(으)로 인해 원격 분석 라이브러리를 로드하지 못했습니다. diff --git a/src/Build/Resources/xlf/Strings.pl.xlf b/src/Build/Resources/xlf/Strings.pl.xlf index 2bbc1d0cfa7..019d29d3fb0 100644 --- a/src/Build/Resources/xlf/Strings.pl.xlf +++ b/src/Build/Resources/xlf/Strings.pl.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + Ładowanie bibliotek telemetrii nie powiodło się. Wyjątek: {0}. diff --git a/src/Build/Resources/xlf/Strings.tr.xlf b/src/Build/Resources/xlf/Strings.tr.xlf index b417c027cde..418670f2595 100644 --- a/src/Build/Resources/xlf/Strings.tr.xlf +++ b/src/Build/Resources/xlf/Strings.tr.xlf @@ -632,7 +632,7 @@ Loading telemetry libraries failed with exception: {0}. - Loading telemetry libraries failed with exception: {0}. + Telemetri kitaplıklarının yüklenmesi şu hayatla başarısız oldu: {0}. From 9ac084356adce82b0329dae5facd292685665112 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 16:54:10 +0200 Subject: [PATCH 104/106] Update dependencies from https://github.com/dotnet/source-build-reference-packages build 20250423.3 (#11757) Microsoft.SourceBuild.Intermediate.source-build-reference-packages From Version 9.0.0-alpha.1.25209.1 -> To Version 9.0.0-alpha.1.25223.3 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 54efc62ca37..56b1d612471 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -3,9 +3,9 @@ - + https://github.com/dotnet/source-build-reference-packages - 7dbf5deea5bdccf513df73cba179c4c0ad106010 + 19eb5ea4e5f9c4e5256843a92805c8c9e942207d From 4734a3be7d247ea6dbdf6161227476f72ce292a9 Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 28 Apr 2025 16:58:30 +0200 Subject: [PATCH 105/106] fix attaching binlog --- src/Build/BackEnd/BuildManager/BuildManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index 667e0ba6616..fa6ae4bbbfd 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -678,7 +678,7 @@ IEnumerable AppendDebuggingLoggers(IEnumerable loggers) var logger = new BinaryLogger { Parameters = binlogPath }; - return (loggers ?? [logger]); + return (loggers ?? []).Concat([logger]); } void InitializeCaches() From 4cc7d356cb74884db8e4af27987d8d7007c2c4ab Mon Sep 17 00:00:00 2001 From: Surayya Huseyn Zada Date: Mon, 28 Apr 2025 16:58:53 +0200 Subject: [PATCH 106/106] fix another misplaced brackets --- src/Tasks/GenerateResource.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tasks/GenerateResource.cs b/src/Tasks/GenerateResource.cs index 1726600bd8c..43678c19db1 100644 --- a/src/Tasks/GenerateResource.cs +++ b/src/Tasks/GenerateResource.cs @@ -1705,7 +1705,7 @@ private void UpdateNewestUncorrelatedInputWriteTime() // Check the timestamp of each of the passed-in references to find the newest; // and then the additional inputs - ITaskItem[] inputs = this.References ?? [.. (this.AdditionalInputs ?? [])]; + var inputs = (this.References ?? []).Concat(this.AdditionalInputs ?? []); foreach (ITaskItem input in inputs) {