From c6c25de1fd1f4498239c054a6b174d0d83cd92d2 Mon Sep 17 00:00:00 2001 From: Sarah Oslund Date: Fri, 4 Jun 2021 15:08:40 -0700 Subject: [PATCH 1/6] First draft of workload target imports design doc --- .../2021/workloads/workload-target-imports.md | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 accepted/2021/workloads/workload-target-imports.md diff --git a/accepted/2021/workloads/workload-target-imports.md b/accepted/2021/workloads/workload-target-imports.md new file mode 100644 index 000000000..aaebb317f --- /dev/null +++ b/accepted/2021/workloads/workload-target-imports.md @@ -0,0 +1,118 @@ +# .NET Workloads Target Import Ordering + +**Owners** [Sarah Oslund](https://github.com/sfoslund) [Daniel Plaisted](https://github.com/dsplaisted) + +## Background + +To support .NET SDK workloads, we [changed the order of targets imports](https://github.com/dotnet/sdk/pull/14393) to allow SDK workloads to change property defaults. When we did this, we also changed the import order of some Windows and WPF targets, as we want to make it a workload in the future, which caused a breaking change. It appears that it is not possible to allow workloads to change property defaults and make windows support a .NET SDK workload without introducing a breaking change. As a result, this document explores possible solutions to minimize the user impact. + +### Original Ordering Change + +Originally, the Windows, WindowsDesktop, and workload targets were imported at the end of Microsoft.NET.Sdk.targets, which was almost the last files imported. However, this was not an appropriate place for the imports if those targets were to override default property values that were set in .NET SDK or MSBuild common targets. The workload needs a chance to set the property if its not already set before the default logic would do so. + +Because of this, the workload targets import [was moved](https://github.com/dotnet/sdk/pull/14393) to come after the target framework parsing. This is because whether a workload is used (and hence needs to be imported) may depend on the target framework or platform, so those conditions should go after the `TargetFramework` has been parsed into the corresponding component properties (`TargetFrameworkIdentifier`, `TargetFrameworkVersion`, `TargetPlatformIdentifier`, and `TargetPlatformVersion`). + +The Windows and WindowsDesktop targets were moved together with the workload targets import, as we expect them to eventually become part of a workload. However, this moved those imports before the import of `Directory.Build.targets`, which meant that properties set in that (such as `UseWPF` and `UseWindowsForms`) would no longer take effect. + +This is an issue not just because it's a breaking change, but because it means that `Directory.Build.targets` can never be used to set a property that determines whether a workload is used. + +### MSBuild Importing Context + +#### MSBuild Evaluation + +We are primarily concerned with "[Pass 1](https://github.com/dotnet/msbuild/blob/6f9e0d620718578aab8dafc439d4501339fa4810/src/Build/Evaluation/Evaluator.cs#L613)" of [MSBuild Evaluation](https://docs.microsoft.com/en-us/visualstudio/msbuild/build-process-overview#evaluation-phase), where properties are evaluated and project imports are loaded. + +Some properties depend on other properties, so the order in which they are declared matters. For example, the default value for the `GenerateDependencyFile` property depends on the `TargetFrameworkIdentifier` property, which itself is derived from the `TargetFramework` property. So these declarations need to be evaluated in the correct order for the values to be set correctly. + +Likewise, project imports (typically used to import `.targets` or `.props` files) can be conditioned on property values. So if WindowsDesktop targets are imported if `UseWPF` or `UseWindowsForms` is true, then those properties need to be set before the WindowsDesktop project import is evaluated. + +The evaluation ordering of MSBuild Targets is usually not important. The order that the Targets are executed is determined by the dependencies between them, not by the order they come in evaluation. The exception to this is that you can override a Target by defining another target with the same name later in evaluation. + +#### Exploring Import Order + +There are many .props and .targets filed that get imported when building a .NET project. One way to see what is imported and in what order is by preprocessing a project, either with the "-pp:" [command line argument](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-command-line-reference?) or with [MSBuild Structured Log Viewer](https://msbuildlog.com/). This will create a single aggregated project file with all project imports expanded inline. + +It is also possible to explore the project imports in the Visual Studio Solution Explorer. Clicking on the "Show All Files" button will add an "Imports" node to the solution tree under the project. You can expand this node and explore the tree of imports active in the project. You can also double click on an imported file to open it up in the editor and view its contents. + +![Project Imports in Solution Explorer](solution-explorer-project-imports.png) + +#### Order of Imports in the .NET SDK + +The following is a simplified list of the files that are imported in an SDK-style .NET project: + +- `Directory.Build.props` +- Main project file +- Version logic (Microsoft.NET.DefaultAssemblyInfo.targets) +- Output path logic (Microsoft.NET.DefaultOutputPaths.targets) +- Publish profile +- Target Framework parsing (Microsoft.NET.TargetFrameworkInference.targets) + - Also appends target framework to output and intermediate paths +- Runtime identifier inference (Microsoft.NET.RuntimeIdentifierInference.targets) + - Also appends Runtime Identifier to output and intermediate paths +- Workload targets imports +- Language targets (e.g. Microsoft.CSharp.targets) and MSBuild common targets +- `Directory.Build.targets` +- (Rest of) .NET SDK targets +- Old location for Windows, WindowsDesktop, and workload targets imports + +#### `Directory.Build.targets` import location + +Conceptually, `Directory.Build.targets` is imported after the body of the main project file. The exact location it is imported is not something most developers likely think about, but it is a good place to put common build logic that depends on properties set in the project file, such as the `TargetFramework`. + +There's not a perfect place to import `Directory.Build.targets`. However, given what we've learned, it may be that the best place to import it is after the TargetFramework parsing, and before the workloads are imported. That way the logic in `Directory.Build.targets` would still be able to depend on the parsed TargetFramework information, but would not be able to override all of the targets and properties set by the .NET SDK and MSBuild common targets that it can today. + +## Proposed Solutions + +The following are the current proposed solutions to the problem outlined above, to be reviewed by the community. + +### Extension Point via Property + +Support an `AfterTargetFrameworkInferenceTargets` property. This property could be used by creating a `Directory.AfterTargetFrameworkInference.targets` file and putting the following in the `Directory.Build.props` file located in the same folder: + +```xml +$(MSBuildThisFileDirectory)Directory.AfterTargetFrameworkInference.targets +``` + +#### Pros: + +- Simple to implement +- Low compat and performance impacts +- Matches existing `BeforeTargetFrameworkInferenceTargets` property + +#### Cons: + +- Doesn’t match existing `Directory.Build.props` and `Directory.Build.targets` pattern, or the general principle of the SDK which is to have sensible convention-based defaults that can be overridden + +### New Automatically Imported .targets File + +Automatically find and import a `Directory.AfterTargetFrameworkInference.targets` file. + +#### Pros: + +- Matches existing convention for `Directory.Build.props` and `Directory.Build.targets` +- Probably closest to what developers would expect + +#### Cons: + +- Possible performance impact +- Adds another way for a build to “leak out” of a repo root (which could be a security issue) +- More extension points may be needed in the future, and we probably don’t want to add a new auto-imported file for each one + +A possible mitigation for some of the cons could be to say that we don’t look for the new file to import independently. Rather, we could say that you need to have a `Directory.Build.props` file and it needs to be in the same folder as `Directory.Build.props`. So a `Directory.AfterTargetFrameworkInference.targets` file outside the repo root wouldn’t automatically be imported unless a `Directory.Build.props` file was going to be imported from outside the repo anyway. + +### Change import location of `Directory.Build.targets` + +Change`Directory.Build.targets` to be imported after TargetFramework parsing but before workloads and most of the .NET SDK and common targets. This would be a big breaking change but might not affect most people who use `Directory.Build.targets`. It would break people who use `Directory.Build.targets` to override properties or targets in the MSBuild common targets. + +#### Pros: + +- Logic to enable workloads in `Directory.Build.targets` would “just work”, which is likely what developers expect + +#### Cons + +- This would be a breaking change, possibly a very big one +- Implementation would be somewhat complicated: + - Logic would be split betweenthe .NET SDK and MSBuild, as the .NET SDK would now be responsible for importing `Directory.Build.targets` for projects that use the .NET SDK, but other projects would still need MSBuild to import `Directory.Build.targets` + - The new behavior would need to be behind a change wave and ideally a separate opt-out flag +- This goes against the original intent behind `Directory.Build.targets`, which was that it would be imported after all built-in targets. However, the .NET SDK (or any MSBuild SDK) already break this. + - As part of this work we could possibly add another extension point that allows you to specify `.targets` files that are imported after all other projects or imports. This would likely be an MSBuild engine feature. From 5d99299aac40bd737d02b801a5d760f2658bd227 Mon Sep 17 00:00:00 2001 From: Sarah Oslund Date: Fri, 4 Jun 2021 15:16:51 -0700 Subject: [PATCH 2/6] Adding project import picture --- .../solution-explorer-project-imports.png | Bin 0 -> 23260 bytes .../2021/workloads/workload-target-imports.md | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 accepted/2021/workloads/solution-explorer-project-imports.png diff --git a/accepted/2021/workloads/solution-explorer-project-imports.png b/accepted/2021/workloads/solution-explorer-project-imports.png new file mode 100644 index 0000000000000000000000000000000000000000..e0f36493599631299507c54993829b28fc144f47 GIT binary patch literal 23260 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 literal 0 HcmV?d00001 diff --git a/accepted/2021/workloads/workload-target-imports.md b/accepted/2021/workloads/workload-target-imports.md index aaebb317f..59b140938 100644 --- a/accepted/2021/workloads/workload-target-imports.md +++ b/accepted/2021/workloads/workload-target-imports.md @@ -34,7 +34,7 @@ There are many .props and .targets filed that get imported when building a .NET It is also possible to explore the project imports in the Visual Studio Solution Explorer. Clicking on the "Show All Files" button will add an "Imports" node to the solution tree under the project. You can expand this node and explore the tree of imports active in the project. You can also double click on an imported file to open it up in the editor and view its contents. -![Project Imports in Solution Explorer](solution-explorer-project-imports.png) +![Project Imports in Solution Explorer](./solution-explorer-project-imports.png) #### Order of Imports in the .NET SDK From e75502e7b9c36794c5518e49a83f3b5f32b029ca Mon Sep 17 00:00:00 2001 From: Immo Landwerth Date: Mon, 7 Jun 2021 09:29:29 -0700 Subject: [PATCH 3/6] Add separator --- accepted/2021/workloads/workload-target-imports.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accepted/2021/workloads/workload-target-imports.md b/accepted/2021/workloads/workload-target-imports.md index 59b140938..1833c62b2 100644 --- a/accepted/2021/workloads/workload-target-imports.md +++ b/accepted/2021/workloads/workload-target-imports.md @@ -1,6 +1,6 @@ # .NET Workloads Target Import Ordering -**Owners** [Sarah Oslund](https://github.com/sfoslund) [Daniel Plaisted](https://github.com/dsplaisted) +**Owners** [Sarah Oslund](https://github.com/sfoslund) | [Daniel Plaisted](https://github.com/dsplaisted) ## Background From a00b933431ae6beec0b2480a0a5679c6580f19c1 Mon Sep 17 00:00:00 2001 From: Immo Landwerth Date: Mon, 7 Jun 2021 11:20:21 -0700 Subject: [PATCH 4/6] Update index --- INDEX.md | 1 + 1 file changed, 1 insertion(+) diff --git a/INDEX.md b/INDEX.md index 0d7da56fa..d4b02fe45 100644 --- a/INDEX.md +++ b/INDEX.md @@ -70,6 +70,7 @@ Use update-index to regenerate it: | 2020 | [Target Framework Names in .NET 5](accepted/2020/net5/net5.md) | [Immo Landwerth](https://github.com/terrajobst) | | 2020 | [Type for holding & converting binary data](accepted/2020/binary-data/binary-data.md) | [Immo Landwerth](https://github.com/terrajobst) | | 2021 | [.NET 6.0 Target Frameworks](accepted/2021/net6.0-tfms/net6.0-tfms.md) | [Immo Landwerth](https://github.com/terrajobst) | +| 2021 | [.NET Workloads Target Import Ordering](accepted/2021/workloads/workload-target-imports.md) | [Sarah Oslund](https://github.com/sfoslund), [Daniel Plaisted](https://github.com/dsplaisted) | | 2021 | [Compile-time source generation for strongly-typed logging messages](accepted/2021/logging-generator.md) | [Maryam Ariyan](https://github.com/maryamariyan), [Martin Taillefer](https://github.com/geeknoid) | | 2021 | [Objective-C interoperability](accepted/2021/objectivec-interop.md) | [Aaron Robinson](https://github.com/AaronRobinsonMSFT) | | 2021 | [Preview Features](accepted/2021/preview-features/preview-features.md) | [Immo Landwerth](https://github.com/terrajobst) | From 516bad33593c86b4d1232e3e0f872bf0e2bf20d0 Mon Sep 17 00:00:00 2001 From: Sarah Oslund Date: Mon, 7 Jun 2021 12:21:57 -0700 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Immo Landwerth --- .../2021/workloads/workload-target-imports.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/accepted/2021/workloads/workload-target-imports.md b/accepted/2021/workloads/workload-target-imports.md index 1833c62b2..7957802c8 100644 --- a/accepted/2021/workloads/workload-target-imports.md +++ b/accepted/2021/workloads/workload-target-imports.md @@ -4,11 +4,11 @@ ## Background -To support .NET SDK workloads, we [changed the order of targets imports](https://github.com/dotnet/sdk/pull/14393) to allow SDK workloads to change property defaults. When we did this, we also changed the import order of some Windows and WPF targets, as we want to make it a workload in the future, which caused a breaking change. It appears that it is not possible to allow workloads to change property defaults and make windows support a .NET SDK workload without introducing a breaking change. As a result, this document explores possible solutions to minimize the user impact. +To support .NET SDK workloads, we [changed the order of targets imports](https://github.com/dotnet/sdk/pull/14393) to allow SDK workloads to change property defaults. When we did this, we also changed the import order of some Windows and WPF targets, as we want to make it a workload in the future, which caused a breaking change. It appears that it is not possible to allow workloads to change property defaults and make support for Windows a .NET SDK workload without introducing a breaking change. As a result, this document explores possible solutions to minimize the user impact. ### Original Ordering Change -Originally, the Windows, WindowsDesktop, and workload targets were imported at the end of Microsoft.NET.Sdk.targets, which was almost the last files imported. However, this was not an appropriate place for the imports if those targets were to override default property values that were set in .NET SDK or MSBuild common targets. The workload needs a chance to set the property if its not already set before the default logic would do so. +Originally, the Windows, WindowsDesktop, and workload targets were imported at the end of `Microsoft.NET.Sdk.targets`, which was almost the last files imported. However, this was not an appropriate place for the imports if those targets were to override default property values that were set in .NET SDK or MSBuild common targets. The workload needs a chance to set the property if its not already set before the default logic would do so. Because of this, the workload targets import [was moved](https://github.com/dotnet/sdk/pull/14393) to come after the target framework parsing. This is because whether a workload is used (and hence needs to be imported) may depend on the target framework or platform, so those conditions should go after the `TargetFramework` has been parsed into the corresponding component properties (`TargetFrameworkIdentifier`, `TargetFrameworkVersion`, `TargetPlatformIdentifier`, and `TargetPlatformVersion`). @@ -26,13 +26,13 @@ Some properties depend on other properties, so the order in which they are decla Likewise, project imports (typically used to import `.targets` or `.props` files) can be conditioned on property values. So if WindowsDesktop targets are imported if `UseWPF` or `UseWindowsForms` is true, then those properties need to be set before the WindowsDesktop project import is evaluated. -The evaluation ordering of MSBuild Targets is usually not important. The order that the Targets are executed is determined by the dependencies between them, not by the order they come in evaluation. The exception to this is that you can override a Target by defining another target with the same name later in evaluation. +The evaluation ordering of MSBuild targets is usually not important. The order that the targets are executed is determined by the dependencies between them, not by the order they come in evaluation. The exception to this is that you can override a target by defining another target with the same name later in evaluation. #### Exploring Import Order -There are many .props and .targets filed that get imported when building a .NET project. One way to see what is imported and in what order is by preprocessing a project, either with the "-pp:" [command line argument](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-command-line-reference?) or with [MSBuild Structured Log Viewer](https://msbuildlog.com/). This will create a single aggregated project file with all project imports expanded inline. +There are many `.props` and `.targets` files that get imported when building a .NET project. One way to see what is imported and in what order is by preprocessing a project, either with the `-pp:` [command line argument](https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-command-line-reference) or with [MSBuild Structured Log Viewer](https://msbuildlog.com/). This will create a single aggregated project file with all project imports expanded inline. -It is also possible to explore the project imports in the Visual Studio Solution Explorer. Clicking on the "Show All Files" button will add an "Imports" node to the solution tree under the project. You can expand this node and explore the tree of imports active in the project. You can also double click on an imported file to open it up in the editor and view its contents. +It is also possible to explore the project imports in the Visual Studio Solution Explorer. Clicking on the **Show All Files** button will add an **Imports** node to the solution tree under the project. You can expand this node and explore the tree of imports active in the project. You can also double click on an imported file to open it up in the editor and view its contents. ![Project Imports in Solution Explorer](./solution-explorer-project-imports.png) @@ -42,15 +42,15 @@ The following is a simplified list of the files that are imported in an SDK-styl - `Directory.Build.props` - Main project file -- Version logic (Microsoft.NET.DefaultAssemblyInfo.targets) -- Output path logic (Microsoft.NET.DefaultOutputPaths.targets) +- Version logic (`Microsoft.NET.DefaultAssemblyInfo.targets`) +- Output path logic (`Microsoft.NET.DefaultOutputPaths.targets`) - Publish profile -- Target Framework parsing (Microsoft.NET.TargetFrameworkInference.targets) +- Target Framework parsing (`Microsoft.NET.TargetFrameworkInference.targets`) - Also appends target framework to output and intermediate paths -- Runtime identifier inference (Microsoft.NET.RuntimeIdentifierInference.targets) +- Runtime identifier inference (`Microsoft.NET.RuntimeIdentifierInference.targets`) - Also appends Runtime Identifier to output and intermediate paths - Workload targets imports -- Language targets (e.g. Microsoft.CSharp.targets) and MSBuild common targets +- Language targets (e.g. `Microsoft.CSharp.targets`) and MSBuild common targets - `Directory.Build.targets` - (Rest of) .NET SDK targets - Old location for Windows, WindowsDesktop, and workload targets imports From fdacd18e70b1581a8eb9eb2ec8dd2190e916b7d8 Mon Sep 17 00:00:00 2001 From: Sarah Oslund Date: Mon, 7 Jun 2021 15:35:11 -0700 Subject: [PATCH 6/6] Updating based on partner sync feedback --- .../2021/workloads/workload-target-imports.md | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/accepted/2021/workloads/workload-target-imports.md b/accepted/2021/workloads/workload-target-imports.md index 7957802c8..60b6ebb11 100644 --- a/accepted/2021/workloads/workload-target-imports.md +++ b/accepted/2021/workloads/workload-target-imports.md @@ -61,9 +61,29 @@ Conceptually, `Directory.Build.targets` is imported after the body of the main p There's not a perfect place to import `Directory.Build.targets`. However, given what we've learned, it may be that the best place to import it is after the TargetFramework parsing, and before the workloads are imported. That way the logic in `Directory.Build.targets` would still be able to depend on the parsed TargetFramework information, but would not be able to override all of the targets and properties set by the .NET SDK and MSBuild common targets that it can today. -## Proposed Solutions +## Proposed Solution: Change import location of `Directory.Build.targets` -The following are the current proposed solutions to the problem outlined above, to be reviewed by the community. +Change`Directory.Build.targets` to be imported after TargetFramework parsing but before workloads and most of the .NET SDK and common targets. This would be a big breaking change but might not affect most people who use `Directory.Build.targets`. It would break people who use `Directory.Build.targets` to override properties or targets in the MSBuild common targets. + +#### Pros: + +- Logic to enable workloads in `Directory.Build.targets` would “just work”, which is likely what developers expect + +#### Cons + +- This would be a breaking change, possibly a very big one +- Implementation would be somewhat complicated: + - Logic would be split betweenthe .NET SDK and MSBuild, as the .NET SDK would now be responsible for importing `Directory.Build.targets` for projects that use the .NET SDK, but other projects would still need MSBuild to import `Directory.Build.targets` + - The new behavior would need to be behind a change wave and ideally a separate opt-out flag +- This goes against the original intent behind `Directory.Build.targets`, which was that it would be imported after all built-in targets. However, the .NET SDK (or any MSBuild SDK) already break this. + - As part of this work we could possibly add another extension point that allows you to specify `.targets` files that are imported after all other projects or imports. This would likely be an MSBuild engine feature. + +#### Proposed Breaking Change Mitigations + +- An opt out property would be implemented to allow for preserving existing behavior. +- We would like to make this change only apply to projects targeting .NET 6 and higher. + +## Alternative solutions ### Extension Point via Property @@ -94,25 +114,7 @@ Automatically find and import a `Directory.AfterTargetFrameworkInference.targets #### Cons: -- Possible performance impact - Adds another way for a build to “leak out” of a repo root (which could be a security issue) - More extension points may be needed in the future, and we probably don’t want to add a new auto-imported file for each one A possible mitigation for some of the cons could be to say that we don’t look for the new file to import independently. Rather, we could say that you need to have a `Directory.Build.props` file and it needs to be in the same folder as `Directory.Build.props`. So a `Directory.AfterTargetFrameworkInference.targets` file outside the repo root wouldn’t automatically be imported unless a `Directory.Build.props` file was going to be imported from outside the repo anyway. - -### Change import location of `Directory.Build.targets` - -Change`Directory.Build.targets` to be imported after TargetFramework parsing but before workloads and most of the .NET SDK and common targets. This would be a big breaking change but might not affect most people who use `Directory.Build.targets`. It would break people who use `Directory.Build.targets` to override properties or targets in the MSBuild common targets. - -#### Pros: - -- Logic to enable workloads in `Directory.Build.targets` would “just work”, which is likely what developers expect - -#### Cons - -- This would be a breaking change, possibly a very big one -- Implementation would be somewhat complicated: - - Logic would be split betweenthe .NET SDK and MSBuild, as the .NET SDK would now be responsible for importing `Directory.Build.targets` for projects that use the .NET SDK, but other projects would still need MSBuild to import `Directory.Build.targets` - - The new behavior would need to be behind a change wave and ideally a separate opt-out flag -- This goes against the original intent behind `Directory.Build.targets`, which was that it would be imported after all built-in targets. However, the .NET SDK (or any MSBuild SDK) already break this. - - As part of this work we could possibly add another extension point that allows you to specify `.targets` files that are imported after all other projects or imports. This would likely be an MSBuild engine feature.