Search code examples
buildmsbuildsubdirectory

How can I build all Solution (.SLN) files under a directory


We needed to automate testing that all of the C#, C++, & VB.NET samples we ship compile properly. We need it to build all files without our listing each one. Listing each one means if someone forgets to add a new one (which will happen someday), explicit calls will miss it. By walking all .sln files, we always get everything.

Doing this is pretty easy:

  1. Install the samples on a clean VM (that we revert back to the snapshot for each test run).
  2. Create a BuildAll.proj (MSBuild) file that calls all the .sln files installed.
  3. Use MSBuild to run the generated BuildAll.proj file

Step 2 requires a means to generate the BuildAll.proj file. Is there any way to tell MSBuild to run all .sln files under a sub-directory or to create a BuildAll.proj that calls all the underlying .slnl files?


Solution

  • We couldn't find anything so we wrote a program that creates a BuildAll.proj that calls all .sln files under a directory. Full solution is at Windward Wrocks (my blog).

    The code is:

    using System; 
    using System.IO; 
    using System.Text; 
    using System.Xml; 
    using System.Xml.Linq; 
    
    
    namespace BuildDotNetTestScript 
    { 
        /// <summary> 
        /// Builds a test script to compile all .sln files under the directory in. 
        /// </summary> 
        public class Program 
        { 
            private static readonly XNamespace xmlns = "http://schemas.microsoft.com/developer/msbuild/2003"; 
    
    
            private enum VS_VER 
            { 
                VS_2005, 
                VS_2008, 
                VS_2010, 
                NONE 
            } 
    
    
            private static VS_VER vsVersion = VS_VER.NONE; 
    
    
            /// <summary> 
            /// Build TestAll.proj for all .sln files in this directory and sub-directories. 
            /// </summary> 
            /// <param name="args">Optional: [-VS2005 | -VS2008 | -VS2010] TestAll.proj root_folder</param> 
            public static void Main(string[] args) 
            { 
    
    
                int indexArgs = 0; 
                if (args.Length >= 1 && args[0][0] == '-') 
                { 
                    indexArgs = 1; 
                    switch (args[0].ToUpper().Trim()) 
                    { 
                        case "-VS2005": 
                            vsVersion = VS_VER.VS_2005; 
                            break; 
                        case "-VS2008": 
                            vsVersion = VS_VER.VS_2008; 
                            break; 
                        case "-VS2010": 
                            vsVersion = VS_VER.VS_2010; 
                            break; 
                        default: 
                            Console.Error.WriteLine("Only options are -VS2005, -VS2008, or -VS2010"); 
                            Environment.Exit(1); 
                            return; 
                    } 
                } 
    
    
                string projFile = Path.GetFullPath(args.Length > indexArgs ? args[indexArgs] : "TestAll.proj"); 
                string rootDirectory = 
                    Path.GetFullPath(args.Length > indexArgs + 1 ? args[indexArgs + 1] : Directory.GetCurrentDirectory()); 
                Console.Out.WriteLine(string.Format("Creating project file {0}", projFile)); 
                Console.Out.WriteLine(string.Format("Root directory {0}", rootDirectory)); 
    
    
                XDocument xdoc = new XDocument(); 
                XElement elementProject = new XElement(xmlns + "Project"); 
                xdoc.Add(elementProject); 
                elementProject.Add(new XAttribute("DefaultTargets", "compile")); 
                elementProject.Add(new XAttribute("ToolsVersion", "3.5")); 
    
    
                XElement elementPropertyGroup = new XElement(xmlns + "PropertyGroup"); 
                elementProject.Add(elementPropertyGroup); 
                XElement elementDevEnv = new XElement(xmlns + "devenv"); 
                elementPropertyGroup.Add(elementDevEnv); 
                elementDevEnv.Value = "devenv.exe"; 
    
    
                XElement elementTarget = new XElement(xmlns + "Target"); 
                elementProject.Add(elementTarget); 
                elementTarget.Add(new XAttribute("Name", "compile")); 
    
    
                // add .sln files - recursively 
                AddSlnFiles(elementTarget, rootDirectory, rootDirectory); 
    
    
                Console.Out.WriteLine("writing project file to disk"); 
                // no BOM 
                using (var writer = new XmlTextWriter(projFile, new UTF8Encoding(false))) 
                { 
                    writer.Formatting = Formatting.Indented; 
                    xdoc.Save(writer); 
                } 
    
    
                Console.Out.WriteLine("all done"); 
            } 
    
    
            private static void AddSlnFiles(XElement elementTarget, string rootDirectory, string folder) 
            { 
    
    
                // add .sln files 
                foreach (string fileOn in Directory.GetFiles(folder, "*.sln")) 
                { 
                    // .../JS/... is VS2005 
                    bool isJSharp = fileOn.ToUpper().Replace('\\', '/').Contains("/JS/"); 
                    bool versionMatch = true; 
                    switch (vsVersion) 
                    { 
                        case VS_VER.VS_2005: 
                            if ((!fileOn.ToUpper().Contains("VS2005")) && (! isJSharp)) 
                                versionMatch = false; 
                            break; 
                        case VS_VER.VS_2008: 
                            if (isJSharp || !fileOn.ToUpper().Contains("VS2008")) 
                                versionMatch = false; 
                            break; 
                        case VS_VER.VS_2010: 
                            if (isJSharp || !fileOn.ToUpper().Contains("VS2010")) 
                                versionMatch = false; 
                            break; 
                        default: 
                            if (isJSharp || fileOn.ToUpper().Contains("VS2005") || fileOn.ToUpper().Contains("VS2008") || fileOn.ToUpper().Contains("VS2010")) 
                                versionMatch = false; 
                            break; 
                    } 
                    if (!versionMatch) 
                        continue; 
    
    
                    string command = string.Format("\"$(devenv)\" \"{0}\" /Rebuild", Path.GetFileName(fileOn)); 
                    XElement elementExec = new XElement(xmlns + "Exec"); 
                    elementExec.Add(new XAttribute("Command", command)); 
    
    
                    string workingFolder; 
                    if (folder.StartsWith(rootDirectory)) 
                    { 
                        workingFolder = folder.Substring(rootDirectory.Length).Trim(); 
                        if ((workingFolder.Length > 0) && (workingFolder[0] == Path.DirectorySeparatorChar || workingFolder[0] == Path.AltDirectorySeparatorChar)) 
                            workingFolder = workingFolder.Substring(1); 
                    } 
                    else 
                        workingFolder = folder; 
                    if (workingFolder.Length > 0) 
                        elementExec.Add(new XAttribute("WorkingDirectory", workingFolder)); 
                    elementTarget.Add(elementExec); 
                } 
    
    
                // look in sub-directories 
                foreach (string subDirectory in Directory.GetDirectories(folder)) 
                    AddSlnFiles(elementTarget, rootDirectory, subDirectory); 
            } 
        } 
    }