-
Notifications
You must be signed in to change notification settings - Fork 162
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
Add proposal to simplify output paths for .NET 8 #278
Changes from all commits
5875dae
927595c
929ecbd
f32ac12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
# Simplify .NET Output Paths | ||
|
||
We'd like to simplify the output paths for .NET 8. | ||
|
||
Currently, the default output path for a .NET project includes folders for the `TargetFramework` and the `RuntimeIdentifier` (if it is set). Additionally, the publish output goes in a `publish` subfolder of the output folder. The resulting paths are as follows: | ||
|
||
- `bin\<Configuration>\<TargetFramework>` - Build output with no `RuntimeIdentifier` | ||
- `bin\<Configuration>\<TargetFramework>\<RuntimeIdentifier>` - Build output with `RuntimeIdentifier` | ||
- `bin\<Configuration>\<TargetFramework>\publish` - Publish output with no `RuntimeIdentifier` | ||
- `bin\<Configuration>\<TargetFramework>\<RuntimeIdentifier>\publish` - Publish output with `RuntimeIdentifier` | ||
|
||
This makes it harder to find the output of a project, and sometimes nests one type of output in another output folder. | ||
|
||
## Proposed behavior | ||
|
||
Projects targeting .NET 8 and higher will by default not include the `TargetFramework` in the output path unless the project is multi-targeted. Similarly, the `RuntimeIdentifier` will not by default be part of the build output path. Finally, the publish output path will be `bin\publish` by default instead of a `publish` subfolder inside of the output path. | ||
|
||
These would be the default output paths for .NET 8 projects: | ||
|
||
- `bin\<Configuration>` - Build output path for single targeted project | ||
- `bin\<Configuration>\<TargetFramework>` - Build output for multi-targeted project | ||
- `bin\publish\<RuntimeIdentifier>` - Publish output for single targeted project | ||
- `bin\publish\<TargetFramework>\<RuntimeIdentifier>` - Publish output for multi-targeted project | ||
|
||
Note that the publish output paths would only apply when publishing the Release Configuration, which we plan to make the default configuration for `dotnet publish` in .NET 8. | ||
|
||
## Implementation | ||
|
||
There are currently properties that control whether the TargetFramework and RuntimeIdentifier should be included in the output path: `AppendTargetFrameworkToOutputPath` and `AppendRuntimeIdentifierToOutputPath`. Both of these currently default to true. We will change the default values as follows: | ||
|
||
- `AppendTargetFrameworkToOutputPath` will default to true if the `TargetFrameworks` property is set (as multi-targeted projects still need different output paths for each TargetFramework), or the project is targeting .NET 7 or lower. Otherwise it will default to false. | ||
- `AppendRuntimeIdentifierToOutputPath` will default to true only when targeting .NET 7 or lower. | ||
- To be discussed: Should we also default it to true if `RuntimeIdentifiers` is set? | ||
|
||
For publish, we would change the publish output path only when targeting .NET 8 or higher and when the Configuration is set to Release (which we [plan to make the default](https://github.com/dotnet/sdk/issues/27066) for .NET 8). In that case we would set the publish output path to `$(BaseOutputPath)\publish` (by default `bin\publish`), and append the `TargetFramework` to the path based on `AppendTargetFrameworkToOutputPath` property. The `RuntimeIdentifier` will always be appended to the publish output path, as is the case today. | ||
|
||
## Considerations | ||
|
||
### Breaking changes | ||
|
||
These changes could break things such as scripts that copy the output of the build or custom MSBuild logic that hard-codes these paths. Tieing these changes to the project TargetFramework ensures that these breaks will be encountered when the project is modified to target a new TargetFramework, not when updating to a new version of the .NET SDK. | ||
|
||
### Why have different output paths at all? | ||
|
||
It is worth considering why we have different output paths at all. The advantage of having different output paths for some pivot (such as configuration, target framework, or RuntimeIdentifier) is that you can keep outputs for the different values of the pivot side-by-side, and that if you are switching back and forth between building them, the builds for a given pivot value can be incremental. | ||
|
||
Therefore, the assumption with these changes is that it is not common to switch back and forth between different target frameworks or different runtime identifiers. Projects where it is common to switch back and forth could still set `AppendTargetFrameworkToOutputPath` or `AppendRuntimeIdentifierToOutputPath` to true. | ||
|
||
Some project types may set `AppendRuntimeIdentifierToOutputPath` to true by default. This will be the case for iOS projects, where different RuntimeIdentifiers are used for targeting a simulator versus a device, and it is common to switch back and forth. | ||
|
||
For publish, we believe it is more common to switch between different RuntimeIdentifiers, so we will continue to include the `RuntimeIdentifier` in the publish output path. | ||
|
||
### Publish versus Configuration | ||
|
||
Putting the publish output in the `bin\publish` folder may appear to conflate publish with a Configuration. This, however, is intentional. We'd like to make publish behave more like a configuration. | ||
|
||
Some factors influencing this: | ||
|
||
- It is rarely correct to publish with the Debug configuration, as publish is used to create artifacts that are deployed. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would just note here that In fact, publishing such builds in I don't think you can just turn |
||
- Currently, there are operations that are only supported during publish, such as `PublishSingleFile`, `PublishTrimmed`, and `PublishAot`. It would be more flexible to allow these to be specified for Build also. | ||
- There's not a good way to set MSBuild properties differently for the Publish operation. This makes it hard to implement properties such as PublishSingleFile and PublishRuntimeIdentifier, and means that those properties will work with `dotnet publish` but not `dotnet build /t:Publish`. Developers may also want to condition properties on whether the Publish operation is being run. On the other hand, conditioning properties based on Configuration is trivial. | ||
|
||
We've been thinking about Publish versus Build for a while. Here's some of that background: | ||
|
||
- https://github.com/dotnet/sdk/issues/26446 | ||
- https://github.com/dotnet/sdk/issues/26247 | ||
- https://github.com/dotnet/core/issues/7566 | ||
- https://github.com/dotnet/docs/issues/30023 | ||
- https://gist.github.com/dsplaisted/f032de83be1dda7e14ca77f350100065 | ||
- https://github.com/dotnet/sdk/issues/15726 | ||
|
||
### Incremental publish | ||
|
||
Originally, the .NET SDK did not support incremental publish. This meant that if you published for one RuntimeIdentifier, and then published for another RuntimeIdentifier using the same publish path, the files from the first RuntimeIdentifier that should not have been included in the second publish would still be left in the folder. Probably because of this, the RuntimeIdentifier is [always added to the publish output path](https://github.com/dotnet/sdk/blob/efef23ab729388ffb081731e5b1adbabc6e6b327/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.BeforeCommon.targets#L122-L126) regardless of the value for `AppendRuntimeIdentifierToOutputPath`. However, we now [support incremental publish](https://github.com/dotnet/sdk/pull/3957), so this isn't a concern anymore. | ||
|
||
### Capitalization | ||
|
||
The default publish output path could either be `bin\publish` or `bin\Publish`. Uppercase matches the Configuration values of `Debug` and `Release`, which the publish folder will be a sibling of. However, the current publish folder capitalization is lowercase. The current plan is to go with the lowercase `bin\publish`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way, or can we add a way, to get the paths, so scripts can programatically get these directories rather than hardcode them? For example
$publishPath = & dotnet publish -f net8.0 -r linux-x64 --output-path
? Is there already a target that we can use, something like$publishPath = & dotnet msbuild -t:GetPublishPath -p:TargetFramework=net8.0 -p:RuntimeIdentifer=linux-x64
?If msbuild doesn't already have a feature to output the value of a single property after an evaluation (and running a target?), maybe that would be a useful feature?
$publishPath = & dotnet msbuild --output-property PublishDir -p:TargetFramework=net8.0 -p:RuntimeIdentifer=linux-x64
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's a direct way for MSBuild to return a value to a script that calls it.
Targets can return properties or items, and this is used by Visual Studio to get information from the build. Maybe it would be an interesting feature to support some way of doing that from the command line (that would be separate from this proposal though). @rainersigwald, what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dotnet/msbuild#3911 tracks some options there, which I'm not opposed to but I don't see a nice elegant solution for it at the moment, so it hasn't gotten a ton of traction. Feedback welcome!