/// <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();
        }
        /// <summary>
        /// Resolves the dependencies in a build graph
        /// </summary>
        /// <param name="UnlinkedNode">The build node template and instance to link.</param>
        /// <param name="Stack">Stack of all the build nodes currently being processed. Used to detect cycles.</param>
        /// <param name="NameToAggregateNode">Mapping of aggregate node names to their template and instance.</param>
        /// <param name="NameToBuildNode">Mapping of build node names to their template and instance.</param>
        /// <param name="NumErrors">Number of errors encountered. Will be incremented for each error encountered.</param>
        private static void LinkNode(UnlinkedBuildNode UnlinkedNode, List <UnlinkedBuildNode> Stack, Dictionary <string, UnlinkedAggregateNode> NameToAggregateNode, Dictionary <string, UnlinkedBuildNode> NameToBuildNode, ref int NumErrors)
        {
            if (UnlinkedNode.Node.OrderDependencies == null)
            {
                Stack.Add(UnlinkedNode);

                int StackIdx = Stack.IndexOf(UnlinkedNode);
                if (StackIdx != Stack.Count - 1)
                {
                    // There's a cycle in the build graph. Print out the chain.
                    CommandUtils.LogError("Build graph contains a cycle ({0})", String.Join(" -> ", Stack.Skip(StackIdx).Select(x => x.Node.Name)));
                    NumErrors++;
                    UnlinkedNode.Node.OrderDependencies = new HashSet <BuildNode>();
                    UnlinkedNode.Node.InputDependencies = new HashSet <BuildNode>();
                }
                else
                {
                    // Find all the direct input dependencies
                    HashSet <BuildNode> DirectInputDependencies = new HashSet <BuildNode>();
                    foreach (string InputDependencyName in UnlinkedNode.InputDependencyNames)
                    {
                        if (!ResolveDependencies(InputDependencyName, NameToAggregateNode, NameToBuildNode, DirectInputDependencies))
                        {
                            CommandUtils.LogError("Node {0} is not in the graph. It is an input dependency of {1}.", InputDependencyName, UnlinkedNode.Node.Name);
                            NumErrors++;
                        }
                    }

                    // Find all the direct order dependencies. All the input dependencies are also order dependencies.
                    HashSet <BuildNode> DirectOrderDependencies = new HashSet <BuildNode>(DirectInputDependencies);
                    foreach (string OrderDependencyName in UnlinkedNode.OrderDependencyNames)
                    {
                        if (!ResolveDependencies(OrderDependencyName, NameToAggregateNode, NameToBuildNode, DirectOrderDependencies))
                        {
                            CommandUtils.LogError("Node {0} is not in the graph. It is an order dependency of {1}.", OrderDependencyName, UnlinkedNode.Node.Name);
                            NumErrors++;
                        }
                    }

                    // Link everything
                    foreach (BuildNode DirectOrderDependency in DirectOrderDependencies)
                    {
                        LinkNode(NameToBuildNode[DirectOrderDependency.Name], Stack, NameToAggregateNode, NameToBuildNode, ref NumErrors);
                    }

                    // Recursively include all the input dependencies
                    UnlinkedNode.Node.InputDependencies = new HashSet <BuildNode>(DirectInputDependencies);
                    foreach (BuildNode DirectInputDependency in DirectInputDependencies)
                    {
                        UnlinkedNode.Node.InputDependencies.UnionWith(DirectInputDependency.InputDependencies);
                    }

                    // Same for the order dependencies
                    UnlinkedNode.Node.OrderDependencies = new HashSet <BuildNode>(DirectOrderDependencies);
                    foreach (BuildNode DirectOrderDependency in DirectOrderDependencies)
                    {
                        UnlinkedNode.Node.OrderDependencies.UnionWith(DirectOrderDependency.OrderDependencies);
                    }
                }

                Stack.RemoveAt(Stack.Count - 1);
            }
        }