/// <summary> /// <para> /// Performs a single step in a recursive depth-first-search traversal /// of the target dependency tree. /// </para> /// <para> /// The current target is first set to the "visiting" state, and pushed /// onto the "visiting" stack. /// </para> /// <para> /// An exception is then thrown if any child of the current node is in /// the visiting state, as that implies a circular dependency. The /// exception contains details of the cycle, using elements of the /// "visiting" stack. /// </para> /// <para> /// If any child has not already been "visited", this method is called /// recursively on it. /// </para> /// <para> /// The current target is then added to the ordered list of targets. /// Note that this is performed after the children have been visited in /// order to get the correct order. The current target is set to the /// "visited" state. /// </para> /// <para> /// By the time this method returns, the ordered list contains the /// sequence of targets up to and including the current target. /// </para> /// </summary> /// <param name="root">The current target to inspect. Must not be <see langword="null" />.</param> /// <param name="targets">A collection of <see cref="Target" /> instances.</param> /// <param name="state">A mapping from targets to states The states in question are "VISITING" and "VISITED". Must not be <see langword="null" />.</param> /// <param name="visiting">A stack of targets which are currently being visited. Must not be <see langword="null" />.</param> /// <param name="executeTargets">The list to add target names to. This will end up containing the complete list of depenencies in dependency order. Must not be <see langword="null" />.</param> /// <exception cref="BuildException"> /// <para>A non-existent target is specified</para> /// <para>-or-</para> /// <para>A circular dependency is detected.</para> /// </exception> private void TopologicalTargetSort(string root, TargetCollection targets, Hashtable state, Stack visiting, TargetCollection executeTargets) { state[root] = Project.Visiting; visiting.Push(root); Target target = (Target) targets.Find(root); if (target == null) { // check if there's a wildcard target defined target = (Target) targets.Find(WildTarget); if (target != null) { // if a wildcard target exists, then treat the wildcard // target as the requested target target = target.Clone(); target.Name = root; } else { StringBuilder sb = new StringBuilder("Target '"); sb.Append(root); sb.Append("' does not exist in this project."); visiting.Pop(); if (visiting.Count > 0) { string parent = (string) visiting.Peek(); sb.Append(" "); sb.Append("It is used from target '"); sb.Append(parent); sb.Append("'."); } throw new BuildException(sb.ToString()); } } foreach (string dependency in target.Dependencies) { string m = (string) state[dependency]; if (m == null) { // Not been visited TopologicalTargetSort(dependency, targets, state, visiting, executeTargets); } else if (m == Project.Visiting) { // Currently visiting this node, so have a cycle throw CreateCircularException(dependency, visiting); } } string p = (string) visiting.Pop(); if (root != p) { throw new Exception("Unexpected internal error: expected to pop " + root + " but got " + p); } state[root] = Project.Visited; executeTargets.Add(target); }
private void PopulateExecutionGraph(string root, TargetCollection targets, ExecutionGraph graph) { Target target = targets.Find(root); ExecutionNode node = graph.GetNode(root); if (target == null) { target = targets.Find(WildTarget); } bool noDependencies = true; foreach (string dependencyName in target.Dependencies) { PopulateExecutionGraph(dependencyName, targets, graph); ExecutionNode dependencyNode = graph.GetNode(dependencyName); dependencyNode.RegisterDependantNode(node); noDependencies = false; } if (noDependencies) { graph.RegisterLeafNode(node); } }