Certain MSBuild configuration causes _wpftmp build output directories to not be deleted after each build
.NET Core 3.1.201 Windows 10 build 18363
Problem description:
Certain MSBuild configuration causes the compiler to leave behind directories with a _wpftmp suffix after a build completes. The problem seems to be related to custom <BaseOutputPath>s.
Actual behavior:
Temporary output directories are left behind after every build. A new directory is created per build.
Expected behavior:
Temporary output directories are deleted after every build.
Minimal repro:

Just confirming this is still an issue with 3.1.301. Only impacts WindowsDesktop sdk projects. In our case we have a custom <OutputPath>
This is not a bug; By Design AFAICT.
Deleting build artifacts generated by the temp projects had never been a goal.
The only goal has been deleting the *wpftmp.csproj itself, because it is created alongside the original project inside the cone of the sources directory.
Depending on various build configuration choices, the output directories can be named after either (a) the target assembly or (b) the project name (or something else, which is uncommon).
When (a) is in effect, the *wpftmp.csproj project will simply happen to use the same folders under $(Obj) and $(Bin) as the original project - they happen to share the same target assembly name by design.
When (b) is in effect, msbuild will provision *wpftmp.csproj with its own folders under $(Obj) and $(Bin) based on the project name.
This works the same in .NET Framework and .NET Core; in SDK style projects and in older style projects.
That's interesting, thanks. In our case however this does only seems to be impact WindowsDesktop sdk projects.
I've modified the solution provided by @nathan-alden-hp to include a wpf .net framework project and .net core console app: repro.zip
These all pick up their output path from the Directory.build.props, but it's only the sdk style WindowsDesktop project that leaves behind the temporary directories:

From what I understand I'm probably falling into scenario b as both the output path and intermediate path are modified based on the $(MSBuildProjectName)
<BaseArtifactsPath>$(MSBuildThisFileDirectory)artifacts\</BaseArtifactsPath><OutputPath>$(BaseArtifactsPath)bin\$(MSBuildProjectName)\</OutputPath> <BaseIntermediateOutputPath>$(BaseArtifactsPath)obj\$(MSBuildProjectName)\</BaseIntermediateOutputPath>
NB: I only see these extra directories left over in the bin directory, the obj directory is behaving as expected.
This does feel like a bug (or at least undesirable behaviour) and only started happening once we migrated our WPF apps to SDK style projects.
That said, I can work around it easily enough, by adding a post build step in Directory.build.targets to delete the directories:
<Target Name="RemoveWpfTemp" AfterTargets="Build"> <ItemGroup> <WpfTempDirectories Include="$([System.IO.Directory]::GetDirectories("$(BaseArtifactsPath)bin","$(MSBuildProjectName)_*_wpftmp"))"/> </ItemGroup> <RemoveDir Directories="@(WpfTempDirectories)" /> </Target>
I'm not sure why it should even be "by design" for a build system to repeatedly generate new directories that appear to never be cleaned or wiped...
Honestly, I also don't agree that temporary files and directories will not be deleted after the build has finished. Also it is not deterministic when and why this happens. We changed some small detail in the project and we even don't know which option we have changed that it came to this behavior.
This behavior has been driving me nuts as well. This shouldn't be by-design. No other project type does this.
Any news on this?
I confirm that this bug also affects non-SDK .NET Framework projects, as suggested by the last paragraph of vatsan-madhavan's comment from Jul 3, 2020.
I disagree with the assessment that it is not a bug. Even if the implementation is operating as designed, that merely shifts the root cause of the bug from the implementation to the design. This arguably makes it more a severe bug since it could have been caught earlier in the production pipeline and was not.
I created this post-build target to deal with these folders:
<!--
Delete temporary compiler output caused by this bug:
https://github.com/dotnet/wpf/issues/2930
-->
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="FOR /D %%G in ("$(BaseOutputBinPath)*_*_wpftmp") DO RMDIR /S /Q "%%~G"" />
</Target>
Any news on this?
I'm running into this issue as well. For those that want to investigate the issue themselves I believe this is where the problem originates and where the files but not directories are deleted: GenerateTemporaryTargetAssembly
The way I've worked around is by adding an extra <OutputPath> in my directory.build.props file, which has a condition like this:
<OutputPath Condition="$(MSBuildProjectName.EndsWith('_wpftmp'))">
$(OutputBase)\Temp\tmpwpf\$(MSBuildProjectName)
</OutputPath>
Then there is a task to delete my entire tmpwpf folder so no artifacts are left behind.
Perhaps this is also a good solution for the WPF team? Just store the wpftmp projects somewhere else and delete that entire folder, instead of enumerating the files and deleting each one. It could be just a special subdirectory in the BaseIntermediateOutputPath or perhaps even a random directory in %temp%.
There are some other places in MSBuild that use the generated project name, so even if it is inside a new random directory the project name should perhaps remain like it is.
OutputPath is never used, it uses IntermediateOutputPath to get the path of the assembly to build in GenerateTemporaryTargetAssembly. The folder created at OutputPath is created by PrepareForBuild but from what I can see, it's never used. This is the same bug as #5711. The problem is that it's evaluating properties with different values than the main build. We can't really evaluate every properties before the build in GenerateTemporaryTargetAssembly because it would break other projects that rely on the evaluation of properties inside GenerateTemporaryTargetAssembly . I have a potential fix that I'm experimenting with right now. It's working but I have to do more tests to make sure that I'm not breaking anything.
And there is also an $(OutDir)... which we needed to set to $(OutputPath) to make our things work.
@ThomasGoulet73 Those are good points you're making, I'll wait to see how your fix progresses.
Any news on this?
@msedi It looks like VS2022 will try its best to clean out these folders...
@lindexi: Thx. Do you know which version. I'm having the latest official version. Are you using the previews?
Even with the latest Visual Studio preview the problem still exists (more than 2 years after....) 👎
@lindexi: We are using VS 2022 17.2.5 and just noticed that our project folders are also polluted by random _wpftmp project files. Unfortunately all these files are also published to Github.
Obviously the problem still isn't solved. Within 7 days my directory for one wpf library is polluted with 186 wpftmp folder. Is there any progress now?

