David Grey's Blog

Friends of Redgate logo

Where did my day go? Oh, wait... I've been trying to copy files with MSBuild

I've spent most of my day today trying to copy some files from one location to another using MSBuild. This is something I've done plenty of times before, but in this case I was trying to do something other than just copy an entire directory of files. I'll be the first to admit to being a bit thick sometimes, but MSBuild really does confuse me sometimes and I guess today was one of those times. MSBuild is certainly powerful but I find some of the operations a little counter-intuitive; I think it has something to do with my (mis)understanding of when/how some of the expression in the build file get evaluated.

The problem I was trying to address in this case was to delete all the files and subdirectories from the destination directory except for a specific subset of files/directories, and then copy a subset of files from the source directory to the destination directory. The image below, which shows the directory hierarchy in the destination directory, illustrates the scenario better

files

I needed to delete all the files in all the directories and subdirectories of the destination except for the files in the B2 directory. I then needed to delete all the subdirectories of the destination except for B and B2.

Whilst this is easy to accomplish with a few lines of C#, it is substantially more tricky with MSBuild and I couldn't make it work with a single list of files. One of the quirks of MSBuild is that to create a list of directories you have to start with a list of files and extract the directory metadata from each list item to create the list of directories, but if a directory has more than one file then it will appear in the directory list multiple times. If you set the <RemoveDir> task to fail on errors then it will cause the build to stop whenever it tries to remove a directory that it has already removed, which is always the case if the directory appears in the list more than once. Setting the task to continue on error seems like a bit of a nasty hack. Surely there must be a better way to define a set of directories which eliminates duplicates, but if there is I don't know what it is.

Anyway, back to the plot... Obtaining a list of all the files except those in the B2 directory is easily achieved by creating an item group and judiciously using the Exclude attribute to ignore the files in B2. But I also need a list of directories which have to be deleted. Using the file list metadata to create the directory list doesn't work because the directory B always appears in the list and the behaviour of <RemoveDir> is to recursively remove all directories. This causes B, B2 and the files I'm trying to preserve to be removed from the destination. Definitely not the outcome I was looking for. I haven't managed to solve the problem fully yet but I have a solution which works well enough for now. What I really need is a custom MSBuild task which does not delete directories recursively; why on earth did the MSBuild team not add that as a conditional switch?!

In total I reckon I've spent about 6 hours trying to MSBuild to copy my files and I wish it didn't have to be so hard. There was one bright point to the day though when I found that Partho P Das has created a freely available MSBuild Visual Debugger tool which is available on CodePlex.  Admittedly it's not quite as fully featured as Visual Studio and is obviously a work in progress but it's a great little tool which lets you set breakpoints in MSBuild files and inspect properties, items and the call stack. My productivity in trying to solve this problem went up dramatically once I found Partho's tool. It's much quicker running and debugging a build file through his tool than it is through Visual Studio. If you find yourself working on MSBuild files then I wholeheartedly recommend you get hold of a copy of it as it will make your life significantly easier.

del.ico.us del.ico.us | Digg It Digg It | Technorati Technorati | StumbleUpon StumbleUpon | Furl Furl | reddit reddit

Comments

No Comments