public void DuplicateInputsAndOutputsMergePredictedBys()
        {
            var predictors = new IProjectPredictor[]
            {
                new MockPredictor(
                    new[] { @"common\inputFile" },
                    new[] { @"common\inputDirectory" },
                    new[] { @"common\outputFile" },
                    new[] { @"common\outputDirectory" }),
                new MockPredictor2(
                    new[] { @"common\inputFile" },
                    new[] { @"common\inputDirectory" },
                    new[] { @"common\outputFile" },
                    new[] { @"common\outputDirectory" }),
            };

            var executor = new ProjectPredictionExecutor(predictors);

            var project = TestHelpers.CreateProjectInstanceFromRootElement(ProjectRootElement.Create());

            ProjectPredictions predictions = executor.PredictInputsAndOutputs(project);

            predictions.AssertPredictions(
                project,
                new[] { new PredictedItem(@"common\inputFile", "MockPredictor", "MockPredictor2"), },
                new[] { new PredictedItem(@"common\inputDirectory", "MockPredictor", "MockPredictor2"), },
                new[] { new PredictedItem(@"common\outputFile", "MockPredictor", "MockPredictor2"), },
                new[] { new PredictedItem(@"common\outputDirectory", "MockPredictor", "MockPredictor2"), });
        }
        public void DistinctInputsAndOutputsAreAggregated()
        {
            /*
             * Missed value in the input files:
             * PredictedItem: 1\inputFile; PredictedBy=MockPredictor
             * from among actual list
             * PredictedItem: E:\MSBuildPrediction\src\BuildPredictionTests\bin\Debug\net472\2\inputFile; PredictedBy=MockPredictor2:: PredictedItem: E:\MSBuildPrediction\src\BuildPredictionTests\bin\Debug\net472\1\inputFile; PredictedBy=MockPredictor
             */
            var predictors = new IProjectPredictor[]
            {
                new MockPredictor(
                    new[] { @"1\inputFile" },
                    new[] { @"1\inputDirectory" },
                    new[] { @"1\outputFile" },
                    new[] { @"1\outputDirectory" }),
                new MockPredictor2(
                    new[] { @"2\inputFile" },
                    new[] { @"2\inputDirectory" },
                    new[] { @"2\outputFile" },
                    new[] { @"2\outputDirectory" }),
            };

            var executor = new ProjectPredictionExecutor(predictors);

            var project = TestHelpers.CreateProjectInstanceFromRootElement(ProjectRootElement.Create());

            ProjectPredictions predictions = executor.PredictInputsAndOutputs(project);

            predictions.AssertPredictions(
                project,
                new[] { new PredictedItem(@"1\inputFile", "MockPredictor"), new PredictedItem(@"2\inputFile", "MockPredictor2"), },
                new[] { new PredictedItem(@"1\inputDirectory", "MockPredictor"), new PredictedItem(@"2\inputDirectory", "MockPredictor2"), },
                new[] { new PredictedItem(@"1\outputFile", "MockPredictor"), new PredictedItem(@"2\outputFile", "MockPredictor2"), },
                new[] { new PredictedItem(@"1\outputDirectory", "MockPredictor"), new PredictedItem(@"2\outputDirectory", "MockPredictor2"), });
        }
        private static ProjectPredictions GetProjectPredictions(Project project)
        {
            // None and Content items are checked more carefully with other mechanisms.
            // Thus, we're excluding these items from the build predictor here.
            IEnumerable <IProjectPredictor> buildUpToDatePredictors = ProjectPredictors.AllPredictors.Where(p => !(p is NoneItemsPredictor) && !(p is ContentItemsPredictor));
            var predictionExecutor = new ProjectPredictionExecutor(buildUpToDatePredictors);

            return(predictionExecutor.PredictInputsAndOutputs(project));
        }
        public void PredictorSparsenessStressTest()
        {
            int[] numPredictorCases     = { 40 }; // Set to 1000 or more for reduced average noise when using tickResults for measurements.
            int[] sparsenessPercentages = { 0, 25, 50, 75, 100 };
            var   tickResults           = new long[numPredictorCases.Length][];

            var proj = TestHelpers.CreateProjectInstanceFromRootElement(ProjectRootElement.Create());

            // Run through twice and keep the second round only - first round affected by JIT overhead.
            for (int iter = 0; iter < 2; iter++)
            {
                for (int p = 0; p < numPredictorCases.Length; p++)
                {
                    int numPredictors = numPredictorCases[p];
                    tickResults[p] = new long[sparsenessPercentages.Length];
                    var predictors  = new IProjectPredictor[numPredictors];
                    int sparseIndex = 0;

                    for (int s = 0; s < sparsenessPercentages.Length; s++)
                    {
                        int sparsenessPercentage = sparsenessPercentages[s];
                        for (int i = 0; i < numPredictors; i++)
                        {
                            if (sparseIndex < sparsenessPercentage)
                            {
                                predictors[i] = new MockPredictor(null, null, null, null);
                            }
                            else
                            {
                                predictors[i] = new MockPredictor(
                                    new[] { $@"{i}\inputFile" },
                                    new[] { $@"{i}\inputDirectory" },
                                    new[] { $@"{i}\outputFile" },
                                    new[] { $@"{i}\outputDirectory" });
                            }

                            sparseIndex++;
                            if (sparseIndex > sparsenessPercentage)
                            {
                                sparseIndex = 0;
                            }
                        }

                        var       executor = new ProjectPredictionExecutor(predictors);
                        Stopwatch sw       = Stopwatch.StartNew();
                        executor.PredictInputsAndOutputs(proj);
                        sw.Stop();
                        tickResults[p][s] = sw.ElapsedTicks;
                        Console.WriteLine($"{numPredictors} @ {sparsenessPercentage}%: {sw.ElapsedTicks} ticks");
                    }
                }
            }
        }
        public void EmptyPredictionsResultInEmptyAggregateResult()
        {
            var predictors = new IProjectPredictor[]
            {
                new MockPredictor(null, null, null, null),
                new MockPredictor(null, null, null, null),
            };

            var executor = new ProjectPredictionExecutor(predictors);

            var project = TestHelpers.CreateProjectInstanceFromRootElement(ProjectRootElement.Create());
            ProjectPredictions predictions = executor.PredictInputsAndOutputs(project);

            Assert.NotNull(predictions);
            Assert.Equal(0, predictions.InputFiles.Count);
            Assert.Equal(0, predictions.InputDirectories.Count);
            Assert.Equal(0, predictions.OutputFiles.Count);
            Assert.Equal(0, predictions.OutputDirectories.Count);
        }
Example #6
0
        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()}"));
                }