Beispiel #1
0
        /// <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);
        }
Beispiel #2
0
        /// <summary>
        /// Topologically sorts a set of targets.
        /// </summary>
        /// <param name="root">The name of the root target. The sort is created in such a way that the sequence of targets up to the root target is the minimum possible such sequence. Must not be <see langword="null" />.</param>
        /// <param name="targets">A collection of <see cref="Target" /> instances.</param>
        /// <returns>
        /// A collection of <see cref="Target" /> instances in sorted order.
        /// </returns>
        /// <exception cref="BuildException">There is a cyclic dependecy among the targets, or a named target does not exist.</exception>
        public TargetCollection TopologicalTargetSort(string root, TargetCollection targets)
        {
            TargetCollection executeTargets = new TargetCollection();
            Hashtable state = new Hashtable();
            Stack visiting = new Stack();

            // We first run a DFS based sort using the root as the starting node.
            // This creates the minimum sequence of Targets to the root node.
            // We then do a sort on any remaining unVISITED targets.
            // This is unnecessary for doing our build, but it catches
            // circular dependencies or missing Targets on the entire
            // dependency tree, not just on the Targets that depend on the
            // build Target.
            TopologicalTargetSort(root, targets, state, visiting, executeTargets);
            Log(Level.Debug, "Build sequence for target `" + root + "' is " + executeTargets);
            foreach (Target target in targets) {
                string st = (string) state[target.Name];

                if (st == null) {
                    TopologicalTargetSort(target.Name, targets, state, visiting, executeTargets);
                } else if (st == Project.Visiting) {
                    throw new Exception("Unexpected node in visiting state: " + target.Name);
                }
            }

            Log(Level.Debug, "Complete build sequence is " + executeTargets);
            return executeTargets;
        }