/// <summary>
        /// Resolves the dependency names in an aggregate to NodeInfo instances, filling in the AggregateInfo.Dependenices array. Any referenced aggregates will also be linked, recursively.
        /// </summary>
        /// <param name="UnlinkedNode">The aggregate to link</param>
        /// <param name="NameToAggregateNode">Map of other aggregate names to their corresponding instance.</param>
        /// <param name="NameToBuildNode">Map from node names to their corresponding instance.</param>
        /// <param name="NumErrors">The number of errors output so far. Incremented if resolving this aggregate fails.</param>
        private static void LinkAggregate(UnlinkedAggregateNode UnlinkedNode, Dictionary <string, UnlinkedAggregateNode> NameToAggregateNode, Dictionary <string, UnlinkedBuildNode> NameToBuildNode, ref int NumErrors)
        {
            if (UnlinkedNode.Node.Dependencies == null)
            {
                UnlinkedNode.Node.Dependencies = new BuildNode[0];

                HashSet <BuildNode> Dependencies = new HashSet <BuildNode>();
                foreach (string DependencyName in UnlinkedNode.DependencyNames)
                {
                    UnlinkedAggregateNode AggregateNodeDependency;
                    if (NameToAggregateNode.TryGetValue(DependencyName, out AggregateNodeDependency))
                    {
                        LinkAggregate(AggregateNodeDependency, NameToAggregateNode, NameToBuildNode, ref NumErrors);
                        Dependencies.UnionWith(AggregateNodeDependency.Node.Dependencies);
                        continue;
                    }

                    UnlinkedBuildNode BuildNodeDependency;
                    if (NameToBuildNode.TryGetValue(DependencyName, out BuildNodeDependency))
                    {
                        Dependencies.Add(BuildNodeDependency.Node);
                        continue;
                    }

                    CommandUtils.LogError("Node {0} is not in the graph. It is a dependency of {1}.", DependencyName, UnlinkedNode.Node.Name);
                    NumErrors++;
                }

                UnlinkedNode.Node.Dependencies = Dependencies.ToArray();
            }
        }
        /// <summary>
        /// Resolves the names of each node and aggregates' dependencies, and links them together into the build graph.
        /// </summary>
        /// <param name="AggregateNameToInfo">Map of aggregate names to their info objects</param>
        /// <param name="NodeNameToInfo">Map of node names to their info objects</param>
        private static void LinkGraph(IEnumerable <AggregateNodeDefinition> AggregateNodeDefinitions, IEnumerable <BuildNodeDefinition> BuildNodeDefinitions, out List <AggregateNode> AggregateNodes, out List <BuildNode> BuildNodes)
        {
            // Create all the build nodes, and a dictionary to find them by name
            Dictionary <string, UnlinkedBuildNode> NameToBuildNode = new Dictionary <string, UnlinkedBuildNode>(StringComparer.InvariantCultureIgnoreCase);

            foreach (BuildNodeDefinition Definition in BuildNodeDefinitions)
            {
                UnlinkedBuildNode UnlinkedNode = new UnlinkedBuildNode();
                UnlinkedNode.InputDependencyNames = ParseSemicolonDelimitedList(Definition.DependsOn);
                UnlinkedNode.OrderDependencyNames = ParseSemicolonDelimitedList(Definition.RunAfter);
                UnlinkedNode.Node = Definition.CreateNode();
                NameToBuildNode.Add(Definition.Name, UnlinkedNode);
            }

            // Create all the aggregate nodes, and add them to a dictionary too
            Dictionary <string, UnlinkedAggregateNode> NameToAggregateNode = new Dictionary <string, UnlinkedAggregateNode>(StringComparer.InvariantCultureIgnoreCase);

            foreach (AggregateNodeDefinition Definition in AggregateNodeDefinitions)
            {
                UnlinkedAggregateNode UnlinkedNode = new UnlinkedAggregateNode();
                UnlinkedNode.DependencyNames = ParseSemicolonDelimitedList(Definition.DependsOn);
                UnlinkedNode.Node            = new AggregateNode(Definition);
                NameToAggregateNode.Add(Definition.Name, UnlinkedNode);
            }

            // Link the graph
            int NumErrors = 0;

            foreach (UnlinkedAggregateNode Pair in NameToAggregateNode.Values)
            {
                LinkAggregate(Pair, NameToAggregateNode, NameToBuildNode, ref NumErrors);
            }
            foreach (UnlinkedBuildNode Pair in NameToBuildNode.Values)
            {
                LinkNode(Pair, new List <UnlinkedBuildNode>(), NameToAggregateNode, NameToBuildNode, ref NumErrors);
            }
            if (NumErrors > 0)
            {
                throw new AutomationException("Failed to link graph ({0} errors).", NumErrors);
            }

            // Set the output lists
            AggregateNodes = NameToAggregateNode.Values.Select(x => x.Node).ToList();
            BuildNodes     = NameToBuildNode.Values.Select(x => x.Node).ToList();
        }