What is the status?
I successfully worked around this issue by replacing:
<OutputPath>some\path\including\$(MSBuildProjectName)\</OutputPath>
with
<OutputDirectory>$(MSBuildProjectName)</OutputDirectory>
<OutputDirectory Condition=" '$(AssemblyName)' != '' ">$(AssemblyName)</OutputDirectory>
<OutputPath>some\path\including\$(OutputDirectory)\</OutputPath>
Obviously this will not work for you if you need $(AssemblyName) to differ from $(MSBuildProjectName).
@chipplyman: You are right, I need the $(AssemblyName) otherwise I would get too many write problems since with the multithreaded built to many files of the same name would be copied into the global target directory.
Any news?
@yamithrain Could you test VisualStudio 2022 17.6.5 ?
The issue is still present with 17.6.6.
Sad...
I also still have this problem in the latest preview: 17.8.0 Preview 1.0
I also ran into this issue. I found the problem to be caused by:
<BaseOutputPath>..\..\bin\$(MSBuildProjectName)</BaseOutputPath>
In order to fix those *_wpftmp folders being created I've resorted to using this instead:
<BaseOutputPath>..\..\bin\$(AssemblyName)</BaseOutputPath>
I'm unaware of any side-effects with this configuration. Everything seems to work fine.
More (possibly relevant) information about my setup:
- SDK-style project file
- NET Framework 4.8
- WPF UserControl
- Output type:
<OutputType>Library</OutputType> - References:
<Reference Include="PresentationCore" /> <Reference Include="PresentationFramework" /> <Reference Include="System.Xaml" /> <Reference Include="WindowsBase" />
Edit: Only after posting this did I notice someone above already mentioned this workaround:
I successfully worked around this issue by replacing:
<OutputPath>some\path\including\$(MSBuildProjectName)\</OutputPath>with<OutputDirectory>$(MSBuildProjectName)</OutputDirectory><OutputDirectory Condition=" '$(AssemblyName)' != '' ">$(AssemblyName)</OutputDirectory><OutputPath>some\path\including\$(OutputDirectory)\</OutputPath>Obviously this will not work for you if you need
$(AssemblyName)to differ from$(MSBuildProjectName).
I am here due to the same problem (.Net 7.0, VS2022 17.8.5). I have thousands of those *wpftmp folders in my build folder... I have a directory.build.props in my solution, I guess it is indirectly related. Any structural update on it ? Thank you