MSBuild in Visual Studio Part 8: Writing Items To the Project File

Reading items from the project file, as we discussed in our last post, is quite straightforward and doesn’t have much in the way of interesting gotchas. Have no fear! We’ll make up for that with all the gotchas around writing items back.

The first thing to realize is that the project system actually holds onto BuildItem objects in the object model for as long as the project is open in the IDE. The implication on the build engine side is that MSBuild has to be very careful not to discard the objects while VS is using them. We couldn’t, for example, be lazy and just dump all the data structures and re-build them when a condition is set.

As for how the actual entries are written to the file, basic items work just as you’d expect. If the project file has the following in it:

<Compile Include=”a.cs”/>

and a.cs gets renamed or deleted through Solution Explorer, MSBuild will update the entry appropriately. That’s pretty straightforward. It gets messy when we start to look at the more complicated ways of specifying files in the Include property.

The first complicated case is a semi-colon separated list of items, like this:

<Compile Include=”a.cs; b.cs; c.cs”/>

Internally we think of this as a single parent item (the whole Compile tag) and three child items (one BuildItem for each of the files in the list). When something happens to one of the child items in the IDE, MSBuild will replace the parent item with three tags for the child items. For example, if c.cs is renamed to foo.cs, the following gets written to the project file:

<Compile Include=”a.cs/>
<Complie Include=”b.cs/>
<Compile Include=”foo.cs”/>

This is far simpler than MSBuild trying to be fancy with text replacement inside the include property.

A similar approach is used if wildcards are specified. If the Include is “*.cs” and a file is renamed or deleted, MSBuild will expand that out to all filenames with one Compile entry for each file. The only exception is for file additions: if the new file is covered by the wildcard, we’re smart and keep the wildcard.

The best way to understand all the above is to give it a try in a project file of your very own. Create empty text files called a.cs, b.cs, and c.cs, and then try the semi-colon and wildcards ways of including them in a simple project file. Manipulate the files through Solution Explorer, save the project, and look at the resulting changes to your Compile items.

[ Author: Neil Enns ]