Nicklas,

Thanks for the feedback.

Nicklas Norling wrote:

I only have two concerns, one being performance, the second being deviations from VS.NET
default behavior when no options are present. As long as performance is not decreased and the
solution task works as close to VS.NET solution builds there shouldn't be any problems.



Certainly the goal of my proposed changes is just to introduce points in the processing of a solution where custom behaviour can be injected. It will require moving from a collection of largely static methods to virtual methods but I do not expect the performance impact of that to be significant. The behaviour of the current solution task would not be changed.


I'm available for all kinds of testing verifications of whatever you might come up with, I also have
a pretty good picture of demands for at least C#. Unfortunately I'm near as skilled in the details
as the real devs around here.



Great. I'm attaching a set of diffs which would give you all an idea of what I am proposing (not formatted or cleaned up). The main changes are


1. Making ProjectFactory instance-based and allowing subclasses of solution to provide a different ProjectFactory instance.

2. Providing two virtual methods in Project. One handles the source files and the other the References. Both these work by allowing a subclass to modify the response file sent to the compiler. These changes are the minimum we need to allow Clover.NET to instrument code prior to its compilation when building a solution. Further methods allowing a subclass to override other aspects of the build are certainly possible.

The remaining changes flow from these two main needs - the change from static to instance based project factories flows into quite a few areas. I'm not a big fan of having a lot of static methods and fields in classes.

Have a look - it's mostly a proof of concept at this time. I'm happy to help with the solution task on a branch or whatever - let me know.

Conor

