2 // SolutionParser.cs: Generates a project file from a solution file.
5 // Jonathan Chambers (joncham@gmail.com)
6 // Ankit Jain <jankit@novell.com>
7 // Lluis Sanchez Gual <lluis@novell.com>
9 // (C) 2009 Jonathan Chambers
10 // Copyright 2008, 2009 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Collections.Generic;
36 using System.Text.RegularExpressions;
38 using Microsoft.Build.BuildEngine;
40 namespace Mono.XBuild.CommandLine {
43 public string FileName;
45 public ProjectInfo (string name, string fileName)
51 public Dictionary<TargetInfo, TargetInfo> TargetMap = new Dictionary<TargetInfo, TargetInfo> ();
52 public Dictionary<Guid, ProjectInfo> Dependencies = new Dictionary<Guid, ProjectInfo> ();
56 public string Configuration;
57 public string Platform;
60 public TargetInfo (string configuration, string platform)
61 : this (configuration, platform, false)
65 public TargetInfo (string configuration, string platform, bool build)
67 Configuration = configuration;
74 class SolutionParser {
75 static string[] buildTargets = new string[] { "Build", "Clean", "Rebuild", "Publish" };
77 static string guidExpression = "{[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}}";
79 static Regex projectRegex = new Regex ("Project\\(\"(" + guidExpression + ")\"\\) = \"(.*?)\", \"(.*?)\", \"(" + guidExpression + ")\"(\\s*?)((\\s*?)ProjectSection\\((.*?)\\) = (.*?)EndProjectSection(\\s*?))*(\\s*?)EndProject?", RegexOptions.Singleline);
80 static Regex projectDependenciesRegex = new Regex ("ProjectSection\\((.*?)\\) = \\w*(.*?)EndProjectSection", RegexOptions.Singleline);
81 static Regex projectDependencyRegex = new Regex ("\\s*(" + guidExpression + ") = (" + guidExpression + ")");
83 static Regex globalRegex = new Regex ("Global(.*)EndGlobal", RegexOptions.Singleline);
84 static Regex globalSectionRegex = new Regex ("GlobalSection\\((.*?)\\) = \\w*(.*?)EndGlobalSection", RegexOptions.Singleline);
86 static Regex solutionConfigurationRegex = new Regex ("\\s*(.*?)\\|(.*?) = (.*?)\\|(.+)");
87 static Regex projectConfigurationActiveCfgRegex = new Regex ("\\s*(" + guidExpression + ")\\.(.+?)\\|(.+?)\\.ActiveCfg = (.+?)\\|(.+)");
88 static Regex projectConfigurationBuildRegex = new Regex ("\\s*(" + guidExpression + ")\\.(.*?)\\|(.*?)\\.Build\\.0 = (.*?)\\|(.+)");
90 static string solutionFolderGuid = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
91 static string vcprojGuid = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
93 public void ParseSolution (string file, Project p)
95 AddGeneralSettings (file, p);
97 StreamReader reader = new StreamReader (file);
98 string line = reader.ReadToEnd ();
99 line = line.Replace ("\r\n", "\n");
100 string solutionDir = Path.GetDirectoryName (file);
102 List<TargetInfo> solutionTargets = new List<TargetInfo> ();
103 Dictionary<Guid, ProjectInfo> projectInfos = new Dictionary<Guid, ProjectInfo> ();
105 Match m = projectRegex.Match (line);
107 ProjectInfo projectInfo = new ProjectInfo (m.Groups[2].Value, m.Groups[3].Value);
108 if (String.Compare (m.Groups [1].Value, solutionFolderGuid,
109 StringComparison.InvariantCultureIgnoreCase) == 0) {
110 // Ignore solution folders
114 if (String.Compare (m.Groups [1].Value, vcprojGuid,
115 StringComparison.InvariantCultureIgnoreCase) == 0) {
117 ErrorUtilities.ReportWarning (0, string.Format("Ignoring vcproj '{0}'.", projectInfo.Name));
122 projectInfos.Add (new Guid (m.Groups[4].Value), projectInfo);
124 Match projectSectionMatch = projectDependenciesRegex.Match (m.Groups[6].Value);
125 while (projectSectionMatch.Success) {
126 Match projectDependencyMatch = projectDependencyRegex.Match (projectSectionMatch.Value);
127 while (projectDependencyMatch.Success) {
128 // we might not have projectInfo available right now, so
129 // set it to null, and fill it in later
130 projectInfo.Dependencies [new Guid (projectDependencyMatch.Groups[1].Value)] = null;
131 projectDependencyMatch = projectDependencyMatch.NextMatch ();
133 projectSectionMatch = projectSectionMatch.NextMatch ();
138 foreach (ProjectInfo projectInfo in projectInfos.Values) {
139 Project currentProject = p.ParentEngine.CreateNewProject ();
140 currentProject.Load (Path.Combine (solutionDir,
141 projectInfo.FileName.Replace ('\\', Path.DirectorySeparatorChar)));
143 foreach (BuildItem bi in currentProject.GetEvaluatedItemsByName ("ProjectReference")) {
144 string projectReferenceGuid = bi.GetEvaluatedMetadata ("Project");
145 Guid guid = new Guid (projectReferenceGuid);
146 ProjectInfo info = projectInfos [guid];
148 // ignore if not found
149 projectInfo.Dependencies [guid] = info;
153 // fill in the project info for deps found in the .sln file
154 foreach (ProjectInfo projectInfo in projectInfos.Values) {
155 List<Guid> missingInfos = new List<Guid> ();
156 foreach (KeyValuePair<Guid, ProjectInfo> dependency in projectInfo.Dependencies) {
157 if (dependency.Value == null)
158 missingInfos.Add (dependency.Key);
161 foreach (Guid guid in missingInfos) {
163 if (projectInfos.TryGetValue (guid, out info))
164 projectInfo.Dependencies [guid] = info;
166 projectInfo.Dependencies.Remove (guid);
170 Match globalMatch = globalRegex.Match (line);
171 Match globalSectionMatch = globalSectionRegex.Match (globalMatch.Groups[1].Value);
172 while (globalSectionMatch.Success) {
173 string sectionType = globalSectionMatch.Groups[1].Value;
174 switch (sectionType) {
175 case "SolutionConfigurationPlatforms":
176 ParseSolutionConfigurationPlatforms (globalSectionMatch.Groups[2].Value, solutionTargets);
178 case "ProjectConfigurationPlatforms":
179 ParseProjectConfigurationPlatforms (globalSectionMatch.Groups[2].Value, projectInfos);
181 case "SolutionProperties":
182 ParseSolutionProperties (globalSectionMatch.Groups[2].Value);
184 case "NestedProjects":
187 ErrorUtilities.ReportWarning (0, string.Format("Don't know how to handle GlobalSection {0}, Ignoring.", sectionType));
190 globalSectionMatch = globalSectionMatch.NextMatch ();
193 int num_levels = AddBuildLevels (p, solutionTargets, projectInfos);
195 AddCurrentSolutionConfigurationContents (p, solutionTargets, projectInfos);
196 AddValidateSolutionConfiguration (p);
197 AddProjectTargets (p, solutionTargets, projectInfos);
198 AddSolutionTargets (p, num_levels);
201 void AddGeneralSettings (string solutionFile, Project p)
203 p.DefaultTargets = "Build";
204 p.InitialTargets = "ValidateSolutionConfiguration";
205 p.AddNewUsingTaskFromAssemblyName ("CreateTemporaryVCProject", "Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
206 p.AddNewUsingTaskFromAssemblyName ("ResolveVCProjectOutput", "Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
208 BuildPropertyGroup aspNetConfigurationPropertyGroup = p.AddNewPropertyGroup (true);
209 aspNetConfigurationPropertyGroup.Condition = " ('$(AspNetConfiguration)' == '') ";
210 aspNetConfigurationPropertyGroup.AddNewProperty ("AspNetConfiguration", "$(Configuration)");
212 string solutionFilePath = Path.GetFullPath (solutionFile);
213 BuildPropertyGroup solutionPropertyGroup = p.AddNewPropertyGroup (true);
214 solutionPropertyGroup.AddNewProperty ("SolutionDir", Path.GetDirectoryName (solutionFilePath) + Path.DirectorySeparatorChar);
215 solutionPropertyGroup.AddNewProperty ("SolutionExt", Path.GetExtension (solutionFile));
216 solutionPropertyGroup.AddNewProperty ("SolutionFileName", Path.GetFileName (solutionFile));
217 solutionPropertyGroup.AddNewProperty ("SolutionName", Path.GetFileNameWithoutExtension (solutionFile));
218 solutionPropertyGroup.AddNewProperty ("SolutionPath", solutionFilePath);
221 void ParseSolutionConfigurationPlatforms (string section, List<TargetInfo> solutionTargets)
223 Match solutionConfigurationPlatform = solutionConfigurationRegex.Match (section);
224 while (solutionConfigurationPlatform.Success) {
225 string solutionConfiguration = solutionConfigurationPlatform.Groups[1].Value;
226 string solutionPlatform = solutionConfigurationPlatform.Groups[2].Value;
227 solutionTargets.Add (new TargetInfo (solutionConfiguration, solutionPlatform));
228 solutionConfigurationPlatform = solutionConfigurationPlatform.NextMatch ();
232 void ParseProjectConfigurationPlatforms (string section, Dictionary<Guid, ProjectInfo> projectInfos)
234 List<Guid> missingGuids = new List<Guid> ();
235 Match projectConfigurationPlatform = projectConfigurationActiveCfgRegex.Match (section);
236 while (projectConfigurationPlatform.Success) {
237 Guid guid = new Guid (projectConfigurationPlatform.Groups[1].Value);
238 ProjectInfo projectInfo;
239 if (!projectInfos.TryGetValue (guid, out projectInfo)) {
240 if (!missingGuids.Contains (guid)) {
241 ErrorUtilities.ReportWarning (0, string.Format("Failed to find project {0}", guid));
242 missingGuids.Add (guid);
244 projectConfigurationPlatform = projectConfigurationPlatform.NextMatch ();
247 string solConf = projectConfigurationPlatform.Groups[2].Value;
248 string solPlat = projectConfigurationPlatform.Groups[3].Value;
249 string projConf = projectConfigurationPlatform.Groups[4].Value;
250 string projPlat = projectConfigurationPlatform.Groups[5].Value;
251 // hack, what are they doing here?
252 if (projPlat == "Any CPU")
254 projectInfo.TargetMap.Add (new TargetInfo (solConf, solPlat), new TargetInfo (projConf, projPlat));
255 projectConfigurationPlatform = projectConfigurationPlatform.NextMatch ();
257 Match projectConfigurationPlatformBuild = projectConfigurationBuildRegex.Match (section);
258 while (projectConfigurationPlatformBuild.Success) {
259 Guid guid = new Guid (projectConfigurationPlatformBuild.Groups[1].Value);
260 ProjectInfo projectInfo;
261 if (!projectInfos.TryGetValue (guid, out projectInfo)) {
262 if (!missingGuids.Contains (guid)) {
263 ErrorUtilities.ReportWarning (0, string.Format("Failed to find project {0}", guid));
264 missingGuids.Add (guid);
266 projectConfigurationPlatformBuild = projectConfigurationPlatformBuild.NextMatch ();
269 string solConf = projectConfigurationPlatformBuild.Groups[2].Value;
270 string solPlat = projectConfigurationPlatformBuild.Groups[3].Value;
271 string projConf = projectConfigurationPlatformBuild.Groups[4].Value;
272 string projPlat = projectConfigurationPlatformBuild.Groups[5].Value;
273 // hack, what are they doing here?
274 if (projPlat == "Any CPU")
276 projectInfo.TargetMap[new TargetInfo (solConf, solPlat)] = new TargetInfo (projConf, projPlat, true);
277 projectConfigurationPlatformBuild = projectConfigurationPlatformBuild.NextMatch ();
281 void ParseSolutionProperties (string section)
285 void AddCurrentSolutionConfigurationContents (Project p, List<TargetInfo> solutionTargets, Dictionary<Guid, ProjectInfo> projectInfos)
287 AddDefaultSolutionConfiguration (p,
288 solutionTargets.Count > 0 ?
289 solutionTargets [0] :
290 new TargetInfo ("Debug", "Any CPU"));
292 foreach (TargetInfo solutionTarget in solutionTargets) {
293 BuildPropertyGroup platformPropertyGroup = p.AddNewPropertyGroup (false);
294 platformPropertyGroup.Condition = string.Format (
295 " ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ",
296 solutionTarget.Configuration,
297 solutionTarget.Platform
300 string solutionConfigurationContents = "<SolutionConfiguration xmlns=\"\">";
301 foreach (KeyValuePair<Guid, ProjectInfo> projectInfo in projectInfos) {
302 foreach (KeyValuePair<TargetInfo, TargetInfo> targetInfo in projectInfo.Value.TargetMap) {
303 if (solutionTarget.Configuration == targetInfo.Key.Configuration && solutionTarget.Platform == targetInfo.Key.Platform) {
304 solutionConfigurationContents += string.Format ("<ProjectConfiguration Project=\"{0}\">{1}|{2}</ProjectConfiguration>",
305 projectInfo.Key.ToString ("B").ToUpper (), targetInfo.Value.Configuration, targetInfo.Value.Platform);
309 solutionConfigurationContents += "</SolutionConfiguration>";
311 platformPropertyGroup.AddNewProperty ("CurrentSolutionConfigurationContents", solutionConfigurationContents);
315 void AddDefaultSolutionConfiguration (Project p, TargetInfo target)
317 BuildPropertyGroup configurationPropertyGroup = p.AddNewPropertyGroup (true);
318 configurationPropertyGroup.Condition = " '$(Configuration)' == '' ";
319 configurationPropertyGroup.AddNewProperty ("Configuration", target.Configuration);
321 BuildPropertyGroup platformPropertyGroup = p.AddNewPropertyGroup (true);
322 platformPropertyGroup.Condition = " '$(Platform)' == '' ";
323 platformPropertyGroup.AddNewProperty ("Platform", target.Platform);
326 void AddWarningForMissingProjectConfiguration (Target target, string slnConfig, string slnPlatform, string projectName)
328 BuildTask task = target.AddNewTask ("Warning");
329 task.SetParameterValue ("Text",
330 String.Format ("The project configuration for project '{0}' corresponding " +
331 "to the solution configuration '{1}|{2}' was not found in the solution file.",
332 projectName, slnConfig, slnPlatform));
333 task.Condition = String.Format ("('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}')",
334 slnConfig, slnPlatform);
338 void AddValidateSolutionConfiguration (Project p)
340 Target t = p.Targets.AddNewTarget ("ValidateSolutionConfiguration");
341 BuildTask task = t.AddNewTask ("Error");
342 task.SetParameterValue ("Text", "Invalid solution configuration and platform: \"$(Configuration)|$(Platform)\".");
343 task.Condition = "('$(CurrentSolutionConfigurationContents)' == '') and ('$(SkipInvalidConfigurations)' != 'true')";
344 task = t.AddNewTask ("Warning");
345 task.SetParameterValue ("Text", "Invalid solution configuration and platform: \"$(Configuration)|$(Platform)\".");
346 task.Condition = "('$(CurrentSolutionConfigurationContents)' == '') and ('$(SkipInvalidConfigurations)' == 'true')";
347 task = t.AddNewTask ("Message");
348 task.SetParameterValue ("Text", "Building solution configuration \"$(Configuration)|$(Platform)\".");
349 task.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
352 void AddProjectTargets (Project p, List<TargetInfo> solutionTargets, Dictionary<Guid, ProjectInfo> projectInfos)
354 foreach (KeyValuePair<Guid, ProjectInfo> projectInfo in projectInfos) {
355 ProjectInfo project = projectInfo.Value;
356 foreach (string buildTarget in buildTargets) {
357 string target_name = project.Name +
358 (buildTarget == "Build" ? string.Empty : ":" + buildTarget);
360 if (IsBuildTargetName (project.Name))
361 target_name = "Solution:" + target_name;
363 Target target = p.Targets.AddNewTarget (target_name);
364 target.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
366 if (project.Dependencies.Count > 0) {
367 StringBuilder dependencies = new StringBuilder ();
368 foreach (ProjectInfo dependentInfo in project.Dependencies.Values) {
369 if (dependencies.Length > 0)
370 dependencies.Append (";");
371 if (IsBuildTargetName (dependentInfo.Name))
372 dependencies.Append ("Solution:");
373 dependencies.Append (dependentInfo.Name);
374 if (buildTarget != "Build")
375 dependencies.Append (":" + buildTarget);
377 target.DependsOnTargets = dependencies.ToString ();
380 foreach (TargetInfo targetInfo in solutionTargets) {
381 BuildTask task = null;
382 TargetInfo projectTargetInfo;
383 if (!project.TargetMap.TryGetValue (targetInfo, out projectTargetInfo)) {
384 AddWarningForMissingProjectConfiguration (target, targetInfo.Configuration,
385 targetInfo.Platform, project.Name);
388 if (projectTargetInfo.Build) {
389 task = target.AddNewTask ("MSBuild");
390 task.SetParameterValue ("Projects", project.FileName);
392 if (buildTarget != "Build")
393 task.SetParameterValue ("Targets", buildTarget);
394 task.SetParameterValue ("Properties", string.Format ("Configuration={0}; Platform={1}; BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)", projectTargetInfo.Configuration, projectTargetInfo.Platform));
396 task = target.AddNewTask ("Message");
397 task.SetParameterValue ("Text", string.Format ("Project \"{0}\" is disabled for solution configuration \"{1}|{2}\".", project.Name, targetInfo.Configuration, targetInfo.Platform));
399 task.Condition = string.Format (" ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ", targetInfo.Configuration, targetInfo.Platform);
405 bool IsBuildTargetName (string name)
407 foreach (string tgt in buildTargets)
413 // returns number of levels
414 int AddBuildLevels (Project p, List<TargetInfo> solutionTargets, Dictionary<Guid, ProjectInfo> projectInfos)
416 List<ProjectInfo>[] infosByLevel = TopologicalSort<ProjectInfo> (projectInfos.Values);
418 foreach (TargetInfo targetInfo in solutionTargets) {
419 BuildItemGroup big = p.AddNewItemGroup ();
420 big.Condition = String.Format (" ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ",
421 targetInfo.Configuration, targetInfo.Platform);
423 //FIXME: every level has projects that can be built in parallel.
424 // levels are ordered on the basis of the dependency graph
426 for (int i = 0; i < infosByLevel.Length; i ++) {
427 string build_level = String.Format ("BuildLevel{0}", i);
428 string skip_level = String.Format ("SkipLevel{0}", i);
429 string missing_level = String.Format ("MissingConfigLevel{0}", i);
431 foreach (ProjectInfo projectInfo in infosByLevel [i]) {
432 TargetInfo projectTargetInfo;
433 if (!projectInfo.TargetMap.TryGetValue (targetInfo, out projectTargetInfo)) {
434 // missing project config
435 big.AddNewItem (missing_level, projectInfo.Name);
439 if (projectTargetInfo.Build) {
440 BuildItem item = big.AddNewItem (build_level, projectInfo.FileName);
441 item.SetMetadata ("Configuration", projectTargetInfo.Configuration);
442 item.SetMetadata ("Platform", projectTargetInfo.Platform);
445 big.AddNewItem (skip_level, projectInfo.Name);
451 return infosByLevel.Length;
454 void AddSolutionTargets (Project p, int num_levels)
456 foreach (string buildTarget in buildTargets) {
457 Target t = p.Targets.AddNewTarget (buildTarget);
458 t.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
460 for (int i = 0; i < num_levels; i ++) {
461 string level_str = String.Format ("BuildLevel{0}", i);
462 BuildTask task = t.AddNewTask ("MSBuild");
463 task.SetParameterValue ("Condition", String.Format ("'@({0})' != ''", level_str));
464 task.SetParameterValue ("Projects", String.Format ("@({0})", level_str));
465 task.SetParameterValue ("Properties",
466 string.Format ("Configuration=%(Configuration); Platform=%(Platform); BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"));
467 if (buildTarget != "Build")
468 task.SetParameterValue ("Targets", buildTarget);
469 //FIXME: change this to BuildInParallel=true, when parallel
470 // build support gets added
471 task.SetParameterValue ("RunEachTargetSeparately", "true");
473 level_str = String.Format ("SkipLevel{0}", i);
474 task = t.AddNewTask ("Message");
475 task.Condition = String.Format ("'@({0})' != ''", level_str);
476 task.SetParameterValue ("Text",
477 String.Format ("The project '%({0}.Identity)' is disabled for solution " +
478 "configuration '$(Configuration)|$(Platform)'.", level_str));
480 level_str = String.Format ("MissingConfigLevel{0}", i);
481 task = t.AddNewTask ("Warning");
482 task.Condition = String.Format ("'@({0})' != ''", level_str);
483 task.SetParameterValue ("Text",
484 String.Format ("The project configuration for project '%({0}.Identity)' " +
485 "corresponding to the solution configuration " +
486 "'$(Configuration)|$(Platform)' was not found.", level_str));
491 // Sorts the ProjectInfo dependency graph, to obtain
492 // a series of build levels with projects. Projects
493 // in each level can be run parallel (no inter-dependency).
494 static List<T>[] TopologicalSort<T> (IEnumerable<T> items) where T: ProjectInfo
497 allItems = items as IList<T>;
498 if (allItems == null)
499 allItems = new List<T> (items);
501 bool[] inserted = new bool[allItems.Count];
502 bool[] triedToInsert = new bool[allItems.Count];
503 int[] levels = new int [allItems.Count];
506 for (int i = 0; i < allItems.Count; ++i) {
507 int d = Insert<T> (i, allItems, levels, inserted, triedToInsert);
512 // Separate out the project infos by build level
513 List<T>[] infosByLevel = new List<T>[maxdepth];
514 for (int i = 0; i < levels.Length; i ++) {
515 int level = levels [i] - 1;
516 if (infosByLevel [level] == null)
517 infosByLevel [level] = new List<T> ();
519 infosByLevel [level].Add (allItems [i]);
525 // returns level# for the project
526 static int Insert<T> (int index, IList<T> allItems, int[] levels, bool[] inserted, bool[] triedToInsert)
529 if (inserted [index])
530 return levels [index];
532 if (triedToInsert[index])
533 throw new InvalidOperationException ("Cyclic dependency found in the project dependency graph");
535 triedToInsert[index] = true;
536 ProjectInfo insertItem = allItems[index];
539 foreach (ProjectInfo dependency in insertItem.Dependencies.Values) {
540 for (int j = 0; j < allItems.Count; ++j) {
541 ProjectInfo checkItem = allItems [j];
542 if (dependency.FileName == checkItem.FileName) {
543 int d = Insert (j, allItems, levels, inserted, triedToInsert);
544 maxdepth = d > maxdepth ? d : maxdepth;
549 levels [index] = maxdepth + 1;
550 inserted [index] = true;
552 return levels [index];