Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge main to 17.0 #6352

Merged
merged 17 commits into from
Apr 16, 2021
Merged

Merge main to 17.0 #6352

merged 17 commits into from
Apr 16, 2021

Conversation

rainersigwald
Copy link
Member

Since 17.0 is flowing to its internal home now, let's keep it up to date.

dotnet-bot and others added 17 commits February 12, 2021 14:11
Co-authored-by: Matt Mitchell <mmitche@microsoft.com>
Co-authored-by: Ben Villalobos <4691428+BenVillalobos@users.noreply.github.com>
Handle unsupported paths in ProjectInSolution.AbsolutePath (#6238)
Add an option to log global properties, properties and items on ProjectEvaluationFinishedEventArgs instead of ProjectStartedEventArgs. This option is currently only turned on by the BinaryLogger.

This has several advantages. Currently only the projects that are built by the central node log their properties and items (properties are translated across nodes only if a special flag is set, and items are never translated). This resulted in properties and items not being available for projects built on other nodes. Now we log them after every evaluation and translate across nodes if needed. Together with the fact that we now log EvaluationId for each ProjectStarted, we can now recover properties and items for all project started events. This is the main purpose of this PR - to not lose properties and items like we currently do. We will still not log for project results that are satisfied by cache, because we don't keep track of evaluation for these. Presumably it will have already been logged previously.

In addition, if more than one project are built from the same evaluation, we do not duplicate properties and items, only logging them once. This results in logging more information, but storing it more efficiently. Together with string and dictionary deduplication we see very significant savings in binlog size and some reduction in build time.

This change has several large parts:

 1. add a way to enumerate evaluation properties and items directly at the end of Evaluate() for PropertyDictionary<ProjectPropertyInstance> and ItemDictionary<ProjectItemInstance>
 2. manual translation logic for ProjectEvaluationStarted and ProjectEvaluationFinished (instead of relying on TranslateDotNet/BinaryFormatter)
 3. reading and writing ProjectEvaluationFinished GlobalProperties, Properties and Items in BuildEventArgsReader/Writer (used by BinaryLogger)
 4. adding IEventSource4 with IncludeEvaluationPropertiesAndItems, to propagate this setting across nodes and threading it through the LoggingService
 5. update the ParallelConsoleLogger and SerialConsoleLogger to print the new data, if present
 6. tests

One controversial design decision here is storing a reference to live evaluation data in ProjectEvaluationFinishedEventArgs. It does not make a snapshot of the data to avoid very significant allocations. It does take the lock on the PropertyDictionary<T>/ItemDictionary<T> when enumerating, because logging is asynchronous and the logging consumer (BinaryLogger) will enumerate the data potentially after the build has already started and the data is being mutated. I did see exceptions when enumerating without the lock. We had the same problem when the data was logged on ProjectStartedEventArgs though. In addition, there's a slight risk of logging not the exact data as it was at the end of evaluation, but the mutated data after some target has modified it. However given that the previous behavior was to not log anything for out-of-proc projects, and given the very significant allocation reduction, I think it's worth it.

To mitigate, we could capture a snapshot at the end of evaluation, so we don't hold a reference to live data. This won't need a lock to enumerate. Ideally we also rely on the immutable collections to avoid allocations, but I didn't see an easy way to do that currently. We can investigate this in a future change.

For items, it doesn't concatenate items of different types into a single large item stream, but keeps multiple lists, one per item type, to reflect the internal representation. Not flattening item types results in savings because we don't have to mention the item type for each item.

This change increments the BinaryLogger file format to 12, to serialize GlobalProperties, Properties and Items on ProjectEvaluationFinishedEventArgs. It also stores items more efficiently, without having to know the number of item types in advance and enumerate in a single pass. It writes the item type and all items of that type, and it writes 0 to signal there are no more item types. It also no longer writes the Message for args as it can be recovered upon reading.

New EnumerateProperties() and EnumerateItems() methods are added to Utilities, to consolidate the logic to enumerate the new data structures in a single location, used by packet translation logic, binary logger and the console loggers.

Perf wise, I'm seeing no significant change on binlog size for small builds (it's a wash, because we log properties/items for all projects now, but they are no longer duplicated). For large projects I expect very significant savings though, as ProjectStarted is the most heavy-weight event in large binlogs.
Build performance with /bl on small-ish builds is improved 27 s -> 24 s for single-core and 18 s -> 17 s for parallel. No observable change without /bl.