Index: src/NAnt.VSNet/Project.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.VSNet/Project.cs,v
retrieving revision 1.66
diff -3 -u -w -p -r1.66 Project.cs
--- src/NAnt.VSNet/Project.cs   6 Sep 2004 02:18:59 -0000       1.66
+++ src/NAnt.VSNet/Project.cs   15 Sep 2004 10:11:52 -0000
@@ -41,7 +41,8 @@ namespace NAnt.VSNet {
     public class Project : ProjectBase {
         #region Public Instance Constructors
 
-        public Project(SolutionTask solutionTask, TempFileCollection tfc, GacCache 
gacCache, ReferencesResolver refResolver, DirectoryInfo outputDir) : 
base(solutionTask, tfc, gacCache, refResolver, outputDir) {
+        public Project(SolutionTask solutionTask, TempFileCollection tfc, GacCache 
gacCache, ReferencesResolver refResolver, DirectoryInfo outputDir) 
+            : base(solutionTask, tfc, gacCache, refResolver, outputDir) {
             _htReferences = CollectionsUtil.CreateCaseInsensitiveHashtable();
             _sourceFiles = CollectionsUtil.CreateCaseInsensitiveHashtable();
             _htResources = CollectionsUtil.CreateCaseInsensitiveHashtable();
@@ -71,7 +72,7 @@ namespace NAnt.VSNet {
             get { 
                 string projectPath;
 
-                if (ProjectFactory.IsUrl(_projectPath)) {
+                if (ProjectUtils.IsUrl(_projectPath)) {
                     // construct uri for project path
                     Uri projectUri = new Uri(_projectPath);
 
@@ -92,7 +93,7 @@ namespace NAnt.VSNet {
         /// </summary>
         public override string ProjectPath {
             get { 
-                if (ProjectFactory.IsUrl(_projectPath)) {
+                if (ProjectUtils.IsUrl(_projectPath)) {
                     return _projectPath;
                 } else {
                     return Path.GetFullPath(_projectPath);
@@ -151,7 +152,7 @@ namespace NAnt.VSNet {
 
             _projectSettings = new ProjectSettings(doc.DocumentElement, (XmlElement) 
doc.SelectSingleNode("//Build/Settings"), this);
 
-            _isWebProject = ProjectFactory.IsUrl(projectPath);
+            _isWebProject = ProjectUtils.IsUrl(projectPath);
             _webProjectBaseUrl = string.Empty;
 
             XmlNodeList nlConfigurations, nlReferences, nlFiles, nlImports;
@@ -235,6 +236,30 @@ namespace NAnt.VSNet {
             }
         }
 
+        /// <summary>
+        /// Add the source files to the build command. Subclasses can process the 
source files
+        /// and write their own list
+        /// </summary>
+        /// <param name="sw">The writer for the file list</param>
+        /// <param name="sourceFiles">The list of files</param>
+        protected virtual void AddSourcesToBuild(StreamWriter sw, IDictionary 
sourceFiles) 
+        {
+            // add the files to compile
+            foreach (string file in sourceFiles.Keys) 
+            {
+                sw.WriteLine(@"""" + file + @"""");
+            }
+        }
+
+        protected virtual void AddReferencesToBuild(StreamWriter sw, StringCollection 
references) 
+        {
+            // write assembly references to response file
+            foreach (string assemblyReference in references) 
+            {
+                sw.WriteLine("/r:\"{0}\"", assemblyReference);
+            }
+        }
+
         protected override bool Build(ConfigurationBase configurationSettings) {
             bool bSuccess = true;
             bool haveCultureSpecificResources = false;
@@ -334,10 +359,8 @@ namespace NAnt.VSNet {
                         }
                     }
 
-                    // wrtie assembly references to response file
-                    foreach (string assemblyReference in GetAssemblyReferences(cs)) {
-                        sw.WriteLine("/r:\"{0}\"", assemblyReference);
-                    }
+                    
+                    AddReferencesToBuild(sw, GetAssemblyReferences(cs));
 
                     if (_htResources.Count > 0) {
                         Log(Level.Verbose, "Compiling resources:");
@@ -369,10 +392,7 @@ namespace NAnt.VSNet {
                         _sourceFiles[tempFile] = null;
                     }
 
-                    // add the files to compile
-                    foreach (string file in _sourceFiles.Keys) {
-                        sw.WriteLine(@"""" + file + @"""");
-                    }
+                    AddSourcesToBuild(sw, _sourceFiles);
                 }
 
                 Log(Level.Verbose, "Starting compiler...");
@@ -613,32 +633,6 @@ namespace NAnt.VSNet {
 
         #region Public Static Methods
 
-        public static bool IsEnterpriseTemplateProject(string fileName) {
-            try {
-                XmlDocument doc = LoadXmlDocument(fileName);
-                return 
doc.DocumentElement.Name.ToString(CultureInfo.InvariantCulture) == "EFPROJECT";
-            } catch (XmlException) {
-                // when the project isn't a valid XML document, it definitely
-                // isn't an enterprise template project
-                return false;
-            } catch (Exception ex) {
-                throw new BuildException(string.Format(CultureInfo.InvariantCulture,
-                    "Error checking whether '{0}' is an enterprise template project.",
-                    fileName), Location.UnknownLocation, ex);
-            }
-        }
-
-        public static string LoadGuid(string fileName) {
-            try {
-                XmlDocument doc = LoadXmlDocument(fileName);
-                return ProjectSettings.GetProjectGuid(doc.DocumentElement);
-            } catch (Exception ex) {
-                throw new BuildException(string.Format(CultureInfo.InvariantCulture,
-                    "Error loading GUID of project '{0}'.", fileName), 
-                    Location.UnknownLocation, ex);
-            }
-        }
-
         #endregion Public Static Methods
 
         #region Private Instance Methods
Index: src/NAnt.VSNet/ProjectBase.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.VSNet/ProjectBase.cs,v
retrieving revision 1.19
diff -3 -u -w -p -r1.19 ProjectBase.cs
--- src/NAnt.VSNet/ProjectBase.cs       20 Aug 2004 14:40:10 -0000      1.19
+++ src/NAnt.VSNet/ProjectBase.cs       15 Sep 2004 10:11:52 -0000
@@ -368,8 +368,8 @@ namespace NAnt.VSNet {
 
         #region Protected Static Methods
 
-        protected static XmlDocument LoadXmlDocument(string fileName) {
-            return ProjectFactory.LoadProjectXml(fileName);
+        protected XmlDocument LoadXmlDocument(string fileName) {
+            return _solutionTask.ProjectFactory.LoadProjectXml(fileName);
         }
 
         #endregion Protected Static Methods
Index: src/NAnt.VSNet/ProjectFactory.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.VSNet/ProjectFactory.cs,v
retrieving revision 1.18
diff -3 -u -w -p -r1.18 ProjectFactory.cs
--- src/NAnt.VSNet/ProjectFactory.cs    11 Aug 2004 17:01:39 -0000      1.18
+++ src/NAnt.VSNet/ProjectFactory.cs    15 Sep 2004 10:11:53 -0000
@@ -31,42 +31,49 @@ using NAnt.Core.Util;
 using NAnt.VSNet.Tasks;
 
 namespace NAnt.VSNet {
+    public class ProjectUtils 
+    {
+        public static bool IsUrl(string fileName) 
+        {
+            if (fileName.StartsWith(Uri.UriSchemeFile) || 
fileName.StartsWith(Uri.UriSchemeHttp) || fileName.StartsWith(Uri.UriSchemeHttps)) 
+            {
+                return true;
+            }
+
+            return false;
+        }
+
+    }
+
     /// <summary>
     /// Factory class for VS.NET projects.
     /// </summary>
-    public sealed class ProjectFactory {
-        #region Private Instance Constructor
+    public class ProjectFactory  : IProjectFactory {
+        #region Public Instance Constructor
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ProjectFactory" />
         /// class.
         /// </summary>
-        private ProjectFactory() {
+        public ProjectFactory() {
         }
 
         #endregion Private Instance Constructor
 
-        #region Static Constructor
-
-        static ProjectFactory() {
-            ClearCache();
-        }
+        #region Public Instance Methods
 
-        #endregion Static Constructor
-
-        #region Public Static Methods
-
-        public static void ClearCache() {
+        public void ClearCache() 
+        {
             _cachedProjects = CollectionsUtil.CreateCaseInsensitiveHashtable();
             _cachedProjectGuids = CollectionsUtil.CreateCaseInsensitiveHashtable();
             _cachedProjectXml = CollectionsUtil.CreateCaseInsensitiveHashtable();
         }
 
-        public static XmlDocument LoadProjectXml(string path) {
+        public XmlDocument LoadProjectXml(string path) {
             if (!_cachedProjectXml.Contains(path)) {
                 XmlDocument doc = new XmlDocument();
 
-                if (!ProjectFactory.IsUrl(path)) {
+                if (!ProjectUtils.IsUrl(path)) {
                     doc.Load(path);
                 } else {
                     Uri uri = new Uri(path);
@@ -83,84 +90,101 @@ namespace NAnt.VSNet {
             return (XmlDocument) _cachedProjectXml[path];
         }    
 
-        public static ProjectBase LoadProject(Solution sln, SolutionTask slnTask, 
TempFileCollection tfc, GacCache gacCache, ReferencesResolver refResolver, 
DirectoryInfo outputDir, string path) {
-            string projectFileName = ProjectFactory.GetProjectFileName(path);
+        public virtual ProjectBase CreateProject(String projectExtension, 
SolutionTask slnTask, TempFileCollection tfc, GacCache gacCache, ReferencesResolver 
refResolver, DirectoryInfo outputDir)
+        {
+            if (projectExtension == ".vbproj" || projectExtension == ".csproj") 
+            {
+                return new Project(slnTask, tfc, gacCache, refResolver, outputDir);
+            } 
+            else if (projectExtension == ".vcproj") 
+            {
+                return new VcProject(slnTask, tfc, gacCache, refResolver, outputDir);
+            } 
+            else 
+            {
+                throw new BuildException(string.Format(CultureInfo.InvariantCulture,
+                    "Unknown project file extension '{0}'.", projectExtension),
+                    Location.UnknownLocation);
+            }
+        }
+
+        public virtual ProjectBase LoadProject(Solution sln, SolutionTask slnTask, 
TempFileCollection tfc, GacCache gacCache, ReferencesResolver refResolver, 
DirectoryInfo outputDir, string path) {
+            string projectFileName = GetProjectFileName(path);
             string projectExt = Path.GetExtension(projectFileName).ToLower(
                 CultureInfo.InvariantCulture);
             
             // check if this a new project?
             if (!_cachedProjects.Contains(path)) {
-                if (projectExt == ".vbproj" || projectExt == ".csproj") {
-                    Project p = new Project(slnTask, tfc, gacCache, refResolver, 
outputDir);
+                ProjectBase p = CreateProject(projectExt, slnTask, tfc, gacCache, 
refResolver, outputDir);
                     p.Load(sln, path);
                     _cachedProjects[path] = p;
-                } else if (projectExt == ".vcproj") {
-                    VcProject p = new VcProject(slnTask, tfc, gacCache, refResolver, 
outputDir);
-                    p.Load(sln, path);
-                    _cachedProjects[path] = p;
-                } else {
-                    throw new 
BuildException(string.Format(CultureInfo.InvariantCulture,
-                        "Unknown project file extension '{0}'.", projectExt),
-                        Location.UnknownLocation);
-                }
             }
 
             return (ProjectBase) _cachedProjects[path];
         }
 
-        public static bool IsUrl(string fileName) {
-            if (fileName.StartsWith(Uri.UriSchemeFile) || 
fileName.StartsWith(Uri.UriSchemeHttp) || fileName.StartsWith(Uri.UriSchemeHttps)) {
-                return true;
-            }
-
-            return false;
-        }
-
-        public static bool IsSupportedProjectType(string path) {
-            string projectFileName = ProjectFactory.GetProjectFileName(path);
+        public virtual bool IsSupportedProjectType(string path) {
+            string projectFileName = GetProjectFileName(path);
             string projectExt = Path.GetExtension(projectFileName).ToLower(
                 CultureInfo.InvariantCulture);
             return projectExt == ".vbproj" || projectExt == ".csproj" || projectExt 
== ".vcproj";
         }
 
-        public static string LoadGuid(string fileName) {
+        public string LoadGuid(string fileName) {
             // check if a project with specified file is already cached
             if (_cachedProjects.ContainsKey(fileName)) {
                 // return the guid of the cached project
                 return ((ProjectBase) _cachedProjects[fileName]).Guid;
             }
 
-            string projectFileName = ProjectFactory.GetProjectFileName(fileName);
+            string projectFileName = GetProjectFileName(fileName);
             string projectExt = Path.GetExtension(projectFileName).ToLower(
                 CultureInfo.InvariantCulture);
 
             // check if GUID of project is already cached
             if (!_cachedProjectGuids.Contains(fileName)) {
+                string projectGuid = null;
+                bool unknownExtension = false;
+                try {
+                    XmlDocument doc = LoadProjectXml(fileName);
                 if (projectExt == ".vbproj" || projectExt == ".csproj") {
                     // add project GUID to cache
-                    _cachedProjectGuids[fileName] = Project.LoadGuid(fileName);
+                        projectGuid = 
ProjectSettings.GetProjectGuid(doc.DocumentElement);
                 } else if (projectExt == ".vcproj") {
                     // add project GUID to cache
-                    _cachedProjectGuids[fileName] = VcProject.LoadGuid(fileName);
+                        projectGuid = doc.DocumentElement.GetAttribute("ProjectGUID");
                 } else {
+                        unknownExtension = true;
+                    }
+                } catch (Exception ex) {
+                    throw new 
BuildException(string.Format(CultureInfo.InvariantCulture,
+                        "Error loading GUID of project '{0}'.", fileName), 
+                        Location.UnknownLocation, ex);
+                }
+
+                if (unknownExtension) 
+                {
                     throw new 
BuildException(string.Format(CultureInfo.InvariantCulture, 
                         "Unknown project file extension '{0}'.", projectExt,
                         Location.UnknownLocation));
                 }
+
+                _cachedProjectGuids[fileName] = projectGuid;
             }
 
             // return project GUID from cache
             return (string) _cachedProjectGuids[fileName];
         }
 
-        #endregion Public Static Methods
+        #endregion Public Instance Methods
 
-        #region Private Static Methods
+        #region Private Instance Methods
 
-        private static string GetProjectFileName(string fileName) {
+        private string GetProjectFileName(string fileName) 
+        {
             string projectPath = null;
 
-            if (ProjectFactory.IsUrl(fileName)) {
+            if (ProjectUtils.IsUrl(fileName)) {
                 // construct uri for project path
                 Uri projectUri = new Uri(fileName);
 
@@ -177,7 +201,7 @@ namespace NAnt.VSNet {
 
         #endregion Private Static Methods
 
-        #region Private Static Fields
+        #region Private Instance Fields
 
         /// <summary>
         /// Holds a case-insensitive list of cached projects.
@@ -187,7 +211,7 @@ namespace NAnt.VSNet {
         /// file (for web projects this can be a URL) and the value is a 
         /// <see cref="Project" /> instance.
         /// </remarks>
-        private static Hashtable _cachedProjects;
+        private Hashtable _cachedProjects;
 
         /// <summary>
         /// Holds a case-insensitive list of cached project GUIDs.
@@ -197,7 +221,7 @@ namespace NAnt.VSNet {
         /// file (for web projects this can be a URL) and the value is the GUID
         /// of the project.
         /// </remarks>
-        private static Hashtable _cachedProjectGuids;
+        private Hashtable _cachedProjectGuids;
 
         /// <summary>
         /// Holds a case-insensitive list of cached project GUIDs.
@@ -207,8 +231,8 @@ namespace NAnt.VSNet {
         /// file (for web projects this can be a URL) and the value is the Xml
         /// of the project.
         /// </remarks>
-        private static Hashtable _cachedProjectXml;
+        private Hashtable _cachedProjectXml;
 
-        #endregion Private Static Fields
+        #endregion Private Instance Fields
     }
 }
Index: src/NAnt.VSNet/Reference.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.VSNet/Reference.cs,v
retrieving revision 1.52
diff -3 -u -w -p -r1.52 Reference.cs
--- src/NAnt.VSNet/Reference.cs 2 Sep 2004 05:29:01 -0000       1.52
+++ src/NAnt.VSNet/Reference.cs 15 Sep 2004 10:11:54 -0000
@@ -102,7 +102,7 @@ namespace NAnt.VSNet {
                     temporaryFiles = ps.TemporaryFiles;
                 }
 
-                ProjectBase project = ProjectFactory.LoadProject(solution, 
parent.SolutionTask, temporaryFiles, _gacCache, _refResolver, outputDir, projectFile);
+                ProjectBase project = solution.ProjectFactory.LoadProject(solution, 
parent.SolutionTask, temporaryFiles, _gacCache, _refResolver, outputDir, projectFile);
 
                 // we don't know what the timestamp of the project is going to be, 
                 // because we don't know what configuration we will be building
Index: src/NAnt.VSNet/Solution.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.VSNet/Solution.cs,v
retrieving revision 1.46
diff -3 -u -w -p -r1.46 Solution.cs
--- src/NAnt.VSNet/Solution.cs  2 Sep 2004 05:29:37 -0000       1.46
+++ src/NAnt.VSNet/Solution.cs  15 Sep 2004 10:11:54 -0000
@@ -37,112 +37,33 @@ namespace NAnt.VSNet {
     public class Solution {
         #region Public Instance Constructors
 
-        public Solution(FileInfo solutionFile, ArrayList additionalProjects, 
ArrayList referenceProjects, TempFileCollection tfc, SolutionTask solutionTask, 
WebMapCollection webMaps, FileSet excludesProjects, DirectoryInfo outputDir, GacCache 
gacCache, ReferencesResolver refResolver) : this(tfc, solutionTask, webMaps, 
excludesProjects, outputDir) {
-            _file = solutionFile;
-
-            string fileContents;
-
-            using (StreamReader sr = new StreamReader(solutionFile.FullName)) {
-                fileContents = sr.ReadToEnd();
-            }
-
-            Regex reProjects = new 
Regex(@"Project\(\""(?<package>\{.*?\})\"".*?\""(?<name>.*?)\"".*?\""(?<project>.*?)\"".*?\""(?<guid>.*?)\""(?<all>[\s\S]*?)EndProject",
 RegexOptions.Multiline);
-            MatchCollection projectMatches = reProjects.Matches(fileContents);
-
-            foreach (Match projectMatch in projectMatches) {
-                string project = projectMatch.Groups["project"].Value;
-                string guid = projectMatch.Groups["guid"].Value;
-                string fullPath;
-
-                // translate URLs to physical paths if using a webmap
-                string map = _webMaps.FindBestMatch(project);
-                if (map != null) {
-                    Log(Level.Debug, "Found webmap match '{0}' for '{1}.", 
-                        map, project);
-                    project = map;
-                }
-
-                try {
-                    Uri uri = new Uri(project);
-                    if (uri.Scheme == Uri.UriSchemeFile) {
-                        fullPath = Path.Combine(solutionFile.DirectoryName, 
uri.LocalPath);
-                    } else {
-                        fullPath = project;
-
-                        if (!solutionTask.EnableWebDav) {
-                            throw new 
BuildException(string.Format(CultureInfo.InvariantCulture,
-                                "Cannot build web project '{0}'.  Please use" 
-                                + " <webmap> to map the given URL to a 
project-relative" 
-                                + " path, or specify enablewebdav=\"true\" on the" 
-                                + " <solution> task element to use WebDAV.", 
fullPath));
-                        }
-                    }
-                } catch (UriFormatException) {
-                    fullPath = Path.Combine(solutionFile.DirectoryName, project);
-                }
-                
-                if (Project.IsEnterpriseTemplateProject(fullPath)) {
-                    RecursiveLoadTemplateProject(fullPath);
-                } else {
-                    // only C#, VB.NET and C++ projects are supported at this moment
-                    if (!ProjectFactory.IsSupportedProjectType(project)) {
-                        // output a warning message in the build log
-                        Log(Level.Warning, "Only C#, VB.NET and C++ projects" +
-                            " are supported.  Skipping project '{0}'.", project);
-                        // skip the project
-                        continue;
+        public Solution(IProjectFactory projectFactory) {
+            this._projectFactory = projectFactory;
                     }
 
-                    // add project path to collection
-                    _htProjectFiles[guid] = fullPath;
-                }
-
-                // set-up project dependencies
-                Regex reDependencies = new 
Regex(@"^\s+(?<guid>\{[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}\})\s+=\s+(?<dep>\{[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}\})",
 RegexOptions.Multiline);
-                MatchCollection dependencyMatches = 
reDependencies.Matches(projectMatch.Value);
-
-                foreach (Match dependencyMatch in dependencyMatches) {
-                    string dependency = dependencyMatch.Groups["dep"].Value;
-                    AddProjectDependency(guid, dependency);
-                }
-
-                // set-up project configuration
-                Regex reProjectBuildConfig = new Regex(@"^\s+" + guid + 
@"\.(?<configuration>[a-zA-Z]+)\.Build\.0\s+\S+", RegexOptions.Multiline);
-                MatchCollection projectBuildMatches = 
reProjectBuildConfig.Matches(fileContents);
-
-                // initialize hashtable that will hold the project build 
configurations
-                Hashtable projectBuildConfiguration = 
CollectionsUtil.CreateCaseInsensitiveHashtable();
+        #endregion Public Instance Constructors
 
-                if (projectBuildMatches.Count > 0) {
-                    foreach (Match projectBuildMatch in projectBuildMatches) {
-                        string configuration = 
projectBuildMatch.Groups["configuration"].Value;
-                        projectBuildConfiguration[configuration] = null;
-                    }
-                }
+        #region Public Instance Properties
 
-                // add project build configuration, this signals that project was 
-                // loaded in context of solution file
-                _htProjectBuildConfigurations[guid] = projectBuildConfiguration;
+        public FileInfo File {
+            get { return _file; }
+            set { _file = value; }
             }
 
-            LoadProjectGuids(additionalProjects, false);
-            LoadProjectGuids(referenceProjects, true);
-            LoadProjects(gacCache, refResolver);
-            GetDependenciesFromProjects();
+        public TempFileCollection TemporaryFiles {
+            get { return _tfc; }
         }
 
-        public Solution(ArrayList projects, ArrayList referenceProjects, 
TempFileCollection tfc, SolutionTask solutionTask, WebMapCollection webMaps, FileSet 
excludesProjects, DirectoryInfo outputDir, GacCache gacCache, ReferencesResolver 
refResolver) : this(tfc, solutionTask, webMaps, excludesProjects, outputDir) {
-            LoadProjectGuids(projects, false);
-            LoadProjectGuids(referenceProjects, true);
-            LoadProjects(gacCache, refResolver);
-            GetDependenciesFromProjects();
+        public IProjectFactory ProjectFactory {
+            get { return _projectFactory; }
         }
 
-        #endregion Public Instance Constructors
+        #endregion Public Instance Properties
 
-        #region Private Instance Constructors
+        #region Public Instance Methods
 
-        private Solution(TempFileCollection tfc, SolutionTask solutionTask, 
WebMapCollection webMaps, FileSet excludesProjects, DirectoryInfo outputDir) {
+        public void Initialize(TempFileCollection tfc, SolutionTask solutionTask, 
WebMapCollection webMaps, FileSet excludesProjects, DirectoryInfo outputDir) 
+        {
             _htProjects = CollectionsUtil.CreateCaseInsensitiveHashtable();
             _htProjectDirectories = CollectionsUtil.CreateCaseInsensitiveHashtable();
             _htOutputFiles = CollectionsUtil.CreateCaseInsensitiveHashtable();
@@ -155,26 +76,24 @@ namespace NAnt.VSNet {
             _outputDir = outputDir;
             _excludesProjects = excludesProjects;
             _webMaps = webMaps;
-            ProjectFactory.ClearCache();
+            _projectFactory.ClearCache();
         }
 
-        #endregion Private Instance Constructors
-
-        #region Public Instance Properties
-
-        public FileInfo File {
-            get { return _file; }
+        public void Load(ArrayList projects, ArrayList referenceProjects, GacCache 
gacCache, ReferencesResolver refResolver) 
+        {
+            if (_file != null) 
+            {
+                LoadSolutionFile();
         }
-
-        public TempFileCollection TemporaryFiles {
-            get { return _tfc; }
+            LoadProjectGuids(projects, false);
+            LoadProjectGuids(referenceProjects, true);
+            LoadProjects(gacCache, refResolver);
+            GetDependenciesFromProjects();
         }
 
-        #endregion Public Instance Properties
-
-        #region Public Instance Methods
 
-        public void RecursiveLoadTemplateProject(string fileName) {
+        public void RecursiveLoadTemplateProject(string fileName) 
+        {
             XmlDocument doc = new XmlDocument();
             doc.Load(fileName);
 
@@ -219,7 +138,7 @@ namespace NAnt.VSNet {
                         fullPath = Path.Combine(Path.GetDirectoryName(fileName), 
subProjectFilename);
                     }
 
-                    if (Project.IsEnterpriseTemplateProject(fullPath)) {
+                    if (IsEnterpriseTemplateProject(fullPath)) {
                         RecursiveLoadTemplateProject(fullPath);
                     } else {
                         _htProjectFiles[projectGuidNode.InnerText] = fullPath;
@@ -430,7 +349,139 @@ namespace NAnt.VSNet {
 
         #region Private Instance Methods
 
-        private void LoadProjectGuids(ArrayList projects, bool isReferenceProject) {
+        private bool IsEnterpriseTemplateProject(string fileName) 
+        {
+            try 
+            {
+                XmlDocument doc = _projectFactory.LoadProjectXml(fileName);
+                return 
doc.DocumentElement.Name.ToString(CultureInfo.InvariantCulture) == "EFPROJECT";
+            } 
+            catch (XmlException) 
+            {
+                // when the project isn't a valid XML document, it definitely
+                // isn't an enterprise template project
+                return false;
+            } 
+            catch (Exception ex) 
+            {
+                throw new BuildException(string.Format(CultureInfo.InvariantCulture,
+                    "Error checking whether '{0}' is an enterprise template project.",
+                    fileName), Location.UnknownLocation, ex);
+            }
+        }
+
+
+
+        private void LoadSolutionFile() 
+        {
+            string fileContents;
+
+
+            FileInfo solutionFile = _file;
+
+            using (StreamReader sr = new StreamReader(solutionFile.FullName)) 
+            {
+                fileContents = sr.ReadToEnd();
+            }
+
+            Regex reProjects = new 
Regex(@"Project\(\""(?<package>\{.*?\})\"".*?\""(?<name>.*?)\"".*?\""(?<project>.*?)\"".*?\""(?<guid>.*?)\""(?<all>[\s\S]*?)EndProject",
 RegexOptions.Multiline);
+            MatchCollection projectMatches = reProjects.Matches(fileContents);
+
+            foreach (Match projectMatch in projectMatches) 
+            {
+                string project = projectMatch.Groups["project"].Value;
+                string guid = projectMatch.Groups["guid"].Value;
+                string fullPath;
+
+                // translate URLs to physical paths if using a webmap
+                string map = _webMaps.FindBestMatch(project);
+                if (map != null) 
+                {
+                    Log(Level.Debug, "Found webmap match '{0}' for '{1}.", 
+                        map, project);
+                    project = map;
+                }
+
+                try 
+                {
+                    Uri uri = new Uri(project);
+                    if (uri.Scheme == Uri.UriSchemeFile) 
+                    {
+                        fullPath = Path.Combine(solutionFile.DirectoryName, 
uri.LocalPath);
+                    } 
+                    else 
+                    {
+                        fullPath = project;
+
+                        if (!_solutionTask.EnableWebDav) 
+                        {
+                            throw new 
BuildException(string.Format(CultureInfo.InvariantCulture,
+                                "Cannot build web project '{0}'.  Please use" 
+                                + " <webmap> to map the given URL to a 
project-relative" 
+                                + " path, or specify enablewebdav=\"true\" on the" 
+                                + " <solution> task element to use WebDAV.", 
fullPath));
+                        }
+                    }
+                } 
+                catch (UriFormatException) 
+                {
+                    fullPath = Path.Combine(solutionFile.DirectoryName, project);
+                }
+                
+                if (IsEnterpriseTemplateProject(fullPath)) 
+                {
+                    RecursiveLoadTemplateProject(fullPath);
+                } 
+                else 
+                {
+                    // only C#, VB.NET and C++ projects are supported at this moment
+                    if (!_projectFactory.IsSupportedProjectType(project)) 
+                    {
+                        // output a warning message in the build log
+                        Log(Level.Warning, "Only C#, VB.NET and C++ projects" +
+                            " are supported.  Skipping project '{0}'.", project);
+                        // skip the project
+                        continue;
+                    }
+
+                    // add project path to collection
+                    _htProjectFiles[guid] = fullPath;
+                }
+
+                // set-up project dependencies
+                Regex reDependencies = new 
Regex(@"^\s+(?<guid>\{[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}\})\s+=\s+(?<dep>\{[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}\})",
 RegexOptions.Multiline);
+                MatchCollection dependencyMatches = 
reDependencies.Matches(projectMatch.Value);
+
+                foreach (Match dependencyMatch in dependencyMatches) 
+                {
+                    string dependency = dependencyMatch.Groups["dep"].Value;
+                    AddProjectDependency(guid, dependency);
+                }
+
+                // set-up project configuration
+                Regex reProjectBuildConfig = new Regex(@"^\s+" + guid + 
@"\.(?<configuration>[a-zA-Z]+)\.Build\.0\s+\S+", RegexOptions.Multiline);
+                MatchCollection projectBuildMatches = 
reProjectBuildConfig.Matches(fileContents);
+
+                // initialize hashtable that will hold the project build 
configurations
+                Hashtable projectBuildConfiguration = 
CollectionsUtil.CreateCaseInsensitiveHashtable();
+
+                if (projectBuildMatches.Count > 0) 
+                {
+                    foreach (Match projectBuildMatch in projectBuildMatches) 
+                    {
+                        string configuration = 
projectBuildMatch.Groups["configuration"].Value;
+                        projectBuildConfiguration[configuration] = null;
+                    }
+                }
+
+                // add project build configuration, this signals that project was 
+                // loaded in context of solution file
+                _htProjectBuildConfigurations[guid] = projectBuildConfiguration;
+            }
+        }
+
+        private void LoadProjectGuids(ArrayList projects, bool isReferenceProject) 
+        {
             foreach (string projectFileName in projects) {
                 string projectGuid = ProjectFactory.LoadGuid(projectFileName);
                 if (_htProjectFiles[projectGuid] != null) {
@@ -648,6 +699,7 @@ namespace NAnt.VSNet {
         private FileSet _excludesProjects;
         private DirectoryInfo _outputDir;
         private TempFileCollection _tfc;
+        private IProjectFactory _projectFactory;
 
         #endregion Private Instance Fields
     }
Index: src/NAnt.VSNet/VcProject.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.VSNet/VcProject.cs,v
retrieving revision 1.23
diff -3 -u -w -p -r1.23 VcProject.cs
--- src/NAnt.VSNet/VcProject.cs 11 Aug 2004 17:01:39 -0000      1.23
+++ src/NAnt.VSNet/VcProject.cs 15 Sep 2004 10:11:55 -0000
@@ -41,7 +41,8 @@ namespace NAnt.VSNet {
     public class VcProject: ProjectBase {
         #region Public Instance Constructors
         
-        public VcProject(SolutionTask solutionTask, TempFileCollection tfc, GacCache 
gacCache, ReferencesResolver refResolver, DirectoryInfo outputDir) : 
base(solutionTask, tfc, gacCache, refResolver, outputDir) {
+        public VcProject(SolutionTask solutionTask, TempFileCollection tfc, GacCache 
gacCache, ReferencesResolver refResolver, DirectoryInfo outputDir) 
+            : base(solutionTask, tfc, gacCache, refResolver, outputDir) {
             _htPlatformConfigurations = 
CollectionsUtil.CreateCaseInsensitiveHashtable();
             _htFiles = CollectionsUtil.CreateCaseInsensitiveHashtable();
             _htReferences = CollectionsUtil.CreateCaseInsensitiveHashtable();
@@ -593,17 +594,6 @@ namespace NAnt.VSNet {
 
         #region Public Static Methods
 
-        public static string LoadGuid(string fileName) {
-            try {
-                XmlDocument doc = LoadXmlDocument(fileName);
-                return doc.DocumentElement.GetAttribute("ProjectGUID");
-            } catch (Exception ex) {
-                throw new BuildException(string.Format(CultureInfo.InvariantCulture,
-                    "Error loading GUID of project '{0}'.", fileName), 
-                    Location.UnknownLocation, ex);
-            }
-        }
-
         #endregion Public Static Methods
 
         #region Private Instance Fields
Index: src/NAnt.VSNet/Tasks/SolutionTask.cs
===================================================================
RCS file: /cvsroot/nant/nant/src/NAnt.VSNet/Tasks/SolutionTask.cs,v
retrieving revision 1.38
diff -3 -u -w -p -r1.38 SolutionTask.cs
--- src/NAnt.VSNet/Tasks/SolutionTask.cs        1 Aug 2004 13:59:44 -0000       1.38
+++ src/NAnt.VSNet/Tasks/SolutionTask.cs        15 Sep 2004 10:11:55 -0000
@@ -315,6 +315,14 @@ namespace NAnt.VSNet.Tasks {
             }
         }
 
+        public IProjectFactory ProjectFactory
+        {
+            get 
+            {
+                return _projectFactory;
+            }
+        }
+
         #endregion Public Instance Properties
 
         #region Public Static Properties
@@ -323,7 +331,8 @@ namespace NAnt.VSNet.Tasks {
         /// Gets the collection of root registry keys under which the 
         /// <see cref="SolutionTask" /> should looks for assembly folders.
         /// </summary>
-        public static StringCollection AssemblyFolderRootKeys {
+        public static StringCollection AssemblyFolderRootKeys 
+        {
             get { return _assemblyFolderRootKeys; }
         }
 
@@ -331,10 +340,16 @@ namespace NAnt.VSNet.Tasks {
 
         #region Override implementation of Task
 
+        protected virtual IProjectFactory CreateProjectFactory() 
+        {
+            return new ProjectFactory();
+        }
+
         protected override void ExecuteTask() {
             Log(Level.Info, "Starting solution build.");
 
-            Solution sln;
+            _projectFactory = CreateProjectFactory();
+            Solution sln = new Solution(ProjectFactory);
 
             if (Projects.FileNames.Count > 0) {
                 Log(Level.Verbose, "Included projects:" );
@@ -372,16 +387,15 @@ namespace NAnt.VSNet.Tasks {
                             typeof(ReferencesResolver).FullName).Unwrap());
 
                         using (GacCache gacCache = new GacCache(this.Project)) {
+                            sln.Initialize(tfc, this, WebMaps, ExcludeProjects, 
OutputDir);
                             // check if solution file was specified
-                            if (SolutionFile == null) {
-                                sln = new Solution(new ArrayList(Projects.FileNames), 
new ArrayList(ReferenceProjects.FileNames), tfc, 
-                                    this, WebMaps, ExcludeProjects, OutputDir, 
gacCache, referencesResolver);
-                            } else {
-                                sln = new Solution(SolutionFile, new 
ArrayList(Projects.FileNames), 
-                                    new ArrayList(ReferenceProjects.FileNames), tfc, 
this, 
-                                    WebMaps, ExcludeProjects, OutputDir, gacCache, 
referencesResolver);
+                            if (SolutionFile != null) {
+                                sln.File = SolutionFile;
                             }
 
+                            sln.Load(new ArrayList(Projects.FileNames), 
+                                    new ArrayList(ReferenceProjects.FileNames), 
gacCache, referencesResolver);
+
                             if (!sln.Compile(Configuration)) {
                                 throw new BuildException("Project build failed.", 
Location);
                             }
@@ -545,6 +559,7 @@ namespace NAnt.VSNet.Tasks {
         private WebMapCollection _webMaps;
         private bool _includeVSFolders = true;
         private bool _enableWebDav;
+        private IProjectFactory _projectFactory;
 
         #endregion Private Instance Fields
 

using System;
using System.CodeDom.Compiler;
using System.IO;
using System.Xml;

using NAnt.VSNet.Tasks;
using NAnt.Core.Util;

namespace NAnt.VSNet
{
        /// <summary>
        /// Interface used by the Solution to create Project instances.
        /// </summary>
        public interface IProjectFactory
        {
        ProjectBase LoadProject(Solution sln, SolutionTask slnTask, TempFileCollection 
tfc, GacCache gacCache, ReferencesResolver refResolver, DirectoryInfo outputDir, 
string path); 
        void ClearCache();
        bool IsSupportedProjectType(string path);
        string LoadGuid(string fileName);
        XmlDocument LoadProjectXml(string path);
    }
}

Reply via email to