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 internal delegate void RaiseWarningHandler (int errorNumber, string message);
76 class SolutionParser {
77 static string[] buildTargets = new string[] { "Build", "Clean", "Rebuild", "Publish" };
79 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}}";
81 static Regex projectRegex = new Regex ("Project\\(\"(" + guidExpression + ")\"\\) = \"(.*?)\", \"(.*?)\", \"(" + guidExpression + ")\"(\\s*?)((\\s*?)ProjectSection\\((.*?)\\) = (.*?)EndProjectSection(\\s*?))*(\\s*?)EndProject?", RegexOptions.Singleline);
82 static Regex projectDependenciesRegex = new Regex ("ProjectSection\\((.*?)\\) = \\w*(.*?)EndProjectSection", RegexOptions.Singleline);
83 static Regex projectDependencyRegex = new Regex ("\\s*(" + guidExpression + ") = (" + guidExpression + ")");
85 static Regex globalRegex = new Regex ("Global(.*)EndGlobal", RegexOptions.Singleline);
86 static Regex globalSectionRegex = new Regex ("GlobalSection\\((.*?)\\) = \\w*(.*?)EndGlobalSection", RegexOptions.Singleline);
88 static Regex solutionConfigurationRegex = new Regex ("\\s*(.*?)\\|(.*?) = (.*?)\\|(.+)");
89 static Regex projectConfigurationActiveCfgRegex = new Regex ("\\s*(" + guidExpression + ")\\.(.+?)\\|(.+?)\\.ActiveCfg = (.+?)\\|(.+)");
90 static Regex projectConfigurationBuildRegex = new Regex ("\\s*(" + guidExpression + ")\\.(.*?)\\|(.*?)\\.Build\\.0 = (.*?)\\|(.+)");
92 static string solutionFolderGuid = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";
93 static string vcprojGuid = "{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}";
95 RaiseWarningHandler RaiseWarning;
97 public void ParseSolution (string file, Project p, RaiseWarningHandler RaiseWarning)
99 this.RaiseWarning = RaiseWarning;
100 AddGeneralSettings (file, p);
102 StreamReader reader = new StreamReader (file);
103 string line = reader.ReadToEnd ();
104 line = line.Replace ("\r\n", "\n");
105 string solutionDir = Path.GetDirectoryName (file);
107 List<TargetInfo> solutionTargets = new List<TargetInfo> ();
108 Dictionary<Guid, ProjectInfo> projectInfos = new Dictionary<Guid, ProjectInfo> ();
110 Match m = projectRegex.Match (line);
112 ProjectInfo projectInfo = new ProjectInfo (m.Groups[2].Value, m.Groups[3].Value);
113 if (String.Compare (m.Groups [1].Value, solutionFolderGuid,
114 StringComparison.InvariantCultureIgnoreCase) == 0) {
115 // Ignore solution folders
119 if (String.Compare (m.Groups [1].Value, vcprojGuid,
120 StringComparison.InvariantCultureIgnoreCase) == 0) {
122 RaiseWarning (0, string.Format("Ignoring vcproj '{0}'.", projectInfo.Name));
127 projectInfos.Add (new Guid (m.Groups[4].Value), projectInfo);
129 Match projectSectionMatch = projectDependenciesRegex.Match (m.Groups[6].Value);
130 while (projectSectionMatch.Success) {
131 Match projectDependencyMatch = projectDependencyRegex.Match (projectSectionMatch.Value);
132 while (projectDependencyMatch.Success) {
133 // we might not have projectInfo available right now, so
134 // set it to null, and fill it in later
135 projectInfo.Dependencies [new Guid (projectDependencyMatch.Groups[1].Value)] = null;
136 projectDependencyMatch = projectDependencyMatch.NextMatch ();
138 projectSectionMatch = projectSectionMatch.NextMatch ();
143 foreach (ProjectInfo projectInfo in projectInfos.Values) {
144 Project currentProject = p.ParentEngine.CreateNewProject ();
145 currentProject.Load (Path.Combine (solutionDir,
146 projectInfo.FileName.Replace ('\\', Path.DirectorySeparatorChar)));
148 foreach (BuildItem bi in currentProject.GetEvaluatedItemsByName ("ProjectReference")) {
149 string projectReferenceGuid = bi.GetEvaluatedMetadata ("Project");
150 Guid guid = new Guid (projectReferenceGuid);
152 if (projectInfos.TryGetValue (guid, out info))
153 // ignore if not found
154 projectInfo.Dependencies [guid] = info;
158 // fill in the project info for deps found in the .sln file
159 foreach (ProjectInfo projectInfo in projectInfos.Values) {
160 List<Guid> missingInfos = new List<Guid> ();
161 foreach (KeyValuePair<Guid, ProjectInfo> dependency in projectInfo.Dependencies) {
162 if (dependency.Value == null)
163 missingInfos.Add (dependency.Key);
166 foreach (Guid guid in missingInfos) {
168 if (projectInfos.TryGetValue (guid, out info))
169 projectInfo.Dependencies [guid] = info;
171 projectInfo.Dependencies.Remove (guid);
175 Match globalMatch = globalRegex.Match (line);
176 Match globalSectionMatch = globalSectionRegex.Match (globalMatch.Groups[1].Value);
177 while (globalSectionMatch.Success) {
178 string sectionType = globalSectionMatch.Groups[1].Value;
179 switch (sectionType) {
180 case "SolutionConfigurationPlatforms":
181 ParseSolutionConfigurationPlatforms (globalSectionMatch.Groups[2].Value, solutionTargets);
183 case "ProjectConfigurationPlatforms":
184 ParseProjectConfigurationPlatforms (globalSectionMatch.Groups[2].Value, projectInfos);
186 case "SolutionProperties":
187 ParseSolutionProperties (globalSectionMatch.Groups[2].Value);
189 case "NestedProjects":
192 RaiseWarning (0, string.Format("Don't know how to handle GlobalSection {0}, Ignoring.", sectionType));
195 globalSectionMatch = globalSectionMatch.NextMatch ();
198 int num_levels = AddBuildLevels (p, solutionTargets, projectInfos);
200 AddCurrentSolutionConfigurationContents (p, solutionTargets, projectInfos);
201 AddValidateSolutionConfiguration (p);
202 AddProjectTargets (p, solutionTargets, projectInfos);
203 AddSolutionTargets (p, num_levels);
206 void AddGeneralSettings (string solutionFile, Project p)
208 p.DefaultTargets = "Build";
209 p.InitialTargets = "ValidateSolutionConfiguration";
210 p.AddNewUsingTaskFromAssemblyName ("CreateTemporaryVCProject", "Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
211 p.AddNewUsingTaskFromAssemblyName ("ResolveVCProjectOutput", "Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
213 BuildPropertyGroup aspNetConfigurationPropertyGroup = p.AddNewPropertyGroup (true);
214 aspNetConfigurationPropertyGroup.Condition = " ('$(AspNetConfiguration)' == '') ";
215 aspNetConfigurationPropertyGroup.AddNewProperty ("AspNetConfiguration", "$(Configuration)");
217 string solutionFilePath = Path.GetFullPath (solutionFile);
218 BuildPropertyGroup solutionPropertyGroup = p.AddNewPropertyGroup (true);
219 solutionPropertyGroup.AddNewProperty ("SolutionDir", Path.GetDirectoryName (solutionFilePath) + Path.DirectorySeparatorChar);
220 solutionPropertyGroup.AddNewProperty ("SolutionExt", Path.GetExtension (solutionFile));
221 solutionPropertyGroup.AddNewProperty ("SolutionFileName", Path.GetFileName (solutionFile));
222 solutionPropertyGroup.AddNewProperty ("SolutionName", Path.GetFileNameWithoutExtension (solutionFile));
223 solutionPropertyGroup.AddNewProperty ("SolutionPath", solutionFilePath);
226 void ParseSolutionConfigurationPlatforms (string section, List<TargetInfo> solutionTargets)
228 Match solutionConfigurationPlatform = solutionConfigurationRegex.Match (section);
229 while (solutionConfigurationPlatform.Success) {
230 string solutionConfiguration = solutionConfigurationPlatform.Groups[1].Value;
231 string solutionPlatform = solutionConfigurationPlatform.Groups[2].Value;
232 solutionTargets.Add (new TargetInfo (solutionConfiguration, solutionPlatform));
233 solutionConfigurationPlatform = solutionConfigurationPlatform.NextMatch ();
237 void ParseProjectConfigurationPlatforms (string section, Dictionary<Guid, ProjectInfo> projectInfos)
239 List<Guid> missingGuids = new List<Guid> ();
240 Match projectConfigurationPlatform = projectConfigurationActiveCfgRegex.Match (section);
241 while (projectConfigurationPlatform.Success) {
242 Guid guid = new Guid (projectConfigurationPlatform.Groups[1].Value);
243 ProjectInfo projectInfo;
244 if (!projectInfos.TryGetValue (guid, out projectInfo)) {
245 if (!missingGuids.Contains (guid)) {
246 RaiseWarning (0, string.Format("Failed to find project {0}", guid));
247 missingGuids.Add (guid);
249 projectConfigurationPlatform = projectConfigurationPlatform.NextMatch ();
252 string solConf = projectConfigurationPlatform.Groups[2].Value;
253 string solPlat = projectConfigurationPlatform.Groups[3].Value;
254 string projConf = projectConfigurationPlatform.Groups[4].Value;
255 string projPlat = projectConfigurationPlatform.Groups[5].Value;
256 // hack, what are they doing here?
257 if (projPlat == "Any CPU")
259 projectInfo.TargetMap.Add (new TargetInfo (solConf, solPlat), new TargetInfo (projConf, projPlat));
260 projectConfigurationPlatform = projectConfigurationPlatform.NextMatch ();
262 Match projectConfigurationPlatformBuild = projectConfigurationBuildRegex.Match (section);
263 while (projectConfigurationPlatformBuild.Success) {
264 Guid guid = new Guid (projectConfigurationPlatformBuild.Groups[1].Value);
265 ProjectInfo projectInfo;
266 if (!projectInfos.TryGetValue (guid, out projectInfo)) {
267 if (!missingGuids.Contains (guid)) {
268 RaiseWarning (0, string.Format("Failed to find project {0}", guid));
269 missingGuids.Add (guid);
271 projectConfigurationPlatformBuild = projectConfigurationPlatformBuild.NextMatch ();
274 string solConf = projectConfigurationPlatformBuild.Groups[2].Value;
275 string solPlat = projectConfigurationPlatformBuild.Groups[3].Value;
276 string projConf = projectConfigurationPlatformBuild.Groups[4].Value;
277 string projPlat = projectConfigurationPlatformBuild.Groups[5].Value;
278 // hack, what are they doing here?
279 if (projPlat == "Any CPU")
281 projectInfo.TargetMap[new TargetInfo (solConf, solPlat)] = new TargetInfo (projConf, projPlat, true);
282 projectConfigurationPlatformBuild = projectConfigurationPlatformBuild.NextMatch ();
286 void ParseSolutionProperties (string section)
290 void AddCurrentSolutionConfigurationContents (Project p, List<TargetInfo> solutionTargets, Dictionary<Guid, ProjectInfo> projectInfos)
292 AddDefaultSolutionConfiguration (p,
293 solutionTargets.Count > 0 ?
294 solutionTargets [0] :
295 new TargetInfo ("Debug", "Any CPU"));
297 foreach (TargetInfo solutionTarget in solutionTargets) {
298 BuildPropertyGroup platformPropertyGroup = p.AddNewPropertyGroup (false);
299 platformPropertyGroup.Condition = string.Format (
300 " ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ",
301 solutionTarget.Configuration,
302 solutionTarget.Platform
305 string solutionConfigurationContents = "<SolutionConfiguration xmlns=\"\">";
306 foreach (KeyValuePair<Guid, ProjectInfo> projectInfo in projectInfos) {
307 foreach (KeyValuePair<TargetInfo, TargetInfo> targetInfo in projectInfo.Value.TargetMap) {
308 if (solutionTarget.Configuration == targetInfo.Key.Configuration && solutionTarget.Platform == targetInfo.Key.Platform) {
309 solutionConfigurationContents += string.Format ("<ProjectConfiguration Project=\"{0}\">{1}|{2}</ProjectConfiguration>",
310 projectInfo.Key.ToString ("B").ToUpper (), targetInfo.Value.Configuration, targetInfo.Value.Platform);
314 solutionConfigurationContents += "</SolutionConfiguration>";
316 platformPropertyGroup.AddNewProperty ("CurrentSolutionConfigurationContents", solutionConfigurationContents);
320 void AddDefaultSolutionConfiguration (Project p, TargetInfo target)
322 BuildPropertyGroup configurationPropertyGroup = p.AddNewPropertyGroup (true);
323 configurationPropertyGroup.Condition = " '$(Configuration)' == '' ";
324 configurationPropertyGroup.AddNewProperty ("Configuration", target.Configuration);
326 BuildPropertyGroup platformPropertyGroup = p.AddNewPropertyGroup (true);
327 platformPropertyGroup.Condition = " '$(Platform)' == '' ";
328 platformPropertyGroup.AddNewProperty ("Platform", target.Platform);
331 void AddWarningForMissingProjectConfiguration (Target target, string slnConfig, string slnPlatform, string projectName)
333 BuildTask task = target.AddNewTask ("Warning");
334 task.SetParameterValue ("Text",
335 String.Format ("The project configuration for project '{0}' corresponding " +
336 "to the solution configuration '{1}|{2}' was not found in the solution file.",
337 projectName, slnConfig, slnPlatform));
338 task.Condition = String.Format ("('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}')",
339 slnConfig, slnPlatform);
343 void AddValidateSolutionConfiguration (Project p)
345 Target t = p.Targets.AddNewTarget ("ValidateSolutionConfiguration");
346 BuildTask task = t.AddNewTask ("Error");
347 task.SetParameterValue ("Text", "Invalid solution configuration and platform: \"$(Configuration)|$(Platform)\".");
348 task.Condition = "('$(CurrentSolutionConfigurationContents)' == '') and ('$(SkipInvalidConfigurations)' != 'true')";
349 task = t.AddNewTask ("Warning");
350 task.SetParameterValue ("Text", "Invalid solution configuration and platform: \"$(Configuration)|$(Platform)\".");
351 task.Condition = "('$(CurrentSolutionConfigurationContents)' == '') and ('$(SkipInvalidConfigurations)' == 'true')";
352 task = t.AddNewTask ("Message");
353 task.SetParameterValue ("Text", "Building solution configuration \"$(Configuration)|$(Platform)\".");
354 task.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
357 void AddProjectTargets (Project p, List<TargetInfo> solutionTargets, Dictionary<Guid, ProjectInfo> projectInfos)
359 foreach (KeyValuePair<Guid, ProjectInfo> projectInfo in projectInfos) {
360 ProjectInfo project = projectInfo.Value;
361 foreach (string buildTarget in buildTargets) {
362 string target_name = project.Name +
363 (buildTarget == "Build" ? string.Empty : ":" + buildTarget);
365 if (IsBuildTargetName (project.Name))
366 target_name = "Solution:" + target_name;
368 Target target = p.Targets.AddNewTarget (target_name);
369 target.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
371 if (project.Dependencies.Count > 0) {
372 StringBuilder dependencies = new StringBuilder ();
373 foreach (ProjectInfo dependentInfo in project.Dependencies.Values) {
374 if (dependencies.Length > 0)
375 dependencies.Append (";");
376 if (IsBuildTargetName (dependentInfo.Name))
377 dependencies.Append ("Solution:");
378 dependencies.Append (dependentInfo.Name);
379 if (buildTarget != "Build")
380 dependencies.Append (":" + buildTarget);
382 target.DependsOnTargets = dependencies.ToString ();
385 foreach (TargetInfo targetInfo in solutionTargets) {
386 BuildTask task = null;
387 TargetInfo projectTargetInfo;
388 if (!project.TargetMap.TryGetValue (targetInfo, out projectTargetInfo)) {
389 AddWarningForMissingProjectConfiguration (target, targetInfo.Configuration,
390 targetInfo.Platform, project.Name);
393 if (projectTargetInfo.Build) {
394 task = target.AddNewTask ("MSBuild");
395 task.SetParameterValue ("Projects", project.FileName);
397 if (buildTarget != "Build")
398 task.SetParameterValue ("Targets", buildTarget);
399 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));
401 task = target.AddNewTask ("Message");
402 task.SetParameterValue ("Text", string.Format ("Project \"{0}\" is disabled for solution configuration \"{1}|{2}\".", project.Name, targetInfo.Configuration, targetInfo.Platform));
404 task.Condition = string.Format (" ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ", targetInfo.Configuration, targetInfo.Platform);
410 bool IsBuildTargetName (string name)
412 foreach (string tgt in buildTargets)
418 // returns number of levels
419 int AddBuildLevels (Project p, List<TargetInfo> solutionTargets, Dictionary<Guid, ProjectInfo> projectInfos)
421 List<ProjectInfo>[] infosByLevel = TopologicalSort<ProjectInfo> (projectInfos.Values);
423 foreach (TargetInfo targetInfo in solutionTargets) {
424 BuildItemGroup big = p.AddNewItemGroup ();
425 big.Condition = String.Format (" ('$(Configuration)' == '{0}') and ('$(Platform)' == '{1}') ",
426 targetInfo.Configuration, targetInfo.Platform);
428 //FIXME: every level has projects that can be built in parallel.
429 // levels are ordered on the basis of the dependency graph
431 for (int i = 0; i < infosByLevel.Length; i ++) {
432 string build_level = String.Format ("BuildLevel{0}", i);
433 string skip_level = String.Format ("SkipLevel{0}", i);
434 string missing_level = String.Format ("MissingConfigLevel{0}", i);
436 foreach (ProjectInfo projectInfo in infosByLevel [i]) {
437 TargetInfo projectTargetInfo;
438 if (!projectInfo.TargetMap.TryGetValue (targetInfo, out projectTargetInfo)) {
439 // missing project config
440 big.AddNewItem (missing_level, projectInfo.Name);
444 if (projectTargetInfo.Build) {
445 BuildItem item = big.AddNewItem (build_level, projectInfo.FileName);
446 item.SetMetadata ("Configuration", projectTargetInfo.Configuration);
447 item.SetMetadata ("Platform", projectTargetInfo.Platform);
450 big.AddNewItem (skip_level, projectInfo.Name);
456 return infosByLevel.Length;
459 void AddSolutionTargets (Project p, int num_levels)
461 foreach (string buildTarget in buildTargets) {
462 Target t = p.Targets.AddNewTarget (buildTarget);
463 t.Condition = "'$(CurrentSolutionConfigurationContents)' != ''";
465 for (int i = 0; i < num_levels; i ++) {
466 string level_str = String.Format ("BuildLevel{0}", i);
467 BuildTask task = t.AddNewTask ("MSBuild");
468 task.SetParameterValue ("Condition", String.Format ("'@({0})' != ''", level_str));
469 task.SetParameterValue ("Projects", String.Format ("@({0})", level_str));
470 task.SetParameterValue ("Properties",
471 string.Format ("Configuration=%(Configuration); Platform=%(Platform); BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"));
472 if (buildTarget != "Build")
473 task.SetParameterValue ("Targets", buildTarget);
474 //FIXME: change this to BuildInParallel=true, when parallel
475 // build support gets added
476 task.SetParameterValue ("RunEachTargetSeparately", "true");
478 level_str = String.Format ("SkipLevel{0}", i);
479 task = t.AddNewTask ("Message");
480 task.Condition = String.Format ("'@({0})' != ''", level_str);
481 task.SetParameterValue ("Text",
482 String.Format ("The project '%({0}.Identity)' is disabled for solution " +
483 "configuration '$(Configuration)|$(Platform)'.", level_str));
485 level_str = String.Format ("MissingConfigLevel{0}", i);
486 task = t.AddNewTask ("Warning");
487 task.Condition = String.Format ("'@({0})' != ''", level_str);
488 task.SetParameterValue ("Text",
489 String.Format ("The project configuration for project '%({0}.Identity)' " +
490 "corresponding to the solution configuration " +
491 "'$(Configuration)|$(Platform)' was not found.", level_str));
496 // Sorts the ProjectInfo dependency graph, to obtain
497 // a series of build levels with projects. Projects
498 // in each level can be run parallel (no inter-dependency).
499 static List<T>[] TopologicalSort<T> (IEnumerable<T> items) where T: ProjectInfo
502 allItems = items as IList<T>;
503 if (allItems == null)
504 allItems = new List<T> (items);
506 bool[] inserted = new bool[allItems.Count];
507 bool[] triedToInsert = new bool[allItems.Count];
508 int[] levels = new int [allItems.Count];
511 for (int i = 0; i < allItems.Count; ++i) {
512 int d = Insert<T> (i, allItems, levels, inserted, triedToInsert);
517 // Separate out the project infos by build level
518 List<T>[] infosByLevel = new List<T>[maxdepth];
519 for (int i = 0; i < levels.Length; i ++) {
520 int level = levels [i] - 1;
521 if (infosByLevel [level] == null)
522 infosByLevel [level] = new List<T> ();
524 infosByLevel [level].Add (allItems [i]);
530 // returns level# for the project
531 static int Insert<T> (int index, IList<T> allItems, int[] levels, bool[] inserted, bool[] triedToInsert)
534 if (inserted [index])
535 return levels [index];
537 if (triedToInsert[index])
538 throw new InvalidOperationException ("Cyclic dependency found in the project dependency graph");
540 triedToInsert[index] = true;
541 ProjectInfo insertItem = allItems[index];
544 foreach (ProjectInfo dependency in insertItem.Dependencies.Values) {
545 for (int j = 0; j < allItems.Count; ++j) {
546 ProjectInfo checkItem = allItems [j];
547 if (dependency.FileName == checkItem.FileName) {
548 int d = Insert (j, allItems, levels, inserted, triedToInsert);
549 maxdepth = d > maxdepth ? d : maxdepth;
554 levels [index] = maxdepth + 1;
555 inserted [index] = true;
557 return levels [index];