I was unable to figure out a way to make CodeSmith handle this issue automatically, so I ended up writing a custom method in the Code Behind file to handle this.
A few notes:
- The proj files are XML, and as thus fairly easy to edit, but the actual "ItemGroup" node that holds the list of files that are included in the project isn't actually labeled in any special way. I ended up picking the "ItemGroup" node that has "Contains" child nodes, but there might be a better way to determine which you should use.
- I recommend doing all the proj file changes at once, instead of as each file is created/updated. Otherwise if you launch the generation from Visual Studio, you might get a flood of "This item has changed, would you like to reload"
- If your files are under source control (they are, right?!), you're going to need to handle checking files out and adding them to source control along with editing the proj files.
Here is (more or less) the code I used to add a file to the project:
/// <summary>
/// Adds the given file to the indicated project
/// </summary>
/// <param name="project">The path of the proj file</param>
/// <param name="projectSubDir">The subdirectory of the project that the
/// file is located in, otherwise an empty string if it is at the project root</param>
/// <param name="file">The name of the file to be added to the project</param>
/// <param name="parent">The name of the parent to group the file to, an
/// empty string if there is no parent file</param>
public static void AddFileToProject(string project, string projectSubDir,
string file, string parent)
{
XDocument proj = XDocument.Load(project);
XNamespace ns = "http://schemas.microsoft.com/developer/msbuild/2003";
var itemGroup = proj.Descendants(ns + "ItemGroup").FirstOrDefault(x => x.Descendants(ns + "Compile").Count() > 0);
if (itemGroup == null)
throw new Exception(string.Format("Unable to find an ItemGroup to add the file {1} to the {0} project", project, file));
//If the file is already listed, don't bother adding it again
if(itemGroup.Descendants(ns + "Compile").Where(x=>x.Attribute("Include").Value.ToString() == file).Count() > 0)
return;
XElement item = new XElement(ns + "Compile",
new XAttribute("Include", Path.Combine(projectSubDir,file)));
//This is used to group files together, in this case the file that is
//regenerated is grouped as a dependent of the user-editable file that
//is not changed by the code generator
if (string.IsNullOrEmpty(parent) == false)
item.Add(new XElement(ns + "DependentUpon", parent));
itemGroup.Add(item);
proj.Save(project);
}