One of the new features in .NET 8 is the option to simplify the output path and folder structure for build outputs. In this blog, I’ll explain what this feature is, why it is useful, and how to use it. At the time of writing this blog, I am using the .NET 8 SDK Version - 8.0.100-preview.6.23330.14.
What is the output path? 📂
The output path is the location where the build artifacts of your .NET application are stored. Build artifacts include compiled assemblies, configuration files, dependencies, symbols, and other files that are needed to run or debug your application.
What was the problem with the previous output path structure? ❌
Previously, .NET applications produced a deep and complex set of output paths for different build artifacts.
For example, if you had a .NET Core console app named MyApp, targeting netcoreapp3.1 and net5.0 frameworks, and built it in Debug mode, you would get something like this:
MyApp ├── bin │ ├── Debug │ │ ├── netcoreapp3.1 │ │ │ ├── MyApp.dll │ │ │ ├── MyApp.pdb │ │ │ ├── MyApp.runtimeconfig.dev.json │ │ │ ├── MyApp.runtimeconfig.json │ │ │ └── MyApp.deps.json │ │ └── net5.0 │ │ ├── MyApp.dll │ │ ├── MyApp.pdb │ │ ├── MyApp.runtimeconfig.dev.json │ │ ├── MyApp.runtimeconfig.json │ │ └── MyApp.deps.json └── obj ├── Debug │ ├── netcoreapp3.1 │ │ ├── MyApp.AssemblyInfo.cs │ │ ├── MyApp.AssemblyInfoInputs.cache │ │ ├── MyApp.assets.cache │ │ ├── MyApp.csproj.FileListAbsolute.txt │ │ └── project.assets.json │ └── net5.0 │ ├── MyApp.AssemblyInfo.cs │ ├── MyApp.AssemblyInfoInputs.cache │ ├── MyApp.assets.cache │ ├── MyApp.csproj.FileListAbsolute.txt │ └── project.assets.json └── project.nuget.cache
As you can see, this structure is quite verbose and redundant. It also has some drawbacks, such as:
It makes it harder to find and manage the output files, especially if you have a large solution with many projects and target frameworks.
It increases the size of the output folder, as it contains duplicate files for shared dependencies across different target frameworks.
It slows down the build process, as it requires copying files across different subfolders.
How does the new output path structure simplify things? ✅
The new output path structure gathers all build outputs into a common location, which makes it easier for tooling to anticipate. It also reduces the verbosity and redundancy of the previous structure.
To opt into the new output path format, you need to run the following command in order to create the Directory.Build.props file.
dotnet new buildprops --use-artifacts
For example, if you enable the simplified output path feature for the same MyApp console app using the command above, you would get something like this:
The directory listing tree will be something similar to the following.
MyApp ├───artifacts │ ├───bin │ │ └───MyApp │ │ └───debug │ │ MyApp.deps.json │ │ MyApp.dll │ │ MyApp.exe │ │ MyApp.pdb │ │ MyApp.runtimeconfig.json │ └───obj │ └───MyApp │ │ MyApp.csproj.nuget.dgspec.json │ │ MyApp.csproj.nuget.g.props │ │ MyApp.csproj.nuget.g.targets │ │ project.assets.json │ │ project.nuget.cache │ │ │ └───debug │ │ .NETCoreApp,Version=v8.0.AssemblyAttributes.cs │ │ apphost.exe │ │ MyApp.AssemblyInfo.cs │ │ ...
Essentially, it created an artifacts directory under the MyApp directory and generated all build files inside that. You can customize the path by editing the property
ArtifactsPath in Directory.Build.props file.
<Project> <!-- See https://aka.ms/dotnet/msbuild/customize for more details on customizing your build --> <PropertyGroup> <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath> </PropertyGroup> </Project>
As you can see, this structure is much simpler and cleaner. It also has some benefits, such as:
It makes it easier to find and manage the output files, as they are all in one place.
It reduces the size of the output folder, as it eliminates duplicate files for shared dependencies across different target frameworks.
It speeds up the build process, as it avoids copying files across different subfolders.
Once you have enabled the simplified output path feature, you can use it as usual. You can build your solution, run your projects, debug them, test them, and deploy them. The only difference is that all your output files will be in the same folder.
You can also use the simplified output path feature with other features and tools that work with .NET 8, such as:
Publishing: You can publish your projects to various platforms and destinations using the
dotnet publishcommand or the Publish tool in Visual Studio. The published files will be placed in the same output folder as well.
Packaging: You can package your projects into NuGet packages using the
dotnet packcommand or the Pack tool in Visual Studio. The package files will be placed in the same output folder as well.
Testing: You can run your tests using the
dotnet testcommand or the Test Explorer in Visual Studio. The test results will be placed in the same output folder as well.
Code analysis: You can run code analysis tools such as Roslyn analyzers or StyleCop on your projects using the
dotnet analyzecommand or the Code Analysis tool in Visual Studio. The analysis results will be placed in the same output folder as well.
The simplified output path feature is a new option that you can enable in .NET 8 to specify a single output path for all your projects. This can make your development workflow simpler, more organized, faster, and more compatible with other features and tools.
I hope you found this blog helpful and informative. If you have any questions or feedback, please leave a comment below.