Fixes #5316
Fixes #3616
Change relative path calculation
…5.9.1.8 (#6327)

NuGet.Build.Tasks
 From Version 5.9.0-rc.7122 -> To Version 5.9.1-rc.8

Co-authored-by: dotnet-maestro[bot] <dotnet-maestro[bot]@users.noreply.github.com>
Improve doc comments
Fixes #2795
and indirectly fixes https://developercommunity.visualstudio.com/t/copytooutputdirectorypreservenewest-ignored-inside/1332219?from=email&viewtype=all#T-ND1363347

Context
There's currently no way to include items in a project such that:

Visual studio sees them in a specific folder (via <Link>).
They are published to a user-defined path (currently controlled via <Link>)
Changes Made
Modify the AssignTargetPath task to return early if TargetPath metadata is already set on a particular item.

Testing
 Need to add one test covering this.
 Tested locally with bootstrapped MSBuild on command line
 Tested locally with a boostrapped msbuild on internal VS
Here's the repro I'm using:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Content Include="Files\**">
      <Link>Files\%(Filename)%(Extension)</Link>
      <TargetPath>%(Filename)%(Extension)</TargetPath>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
</Project>
Notes
The other way of solving this problem has to do with Microsoft.Common.CurrentVersion.targets. We modify it so that the AssignTargetPath task look something like this:

    <AssignTargetPath Files="@(Content)" RootFolder="$(MSBuildProjectDirectory)" Condition="'%(Content.TargetPath)'==''">
      <Output TaskParameter="AssignedFiles" ItemName="ContentWithTargetPath" />
    </AssignTargetPath>
    <ItemGroup>
        <ContentWithTargetPath Include="@(Content)" Condition="'%(Content.TargetPath)'!=''"/>
    </ItemGroup>
This seems less efficient to me. AssignTargetPath is also called for all None, Content, and EmbeddedResource files. So if we go this batching route and want None or EmbeddedResource to have this feature, we'd need to batch those as well.
…6285)

Fixes #2281

Context
This change allows the WriteCodeFragment task to define assembly attributes that require parameters that are not of type System.String. For example, CSLCompliantAttribute can be generated with a parameter of true instead of "true".

Changes Made
Additional metadata can be defined on an AssemblyAttribute that specifies how to treat the parameters specified in the metadata. There are three different ways that the parameters can be treated.

Infer the Type
Without specifying any additional metadata, attributes that are defined in the mscorlib assembly (i.e. types that can be loaded via System.Type.GetType(string)) will have their parameter types inferred by finding the constructor where the parameter count matches the number of parameters specified in the metadata. For example, this:

<ItemGroup>
  <AssemblyAttribute Include="CLSCompliantAttribute">
    <_Parameter1>true</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>
Will produce the code:

[assembly: CLSCompliantAttribute(true)]
For backward-compatibility, if the attribute cannot be found, or no matching constructor is found, the parameter is treated as a string.

Declare the Type
An additional metadata item can be used to specify the full name of the parameter type. To do this, add a metadata item that has the same name as the parameter with "_TypeName" appended to the end. For example, this:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <_Parameter1>True</_Parameter1>
    <_Parameter1_TypeName>System.Boolean</_Parameter1_TypeName>
  </AssemblyAttribute>
</ItemGroup>
Will produce the code:

[assembly: TestAttribute(true)]
This also works with named parameters:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <Foo>42</IdentifyLevel>
    <Foo_TypeName>System.Int32</Foo_TypeName>
  </AssemblyAttribute>
</ItemGroup>
[assembly: TestAttribute(42)]
All types that can be used as attribute parameters are supported, except for arrays.

For backward-compatibility, if a metadata item ends with "_TypeName", but there is no metadata item for the parameter with that name, then it will be treated as another named property. For example, this:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <Foo_TypeName>System.Int32</Foo_TypeName>
  </AssemblyAttribute>
</ItemGroup>
Would produce the code:

