Recursive Delete - A real Clean build
I'm having trouble recursively deleting through MSBuild. All the examples I've seen of the RemoveDir Task assume a single folder as the input. This is useless to me because the project I'm building a web project so the root deployment folder is always locked by IIS. Here's my folder structure:
- Build Root
- Version
- Source
- Compiled
- Deploy
- Archive
Build Root: where the msbuild file sits
Version: Container folder for Source & Compiled
Source: Latest uncompiled source code from version control (Subversion)
Compiled: Target for compilation of Source
Deploy: Website virtual root
Archive: Zip archives of previous successful buildsThe trouble is the Deploy folder. I am able to easily delete every file in Deploy using the Delete Task and an ItemGroup array but I cannot seem to do the same thing for folders.
<ItemGroup>
<DeployCleanFiles Include="$(DeployRoot)\**\*.*" />
<DeployCleanFolders Include="$(DeployRoot)\**" />
</ItemGroup><Target Name="Deploy">
<Delete Files="@(DeployCleanFiles)" />
<RemoveDir Directories="@(DeployCleanFolders)" />
<CreateItem Include="$(VersionCompiled)\**\*.*">
<Output TaskParameter="Include" ItemName="DeploySourceFiles" />
</CreateItem>
<Copy
SourceFiles="@(DeploySourceFiles)"
DestinationFolder="$(DeployRoot)\%(DeploySourceFiles.RecursiveDir)"
ContinueOnError="true"
/>
</Target>
Answers
Ultimately there's no good way to get a directory listing without writing a custom task. I'll open a bug on this.
Here's an admittedly hacky way to do this w/o writing a custom task, using the Exec command:<PropertyGroup>
<DeployRoot>.\Deploy</DeployRoot>
</PropertyGroup>
<!-- this item will contain all FILES underneath $(DeployRoot) -->
<ItemGroup>
<DeployCleanFiles Include="$(DeployRoot)\**\*.*" />
</ItemGroup>
<Target Name="Deploy">
<Message Text="Clean Folders @(DeployCleanFolders)" />
<Delete Files="@(DeployCleanFiles)" />
<Exec Command="for /f %%d in ('dir /ad /b') do rd /s /q %%d"
WorkingDirectory="$(DeployRoot)" />
<CreateItem Include="$(VersionCompiled)\**\*.*">
<Output TaskParameter="Include" ItemName="DeploySourceFiles" />
</CreateItem>
<Copy
SourceFiles="@(DeploySourceFiles)"
DestinationFolder="$(DeployRoot)\%(DeploySourceFiles.RecursiveDir)"
ContinueOnError="true"
/>
</Target>
All Replies
Hi monsoondawn,
What problem are you having? Are some but not all folders deleted? Are you seeing an error? A snippet from your build log while it's building the "Deploy" target would also help me figure out what's happening.
Thanks,
jeff.
- Assume that I have no way of knowing how many folders any particular compilation will produce. But I need to make sure every single folder under Deploy is deleted prior to running another compilation. Deploy has to be completely emptied but I am not allowed to just delete and recreate the Deploy folder.
Here's a snippet of what I tried:
<PropertyGroup><DeployRoot>.\Deploy</DeployRoot></PropertyGroup>
<ItemGroup><DeployCleanFolders Include="$(DeployRoot)\**\*.*" /></ItemGroup>
<Target Name="Deploy"><Message Text="Clean Folders @(DeployCleanFolders)" /><Delete Files="@(DeployCleanFiles)" />
<RemoveDir Directories="@(DeployCleanFolders)" />
<CreateItem Include="$(VersionCompiled)\**\*.*">
<Output TaskParameter="Include" ItemName="DeploySourceFiles" />
</CreateItem>
<Copy
SourceFiles="@(DeploySourceFiles)"
DestinationFolder="$(DeployRoot)\%(DeploySourceFiles.RecursiveDir)"
ContinueOnError="true"
/>
</Target>
Now of course this doesn't work because there is no way to obtain just a list of folders with writing a custom task. I could of course hard-code in a list of folders but that's really not efficient. I'm not going to update the build script every time someone on the team requires a new folder. Ultimately there's no good way to get a directory listing without writing a custom task. I'll open a bug on this.
Here's an admittedly hacky way to do this w/o writing a custom task, using the Exec command:<PropertyGroup>
<DeployRoot>.\Deploy</DeployRoot>
</PropertyGroup>
<!-- this item will contain all FILES underneath $(DeployRoot) -->
<ItemGroup>
<DeployCleanFiles Include="$(DeployRoot)\**\*.*" />
</ItemGroup>
<Target Name="Deploy">
<Message Text="Clean Folders @(DeployCleanFolders)" />
<Delete Files="@(DeployCleanFiles)" />
<Exec Command="for /f %%d in ('dir /ad /b') do rd /s /q %%d"
WorkingDirectory="$(DeployRoot)" />
<CreateItem Include="$(VersionCompiled)\**\*.*">
<Output TaskParameter="Include" ItemName="DeploySourceFiles" />
</CreateItem>
<Copy
SourceFiles="@(DeploySourceFiles)"
DestinationFolder="$(DeployRoot)\%(DeploySourceFiles.RecursiveDir)"
ContinueOnError="true"
/>
</Target>- All the File System tasks in MSBuild are so incredibly rubish you wouldn't believe. I tend to rely on the native DOS commands (xcopy ...). In your example, I would do:
<
Exec Command="RmDir /S $(DeployRoot)" />
Alternatively, you could off course use a propper build tool ;) - You could also try the following task
<FTDFolder TaskAction="RemoveContent" Path="c:\Demo"/>
That will remove all files and folders, but leave c:\Demo. If there are readonly files, pass in Force="true".
All available here: http://www.codeplex.com/freetodevtasks. (A new release is due in the next few days...)

