Skip to content

Conversation

@mmitche
Copy link
Member

@mmitche mmitche commented Jan 24, 2023

This is the initial implementation of an experimental intrinsic designed to support the ability to build subsets of a repo/solution/project. This ability can reduce build time and support the ability to build projects based on available target resources per-platforms.

The idea is that this intrinsic would be used in a set of targets that are imported early, before standard .NET SDK targets, and could be used to filter the target frameworks that a project is going to build for. .NET's arcade will contain an initial implementation of those targets, looking something like this:

<PropertyGroup Condition="$(_EnableTargetFrameworkFiltering)">
  <_OriginalTargetFrameworks Condition="'$(TargetFrameworks)' != ''">$(TargetFrameworks)</_OriginalTargetFrameworks>
  <_OriginalTargetFrameworks Condition="'$(TargetFramework)' != ''">$(TargetFramework)</_OriginalTargetFrameworks>
  <_FilteredTargetFrameworks>$([MSBuild]::Unescape($([MSBuild]::IntersectTargetFrameworks('$(_OriginalTargetFrameworks)', '$(DotNetTargetFrameworkFilter)'))))</_FilteredTargetFrameworks>
  <!-- Maintain usage of the original property,  -->
  <TargetFrameworks Condition="'$(TargetFrameworks)' != ''">$(_FilteredTargetFrameworks)</TargetFrameworks>
  <TargetFramework Condition="'$(TargetFramework)' != ''">$(_FilteredTargetFrameworks)</TargetFramework>
  <ExcludeFromBuild Condition="'$(_FilteredTargetFrameworks)' == ''">true</ExcludeFromBuild>
</PropertyGroup>

Initially, the intrinsic is implemented as follows:

  • A framework is maintained if the Framework name (Framework property of parsed nuget property) exists in both lists AND
  • The version matches, if specified.

Thus, if a TFM list is: net6.0-windows;netstandard2.0;net472 and the intersect list is net6.0;netstandard2.0, the result would be net6.0;netstandard2.0.