[assembly: TestAttribute(Foo_TypeName="System.Int32")]
Specify the Exact Code
For cases where declaring the type is insufficient (such as when the parameter is an array), you can specify the exact that that will be generated for the parameter by adding metadata that has the same name as the parameter with "_IsLiteral" appended to the end. For example, this:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <_Parameter1>new int[] { 1, 3, 5 } /* odd numbers */</_Parameter1>
    <_Parameter1_IsLiteral>true</_Parameter1_IsLiteral>
  </AssemblyAttribute>
</ItemGroup>
Will produce the code:

[assembly: TestAttribute(new int[] { 1, 3, 5 } /* odd numbers */)]
The limitation with this is that the code you provide is language-specific. For example, the literal value in the metadata above will only work in C#. If you used that same metadata in a VB.NET project, you would receive a compiler error.

This works with both positional and named parameters. As with the ..._TypeName metadata, if an ..._IsLiteral metadata does not have a corresponding parameter name, it will be treated as a named parameter for backward-compatibility.

Mixed Parameter Behavior
Because the additional metadata only applies to a specific parameter, you can choose to treat different parameters in different ways. For example, you can infer/use the default behavior for one parameter, specify the type for the second parameter and use a literal value for the third. For example:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <_Parameter1>This is a string</_Parameter1>
    <_Parameter2>42></Parameter2>
    <_Parameter2_TypeName>System.Int32</_Parameter2_TypeName>
    <_Parameter3>new int[] { 1 }</_Parameter3>
    <_Parameter3_IsLiteral>true</_Parameter3_IsLiteral>
  </AssemblyAttribute>
</ItemGroup>
…#6312)

Fixes #6281

Context
BuildRequestDataFlags and ProjectLoadSettings are set during /t:restore in a best effort to run the Restore target in hopes that it will correct the potentially bad state that a project is in. Visual Studio also sets ProjectLoadSettings.IgnoreMissingImports so that an unresolved MSBuild project SDK doesn't prevent loading of a bad project so it can give the user an error and let them edit the file.

However, this means that from the command-line an unresolved SDK doesn't fail /t:restore. This is because the missing "import" is ignored and non-existent targets are ignored so the build succeeds.

Changes Made
Introduced two new BuildRequestDataFlags:

SkipNonexistentNonTopLevelTargets - A new flag to be used in this context to tell the build to ignore non-existent targets but not top level ones. In this case we're specifying /t:restore so if the Restore target doesn't exist, that should be an error. Only other targets that are trying to run are ignored (like InitialTargets, Before/AfterTargets, etc).
FailOnUnresolvedSdk - We still need to ignore missing imports and I can't introduce a new flag to split the implementation now since Visual Studio sets ProjectLoadSettings.IgnoreMissingImports as a way to ignore unresolved SDKs. So this flag tells the evaluator to fail on an unresolved SDK but to continue ignoring other missing imports.
… build successfully when using exec (#6223)

Fixes https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1264667

Context
Users with profile names that contain parens in them will never be able to succesfully build a program that calls Exec with any command. This is because exec does the following:

Generate a cmd file in the users temp directory
Calls cmd /Q /D /C <path-to-temp-file>
The problem with this is that running cmd /C "some command" does not work if the command has any parens in it. It needs to be escaped like so: ^( and ^).

Changes Made
When user sets EscapeSpecialCharacters in the exec task (boolean parameter), we escape characters that need to be escaped when calling cmd /c. We preserve the original functionality of always removing spaces and escaping '^'
Added under changewave 16.10
Added documentation for it.
Also add a line about how to build Release.
Some loggers depended on ProjectStartedEventArgs.GlobalProperties being not null and set. It will take a long time to move them to ProjectEvaluationFinished (needs to bump MSBuild dependency to 16.10).

For now log GlobalProperties in both places (ProjectStarted and ProjectEvaluationFinished). Hopefully the deduplication will save us from any significant increase in binlog size.

Fixes #6341
Fix some URLs from Microsoft/msbuild -> dotnet/msbuild

Also changes some branches from master to main.
@rainersigwald rainersigwald merged commit 71978fd into vs17.0 Apr 16, 2021
@rainersigwald rainersigwald deleted the main-to-17.0 branch April 16, 2021 21:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants