Merge branch 'vs17.7' into merge/vs17.6-to-vs17.7

This commit is contained in:
Jan Krivanek 2023-06-20 14:54:25 +02:00 коммит произвёл GitHub
Родитель d99c042f5c cf384834dd
Коммит f9c08e5028
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
293 изменённых файлов: 9470 добавлений и 3628 удалений

Просмотреть файл

@ -397,4 +397,4 @@ dotnet_diagnostic.IDE0251.severity = suggestion
dotnet_diagnostic.IDE0270.severity = suggestion
# naming rule violation
dotnet_diagnostic.IDE1006.severity = suggestion
dotnet_diagnostic.IDE1006.severity = suggestion

Просмотреть файл

@ -1,7 +1,7 @@
name: 💡 Feature Request
description: Suggest an idea for this project.
title: "[Feature Request]: "
labels: ["Feature Request"]
labels: ["Feature Request", "needs-triage"]
body:
- type: textarea
attributes:
@ -24,4 +24,4 @@ body:
- type: textarea
attributes:
label: Alternative Designs
description: If you have an idea how to achieve this new feature, let us know that here. Please include any pointers to code, relevant changes, or related issues you know of.
description: If you have an idea how to achieve this new feature, let us know that here. Please include any pointers to code, relevant changes, or related issues you know of.

6
.gitignore поставляемый
Просмотреть файл

@ -21,6 +21,9 @@ artifacts/
# Visual Studio 2015 cache/options directory
.vs/
# Verify result files
*.received.*
*_i.c
*_p.c
*_i.h
@ -218,3 +221,6 @@ stage1/
# .DS_Store for macOS
**/.DS_Store
# We keep launchSettings.json local
**/launchSettings.json

Просмотреть файл

@ -100,7 +100,12 @@ jobs:
- job: FullReleaseOnWindows
displayName: "Windows Full Release (no bootstrap)"
pool:
vmImage: 'windows-2022'
${{ if eq(variables['System.TeamProject'], 'public') }}:
name: NetCore-Public
demands: ImageOverride -equals windows.vs2022preview.amd64.open
${{ if ne(variables['System.TeamProject'], 'public') }}:
name: VSEngSS-MicroBuild2022-1ES
demands: agent.os -equals Windows_NT
steps:
- task: BatchScript@1
displayName: cibuild.cmd

Просмотреть файл

@ -23,12 +23,9 @@ variables:
- name: SourceBranch
value: $(IbcSourceBranchName)
# If we're not on a vs* branch, use main as our optprof collection branch
# NOTE: the code is temporarily fixed. For the branches that should use opt-prof from the main branch we should use the latest working Opt-Prof collected from main 20230217.4.
- ${{ if not(startsWith(variables['Build.SourceBranch'], 'refs/heads/vs')) }}:
- name: OptProfDrop
value: 'OptimizationData/DotNet-msbuild-Trusted/main/20230217.4/7352286/1'
- name: SourceBranch
value: ''
value: main
# if OptProfDropName is set as a parameter, set OptProfDrop to the parameter and unset SourceBranch
- ${{ if ne(parameters.OptProfDropName, 'default') }}:
- name: OptProfDrop

Просмотреть файл

@ -24,8 +24,12 @@
scripts/Deploy-MSBuild.ps1
src/Framework/README.md
src/Utilities/README.md
Special-case while MSBuild uses Arcade 6 to build: 17.7 should
continue to target .NET 7, so bump a 6 here to 7.
-->
<LatestDotNetCoreForMSBuild>net7.0</LatestDotNetCoreForMSBuild>
<LatestDotNetCoreForMSBuild>$(NetCurrent)</LatestDotNetCoreForMSBuild>
<LatestDotNetCoreForMSBuild Condition=" '$(NetCurrent)' == 'net6.0' ">net7.0</LatestDotNetCoreForMSBuild>
</PropertyGroup>
<PropertyGroup>
@ -77,10 +81,6 @@
<MachineIndependentBuild>true</MachineIndependentBuild>
</PropertyGroup>
<PropertyGroup>
<AssemblyInformationCachePaths Condition="Exists('$(NetCoreRoot)sdk\$(NetCoreSdkVersion)\SdkPrecomputedAssemblyReferences.cache')">$(AssemblyInformationCachePaths);$(NetCoreRoot)sdk\$(NetCoreSdkVersion)\SDKPrecomputedAssemblyReferences.cache</AssemblyInformationCachePaths>
</PropertyGroup>
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);*.log</DefaultItemExcludes>
<DefaultItemExcludes>$(DefaultItemExcludes);*.binlog</DefaultItemExcludes>

Просмотреть файл

@ -1,34 +0,0 @@
<Project>
<!--
This is for the internal orchestrated build scenarios and will likely never be run on a
developer's machine. The official build definition builds this file directly.
-->
<PropertyGroup>
<FeedTasksPackage>Microsoft.DotNet.Build.Tasks.Feed</FeedTasksPackage>
<!-- This version should be kept in sync with `project.json` -->
<FeedTasksPackageVersion>2.1.0-prerelease-02419-02</FeedTasksPackageVersion>
</PropertyGroup>
<Import Project="$(MSBuildThisFileDirectory)packages\$(FeedTasksPackage)\$(FeedTasksPackageVersion)\build\$(FeedTasksPackage).targets" />
<ItemGroup>
<ItemsToPush Include="$(MSBuildThisFileDirectory)bin\Packages\*.nupkg" />
</ItemGroup>
<Target Name="Build">
<PushToBlobFeed ExpectedFeedUrl="$(ExpectedFeedUrl)"
AccountKey="$(AccountKey)"
ItemsToPush="@(ItemsToPush)"
Overwrite="$(PublishOverwrite)"
ManifestBranch="$(ManifestBranch)"
ManifestBuildId="$(ManifestBuildId)"
ManifestCommit="$(ManifestCommit)"
ManifestName="msbuild"
SkipCreateManifest="false" />
</Target>
</Project>

Просмотреть файл

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink

Просмотреть файл