It's possible that before this MSBuild version goes out of preview. There are two other options:

  • An implementation that selects based on compatibilty. The inherent problem with a compatibility check (based on NuGet's notion) is that some TFMs are compatible with TFMs you may want to remove. For instance, you may want to keep netstandard2.0, but not net472. However, since net472 is compatible with netstandard2.0, it would not be eliminated.
  • Strict intersection based on all TFM properties. This is simpler and more straightforward, but requires a more verbose input list. For instance, if you wanted to preserve net6 targets, especially in cross-targeting situations, you may have list many TFMs (e.g. specific OS versions like net6.0-windows).

Of these alternate approaches, I think strict is the most likely to be usable and understandable.

Fixes #

Context

Changes Made

Testing

Notes

This is the initial implementation of an experimental intrinsic designed to support the ability to build subsets of a repo/solution/project. This ability can reduce build time and support the ability to build projects based on available target resources per-platforms.

The idea is that this intrinsic would be used in a set of targets that are imported early, before standard .NET SDK targets, and could be used to filter the target frameworks that a project is going to build for. .NET's arcade will contain an initial implementation of those targets, looking something like this:

```
<PropertyGroup Condition="$(_EnableTargetFrameworkFiltering)">
  <_OriginalTargetFrameworks Condition="'$(TargetFrameworks)' != ''">$(TargetFrameworks)</_OriginalTargetFrameworks>
  <_OriginalTargetFrameworks Condition="'$(TargetFramework)' != ''">$(TargetFramework)</_OriginalTargetFrameworks>
  <_FilteredTargetFrameworks>$([MSBuild]::Unescape($([MSBuild]::IntersectTargetFrameworks('$(_OriginalTargetFrameworks)', '$(DotNetTargetFrameworkFilter)'))))</_FilteredTargetFrameworks>
  <!-- Maintain usage of the original property,  -->
  <TargetFrameworks Condition="'$(TargetFrameworks)' != ''">$(_FilteredTargetFrameworks)</TargetFrameworks>
  <TargetFramework Condition="'$(TargetFramework)' != ''">$(_FilteredTargetFrameworks)</TargetFramework>
  <ExcludeFromBuild Condition="'$(_FilteredTargetFrameworks)' == ''">true</ExcludeFromBuild>
</PropertyGroup>
```

Initially, the intrinsic is implemented as follows:
- A framework is maintained if the Framework name (Framework property of parsed nuget property) exists in both lists AND
- The version matches, if specified.

Thus, if a TFM list is: `net6.0-windows;netstandard2.0;net472` and the intersect list is `net6.0;netstandard2.0`, the result would be net6.0;netstandard2.0.

It's possible that before this MSBuild version goes out of preview. There are two other options:
- **An implementation that selects based on compatibilty**. The inherent problem with a compatibility check (based on NuGet's notion) is that some TFMs are compatible with TFMs you may want to remove. For instance, you may want to keep netstandard2.0, but not net472. However, since net472 is compatible with netstandard2.0, it would not be eliminated.
- **Strict intersection based on all TFM properties**. This is simpler and more straightforward, but requires a more verbose input list. For instance, if you wanted to preserve net6 targets, especially in cross-targeting situations, you may have list many TFMs (e.g. specific OS versions like net6.0-windows).

Of these alternate approaches, I think strict is the most likely to be usable and understandable.
@mmitche mmitche requested a review from rainersigwald January 24, 2023 20:31
@mmitche
Copy link
Member Author

mmitche commented Jan 24, 2023

@rainersigwald Since we have a bit before 17.6 ships, my thought is to get this checked in, dogfooded a bit, and then fail fast and change the intersection approach ASAP if need be. I assume I also need to add tests.

I currently have this working in the VMR for roslyn, with arcade targets. It works pretty well, though I'd like to be able to test outside of the VMR and in more traditonal arcade scenarios.

@mmitche
Copy link
Member Author

mmitche commented Jan 24, 2023

/cc @ViktorHofer

mmitche added a commit to mmitche/arcade that referenced this pull request Jan 24, 2023
Relies on dotnet/msbuild#8350 to be active or not cause build errors when not enabled. This is largely still experimental and may change.
Enables the ability for an invocation of an arcade build to only build a desired set of target frameworks. The primary consumer is source-build. This allows source-build to simply remove TFMs from the build that would require SBRPs without extensive changes in project files.
mmitche added a commit to mmitche/arcade that referenced this pull request Jan 24, 2023
Relies on dotnet/msbuild#8350 to be active or not cause build errors when not enabled. This is largely still experimental and may change.
Enables the ability for an invocation of an arcade build to only build a desired set of target frameworks. The primary consumer is source-build. This allows source-build to simply remove TFMs from the build that would require SBRPs without extensive changes in project files.
@mmitche
Copy link
Member Author

mmitche commented Jan 25, 2023

@Forgind @rainersigwald I realize now that Expander_Tests.cs do not test the intrinsics. They test the property/item expansions. Is there a place where intrinsics can be tested? I can't seem to find one.

@Forgind
Copy link
Contributor

Forgind commented Jan 25, 2023

@Forgind @rainersigwald I realize now that Expander_Tests.cs do not test the intrinsics. They test the property/item expansions. Is there a place where intrinsics can be tested? I can't seem to find one.

I'm a little confused by this question. Do you mean directly testing the intrinsic function without going through expanding the $([MSBuild]::Function(...)) call?

@mmitche
Copy link
Member Author

mmitche commented Jan 25, 2023

@Forgind @rainersigwald I realize now that Expander_Tests.cs do not test the intrinsics. They test the property/item expansions. Is there a place where intrinsics can be tested? I can't seem to find one.

I'm a little confused by this question. Do you mean directly testing the intrinsic function without going through expanding the $([MSBuild]::Function(...)) call?

I think I may have been a little confused by what was happening in some of the test methods. I'll let you know if I need more help.

@mmitche
Copy link
Member Author

mmitche commented Jan 25, 2023

I got what I wanted to working. Thanks!

@mmitche mmitche merged commit 3ae10d3 into dotnet:main Jan 26, 2023
rainersigwald added a commit to rainersigwald/msbuild that referenced this pull request Jan 26, 2023
Recent changes dotnet#8336 and dotnet#8350 collided to produce a build break
on just-introduced formatting.
rainersigwald added a commit that referenced this pull request Jan 26, 2023
Recent changes #8336 and #8350 collided to produce a build break
on just-introduced formatting.
mmitche added a commit to dotnet/arcade that referenced this pull request Feb 1, 2023
* Introduce targets to filter target frameworks
Relies on dotnet/msbuild#8350 to be active or not cause build errors when not enabled. This is largely still experimental and may change.
Enables the ability for an invocation of an arcade build to only build a desired set of target frameworks. The primary consumer is source-build. This allows source-build to simply remove TFMs from the build that would require SBRPs without extensive changes in project files.
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.

2 participants