/// <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); } }