@ -47,7 +47,7 @@ When `Clean`ing the output of a project, `CleanReferencedProjects` ensures that
## Targets required to be referenceable
These targets should exist in a project to be compatible with the common targets' `ProjectReference`. Some are called only conditionally.
These targets should exist in a project to be compatible with the common targets' `ProjectReference` (unless [marked with the `SkipNonexistentTargets='true'` metadatum](#targets-marked-with-skipnonexistenttargetstrue-metadatum)). Some are called only conditionally.
These targets are all defined in `Microsoft.Common.targets` and are defined in Microsoft SDKs. You should only have to implement them yourself if you require custom behavior or are authoring a project that doesn't import the common targets.
@ -85,6 +85,10 @@ If implementing a project with an “outer” (determine what properties to pass
* As of 15.7, this is _optional_. If a project does not contain a `GetCopyToOutputDirectoryItems` target, projects that reference it will not copy any of its outputs to their own output folders, but the build can succeed.
* `Clean` should delete all outputs of the project.
* It is not called during a normal build, only during "Clean" and "Rebuild".
### Targets Marked With `SkipNonexistentTargets='true'` Metadatum
`GetTargetFrameworks` and `GetTargetFrameworksWithPlatformForSingleTargetFramework` are skippable if nonexistent since some project types (for example, `wixproj` projects) may not define them. See [this comment](https://github.com/dotnet/msbuild/blob/cc55017f88688cbe3f9aa810cdf44273adea76ea/src/Tasks/Microsoft.Managed.After.targets#L74-L77) for more details.
## Other protocol requirements
As with all MSBuild logic, targets can be added to do other work with `ProjectReference`s.

Просмотреть файл

@ -39,6 +39,7 @@ The folder contains collection of docs and references for MSBuild, detailed info
### Problems?
* [Rebuilding when nothing changed](wiki/Rebuilding-when-nothing-changed.md)
* [Controling References Behavior](wiki/Controlling-Dependencies-Behavior.md)
* [Something's wrong in my build](wiki/Something's-wrong-in-my-build.md)
* [Some gotchas around the Microsoft.Build.Framework project/assembly](wiki/Microsoft.Build.Framework.md)
* [GAC and MSBuild](wiki/UnGAC.md)
@ -50,16 +51,17 @@ The folder contains collection of docs and references for MSBuild, detailed info
* [`ProjectReference`](ProjectReference-Protocol.md)
* [MSBuild Server](MSBuild-Server.md)
* [Low priority nodes](specs/low-priority-switch.md)
* [Threading in MSBuild worker nodes](specs/threading.md)
* [Nodes orchestration](wiki/Nodes-Orchestration.md)
* [Project cache plugin](specs/project-cache.md)
* [Support for remote host objects](specs/remote-host-object.md)
* [Static graph](specs/static-graph.md)
* [Single project isolated builds: implementation details](specs/single-project-isolated-builds.md)
* [Task isolation](specs/task-isolation-and-dependencies.md)
* [Threading in MSBuild worker nodes](specs/threading.md)
* [Target maps](wiki/Target-Maps.md)
* [Managing parallelism in MSBuild](specs/resource-management.md)
* [SDK resolution](specs/sdk-resolvers-algorithm.md)
* [Nodes orchestration](wiki/Nodes-Orchestration.md)
* [RAR core scenarios](specs/rar-core-scenarios.md)
### Tasks
@ -74,11 +76,14 @@ The folder contains collection of docs and references for MSBuild, detailed info
* [Binary log](wiki/Binary-Log.md)
* [Live logger: how to opt in](livelogger/Opt-In-Mechanism.md)
## Designs
* [Resolve Assembly Reference as a service](design/rar-as-service.md)
## Archived Designs
* [Resolve Assembly Reference as a service](specs/rar-as-service.md)
* Prototype: https://github.com/dotnet/msbuild/issues/6193
## Proposed Designs
* [Packages Sourcing](specs/proposed/interactive-package-references.md)
* [Secrets Metadata](specs/proposed/security-metadata.md)
## Community contributions
* [MSBuild overview](Contributions/MSBuild-overview.md)

Просмотреть файл

@ -29,7 +29,7 @@ EventSource is primarily used to profile code. For MSBuild specifically, a major
| RarComputeClosure | Resolves references from, for example, properties to explicit values. Used in resolving assembly references (RAR). |
| RarLogResults | Logs the results from having resolved assembly references (RAR). |
| RarOverall | Initiates the process of resolving assembly references (RAR). |
| RarRemoveReferencesMarkedForExclusion | Removes blacklisted references from the reference table, putting primary and dependency references in invalid file lists. |
| RarRemoveReferencesMarkedForExclusion | Removes denylisted references from the reference table, putting primary and dependency references in invalid file lists. |
| RequestThreadProc | A function to requesting a new builder thread. |
| ReusableStringBuilderFactory | Uses and resizes (if necessary) of ReusableStringBuilders. |
| ReusableStringBuilderFactoryUnbalanced | Identifies improper usage from multiple threads or buggy code: multiple Gets were called without a Relase. |

Просмотреть файл

@ -0,0 +1,253 @@
# Security Metadata
The feature is meant to improve the security of builds executed via MSBuild, by reducing the chances of spilling secrets (and possibly other sensitive data) from otherwise secured or/and inaccessible build environments.
It builds upon the other efforts reducing the cases accidentaly logging secrets - ['not logging unused environemnt variables'](https://github.com/dotnet/msbuild/pull/7484), 'redacting known secret patterns' (internal, by @michaelcfanning). Distinction here is that we want to give users option how to configure their build scripts and build data so that they can indicate what contains secret/sensitive data and shouldn't get output into logs.
The feature is envisioned to be delivered in multiple interations, while first itearation will be facilitated via global items and/or properties that will be indicating masking logging of specific types of data in log entries (hence no syntactic changes will be imposed for now).
# North Star / Longer-term vision
We envision MSBuild to have a first-class-citisen type system for it's data and tasks. 'Secret' would be one of the data types - allowable to be passed only to other variables or task inputs denoted as 'secret' (so e.g. it would not be possible to pass secrets to [`WriteLinesToFile` task](https://learn.microsoft.com/en-us/visualstudio/msbuild/writelinestofile-task)) and vice versa 'secret' task input or data type could be initialized/evaluated only from other 'secrets' or predefined external sources of data - environment variables, commandline arguments, files, apropriately denoted task output parameters.
Such a strong typing would allow to hold to stronger guarantees of not spilling properly denoted sensitive data and redact them with minimal impact on build performance (as opposed to intermediate attempts that will need to perform string inspections).
**Ilustrative sample:**
```xml
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Secrets>
<!-- initialize from command line -->
<GH_token />
<!-- initialize from env -->
<ACR_login>$(ACR_login)</ACR_login>
<!-- initialize by task -->
<ACR_password />
</Secrets>
<UsingTask TaskName="ReadCreadentialFromValut" AssemblyFile="$(MSBuildProjectDirectory)/Tasks/ACR-Tasks.dll" />
<UsingTask TaskName="PushImageToACR" AssemblyFile="$(MSBuildProjectDirectory)/Tasks/ACR-Tasks.dll" />
<Target Name='PushImage'>
<Message Text="Pushin image to ACR" />
<ReadCreadentialFromValut
Key="$(ACR_password_key)"
>
<Output TaskParameter="Value" PropertyName="ACR_password"/>
</ReadCreadentialFromValut>
<PushImageToACR
Login="$(ACR_login)"
Password="$(ACR_password)"
/>
</Target>
</Project>
```
```cs
ReadCreadentialFromValut : Task
{
/// <summary>
/// Key to be fetched
/// </summary>
public string Key { get; set; }
/// <summary>
/// Fetched value
/// </summary>
[Output]
[Secret]
public string Value { get; set; }
// ... Execute() ...
}
```
```cs
PushImageToACR : Task
{
/// <summary>
/// Azure Container Registry Login
/// </summary>
public Secret Login { get; set; }
/// <summary>
/// Azure Container Registry Password
/// </summary>
public Secret Password { get; set; }
// ... Execute() ...
}
```
An opt-out mechanism would allow usage of properly denoted tasks with plain string input data (and vice versa) - to allow smoother gradual onboarding to the new type system, without the need to rework the entire build script suite at one shot.
# Scope of initial iteration
## In scope
* Following data can be opted-in for redacting:
* property values
* item values
* item metadata values
* all item metadata
* any combination of above
* task input parameters (to denote that task is requiring sensitive data and only such can be passed in)
* task OutputItems (This can be handy in cases similar to [`ReadLinesFromFile` task](https://learn.microsoft.com/en-us/visualstudio/msbuild/readlinesfromfile-task))
* Redacting the above will happen in all log events before they are being sent to subscribed loggers.
* Redacting will apply to data initializations and passing:
* task input parameters
* task OutputItems
* transfering value to other properties/items via evaluation, transforms, flattening, [Property functions](https://learn.microsoft.com/en-us/visualstudio/msbuild/property-functions), [Item functions](https://learn.microsoft.com/en-us/visualstudio/msbuild/item-functions)
* initialization from environemnt variables or command line
* Redacting **will NOT** occure on:
* log events emited from tasks (this might be added as extra opt-in option - but would lead to significant build performance degradation).
* any other alternative output of tasks (direct writes to file system, network connections etc.)
## Out of scope
* Redacting **will NOT** occure on:
* Log events emited from tasks (this might be added as extra opt-in option - but would lead to significant build performance degradation).
* Any other alternative output of tasks (direct writes to file system, network connections etc.)
* MSBuild xml nodes (elements/attributes) names. (Sensitive data within MSBuild script itself is strongly discouraged)
* Passing values to task and there embedding into additional text and passing out as output parameter - unless such is explicitly marked as containing sensitive data.
* Encrypting/securing data in memory during therun of the build.
# User interaction
There needs to be a way how user specifies which data should be redacted from logs. We have several options:
* New data type - this is part of the [North Star vision](#north-star--longer-term-vision), but is out of scope for the initial iteration.
* [Not recomended] Denoting those via some metadata on a definition of the data to be redacted - this has two main drawbacks - a) For some data types (properties, metadata) we'd need new constructs how to attach additional info (property metadata; item meta-metadata). b) some data can be defined implicitly or dynamicaly
* Property with global scope - e.g.
```xml
<DataToRedactFromLogs>Foo;Bar;Baz->SomeMetadata;MyItem->*</DataToRedactFromLogs>
```
single property might look bit cryptic for denoting different data types. On the other hand it might be more efficient in simple redacting scenarios (pointing to a set of regexes; single sustom redactor etc.) and would allow limiting the log events pre-buffering needs.
* Item with global scope - e.g.
```xml
<ItemGroup>
<!-- Redacting property content based on the name of the property (or environment variable) -->
<DataToRedactFromLogs Include="Foo" Type="Property">
</DataToRedactFromLogs>
<!-- Redacting item content based on the name of the item. Metadat are not redacted -->
<DataToRedactFromLogs Include="Bar" Type="Item" RedactValue=True>
</DataToRedactFromLogs>
<!-- Redacting item metadata content based on the name. -->
<DataToRedactFromLogs Include="Baz" Type="Item" RedactValue=False Metadata="SomeMetadata">
</DataToRedactFromLogs>
<!-- Redacting all metadata content of specific item based on the name of the item. -->
<DataToRedactFromLogs Include="MyItem" Type="Item" RedactValue=False Metadata="*" />
<!-- Redacting property content passed from the task. At the same time requiring that the data receiving the output of the task are denoted as secret as well. -->
<DataToRedactFromLogs Include="OutputA" Type="TaskOutput" TaskName="TaskX" />
<!-- Redacting task parameter value. At the same time requiring that the data passed to the parameter of the task are denoted as secret as well. -->
<DataToRedactFromLogs Include="ParamA" Type="TaskParameter" TaskName="TaskX" />
</DataToRedactFromLogs>
</ItemGroup>
```
This can offer a more chatty, but better understandable (and possibly beter script generatable) way of denoting the redacting intent.
* A regex on *value* to redact above discused data types based on their content - e.g.:
```xml
<ItemGroup>
<!-- Redact GH tokens based on https://github.blog/changelog/2021-03-31-authentication-token-format-updates-are-generally-available -->
<DataToRedactFromLogs Include="ghp_[A-Za-z0-9_]" Type="ValueRegex">
</DataToRedactFromLogs>
</ItemGroup>
```
This way we can give build architects a tool to define common `.props` files opting-in for redacting specific types strings known to be tokens/secrets/sensitive data, without the need to guess under which properties or items they would show within the build
* A custom plugin flagging values for redaction. e.g.:
```xml
<ItemGroup>
<DataToRedactFromLogs Include="MySecretsClassifier.dll,Contoso.Secrets.Classifier.ClassifySecrets" Type="ValueClassifierPlugin">
</DataToRedactFromLogs>
</ItemGroup>
```
where:
```csharp
Contoso.Secrets;
public class Classifier: IValueClassifier
{
public ISet<string>? GetPartsToRedact(string value) {/* Logic goes here */}
}
```
This option has additional security considerations, but allows most versatile secrets redaction.
The last option can possibly be allowed to be injected via other means, that MSBuild currently uses for injecting pluggable fnctionality (command line argument; environment variable; binary placed in a specific search location)
* A built in redacting plugin - to be opted-in via env var or command line. Plugin will use same extension point as custom plugins - with extended interface allowing to provide redaction values as well:
```csharp
public interface IValueRedactor
{
public ISet<Tuple<string, string>>? GetPartsToRedact(string value);
}
```
This plugin will allow for no-touch redacting of most comon secret patterns by various providers. The default plugin is going to be provided as contribution by 1ES (by @michaelcfanning) and is currently out of scope of this document.
First presented option is not to be used. All the other options will likely be supported.
# Special considerations
* There should be no (or very minimal) performance impact to cases where redacting is not opted-in and/or to cases where there is lower/minimal level of logging. In another words - we should not spend cycles detecting and redacting secrets on log events that are not going to be loged (todo: second case might be more problematic - as loggers can decide their level of logging).
* Order of processing and imports is important here - if we indicate secret metadata in items, the properties are processed first and hence we can miss preanalyzing (or even redacting) some data. Same applies for order of processing of the properties.
* Considering above two facts - we need a opt-in commandline switch or environemnt variable (or combination) to indicate that secrets metadata might be used - in which case we'll need to buffer build/log events before we have processed all the metadata indicating what needs to be redacted. Extra care will need to be given to sending command line args via EventSource ([source](https://github.com/dotnet/msbuild/blob/main/src/MSBuild/XMake.cs#L655))
* There are no global items today - this can be simulated by putting those to directory.props
* Even seemingly innocent tasks with seemingly innocent logging can spill possibly sensitive data (e.g. think the RAR task, logging all the inputs, while those are just reference related info - those can contain paths that might already by itself be sensitive info). Related: [#8493](https://github.com/dotnet/msbuild/issues/8493)
* `MSBuild` task can pose a boundary for some context passing (e.g. properties/items).
* Properties/items can occure dynamically after the initial processing of the script - e.g. [`CreateProperty task`](https://learn.microsoft.com/en-us/visualstudio/msbuild/createproperty-task). That should not be a problem, but we should keep it in mind (as additional entrypoint of external data into internal data holders).
* Task authors and consumers are posibly different personas with disconected codebases. For this reason we want to support ability to indicate that task input/output is meant to be a secret. A user of the task should follow the contract and denote the data to be mounted to the task appropriately (otherwise a build warning/error will be issued).
# Suggested Implementation
* Need for explicit opt-in - command line switch or environment variable.
* On detection of opt in, all build events for loggers need to be buffered for a deffered dispatch to loggers (similarly as we have ['DeferredBuildMessage'](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/BuildManager/BuildManager.cs#L400) and [`LogDeferredMessages`](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/BuildManager/BuildManager.cs#L2890)), until the full pass through the build script (including all imports and `.props` and `.targets` files) so that properties initialization and items initialization is fully performed - as only then we know the full extent of requested redacting.
* In the future version - with first-class citizen type for secrets, we can possibly frontload single pass through the script just for detection of the secret redaction declarations and avoid the buffering and post-process need.
* Buffered events need to be post-processed in respect with the redaction requests, only then dispatched.
* We'll maintain lookup of elements requested for redaction - those explicitly requested by the name of property/item and those identified as sensitive by value or by transfer of value from other sensitive element.
* We'll intercept assigments of value to property ([`TrackPropertyWrite`](https://github.com/dotnet/msbuild/blob/main/src/Build/Evaluation/PropertyTrackingEvaluatorDataWrapper.cs#L223)), item and task parameter
* If value is assigned to a task parameter and such is indicated by user as sensitive, the holder of the value (the R-value - parameter/item being assigned to the task input) needs to be as well tracked as sensitive, otherwise build waring/error will be issued.
* If value is assigned to a task parameter and such is not indicated by user as sensitive, but the holder of the value (the R-value - parameter/item being assigned to the task input) is tracked as sensitive (either because it was explicitly denoted by name, or it was later marked by MSBuild due to holding value matching a sensitivity value regex or callback) - a build warning/error will be issued.
* If value is assigned to property/item from a task output and such is indicated by user as sensitive, the L-value holder of the value (the property/item being assigned to) need to be as well tracked as sensitive, otherwise build waring/error will be issued.
* If value is being assigned to property or item
* and such is indicated by user as sensitive, the generated build event needs to be redacted.
* and such is not indicated by user as sensitive, but the R-value is indicated as sensitive - the data holder (property/item) is marked as holding sensitive data and treated accordingly.
* and such is not indicated by user as sensitive, the value is passed to sensitivity indicating regex or callback (in case any of those are configured by user) and if matched - the data holder (property/item) is marked as holding sensitive data and treated accordingly.
* No other redacting of log events will be performed. This is not a strong requirement - we can introduce another opt-in level of strict inspection of all log events. The gain is very questionable though, while the performance impact is severe (internal experiments by @michaelcfanning measured by @rokonec indicate 4-times slow-down on mid-size build). Additionally to being perf-expensive, it can possibly get easily confused - e.g.:
```xml
<ItemGroup>
<DataToRedactFromLogs>MySecret</DataToRedactFromLogs>
<MySecret>a</MySecret>
<MyInnocentData>hahaha</MyInnocentData>
<SomeProp></SomeProp>
</ItemGroup>
<Target Name="Test">
<MyTask FirstInput="MySecret" SecondInput="MyInnocentData">
<Output PropertyName="SomeProp" TaskParameter="Result">
</MyTask>
<!-- Might log:
Result from task: h<redacted>h<redacted>h<redacted>
-->
<Message Text="Result from task: $(SomeProp)">
</Target>
```
In case we'd want to redact all occurences of value of `MySecret` from the task result - we might get a lot of false positives and very confusing results.
# Open questions
* What to use as a replacement of the data to be redacted? (Randomized hash, fixed token, etc.) - *very likely just a static pattern ('******'). The built-in redactor plugin will be allowed to provide custom replacements*
* Do we want to allow to supply custom replacement value for injectable redaction functionality? There would need to be very strong compeling reason, as this is easily suspectible to [log forging attack](https://owasp.org/www-community/attacks/Log_Injection) - *most likely no.*
* Balancing performance and accuracy - can we afford to not support arbitrary output of tasks? Otherwise we'd need to process all log events (similar experiments indicate 4 times slowdown of the build of mid-size project (Orchard)). On the other with explicit 'secret metadata' feature users might expect 100% correctness. Should we make this configurable as well (input data only vs all log entries)? Plus this might be suspectible to false positives (see above).
# Links
* Nightfall data redaction syntax: https://docs.nightfall.ai/docs/redacting-sensitive-data-in-4-lines-of-code
* `spark.redaction.regex`: https://people.apache.org/~pwendell/spark-releases/latest/configuration.html
* Redacting secrets in k8s logs in ops tool `Komodor`: https://docs.komodor.com/Learn/Sensitive-Information-Redaction.html
* MSBuild opt-in functionality for properties/items/metadata logging disabling: https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/TaskExecutionHost/TaskExecutionHost.cs#L1199

Просмотреть файл

@ -0,0 +1,200 @@
# Resolve Assembly Reference core scenarios
This document aims to capture the core functionality provided by the ResolveAssemblyReference task when building .NET (_Core_ - pun intended) projects.
The goal is to rationalize and optimize the task, ultimately achieving substantially better performance and crossing out RAR from the list of notoriously
slow build tasks.
## Overview
RAR is the Swiss army knife of assembly resolution. Very extensible and universal, exposing over 50 documented parameters and supporting 10 different
locations where it searches for assemblies. Please see the [official documentation](https://learn.microsoft.com/visualstudio/msbuild/resolveassemblyreference-task) and
the [ResolveAssemblyReference page](https://github.com/dotnet/msbuild/blob/main/documentation/wiki/ResolveAssemblyReference.md) for a detailed description
of its features.
While all of RAR's functionality has to be supported for backward compatibility, some parts are more relevant for modern builds than others. For example,
if we focus only on building .NET Core / .NET 5+ projects, resolving assemblies in the Global Assembly Cache (GAC) is not supported. In fact, most of
the "resolvers", internal classes implementing various resolution strategies, are not used in modern scenarios.
## Requirements
Looking at the RAR contract at a high-level, it is effectively transforming one string array to another. It is passed an array of strings specifying the
assemblies required for the build, and returns an array of strings specifying full paths to assembly files on disk. Not necessarily a 1:1 mapping because
assemblies are transitively probed for dependencies, thus the output array may be larger than input. Additionally, if an input assembly cannot be resolved,
RAR issues a warning and otherwise ignores the assembly. This may lead to the output array being smaller than input.
### Inputs
In a typical build targeting modern .NET (*not* .NET Framework), RAR inputs come from three sources.
1. SDK reference assemblies. These are full paths to assemblies distributed with the SDK. The SDK may get the list of assemblies for example by parsing the
corresponding `FrameworkList.xml`. Reference assemblies are passed to RAR with the `ExternallyResolved` metadatum set, which means that they are
transitively closed with respect to their dependencies. In other words, all dependencies, including transitive dependencies, of these assemblies are
guaranteed to be passed in.
1. NuGet references. These are again full paths to assemblies pre-resolved by the NuGet system. The `ExternallyResolved` metadatum is set for these as well,
signalling to RAR that it doesn't have to open the assembly files to read their AssemblyRef tables.
1. Project references. When a project depends on another project, the output file of the dependency is passed to RAR. Alternatively, a project may directly
reference a random file o disk, resulting in the same code path. Unlike SDK and NuGet, these references are not pre-resolved and RAR must open the assembly
files and use a .NET metadata reader to enumerate the AssemblyRef table to get the list of dependent assembly names. The dependent assembly names are
resolved to assembly files and newly discovered assembly files are again scanned for AssemblyRef's. This process repeats itself until a closure is
established.
The above sums up the functionality required from RAR in a nutshell. For extra clarity, note that RAR is invoked only once during build, and is passed the
combined SDK, NuGet, and project references in one input array.
## Design
To meet the requirements, RAR must internally be able to do the following.
- For each input reference passed as a file path, it must verify that the file path exists. If the file does not exist, RAR issues a warning and ignores
the reference.
- For each input reference passed as a file path, it must know what its assembly name is. For example, for a reference given as
`C:\_nugetpackages\microsoft.netcore.app.ref\7.0.2\ref\net7.0\Microsoft.VisualBasic.Core.dll`, RAR must figure out the assembly name to be
`Microsoft.VisualBasic.Core, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a` so it can map it back to the reference when it sees
such an AssemblyRef in another assembly.
- For project references (reference given as a path to an assembly file that is not guaranteed to have its dependencies resolved), RAR must be able to look
up dependencies. If a dependency is not found in the assembly name -> file path map, RAR first searches the directory where the primary reference is located.
Failing that, it then uses pre-defined strategies, four of which are enabled by default when building modern projects: `CandidateAssemblyFiles`, `HintPathFromItem`,
`TargetFrameworkDirectory`, and `RawFileName`. Of these, only `CandidateAssemblyFiles` tends to actually be of potential use. `CandidateAssemblyFiles` is
configured to use all `Content` and `None` items defined in the build. Given an AssemblyRef found in a project reference, for example `MyLibrary, Version=1.0.0.0`,
if `Content` or `None` contains `MyLibrary.dll`, RAR will get its assembly name and see if there is a match.
## Scale
The typical total number of references processed by RAR when building one project is in the order of hundreds. Even if the project referenced everything
that comes with the .NET SDK, consumed a large number of NuGet packages, and was part of a large solution, it would likely reach only low thousands of references.
In the OrchardCore solution, for example, the median number of references passed to and from RAR is 317 and the maximum is 629.
While CPU optimizations can definitely make a difference, at this scale I/O is far more concerning. Building upon the design notes above, here is where RAR
has to touch the disk.
- **File existence checks**. RAR tends to invoke its resolvers sequentially with the first-one-wins semantics. It returns the first suitable file that actually
exists. As a special case, if RAR is given a full path, it checks if the file exists before returning it.
- Assembly name extraction. Given a file on disk, RAR needs to know its assembly name, i.e. version, culture, public key token, ... This requires opening the
file with a .NET metadata reader.
- **AssemblyRef extraction**. For references that are not marked with `ExternallyResolved`, RAR needs enumerate their dependencies. This, again, requires opening
the file with a .NET metadata reader.
## Optimizations
RAR optimizations tend to revolve around caching of information that is expensive to calculate.
### Existing caching
Over the years RAR has implemented several layers of caches, both in-memory and on-disk. An inventory follows.
#### **Per invocation in-memory cache**
Not surprisingly, RAR caches the result of I/O operations in a cache in memory. The lifetime of this cache is one RAR invocation because generally we cannot
assume that files on disk don't change between builds. It is implemented inside `SystemState` as several dictionaries keyed off of the absolute file path.
One issue with this cache is that the key is not normalized so a file specified as `C:\dir\file` will use a different cache entry than the same file specified as
`C:/dir/file`.
#### **Per process in-memory cache**
This comes in multiple forms. `SystemState` has its own process-wide cache which maps file paths to data we need - assembly name, dependencies (AssemblyRef's), last
modification time (time stamp). It uses the time stamp to filter out invalid entries, i.e if the last modification time stamp changes, the cache contents is no longer
considered valid.
Another form of such a process-wide cache is a low-level cache of timestamps of immutable files, as implemented in `NativeMethods.GetLastWriteFileUtcTime`
and `FileClassifier`. The idea is that some files are not expected to be updated or deleted during inner loop development. For instance, a reference assembly that
comes with the SDK should never change and may get deleted only by uninstalling the SDK. The problem with this cache is that the file path-based classification is
more or less a heuristic and doesn't seem to work in all cases. Currently it is failing to recognize SDK reference assemblies under paths like
`C:\_nugetpackages\microsoft.aspnetcore.app.ref\7.0.2\ref\net7.0`, for example.
#### **Per project disk cache**
To help in cold build scenarios where RAR has not seen the project yet and the in-memory caches are empty or not relevant, RAR supports an on-disk cache using the
`StateFile` parameter. If specified, RAR will attempt to populate `SystemState` by deserializing the file before it starts. If `SystemState` has been modified
during RAR execution, its new contents will be serialized back to the file after RAR is done. This is somewhat non-deterministic because the cache being written
back is a union of what was read from the disk and what's in the memory, the latter depending on what other projects have been built by the current MSBuild process.
Building the exact same project with the exact same disk state will sometimes write the cache, sometimes it will not.
From performance point of view, while helping when RAR is cold, reading the cache unnecessarily slows down the execution when RAR is hot, because the cache contents
already is in memory so there is nothing to gain from reading it again. Of note here is the fact that as of _On disk cache serialization (#6094)_, RAR uses a custom
hand-optimized serializer for the cache file. It has better peformance than the previously used `BinaryFormatter`, not to mention being considered more secure.
#### **SDK disk pre-cache**
The observation that if there is no per project disk cache and RAR is cold, it has to read information about many SDK assemblies, led to the advent of the global
pre-cache. The idea is that the pre-cache is created as part of building the SDK and distributed with it. I.e. it is the SDK vendor's responsibility to create the
file, make it available on developer machines, and pass it to RAR in the `AssemblyInformationCachePaths` parameter when building relevant projects.
The pre-cache functionality is generic and available to any SDK vendor. The .NET SDK currently builds and distributes a file named `SDKPrecomputedAssemblyReferences.cache`
but it is not passed to RAR by default. Only a couple of projects in the dotnet organization are explicitly opted into consuming the pre-cache at the moment.
The downside of the current pre-cache design is that the full pre-cache ends up being written to each per project cache file upon completing the first RAR invocation.
For the .NET SDK the pre-cache contains more than 3000 assemblies. All of them stay in memory in the per process cache and all of them become part of the
per-project cache file, meaning that they will be read back from disk on each subsequent hot invocation. Not only does it hurt build performance, but it is also
wasteful to duplicate >2 MB worth of serialized assembly information in each project's intermediate directory.
## Proposed design
Completely rewriting RAR doesn't appear to be worthwhile. The requirements described above are for a typical build, not necessarily for all builds. RAR is highly
configurable and customizable, thus the bar for backward compatibility is very high. There are definitely opportunities for micro-optimizations without any functional
effect. Be it eliminating allocations, simplifying tight loops, reordering cases in hot switches, ..., there is a lot of low-hanging fruit. This by itself won't help
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.
- In a hot scenario, where there is state in memory, the only I/O on the happy path is the timestamp check to verify that the file hasn't changed since last time.
There is a chance that the timestamp check can be replaced with something faster, although historically we haven't been able to come up with anything solid.
File watchers, for example, while tempting to use because the validity check in the happy case would cost literally nothing, suffer from an inherent race
condition. When a watched file is modified, the file watcher routine is not guaranteed to run by the time we need to reliably know whether the file is unchanged.
The exact time the routine is executed depends on the latency of the asynchronous OS callback, on thread pool availability, CPU scheduling, and more.
The focus of the following paragraphs is instead on SDK and NuGet references, because there are typically one to two orders of magnitude more of them than project
references, so optimizing them has the best bang for the buck.
### Obtain assembly names from the SDK
The SDK is currently already passing relevant metadata such as `AssemblyVersion` and `PublicKeyToken`, so there is no need for RAR to open the file and parse its
.NET metadata tables to get this information. This, together with the fact that SDK references are marked with `ExternallyResolved` so they cannot have dependencies
outside of the primary set, means that there is no need to cache anything about these assemblies. Everything RAR needs comes (or can come if it's not there already)
from the `Assemblies` parameter, explicitly provided on each invocation. Note, it may make sense to keep a cache in memory but it definitely doesn't make sense
to save it to disk.
If we do this, then in the warm and hot scenarios where the per project disk cache exists, we use it only to cache data about NuGet references and project references,
significantly reducing its size. By eliminating per-reference I/O for most references, RAR would see a significant performance boost.
This is assuming we trust the SDK that it passes correct data and we trust the user that they don't delete or overwrite their SDK files. If this assumption is not
valid, the mitigation would be to store and check the timestamp of each individual file. We would still benefit from smaller on disk caches, being able to store only
the timestamp and not assembly name for intact SDK references, but the hot scenario wouldn't get any faster than today.
### Treat NuGet references as immutable [shelved]
NuGet references live in the NuGet cache which is conceptually immutable. If RAR takes advantage of this, it can eliminate timestamp checks for NuGet references as
well. The risk is higher than for SDK references because overwriting files in the NuGet cache is commonly used as a cut-the-corner workaround. The benefit is smaller
because the number of NuGet references is typically lower. The proposal is to shelve this opportunity for now due to the unfavorable risk-benefit ratio.
### Don't load the per project disk cache when not needed
As described above, the on disk cache is not adding any value in the hot scenario because its contents already lives in the in-memory cache. The proposal is to
load it lazily only when (and if) RAR runs into an assembly that does not have a record in the in-memory cache. In developer inner loop, when the same solution is
built over and over again, the cache would typically not be loaded at all, unless the developer makes a change that actually changes the dependency graph.
### Save only relevant data to the per project disk cache
As for saving the per-project cache, we would guarantee that after RAR is done, the cache contains exactly the data needed for this specific project. This would
be done by keeping track of the items used during RAR execution, and writing those and only those to the cache. Having a cache that's guaranteed to have certain
well-defined content after each build is a very good property to have. For instance, in dev box scenarios it would otherwise be hard to reliably "prime" a repo
enlistment - the system may prime by building the full solution and then the developer uses the box to build a specific project that happens to have an incomplete
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.
### Don't use the SDK disk pre-cache
The idea of pre-generated on-disk cache is sound. For the `ExternallyResolved` SDK assemblies specifically, though, it effectively duplicates the information already
present in `FrameworkList.xml`. That is, it maps assembly paths to assembly names. If the need arises we may want to re-design the pre-cache to remove the major
drawback that it duplicates itself into all per-project caches. Cold RAR would load both caches and combine their contents (currently it's either or). Until then,
it should be OK to leave it unchanged and unused.

Просмотреть файл

@ -1,54 +1,43 @@
# Single project isolated builds: implementation details
# Single Project Isolated Builds: Implementation Details
<!-- workflow -->
Single project isolated builds can be achieved by providing MSBuild with input and output cache files.
The input cache files contain the cached results of all the targets that a project calls on its references. When a project builds without isolation, it builds its references via [MSBuild task](aka.ms/msbuild_tasks) calls. In isolated builds, the engine, instead of executing these tasks, serves their results from the provided input caches. In an isolated project build, only the top level project (built via the BuildManager APIs) should build targets. Any referenced projects by the top level project should be provided from the input caches.
The input cache files contain the cached `TargetResult`s of all targets that a project calls on its references. When a project builds without isolation, it builds its references via [MSBuild task](aka.ms/msbuild_tasks) calls. In isolated builds, the engine, instead of executing these tasks, serves their results from the provided input caches. In an isolated project build, only the top level project (built via the `BuildManager` APIs) should build targets; Any referenced projects by the top level project should be provided from the input caches.
The output cache file tells MSBuild where to serialize the results of building the current project. This output cache becomes an input cache for all other projects that depend on the current project.
The output cache file can be omitted in which case the build reuses prior results but does not write out any new results. This is useful when one wants to re-execute the build for a project without building its references.
The output cache file tells MSBuild where to serialize the `TargetResult`s for a project's built targets and becomes an input cache for dependent projects.
The presence of either input or output caches turns on [isolated build constraints](static-graph.md##single-project-isolated-builds).
## Input / Output cache implementation
## Input / Output Cache Implementation
<!-- cache structure -->
The cache files contain the serialized state of MSBuild's [ConfigCache](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ConfigCache.cs) and [ResultsCache](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ResultsCache.cs). These two caches have been traditionally used by the engine to cache build results. For example, it is these caches which ensure that a target is only built once per build submission. The `ConfigCache` entries are instances of [BuildRequestConfiguration](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs#L25). The `ResultsCache` entries are instances of [BuildResult](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/BuildResult.cs#L34), which contain or more instances of [TargetResult](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/TargetResult.cs#L22).
One can view the two caches as the following mapping: `(project path, global properties) -> results`. `(project path, global properties)` is represented by a `BuildRequestConfiguration`, and the results are represented by `BuildResult` and `TargetResult`.
The cache files contain the serialized state of MSBuild's [`ConfigCache`](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ConfigCache.cs) and [`ResultsCache`](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ResultsCache.cs), which have been traditionally used by the engine to cache build results. They ensure that a target is only built once per build submission. `ConfigCache` entries are instances of [`BuildRequestConfiguration`](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs#L25)s (a `(project path, global properties)` tuple), and `ResultsCache` entries are instances of [`BuildResult`](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/BuildResult.cs#L34)s, which contain [`TargetResult`](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/TargetResult.cs#L22)s. The `ConfigCache` entries and `ResultsCache` entries form a [bijection](https://en.wikipedia.org/wiki/Bijection).
<!-- cache lifetime -->
The input and output cache files have the same lifetime as the `ConfigCache` and the `ResultsCache`. The `ConfigCache` and the `ResultsCache` are owned by the [BuildManager](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/BuildManager/BuildManager.cs), and their lifetimes are one `BuildManager.BeginBuild` / `BuildManager.EndBuild` session. On commandline builds, since MSBuild.exe uses one BuildManager with one BeginBuild / EndBuild session, the cache lifetime is the same as the entire process lifetime. When other processes (e.g. Visual Studio's devenv.exe) perform msbuild builds via the `BuildManager` APIs, there can be multiple build sessions in the same process.
In a build, the input and output cache files have the same lifetime as the `ConfigCache` and `ResultsCache`. The `ConfigCache` and `ResultsCache` are owned by the [`BuildManager`](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/BuildManager/BuildManager.cs), and their lifetimes are one `BuildManager.BeginBuild` / `BuildManager.EndBuild` session. On command-line builds, the cache lifetime is the same as the entire process lifetime since `MSBuild.exe` uses one `BuildManager` with one `BeginBuild` / `EndBuild` session. When other processes (e.g. Visual Studio's `devenv.exe`) perform MSBuild builds via the `BuildManager` APIs, there can be multiple build sessions in the same process.
<!-- constraints -->
When MSBuild is loading input cache files, it has to merge multiple incoming instances of `ConfigCache` and `ResultsCache` into one instance of each. The [CacheAggregator](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/BuildManager/CacheAggregator.cs#L13) is responsible for stitching together the pairs of deserialized `ConfigCache`/`ResultsCache` entries from each input cache file.
The following constraints are enforced during cache aggregation:
- For each input cache, `ConfigCache.Entries.Size == ResultsCache.Entries.Size`
- For each input cache, there is exactly one mapping from ConfigCache to ResultsCache (on `BuildResult.ConfigurationId` == `BuildRequestConfiguration.ConfigurationId`)
- Colliding configurations (defined as tuples of `(project path, global properties)`) get their corresponding BuildResult entries merged at the level of TargetResult entries. TargetResult conflicts are handled via the "first one wins" strategy. This is in line with vanilla msbuild's behaviour where a target tuple of `(project path, global properties, target)` gets executed only once.
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`
The output cache file **only contains results for additional work performed in the current BeginBuild / EndBuild session**. Entries from input caches are not transferred to the output cache.
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.
<!-- How input / output cache entries are separated with the override caches -->
Entries that make it into the output cache file are separated from entries serialized from input cache files via the use of [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). These are composite caches. Each contains two underlying caches: a cache where input caches files are loaded into (called the override cache), and a cache where new results are written into (called the current cache). Cache reads are satisified from both underlying caches (override cache is queried first, current cache is queried second). Writes are only written to the current cache, never into the override cache. The output cache file only contains the serialized current cache, and not the override cache, thus ensuring that only newly built results are serialized in the output cache file. 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 read.
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 Implementation
[Isolation constraints](static-graph.md##single-project-isolated-builds) are implemented in the Scheduler and the 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 `ProjectReference`. [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 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.
### How isolation exemption complicates everything
<!-- Potential cache scenarios caused by exemption -->
Project references [can be exempt](static-graph.md#exempting-references-from-isolation-constraints) from isolation constraints via the `GraphIsolationExemptReference` item.
### Isolation Exemption
The `Scheduler` [skips isolation constraints](static-graph.md#exempting-references-from-isolation-constraints) on project references via the:
The `Scheduler` knows to skip isolation constraints on an exempt `BuildRequest` because the [ProjectBuilder compares](https://github.com/dotnet/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Components/RequestBuilder/RequestBuilder.cs#L349) each new `BuildRequest` against the `GraphIsolationExemptReference` items defined in the calling project, and if exempt, sets `BuildRequest.SkipStaticGraphIsolationConstraints`. When a `BuildRequest` is marked as exempt, the `Scheduler` also marks its corresponding `BuildRequestConfiguration` as exempt as well, which aids in further identification of exempt projects outside the `Scheduler`.
* `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.
The build results for the exempt project are also included in the current cache (according to the above mentioned rule that newly built results are serialized in the output cache file). This complicates the way caches interact in several scenarios:
1. the same project can be exempt via multiple references, thus potentially colliding when multiple output cache files containing the same exempt project get aggregated. For example, given the graph `{A->B, A->C}`, where both `B` and `C` build the exempt project `D`, `D` will appear in the output caches of both `B` and `C`. When `A` aggregates these two caches, it will encounter duplicate entries for `D`, and will need to merge the results.
2. a project can be both exempt and present in the graph at the same time. For example, given the graph `{A->B}`, both `A` and `B` are in the graph, but `A` can also mark `B` as exempt (meaning that `A` contains both a `ProjectReference` item to `B`, and a `GraphIsolationExemptReference` item to `B`). The fact that `B` is in the graph means that `A` will receive an input cache containing B's build results. There are two subcases here:
1. `A` builds targets from `B` that already exist in the input cache file from `B`. In this case, all the builds of `B` will be cache hits, and no target results from `B` will make it into `A`'s output cache, since nothing new was built.
2. `A` builds targets from `B` that do not exist in the input cache file from `B`. If `B` weren't exempt from isolation constraints, this scenario would lead to a build break, as cache misses are illegal under isolation. With `B` being exempt, the new builds of `B` will get included in `A`'s output cache. The results from `B`'s cache file won't get included in `A`'s output cache file, as they weren't built by `A`.
3. A project, which is not in the graph, can be exempt by two parent/child projects from the graph. For example, given the graph `{A->B}`, both `A` and `B` can exempt project `D` (meaning that neither `A` nor `B` have a `ProjectReference` to `D`, but both `A` and `B` have a `GraphIsolationExemptReference` to `D`). The fact that `B` is in the graph means that `A` will receive an input cache containing `B`'s build results. Since `B` builds targets from `D`, it means that `B`'s output cache file also contains target results from `D`. There are two subcases here:
1. `A` builds targets from `D` that already exist in the input cache file from `B`. This is handled in the same way as the above case `2.1.`
2. `A` builds targets from `D` that do not exist in the input cache file from `B`, meaning that `A` builds additional targets from `D` which `B` didn't build. This is handled in the same way as teh above case `2.2.`
* `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.
**Current issue:** if multiple nodes in the graph exempt the same project file, the build results of the exempt project will trickle up and conflict in the first parent that tries to merge them. Documented in issue [#4386](https://github.com/dotnet/msbuild/issues/4386).
\* 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 T<sub>cached</sub> for targets t<sub>1</sub>, t<sub>2</sub>, ..., t<sub>m</sub> and a dependent project B uses F as an input cache file but builds and obtains the `TargetResult`s T<sub>new</sub> for targets t<sub>m + 1</sub>, t<sub>m + 2</sub>, ..., t<sub>n</sub> such that 0 < m < n. In this case, T<sub>new</sub> will be placed into the `ResultsCache` containing T<sub>cached</sub> to enforce no overlap between the override and current caches in the `ConfigCacheWithOverride`.

Просмотреть файл

@ -223,15 +223,17 @@ A project reference protocol may contain multiple targets, for example `A -> B,
The common project reference protocols (Build, Rebuild, Restore, Clean) will be specified by the common props and targets file in the msbuild repository. Other SDKs can implement their own protocols (e.g. ASPNET implementing Publish).
For this section and the remainder of this spec, a project's default target(s) (what it would execute if no other targets are specified, so often Build but configurable via DefaultTargets) will be referred to as `.default`. That is also how it is used in MSBuild code.
Here are the rules for the common protocols:
`Build -> GetTargetFrameworks, <default>, GetNativeManifest, GetCopyToOutputDirectoryItems`
`Build -> GetTargetFrameworks, .default, GetNativeManifest, GetCopyToOutputDirectoryItems`
The default target (represented in this spec's pseudo protocol representation as `<default>`) is resolved for each project.
`.default` is resolved for each project.
`Clean -> GetTargetFrameworks, Clean`
`Rebuild -> GetTargetFrameworks, Clean, <default>, GetNativeManifest, GetCopyToOutputDirectoryItems`
`Rebuild -> GetTargetFrameworks, Clean, .default, GetNativeManifest, GetCopyToOutputDirectoryItems`
`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.
@ -358,7 +360,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 pre-computed and somehow provided as inputs to the referencing project.
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,7 +369,7 @@ Because referenced projects and their entry targets are guaranteed to be in the
### 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.
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.
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.
@ -387,9 +389,9 @@ 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.
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.
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`.
@ -401,14 +403,21 @@ In certain situations one may want to exempt a reference from isolation constrai
- 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)
A project is exempt from isolation constraints by adding its full path to the `GraphIsolationExemptReference` item. For example, if project A.csproj references project B.csproj, the following snippet exempts B.csproj from isolation constraints while A.csproj is built:
```xml
<ItemGroup>
<GraphIsolationExemptReference Include="/Full/Path/To/B.csproj" />
</ItemGroup>
```
A project may be exempt from isolation constraints in two ways:
A reference is exempt only in projects that add the reference in `GraphIsolationExemptReference`. If multiple projects need to exempt the same reference, all of them need to add the reference to `GraphIsolationExemptReference`.
<!-- List is encoded in HTML since XML code block
and its following text won't be indented properly. -->
<ul>
<li>its full path is added to the <code>GraphIsolationExemptReference</code> item. For example, if project <code>A.csproj</code> references project <code>B.csproj</code>, the following snippet exempts <code>B.csproj</code> from isolation constraints while <code>A.csproj</code> is built:
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">ItemGroup</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">GraphIsolationExemptReference</span> <span class="hljs-attr">Include</span>=<span class="hljs-string">"/Full/Path/To/B.csproj"</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">ItemGroup</span>&gt;</span>
</code></pre>
If multiple projects need to exempt the same reference, all of them need to add the reference to <code>GraphIsolationExemptReference</code>.
</li>
<li> via the <code>isolate:MessageUponIsolationViolation</code> switch
</li>
</ul>
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.

Просмотреть файл

@ -1,4 +1,4 @@
⚠ This doc is intended for internal teams.
⚠ This doc is intended for internal teams. For information about how to deal with MSBuild Change Waves as an MSBuild _user_, see [ChangeWaves.md](ChangeWaves.md).
# What are Change Waves?
A Change Wave is a set of risky features developed under the same opt-out flag. The purpose of this is to warn developers of risky changes that will become standard functionality down the line. If there's something we think is worth the risk, we found that Change Waves were a good middle ground between making necessary changes and warning customers of what will soon be permanent.

Просмотреть файл

@ -22,6 +22,11 @@ A wave of features is set to "rotate out" (i.e. become standard functionality) t
# Change Waves & Associated Features
## Current Rotation of Change Waves
### 17.8
- [[RAR] Don't do I/O on SDK-provided references](https://github.com/dotnet/msbuild/pull/8688)
### 17.8
- [Delete destination file before copy](https://github.com/dotnet/msbuild/pull/8685)
### 17.6
- [Parse invalid property under target](https://github.com/dotnet/msbuild/pull/8190)
@ -29,6 +34,7 @@ A wave of features is set to "rotate out" (i.e. become standard functionality) t
- [Log an error when no provided search path for an import exists](https://github.com/dotnet/msbuild/pull/8095)
- [Log assembly loads](https://github.com/dotnet/msbuild/pull/8316)
- [AnyHaveMetadataValue returns false when passed an empty list](https://github.com/dotnet/msbuild/pull/8603)
- [Log item self-expansion](https://github.com/dotnet/msbuild/pull/8581)
### 17.4
- [Respect deps.json when loading assemblies](https://github.com/dotnet/msbuild/pull/7520)

Просмотреть файл

@ -0,0 +1,264 @@
# Controlling references behavior
MSBuild recognizes a [few types of references](https://learn.microsoft.com/previous-versions/visualstudio/visual-studio-2015/msbuild/common-msbuild-project-items) (here we are mainly interested in `ProjectReference`, `PackageReference`, `Reference` aka assembly reference) and offers optional mechanisms to tailor some aspects of the references workings - transitive references resolution, multitargeted references resolution, copying references to output directory.
## .NET SDK projects and access to transitive references
[.NET SDK projects](https://learn.microsoft.com/dotnet/core/project-sdk/overview) by default make all transitive references accessible as if they were direct references.
This is provided for the compiler and analyzers to be able to properly inspect the whole dependency or/and inheritance chain of types when deciding about particular checks.
It is facilitated via `project.assets.json` file created by NuGet client during the restore operation. This file captures the whole transitive closure of the project dependency tree.
SDK build tasks require existence of this file (hence the infamous `Assets file <path>\project.assets.json not found` if the MSBuild.exe is run without prior restore operation). It is used to reconstruct the `ProjectReference`s and create `Reference` items for the content of `PackageReference`s for the project and make them available to the rest of the build. For this reason MSBuild and compiler by default sees those transitive references as if they were direct references.
## Access to transitive project references
Above described behavior can lead to easy unintentional breaking out of layering architecture separation.
This behavior can be opted-out for `ProjectReference`s via `DisableTransitiveProjectReferences` property on the referencing project.
<a name="OnionArchSample"></a>*Example*:
Let's imagine an `Onion Architecture` design:
```mermaid
flowchart LR
Service[Service Layer] --> Repository
Repository[Repository Layer] --> Domain[Domain Layer]
```
Service Layer definition:
```xml
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\Repository\Repository.csproj" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<LangVersion>10</LangVersion>
<!-- This prevents referencing types from transitive project references. -->
<DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
</PropertyGroup>
</Project>
```
```csharp
namespace Service;
public class PersonsAccessor
{
private Repository.Persona _persona;
// This is allowed unless DisableTransitiveProjectReferences=true is passed into build.
// private Domain.PersonTable _tbl;
}
```
## Access to transitive package references
The transitive access to references works by default for package references as well. This can be opted out for referencing projects via `PrivateAssets=compile` on the `PackageReference` of the concern. (More details on [Controlling package dependency assets](https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets)).
When using this metadatum - the access to the package, its dirrect and transitive dependencies is **not** restricted for the project declaring the refenerence on the package in its `Project` element. It is restricted for the projects referencing the project (or package) that specified the `PackageRegerence` with the `PrivateAssets` metadatum.
*Example*:
In our previous example let's have `Repository Layer` reference `newtonsoft.json`:
```mermaid
flowchart LR
Service[Service Layer] --> Repository
Repository[Repository Layer] --> newtonsoft.json[newtonsoft.json]
```
We are not able to influence access to `newtonsoft.json` and its dependencies (would there be any) in the `Repository Layer`, but we can prevent it from propagating to `Service Layer`.
`Repository Layer`:
```xml
<ItemGroup>
<PackageReference Include="newtonsoft.json" Version="13.0.1">
<!-- This prevents the reference to be available to referencing types. -->
<PrivateAssets>compile</PrivateAssets>
</PackageReference>
</ItemGroup>
```
Unless opted out via `PrivateAssets=compile`, our `Service Layer` would have access to `newtonsoft.json`:
```csharp
namespace Service;
//This is allowed unless PrivateAssets=compile is set on the PackageDependency in Repository.
//using Newtonsoft.Json;
public class PersonsAccessor
{
private Repository.Persona _persona;
}
```
**Notes:**
`PrivateAssets` metadatum (and it's counterparts `IncludeAssets` and `ExcludeAssets`) is applicable to `PackageReference` and controls exposure of dependencies to the consuming projects, not the current project. It is currently not possible to prevent access to package references from within directly referencing project - this is purely decision of the package itself (as it can define it's dependencies as `PrivateAssets`).
## Not copying dependencies to output
By default the above mentioned dependency types are copied to the build output directory during the build. There can be various scenarios where this behavior is not desired (examples: dependency is compile time only or contains a logic for build; component is plugin to a main app and there is a desire not to duplicate common dependencies in output).
Overriding this logic depends on the type of the dependency.
### Not copying Assembly Reference
Copying can be opted out via [Private metadata on the Reference item](https://learn.microsoft.com/previous-versions/visualstudio/visual-studio-2015/msbuild/common-msbuild-project-items?view=vs-2015#reference) (which corresponds to the `Copy Local` property of the reference in the Visual Studio properties dialog for the reference):
```xml
<ItemGroup>
<Reference Include="mydll">
<HintPath>..\somepath\mydll.dll</HintPath>
<!-- This indicates that the reference should not be copied to output folder. -->
<Private>false</Private>
</Reference>
</ItemGroup>
```
### Not copying PackageReference
Detailed options description can be found in [Controlling package dependency assets](https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets). Here we'll offer three artifical examples:
**Not copying package dependency to the immediate output folder:**
```xml
<ItemGroup>
<PackageReference Include="newtonsoft.json" Version="13.0.1">
<!-- This allows compiling against the dependency, but prevents it's copying to output folder or flow to downstream dependant projects. -->
<IncludeAssets>compile</IncludeAssets>
</PackageReference>
</ItemGroup>
```
**Not copying package dependency to the downstream dependants output folder:**
```xml
<ItemGroup>
<PackageReference Include="newtonsoft.json" Version="13.0.1">
<!-- The dependency is copied to output folder in current referencing project,
but it's not copied to output folder of projects referencing current project. -->
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
```
**Not copying package dependency from the upstream dependencies:**
```xml
<ItemGroup>
<ProjectReference Include="../somepath/MyProj.csproj">
<!-- This prevents PackageReferences from MyProj.csproj to be copied to output of current project. -->
<ExcludeAssets>all</ExcludeAssets>
</ProjectReference>
</ItemGroup>
```
### Not copying ProjectReference
The opt-out mechanism is analogous to [Assembly Reference copy opt-out](#not-copying-assembly-reference):
```xml
<ItemGroup>
<ProjectReference Include="../somepath/MyProj.csproj">
<!-- This indicates that the referenced project output should not be copied to output folder. -->
<Private>false</Private>
</ProjectReference>
</ItemGroup>
```
Same metadata and logic applies here as it is being inherited from the `Reference` Item definition and the logic treats it identicaly.
## ProjectReference without accessibility and copying to output
In a specific scenarios we might want to indicate that specific project should be built prior our project but said project should not be reference accessible nor its output copied to current project output. This can be helpful for build time only dependencies - projects defining behavior that is going to be used as build step of a current project.
Such a behavior can be achived with [`ReferenceOutputAssembly` metadata](https://learn.microsoft.com/visualstudio/msbuild/common-msbuild-project-items?view=vs-2022#projectreference):
```xml
<ItemGroup>
<ProjectReference Include="../somepath/MyProj.csproj">
<!-- This indicates that the referenced project should not be referenced in code and output should not be copied to output folder.
This way we basically only indicate the build order.
-->
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
</ItemGroup>
```
**Note:** This technique has possibly unexpected behavior when referencing project with executable output type (`<OutputType>Exe</OutputType>`) - in such case the output assembly (`.dll`) is still not copied and referenced (as the metadatum name implies) and hence the types defined within the project cannot be referenced, however other supplementary output (added as `content` or `none`) is copied to the current project output folder (for .NET Core this includes `deps.json`, `runtimeconfig.json` and mainly `<app>.exe`). In that case we can combine (or replace) the `ReferenceOutputAssembly` metadata with `Private` metadata - [as described above](#not-copying-projectreference). More details on this case [here](https://github.com/dotnet/msbuild/issues/4795#issuecomment-1442390297)
## Forcing TargetFramework of a referenced multitargeted project
Consider agaoin our previous [Onion architecture example](#OnionArchSample), but now the individual projects will be [multitargeted](https://learn.microsoft.com/nuget/create-packages/multiple-target-frameworks-project-file).
Repository Layer:
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net48</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
<ProjectReference Include="..\Domain-net48\Domain-net48.csproj" />
<PackageReference Include="System.Text.Json" Version="7.0.2" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<ProjectReference Include="..\Domain-netstd20\Domain-netstd20.csproj" />
<PackageReference Include="newtonsoft.json" Version="13.0.1">
</ItemGroup>
</Project>
```
And it's going to be referenced by Service Layer:
```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net48;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Repository\Repository.csproj" />
</ItemGroup>
</Project>
```
Building the Service Layer will create output folders for `net7` and `net48`:
```
net48
|---- Repository.dll (targeted for net48)
|---- Domain-net48.dll
|---- System.Text.Json.dll
net7
|---- Repository.dll (targeted for netstandard2.0)
|---- Domain-netstd20.dll
|---- Newtonsoft.Json.dll
```
Should we want to reference the netstandard version of the Repository Layer in our Service Layer - we can force the reference chain via `SetTargetFramework` metadata on `ProjectReference` item:
```xml
<ItemGroup>
<ProjectReference Include="..\Repository\Repository.csproj" SetTargetFramework="TargetFramework=netstandard2.0" />
</ItemGroup>
```
**Notes:**
`SetTargetFramework` is currently not honored by the NuGet client([nuget issue #12436](https://github.com/NuGet/Home/issues/12436)), so the output folder will contain binaries from nuget packages as if this metadata was not used. To workaround this the apropriate nuget needs to be directly referenced from the project enforcing reference framework via `SetTargetFramework`, or copied to output/publish folder via different means.
`SetTargetFramework` will properly enforce the framework for the `ProjectReference` chain. Once the `TargetFramework` overriding is encountered it is passed down the reference chain and the `ProjectReference`s respect it during the `TargetFramework` resolution. Due to the nature of handling of [transitive references in .NET-SDK style projects](#net-sdk-projects-and-access-to-transitive-references) and the fact that NuGet client doesn't honor `SetTargetFramework`, the transitive references can get resolved and built for multiple `TargetFramework`s. This means the output folder will contain proper version of the direct dependency - Repository Layer. The transitive references might overbuild, and output folder of current project (Service Layer) might contain both versions of the transitive project dependency (Domain-net48.dll and Domain-netstd20.dll). This limitation can be workarounded by switching of the transitive project references via `DisableTransitiveProjectReferences` (same as shown in [Access to transitive project references](#access-to-transitive-project-references))

Просмотреть файл

@ -6,25 +6,39 @@ However, you should be aware what type of information is captured in the binary
⚠ NOTE: some build environments make secrets available using environment variables. Before sharing a binary log, make sure it does not expose API tokens or other important secrets.
## Capturing Binary Logs for command-line builds
You can create a binary log by passing the `-bl` parameter to MSBuild (`MSBuild.exe` or `dotnet build`). You can explore the contents of the generated .binlog file using [MSBuild Structured Log Viewer](http://msbuildlog.com/) or in your browser using [Live Structured Log Viewer](https://live.msbuildlog.com). Note: We don't capture any data from binary logs viewed on your browser.
Examples:
```sh
dotnet build -bl
dotnet build -bl:SpecificStep.binlog
MSBuild.exe -bl:ServiceRelease.binlog -p:Configuration=Release
```
[More details about binary logs](Binary-Log.md)
## Capturing Binary Logs Through Visual Studio
### (Preferred way) Capturing logs for all MSBuild invocations
### Capturing logs for all MSBuild invocations
Set `MSBUILDDEBUGENGINE` environment variable to `'1'` and (optionally) set `MSBUILDDEBUGPATH` to an existing destination folder to store the captured logs. Then start Visual Studio from the same shell to inherit the environment:
`cmd:`
```
```batch
> SET MSBUILDDEBUGENGINE=1
> SET MSBUILDDEBUGPATH=C:\MSBuildReproLogs
> devenv.exe MySolution.sln
```
`PowerShell:`
```
```powershell
> $env:MSBUILDDEBUGENGINE = 1
> $env:MSBUILDDEBUGPATH= C:\MSBuildReproLogs
> $env:MSBUILDDEBUGPATH="C:\MSBuildReproLogs"
> & "devenv.exe" MySolution.sln
```
@ -33,8 +47,10 @@ MSBuild binary logs are then captured to a location specified via `MSBUILDDEBUGP
⚠ NOTE: logs are being recorded for each MSBuild invocation (including design time builds) and kept in the folder without removing older ones - so the number of log files can grow quickly. It is recommended to set the opt-in environment variable only for the short duration of reproducing the issue to be investigated (though it is understandable that some nondeterministic issues might need multiple reproduction attempts)
Further reading:
* [More technical info](Building-Testing-and-Debugging-on-Full-Framework-MSBuild.md#logs)
* [Design time builds logs](https://github.com/dotnet/project-system/blob/main/docs/repo/debugging/design-time-builds.md#gathering-full-fidelity-binlogs)
### Capturing specific logs for chosen build invocations
See [this guide](https://github.com/dotnet/project-system-tools) in the Project System Tools repo for capturing binlogs through Visual Studio.

Просмотреть файл

@ -38,9 +38,12 @@
<_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('NuGetSdkResolver'))' == 'True'" />
<_NuGetRuntimeDependencies Include="%(RuntimeCopyLocalItems.Identity)" Condition="'@(RuntimeCopyLocalItems->Contains('Microsoft.Extensions.'))' == 'True'" />
<!-- NuGet.targets will be in the ResolvedRuntimeTargets ItemGroup -->
<!-- NuGet.targets and NuGet.RestoreEx.targets will be in the RuntimeTargetsCopyLocalItems ItemGroup -->
<_NuGetRuntimeDependencies Include="%(RuntimeTargetsCopyLocalItems.Identity)" Condition="'@(RuntimeTargetsCopyLocalItems->Contains('NuGet.'))' == 'True'" />
<!-- NuGet.Build.Tasks.Console.exe will be in the None ItemGroup -->
<_NuGetRuntimeDependencies Include="%(None.Identity)" Condition="'@(None->Contains('NuGet.'))' == 'True'" />
<_NuGetRuntimeDependencies Include="$(DOTNET_INSTALL_DIR)\sdk\$(DotNetCliVersion)\RuntimeIdentifierGraph.json" />
</ItemGroup>
</Target>
@ -122,35 +125,45 @@
<!-- Copy in props and targets from the machine-installed MSBuildExtensionsPath -->
<Copy SourceFiles="@(InstalledVersionedExtensions)"
DestinationFiles="@(InstalledVersionedExtensions->'$(BootstrapDestination)$(TargetMSBuildToolsVersion)\%(RecursiveDir)%(Filename)%(Extension)')" />
DestinationFiles="@(InstalledVersionedExtensions->'$(BootstrapDestination)$(TargetMSBuildToolsVersion)\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(SdkResolverFiles)"
DestinationFiles="@(SdkResolverFiles->'$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\SdkResolvers\Microsoft.DotNet.MSBuildSdkResolver\%(RecursiveDir)%(Filename)%(Extension)')" />
DestinationFiles="@(SdkResolverFiles->'$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\SdkResolvers\Microsoft.DotNet.MSBuildSdkResolver\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(InstalledMicrosoftExtensions)"
DestinationFiles="@(InstalledMicrosoftExtensions->'$(BootstrapDestination)Microsoft\%(RecursiveDir)%(Filename)%(Extension)')" />
DestinationFiles="@(InstalledMicrosoftExtensions->'$(BootstrapDestination)Microsoft\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(InstalledSdks)"
DestinationFiles="@(InstalledSdks -> '$(BootstrapDestination)Sdks\%(RecursiveDir)%(Filename)%(Extension)')"
Condition="'$(MonoBuild)' != 'true'" />
Condition="'$(MonoBuild)' != 'true'"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(InstalledSdks)"
DestinationFiles="@(InstalledSdks -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\Sdks\%(RecursiveDir)%(Filename)%(Extension)')"
Condition="'$(MonoBuild)' == 'true'" />
Condition="'$(MonoBuild)' == 'true'"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(InstalledStaticAnalysisTools)"
DestinationFiles="@(InstalledStaticAnalysisTools -> '$(BootstrapDestination)..\Team Tools\Static Analysis Tools\%(RecursiveDir)%(Filename)%(Extension)')" />
DestinationFiles="@(InstalledStaticAnalysisTools -> '$(BootstrapDestination)..\Team Tools\Static Analysis Tools\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(InstalledNuGetFiles)"
DestinationFiles="@(InstalledNuGetFiles->'$(BootstrapDestination)Microsoft\NuGet\%(Filename)%(Extension)')" />
DestinationFiles="@(InstalledNuGetFiles->'$(BootstrapDestination)Microsoft\NuGet\%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<Copy Condition="'$(MonoBuild)' != 'true'"
SourceFiles="@(_NuGetRuntimeDependencies)"
DestinationFolder="$(BootstrapDestination)..\Common7\IDE\CommonExtensions\Microsoft\NuGet\" />
DestinationFolder="$(BootstrapDestination)..\Common7\IDE\CommonExtensions\Microsoft\NuGet\"
SkipUnchangedFiles="true" />
<Copy Condition="'$(MonoBuild)' == 'true'"
SourceFiles="@(_NuGetRuntimeDependencies)"
DestinationFolder="$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin" />
DestinationFolder="$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(NuGetSdkResolverManifest)"
DestinationFolder="$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\SdkResolvers\Microsoft.Build.NuGetSdkResolver" />
DestinationFolder="$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\SdkResolvers\Microsoft.Build.NuGetSdkResolver"
SkipUnchangedFiles="true" />
<!-- Delete shim projects, because they point where we can't follow. -->
<!-- It would be better to just not copy these. -->
@ -158,27 +171,36 @@
<!-- Copy our binaries -->
<Copy SourceFiles="@(FreshlyBuiltBinaries)"
DestinationFiles="@(FreshlyBuiltBinaries -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\%(RecursiveDir)%(Filename)%(Extension)')" />
DestinationFiles="@(FreshlyBuiltBinaries -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(RoslynBinaries)"
DestinationFiles="@(RoslynBinaries -> '$(BootstrapDestination)15.0\Bin\Roslyn\%(RecursiveDir)%(Filename)%(Extension)')" />
DestinationFiles="@(RoslynBinaries -> '$(BootstrapDestination)15.0\Bin\Roslyn\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<!-- Copy our binaries to the x64 location. -->
<Copy SourceFiles="@(FreshlyBuiltBinariesx64)"
DestinationFiles="@(FreshlyBuiltBinariesx64 -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\amd64\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(FreshlyBuiltBinariesx64)"
DestinationFiles="@(FreshlyBuiltBinariesx64 -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\amd64\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<!-- Copy our binaries to the arm64 location. -->
<Copy SourceFiles="@(FreshlyBuiltBinariesArm64)"
DestinationFiles="@(FreshlyBuiltBinariesArm64 -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\arm64\%(RecursiveDir)%(Filename)%(Extension)')" />
<Copy SourceFiles="@(FreshlyBuiltBinariesArm64)"
DestinationFiles="@(FreshlyBuiltBinariesArm64 -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\arm64\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<!-- Copy our freshly-built props and targets, overwriting anything we copied from the machine -->
<Copy SourceFiles="@(FreshlyBuiltRootProjects)"
DestinationFiles="@(FreshlyBuiltRootProjects -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\%(Filename)%(Extension)')" />
DestinationFiles="@(FreshlyBuiltRootProjects -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(FreshlyBuiltProjects)"
DestinationFiles="@(FreshlyBuiltProjects -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\%(RecursiveDir)%(Filename)%(Extension)')" />
DestinationFiles="@(FreshlyBuiltProjects -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(FreshlyBuiltProjects)"
DestinationFiles="@(FreshlyBuiltProjects -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\amd64\%(RecursiveDir)%(Filename)%(Extension)')" />
DestinationFiles="@(FreshlyBuiltProjects -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\amd64\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
<Copy SourceFiles="@(FreshlyBuiltProjects)"
DestinationFiles="@(FreshlyBuiltProjects -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\arm64\%(RecursiveDir)%(Filename)%(Extension)')" />
DestinationFiles="@(FreshlyBuiltProjects -> '$(BootstrapDestination)$(TargetMSBuildToolsVersion)\Bin\arm64\%(RecursiveDir)%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
</Target>
@ -221,6 +243,9 @@
<Copy SourceFiles="@(_NuGetRuntimeDependencies)"
DestinationFolder="$(BootstrapDestination)" />
<Copy SourceFiles="$(RepoRoot)src\MSBuild.Bootstrap\RedirectNuGetConsoleProcess.After.Microsoft.Common.targets"
DestinationFolder="$(BootstrapDestination)\Current\Microsoft.Common.targets\ImportAfter" />
<!-- Disable workload resolver until we can figure out whether it can work in the bootstrap
https://github.com/dotnet/msbuild/issues/6566 -->
<Touch Files="$(BootstrapDestination)\DisableWorkloadResolver.sentinel" AlwaysCreate="true" />

Просмотреть файл

@ -13,8 +13,9 @@
<PackageVersion Include="Microsoft.CodeAnalysis.Collections" Version="$(MicrosoftCodeAnalysisCollectionsVersion)" />
<PackageVersion Include="Microsoft.DotNet.XUnitExtensions" Version="$(MicrosoftDotNetXUnitExtensionsVersion)" />
<PackageVersion Include="Microsoft.IO.Redist" Version="$(MicrosoftIORedistVersion)" />
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="$(MicrosoftNetCompilersToolsetVersion)" Condition="'$(UsingToolMicrosoftNetCompilers)' != 'true'" />
<PackageVersion Include="Microsoft.Net.Compilers.Toolset" Version="$(MicrosoftNetCompilersToolsetVersion)" Condition="'$(UsingToolMicrosoftNetCompilers)' != 'true'" />
<PackageVersion Include="NuGet.Build.Tasks" Version="$(NuGetBuildTasksVersion)" />
<PackageVersion Include="NuGet.Build.Tasks.Console" Version="$(NuGetBuildTasksVersion)" />
<PackageVersion Include="NuGet.Frameworks" Version="$(NuGetBuildTasksVersion)" />
<PackageVersion Include="System.Collections.Immutable" Version="$(SystemCollectionsImmutableVersion)" />
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="$(SystemConfigurationConfigurationManagerVersion)" />

Просмотреть файл

@ -1,3 +1,5 @@
<!-- Whenever altering this or other Source Build files, please include @dotnet/source-build-internal as a reviewer. -->
<Project>
<PropertyGroup>

Просмотреть файл

@ -1,5 +1,37 @@
<!-- Whenever altering this or other Source Build files, please include @dotnet/source-build-internal as a reviewer. -->
<!-- See aka.ms/dotnet/prebuilts for guidance on what pre-builts are and how to eliminate them. -->
<UsageData>
<IgnorePatterns>
<UsagePattern IdentityGlob="*/*" />
<UsagePattern IdentityGlob="Microsoft.SourceBuild.Intermediate.*/*" />
<!-- These dependencies are a result of building for netframework TFMs. These are filtered out
in full source-build, and would be filtered out if msbuild was using an 8.0 arcade + 8.0 SDK -->
<UsagePattern IdentityGlob="Microsoft.NETFramework.ReferenceAssemblies/*1.0.3*" />
<UsagePattern IdentityGlob="Microsoft.NETFramework.ReferenceAssemblies.net472/*1.0.3*" />
<!-- Baseline 7.0 dependencies until msbuild targets net8 and uses a net8 arcade, SBRP, etc.
These cannot be added to 7.0 SBRP, because they would are produced in the 7.0 build. -->
<UsagePattern IdentityGlob="Microsoft.Bcl.AsyncInterfaces/*7.0.0*" />
<UsagePattern IdentityGlob="Microsoft.Win32.SystemEvents/*7.0.0*" />
<UsagePattern IdentityGlob="System.CodeDom/*7.0.0*" />
<UsagePattern IdentityGlob="System.Collections.Immutable/*7.0.0*" />
<UsagePattern IdentityGlob="System.Configuration.ConfigurationManager/*7.0.0*" />
<UsagePattern IdentityGlob="System.Diagnostics.EventLog/*7.0.0*" />
<UsagePattern IdentityGlob="System.Drawing.Common/*7.0.0*" />
<UsagePattern IdentityGlob="System.Formats.Asn1/*7.0.0*" />
<UsagePattern IdentityGlob="System.Reflection.Metadata/*7.0.0*" />
<UsagePattern IdentityGlob="System.Reflection.MetadataLoadContext/*7.0.0*" />
<UsagePattern IdentityGlob="System.Resources.Extensions/*7.0.0*" />
<UsagePattern IdentityGlob="System.Security.Cryptography.Pkcs/*7.0.0*" />
<UsagePattern IdentityGlob="System.Security.Cryptography.ProtectedData/*7.0.0*" />
<UsagePattern IdentityGlob="System.Security.Cryptography.Xml/*7.0.1*" />
<UsagePattern IdentityGlob="System.Security.Permissions/*7.0.0*" />
<UsagePattern IdentityGlob="System.Text.Encoding.CodePages/*7.0.0*" />
<UsagePattern IdentityGlob="System.Text.Encodings.Web/*7.0.0*" />
<UsagePattern IdentityGlob="System.Text.Json/*7.0.0*" />
<UsagePattern IdentityGlob="System.Threading.Tasks.Dataflow/*7.0.0*" />
<UsagePattern IdentityGlob="System.Windows.Extensions/*7.0.0*" />
</IgnorePatterns>
</UsageData>

Просмотреть файл

@ -1,11 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<Dependencies>
<ProductDependencies>
<Dependency Name="Microsoft.SourceBuild.Intermediate.source-build-reference-packages" Version="7.0.0-alpha.1.23219.1">
<Uri>https://github.com/dotnet/source-build-reference-packages</Uri>
<Sha>525b6c35cc5c5c9b80b47044be2e4e77858d505a</Sha>
<SourceBuild RepoName="source-build-reference-packages" ManagedOnly="true" />
</Dependency>
<!-- Necessary for source-build. This allows the packages to be retrieved from previously-source-built artifacts
and flow in as dependencies of the packages produced by msbuild. -->
<Dependency Name="System.Configuration.ConfigurationManager" Version="7.0.0">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>d099f075e45d2aa6007a22b71b45a08758559f80</Sha>
</Dependency>
<Dependency Name="System.Security.Cryptography.Pkcs" Version="7.0.0">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>d099f075e45d2aa6007a22b71b45a08758559f80</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="6.0.0-beta.23313.5">
<Uri>https://github.com/dotnet/arcade</Uri>
<Sha>91616785a1a6578c83f7e93d98c34a1eb83d6223</Sha>
<SourceBuild RepoName="arcade" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.SourceLink.GitHub" Version="1.1.0-beta-21480-02" CoherentParentDependency="Microsoft.DotNet.Arcade.Sdk">
<Uri>https://github.com/dotnet/sourcelink</Uri>
<Sha>8031e5220baf2acad991e661d8308b783d2acf3e</Sha>
<SourceBuild RepoName="sourcelink" ManagedOnly="true" />
</Dependency>
<Dependency Name="Microsoft.DotNet.XliffTasks" Version="1.0.0-beta.21431.1" CoherentParentDependency="Microsoft.DotNet.Arcade.Sdk">
<Uri>https://github.com/dotnet/xliff-tasks</Uri>
<Sha>bc3233146e1fcd393ed471d5005333c83363e0fe</Sha>
<SourceBuild RepoName="xliff-tasks" ManagedOnly="true" />
</Dependency>
<Dependency Name="NuGet.Build.Tasks" Version="6.7.0-preview.2.51">
<Uri>https://github.com/nuget/nuget.client</Uri>
<Sha>f3bb337e310ce44abda4ad73cdb0755ed940809d</Sha>

Просмотреть файл

@ -2,9 +2,8 @@
<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the MIT license. See License.txt in the project root for full license information. -->
<Project>
<PropertyGroup>
<VersionPrefix>17.6.7</VersionPrefix>
<DotNetFinalVersionKind>release</DotNetFinalVersionKind>
<PackageValidationBaselineVersion>17.5.0</PackageValidationBaselineVersion>
<VersionPrefix>17.7.0</VersionPrefix>
<PackageValidationBaselineVersion>17.6.3</PackageValidationBaselineVersion>
<AssemblyVersion>15.1.0.0</AssemblyVersion>
<PreReleaseVersionLabel>preview</PreReleaseVersionLabel>
<DotNetUseShippingVersions>true</DotNetUseShippingVersions>
@ -57,6 +56,7 @@
<SystemRuntimeCompilerServicesUnsafeVersion>6.0.0</SystemRuntimeCompilerServicesUnsafeVersion>
<SystemTextJsonVersion>7.0.0</SystemTextJsonVersion>
<SystemThreadingTasksDataflowVersion>7.0.0</SystemThreadingTasksDataflowVersion>
<XunitVersion>2.4.2</XunitVersion>
</PropertyGroup>
<Target Name="OverrideArcadeFileVersion" AfterTargets="_InitializeAssemblyVersion">
<!-- See https://github.com/dotnet/arcade/issues/3386

Просмотреть файл

@ -48,6 +48,9 @@
<PackageVersion Include="System.Security.Cryptography.X509Certificates" Version="4.3.2" />
<PackageVersion Update="System.Security.Cryptography.X509Certificates" Condition="'$(SystemSecurityCryptographyX509CertificatesVersion)' != ''" Version="$(SystemSecurityCryptographyX509CertificatesVersion)" />
<PackageVersion Include="Verify.Xunit" Version="19.14.1" />
<PackageVersion Update="Verify.XUnit" Condition="'$(VerifyXUnitVersion)' != ''" Version="$(VerifyXUnitVersion)" />
</ItemGroup>
<ItemGroup Condition="'$(DotNetBuildFromSource)' != 'true' AND $(ProjectIsDeprecated) != 'true'">

11
eng/sdl-tsa-vars.config Normal file
Просмотреть файл

@ -0,0 +1,11 @@
-SourceToolsList @("policheck","credscan")
-TsaInstanceURL https://devdiv.visualstudio.com/
-TsaProjectName DEVDIV
-TsaNotificationEmail dotnetdevexcli@microsoft.com
-TsaCodebaseAdmin REDMOND\marcpop
-TsaBugAreaPath "DevDiv\NET Tools\MSBuild"
-TsaIterationPath DevDiv
-TsaRepositoryName DotNet-msbuild-Trusted
-TsaCodebaseName DotNet-msbuild-Trusted
-TsaOnboard $True
-TsaPublish $True

2
newc/Program.cs Normal file
Просмотреть файл

@ -0,0 +1,2 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

10
newc/newc.csproj Normal file
Просмотреть файл

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

Просмотреть файл

@ -82,6 +82,30 @@ namespace Microsoft.Build.UnitTests.OM.Construction
Assert.Equal(0, Helpers.Count(item.Metadata));
}
[Fact]
public void ReadMetadataLocationPreserved()
{
string project = """
<Project>
<Target Name='t'>
<ItemGroup>
<i Include='i' MetadataA='123' MetadataB='xyz' />
</ItemGroup>
</Target>
</Project>
""";
ProjectItemElement item = GetItemFromContent(project);
Assert.Equal(2, item.Metadata.Count);
ProjectMetadataElement metadatum1 = item.Metadata.First();
ProjectMetadataElement metadatum2 = item.Metadata.Skip(1).First();
Assert.Equal(4, metadatum1.Location.Line);
Assert.Equal(4, metadatum2.Location.Line);
Assert.Equal(27, metadatum1.Location.Column);
Assert.Equal(43, metadatum2.Location.Column);
}
/// <summary>
/// Read item with no include
/// </summary>

Просмотреть файл

@ -35,9 +35,6 @@
<Compile Include="..\Shared\EncodingStringWriter.cs">
<Link>EncodingStringWriter.cs</Link>
</Compile>
<Compile Include="..\Shared\EncodingUtilities.cs">
<Link>EncodingUtilities.cs</Link>
</Compile>
<Compile Include="..\Shared\UnitTests\ObjectModelHelpers.cs">
<ExcludeFromStyleCop>true</ExcludeFromStyleCop>
</Compile>

Просмотреть файл

@ -1068,7 +1068,7 @@ namespace Microsoft.Build.UnitTests.Logging
public void ImportanceReflectsUnknownLoggerVerbosity()
{
// Minimum message importance is Low (i.e. we're logging everything) even when all registered loggers have
// Normal verbosity if at least of one them is not on our whitelist.
// Normal verbosity if at least of one them is not on our allowlist.
_initializedService.RegisterLogger(new ConsoleLogger(LoggerVerbosity.Normal));
_initializedService.RegisterLogger(new MockLogger() { Verbosity = LoggerVerbosity.Normal });
_initializedService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Normal));

Просмотреть файл

@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
@ -772,6 +771,121 @@ namespace Microsoft.Build.UnitTests
}
}
/// <summary>
/// Referring to an item outside of target leads to 'naturally expected' reference to the item being processed.
/// No expansion occurs.
/// </summary>
[Fact]
public void ItemsRecursionOutsideTarget()
{
using TestEnvironment env = TestEnvironment.Create();
string projectContent = """
<Project ToolsVersion='msbuilddefaulttoolsversion' xmlns='msbuildnamespace'>
<ItemGroup>
<iout1 Include='a/b.foo' TargetPath='%(Filename)%(Extension)' />
<iout1 Include='c/d.foo' TargetPath='%(Filename)%(Extension)' />
<iout1 Include='g/h.foo' TargetPath='%(Filename)%(Extension)' />
</ItemGroup>
<Target Name='a'>
<Message Text="iout1=[@(iout1)]" Importance='High' />
<Message Text="iout1-target-paths=[@(iout1->'%(TargetPath)')]" Importance='High' />
</Target>
</Project>
""";
var projectFile = env.CreateFile("test.proj", ObjectModelHelpers.CleanupFileContents(projectContent));
MockLogger logger = new MockLogger(_testOutput);
ObjectModelHelpers.BuildTempProjectFileExpectSuccess(projectFile.Path, logger);
_testOutput.WriteLine(logger.FullLog);
logger.AssertLogContains("iout1=[a/b.foo;c/d.foo;g/h.foo]");
logger.AssertLogContains("iout1-target-paths=[b.foo;d.foo;h.foo]");
}
/// <summary>
/// Referring to an item within target leads to item expansion which might be unintended behavior - hence warning.
/// </summary>
[Fact]
public void ItemsRecursionWithinTarget()
{
using TestEnvironment env = TestEnvironment.Create();
string projectContent = """
<Project ToolsVersion='msbuilddefaulttoolsversion' xmlns='msbuildnamespace'>
<Target Name='a'>
<ItemGroup>
<iin1 Include='a/b.foo' TargetPath='%(Filename)%(Extension)' />
<iin1 Include='c/d.foo' TargetPath='%(Filename)%(Extension)' />
<iin1 Include='g/h.foo' TargetPath='%(Filename)%(Extension)' />
</ItemGroup>
<Message Text="iin1=[@(iin1)]" Importance='High' />
<Message Text="iin1-target-paths=[@(iin1->'%(TargetPath)')]" Importance='High' />
</Target>
</Project>
""";
string projFileName = "test.proj";
var projectFile = env.CreateFile(projFileName, ObjectModelHelpers.CleanupFileContents(projectContent));
MockLogger logger = new MockLogger(_testOutput);
ObjectModelHelpers.BuildTempProjectFileExpectSuccess(projectFile.Path, logger);
_testOutput.WriteLine(logger.FullLog);
logger.AssertLogDoesntContain("iin1=[a/b.foo;c/d.foo;g/h.foo]");
logger.AssertLogDoesntContain("iin1-target-paths=[b.foo;d.foo;h.foo]");
logger.AssertLogContains("iin1=[a/b.foo;c/d.foo;g/h.foo;g/h.foo]");
logger.AssertLogContains("iin1-target-paths=[;b.foo;b.foo;d.foo]");
logger.AssertLogContains(string.Format(ResourceUtilities.GetResourceString("ItemReferencingSelfInTarget"), "iin1", "Filename"));
logger.AssertLogContains(string.Format(ResourceUtilities.GetResourceString("ItemReferencingSelfInTarget"), "iin1", "Extension"));
logger.AssertMessageCount("MSB4120", 6);
// The location of the offending attribute (TargetPath) is transferred - for both metadatums (%(Filename) and %(Extension)) on correct locations in xml
logger.AssertMessageCount($"{projFileName}(4,34):", 2, false);
logger.AssertMessageCount($"{projFileName}(5,34):", 2, false);
logger.AssertMessageCount($"{projFileName}(6,34):", 2, false);
Assert.Equal(0, logger.WarningCount);
Assert.Equal(0, logger.ErrorCount);
}
/// <summary>
/// Referring to an unrelated item within target leads to expected expansion.
/// </summary>
[Fact]
public void UnrelatedItemsRecursionWithinTarget()
{
using TestEnvironment env = TestEnvironment.Create();
string projectContent = """
<Project ToolsVersion='msbuilddefaulttoolsversion' xmlns='msbuildnamespace'>
<ItemGroup>
<iout1 Include='a/b.foo'/>
<iout1 Include='c/d.foo'/>
<iout1 Include='g/h.foo'/>
</ItemGroup>
<Target Name='a'>
<ItemGroup>
<iin1 Include='@(iout1)' TargetPath='%(Filename)%(Extension)' />
</ItemGroup>
<Message Text="iin1=[@(iin1)]" Importance='High' />
<Message Text="iin1-target-paths=[@(iin1->'%(TargetPath)')]" Importance='High' />
</Target>
</Project>
""";
var projectFile = env.CreateFile("test.proj", ObjectModelHelpers.CleanupFileContents(projectContent));
MockLogger logger = new MockLogger(_testOutput);
ObjectModelHelpers.BuildTempProjectFileExpectSuccess(projectFile.Path, logger);
_testOutput.WriteLine(logger.FullLog);
logger.AssertLogContains("iin1=[a/b.foo;c/d.foo;g/h.foo]");
logger.AssertLogContains("iin1-target-paths=[b.foo;d.foo;h.foo]");
logger.AssertLogDoesntContain("MSB4120");
Assert.Equal(0, logger.WarningCount);
Assert.Equal(0, logger.ErrorCount);
}
/// <summary>
/// Check if passing different global properties via metadata works
/// </summary>

Просмотреть файл

@ -236,6 +236,41 @@ Done building target ""Build"" in project ""build.proj"".".Replace("\r\n", "\n")
}
}
[Fact]
public void TestErrorForSkippedTargetInputsAndOutputs()
{
string projectContents = @"
<Project>
<Target Name=""Build"" Inputs=""a.txt;b.txt"" Outputs=""c.txt"">
<Message Text=""test"" Importance=""High"" />
</Target>
</Project>";
using (var env = TestEnvironment.Create())
{
var buildParameters = new BuildParameters()
{
Question = true,
};
using (var buildSession = new Helpers.BuildManagerSession(env, buildParameters))
{
var files = env.CreateTestProjectWithFiles(projectContents, new[] { "a.txt", "b.txt", "c.txt" });
var fileA = new FileInfo(files.CreatedFiles[0]);
var fileB = new FileInfo(files.CreatedFiles[1]);
var fileC = new FileInfo(files.CreatedFiles[2]);
var now = DateTime.UtcNow;
fileA.LastWriteTimeUtc = now - TimeSpan.FromSeconds(10);
fileB.LastWriteTimeUtc = now + TimeSpan.FromSeconds(10);
fileC.LastWriteTimeUtc = now;
var result = buildSession.BuildProjectFile(files.ProjectFile);
result.OverallResult.ShouldBe(BuildResultCode.Failure);
}
}
}
/// <summary>
/// Ensure that skipped targets only infer outputs once
/// </summary>

Просмотреть файл

@ -572,7 +572,7 @@ namespace Microsoft.Build.UnitTests.BackEnd
ItemBucket itemBucket = new ItemBucket(null, null, new Lookup(itemsByName, new PropertyDictionary<ProjectPropertyInstance>()), 0);
TargetUpToDateChecker analyzer = new TargetUpToDateChecker(p, p.Targets["Build"], _mockHost, BuildEventContext.Invalid);
return analyzer.PerformDependencyAnalysis(itemBucket, out changedTargetInputs, out upToDateTargetInputs);
return analyzer.PerformDependencyAnalysis(itemBucket, false, out changedTargetInputs, out upToDateTargetInputs);
}
finally
{

Просмотреть файл

@ -164,7 +164,6 @@ namespace Microsoft.Build.UnitTests.BackEnd
Loggers = new ILogger[] { logger },
EnableNodeReuse = false
};
;
BuildRequestData data = new BuildRequestData(project.CreateProjectInstance(), new string[] { "test" }, collection.HostServices);
manager.BeginBuild(_parameters);
@ -597,6 +596,31 @@ namespace Microsoft.Build.UnitTests.BackEnd
logger.AssertLogContains("[foo: ]");
}
/// <summary>
/// If an item returned from a task has bare-minimum metadata implementation, we shouldn't crash.
/// </summary>
[Fact]
public void MinimalLegacyOutputItems()
{
string customTaskPath = Assembly.GetExecutingAssembly().Location;
string projectContents = $"""
<Project>
<UsingTask TaskName="TaskThatReturnsMinimalItem" AssemblyFile="{customTaskPath}" />
<Target Name="Build">
<TaskThatReturnsMinimalItem>
<Output TaskParameter="MinimalTaskItemOutput" ItemName="Outputs"/>
</TaskThatReturnsMinimalItem>
<Message Text="[%(Outputs.Identity): %(Outputs.a)]" Importance="High" />
</Target>
</Project>
""";
MockLogger logger = ObjectModelHelpers.BuildProjectExpectSuccess(projectContents, _testOutput, LoggerVerbosity.Diagnostic);
}
/// <summary>
/// Regression test for https://github.com/dotnet/msbuild/issues/5080
/// </summary>

Просмотреть файл

@ -262,7 +262,8 @@ namespace Microsoft.Build.UnitTests
// Can't just compare `Name` because `ZipArchive` does not handle unix directory separators well
// thus producing garbled fully qualified paths in the actual .ProjectImports.zip entries
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith("testtaskoutputfile.txt"));
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith("testtaskoutputfile.txt"),
() => $"Embedded files: {string.Join(",", zipArchive.Entries)}");
}
[RequiresSymbolicLinksFact]
@ -321,10 +322,14 @@ namespace Microsoft.Build.UnitTests
// Can't just compare `Name` because `ZipArchive` does not handle unix directory separators well
// thus producing garbled fully qualified paths in the actual .ProjectImports.zip entries
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith("testtaskoutputfile.txt"));
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith(symlinkName));
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith(symlinkLvl2Name));
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith(emptyFileName));
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith("testtaskoutputfile.txt"),
() => $"Embedded files: {string.Join(",", zipArchive.Entries)}");
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith(symlinkName),
() => $"Embedded files: {string.Join(",", zipArchive.Entries)}");
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith(symlinkLvl2Name),
() => $"Embedded files: {string.Join(",", zipArchive.Entries)}");
zipArchive.Entries.ShouldContain(zE => zE.Name.EndsWith(emptyFileName),
() => $"Embedded files: {string.Join(",", zipArchive.Entries)}");
}
[Fact]

Просмотреть файл

@ -130,26 +130,44 @@ namespace Microsoft.Build.UnitTests.Definition
Should.Throw<ArgumentException>(() => EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated, fileSystem));
}
[Fact]
public void EvaluationShouldUseDirectoryCache()
[Theory]
[InlineData(false)]
[InlineData(true)]
public void EvaluationShouldUseDirectoryCache(bool useProjectInstance)
{
var projectFile = _env.CreateFile("1.proj", @"<Project> <ItemGroup Condition=`Exists('1.file')`> <Compile Include='*.cs'/> </ItemGroup> </Project>".Cleanup()).Path;
var projectFile = _env.CreateFile("1.proj", @"<Project> <Import Project='1.file' Condition=`Exists('1.file')`/> <ItemGroup><Compile Include='*.cs'/></ItemGroup> </Project>".Cleanup()).Path;
var projectCollection = _env.CreateProjectCollection().Collection;
var directoryCacheFactory = new Helpers.LoggingDirectoryCacheFactory();
var project = Project.FromFile(
projectFile,
new ProjectOptions
{
ProjectCollection = projectCollection,
DirectoryCacheFactory = directoryCacheFactory,
});
int expectedEvaluationId;
if (useProjectInstance)
{
var projectInstance = ProjectInstance.FromFile(
projectFile,
new ProjectOptions
{
ProjectCollection = projectCollection,
DirectoryCacheFactory = directoryCacheFactory,
});
expectedEvaluationId = projectInstance.EvaluationId;
}
else
{
var project = Project.FromFile(
projectFile,
new ProjectOptions
{
ProjectCollection = projectCollection,
DirectoryCacheFactory = directoryCacheFactory,
});
expectedEvaluationId = project.LastEvaluationId;
}
directoryCacheFactory.DirectoryCaches.Count.ShouldBe(1);
var directoryCache = directoryCacheFactory.DirectoryCaches[0];
directoryCache.EvaluationId.ShouldBe(project.LastEvaluationId);
directoryCache.EvaluationId.ShouldBe(expectedEvaluationId);
directoryCache.ExistenceChecks.OrderBy(kvp => kvp.Key).ShouldBe(
new Dictionary<string, int>

Просмотреть файл

@ -868,15 +868,15 @@ namespace Microsoft.Build.UnitTests.EscapingInProjects_Tests
<MyUserMacro>foo%3bbar</MyUserMacro>
</PropertyGroup>
<ItemGroup>
<CrazyList Include=""a"" />
<CrazyList Include=""b%3bc"" />
<CrazyList Include=""$(MyUserMacro)"" />
<DifferentList Include=""a"" />
<DifferentList Include=""b%3bc"" />
<DifferentList Include=""$(MyUserMacro)"" />
</ItemGroup>
</Project>";
System.Xml.XmlReader reader = new System.Xml.XmlTextReader(new StringReader(projectString));
Project project = new Project(reader);
IEnumerable<ProjectItem> items = project.GetItems("CrazyList");
IEnumerable<ProjectItem> items = project.GetItems("DifferentList");
Assert.Equal(3, items.Count());
Assert.Equal("a", items.ElementAt(0).EvaluatedInclude);
@ -900,15 +900,15 @@ namespace Microsoft.Build.UnitTests.EscapingInProjects_Tests
<MyUserMacro>foo;bar</MyUserMacro>
</PropertyGroup>
<ItemGroup>
<CrazyList Include=""a"" />
<CrazyList Include=""b%3bc"" />
<CrazyList Include=""$(MyUserMacro)"" />
<DifferentList Include=""a"" />
<DifferentList Include=""b%3bc"" />
<DifferentList Include=""$(MyUserMacro)"" />
</ItemGroup>
</Project>";
System.Xml.XmlReader reader = new System.Xml.XmlTextReader(new StringReader(projectString));
Project project = new Project(reader);
IEnumerable<ProjectItem> items = project.GetItems("CrazyList");
IEnumerable<ProjectItem> items = project.GetItems("DifferentList");
Assert.Equal(4, items.Count());
Assert.Equal("a", items.ElementAt(0).EvaluatedInclude);
@ -1446,11 +1446,11 @@ namespace Microsoft.Build.UnitTests.EscapingInProjects_Tests
/// <summary>
/// Build a .SLN file using MSBuild. The .SLN and the projects contained within
/// have all sorts of crazy characters in their name. There
/// have all sorts of different characters in their name. There
/// is even a P2P reference between the two projects in the .SLN.
/// </summary>
[Fact(Skip = "This is a known issue in Roslyn. This test should be enabled if Roslyn is updated for this scenario.")]
public void SolutionWithLotsaCrazyCharacters()
public void SolutionWithLotsaDifferentCharacters()
{
ObjectModelHelpers.DeleteTempProjectDirectory();
@ -1612,11 +1612,11 @@ namespace Microsoft.Build.UnitTests.EscapingInProjects_Tests
/// <summary>
/// Build a .SLN file using MSBuild. The .SLN and the projects contained within
/// have all sorts of crazy characters in their name. There
/// have all sorts of different characters in their name. There
/// is even a P2P reference between the two projects in the .SLN.
/// </summary>
[Fact(Skip = "This is a known issue in Roslyn. This test should be enabled if Roslyn is updated for this scenario.")]
public void SolutionWithLotsaCrazyCharacters_UsingTaskHost()
public void SolutionWithLotsaDifferentCharacters_UsingTaskHost()
{
string originalOverrideTaskHostVariable = Environment.GetEnvironmentVariable("MSBUILDFORCEALLTASKSOUTOFPROC");

Просмотреть файл

@ -4473,6 +4473,38 @@ namespace Microsoft.Build.UnitTests.Evaluation
}
}
[Theory]
[InlineData("$(Hello)", 0, 8, "Hello")]
[InlineData("$(Hello)|$(World)", 9, 8, "World")]
[InlineData("$(He()o)", 0, 8, null)]
[InlineData("$)Hello(", 0, 8, null)]
[InlineData("$(Helloo", 0, 8, null)]
[InlineData("$Heello)", 0, 8, null)]
[InlineData("$(He$$o)", 0, 8, null)]
[InlineData(" $(Helo)", 0, 8, null)]
[InlineData("$(Helo) ", 0, 8, null)]
[InlineData("$()", 0, 3, "")]
[InlineData("$( Hello )", 0, 10, " Hello ")]
[InlineData("$(He-ll-o)", 0, 10, "He-ll-o")]
[InlineData("$(He ll o)", 0, 10, "He ll o")]
[InlineData("aaa$(Hello)", 3, 8, "Hello")]
[InlineData("aaa$(Hello)bbb", 3, 8, "Hello")]
public void TryGetSingleProperty(string input, int start, int length, string expected)
{
bool result = ConditionEvaluator.TryGetSingleProperty(input.AsSpan(), start, length, out ReadOnlySpan<char> actual);
if (expected is null)
{
Assert.False(result);
Assert.True(actual.IsEmpty);
}
else
{
Assert.True(result);
Assert.Equal(expected, actual.ToString());
}
}
[Fact]
public void VerifyMSBuildLastModifiedProjectForImport()
{

Просмотреть файл

@ -3180,17 +3180,17 @@ namespace Microsoft.Build.UnitTests.Evaluation
{
PropertyDictionary<ProjectPropertyInstance> pg = new PropertyDictionary<ProjectPropertyInstance>();
pg["BonkersTargetsPath"] = ProjectPropertyInstance.Create("BonkersTargetsPath", "Bonkers");
pg["DifferentTargetsPath"] = ProjectPropertyInstance.Create("DifferentTargetsPath", "Different");
Expander<ProjectPropertyInstance, ProjectItemInstance> expander = new Expander<ProjectPropertyInstance, ProjectItemInstance>(pg, FileSystems.Default);
string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::ValueOrDefault('$(BonkersTargetsPath)', '42'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
string result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::ValueOrDefault('$(DifferentTargetsPath)', '42'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
Assert.Equal("Bonkers", result);
Assert.Equal("Different", result);
pg["BonkersTargetsPath"] = ProjectPropertyInstance.Create("BonkersTargetsPath", String.Empty);
pg["DifferentTargetsPath"] = ProjectPropertyInstance.Create("DifferentTargetsPath", String.Empty);
result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::ValueOrDefault('$(BonkersTargetsPath)', '43'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::ValueOrDefault('$(DifferentTargetsPath)', '43'))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
Assert.Equal("43", result);
}
@ -3400,22 +3400,25 @@ namespace Microsoft.Build.UnitTests.Evaluation
double expectedResult = 9223372036854775807D + 20D;
Assert.Equal(expectedResult.ToString(), result);
}
result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::BitwiseOr(40, 2))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
/// <summary>
/// Expand intrinsic property functions that call a bit operator
/// </summary>
[Fact]
public void PropertyFunctionStaticMethodIntrinsicBitOperations()
{
PropertyDictionary<ProjectPropertyInstance> pg = new PropertyDictionary<ProjectPropertyInstance>();
Assert.Equal((40 | 2).ToString(), result);
Expander<ProjectPropertyInstance, ProjectItemInstance> expander = new Expander<ProjectPropertyInstance, ProjectItemInstance>(pg, FileSystems.Default);
result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::BitwiseAnd(42, 2))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
Assert.Equal((42 & 2).ToString(), result);
result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::BitwiseXor(213, 255))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
Assert.Equal((213 ^ 255).ToString(), result);
result = expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::BitwiseNot(-43))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance);
Assert.Equal((~-43).ToString(), result);
expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::BitwiseOr(40, 2))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ShouldBe((40 | 2).ToString());
expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::BitwiseAnd(42, 2))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ShouldBe((42 & 2).ToString());
expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::BitwiseXor(213, 255))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ShouldBe((213 ^ 255).ToString());
expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::BitwiseNot(-43))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ShouldBe((~-43).ToString());
expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::LeftShift(1, 2))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ShouldBe((1 << 2).ToString());
expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::RightShift(-8, 2))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ShouldBe((-8 >> 2).ToString());
expander.ExpandIntoStringLeaveEscaped(@"$([MSBuild]::RightShiftUnsigned(-8, 2))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance).ShouldBe((-8 >>> 2).ToString());
}
/// <summary>

Просмотреть файл

@ -0,0 +1,42 @@
// 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.UnitTests;
using Xunit;
namespace Microsoft.Build.Evaluation;
public sealed class UsedUninitializedProperties_Tests
{
[Fact]
public void Basics()
{
UsedUninitializedProperties props = new();
Assert.False(props.TryGetPropertyElementLocation("Hello", out IElementLocation? elementLocation));
Assert.Null(elementLocation);
props.RemoveProperty("Hello");
IElementLocation location1 = new MockElementLocation("File1");
IElementLocation location2 = new MockElementLocation("File2");
props.TryAdd("Hello", location1);
props.TryAdd("Hello", location2);
Assert.True(props.TryGetPropertyElementLocation("Hello", out elementLocation));
Assert.Same(location1, elementLocation);
Assert.True(props.TryGetPropertyElementLocation("Hello", out elementLocation));
Assert.Same(location1, elementLocation);
props.RemoveProperty("Hello");
Assert.False(props.TryGetPropertyElementLocation("Hello", out elementLocation));
Assert.Null(elementLocation);
props.RemoveProperty("Hello");
}
}

Просмотреть файл

@ -524,18 +524,37 @@ namespace Microsoft.Build.Graph.UnitTests
var graphFromSolutionEdges = graphFromSolution.TestOnly_Edges.TestOnly_AsConfigurationMetadata();
// Solutions add the CurrentSolutionConfigurationContents global property for platform resolution
foreach ((ConfigurationMetadata, ConfigurationMetadata) graphFromSolutionEdge in graphFromSolutionEdges.Keys)
// These are global properties added by GraphBuilder when building a solution
HashSet<string> propertiesToIgnore = new(StringComparer.OrdinalIgnoreCase)
{
graphFromSolutionEdge.Item1.GlobalProperties.ShouldContainKey("CurrentSolutionConfigurationContents");
graphFromSolutionEdge.Item2.GlobalProperties.ShouldContainKey("CurrentSolutionConfigurationContents");
"CurrentSolutionConfigurationContents",
"BuildingSolutionFile",
"SolutionDir",
"SolutionExt",
"SolutionFileName",
"SolutionName",
SolutionProjectGenerator.SolutionPathPropertyName
};
// Solutions add these global properties
foreach (string propertyToIgnore in propertiesToIgnore)
{
foreach ((ConfigurationMetadata, ConfigurationMetadata) graphFromSolutionEdge in graphFromSolutionEdges.Keys)
{
graphFromSolutionEdge.Item1.GlobalProperties.ShouldContainKey(propertyToIgnore);
graphFromSolutionEdge.Item2.GlobalProperties.ShouldContainKey(propertyToIgnore);
}
}
// Remove CurrentSolutionConfigurationContents for comparison purposes. This is done as a separate pass since some edges may be sharing an instance.
foreach ((ConfigurationMetadata, ConfigurationMetadata) graphFromSolutionEdge in graphFromSolutionEdges.Keys)
// Remove some properties for comparison purposes as we are comparing a graph created from a solution against the graph (without solution properties) used to make the solution.
// This is done as a separate pass since some edges may be sharing an instance.
foreach (string propertyToIgnore in propertiesToIgnore)
{
graphFromSolutionEdge.Item1.GlobalProperties.Remove("CurrentSolutionConfigurationContents");
graphFromSolutionEdge.Item2.GlobalProperties.Remove("CurrentSolutionConfigurationContents");
foreach ((ConfigurationMetadata, ConfigurationMetadata) graphFromSolutionEdge in graphFromSolutionEdges.Keys)
{
graphFromSolutionEdge.Item1.GlobalProperties.Remove(propertyToIgnore);
graphFromSolutionEdge.Item2.GlobalProperties.Remove(propertyToIgnore);
}
}
// Original edges get preserved.

Просмотреть файл

@ -681,6 +681,8 @@ namespace Microsoft.Build.Graph.UnitTests
// This test exercises two key features of solution-based builds from AssignProjectConfiguration:
// 1. Adding synthetic project references
// 2. Resolving project configuration based on the sln
// 3. Handling unresolved project references with ShouldUnsetParentConfigurationAndPlatform=true
// 4. Handling unresolved project references with ShouldUnsetParentConfigurationAndPlatform=false
using (var env = TestEnvironment.Create())
{
const string SolutionFileContents = """
@ -766,21 +768,37 @@ namespace Microsoft.Build.Graph.UnitTests
project1Xml.AddItem("ProjectReference", "Project2.vcxproj");
ProjectRootElement project2Xml = ProjectRootElement.Create();
// Project 2 depends on Project 4, which is not in the solution and uses ShouldUnsetParentConfigurationAndPlatform=true (the default)
project2Xml.AddItem("ProjectReference", "Project4.vcxproj");
project2Xml.AddProperty("ShouldUnsetParentConfigurationAndPlatform", "true");
ProjectRootElement project3Xml = ProjectRootElement.Create();
// Project 3 depends on Project 5, which is not in the solution and uses ShouldUnsetParentConfigurationAndPlatform=false
project3Xml.AddItem("ProjectReference", "Project5.vcxproj");
project3Xml.AddProperty("ShouldUnsetParentConfigurationAndPlatform", "false");
ProjectRootElement project4Xml = ProjectRootElement.Create();
ProjectRootElement project5Xml = ProjectRootElement.Create();
string project1Path = Path.Combine(env.DefaultTestDirectory.Path, "Project1.csproj");
string project2Path = Path.Combine(env.DefaultTestDirectory.Path, "Project2.vcxproj");
string project3Path = Path.Combine(env.DefaultTestDirectory.Path, "Project3.vcxproj");
string project4Path = Path.Combine(env.DefaultTestDirectory.Path, "Project4.vcxproj");
string project5Path = Path.Combine(env.DefaultTestDirectory.Path, "Project5.vcxproj");
project1Xml.Save(project1Path);
project2Xml.Save(project2Path);
project3Xml.Save(project3Path);
project4Xml.Save(project4Path);
project5Xml.Save(project5Path);
var projectGraph = new ProjectGraph(slnFile.Path);
projectGraph.EntryPointNodes.Count.ShouldBe(3);
projectGraph.GraphRoots.Count.ShouldBe(1);
projectGraph.GraphRoots.First().ProjectInstance.FullPath.ShouldBe(project1Path);
projectGraph.ProjectNodes.Count.ShouldBe(3);
projectGraph.ProjectNodes.Count.ShouldBe(5);
ProjectGraphNode project1Node = projectGraph.ProjectNodes.Single(node => node.ProjectInstance.FullPath == project1Path);
project1Node.ProjectInstance.GlobalProperties["Configuration"].ShouldBe("Debug");
@ -790,12 +808,24 @@ namespace Microsoft.Build.Graph.UnitTests
ProjectGraphNode project2Node = projectGraph.ProjectNodes.Single(node => node.ProjectInstance.FullPath == project2Path);
project2Node.ProjectInstance.GlobalProperties["Configuration"].ShouldBe("Debug");
project2Node.ProjectInstance.GlobalProperties["Platform"].ShouldBe("Win32");
project2Node.ProjectReferences.Count.ShouldBe(0);
project2Node.ProjectReferences.Count.ShouldBe(1);
ProjectGraphNode project3Node = projectGraph.ProjectNodes.Single(node => node.ProjectInstance.FullPath == project3Path);
project3Node.ProjectInstance.GlobalProperties["Configuration"].ShouldBe("Debug");
project3Node.ProjectInstance.GlobalProperties["Platform"].ShouldBe("Win32");
project3Node.ProjectReferences.Count.ShouldBe(0);
project3Node.ProjectReferences.Count.ShouldBe(1);
// Configuration and Platform get unset
ProjectGraphNode project4Node = projectGraph.ProjectNodes.Single(node => node.ProjectInstance.FullPath == project4Path);
project4Node.ProjectInstance.GlobalProperties.ContainsKey("Configuration").ShouldBeFalse();
project4Node.ProjectInstance.GlobalProperties.ContainsKey("Platform").ShouldBeFalse();
project4Node.ProjectReferences.Count.ShouldBe(0);
// Configuration and Platform are inherited from the referencing project
ProjectGraphNode project5Node = projectGraph.ProjectNodes.Single(node => node.ProjectInstance.FullPath == project5Path);
project5Node.ProjectInstance.GlobalProperties["Configuration"].ShouldBe("Debug");
project5Node.ProjectInstance.GlobalProperties["Platform"].ShouldBe("Win32");
project5Node.ProjectReferences.Count.ShouldBe(0);
}
}
@ -2633,6 +2663,71 @@ $@"
targetLists[project2].ShouldBe(new[] { "SomeDefaultTarget2", "SomeOtherTarget" });
}
[Fact]
public void MultitargettingTargetsWithBuildProjectReferencesFalse()
{
// This test should emulate Microsoft.Managed.After.targets's handling of multitargetting projects.
ProjectGraph graph = Helpers.CreateProjectGraph(
env: _env,
dependencyEdges: new Dictionary<int, int[]>()
{
{ 1, new[] { 2 } },
},
globalProperties: new Dictionary<string, string> { { "BuildProjectReferences", "false" } },
extraContentForAllNodes: """
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net6.0;net7.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFrameworks)' != '' and '$(TargetFramework)' == ''">
<IsCrossTargetingBuild>true</IsCrossTargetingBuild>
</PropertyGroup>
<PropertyGroup>
<InnerBuildProperty>TargetFramework</InnerBuildProperty>
<InnerBuildPropertyValues>TargetFrameworks</InnerBuildPropertyValues>
</PropertyGroup>
<PropertyGroup Condition="'$(IsGraphBuild)' == 'true' and '$(IsCrossTargetingBuild)' != 'true'">
<_MainReferenceTargetForBuild Condition="'$(BuildProjectReferences)' == '' or '$(BuildProjectReferences)' == 'true'">.projectReferenceTargetsOrDefaultTargets</_MainReferenceTargetForBuild>
<_MainReferenceTargetForBuild Condition="'$(_MainReferenceTargetForBuild)' == ''">GetTargetPath</_MainReferenceTargetForBuild>
<ProjectReferenceTargetsForBuild>$(_MainReferenceTargetForBuild);GetNativeManifest;$(_RecursiveTargetForContentCopying);$(ProjectReferenceTargetsForBuild)</ProjectReferenceTargetsForBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(IsGraphBuild)' == 'true' and '$(IsCrossTargetingBuild)' == 'true'">
<ProjectReferenceTargetsForBuild>.default;$(ProjectReferenceTargetsForBuild)</ProjectReferenceTargetsForBuild>
</PropertyGroup>
<ItemGroup Condition="'$(IsGraphBuild)' == 'true'">
<ProjectReferenceTargets Include="Build" Targets="GetTargetFrameworks" OuterBuild="true" SkipNonexistentTargets="true" Condition="'$(IsCrossTargetingBuild)' != 'true'" />
<ProjectReferenceTargets Include="Build" Targets="$(ProjectReferenceTargetsForBuild)" Condition=" '$(ProjectReferenceTargetsForBuild)' != '' " />
<ProjectReferenceTargets Include="Build" Targets="GetTargetFrameworksWithPlatformForSingleTargetFramework" SkipNonexistentTargets="true" Condition="'$(IsCrossTargetingBuild)' != 'true'" />
</ItemGroup>
<Target Name="Build" />
<Target Name="GetTargetPath" />
<Target Name="GetNativeManifest" />
<Target Name="GetTargetFrameworks" />
<Target Name="GetTargetFrameworksWithPlatformForSingleTargetFramework" />
""");
IReadOnlyDictionary<ProjectGraphNode, ImmutableList<string>> targetLists = graph.GetTargetLists(Array.Empty<string>());
targetLists[GetOuterBuild(graph, 1)].ShouldBe(new[] { "Build" });
foreach (ProjectGraphNode inner in GetInnerBuilds(graph, 1))
{
targetLists[inner].ShouldBe(new[] { "Build" });
}
targetLists[GetOuterBuild(graph, 2)].ShouldBe(new[] { "GetTargetFrameworks" });
foreach (ProjectGraphNode inner in GetInnerBuilds(graph, 2))
{
// GetTargetFrameworks actually shouldn't be here...
targetLists[inner].ShouldBe(new[] { "GetTargetFrameworks", "GetTargetPath", "GetNativeManifest", "GetTargetFrameworksWithPlatformForSingleTargetFramework" });
}
}
public void Dispose()
{
_env.Dispose();

Просмотреть файл

@ -15,6 +15,8 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System.IO.Compression" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />
<PackageReference Include="System.Configuration.ConfigurationManager" />
<PackageReference Include="Shouldly" />
<PackageReference Include="System.Net.Http" />

Просмотреть файл

@ -898,10 +898,10 @@ namespace Microsoft.Build.UnitTests
}
/// <summary>
/// Synthesizes Link metadata if the items are defined in an import and are on the whitelist
/// Synthesizes Link metadata if the items are defined in an import and are on the allowlist
/// </summary>
[Fact]
public void SynthesizeLinkMetadataForItemsOnWhitelist()
public void SynthesizeLinkMetadataForItemsOnAllowlist()
{
string outputPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
string directoryToDelete = null;

Просмотреть файл

@ -0,0 +1,48 @@
// 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 Microsoft.Build.Framework;
namespace Microsoft.Build.Engine.UnitTests;
/// <summary>
/// Task that emulates .NET 3.5 tasks.
/// </summary>
public sealed class TaskThatReturnsMinimalItem : ITask
{
public IBuildEngine? BuildEngine { get; set; }
public ITaskHost? HostObject { get; set; }
[Output]
public ITaskItem MinimalTaskItemOutput { get => new MinimalTaskItem(); }
public bool Execute() => true;
/// <summary>
/// Minimal implementation of <see cref="ITaskItem"/> that uses a <see cref="Hashtable"/> for metadata,
/// like MSBuild 3 did.
/// </summary>
internal sealed class MinimalTaskItem : ITaskItem
{
public string ItemSpec { get => $"{nameof(MinimalTaskItem)}spec"; set => throw new NotImplementedException(); }
public ICollection MetadataNames => throw new NotImplementedException();
public int MetadataCount => throw new NotImplementedException();
public IDictionary CloneCustomMetadata()
{
Hashtable t = new();
t["key"] = "value";
return t;
}
public void CopyMetadataTo(ITaskItem destinationItem) => throw new NotImplementedException();
public string GetMetadata(string metadataName) => "value";
public void RemoveMetadata(string metadataName) => throw new NotImplementedException();
public void SetMetadata(string metadataName, string metadataValue) => throw new NotImplementedException();
}
}

Просмотреть файл

@ -205,6 +205,8 @@ namespace Microsoft.Build.Execution
/// </summary>
private bool _logInitialPropertiesAndItems;
private bool _question;
/// <summary>
/// The settings used to load the project under build
/// </summary>
@ -303,6 +305,7 @@ namespace Microsoft.Build.Execution
_outputResultsCacheFile = other._outputResultsCacheFile;
DiscardBuildResults = other.DiscardBuildResults;
LowPriority = other.LowPriority;
Question = other.Question;
ProjectCacheDescriptor = other.ProjectCacheDescriptor;
}
@ -808,6 +811,15 @@ namespace Microsoft.Build.Execution
/// </summary>
public bool LowPriority { get; set; }
/// <summary>
/// Gets or sets a value that will error when the build process fails an incremental check.
/// </summary>
public bool Question
{
get => _question;
set => _question = value;
}
/// <summary>
/// Gets or sets the project cache description to use for all <see cref="BuildSubmission"/> or <see cref="GraphBuildSubmission"/>
/// in addition to any potential project caches described in each project.
@ -871,6 +883,7 @@ namespace Microsoft.Build.Execution
translator.Translate(ref _logInitialPropertiesAndItems);
translator.TranslateEnum(ref _projectLoadSettings, (int)_projectLoadSettings);
translator.Translate(ref _interactive);
translator.Translate(ref _question);
translator.TranslateEnum(ref _projectIsolationMode, (int)_projectIsolationMode);
// ProjectRootElementCache is not transmitted.

Просмотреть файл

@ -229,11 +229,7 @@ namespace Microsoft.Build.Experimental
CommunicationsUtilities.Trace("Build finished.");
}
if (NativeMethodsShared.IsWindows && _originalConsoleMode is not null)
{
IntPtr stdOut = NativeMethodsShared.GetStdHandle(NativeMethodsShared.STD_OUTPUT_HANDLE);
NativeMethodsShared.SetConsoleMode(stdOut, _originalConsoleMode.Value);
}
NativeMethodsShared.RestoreConsoleMode(_originalConsoleMode);
return _exitResult;
}
@ -265,9 +261,10 @@ namespace Microsoft.Build.Experimental
return true;
}
// Check that server is not busy.
bool serverWasBusy = ServerWasBusy();
if (serverWasBusy)
// Check and wait for server to be not busy for some short time to avoid race condition when server reports build is finished but had not released ServerBusy mutex yet.
// If during that short time, a script would try to shutdown server, it would be rejected and server would continue to run.
bool serverIsBusy = ServerIsBusyWithWaitAndRetry(250);
if (serverIsBusy)
{
CommunicationsUtilities.Trace("Server cannot be shut down for it is not idle.");
return false;
@ -291,6 +288,20 @@ namespace Microsoft.Build.Experimental
return _exitResult.MSBuildClientExitType == MSBuildClientExitType.Success;
}
private bool ServerIsBusyWithWaitAndRetry(int milliseconds)
{
bool isBusy = ServerWasBusy();
Stopwatch sw = Stopwatch.StartNew();
while (isBusy && sw.ElapsedMilliseconds < milliseconds)
{
CommunicationsUtilities.Trace("Wait for server to be not busy - will retry soon...");
Thread.Sleep(100);
isBusy = ServerWasBusy();
}
return isBusy;
}
internal bool ServerIsRunning()
{
string serverRunningMutexName = OutOfProcServerNode.GetRunningServerMutexName(_handshake);
@ -362,63 +373,13 @@ namespace Microsoft.Build.Experimental
private void ConfigureAndQueryConsoleProperties()
{
var (acceptAnsiColorCodes, outputIsScreen) = QueryIsScreenAndTryEnableAnsiColorCodes();
(var acceptAnsiColorCodes, var outputIsScreen, _originalConsoleMode) = NativeMethodsShared.QueryIsScreenAndTryEnableAnsiColorCodes();
int bufferWidth = QueryConsoleBufferWidth();
ConsoleColor backgroundColor = QueryConsoleBackgroundColor();
_consoleConfiguration = new TargetConsoleConfiguration(bufferWidth, acceptAnsiColorCodes, outputIsScreen, backgroundColor);
}
private (bool acceptAnsiColorCodes, bool outputIsScreen) QueryIsScreenAndTryEnableAnsiColorCodes()
{
bool acceptAnsiColorCodes = false;
bool outputIsScreen = false;
if (NativeMethodsShared.IsWindows)
{
try
{
IntPtr stdOut = NativeMethodsShared.GetStdHandle(NativeMethodsShared.STD_OUTPUT_HANDLE);
if (NativeMethodsShared.GetConsoleMode(stdOut, out uint consoleMode))
{
bool success;
if ((consoleMode & NativeMethodsShared.ENABLE_VIRTUAL_TERMINAL_PROCESSING) == NativeMethodsShared.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
{
// Console is already in required state
success = true;
}
else
{
_originalConsoleMode = consoleMode;
consoleMode |= NativeMethodsShared.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
success = NativeMethodsShared.SetConsoleMode(stdOut, consoleMode);
}
if (success)
{
acceptAnsiColorCodes = true;
}
uint fileType = NativeMethodsShared.GetFileType(stdOut);
// The std out is a char type(LPT or Console)
outputIsScreen = fileType == NativeMethodsShared.FILE_TYPE_CHAR;
acceptAnsiColorCodes &= outputIsScreen;
}
}
catch (Exception ex)
{
CommunicationsUtilities.Trace("MSBuild client warning: problem during enabling support for VT100: {0}.", ex);
}
}
else
{
// On posix OSes we expect console always supports VT100 coloring unless it is redirected
acceptAnsiColorCodes = outputIsScreen = !Console.IsOutputRedirected;
}
return (acceptAnsiColorCodes: acceptAnsiColorCodes, outputIsScreen: outputIsScreen);
}
private int QueryConsoleBufferWidth()
{
int consoleBufferWidth = -1;

Просмотреть файл

@ -128,6 +128,36 @@ namespace Microsoft.Build.BackEnd.Logging
_loggingService.LogComment(_eventContext, importance, messageResourceName, messageArgs);
}
/// <summary>
/// Helper method to create a message build event from a string resource and some parameters
/// </summary>
/// <param name="importance">Importance level of the message</param>
/// <param name="file">The file in which the event occurred</param>
/// <param name="messageResourceName">string within the resource which indicates the format string to use</param>
/// <param name="messageArgs">string resource arguments</param>
internal void LogComment(MessageImportance importance, BuildEventFileInfo file, string messageResourceName, params object[] messageArgs)
{
ErrorUtilities.VerifyThrow(_isValid, "must be valid");
_loggingService.LogBuildEvent(new BuildMessageEventArgs(
null,
null,
file.File,
file.Line,
file.Column,
file.EndLine,
file.EndColumn,
ResourceUtilities.GetResourceString(messageResourceName),
helpKeyword: null,
senderName: "MSBuild",
importance,
DateTime.UtcNow,
messageArgs)
{
BuildEventContext = _eventContext
});
}
/// <summary>
/// Helper method to create a message build event from a string
/// </summary>

Просмотреть файл

@ -1637,7 +1637,7 @@ namespace Microsoft.Build.BackEnd.Logging
/// </remarks>
private void UpdateMinimumMessageImportance(ILogger logger)
{
var innerLogger = (logger is Evaluation.ProjectCollection.ReusableLogger reusableLogger) ? reusableLogger.OriginalLogger : logger;
var innerLogger = (logger is ProjectCollection.ReusableLogger reusableLogger) ? reusableLogger.OriginalLogger : logger;
MessageImportance? minimumImportance = innerLogger switch
{
@ -1651,8 +1651,11 @@ namespace Microsoft.Build.BackEnd.Logging
// The null logger has no effect on minimum verbosity.
Execution.BuildManager.NullLogger => null,
// If the logger is not on our whitelist, there are no importance guarantees. Fall back to "any importance".
_ => MessageImportance.Low
// The live logger consumes only high priority messages.
_ => innerLogger.GetType().FullName == "Microsoft.Build.Logging.LiveLogger.LiveLogger"
? MessageImportance.High
// If the logger is not on our allow list, there are no importance guarantees. Fall back to "any importance".
: MessageImportance.Low,
};
if (minimumImportance != null)

Просмотреть файл

@ -184,7 +184,17 @@ namespace Microsoft.Build.BackEnd
if (condition)
{
string evaluatedValue = bucket.Expander.ExpandIntoStringLeaveEscaped(metadataInstance.Value, ExpanderOptions.ExpandAll, metadataInstance.Location, loggingContext);
ExpanderOptions expanderOptions = ExpanderOptions.ExpandAll;
if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_6) &&
// 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)
{
expanderOptions |= ExpanderOptions.LogOnItemMetadataSelfReference;
}
string evaluatedValue = bucket.Expander.ExpandIntoStringLeaveEscaped(metadataInstance.Value, expanderOptions, metadataInstance.Location, loggingContext);
// This both stores the metadata so we can add it to all the items we just created later, and
// exposes this metadata to further metadata evaluations in subsequent loop iterations.
@ -612,7 +622,7 @@ namespace Microsoft.Build.BackEnd
/// 1. The metadata table created for the bucket, may be null.
/// 2. The metadata table derived from the item definition group, may be null.
/// </summary>
private class NestedMetadataTable : IMetadataTable
private class NestedMetadataTable : IMetadataTable, IItemTypeDefinition
{
/// <summary>
/// The table for all metadata added during expansion
@ -722,6 +732,8 @@ namespace Microsoft.Build.BackEnd
{
_addTable[name] = value;
}
string IItemTypeDefinition.ItemType => _itemType;
}
}
}

Просмотреть файл

@ -462,7 +462,7 @@ namespace Microsoft.Build.BackEnd
// UNDONE: (Refactor) Refactor TargetUpToDateChecker to take a logging context, not a logging service.
MSBuildEventSource.Log.TargetUpToDateStart();
TargetUpToDateChecker dependencyAnalyzer = new TargetUpToDateChecker(requestEntry.RequestConfiguration.Project, _target, targetLoggingContext.LoggingService, targetLoggingContext.BuildEventContext);
DependencyAnalysisResult dependencyResult = dependencyAnalyzer.PerformDependencyAnalysis(bucket, out changedTargetInputs, out upToDateTargetInputs);
DependencyAnalysisResult dependencyResult = dependencyAnalyzer.PerformDependencyAnalysis(bucket, _host.BuildParameters.Question, out changedTargetInputs, out upToDateTargetInputs);
MSBuildEventSource.Log.TargetUpToDateStop((int)dependencyResult);
switch (dependencyResult)
@ -471,6 +471,13 @@ namespace Microsoft.Build.BackEnd
case DependencyAnalysisResult.FullBuild:
case DependencyAnalysisResult.IncrementalBuild:
case DependencyAnalysisResult.SkipUpToDate:
if (dependencyResult != DependencyAnalysisResult.SkipUpToDate && _host.BuildParameters.Question && !string.IsNullOrEmpty(_target.Inputs) && !string.IsNullOrEmpty(_target.Outputs))
{
targetSuccess = false;
aggregateResult = aggregateResult.AggregateResult(new WorkUnitResult(WorkUnitResultCode.Canceled, WorkUnitActionCode.Stop, null));
break;
}
// Create the lookups used to hold the current set of properties and items
lookupForInference = bucket.Lookup;
lookupForExecution = bucket.Lookup.Clone();

Просмотреть файл

@ -118,6 +118,7 @@ namespace Microsoft.Build.BackEnd
/// incremental build is needed.
/// </remarks>
/// <param name="bucket"></param>
/// <param name="question"></param>
/// <param name="changedTargetInputs"></param>
/// <param name="upToDateTargetInputs"></param>
/// <returns>
@ -129,6 +130,7 @@ namespace Microsoft.Build.BackEnd
/// </returns>
internal DependencyAnalysisResult PerformDependencyAnalysis(
ItemBucket bucket,
bool question,
out ItemDictionary<ProjectItemInstance> changedTargetInputs,
out ItemDictionary<ProjectItemInstance> upToDateTargetInputs)
{
@ -252,7 +254,7 @@ namespace Microsoft.Build.BackEnd
}
}
LogReasonForBuildingTarget(result);
LogReasonForBuildingTarget(result, question);
return result;
}
@ -261,15 +263,23 @@ namespace Microsoft.Build.BackEnd
/// Does appropriate logging to indicate why this target is being built fully or partially.
/// </summary>
/// <param name="result"></param>
private void LogReasonForBuildingTarget(DependencyAnalysisResult result)
/// <param name="question"></param>
private void LogReasonForBuildingTarget(DependencyAnalysisResult result, bool question)
{
// Only if we are not logging just critical events should we be logging the details
if (!_loggingService.OnlyLogCriticalEvents)
{
if (result == DependencyAnalysisResult.FullBuild && _dependencyAnalysisDetail.Count > 0)
{
// For the full build decision the are three possible outcomes
_loggingService.LogComment(_buildEventContext, MessageImportance.Low, "BuildTargetCompletely", _targetToAnalyze.Name);
if (question)
{
_loggingService.LogError(_buildEventContext, new BuildEventFileInfo(String.Empty), "BuildTargetCompletely", _targetToAnalyze.Name);
}
else
{
// For the full build decision, there are three possible outcomes
_loggingService.LogComment(_buildEventContext, MessageImportance.Low, "BuildTargetCompletely", _targetToAnalyze.Name);
}
foreach (DependencyAnalysisLogDetail logDetail in _dependencyAnalysisDetail)
{
@ -279,8 +289,15 @@ namespace Microsoft.Build.BackEnd
}
else if (result == DependencyAnalysisResult.IncrementalBuild)
{
// For the partial build decision the are three possible outcomes
_loggingService.LogComment(_buildEventContext, MessageImportance.Normal, "BuildTargetPartially", _targetToAnalyze.Name);
if (question)
{
_loggingService.LogError(_buildEventContext, new BuildEventFileInfo(String.Empty), "BuildTargetPartially", _targetToAnalyze.Name);
}
else
{
// For the partial build decision the are three possible outcomes
_loggingService.LogComment(_buildEventContext, MessageImportance.Normal, "BuildTargetPartially", _targetToAnalyze.Name);
}
foreach (DependencyAnalysisLogDetail logDetail in _dependencyAnalysisDetail)
{
string reason = GetIncrementalBuildReason(logDetail);

Просмотреть файл

@ -369,6 +369,11 @@ namespace Microsoft.Build.BackEnd
}
}
if (this.TaskInstance is IIncrementalTask incrementalTask)
{
incrementalTask.FailIfNotIncremental = _buildComponentHost.BuildParameters.Question;
}
if (taskInitialized)
{
// See if any required properties were not set

Просмотреть файл

@ -82,7 +82,7 @@ namespace Microsoft.Build.Collections
/// <summary>
/// Get the current element, converted
/// </summary>
public TTo2 Current
public readonly TTo2 Current
{
get
{

Просмотреть файл

@ -2,6 +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.Diagnostics;
using Microsoft.Build.Shared;
@ -15,7 +16,7 @@ namespace Microsoft.Build.Collections
/// </summary>
/// <typeparam name="K">Type of key</typeparam>
/// <typeparam name="V">Type of value, without the WeakReference wrapper.</typeparam>
internal class WeakValueDictionary<K, V>
internal class WeakValueDictionary<K, V> : IEnumerable<KeyValuePair<K, V>>
where V : class
{
/// <summary>
@ -233,5 +234,22 @@ namespace Microsoft.Build.Collections
{
_dictionary.Clear();
}
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
{
foreach (KeyValuePair<K, WeakReference<V>> kvp in _dictionary)
{
if (kvp.Value is null)
{
yield return new KeyValuePair<K, V>(kvp.Key, null);
}
else if (kvp.Value.TryGetTarget(out V target))
{
yield return new KeyValuePair<K, V>(kvp.Key, target);
}
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

Просмотреть файл

@ -742,7 +742,7 @@ namespace Microsoft.Build.Construction
/// <summary>
/// Get enumerator
/// </summary>
public IEnumerator<ProjectElement> GetEnumerator()
public readonly IEnumerator<ProjectElement> GetEnumerator()
{
return _enumerator;
}
@ -808,7 +808,7 @@ namespace Microsoft.Build.Construction
/// <summary>
/// Dispose. Do nothing.
/// </summary>
public void Dispose()
public readonly void Dispose()
{
}

Просмотреть файл

@ -100,13 +100,13 @@ namespace Microsoft.Build.Construction
/// Creates an unparented ProjectMetadataElement, wrapping an unparented XmlElement.
/// Caller should then ensure the element is added to a parent.
/// </summary>
internal static ProjectMetadataElement CreateDisconnected(string name, ProjectRootElement containingProject)
internal static ProjectMetadataElement CreateDisconnected(string name, ProjectRootElement containingProject, ElementLocation location = null)
{
XmlUtilities.VerifyThrowArgumentValidElementName(name);
ErrorUtilities.VerifyThrowArgument(!FileUtilities.ItemSpecModifiers.IsItemSpecModifier(name), "ItemSpecModifierCannotBeCustomMetadata", name);
ErrorUtilities.VerifyThrowInvalidOperation(!XMakeElements.ReservedItemNames.Contains(name), "CannotModifyReservedItemMetadata", name);
XmlElementWithLocation element = containingProject.CreateElement(name);
XmlElementWithLocation element = containingProject.CreateElement(name, location);
return new ProjectMetadataElement(element, containingProject);
}

Просмотреть файл

@ -1327,13 +1327,22 @@ namespace Microsoft.Build.Construction
/// Caller must add it to the location of choice in the project.
/// </summary>
public ProjectMetadataElement CreateMetadataElement(string name, string unevaluatedValue)
{
return this.CreateMetadataElement(name, unevaluatedValue, null);
}
/// <summary>
/// Creates a metadata node.
/// Caller must add it to the location of choice in the project.
/// </summary>
public ProjectMetadataElement CreateMetadataElement(string name, string unevaluatedValue, ElementLocation location)
{
if (Link != null)
{
return RootLink.CreateMetadataElement(name, unevaluatedValue);
}
ProjectMetadataElement metadatum = ProjectMetadataElement.CreateDisconnected(name, this);
ProjectMetadataElement metadatum = ProjectMetadataElement.CreateDisconnected(name, this, location);
metadatum.Value = unevaluatedValue;
@ -1785,14 +1794,23 @@ namespace Microsoft.Build.Construction
return projectRootElement;
}
/// <summary>
/// Creates a metadata node.
/// Caller must add it to the location of choice in the project.
/// </summary>
internal ProjectMetadataElement CreateMetadataElement(XmlAttributeWithLocation attribute)
{
return CreateMetadataElement(attribute.Name, attribute.Value, attribute.Location);
}
/// <summary>
/// Creates a XmlElement with the specified name in the document
/// containing this project.
/// </summary>
internal XmlElementWithLocation CreateElement(string name)
internal XmlElementWithLocation CreateElement(string name, ElementLocation location = null)
{
ErrorUtilities.VerifyThrow(Link == null, "External project");
return (XmlElementWithLocation)XmlDocument.CreateElement(name, XmlNamespace);
return (XmlElementWithLocation)XmlDocument.CreateElement(name, XmlNamespace, location);
}
/// <summary>

Просмотреть файл

@ -1809,18 +1809,6 @@ namespace Microsoft.Build.Evaluation
ErrorUtilities.VerifyThrowInvalidOperation(ReferenceEquals(Xml, otherXml), "OM_CannotModifyEvaluatedObjectInImportedFile", otherXml.Location.File);
}
/// <summary>
/// Returns <see cref="IDirectoryCache"/> as provided by the <see cref="IDirectoryCacheFactory"/> passed when creating the
/// project, specific for a given evaluation ID.
/// </summary>
/// <param name="evaluationId">The evaluation ID for which the cache is requested.</param>
/// <returns>An <see cref="IDirectoryCache"/> implementation, or null if this project has no <see cref="IDirectoryCacheFactory"/>
/// associated with it or it returned null.</returns>
internal IDirectoryCache GetDirectoryCacheForEvaluation(int evaluationId)
{
return _directoryCacheFactory?.GetDirectoryCacheForEvaluation(evaluationId);
}
/// <summary>
/// Internal project evaluation implementation.
/// </summary>
@ -2558,7 +2546,7 @@ namespace Microsoft.Build.Evaluation
};
}
public void AccumulateInformationFromRemoveItemSpec(EvaluationItemSpec removeSpec)
public readonly void AccumulateInformationFromRemoveItemSpec(EvaluationItemSpec removeSpec)
{
IEnumerable<string> removeSpecFragmentStrings = removeSpec.FlattenFragmentsAsStrings();
var removeGlob = removeSpec.ToMSBuildGlob();
@ -3737,6 +3725,7 @@ namespace Microsoft.Build.Evaluation
loggingServiceForEvaluation,
new ProjectItemFactory(Owner),
ProjectCollection,
Owner._directoryCacheFactory,
ProjectCollection.ProjectRootElementCache,
s_buildEventContext,
evaluationContext.SdkResolverService,

Просмотреть файл

@ -87,8 +87,8 @@ namespace Microsoft.Build.Evaluation
/// <remarks>
/// ProjectCollection is highly reentrant - project creation, toolset and logger changes, and so on
/// all need lock protection, but there are a lot of read cases as well, and calls to create Projects
/// call back to the ProjectCollection under locks. Use a RW lock, but default to always using
/// upgradable read locks to avoid adding reentrancy bugs.
/// call back to the ProjectCollection under locks. Use a RW lock with recursion support to avoid
/// adding reentrancy bugs.
/// </remarks>
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
@ -508,7 +508,7 @@ namespace Microsoft.Build.Evaluation
{
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
ErrorUtilities.VerifyThrow(_defaultToolsVersion != null, "Should have a default");
return _defaultToolsVersion;
@ -558,7 +558,7 @@ namespace Microsoft.Build.Evaluation
{
Dictionary<string, string> dictionary;
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
if (_globalProperties.Count == 0)
{
@ -591,7 +591,7 @@ namespace Microsoft.Build.Evaluation
{
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _loadedProjects.Count;
}
@ -609,7 +609,7 @@ namespace Microsoft.Build.Evaluation
[DebuggerStepThrough]
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _loggingService.Loggers == null
? (ICollection<ILogger>)ReadOnlyEmptyCollection<ILogger>.Instance
@ -628,7 +628,7 @@ namespace Microsoft.Build.Evaluation
{
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return new List<Toolset>(_toolsets.Values);
}
@ -650,7 +650,7 @@ namespace Microsoft.Build.Evaluation
[DebuggerStepThrough]
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _isBuildEnabled;
}
@ -683,7 +683,7 @@ namespace Microsoft.Build.Evaluation
{
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _onlyLogCriticalEvents;
}
@ -720,17 +720,20 @@ namespace Microsoft.Build.Evaluation
get
{
// Avoid write lock if possible, this getter is called a lot during Project construction.
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
if (_hostServices != null)
{
return _hostServices;
}
using (_locker.EnterDisposableWriteLock())
}
using (_locker.EnterDisposableWriteLock())
{
if (_hostServices == null)
{
return _hostServices ?? (_hostServices = new HostServices());
_hostServices = new HostServices();
}
return _hostServices;
}
}
@ -763,7 +766,7 @@ namespace Microsoft.Build.Evaluation
{
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _skipEvaluation;
}
@ -799,7 +802,7 @@ namespace Microsoft.Build.Evaluation
{
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _disableMarkDirty;
}
@ -849,7 +852,7 @@ namespace Microsoft.Build.Evaluation
[DebuggerStepThrough]
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _loggingService;
}
@ -867,7 +870,7 @@ namespace Microsoft.Build.Evaluation
{
var clone = new PropertyDictionary<ProjectPropertyInstance>();
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
foreach (ProjectPropertyInstance property in _globalProperties)
{
@ -886,23 +889,24 @@ namespace Microsoft.Build.Evaluation
{
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
// Retrieves the environment properties.
// This is only done once, when the project collection is created. Any subsequent
// environment changes will be ignored. Child nodes will be passed this set
// of properties in their build parameters.
using (_locker.EnterDisposableReadLock())
{
if (_environmentProperties != null)
{
return new PropertyDictionary<ProjectPropertyInstance>(_environmentProperties);
}
}
using (_locker.EnterDisposableWriteLock())
{
// Retrieves the environment properties.
// This is only done once, when the project collection is created. Any subsequent
// environment changes will be ignored. Child nodes will be passed this set
// of properties in their build parameters.
if (_environmentProperties == null)
{
using (_locker.EnterDisposableWriteLock())
{
if (_environmentProperties == null)
{
_environmentProperties = Utilities.GetEnvironmentProperties();
}
}
_environmentProperties = Utilities.GetEnvironmentProperties();
}
return new PropertyDictionary<ProjectPropertyInstance>(_environmentProperties);
}
}
@ -917,7 +921,7 @@ namespace Microsoft.Build.Evaluation
[DebuggerStepThrough]
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _toolsetsVersion;
}
@ -931,7 +935,7 @@ namespace Microsoft.Build.Evaluation
{
get
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _maxNodeCount;
}
@ -1419,7 +1423,7 @@ namespace Microsoft.Build.Evaluation
/// </summary>
public ProjectPropertyInstance GetGlobalProperty(string name)
{
using (_locker.EnterDisposableUpgradeableReadLock())
using (_locker.EnterDisposableReadLock())
{
return _globalProperties[name];
}

Просмотреть файл

@ -26,7 +26,7 @@ namespace Microsoft.Build.Evaluation
/// ProjectMetadataElement, and these can be added, removed, and modified.
/// </remarks>
[DebuggerDisplay("{_itemType} #Metadata={MetadataCount}")]
public class ProjectItemDefinition : IKeyed, IMetadataTable, IItemDefinition<ProjectMetadata>, IProjectMetadataParent
public class ProjectItemDefinition : IKeyed, IMetadataTable, IItemDefinition<ProjectMetadata>, IProjectMetadataParent, IItemTypeDefinition
{
/// <summary>
/// Project that this item definition lives in.

Просмотреть файл

@ -817,7 +817,7 @@ namespace Microsoft.Build.Evaluation
/// <summary>
/// Returns the corresponding property name - eg. "$(MSBuildExtensionsPath32)"
/// </summary>
public string MSBuildPropertyName => String.Format($"$({StringRepresentation})");
public readonly string MSBuildPropertyName => String.Format($"$({StringRepresentation})");
/// <summary>
/// Tries to find a reference to MSBuildExtensionsPath* property in the given string

Просмотреть файл

@ -3,6 +3,7 @@
using System;
using System.IO;
using System.Threading;
using System.Xml;
using Microsoft.Build.Internal;
using Microsoft.Build.Shared;
@ -59,6 +60,13 @@ namespace Microsoft.Build.Construction
/// </summary>
private bool? _loadAsReadOnly;
/// <summary>
/// Location of the element to be created via 'CreateElement' call. So that we can
/// receive and use location from the caller up the stack even if we are being called via
/// <see cref="XmlDocument"/> internal methods.
/// </summary>
private readonly AsyncLocal<ElementLocation> _elementLocation = new AsyncLocal<ElementLocation>();
/// <summary>
/// Constructor
/// </summary>
@ -180,6 +188,31 @@ namespace Microsoft.Build.Construction
}
}
/// <summary>
/// Called during parse, to add an element.
/// </summary>
/// <remarks>
/// We create our own kind of element, that we can give location information to.
/// In order to pass the location through the callchain, that contains XmlDocument function
/// that then calls back to our XmlDocumentWithLocation (so we cannot use call stack via passing via parameters),
/// we use async local field, that simulates variable on call stack.
/// </remarks>
internal XmlElement CreateElement(string localName, string namespaceURI, ElementLocation location)
{
if (location != null)
{
this._elementLocation.Value = location;
}
try
{
return CreateElement(localName, namespaceURI);
}
finally
{
this._elementLocation.Value = null;
}
}
/// <summary>
/// Called during load, to add an element.
/// </summary>
@ -192,6 +225,10 @@ namespace Microsoft.Build.Construction
{
return new XmlElementWithLocation(prefix, localName, namespaceURI, this, _reader.LineNumber, _reader.LinePosition);
}
else if (_elementLocation?.Value != null)
{
return new XmlElementWithLocation(prefix, localName, namespaceURI, this, _elementLocation.Value.Line, _elementLocation.Value.Column);
}
// Must be a subsequent edit; we can't provide location information
return new XmlElementWithLocation(prefix, localName, namespaceURI, this);

Просмотреть файл

@ -4,26 +4,20 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading;
#nullable disable
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Shared;
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
{
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.Shared;
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;
internal static class ConditionEvaluator
{
private static readonly Lazy<Regex> s_singlePropertyRegex = new Lazy<Regex>(
() => new Regex(@"^\$\(([^\$\(\)]*)\)$", RegexOptions.Compiled));
/// <summary>
/// Update our table which keeps track of all the properties that are referenced
/// inside of a condition and the string values that they are being tested against.
@ -61,9 +55,7 @@ namespace Microsoft.Build.Evaluation
var lastPiece = pieceSeparator < 0;
var pieceEnd = lastPiece ? leftValue.Length : pieceSeparator;
var singlePropertyMatch = s_singlePropertyRegex.Value.Match(leftValue, pieceStart, pieceEnd - pieceStart);
if (singlePropertyMatch.Success)
if (TryGetSingleProperty(leftValue.AsSpan(), pieceStart, pieceEnd - pieceStart, out ReadOnlySpan<char> propertyName))
{
// Find the first vertical bar on the right-hand-side expression.
var indexOfVerticalBar = rightValueExpanded.IndexOf('|');
@ -88,18 +80,13 @@ namespace Microsoft.Build.Evaluation
rightValueExpanded = rightValueExpanded.Substring(indexOfVerticalBar + 1);
}
// Capture the property name out of the regular expression.
var propertyName = singlePropertyMatch.Groups[1].ToString();
// Get the string collection for this property name, if one already exists.
List<string> conditionedPropertyValues;
// If this property is not already represented in the table, add a new entry
// for it.
if (!conditionedPropertiesTable.TryGetValue(propertyName, out conditionedPropertyValues))
// If not already in the table, add a new entry for it.
string propertyNameString = propertyName.ToString();
if (!conditionedPropertiesTable.TryGetValue(propertyNameString, out List<string>? conditionedPropertyValues))
{
conditionedPropertyValues = new List<string>();
conditionedPropertiesTable[propertyName] = conditionedPropertyValues;
conditionedPropertiesTable[propertyNameString] = conditionedPropertyValues;
}
// If the "rightValueExpanded" is not already in the string collection
@ -120,6 +107,32 @@ namespace Microsoft.Build.Evaluation
}
}
// Internal for testing purposes
internal static bool TryGetSingleProperty(ReadOnlySpan<char> input, int beginning, int length, out ReadOnlySpan<char> propertyName)
{
// This code is simulating the regex pattern: ^\$\(([^\$\(\)]*)\)$
if (input.Length < beginning + 3 ||
input[beginning] != '$' ||
input[beginning + 1] != '(' ||
input[beginning + length - 1] != ')' ||
ContainsInvalidCharacter(input.Slice(beginning + 2, length - 3)))
{
propertyName = null;
return false;
}
propertyName = input.Slice(beginning + 2, length - 3);
return true;
static bool ContainsInvalidCharacter(ReadOnlySpan<char> span)
{
return
span.IndexOf('$') != -1 ||
span.IndexOf('(') != -1 ||
span.IndexOf(')') != -1;
}
}
// Implements a pool of expression trees for each condition.
// This is because an expression tree is a mutually exclusive resource (has non thread safe state while it evaluates).
// During high demand when all expression trees are busy evaluating, a new expression tree is created and added to the pool.
@ -130,11 +143,11 @@ namespace Microsoft.Build.Evaluation
private readonly ConcurrentDictionary<string, ConcurrentStack<GenericExpressionNode>> _conditionPools;
private int _mOptimisticSize;
public int OptimisticSize => _mOptimisticSize;
public readonly int OptimisticSize => _mOptimisticSize;
public ExpressionTreeForCurrentOptionsWithSize(ConcurrentDictionary<string, ConcurrentStack<GenericExpressionNode>> conditionPools)
{
this._conditionPools = conditionPools;
_conditionPools = conditionPools;
_mOptimisticSize = conditionPools.Count;
}
@ -176,8 +189,8 @@ namespace Microsoft.Build.Evaluation
ILoggingService loggingServices,
BuildEventContext buildEventContext,
IFileSystem fileSystem,
ProjectRootElementCacheBase projectRootElementCache = null,
LoggingContext loggingContext = null)
ProjectRootElementCacheBase? projectRootElementCache = null,
LoggingContext? loggingContext = null)
where P : class, IProperty
where I : class, IItem
{
@ -186,7 +199,7 @@ namespace Microsoft.Build.Evaluation
options,
expander,
expanderOptions,
null /* do not collect conditioned properties */,
conditionedPropertiesTable: null /* do not collect conditioned properties */,
evaluationDirectory,
elementLocation,
loggingServices,
@ -208,14 +221,14 @@ namespace Microsoft.Build.Evaluation
ParserOptions options,
Expander<P, I> expander,
ExpanderOptions expanderOptions,
Dictionary<string, List<string>> conditionedPropertiesTable,
Dictionary<string, List<string>>? conditionedPropertiesTable,
string evaluationDirectory,
ElementLocation elementLocation,
ILoggingService loggingServices,
BuildEventContext buildEventContext,
IFileSystem fileSystem,
ProjectRootElementCacheBase projectRootElementCache = null,
LoggingContext loggingContext = null)
ProjectRootElementCacheBase? projectRootElementCache = null,
LoggingContext? loggingContext = null)
where P : class, IProperty
where I : class, IItem
{
@ -343,13 +356,13 @@ namespace Microsoft.Build.Evaluation
/// If this is null, as it is for command line builds, conditioned properties
/// are not recorded.
/// </summary>
Dictionary<string, List<string>> ConditionedPropertiesInProject { get; }
Dictionary<string, List<string>>? ConditionedPropertiesInProject { get; }
/// <summary>
/// May return null if the expression would expand to non-empty and it broke out early.
/// Otherwise, returns the correctly expanded expression.
/// </summary>
string ExpandIntoStringBreakEarly(string expression, LoggingContext loggingContext = null);
string ExpandIntoStringBreakEarly(string expression, LoggingContext? loggingContext = null);
/// <summary>
/// Expands the specified expression into a list of TaskItem's.
@ -359,12 +372,12 @@ namespace Microsoft.Build.Evaluation
/// <summary>
/// Expands the specified expression into a string.
/// </summary>
string ExpandIntoString(string expression, LoggingContext loggingContext = null);
string ExpandIntoString(string expression, LoggingContext? loggingContext = null);
/// <summary>
/// PRE cache
/// </summary>
ProjectRootElementCacheBase LoadedProjectsCache { get; }
ProjectRootElementCacheBase? LoadedProjectsCache { get; }
IFileSystem FileSystem { get; }
}
@ -398,22 +411,22 @@ namespace Microsoft.Build.Evaluation
/// If this is null, as it is for command line builds, conditioned properties
/// are not recorded.
/// </summary>
public Dictionary<string, List<string>> ConditionedPropertiesInProject { get; }
public Dictionary<string, List<string>>? ConditionedPropertiesInProject { get; }
/// <summary>
/// PRE collection.
/// </summary>
public ProjectRootElementCacheBase LoadedProjectsCache { get; }
public ProjectRootElementCacheBase? LoadedProjectsCache { get; }
internal ConditionEvaluationState(
string condition,
Expander<P, I> expander,
ExpanderOptions expanderOptions,
Dictionary<string, List<string>> conditionedPropertiesInProject,
Dictionary<string, List<string>>? conditionedPropertiesInProject,
string evaluationDirectory,
ElementLocation elementLocation,
IFileSystem fileSystem,
ProjectRootElementCacheBase projectRootElementCache = null)
ProjectRootElementCacheBase? projectRootElementCache = null)
{
ErrorUtilities.VerifyThrowArgumentNull(condition, nameof(condition));
ErrorUtilities.VerifyThrowArgumentNull(expander, nameof(expander));
@ -434,7 +447,7 @@ namespace Microsoft.Build.Evaluation
/// May return null if the expression would expand to non-empty and it broke out early.
/// Otherwise, returns the correctly expanded expression.
/// </summary>
public string ExpandIntoStringBreakEarly(string expression, LoggingContext loggingContext = null)
public string ExpandIntoStringBreakEarly(string expression, LoggingContext? loggingContext = null)
{
var originalValue = _expander.WarnForUninitializedProperties;
@ -467,7 +480,7 @@ namespace Microsoft.Build.Evaluation
/// <param name="expression">The expression to expand.</param>
/// <param name="loggingContext"></param>
/// <returns>The expanded string.</returns>
public string ExpandIntoString(string expression, LoggingContext loggingContext = null)
public string ExpandIntoString(string expression, LoggingContext? loggingContext = null)
{
var originalValue = _expander.WarnForUninitializedProperties;

Просмотреть файл

@ -202,6 +202,7 @@ namespace Microsoft.Build.Evaluation
PropertyDictionary<ProjectPropertyInstance> environmentProperties,
IItemFactory<I, I> itemFactory,
IToolsetProvider toolsetProvider,
IDirectoryCacheFactory directoryCacheFactory,
ProjectRootElementCacheBase projectRootElementCache,
ISdkResolverService sdkResolverService,
int submissionId,
@ -231,7 +232,7 @@ namespace Microsoft.Build.Evaluation
// If the host wishes to provide a directory cache for this evaluation, create a new EvaluationContext with the right file system.
_evaluationContext = evaluationContext;
IDirectoryCache directoryCache = project?.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId);
IDirectoryCache directoryCache = directoryCacheFactory?.GetDirectoryCacheForEvaluation(_evaluationLoggingContext.BuildEventContext.EvaluationId);
if (directoryCache is not null)
{
IFileSystem fileSystem = new DirectoryCacheFileSystemWrapper(evaluationContext.FileSystem, directoryCache);
@ -308,6 +309,7 @@ namespace Microsoft.Build.Evaluation
ILoggingService loggingService,
IItemFactory<I, I> itemFactory,
IToolsetProvider toolsetProvider,
IDirectoryCacheFactory directoryCacheFactory,
ProjectRootElementCacheBase projectRootElementCache,
BuildEventContext buildEventContext,
ISdkResolverService sdkResolverService,
@ -326,6 +328,7 @@ namespace Microsoft.Build.Evaluation
environmentProperties,
itemFactory,
toolsetProvider,
directoryCacheFactory,
projectRootElementCache,
sdkResolverService,
submissionId,
@ -1313,12 +1316,12 @@ namespace Microsoft.Build.Evaluation
{
// Is the property we are currently setting in the list of properties which have been used but not initialized
IElementLocation elementWhichUsedProperty;
bool isPropertyInList = _expander.UsedUninitializedProperties.Properties.TryGetValue(propertyElement.Name, out elementWhichUsedProperty);
bool isPropertyInList = _expander.UsedUninitializedProperties.TryGetPropertyElementLocation(propertyElement.Name, out elementWhichUsedProperty);
if (isPropertyInList)
{
// Once we are going to warn for a property once, remove it from the list so we do not add it again.
_expander.UsedUninitializedProperties.Properties.Remove(propertyElement.Name);
_expander.UsedUninitializedProperties.RemoveProperty(propertyElement.Name);
_evaluationLoggingContext.LogWarning(null, new BuildEventFileInfo(propertyElement.Location), "UsedUninitializedProperty", propertyElement.Name, elementWhichUsedProperty.LocationString);
}
}

Просмотреть файл

@ -8,8 +8,6 @@ using Microsoft.Build.Collections;
using Microsoft.Build.Construction;
using EscapingUtilities = Microsoft.Build.Shared.EscapingUtilities;
#nullable disable
namespace Microsoft.Build.Evaluation
{
/// <summary>
@ -22,19 +20,25 @@ namespace Microsoft.Build.Evaluation
/// <summary>
/// The actual metadata dictionary.
/// </summary>
private Dictionary<string, EvaluatorMetadata> _metadata;
private Dictionary<string, EvaluatorMetadata>? _metadata;
/// <summary>
/// The type of item the metadata should be considered to apply to.
/// </summary>
private string _implicitItemType;
/// <summary>
/// The expected number of metadata entries in this table.
/// </summary>
private readonly int _capacity;
/// <summary>
/// Creates a new table using the specified item type.
/// </summary>
public EvaluatorMetadataTable(string implicitItemType)
public EvaluatorMetadataTable(string implicitItemType, int capacity = 0)
{
_implicitItemType = implicitItemType;
_capacity = capacity;
}
/// <summary>
@ -56,7 +60,7 @@ namespace Microsoft.Build.Evaluation
/// Retrieves any value we have in our metadata table for the metadata name and item type specified.
/// If no value is available, returns empty string.
/// </summary>
public string GetEscapedValue(string itemType, string name)
public string GetEscapedValue(string? itemType, string name)
{
return GetEscapedValueIfPresent(itemType, name) ?? String.Empty;
}
@ -65,21 +69,18 @@ namespace Microsoft.Build.Evaluation
/// Retrieves any value we have in our metadata table for the metadata name and item type specified.
/// If no value is available, returns null.
/// </summary>
public string GetEscapedValueIfPresent(string itemType, string name)
public string? GetEscapedValueIfPresent(string? itemType, string name)
{
if (_metadata == null)
{
return null;
}
string value = null;
string? value = null;
if (itemType == null || String.Equals(_implicitItemType, itemType, StringComparison.OrdinalIgnoreCase))
{
EvaluatorMetadata metadatum;
_metadata.TryGetValue(name, out metadatum);
if (metadatum != null)
if (_metadata.TryGetValue(name, out EvaluatorMetadata? metadatum))
{
value = metadatum.EvaluatedValueEscaped;
}
@ -95,7 +96,7 @@ namespace Microsoft.Build.Evaluation
{
if (_metadata == null)
{
_metadata = new Dictionary<string, EvaluatorMetadata>(MSBuildNameIgnoreCaseComparer.Default);
_metadata = new Dictionary<string, EvaluatorMetadata>(_capacity, MSBuildNameIgnoreCaseComparer.Default);
}
_metadata[xml.Name] = new EvaluatorMetadata(xml, evaluatedValueEscaped);

Просмотреть файл

@ -88,6 +88,13 @@ namespace Microsoft.Build.Evaluation
/// </summary>
Truncate = 0x40,
/// <summary>
/// Issues build message if item references unqualified or qualified metadata odf self - as this can lead to unintended expansion and
/// cross-combination of other items.
/// More info: https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-batching#item-batching-on-self-referencing-metadata
/// </summary>
LogOnItemMetadataSelfReference = 0x80,
/// <summary>
/// Expand only properties and then item lists
/// </summary>
@ -203,7 +210,7 @@ namespace Microsoft.Build.Evaluation
/// concatenation of the string representation of the values, each additionally subjected
/// to file path adjustment.
/// </returns>
public object GetResult()
public readonly object GetResult()
{
CheckDisposed();
if (_firstObject != null)
@ -228,7 +235,7 @@ namespace Microsoft.Build.Evaluation
/// <summary>
/// Throws <see cref="ObjectDisposedException"/> if this concatenator is already disposed.
/// </summary>
private void CheckDisposed() =>
private readonly void CheckDisposed() =>
ErrorUtilities.VerifyThrowObjectDisposed(!_disposed, nameof(SpanBasedConcatenator));
/// <summary>
@ -441,7 +448,7 @@ namespace Microsoft.Build.Evaluation
ErrorUtilities.VerifyThrowInternalNull(elementLocation, nameof(elementLocation));
string result = MetadataExpander.ExpandMetadataLeaveEscaped(expression, _metadata, options, elementLocation);
string result = MetadataExpander.ExpandMetadataLeaveEscaped(expression, _metadata, options, elementLocation, loggingContext);
result = PropertyExpander<P>.ExpandPropertiesLeaveEscaped(result, _properties, options, elementLocation, _usedUninitializedProperties, _fileSystem, loggingContext);
result = ItemExpander.ExpandItemVectorsIntoString<I>(this, result, _items, options, elementLocation);
result = FileUtilities.MaybeAdjustFilePath(result);
@ -871,8 +878,9 @@ namespace Microsoft.Build.Evaluation
/// <param name="metadata">The metadata to be expanded.</param>
/// <param name="options">Used to specify what to expand.</param>
/// <param name="elementLocation">The location information for error reporting purposes.</param>
/// <param name="loggingContext">The logging context for this operation.</param>
/// <returns>The string with item metadata expanded in-place, escaped.</returns>
internal static string ExpandMetadataLeaveEscaped(string expression, IMetadataTable metadata, ExpanderOptions options, IElementLocation elementLocation)
internal static string ExpandMetadataLeaveEscaped(string expression, IMetadataTable metadata, ExpanderOptions options, IElementLocation elementLocation, LoggingContext loggingContext = null)
{
try
{
@ -896,7 +904,7 @@ namespace Microsoft.Build.Evaluation
{
// if there are no item vectors in the string
// run a simpler Regex to find item metadata references
MetadataMatchEvaluator matchEvaluator = new MetadataMatchEvaluator(metadata, options);
MetadataMatchEvaluator matchEvaluator = new MetadataMatchEvaluator(metadata, options, elementLocation, loggingContext);
result = RegularExpressions.ItemMetadataPattern.Value.Replace(expression, new MatchEvaluator(matchEvaluator.ExpandSingleMetadata));
}
else
@ -915,7 +923,7 @@ namespace Microsoft.Build.Evaluation
using SpanBasedStringBuilder finalResultBuilder = Strings.GetSpanBasedStringBuilder();
int start = 0;
MetadataMatchEvaluator matchEvaluator = new MetadataMatchEvaluator(metadata, options);
MetadataMatchEvaluator matchEvaluator = new MetadataMatchEvaluator(metadata, options, elementLocation, loggingContext);
if (itemVectorExpressions != null)
{
@ -993,13 +1001,23 @@ namespace Microsoft.Build.Evaluation
/// </summary>
private ExpanderOptions _options;
private IElementLocation _elementLocation;
private LoggingContext _loggingContext;
/// <summary>
/// Constructor taking a source of metadata.
/// </summary>
internal MetadataMatchEvaluator(IMetadataTable metadata, ExpanderOptions options)
internal MetadataMatchEvaluator(
IMetadataTable metadata,
ExpanderOptions options,
IElementLocation elementLocation,
LoggingContext loggingContext)
{
_metadata = metadata;
_options = options & (ExpanderOptions.ExpandMetadata | ExpanderOptions.Truncate);
_options = options & (ExpanderOptions.ExpandMetadata | ExpanderOptions.Truncate | ExpanderOptions.LogOnItemMetadataSelfReference);
_elementLocation = elementLocation;
_loggingContext = loggingContext;
ErrorUtilities.VerifyThrow(options != ExpanderOptions.Invalid, "Must be expanding metadata of some kind");
}
@ -1030,6 +1048,17 @@ namespace Microsoft.Build.Evaluation
(!isBuiltInMetadata && ((_options & ExpanderOptions.ExpandCustomMetadata) != 0)))
{
metadataValue = _metadata.GetEscapedValue(itemType, metadataName);
if ((_options & ExpanderOptions.LogOnItemMetadataSelfReference) != 0 &&
_loggingContext != null &&
!string.IsNullOrEmpty(metadataName) &&
_metadata is IItemTypeDefinition itemMetadata &&
(string.IsNullOrEmpty(itemType) || string.Equals(itemType, itemMetadata.ItemType, StringComparison.Ordinal)))
{
_loggingContext.LogComment(MessageImportance.High, new BuildEventFileInfo(_elementLocation),
"ItemReferencingSelfInTarget", itemMetadata.ItemType, metadataName);
}
if (IsTruncationEnabled(_options) && metadataValue.Length > CharacterLimitPerExpansion)
{
metadataValue = metadataValue.Substring(0, CharacterLimitPerExpansion - 3) + "...";
@ -1492,18 +1521,16 @@ namespace Microsoft.Build.Evaluation
// We also do not want to add the property to the list if the environment variable is not set, also we do not want to add the property to the list if we are currently
// evaluating a condition because a common pattern for msbuild projects is to see if the property evaluates to empty and then set a value as this would cause a considerable number of false positives. <A Condition="'$(A)' == ''">default</A>
//
// Another pattern used is where a property concatonates with other values, <a>$(a);something</a> however we do not want to add the a element to the list because again this would make a number of
// Another pattern used is where a property concatenates with other values, <a>$(a);something</a> however we do not want to add the a element to the list because again this would make a number of
// false positives. Therefore we check to see what element we are currently evaluating and if it is the same as our property we do not add the property to the list.
if (usedUninitializedProperties.Warn && usedUninitializedProperties.CurrentlyEvaluatingPropertyElementName != null)
{
// Check to see if the property name does not match the property we are currently evaluating, note the property we are currently evaluating in the element name, this means no $( or )
if (!MSBuildNameIgnoreCaseComparer.Default.Equals(usedUninitializedProperties.CurrentlyEvaluatingPropertyElementName, propertyName, startIndex, endIndex - startIndex + 1))
{
string propertyTrimed = propertyName.Substring(startIndex, endIndex - startIndex + 1);
if (!usedUninitializedProperties.Properties.ContainsKey(propertyTrimed))
{
usedUninitializedProperties.Properties.Add(propertyTrimed, elementLocation);
}
usedUninitializedProperties.TryAdd(
propertyName: propertyName.Substring(startIndex, endIndex - startIndex + 1),
elementLocation);
}
}
@ -3113,7 +3140,7 @@ namespace Microsoft.Build.Evaluation
/// </summary>
public UsedUninitializedProperties UsedUninitializedProperties { get; set; }
internal Function<T> Build()
internal readonly Function<T> Build()
{
return new Function<T>(
ReceiverType,
@ -3887,6 +3914,14 @@ namespace Microsoft.Build.Evaluation
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.Unescape), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArg(args, out string arg0))
{
returnVal = IntrinsicFunctions.Unescape(arg0);
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.GetPathOfFileAbove), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArgs(args, out string arg0, out string arg1))
@ -3899,7 +3934,7 @@ namespace Microsoft.Build.Evaluation
{
if (TryGetArgs(args, out double arg0, out double arg1))
{
returnVal = arg0 + arg1;
returnVal = IntrinsicFunctions.Add(arg0, arg1);
return true;
}
}
@ -3907,7 +3942,7 @@ namespace Microsoft.Build.Evaluation
{
if (TryGetArgs(args, out double arg0, out double arg1))
{
returnVal = arg0 - arg1;
returnVal = IntrinsicFunctions.Subtract(arg0, arg1);
return true;
}
}
@ -3915,7 +3950,7 @@ namespace Microsoft.Build.Evaluation
{
if (TryGetArgs(args, out double arg0, out double arg1))
{
returnVal = arg0 * arg1;
returnVal = IntrinsicFunctions.Multiply(arg0, arg1);
return true;
}
}
@ -3923,7 +3958,15 @@ namespace Microsoft.Build.Evaluation
{
if (TryGetArgs(args, out double arg0, out double arg1))
{
returnVal = arg0 / arg1;
returnVal = IntrinsicFunctions.Divide(arg0, arg1);
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.Modulo), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArgs(args, out double arg0, out double arg1))
{
returnVal = IntrinsicFunctions.Modulo(arg0, arg1);
return true;
}
}
@ -4113,6 +4156,62 @@ namespace Microsoft.Build.Evaluation
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.BitwiseOr), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArgs(args, out int arg0, out int arg1))
{
returnVal = IntrinsicFunctions.BitwiseOr(arg0, arg1);
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.BitwiseAnd), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArgs(args, out int arg0, out int arg1))
{
returnVal = IntrinsicFunctions.BitwiseAnd(arg0, arg1);
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.BitwiseXor), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArgs(args, out int arg0, out int arg1))
{
returnVal = IntrinsicFunctions.BitwiseXor(arg0, arg1);
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.BitwiseNot), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArgs(args, out int arg0))
{
returnVal = IntrinsicFunctions.BitwiseNot(arg0);
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.LeftShift), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArgs(args, out int arg0, out int arg1))
{
returnVal = IntrinsicFunctions.LeftShift(arg0, arg1);
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.RightShift), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArgs(args, out int arg0, out int arg1))
{
returnVal = IntrinsicFunctions.RightShift(arg0, arg1);
return true;
}
}
else if (string.Equals(_methodMethodName, nameof(IntrinsicFunctions.RightShiftUnsigned), StringComparison.OrdinalIgnoreCase))
{
if (TryGetArgs(args, out int arg0, out int arg1))
{
returnVal = IntrinsicFunctions.RightShiftUnsigned(arg0, arg1);
return true;
}
}
}
else if (_receiverType == typeof(Path))
{
@ -4489,6 +4588,18 @@ namespace Microsoft.Build.Evaluation
return Enum.TryParse(comparisonTypeName, out arg1);
}
private static bool TryGetArgs(object[] args, out int arg0)
{
arg0 = 0;
if (args.Length != 1)
{
return false;
}
return TryConvertToInt(args[0], out arg0);
}
private static bool TryGetArgs(object[] args, out int arg0, out int arg1)
{
arg0 = 0;
@ -4594,7 +4705,7 @@ namespace Microsoft.Build.Evaluation
return null;
}
// Check if the type is in the whitelist cache. If it is, use it or load it.
// Check if the type is in the allowlist cache. If it is, use it or load it.
cachedTypeInformation = AvailableStaticMethods.GetTypeInformationFromTypeCache(typeName, simpleMethodName);
if (cachedTypeInformation != null)
{
@ -4617,7 +4728,7 @@ namespace Microsoft.Build.Evaluation
// If the type information from the cache is not loadable, it means the cache information got corrupted somehow
// Throw here to prevent adding null types in the cache
ErrorUtilities.VerifyThrowInternalNull(receiverType, $"Type information for {typeName} was present in the whitelist cache as {assemblyQualifiedTypeName} but the type could not be loaded.");
ErrorUtilities.VerifyThrowInternalNull(receiverType, $"Type information for {typeName} was present in the allowlist cache as {assemblyQualifiedTypeName} but the type could not be loaded.");
// If we've used it once, chances are that we'll be using it again
// We can record the type here since we know it's available for calling from the fact that is was in the AvailableStaticMethods table
@ -4635,7 +4746,7 @@ namespace Microsoft.Build.Evaluation
// DO NOT CACHE THE TYPE HERE!
// We don't add the resolved type here in the AvailableStaticMethods table. This is because that table is used
// during function parse, but only later during execution do we check for the ability to call specific methods on specific types.
// Caching it here would load any type into the white list.
// Caching it here would load any type into the allow list.
return receiverType;
}
@ -5038,7 +5149,7 @@ namespace Microsoft.Build.Evaluation
}
/// <summary>
/// Check the property function whitelist whether this method is available.
/// Check the property function allowlist whether this method is available.
/// </summary>
private static bool IsStaticMethodAvailable(Type receiverType, string methodName)
{
@ -5166,26 +5277,45 @@ namespace Microsoft.Build.Evaluation
}
}
#nullable enable
/// <summary>
/// This class wraps information about properties which have been used before they are initialized.
/// </summary>
internal class UsedUninitializedProperties
internal sealed class UsedUninitializedProperties
{
/// <summary>
/// This class wraps information about properties which have been used before they are initialized.
/// Lazily allocated collection of properties and the element which used them.
/// </summary>
internal UsedUninitializedProperties()
private Dictionary<string, IElementLocation>? _properties;
internal void TryAdd(string propertyName, IElementLocation elementLocation)
{
Properties = new Dictionary<string, IElementLocation>(StringComparer.OrdinalIgnoreCase);
if (_properties is null)
{
_properties = new(StringComparer.OrdinalIgnoreCase);
}
else if (_properties.ContainsKey(propertyName))
{
return;
}
_properties.Add(propertyName, elementLocation);
}
/// <summary>
/// Hash set of properties which have been used before being initialized.
/// </summary>
internal IDictionary<string, IElementLocation> Properties
internal bool TryGetPropertyElementLocation(string propertyName, [NotNullWhen(returnValue: true)] out IElementLocation? elementLocation)
{
get;
set;
if (_properties is null)
{
elementLocation = null;
return false;
}
return _properties.TryGetValue(propertyName, out elementLocation);
}
internal void RemoveProperty(string propertyName)
{
_properties?.Remove(propertyName);
}
/// <summary>
@ -5200,7 +5330,7 @@ namespace Microsoft.Build.Evaluation
/// <summary>
/// What is the currently evaluating property element, this is so that we do not add a un initialized property if we are evaluating that property.
/// </summary>
internal string CurrentlyEvaluatingPropertyElementName
internal string? CurrentlyEvaluatingPropertyElementName
{
get;
set;

Просмотреть файл

@ -39,7 +39,7 @@ namespace Microsoft.Build.Evaluation
/// <summary>
/// Returns the metadata with the specified key.
/// Returns null if it does not exist.
/// Returns an empty string if it does not exist.
/// Attempting to get built-in metadata on a value that is not a valid path throws InvalidOperationException.
/// Metadata value is unescaped.
/// </summary>
@ -47,7 +47,7 @@ namespace Microsoft.Build.Evaluation
/// <summary>
/// Returns the metadata with the specified key.
/// Returns null if it does not exist.
/// Returns an empty string if it does not exist.
/// Attempting to get built-in metadata on a value that is not a valid path throws InvalidOperationException.
/// Metadata value is the escaped value initially set.
/// </summary>

Просмотреть файл

@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Microsoft.Build.Evaluation;
internal interface IItemTypeDefinition
{
/// <summary>
/// The item type to which this metadata applies.
/// </summary>
string ItemType { get; }
}

Просмотреть файл

@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
namespace Microsoft.Build.Evaluation
{
/// <summary>
@ -21,12 +19,12 @@ namespace Microsoft.Build.Evaluation
/// If item type is null, it is ignored.
/// If no value is available, returns empty string.
/// </summary>
string GetEscapedValue(string itemType, string name);
string GetEscapedValue(string? itemType, string name);
/// <summary>
/// Returns the value if it exists, null otherwise.
/// If item type is null, it is ignored.
/// </summary>
string GetEscapedValueIfPresent(string itemType, string name);
string? GetEscapedValueIfPresent(string? itemType, string name);
}
}

Просмотреть файл

@ -165,6 +165,21 @@ namespace Microsoft.Build.Evaluation
return ~first;
}
internal static int LeftShift(int operand, int count)
{
return operand << count;
}
internal static int RightShift(int operand, int count)
{
return operand >> count;
}
internal static int RightShiftUnsigned(int operand, int count)
{
return operand >>> count;
}
/// <summary>
/// Get the value of the registry key and value, default value is null
/// </summary>

Просмотреть файл

@ -80,7 +80,7 @@ namespace Microsoft.Build.Evaluation
public override int MatchCount(string itemToMatch)
{
return ReferencedItems.Count(v => v.ItemAsValueFragment.MatchCount(itemToMatch) > 0);
return ReferencedItems.Count(v => v.ItemAsValueFragment.IsMatch(itemToMatch));
}
public override bool IsMatch(string itemToMatch)

Просмотреть файл

@ -40,7 +40,7 @@ namespace Microsoft.Build.Evaluation
/// </summary>
internal HashSet<string> Items
{
get
readonly get
{
return _items;
}
@ -58,7 +58,7 @@ namespace Microsoft.Build.Evaluation
/// </summary>
internal Dictionary<string, MetadataReference> Metadata
{
get
readonly get
{
return _metadata;
}

Просмотреть файл

@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.Build.BackEnd;
using Microsoft.Build.BackEnd.Logging;
using Microsoft.Build.BackEnd.SdkResolution;
@ -19,13 +21,13 @@ namespace Microsoft.Build.Evaluation
{
private class EvaluatorData : IEvaluatorData<P, I, M, D>
{
private IEvaluatorData<P, I, M, D> _wrappedData;
private Func<string, ICollection<I>> _itemGetter;
private readonly IEvaluatorData<P, I, M, D> _wrappedData;
private readonly IReadOnlyDictionary<string, LazyItemList> _itemsByType;
public EvaluatorData(IEvaluatorData<P, I, M, D> wrappedData, Func<string, ICollection<I>> itemGetter)
public EvaluatorData(IEvaluatorData<P, I, M, D> wrappedData, IReadOnlyDictionary<string, LazyItemList> itemsByType)
{
_wrappedData = wrappedData;
_itemGetter = itemGetter;
_itemsByType = itemsByType;
}
public ItemDictionary<I> Items
@ -46,10 +48,11 @@ namespace Microsoft.Build.Evaluation
public ICollection<I> GetItems(string itemType)
{
return _itemGetter(itemType);
return _itemsByType.TryGetValue(itemType, out LazyItemList items)
? items.GetMatchedItems(globsToIgnore: ImmutableHashSet<string>.Empty)
: Array.Empty<I>();
}
public IDictionary<string, List<TargetSpecification>> AfterTargets
{
get

Просмотреть файл

@ -12,8 +12,6 @@ using Microsoft.Build.Internal;
using Microsoft.Build.Shared;
using Microsoft.CodeAnalysis.Collections;
#nullable disable
namespace Microsoft.Build.Evaluation
{
internal partial class LazyItemEvaluator<P, I, M, D>
@ -21,9 +19,9 @@ namespace Microsoft.Build.Evaluation
private class IncludeOperation : LazyItemOperation
{
private readonly int _elementOrder;
private readonly string _rootDirectory;
private readonly string? _rootDirectory;
private readonly ImmutableSegmentedList<string> _excludes;
private readonly ImmutableList<ProjectMetadataElement> _metadata;
private readonly ImmutableArray<ProjectMetadataElement> _metadata;
public IncludeOperation(IncludeOperationBuilder builder, LazyItemEvaluator<P, I, M, D> lazyEvaluator)
: base(builder, lazyEvaluator)
@ -35,11 +33,11 @@ namespace Microsoft.Build.Evaluation
_metadata = builder.Metadata.ToImmutable();
}
protected override ImmutableList<I> SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet<string> globsToIgnore)
protected override ImmutableArray<I> SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet<string> globsToIgnore)
{
var itemsToAdd = ImmutableList.CreateBuilder<I>();
ImmutableArray<I>.Builder? itemsToAdd = null;
Lazy<Func<string, bool>> excludeTester = null;
Lazy<Func<string, bool>>? excludeTester = null;
ImmutableList<string>.Builder excludePatterns = ImmutableList.CreateBuilder<string>();
if (_excludes != null)
{
@ -57,7 +55,7 @@ namespace Microsoft.Build.Evaluation
}
}
ISet<string> excludePatternsForGlobs = null;
ISet<string>? excludePatternsForGlobs = null;
foreach (var fragment in _itemSpec.Fragments)
{
@ -73,6 +71,7 @@ namespace Microsoft.Build.Evaluation
isTransformExpression: out _,
elementLocation: _itemElement.IncludeLocation);
itemsToAdd ??= ImmutableArray.CreateBuilder<I>();
itemsToAdd.AddRange(
excludeTester != null
? itemsFromExpression.Where(item => !excludeTester.Value(item.EvaluatedInclude))
@ -84,8 +83,8 @@ namespace Microsoft.Build.Evaluation
if (excludeTester?.Value(EscapingUtilities.UnescapeAll(value)) != true)
{
var item = _itemFactory.CreateItem(value, value, _itemElement.ContainingProject.FullPath);
itemsToAdd.Add(item);
itemsToAdd ??= ImmutableArray.CreateBuilder<I>();
itemsToAdd.Add(_itemFactory.CreateItem(value, value, _itemElement.ContainingProject.FullPath));
}
}
else if (fragment is GlobFragment globFragment)
@ -127,6 +126,7 @@ namespace Microsoft.Build.Evaluation
foreach (string includeSplitFileEscaped in includeSplitFilesEscaped)
{
itemsToAdd ??= ImmutableArray.CreateBuilder<I>();
itemsToAdd.Add(_itemFactory.CreateItem(includeSplitFileEscaped, glob, _itemElement.ContainingProject.FullPath));
}
}
@ -137,7 +137,7 @@ namespace Microsoft.Build.Evaluation
}
}
return itemsToAdd.ToImmutable();
return itemsToAdd?.ToImmutable() ?? ImmutableArray<I>.Empty;
}
private static ISet<string> BuildExcludePatternsForGlobs(ImmutableHashSet<string> globsToIgnore, ImmutableList<string>.Builder excludePatterns)
@ -153,12 +153,12 @@ namespace Microsoft.Build.Evaluation
return anyExcludes ? excludePatterns.ToImmutableHashSet() : globsToIgnore;
}
protected override void MutateItems(ImmutableList<I> items)
protected override void MutateItems(ImmutableArray<I> items)
{
DecorateItemsWithMetadata(items.Select(i => new ItemBatchingContext(i)), _metadata);
}
protected override void SaveItems(ImmutableList<I> items, OrderedItemDataCollection.Builder listBuilder)
protected override void SaveItems(ImmutableArray<I> items, OrderedItemDataCollection.Builder listBuilder)
{
foreach (var item in items)
{
@ -170,7 +170,7 @@ namespace Microsoft.Build.Evaluation
private class IncludeOperationBuilder : OperationBuilderWithMetadata
{
public int ElementOrder { get; set; }
public string RootDirectory { get; set; }
public string? RootDirectory { get; set; }
public ImmutableSegmentedList<string>.Builder Excludes { get; } = ImmutableSegmentedList.CreateBuilder<string>();

Просмотреть файл

@ -43,7 +43,7 @@ namespace Microsoft.Build.Evaluation
_lazyEvaluator = lazyEvaluator;
_evaluatorData = new EvaluatorData(_lazyEvaluator._outerEvaluatorData, itemType => GetReferencedItems(itemType, ImmutableHashSet<string>.Empty));
_evaluatorData = new EvaluatorData(_lazyEvaluator._outerEvaluatorData, _referencedItemLists);
_itemFactory = new ItemFactoryWrapper(_itemElement, _lazyEvaluator._itemFactory);
_expander = new Expander<P, I>(_evaluatorData, _evaluatorData, _lazyEvaluator.EvaluationContext);
@ -72,28 +72,16 @@ namespace Microsoft.Build.Evaluation
/// <summary>
/// Produce the items to operate on. For example, create new ones or select existing ones
/// </summary>
protected virtual ImmutableList<I> SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet<string> globsToIgnore)
protected virtual ImmutableArray<I> SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet<string> globsToIgnore)
{
return listBuilder.Select(itemData => itemData.Item)
.ToImmutableList();
.ToImmutableArray();
}
// todo Refactoring: MutateItems should clone each item before mutation. See https://github.com/dotnet/msbuild/issues/2328
protected virtual void MutateItems(ImmutableList<I> items) { }
protected virtual void MutateItems(ImmutableArray<I> items) { }
protected virtual void SaveItems(ImmutableList<I> items, OrderedItemDataCollection.Builder listBuilder) { }
private IList<I> GetReferencedItems(string itemType, ImmutableHashSet<string> globsToIgnore)
{
if (_referencedItemLists.TryGetValue(itemType, out var itemList))
{
return itemList.GetMatchedItems(globsToIgnore);
}
else
{
return ImmutableList<I>.Empty;
}
}
protected virtual void SaveItems(ImmutableArray<I> items, OrderedItemDataCollection.Builder listBuilder) { }
[DebuggerDisplay(@"{DebugString()}")]
protected readonly struct ItemBatchingContext
@ -174,9 +162,9 @@ namespace Microsoft.Build.Evaluation
}
}
protected void DecorateItemsWithMetadata(IEnumerable<ItemBatchingContext> itemBatchingContexts, ImmutableList<ProjectMetadataElement> metadata, bool? needToExpandMetadata = null)
protected void DecorateItemsWithMetadata(IEnumerable<ItemBatchingContext> itemBatchingContexts, ImmutableArray<ProjectMetadataElement> metadata, bool? needToExpandMetadata = null)
{
if (metadata.Count > 0)
if (metadata.Length > 0)
{
////////////////////////////////////////////////////
// UNDONE: Implement batching here.
@ -238,11 +226,11 @@ namespace Microsoft.Build.Evaluation
{
// Metadata expressions are allowed here.
// Temporarily gather and expand these in a table so they can reference other metadata elements above.
EvaluatorMetadataTable metadataTable = new EvaluatorMetadataTable(_itemType);
EvaluatorMetadataTable metadataTable = new EvaluatorMetadataTable(_itemType, capacity: metadata.Length);
_expander.Metadata = metadataTable;
// Also keep a list of everything so we can get the predecessor objects correct.
List<Pair<ProjectMetadataElement, string>> metadataList = new List<Pair<ProjectMetadataElement, string>>(metadata.Count);
List<Pair<ProjectMetadataElement, string>> metadataList = new(metadata.Length);
foreach (var metadataElement in metadata)
{
@ -282,7 +270,7 @@ namespace Microsoft.Build.Evaluation
}
}
private static IEnumerable<string> GetMetadataValuesAndConditions(ImmutableList<ProjectMetadataElement> metadata)
private static IEnumerable<string> GetMetadataValuesAndConditions(ImmutableArray<ProjectMetadataElement> metadata)
{
foreach (var metadataElement in metadata)
{
@ -291,7 +279,7 @@ namespace Microsoft.Build.Evaluation
}
}
protected bool NeedToExpandMetadataForEachItem(ImmutableList<ProjectMetadataElement> metadata, out ItemsAndMetadataPair itemsAndMetadataFound)
protected bool NeedToExpandMetadataForEachItem(ImmutableArray<ProjectMetadataElement> metadata, out ItemsAndMetadataPair itemsAndMetadataFound)
{
itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(GetMetadataValuesAndConditions(metadata));

Просмотреть файл

@ -16,7 +16,7 @@ namespace Microsoft.Build.Evaluation
{
private class UpdateOperation : LazyItemOperation
{
private readonly ImmutableList<ProjectMetadataElement> _metadata;
private readonly ImmutableArray<ProjectMetadataElement> _metadata;
private ImmutableList<ItemBatchingContext>.Builder _itemsToUpdate = null;
private ItemSpecMatchesItem _matchItemSpec = null;
private bool? _needToExpandMetadataForEachItem = null;
@ -147,7 +147,7 @@ namespace Microsoft.Build.Evaluation
}
}
private bool QualifiedMetadataReferencesExist(ImmutableList<ProjectMetadataElement> metadata, out bool? needToExpandMetadataForEachItem)
private bool QualifiedMetadataReferencesExist(ImmutableArray<ProjectMetadataElement> metadata, out bool? needToExpandMetadataForEachItem)
{
needToExpandMetadataForEachItem = NeedToExpandMetadataForEachItem(metadata, out var itemsAndMetadataFound);

Просмотреть файл

@ -52,7 +52,7 @@ namespace Microsoft.Build.Evaluation
{
_outerEvaluatorData = data;
_outerExpander = new Expander<P, I>(_outerEvaluatorData, _outerEvaluatorData, evaluationContext);
_evaluatorData = new EvaluatorData(_outerEvaluatorData, itemType => GetItems(itemType));
_evaluatorData = new EvaluatorData(_outerEvaluatorData, _itemLists);
_expander = new Expander<P, I>(_evaluatorData, _evaluatorData, evaluationContext);
_itemFactory = itemFactory;
_loggingContext = loggingContext;
@ -61,13 +61,6 @@ namespace Microsoft.Build.Evaluation
EvaluationContext = evaluationContext;
}
private ImmutableList<I> GetItems(string itemType)
{
return _itemLists.TryGetValue(itemType, out LazyItemList itemList) ?
itemList.GetMatchedItems(ImmutableHashSet<string>.Empty) :
ImmutableList<I>.Empty;
}
public bool EvaluateConditionWithCurrentState(ProjectElement element, ExpanderOptions expanderOptions, ParserOptions parserOptions)
{
return EvaluateCondition(element.Condition, element, expanderOptions, parserOptions, _expander, this);
@ -135,7 +128,7 @@ namespace Microsoft.Build.Evaluation
_normalizedItemValue = normalizedItemValue;
}
public ItemData Clone(IItemFactory<I, I> itemFactory, ProjectItemElement initialItemElementForFactory)
public readonly ItemData Clone(IItemFactory<I, I> itemFactory, ProjectItemElement initialItemElementForFactory)
{
// setting the factory's item element to the original item element that produced the item
// otherwise you get weird things like items that appear to have been produced by update elements
@ -492,7 +485,7 @@ namespace Microsoft.Build.Evaluation
private class OperationBuilderWithMetadata : OperationBuilder
{
public ImmutableList<ProjectMetadataElement>.Builder Metadata = ImmutableList.CreateBuilder<ProjectMetadataElement>();
public readonly ImmutableArray<ProjectMetadataElement>.Builder Metadata = ImmutableArray.CreateBuilder<ProjectMetadataElement>();
public OperationBuilderWithMetadata(ProjectItemElement itemElement, bool conditionResult) : base(itemElement, conditionResult)
{

Просмотреть файл

@ -324,7 +324,7 @@ namespace Microsoft.Build.Construction
}
else if (isValidMetadataNameInAttribute)
{
ProjectMetadataElement metadatum = _project.CreateMetadataElement(attribute.Name, attribute.Value);
ProjectMetadataElement metadatum = _project.CreateMetadataElement(attribute);
metadatum.ExpressedAsAttribute = true;
metadatum.Parent = item;
@ -744,7 +744,7 @@ namespace Microsoft.Build.Construction
}
else if (isValidMetadataNameInAttribute)
{
ProjectMetadataElement metadatum = _project.CreateMetadataElement(attribute.Name, attribute.Value);
ProjectMetadataElement metadatum = _project.CreateMetadataElement(attribute);
metadatum.ExpressedAsAttribute = true;
metadatum.Parent = itemDefinition;

Просмотреть файл

@ -522,25 +522,27 @@ namespace Microsoft.Build.Evaluation
LinkedList<ProjectRootElement> oldStrongCache = _strongCache;
_strongCache = new LinkedList<ProjectRootElement>();
foreach (string projectPath in oldWeakCache.Keys)
foreach (KeyValuePair<string, ProjectRootElement> kvp in oldWeakCache)
{
ProjectRootElement rootElement;
if (oldWeakCache.TryGetValue(projectPath, out rootElement))
if (kvp.Value is null)
{
if (rootElement.IsExplicitlyLoaded)
{
_weakCache[projectPath] = rootElement;
}
continue;
}
if (rootElement.IsExplicitlyLoaded && oldStrongCache.Contains(rootElement))
if (kvp.Value.IsExplicitlyLoaded)
{
_weakCache[kvp.Key] = kvp.Value;
}
if (oldStrongCache.Contains(kvp.Value))
{
if (kvp.Value.IsExplicitlyLoaded)
{
_strongCache.AddFirst(rootElement);
_strongCache.AddFirst(kvp.Value);
}
else
{
_strongCache.Remove(rootElement);
RaiseProjectRootElementRemovedFromStrongCache(rootElement);
RaiseProjectRootElementRemovedFromStrongCache(kvp.Value);
}
}
}

Просмотреть файл

@ -5,6 +5,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
@ -280,8 +281,22 @@ namespace Microsoft.Build.Graph
SolutionConfigurationInSolution currentSolutionConfiguration = SelectSolutionConfiguration(solution, solutionEntryPoint.GlobalProperties);
// Mimic behavior of SolutionProjectGenerator
string solutionConfigurationXml = SolutionProjectGenerator.GetSolutionConfiguration(solution, currentSolutionConfiguration);
solutionGlobalPropertiesBuilder["CurrentSolutionConfigurationContents"] = solutionConfigurationXml;
solutionGlobalPropertiesBuilder["BuildingSolutionFile"] = "true";
string solutionDirectoryName = solution.SolutionFileDirectory;
if (!solutionDirectoryName.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
{
solutionDirectoryName += Path.DirectorySeparatorChar;
}
solutionGlobalPropertiesBuilder["SolutionDir"] = EscapingUtilities.Escape(solutionDirectoryName);
solutionGlobalPropertiesBuilder["SolutionExt"] = EscapingUtilities.Escape(Path.GetExtension(solution.FullPath));
solutionGlobalPropertiesBuilder["SolutionFileName"] = EscapingUtilities.Escape(Path.GetFileName(solution.FullPath));
solutionGlobalPropertiesBuilder["SolutionName"] = EscapingUtilities.Escape(Path.GetFileNameWithoutExtension(solution.FullPath));
solutionGlobalPropertiesBuilder[SolutionProjectGenerator.SolutionPathPropertyName] = EscapingUtilities.Escape(Path.Combine(solution.SolutionFileDirectory, Path.GetFileName(solution.FullPath)));
// Project configurations are reused heavily, so cache the global properties for each
Dictionary<string, ImmutableDictionary<string, string>> globalPropertiesForProjectConfiguration = new(StringComparer.OrdinalIgnoreCase);

Просмотреть файл

@ -766,7 +766,7 @@ namespace Microsoft.Build.Graph
public ImmutableList<string> RequestedTargets { get; }
public bool Equals(ProjectGraphBuildRequest other)
public readonly bool Equals(ProjectGraphBuildRequest other)
{
if (Node != other.Node
|| RequestedTargets.Count != other.RequestedTargets.Count)
@ -786,12 +786,12 @@ namespace Microsoft.Build.Graph
return true;
}
public override bool Equals(object obj)
public override readonly bool Equals(object obj)
{
return !(obj is null) && obj is ProjectGraphBuildRequest graphNodeWithTargets && Equals(graphNodeWithTargets);
}
public override int GetHashCode()
public override readonly int GetHashCode()
{
unchecked
{

Просмотреть файл

@ -61,7 +61,7 @@ namespace Microsoft.Build.Graph
}
}
internal IEnumerable<ProjectGraphEntryPoint> AsEnumerable()
internal readonly IEnumerable<ProjectGraphEntryPoint> AsEnumerable()
{
yield return this;
}

Просмотреть файл

@ -34,8 +34,9 @@ namespace Microsoft.Build.Graph
private const string PlatformLookupTableMetadataName = "PlatformLookupTable";
private const string PlatformMetadataName = "Platform";
private const string PlatformsMetadataName = "Platforms";
private const string EnableDynamicPlatformResolutionMetadataName = "EnableDynamicPlatformResolution";
private const string EnableDynamicPlatformResolutionPropertyName = "EnableDynamicPlatformResolution";
private const string OverridePlatformNegotiationValue = "OverridePlatformNegotiationValue";
private const string ShouldUnsetParentConfigurationAndPlatformPropertyName = "ShouldUnsetParentConfigurationAndPlatform";
private const string ProjectMetadataName = "Project";
private const string ConfigurationMetadataName = "Configuration";
@ -120,7 +121,7 @@ namespace Microsoft.Build.Graph
}
string projectReferenceFullPath = projectReferenceItem.GetMetadataValue(FullPathMetadataName);
bool enableDynamicPlatformResolution = ConversionUtilities.ValidBooleanTrue(requesterInstance.GetPropertyValue(EnableDynamicPlatformResolutionMetadataName));
bool enableDynamicPlatformResolution = ConversionUtilities.ValidBooleanTrue(requesterInstance.GetPropertyValue(EnableDynamicPlatformResolutionPropertyName));
PropertyDictionary<ProjectPropertyInstance> referenceGlobalProperties = GetGlobalPropertiesForItem(
projectReferenceItem,
@ -153,8 +154,13 @@ namespace Microsoft.Build.Graph
}
else
{
referenceGlobalProperties.Remove(ConfigurationMetadataName);
referenceGlobalProperties.Remove(PlatformMetadataName);
// Note: ShouldUnsetParentConfigurationAndPlatform defaults to true in the AssignProjectConfiguration target when building a solution, so check that it's not false instead of checking that it's true.
bool shouldUnsetParentConfigurationAndPlatform = !ConversionUtilities.ValidBooleanFalse(requesterInstance.GetPropertyValue(ShouldUnsetParentConfigurationAndPlatformPropertyName));
if (shouldUnsetParentConfigurationAndPlatform)
{
referenceGlobalProperties.Remove(ConfigurationMetadataName);
referenceGlobalProperties.Remove(PlatformMetadataName);
}
}
}

Просмотреть файл

@ -19,6 +19,7 @@ using Microsoft.Build.Construction;
using Microsoft.Build.Definition;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Evaluation.Context;
using Microsoft.Build.FileSystem;
using Microsoft.Build.Framework;
using Microsoft.Build.Internal;
using Microsoft.Build.Shared;
@ -241,7 +242,7 @@ namespace Microsoft.Build.Execution
/// <param name="projectCollection">Project collection</param>
/// <returns>A new project instance</returns>
public ProjectInstance(string projectFile, IDictionary<string, string> globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection)
: this(projectFile, globalProperties, toolsVersion, subToolsetVersion, projectCollection, projectLoadSettings: null, evaluationContext: null, interactive: false)
: this(projectFile, globalProperties, toolsVersion, subToolsetVersion, projectCollection, projectLoadSettings: null, evaluationContext: null, directoryCacheFactory: null, interactive: false)
{
}
@ -260,9 +261,11 @@ namespace Microsoft.Build.Execution
/// <param name="projectCollection">Project collection</param>
/// <param name="projectLoadSettings">Project load settings</param>
/// <param name="evaluationContext">The context to use for evaluation.</param>
/// <param name="directoryCacheFactory">The directory cache factory to use for file I/O.</param>
/// <param name="interactive">Indicates if loading the project is allowed to interact with the user.</param>
/// <returns>A new project instance</returns>
private ProjectInstance(string projectFile, IDictionary<string, string> globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings? projectLoadSettings, EvaluationContext evaluationContext, bool interactive)
private ProjectInstance(string projectFile, IDictionary<string, string> globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection,
ProjectLoadSettings? projectLoadSettings, EvaluationContext evaluationContext, IDirectoryCacheFactory directoryCacheFactory, bool interactive)
{
ErrorUtilities.VerifyThrowArgumentLength(projectFile, nameof(projectFile));
ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(toolsVersion, nameof(toolsVersion));
@ -279,7 +282,8 @@ namespace Microsoft.Build.Execution
BuildEventContext buildEventContext = new BuildEventContext(buildParameters.NodeId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId);
ProjectRootElement xml = ProjectRootElement.OpenProjectOrSolution(projectFile, globalProperties, toolsVersion, buildParameters.ProjectRootElementCache, true /*Explicitly Loaded*/);
Initialize(xml, globalProperties, toolsVersion, subToolsetVersion, 0 /* no solution version provided */, buildParameters, projectCollection.LoggingService, buildEventContext, projectLoadSettings: projectLoadSettings, evaluationContext: evaluationContext);
Initialize(xml, globalProperties, toolsVersion, subToolsetVersion, 0 /* no solution version provided */, buildParameters, projectCollection.LoggingService, buildEventContext,
projectLoadSettings: projectLoadSettings, evaluationContext: evaluationContext, directoryCacheFactory: directoryCacheFactory);
}
/// <summary>
@ -327,7 +331,7 @@ namespace Microsoft.Build.Execution
/// <param name="projectCollection">Project collection</param>
/// <returns>A new project instance</returns>
public ProjectInstance(ProjectRootElement xml, IDictionary<string, string> globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection)
: this(xml, globalProperties, toolsVersion, subToolsetVersion, projectCollection, projectLoadSettings: null, evaluationContext: null, interactive: false)
: this(xml, globalProperties, toolsVersion, subToolsetVersion, projectCollection, projectLoadSettings: null, evaluationContext: null, directoryCacheFactory: null, interactive: false)
{
}
@ -399,9 +403,11 @@ namespace Microsoft.Build.Execution
/// <param name="projectCollection">Project collection</param>
/// <param name="projectLoadSettings">Project load settings</param>
/// <param name="evaluationContext">The context to use for evaluation.</param>
/// <param name="directoryCacheFactory">The directory cache factory to use for file I/O.</param>
/// <param name="interactive">Indicates if loading the project is allowed to interact with the user.</param>
/// <returns>A new project instance</returns>
private ProjectInstance(ProjectRootElement xml, IDictionary<string, string> globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings? projectLoadSettings, EvaluationContext evaluationContext, bool interactive)
private ProjectInstance(ProjectRootElement xml, IDictionary<string, string> globalProperties, string toolsVersion, string subToolsetVersion, ProjectCollection projectCollection,
ProjectLoadSettings? projectLoadSettings, EvaluationContext evaluationContext, IDirectoryCacheFactory directoryCacheFactory, bool interactive)
{
BuildEventContext buildEventContext = new BuildEventContext(0, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTaskId);
@ -410,7 +416,8 @@ namespace Microsoft.Build.Execution
Interactive = interactive
};
Initialize(xml, globalProperties, toolsVersion, subToolsetVersion, 0 /* no solution version specified */, buildParameters, projectCollection.LoggingService, buildEventContext, projectLoadSettings: projectLoadSettings, evaluationContext: evaluationContext);
Initialize(xml, globalProperties, toolsVersion, subToolsetVersion, 0 /* no solution version specified */, buildParameters, projectCollection.LoggingService, buildEventContext,
projectLoadSettings: projectLoadSettings, evaluationContext: evaluationContext, directoryCacheFactory: directoryCacheFactory);
}
/// <summary>
@ -755,6 +762,7 @@ namespace Microsoft.Build.Execution
options.ProjectCollection ?? ProjectCollection.GlobalProjectCollection,
options.LoadSettings,
options.EvaluationContext,
options.DirectoryCacheFactory,
options.Interactive);
}
@ -773,6 +781,7 @@ namespace Microsoft.Build.Execution
options.ProjectCollection ?? ProjectCollection.GlobalProjectCollection,
options.LoadSettings,
options.EvaluationContext,
options.DirectoryCacheFactory,
options.Interactive);
}
@ -2702,7 +2711,8 @@ namespace Microsoft.Build.Execution
ISdkResolverService sdkResolverService = null,
int submissionId = BuildEventContext.InvalidSubmissionId,
ProjectLoadSettings? projectLoadSettings = null,
EvaluationContext evaluationContext = null)
EvaluationContext evaluationContext = null,
IDirectoryCacheFactory directoryCacheFactory = null)
{
ErrorUtilities.VerifyThrowArgumentNull(xml, nameof(xml));
ErrorUtilities.VerifyThrowArgumentLengthIfNotNull(explicitToolsVersion, "toolsVersion");
@ -2792,8 +2802,8 @@ namespace Microsoft.Build.Execution
evaluationContext = evaluationContext?.ContextForNewProject() ?? EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated);
Evaluator<ProjectPropertyInstance, ProjectItemInstance, ProjectMetadataInstance, ProjectItemDefinitionInstance>.Evaluate(
this,
null,
data: this,
project: null,
xml,
projectLoadSettings ?? buildParameters.ProjectLoadSettings, /* Use override ProjectLoadSettings if specified */
buildParameters.MaxNodeCount,
@ -2801,6 +2811,7 @@ namespace Microsoft.Build.Execution
loggingService,
new ProjectItemInstanceFactory(this),
buildParameters.ToolsetProvider,
directoryCacheFactory,
ProjectRootElementCache,
buildEventContext,
sdkResolverService ?? evaluationContext.SdkResolverService, /* Use override ISdkResolverService if specified */
@ -2942,11 +2953,9 @@ namespace Microsoft.Build.Execution
if (item.DirectMetadata != null)
{
directMetadata = new CopyOnWritePropertyDictionary<ProjectMetadataInstance>();
foreach (ProjectMetadata directMetadatum in item.DirectMetadata)
{
ProjectMetadataInstance directMetadatumInstance = new ProjectMetadataInstance(directMetadatum);
directMetadata.Set(directMetadatumInstance);
}
IEnumerable<ProjectMetadataInstance> projectMetadataInstances = item.DirectMetadata.Select(directMetadatum => new ProjectMetadataInstance(directMetadatum));
directMetadata.ImportProperties(projectMetadataInstances);
}
// For externally constructed ProjectItem, fall back to the publicly available EvaluateInclude

Просмотреть файл

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.Build.BackEnd;
using Microsoft.Build.Collections;
using Microsoft.Build.Construction;
@ -20,7 +21,7 @@ namespace Microsoft.Build.Execution
/// Immutable.
/// </summary>
[DebuggerDisplay("{_itemType} #Metadata={MetadataCount}")]
public class ProjectItemDefinitionInstance : IKeyed, IMetadataTable, IItemDefinition<ProjectMetadataInstance>, ITranslatable
public class ProjectItemDefinitionInstance : IKeyed, IMetadataTable, IItemDefinition<ProjectMetadataInstance>, ITranslatable, IItemTypeDefinition
{
/// <summary>
/// Item type, for example "Compile", that this item definition applies to
@ -58,11 +59,9 @@ namespace Microsoft.Build.Execution
if (itemDefinition.MetadataCount > 0)
{
_metadata = new CopyOnWritePropertyDictionary<ProjectMetadataInstance>();
}
foreach (ProjectMetadata originalMetadata in itemDefinition.Metadata)
{
_metadata.Set(new ProjectMetadataInstance(originalMetadata));
IEnumerable<ProjectMetadataInstance> projectMetadataInstances = itemDefinition.Metadata.Select(originalMetadata => new ProjectMetadataInstance(originalMetadata));
_metadata.ImportProperties(projectMetadataInstances);
}
}
@ -235,5 +234,7 @@ namespace Microsoft.Build.Execution
return instance;
}
string IItemTypeDefinition.ItemType => _itemType;
}
}

Просмотреть файл

@ -33,7 +33,8 @@ namespace Microsoft.Build.Execution
ITaskItem2,
IMetadataTable,
ITranslatable,
IMetadataContainer
IMetadataContainer,
IItemTypeDefinition
{
/// <summary>
/// The project instance to which this item belongs.
@ -113,10 +114,8 @@ namespace Microsoft.Build.Execution
if (directMetadata?.GetEnumerator().MoveNext() == true)
{
metadata = new CopyOnWritePropertyDictionary<ProjectMetadataInstance>();
foreach (KeyValuePair<string, string> metadatum in directMetadata)
{
metadata.Set(new ProjectMetadataInstance(metadatum.Key, metadatum.Value));
}
IEnumerable<ProjectMetadataInstance> directMetadataInstances = directMetadata.Select(metadatum => new ProjectMetadataInstance(metadatum.Key, metadatum.Value));
metadata.ImportProperties(directMetadataInstances);
}
CommonConstructor(project, itemType, includeEscaped, includeEscaped, metadata, null /* need to add item definition metadata */, definingFileEscaped);
@ -586,11 +585,10 @@ namespace Microsoft.Build.Execution
internal static void SetMetadata(IEnumerable<KeyValuePair<string, string>> metadataList, IEnumerable<ProjectItemInstance> items)
{
// Set up a single dictionary that can be applied to all the items
CopyOnWritePropertyDictionary<ProjectMetadataInstance> metadata = new CopyOnWritePropertyDictionary<ProjectMetadataInstance>();
foreach (KeyValuePair<string, string> metadatum in metadataList)
{
metadata.Set(new ProjectMetadataInstance(metadatum.Key, metadatum.Value));
}
CopyOnWritePropertyDictionary<ProjectMetadataInstance> metadata = new();
IEnumerable<ProjectMetadataInstance> projectMetadataInstances = metadataList.Select(metadatum => new ProjectMetadataInstance(metadatum.Key, metadatum.Value));
metadata.ImportProperties(projectMetadataInstances);
foreach (ProjectItemInstance item in items)
{
@ -1095,40 +1093,45 @@ namespace Microsoft.Build.Execution
CopyOnWritePropertyDictionary<ProjectMetadataInstance> allMetadata = new CopyOnWritePropertyDictionary<ProjectMetadataInstance>();
// Next, any inherited item definitions. Front of the list is highest priority,
// so walk backwards.
for (int i = _itemDefinitions.Count - 1; i >= 0; i--)
{
foreach (ProjectMetadataInstance metadatum in _itemDefinitions[i].Metadata)
{
if (metadatum != null)
{
allMetadata.Set(metadatum);
}
else
{
Debug.Fail($"metadatum from {_itemDefinitions[i]} is null, see https://github.com/dotnet/msbuild/issues/5267");
}
}
}
// Finally any direct metadata win.
if (_directMetadata != null)
{
foreach (ProjectMetadataInstance metadatum in _directMetadata)
{
if (metadatum != null)
{
allMetadata.Set(metadatum);
}
else
{
Debug.Fail("metadatum in _directMetadata is null, see https://github.com/dotnet/msbuild/issues/5267");
}
}
}
allMetadata.ImportProperties(metaData());
return allMetadata;
IEnumerable<ProjectMetadataInstance> metaData()
{
// Next, any inherited item definitions. Front of the list is highest priority,
// so walk backwards.
for (int i = _itemDefinitions.Count - 1; i >= 0; i--)
{
foreach (ProjectMetadataInstance metadatum in _itemDefinitions[i].Metadata)
{
if (metadatum != null)
{
yield return metadatum;
}
else
{
Debug.Fail($"metadatum from {_itemDefinitions[i]} is null, see https://github.com/dotnet/msbuild/issues/5267");
}
}
}
// Finally any direct metadata win.
if (_directMetadata != null)
{
foreach (ProjectMetadataInstance metadatum in _directMetadata)
{
if (metadatum != null)
{
yield return metadatum;
}
else
{
Debug.Fail("metadatum in _directMetadata is null, see https://github.com/dotnet/msbuild/issues/5267");
}
}
}
}
}
}
@ -1693,12 +1696,21 @@ namespace Microsoft.Build.Execution
if (translator.TranslateNullable(_directMetadata))
{
int count = translator.Reader.ReadInt32();
_directMetadata = (count == 0) ? null : new CopyOnWritePropertyDictionary<ProjectMetadataInstance>();
for (int i = 0; i < count; i++)
if (count > 0)
{
int key = translator.Reader.ReadInt32();
int value = translator.Reader.ReadInt32();
_directMetadata.Set(new ProjectMetadataInstance(interner.GetString(key), interner.GetString(value), allowItemSpecModifiers: true));
IEnumerable<ProjectMetadataInstance> metaData =
Enumerable.Range(0, count).Select(_ =>
{
int key = translator.Reader.ReadInt32();
int value = translator.Reader.ReadInt32();
return new ProjectMetadataInstance(interner.GetString(key), interner.GetString(value), allowItemSpecModifiers: true);
});
_directMetadata = new CopyOnWritePropertyDictionary<ProjectMetadataInstance>();
_directMetadata.ImportProperties(metaData);
}
else
{
_directMetadata = null;
}
}
}
@ -1961,10 +1973,8 @@ namespace Microsoft.Build.Execution
{
// Set up a single dictionary that can be applied to all the items
CopyOnWritePropertyDictionary<ProjectMetadataInstance> metadata = new CopyOnWritePropertyDictionary<ProjectMetadataInstance>();
foreach (Pair<ProjectMetadataElement, string> metadatum in metadataList)
{
metadata.Set(new ProjectMetadataInstance(metadatum.Key.Name, metadatum.Value));
}
IEnumerable<ProjectMetadataInstance> projectMetadataInstances = metadataList.Select(metadatum => new ProjectMetadataInstance(metadatum.Key.Name, metadatum.Value));
metadata.ImportProperties(projectMetadataInstances);
foreach (ProjectItemInstance item in destinationItems)
{
@ -2137,7 +2147,7 @@ namespace Microsoft.Build.Execution
/// Also, more importantly, because typically the same regular metadata values can be shared by many items,
/// and keeping item-specific metadata out of it could allow it to be implemented as a copy-on-write table.
/// </summary>
private class BuiltInMetadataTable : IMetadataTable
private class BuiltInMetadataTable : IMetadataTable, IItemTypeDefinition
{
/// <summary>
/// Item type
@ -2195,6 +2205,8 @@ namespace Microsoft.Build.Execution
return value;
}
string IItemTypeDefinition.ItemType => _itemType;
}
}

Просмотреть файл

@ -97,6 +97,7 @@ namespace Microsoft.Build.Logging
ErrorUtilities.VerifyThrowArgumentNull(eventSource, nameof(eventSource));
ParseFileLoggerParameters();
string fileName = _logFile;
try
{
// Create a new file logger and pass it some parameters to make the build log very detailed

Просмотреть файл

@ -39,6 +39,11 @@ namespace Microsoft.Build.Logging
colorReset: BaseConsoleLogger.DontResetColor)
{
WriteHandler = Write;
if (EncodingUtilities.GetExternalOverriddenUILanguageIfSupportableWithEncoding() != null)
{
_encoding = Encoding.UTF8;
}
}
#endregion

Просмотреть файл

@ -49,16 +49,13 @@
<PackageReference Include="System.Memory" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' != '.NETFramework'">
<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard'">
<PackageReference Include="System.Reflection.Metadata" />
<PackageReference Include="System.Security.Principal.Windows" />
<PackageReference Include="System.Text.Encoding.CodePages" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Shared\EncodingUtilities.cs">
<Link>SharedUtilities\EncodingUtilities.cs</Link>
</Compile>
<Compile Include="..\Shared\EnvironmentUtilities.cs">
<Link>SharedUtilities\EnvironmentUtilities.cs</Link>
</Compile>
@ -163,6 +160,7 @@
<Compile Include="BackEnd\Components\SdkResolution\SdkResolverException.cs" />
<Compile Include="BackEnd\Components\SdkResolution\TranslationHelpers.cs" />
<Compile Include="FileSystem\*.cs" />
<Compile Include="Evaluation\IItemTypeDefinition.cs" />
<Compile Include="Utilities\ReaderWriterLockSlimExtensions.cs" />
<Compile Include="BackEnd\Node\ConsoleOutput.cs" />
<Compile Include="BackEnd\Node\PartialBuildTelemetry.cs" />

Просмотреть файл

@ -1983,4 +1983,8 @@ Utilization: {0} Average Utilization: {1:###.0}</value>
<data name="NodeReused" xml:space="preserve">
<value>Reusing node {0} (PID: {1}).</value>
</data>
</root>
<data name="ItemReferencingSelfInTarget" xml:space="preserve">
<value>MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</value>
<comment>{StrBegin="MSB4120: "}</comment>
</data>
</root>

5
src/Build/Resources/xlf/Strings.cs.xlf сгенерированный
Просмотреть файл

@ -154,6 +154,11 @@
<target state="translated">Objekty EvaluationContext vytvořené pomocí SharingPolicy.Isolated nepodporují předávání souborového systému MSBuildFileSystemBase.</target>
<note />
</trans-unit>
<trans-unit id="ItemReferencingSelfInTarget">
<source>MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</source>
<target state="new">MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</target>
<note>{StrBegin="MSB4120: "}</note>
</trans-unit>
<trans-unit id="KillingProcessWithPid">
<source>Killing process with pid = {0}.</source>
<target state="translated">Ukončuje se proces s pid = {0}.</target>

5
src/Build/Resources/xlf/Strings.de.xlf сгенерированный
Просмотреть файл

@ -154,6 +154,11 @@
<target state="translated">Die Übergabe eines MSBuildFileSystemBase-Dateisystems an EvaluationContext-Objekte, die mit "SharingPolicy.Isolated" erstellt wurden, wird nicht unterstützt.</target>
<note />
</trans-unit>
<trans-unit id="ItemReferencingSelfInTarget">
<source>MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</source>
<target state="new">MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</target>
<note>{StrBegin="MSB4120: "}</note>
</trans-unit>
<trans-unit id="KillingProcessWithPid">
<source>Killing process with pid = {0}.</source>
<target state="translated">Der Prozess mit PID {0} wird beendet.</target>

5
src/Build/Resources/xlf/Strings.es.xlf сгенерированный
Просмотреть файл

@ -154,6 +154,11 @@
<target state="translated">Los objetos EvaluationContext creados con SharingPolicy.Isolated no admiten que se les pase un sistema de archivos MSBuildFileSystemBase.</target>
<note />
</trans-unit>
<trans-unit id="ItemReferencingSelfInTarget">
<source>MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</source>
<target state="new">MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</target>
<note>{StrBegin="MSB4120: "}</note>
</trans-unit>
<trans-unit id="KillingProcessWithPid">
<source>Killing process with pid = {0}.</source>
<target state="translated">Terminando el proceso con el PID = {0}.</target>

5
src/Build/Resources/xlf/Strings.fr.xlf сгенерированный
Просмотреть файл

@ -154,6 +154,11 @@
<target state="translated">Les objets EvaluationContext créés avec SharingPolicy.Isolated ne prennent pas en charge le passage d'un système de fichiers MSBuildFileSystemBase.</target>
<note />
</trans-unit>
<trans-unit id="ItemReferencingSelfInTarget">
<source>MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</source>
<target state="new">MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</target>
<note>{StrBegin="MSB4120: "}</note>
</trans-unit>
<trans-unit id="KillingProcessWithPid">
<source>Killing process with pid = {0}.</source>
<target state="translated">Arrêt du processus ayant le PID = {0}.</target>

5
src/Build/Resources/xlf/Strings.it.xlf сгенерированный
Просмотреть файл

@ -154,6 +154,11 @@
<target state="translated">Agli oggetti EvaluationContext creati con SharingPolicy.Isolated non è possibile passare un file system MSBuildFileSystemBase.</target>
<note />
</trans-unit>
<trans-unit id="ItemReferencingSelfInTarget">
<source>MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</source>
<target state="new">MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</target>
<note>{StrBegin="MSB4120: "}</note>
</trans-unit>
<trans-unit id="KillingProcessWithPid">
<source>Killing process with pid = {0}.</source>
<target state="translated">Terminazione del processo con PID = {0}.</target>

5
src/Build/Resources/xlf/Strings.ja.xlf сгенерированный
Просмотреть файл

@ -154,6 +154,11 @@
<target state="translated">SharingPolicy.Isolated を指定して作成された EvaluationContext オブジェクトに MSBuildFileSystemBase ファイル システムを渡すことはサポートされていません。</target>
<note />
</trans-unit>
<trans-unit id="ItemReferencingSelfInTarget">
<source>MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</source>
<target state="new">MSB4120: Item '{0}' definition within target references itself via (qualified or unqualified) metadatum '{1}'. This can lead to unintended expansion and cross-applying of pre-existing items. More info: https://aka.ms/msbuild/metadata-self-ref</target>
<note>{StrBegin="MSB4120: "}</note>
</trans-unit>
<trans-unit id="KillingProcessWithPid">
<source>Killing process with pid = {0}.</source>
<target state="translated">PID = {0} のプロセスを中止しています。</target>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше