When you are creating your own targets, and tasks, they may generate temporary files that need to be cleaned. This is especailly true if you are using third party tools, because alot of times they create a cache directory or give files names that are not agreeable to the developer. When youre MSBuild targets create these files, you may think that if you place the files in the OutputPath then they will be cleaned whenever the Clean is invoked, but this assumption is wrong. Visual Studio actually uses a file that keeps track of what files it generates and only deletes those files. You can have a look at this file its named ProjectName.ProjectType.FileList.txt, For instance the file may be named DataAccess.csproj.FileList.txt. A sample of what this file contains is:
    bin\Debug\Dreamcatcher.DataAccess.dll
    bin\Debug\Dreamcatcher.DataAccess.pdb
    obj\Debug\ResolveAssemblyReference.cache
    obj\Debug\Dreamcatcher.DataAccess.dll
    obj\Debug\Dreamcatcher.DataAccess.pdb
Pretty simple, one line for each file this file is written using the WriteLinesToFile MSBuild task. When the Clean is invoked then this file is read and each of these files are deleted. You could add lines to this file in an attempt to have MSBuild clean them, but this is tricky and you have no control over what happens during the clean. A better way is to create a custom target and inject that into the Clean process. This gives you much more flexibility and is more robust. So let's take a look at how we can do this.

This sample is from a targets file I wrote to invoke xsd.exe against xsd files in certain directories. I might use the rest of that file for later blogs. Here is the relevant portion

<PropertyGroup>
    <
CleanDependsOn>
       
$(CleanDependsOn);
        CleanXsd;
   
CleanDependsOn>
PropertyGroup>


<
Target Name="CleanXsd">
    <
CreateItem Include="$(XsdTempClasses)**\*">
        <
Output TaskParameter="Include" ItemName="TempClassFiles"/>
    CreateItem>
    <
Delete Files="@(TempClassFiles)"/>
   
<CreateItem Include="$(XsdTempDataSet)\**.*">
        <
Output TaskParameter="Include" ItemName="TempDSFiles"/>
    CreateItem>
    <
Delete Files="@(TempDSFiles)"/>

    <CreateItem Include="$(XsdTempDir)\**.*">
        <
Output TaskParameter="Include" ItemName="OtherFiles"/>
    CreateItem>

    <Delete Files="@(OtherFiles)"/>

   
    <
RemoveDir Directories="$(XsdTempClasses);$(XsdTempDataSet);$(XsdTempDir)"/>
Target>

Pay close attention to the declaration of the CleanDependsOn, this will get the current value of it with $(CleanDependsOn) and append the custom target CleanXsd to the end of it. What's great about doing this is that if you have many different indpendent targets that each need to have items cleaned then they will not affect each other, they will all have have their targets appended to the lits of targets to be executed when a Clean is invoked. If you simply created the AfterClean target in many different targets files then only the last one defined would actually be executed. Also have a look at how the target exclusively uses CreateItem as opposed to items declared in the normal means. Items and Properties are evaluated at the begining of the build. So if I create the Item declaration:


    

Then if I created Xsd files and cleaned in the same MSBuild instance then I would miss some files. Since it only uses CreateItem for the items then all files will be picked up, no matter when they were created.


Comment Section


Comments are closed.