86

I'm trying to write a MSBuild Task that deletes the Obj directory and PDBs from my bin folder on my production build scripts and can't seem to get it to work right.

Does anyone have an example where they do this or similar, or a link to a simple example of removing files and a directory with MSBuild?

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258

8 Answers8

143

You can delete the files in those directories first and then the dir itself with

<Target Name="SomeTarget">
    <ItemGroup>
        <FilesToDelete Include="Path\To\Obj\**\*"/>
    </ItemGroup>   
    <Delete Files="@(FilesToDelete)" />   
    <RemoveDir Directories="Path\To\Obj\" />
</Target>
SliverNinja - MSFT
  • 31,051
  • 11
  • 110
  • 173
Sayed Ibrahim Hashimi
  • 43,864
  • 17
  • 144
  • 178
  • 1
    @pylover where I have Path\To\Obj\**\\* the path should be a real folder path; for example C:\temp\folder to delete. Could that be the reason it doesn't work for you? Can you give more details why it's not working? – Sayed Ibrahim Hashimi Aug 21 '12 at 05:01
  • 1
    Using 2012, I've just verified that RemoveDir does what docs says - removes files and folder. Shouldn't need the extra steps. – dudeNumber4 Oct 30 '12 at 20:30
  • @SayedIbrahimHashimi sorry for the direct approach, but I have your book and still I can't figure a way round this problem. http://stackoverflow.com/questions/25525197/exclude-js-files-but-not-min-js-files-from-msbuild-publish . Can you help? – Blowsie Aug 27 '14 at 11:13
101

If you're looking to delete an entire directory you require the RemoveDir task:

<RemoveDir Directories="Path/To/Obj" />

And if you're wanting to delete the PDB files from bin you'll want the Delete task:

<Delete Files="Path/To/Bin/MyApp.pdb" />

Note that you cannot use wildcards in the Delete task, so if you have multiple pdb files you're have to provide an ItemGroup as an argument.

Gavin Miller
  • 43,168
  • 21
  • 122
  • 188
  • 8
    The *RemoveDir* task reference page (see above) says all files and subdirectories will be deleted. – Yawar Sep 27 '09 at 02:17
  • I wish I could mark both of your answers because the link to the RemoveDir task showed me what was really missing was I that I didn't dereference my OutputPath property for finding the right directory. Sayeds response with the ItemGroup syntax was what I needed to do to grab all of the pdb's to delete them. Thank you both! – Chris Marisic Oct 11 '09 at 17:48
  • 1
    Works for me, so thanks, but its better to add `ContinueOnError="true"` attribute , for first rebuild – pylover Aug 20 '12 at 06:56
  • 1
    Sidenote - if path is longer than 260 characters the RemoveDir will fail; and you have to use Exec with rmdir instead. – Ondrej Svejdar Jan 08 '16 at 13:59
  • I've been playing with the `` task and noticed that when I do the build from Visual Studio the files are deleted and I see that information in the output. But when I run it has an msbuild command line the files are *not* deleted and I see nothing about it in the output, regardless of verbosity. – Howiecamp Dec 12 '17 at 20:50
20

Posting for others that might have ran into the same problem I was having.

The Delete task cannot delete readonly files, which I needed to be able to do, as when MSBuild gets latest from TFS, the files are marked as readonly. I used the EXEC command to delete readonly files:

<ItemGroup>
    <FileToDelete Include="c:\temp\fileToDelete.txt"/>
</ItemGroup>
<Exec Command="del /F /Q &quot;@(FileToDelete)&quot;"/>
Garry English
  • 5,070
  • 1
  • 36
  • 23
  • This is the only thing that would work for me when the files are read only! (e.g. checked out from TFS) :) Thanks. – Tod Thomson Mar 25 '14 at 05:01
  • 1
    I had an ItemGroup with wildcards that expanded to so many files (full paths) that the `del` command line got too long. My solution was to pass the wildcards to `del` directly rather than use an ItemGroup. – yoyo Jun 13 '16 at 17:33
13

The posted answers will work as long as you have to deal with a single directory. If you happen to have nested folders, RemoveDir will fail with Directory not empty error.

