public IReadOnlyDictionary <ProjectGraphNode, ImmutableList <string> > GetTargetLists(ICollection <string> entryProjectTargets) { ThrowOnEmptyTargetNames(entryProjectTargets); // Seed the dictionary with empty lists for every node. In this particular case though an empty list means "build nothing" rather than "default targets". var targetLists = ProjectNodes.ToDictionary(node => node, node => ImmutableList <string> .Empty); var encounteredEdges = new HashSet <ProjectGraphBuildRequest>(); var edgesToVisit = new Queue <ProjectGraphBuildRequest>(); // Initial state for the graph roots foreach (var entryPointNode in GraphRoots) { var entryTargets = entryProjectTargets == null || entryProjectTargets.Count == 0 ? ImmutableList.CreateRange(entryPointNode.ProjectInstance.DefaultTargets) : ImmutableList.CreateRange(entryProjectTargets); var entryEdge = new ProjectGraphBuildRequest(entryPointNode, entryTargets); encounteredEdges.Add(entryEdge); edgesToVisit.Enqueue(entryEdge); } // Traverse the entire graph, visiting each edge once. while (edgesToVisit.Count > 0) { var buildRequest = edgesToVisit.Dequeue(); var node = buildRequest.Node; var requestedTargets = buildRequest.RequestedTargets; targetLists[node] = targetLists[node].AddRange(requestedTargets); // No need to continue if this node has no project references. if (node.ProjectReferences.Count == 0) { continue; } // Based on the entry points of this project, determine which targets to propagate down to project references. var targetsToPropagate = ProjectInterpretation.TargetsToPropagate.FromProjectAndEntryTargets(node.ProjectInstance, requestedTargets); // Queue the project references for visitation, if the edge hasn't already been traversed. foreach (var referenceNode in node.ProjectReferences) { var applicableTargets = targetsToPropagate.GetApplicableTargetsForReference(referenceNode.ProjectInstance); if (applicableTargets.IsEmpty) { continue; } var expandedTargets = ExpandDefaultTargets( applicableTargets, referenceNode.ProjectInstance.DefaultTargets, Edges[(node, referenceNode)]);
/// <summary> /// Gets the target list to be executed for every project in the graph, given a particular target list for the entry project. /// </summary> /// <remarks> /// This method uses the ProjectReferenceTargets items to determine the targets to run per node. The results can then be used /// to start building each project individually, assuming a given project is built after its references. /// </remarks> /// <param name="entryProjectTargets">The target list for the entry project. May be null or empty, in which case the entry projects' default targets will be used.</param> /// <returns>A dictionary containing the target list for each node.</returns> public IReadOnlyDictionary <ProjectGraphNode, ImmutableList <string> > GetTargetLists(ICollection <string> entryProjectTargets) { // Seed the dictionary with empty lists for every node. In this particular case though an empty list means "build nothing" rather than "default targets". Dictionary <ProjectGraphNode, ImmutableList <string> > targetLists = ProjectNodes.ToDictionary(node => node, node => ImmutableList <string> .Empty); var encounteredEdges = new HashSet <ProjectGraphBuildRequest>(); var edgesToVisit = new Queue <ProjectGraphBuildRequest>(); // Initial state of the graph traversal. foreach (var entryPointNode in EntryPointNodes) { ImmutableList <string> entryTargets = entryProjectTargets == null || entryProjectTargets.Count == 0 ? ImmutableList.CreateRange(entryPointNode.ProjectInstance.DefaultTargets) : ImmutableList.CreateRange(entryProjectTargets); var entryEdge = new ProjectGraphBuildRequest(entryPointNode, entryTargets); encounteredEdges.Add(entryEdge); edgesToVisit.Enqueue(entryEdge); } // Traverse the entire graph, visiting each edge once. while (edgesToVisit.Count > 0) { ProjectGraphBuildRequest buildRequest = edgesToVisit.Dequeue(); ProjectGraphNode node = buildRequest.Node; ImmutableList <string> requestedTargets = buildRequest.RequestedTargets; targetLists[node] = targetLists[node].AddRange(requestedTargets); // No need to continue if this node has no project references. if (node.ProjectReferences.Count == 0) { continue; } // Based on the entry points of this project, determine which targets to propagate down to project references. ImmutableList <string> targetsToPropagate = DetermineTargetsToPropagate(node, requestedTargets); // Queue the project references for visitation, if the edge hasn't already been traversed. foreach (var projectReference in node.ProjectReferences) { var projectReferenceEdge = new ProjectGraphBuildRequest( projectReference, ExpandDefaultTargets(projectReference.ProjectInstance, targetsToPropagate)); if (encounteredEdges.Add(projectReferenceEdge)) { edgesToVisit.Enqueue(projectReferenceEdge); } } } // Dedupe target lists List <KeyValuePair <ProjectGraphNode, ImmutableList <string> > > entriesToUpdate = new List <KeyValuePair <ProjectGraphNode, ImmutableList <string> > >(); foreach (KeyValuePair <ProjectGraphNode, ImmutableList <string> > pair in targetLists) { ImmutableList <string> targetList = pair.Value; SortedSet <string> seenTargets = new SortedSet <string>(StringComparer.OrdinalIgnoreCase); int i = 0; while (i < targetList.Count) { if (seenTargets.Add(targetList[i])) { i++; } else { targetList = targetList.RemoveAt(i); } } // Only update if it changed if (targetList != pair.Value) { entriesToUpdate.Add(new KeyValuePair <ProjectGraphNode, ImmutableList <string> >(pair.Key, targetList)); } } // Update in a separate pass to avoid modifying a collection while iterating it. foreach (KeyValuePair <ProjectGraphNode, ImmutableList <string> > pair in entriesToUpdate) { targetLists[pair.Key] = pair.Value; } return(targetLists); }