wpf icon indicating copy to clipboard operation
wpf copied to clipboard

Certain MSBuild configuration causes _wpftmp build output directories to not be deleted after each build

Open nathan-alden-hp opened this issue 5 years ago • 34 comments

.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:

WpfApp1.zip

image

nathan-alden-hp avatar Apr 24 '20 01:04 nathan-alden-hp

Just confirming this is still an issue with 3.1.301. Only impacts WindowsDesktop sdk projects. In our case we have a custom <OutputPath>

jonesdwg avatar Jul 03 '20 10:07 jonesdwg

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.

vatsan-madhavan avatar Jul 03 '20 14:07 vatsan-madhavan

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:

image

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(&quot;$(BaseArtifactsPath)bin&quot;,&quot;$(MSBuildProjectName)_*_wpftmp&quot;))"/> </ItemGroup> <RemoveDir Directories="@(WpfTempDirectories)" /> </Target>

jonesdwg avatar Jul 06 '20 09:07 jonesdwg

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...

nathan-alden-hp avatar Jul 06 '20 13:07 nathan-alden-hp

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.

msedi avatar Jul 16 '20 14:07 msedi

This behavior has been driving me nuts as well. This shouldn't be by-design. No other project type does this.

dotMorten avatar Jul 23 '20 20:07 dotMorten

Any news on this?

msedi avatar Jan 30 '21 14:01 msedi

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.

chipplyman avatar Apr 14 '21 18:04 chipplyman

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 (&quot;$(BaseOutputBinPath)*_*_wpftmp&quot;) DO RMDIR /S /Q &quot;%%~G&quot;" />
</Target>

nathan-alden-sr avatar Apr 29 '21 15:04 nathan-alden-sr

Any news on this?

msedi avatar Jan 13 '22 12:01 msedi

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.

HermanEldering avatar Jan 14 '22 01:01 HermanEldering

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.

ThomasGoulet73 avatar Jan 14 '22 05:01 ThomasGoulet73

And there is also an $(OutDir)... which we needed to set to $(OutputPath) to make our things work.

msedi avatar Jan 14 '22 09:01 msedi

@ThomasGoulet73 Those are good points you're making, I'll wait to see how your fix progresses.

HermanEldering avatar Jan 14 '22 09:01 HermanEldering

Any news on this?

msedi avatar Feb 22 '22 10:02 msedi

@msedi It looks like VS2022 will try its best to clean out these folders...

lindexi avatar Mar 03 '22 00:03 lindexi

@lindexi: Thx. Do you know which version. I'm having the latest official version. Are you using the previews?

msedi avatar Mar 03 '22 20:03 msedi

Even with the latest Visual Studio preview the problem still exists (more than 2 years after....) 👎

msedi avatar Jun 10 '22 14:06 msedi

@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.

Wolf-K avatar Jul 06 '22 15:07 Wolf-K

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?

image

msedi avatar Aug 26 '22 11:08 msedi

What is the status?

msedi avatar Feb 02 '23 09:02 msedi

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 avatar Mar 28 '23 17:03 chipplyman

@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.

msedi avatar May 22 '23 07:05 msedi

Any news?

yamithrain avatar Aug 29 '23 13:08 yamithrain

@yamithrain Could you test VisualStudio 2022 17.6.5 ?

lindexi avatar Aug 30 '23 00:08 lindexi

The issue is still present with 17.6.6.

yamithrain avatar Aug 30 '23 08:08 yamithrain

Sad...

lindexi avatar Aug 30 '23 08:08 lindexi

I also still have this problem in the latest preview: 17.8.0 Preview 1.0

msedi avatar Aug 30 '23 19:08 msedi

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).

luttje avatar Oct 11 '23 20:10 luttje

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

AFract avatar Jan 30 '24 16:01 AFract