A slightly generic approach takes care of nested folders as well:

<Target Name="CleanOutDir">
    <ItemGroup>
        <FilesToClean Include="$(OutDir)\**\*.*" />

        <!-- Bit of .Net to get all folders and subfolders -->
        <FoldersToClean Include="$([System.IO.Directory]::GetDirectories(&quot;$(OutDir)&quot;))" />
    </ItemGroup>

    <Delete Files="@(FilesToClean)"/>
    <RemoveDir Directories="@(FoldersToClean)" />
</Target>
Mrchief
  • 75,126
  • 20
  • 142
  • 189
5

This code is so ugly it should come with an airsickness bag. ;-) But it is fast because it doesn't build a list of files to delete etc.

<Target Name="DeleteBuildFolder">
    <Exec Command="RmDir /S /Q &quot;$(BuildFolder)&quot;" />
    <Exec Command="RmDir /S /Q &quot;$(BuildFolder)&quot;" />
    <Exec Command="RmDir /S /Q &quot;$(BuildFolder)&quot;" />
    <Exec Command="RmDir /S /Q &quot;$(BuildFolder)&quot;" />
    <Exec Command="RmDir /S /Q &quot;$(BuildFolder)&quot;" />
    <Exec Command="RmDir /S /Q &quot;$(BuildFolder)&quot;" />
    <Exec Command="RmDir /S /Q &quot;$(BuildFolder)&quot;" />
</Target>

How many RmDir commands are needed? Enough so a few RmDir commands return "The system cannot find the file specified" instead of "The directory is not empty." On my machine it seems to take another RmDir if $(BuildFolder) is open in Windows Explorer. The antivirus program may affect RmDir like it occasionally does Subversion but I'd rather have blanket AV protection than (mis)manage an exclusion list.

Mark Jerde
  • 672
  • 1
  • 7
  • 12
4

It is also possible to first remove the readonly property from the file and to execute the msbuild delete Task.

Like so:

<Target Name="DeleteFiles">
 <Message Text="Delete File" Importance="high"/>
 <Attrib Files="$(FileToDelete)" ReadOnly="false" />
 <Delete Files="$(FileToDelete)" />
</Target>`
kprobst
  • 16,165
  • 5
  • 32
  • 53
Ole
  • 41
  • 2
  • 2
    It looks as though the Attrib task referenced here is part of the [MSBuild Community Tasks](https://github.com/loresoft/msbuildtasks) project. – Scott Munro Jan 29 '13 at 10:25
4

In Visual Studio 2013, added this to the end of my .csproj file just before the </Project> closing tag

<Target Name = "clean_folders" AfterTargets="Clean">
 <Exec Command = "rd /S /Q obj" />
 <Exec Command = "rd /S /Q bin" />
</Target>

At first it didn't appear to work but I noticed that Visual Studio (or R#, not sure) re-re-added DesignTimeResolveAssemblyReferencesInput.cache to the obj folder and it also re-added the current \bin folder (I have different builds in different subfolders under \bin). It cleaned away everything else, including the 25 other build configs I have from imported .csproj files (yes, I know).

Be careful if you Batch Rebuild more than one config as it just wipes all previous efforts on each rebuild leaving you with only the last one. Whups.

CAD bloke
  • 8,578
  • 7
  • 65
  • 114
2

Just to add one more wrinkle that I've discovered. I'm using Visual Studio 2015. The posted answers that are deleting via wildcard are still troublesome for me. I suspect that the wildcards are evaluated before the build, not after. That means the deletions won't happen if the files you want to delete are created during the build. It also leads to wonderful behavior where the delete works every second time you build, which makes this all very enjoyable to test.

I'm giving up on wildcards. For what I'm doing I know the files that are causing trouble and I'm hard-coding (if it can be called that inside of a project file) the actual file names.

rrreee
  • 753
  • 1
  • 6
  • 20
  • 2
    If the ItemGroup using the wildcards is inside the Target, it should be evaluated each time the Target is executed. ItemGroups at the project level are indeed evaluated when the Project is executed. – Zastai Nov 11 '16 at 09:30