private IEnumerable <(string pathPostfix, string contents)> GetDotNotations( FileInfo projectFile, ImmutableDictionary <string, string> globalProperties, string[] targets, string[] endNodes) { Console.WriteLine("Loading graph..."); var sw = Stopwatch.StartNew(); var graph = new ProjectGraph(new ProjectGraphEntryPoint(projectFile.FullName, globalProperties), ProjectCollection.GlobalProjectCollection); sw.Stop(); Console.WriteLine($@"{projectFile} loaded {graph.ProjectNodes.Count} node(s) in {sw.ElapsedMilliseconds}ms."); var entryTargetsPerNode = graph.GetTargetLists(targets); if (endNodes != null) { var endGraphNodes = graph.ProjectNodes.Where(n => endNodes.Any(en => n.ProjectInstance.FullPath.Contains(en))); var paths = GraphPaths.FindAllPathsBetween(graph.GraphRoots, endGraphNodes); var deduplicatedNodes = paths.SelectMany(p => p).ToHashSet(); yield return($"_PathsEndingIn_{string.Join(",", endNodes)}", GraphVis.Create(deduplicatedNodes, entryTargetsPerNode)); } yield return("", GraphVis.Create(graph, entryTargetsPerNode)); }
public void ConstructWithNoNodes() { var projectGraph = new ProjectGraph(Enumerable.Empty <ProjectGraphEntryPoint>()); projectGraph.ProjectNodes.ShouldBeEmpty(); projectGraph.EntryPointNodes.ShouldBeEmpty(); projectGraph.GraphRoots.ShouldBeEmpty(); projectGraph.ProjectNodesTopologicallySorted.ShouldBeEmpty(); projectGraph.GetTargetLists(new [] { "restore", "build" }).ShouldBeEmpty(); }
private static bool BuildGraphWithCacheFileRoundtrip(IReadOnlyCollection <string> projectFiles, string cacheRoot) { ProjectGraph graph; var success = true; using (var collection = new ProjectCollection()) { //collection.RegisterLogger(new EvaluationLogger()); graph = new ProjectGraph(projectFiles, null, collection); } if (DebugBuild) { Console.WriteLine(ToDot(graph)); } var cacheFiles = new Dictionary <ProjectGraphNode, string>(graph.ProjectNodes.Count); var targetLists = graph.GetTargetLists(null); var topoSortedNodes = graph.ProjectNodesTopologicallySorted; foreach (var node in topoSortedNodes) { var outputCacheFile = graph.GraphRoots.Contains(node) ? null : Path.Combine(cacheRoot, node.CacheFileName()); var inputCachesFiles = node.ProjectReferences.Select(r => cacheFiles[r]).ToArray(); cacheFiles[node] = outputCacheFile; var targets = targetLists[node]; var result = BuildProject(node.ProjectInstance.FullPath, node.ProjectInstance.GlobalProperties, targets, inputCachesFiles, outputCacheFile); if (result.OverallResult == BuildResultCode.Failure) { success = false; } } return(success); }
private static bool TryConstructGraph( ProjectGraph projectGraph, GraphBuilderReporter reporter, ConcurrentDictionary <ProjectInstance, Project> projectInstanceToProjectCache, IReadOnlyCollection <string> entryPointTargets, IReadOnlyCollection <IProjectPredictor> projectPredictorsForTesting, bool allowProjectsWithoutTargetProtocol, out ProjectGraphWithPredictions projectGraphWithPredictions, out string failure) { Contract.Assert(projectGraph != null); var projectNodes = new ProjectWithPredictions[projectGraph.ProjectNodes.Count]; var nodes = projectGraph.ProjectNodes.ToArray(); // Compute the list of targets to run per project reporter.ReportMessage("Computing targets to execute for each project..."); // This dictionary should be exclusively read only at this point, and therefore thread safe var targetsPerProject = projectGraph.GetTargetLists(entryPointTargets.ToArray()); // Bidirectional access from nodes with predictions to msbuild nodes in order to compute node references in the second pass // TODO: revisit the structures, since the projects are known upfront we might be able to use lock-free structures var nodeWithPredictionsToMsBuildNodes = new ConcurrentDictionary <ProjectWithPredictions, ProjectGraphNode>(Environment.ProcessorCount, projectNodes.Length); var msBuildNodesToNodeWithPredictionIndex = new ConcurrentDictionary <ProjectGraphNode, ProjectWithPredictions>(Environment.ProcessorCount, projectNodes.Length); reporter.ReportMessage("Statically predicting inputs and outputs..."); // Create the registered predictors and initialize the prediction executor // The prediction executor potentially initializes third-party predictors, which may contain bugs. So let's be very defensive here IReadOnlyCollection <IProjectPredictor> predictors; try { predictors = projectPredictorsForTesting ?? ProjectPredictors.AllPredictors; } catch (Exception ex) { failure = $"Cannot create standard predictors. An unexpected error occurred. Please contact BuildPrediction project owners with this stack trace: {ex.ToString()}"; projectGraphWithPredictions = new ProjectGraphWithPredictions(new ProjectWithPredictions <string>[] { }); return(false); } // Using single-threaded prediction since we're parallelizing on project nodes instead. var predictionExecutor = new ProjectPredictionExecutor(predictors, new ProjectPredictionOptions { MaxDegreeOfParallelism = 1 }); // Each predictor may return unexpected/incorrect results and targets may not be able to be predicted. We put those failures here for post-processing. ConcurrentQueue <(string predictorName, string failure)> predictionFailures = new ConcurrentQueue <(string, string)>(); var predictedTargetFailures = new ConcurrentQueue <string>(); // The predicted targets to execute (per project) go here var computedTargets = new ConcurrentBigMap <ProjectGraphNode, PredictedTargetsToExecute>(); // When projects are allowed to not implement the target protocol, its references need default targets as a post-processing step var pendingAddDefaultTargets = new ConcurrentBigSet <ProjectGraphNode>(); // First pass // Predict all projects in the graph in parallel and populate ProjectNodes Parallel.For(0, projectNodes.Length, (int i) => { ProjectGraphNode msBuildNode = nodes[i]; ProjectInstance projectInstance = msBuildNode.ProjectInstance; Project project = projectInstanceToProjectCache[projectInstance]; var outputFolderPredictions = new List <string>(); var predictionCollector = new MsBuildOutputPredictionCollector(outputFolderPredictions, predictionFailures); try { // Again, be defensive when using arbitrary predictors predictionExecutor.PredictInputsAndOutputs(project, predictionCollector); } catch (Exception ex) { predictionFailures.Enqueue(( "Unknown predictor", $"Cannot run static predictor on project '{project.FullPath ?? "Unknown project"}'. An unexpected error occurred. Please contact BuildPrediction project owners with this stack trace: {ex.ToString()}")); }
public void GetTargetListsDefaultComplexPropagation() { var projectReferenceTargets = new Dictionary <string, string[]> { { "Build", new[] { "A", ".default" } }, { "X", new[] { "B", ".default" } }, { "Y", new[] { "C", ".default" } }, }; using (var env = TestEnvironment.Create()) { TransientTestFile entryProject = CreateProjectFile(env, 1, new[] { 2, 3, 4 }, projectReferenceTargets, defaultTargets: null); CreateProjectFile(env, 2, new[] { 5 }, projectReferenceTargets, defaultTargets: null); CreateProjectFile(env, 3, new[] { 6 }, projectReferenceTargets, defaultTargets: "X"); CreateProjectFile(env, 4, new[] { 7 }, projectReferenceTargets, defaultTargets: "Y"); CreateProjectFile(env, 5, defaultTargets: null); CreateProjectFile(env, 6, defaultTargets: null); CreateProjectFile(env, 7, defaultTargets: "Z;W"); var projectGraph = new ProjectGraph(entryProject.Path); projectGraph.ProjectNodes.Count.ShouldBe(7); IReadOnlyDictionary <ProjectGraphNode, ImmutableList <string> > targetLists = projectGraph.GetTargetLists(null); targetLists.Count.ShouldBe(projectGraph.ProjectNodes.Count); targetLists[GetNodeForProject(projectGraph, 1)].ShouldBe(new[] { "Build" }); targetLists[GetNodeForProject(projectGraph, 2)].ShouldBe(new[] { "A", "Build" }); targetLists[GetNodeForProject(projectGraph, 3)].ShouldBe(new[] { "A", "X" }); targetLists[GetNodeForProject(projectGraph, 4)].ShouldBe(new[] { "A", "Y" }); targetLists[GetNodeForProject(projectGraph, 5)].ShouldBe(new[] { "A", "Build" }); targetLists[GetNodeForProject(projectGraph, 6)].ShouldBe(new[] { "B", "Build" }); targetLists[GetNodeForProject(projectGraph, 7)].ShouldBe(new[] { "C", "Z", "W" }); } }
public void GetTargetListsUnspecifiedTargetsDefaultToBuild() { using (var env = TestEnvironment.Create()) { TransientTestFile entryProject = CreateProjectFile(env, 1, new[] { 2 }, new Dictionary <string, string[]> { { "Build", new[] { "A", ".default" } } }); CreateProjectFile(env, 2); var projectGraph = new ProjectGraph(entryProject.Path); projectGraph.ProjectNodes.Count.ShouldBe(2); IReadOnlyDictionary <ProjectGraphNode, ImmutableList <string> > targetLists = projectGraph.GetTargetLists(null); targetLists.Count.ShouldBe(projectGraph.ProjectNodes.Count); targetLists[GetNodeForProject(projectGraph, 1)].ShouldBe(new[] { "Build" }); targetLists[GetNodeForProject(projectGraph, 2)].ShouldBe(new[] { "A", "Build" }); } }
public void GetTargetListsForComplexGraph() { var projectReferenceTargets = new Dictionary <string, string[]> { { "A", new[] { "B" } }, { "B", new[] { "C" } }, { "C", new[] { "D" } }, { "D", new[] { "E" } }, }; using (var env = TestEnvironment.Create()) { TransientTestFile entryProject = CreateProjectFile(env, 1, new[] { 2, 3, 5 }, projectReferenceTargets); CreateProjectFile(env, 2, new[] { 4, 5 }, projectReferenceTargets); CreateProjectFile(env, 3, new[] { 5, 6 }, projectReferenceTargets); CreateProjectFile(env, 4, new[] { 5 }, projectReferenceTargets); CreateProjectFile(env, 5, new[] { 6 }, projectReferenceTargets); CreateProjectFile(env, 6, Array.Empty <int>(), projectReferenceTargets); var projectGraph = new ProjectGraph(entryProject.Path); projectGraph.ProjectNodes.Count.ShouldBe(6); IReadOnlyDictionary <ProjectGraphNode, ImmutableList <string> > targetLists = projectGraph.GetTargetLists(new[] { "A" }); targetLists.Count.ShouldBe(projectGraph.ProjectNodes.Count); targetLists[GetNodeForProject(projectGraph, 1)].ShouldBe(new[] { "A" }); targetLists[GetNodeForProject(projectGraph, 2)].ShouldBe(new[] { "B" }); targetLists[GetNodeForProject(projectGraph, 3)].ShouldBe(new[] { "B" }); targetLists[GetNodeForProject(projectGraph, 4)].ShouldBe(new[] { "C" }); targetLists[GetNodeForProject(projectGraph, 5)].ShouldBe(new[] { "B", "C", "D" }); targetLists[GetNodeForProject(projectGraph, 6)].ShouldBe(new[] { "C", "D", "E" }); } }
public void GetTargetListsDedupesTargets() { var projectReferenceTargets = new Dictionary <string, string[]> { { "A", new[] { "B", "X", "C" } }, { "B", new[] { "X", "Y" } }, { "C", new[] { "X", "Z" } }, }; using (var env = TestEnvironment.Create()) { TransientTestFile entryProject = CreateProjectFile(env, 1, new[] { 2 }, projectReferenceTargets); CreateProjectFile(env, 2, new[] { 3 }, projectReferenceTargets); CreateProjectFile(env, 3, Array.Empty <int>(), projectReferenceTargets); var projectGraph = new ProjectGraph(entryProject.Path); projectGraph.ProjectNodes.Count.ShouldBe(3); IReadOnlyDictionary <ProjectGraphNode, ImmutableList <string> > targetLists = projectGraph.GetTargetLists(new[] { "A" }); targetLists.Count.ShouldBe(projectGraph.ProjectNodes.Count); targetLists[GetNodeForProject(projectGraph, 1)].ShouldBe(new[] { "A" }); targetLists[GetNodeForProject(projectGraph, 2)].ShouldBe(new[] { "B", "X", "C" }); targetLists[GetNodeForProject(projectGraph, 3)].ShouldBe(new[] { "X", "Y", "Z" }); // Simplified from X, Y, X, Z } }
public void GetTargetListsAggregatesFromMultipleEdges() { using (var env = TestEnvironment.Create()) { TransientTestFile entryProject = CreateProjectFile(env, 1, new[] { 2, 3 }, new Dictionary <string, string[]> { { "A", new[] { "B" } } }); CreateProjectFile(env, 2, new[] { 4 }, new Dictionary <string, string[]> { { "B", new[] { "C" } } }); CreateProjectFile(env, 3, new[] { 4 }, new Dictionary <string, string[]> { { "B", new[] { "D" } } }); CreateProjectFile(env, 4); var projectGraph = new ProjectGraph(entryProject.Path); projectGraph.ProjectNodes.Count.ShouldBe(4); IReadOnlyDictionary <ProjectGraphNode, ImmutableList <string> > targetLists = projectGraph.GetTargetLists(new[] { "A" }); targetLists.Count.ShouldBe(projectGraph.ProjectNodes.Count); targetLists[GetNodeForProject(projectGraph, 1)].ShouldBe(new[] { "A" }); targetLists[GetNodeForProject(projectGraph, 2)].ShouldBe(new[] { "B" }); targetLists[GetNodeForProject(projectGraph, 3)].ShouldBe(new[] { "B" }); targetLists[GetNodeForProject(projectGraph, 4)].ShouldBe(new[] { "C", "D" }); // From B => C and B => D } }