/// <summary> /// Order the list in build-order. /// </summary> /// <param name="projectList">List of file paths to projects</param> /// <returns>True if build order was determined. False if there was a circular dependency.</returns> public static bool SetBuildOrder(List <string> projectList) { bool changed; int changeCount = 0; int maxChanges = projectList.Count * projectList.Count; do { if (changeCount > maxChanges) { ErrorManager.Error("ParseProjects", "Project dependency dead-lock. There's a circular dependency in projects."); return(false); } changed = false; for (int i = 0; i < projectList.Count; ++i) { string proj1 = projectList[i]; using (ParseProject po = new ParseProject(proj1)) { for (int j = i + 1; j < projectList.Count; ++j) { string proj2 = projectList[j]; if (po.ProjectReferences.Contains(proj2, StringComparer.OrdinalIgnoreCase)) { ErrorManager.DebugF("'{0}' depends on '{1}'.\n\tPutting '{1}' in index '{2}'\n\tPutting '{0}' in index '{3}'" , proj1 , proj2 , i , i + 1 ); changed = true; projectList.RemoveAt(j); projectList.Insert(i, proj2); break; } } } if (changed) { ++changeCount; break; } } } while (changed); return(true); }
/// <summary> /// C'tor. /// </summary> /// <param name="projFile">Project file for which references will be listed.</param> public ParseProject(string projFile) { try { ErrorManager.DebugF("Parsing project '{0}' ...", projFile); _msbuildProj = new Project(projFile); // Check cache. Only valid project files get into the cache. _myCache = GetCache(_msbuildProj.FullPath); if (_myCache == null) { _cache.Add(this); } } catch (Microsoft.Build.Exceptions.InvalidProjectFileException ex) // Probably a .vdproj file. ignoreable. { ErrorManager.DebugF("Exception '{0}' while parsing '{1}'", ex.GetType().FullName, projFile); _msbuildProj = null; } }
/// <summary> /// Find projects that depend on a given project, and it's own project dependencies. /// </summary> /// <param name="args"></param> /// <param name="/project">The name of the project</param> /// <param name="/folder">Root folder where projects that depend on the given project are searched for</param> /// <param name="/out">Optional. File to dump results to. If null then results will be dumped to Console.</param> /// <returns></returns> public static int Main(string[] args) { string projFile; string outFile; List <string> topFolders = new List <string>(); if (!ParseCommandLine(topFolders, out projFile, out outFile)) { Usage(); return(-1); } // Setup search folders and project pattern projFile = ParseProject.NormalizeReferenceName(projFile); SearchFolders folders = new SearchFolders(); folders.AddFolders(topFolders); folders.AddPattern(projFile + ".*proj"); ParseProject.SetSearchFolders(folders); // List the projects the target project depends on List <string> dependentProjects = new List <string>(); foreach (string proj in folders.Search()) { dependentProjects.Add(proj); using (ParseProject po = new ParseProject(proj)) { dependentProjects.AddRange(po.ProjectReferences); } } // List the projects that depend on the target project: // Find all project files in the folders. IEnumerable <string> projFiles = folders.Search("*.*proj"); foreach (string p in projFiles) { using (ParseProject po = new ParseProject(p)) { // Test if this project depends on the target project if (po.ProjectReferences.Contains(projFile, StringComparer.OrdinalIgnoreCase)) { dependentProjects.Add(p); } } } // Unique, sorted list if (ErrorManager.Good) { dependentProjects = new List <string>(dependentProjects.Distinct(StringComparer.OrdinalIgnoreCase)); ParseProject.SetBuildOrder(dependentProjects); } // Dump project file, even if error(s) occured. if (!string.IsNullOrWhiteSpace(outFile)) { ParseProject.CreateBuildFile(outFile, dependentProjects); } else { foreach (string s in dependentProjects) { Console.WriteLine(s); } } return(ErrorManager.ExitCode); }
/// <summary> /// Parse a build.proj file, that already has all parsed dependent projects. /// Initially checks that build.proj file exists and had a newer modification time compared to the project file. /// </summary> private void ParseBuildProj() { try { if (_msbuildProj == null) { return; } // build.proj exists? string buildProjFile = Path.Combine(Path.GetDirectoryName(_msbuildProj.FullPath), "build.proj"); if (!File.Exists(buildProjFile)) { return; } // build.proj is newer than the project file? if (File.GetLastWriteTime(buildProjFile) < File.GetLastWriteTime(_msbuildProj.FullPath)) { return; } // Parse it! _allReferences = new List <string>(); _projReferences = new List <string>(); Project buildProj = new Project(buildProjFile); ProjectTargetInstance tgt = buildProj.Targets["Build"]; if (tgt == null) { return; } foreach (ProjectTaskInstance tsk in tgt.Tasks) { if (tsk.Name.Equals("MSBuild")) { if (tsk.Parameters.Keys.Contains("Projects")) { // Once a build.proj includes our project we know that subsequent project depend on us. string p = tsk.Parameters["Projects"]; if (Path.GetFileName(p).Equals(Path.GetFileName(_msbuildProj.FullPath), StringComparison.OrdinalIgnoreCase)) { break; } _allReferences.Add(p); AddProjectReference(p); using (ParseProject po = new ParseProject(p)) { _allReferences.AddRange(po.AllReferences); AddProjectReferences(po.ProjectReferences); } } } } } catch (Exception ex) { ErrorManager.DebugF("Failed parsing build.proj: {0}", ex.Message); _allReferences = null; _projReferences = null; return; } }