Пример #1
0
        private void ScheduleBuildStep(BuilderContext builderContext, BuildStep instigator, BuildStep buildStep, IDictionary <string, string> variables)
        {
            if (buildStep.ExecutionId == 0)
            {
                if (buildStep.Parent != null && buildStep.Parent != instigator)
                {
                    throw new InvalidOperationException("Scheduling a BuildStep with a different instigator that its parent");
                }
                if (buildStep.Parent == null)
                {
                    buildStep.Parent = instigator;
                }

                // Compute content dependencies before scheduling the build
                GenerateDependencies(buildStep);

                // TODO: Big review of the log infrastructure of CompilerApp & BuildEngine!
                // Create a logger that redirects to various places (BuildStep.Logger, timestampped log, global log, etc...)
                var buildStepLogger = new BuildStepLogger(buildStep, Logger, startTime);
                var logger          = (Logger)buildStepLogger;
                // Apply user-registered callbacks to the logger
                buildStep.TransformExecuteContextLogger?.Invoke(ref logger);

                // Create execute context
                var executeContext = new ExecuteContext(this, builderContext, buildStep, logger)
                {
                    Variables = new Dictionary <string, string>(variables)
                };
                //buildStep.ExpandStrings(executeContext);

                if (runMode == Mode.Build)
                {
                    MicroThread microThread = scheduler.Create();

                    // Set priority from this build step, if we have one.
                    if (buildStep.Priority.HasValue)
                    {
                        microThread.Priority = buildStep.Priority.Value;
                    }

                    buildStep.ExecutionId = microThread.Id;

                    microThread.Name = buildStep.ToString();

                    // Default:
                    // Schedule continuations as early as possible to help EnumerableBuildStep finish when all its task are finished.
                    // Otherwise, it would wait for all leaf to finish first before finishing parent EnumerableBuildStep.
                    // This should also reduce memory usage, and might improve cache coherency as well.
                    microThread.ScheduleMode = ScheduleMode.First;

                    microThread.Start(async() =>
                    {
                        // Wait for prerequisites
                        await Task.WhenAll(buildStep.PrerequisiteSteps.Select(x => x.ExecutedAsync()).ToArray());

                        // Check for failed prerequisites
                        var status = ResultStatus.NotProcessed;

                        if (buildStep.ArePrerequisitesSuccessful)
                        {
                            try
                            {
                                var outputObjectsGroups = executeContext.GetOutputObjectsGroups();
                                MicrothreadLocalDatabases.MountDatabase(outputObjectsGroups);

                                // Execute
                                status = await buildStep.Execute(executeContext, builderContext);
                            }
                            catch (TaskCanceledException e)
                            {
                                // Benlitz: I'm NOT SURE this is the correct explanation, it might be a more subtle race condition, but I can't manage to reproduce it again
                                executeContext.Logger.Warning("A child task of build step " + buildStep + " triggered a TaskCanceledException that was not caught by the parent task. The command has not handled cancellation gracefully.");
                                executeContext.Logger.Warning(e.Message);
                                status = ResultStatus.Cancelled;
                            }
                            catch (Exception e)
                            {
                                executeContext.Logger.Error("Exception in command " + buildStep + ": " + e);
                                status = ResultStatus.Failed;
                            }
                            finally
                            {
                                MicrothreadLocalDatabases.UnmountDatabase();

                                // Ensure the command set at least the result status
                                if (status == ResultStatus.NotProcessed)
                                {
                                    throw new InvalidDataException("The build step " + buildStep + " returned ResultStatus.NotProcessed after completion.");
                                }
                            }
                            if (microThread.Exception != null)
                            {
                                executeContext.Logger.Error("Exception in command " + buildStep + ": " + microThread.Exception);
                                status = ResultStatus.Failed;
                            }
                        }
                        else
                        {
                            status = ResultStatus.NotTriggeredPrerequisiteFailed;
                        }

                        //if (completedTask.IsCanceled)
                        //{
                        //    completedStep.Status = ResultStatus.Cancelled;
                        //}
                        var logType    = LogMessageType.Info;
                        string logText = null;

                        switch (status)
                        {
                        case ResultStatus.Successful:
                            logType = LogMessageType.Verbose;
                            logText = "BuildStep {0} was successful.".ToFormat(buildStep.ToString());
                            break;

                        case ResultStatus.Failed:
                            logType = LogMessageType.Error;
                            logText = "BuildStep {0} failed.".ToFormat(buildStep.ToString());
                            break;

                        case ResultStatus.NotTriggeredPrerequisiteFailed:
                            logType = LogMessageType.Error;
                            logText = "BuildStep {0} failed of previous failed prerequisites.".ToFormat(buildStep.ToString());
                            break;

                        case ResultStatus.Cancelled:
                            logType = LogMessageType.Warning;
                            logText = "BuildStep {0} cancelled.".ToFormat(buildStep.ToString());
                            break;

                        case ResultStatus.NotTriggeredWasSuccessful:
                            logType = LogMessageType.Verbose;
                            logText = "BuildStep {0} is up-to-date and has been skipped".ToFormat(buildStep.ToString());
                            break;

                        case ResultStatus.NotProcessed:
                            throw new InvalidDataException("BuildStep has neither succeeded, failed, nor been cancelled");
                        }
                        if (logText != null)
                        {
                            var logMessage = new LogMessage(null, logType, logText);
                            executeContext.Logger.Log(logMessage);
                        }

                        buildStep.RegisterResult(executeContext, status);
                        stepCounter.AddStepResult(status);
                    });
                }
                else
                {
                    buildStep.Clean(executeContext, builderContext, runMode == Mode.CleanAndDelete);
                }
            }
        }
Пример #2
0
        private void ScheduleBuildStep(BuilderContext builderContext, BuildStep instigator, BuildStep buildStep, IDictionary<string, string> variables)
        {
            if (buildStep.ExecutionId == 0)
            {
                if (buildStep.Parent != null && buildStep.Parent != instigator)
                    throw new InvalidOperationException("Scheduling a BuildStep with a different instigator that its parent");
                if (buildStep.Parent == null)
                {
                    buildStep.Parent = instigator;
                }

                // Compute content dependencies before scheduling the build
                GenerateDependencies(buildStep);

                var executeContext = new ExecuteContext(this, builderContext, buildStep) { Variables = new Dictionary<string, string>(variables) };
                //buildStep.ExpandStrings(executeContext);

                if (runMode == Mode.Build)
                {
                    MicroThread microThread = scheduler.Create();

                    // Find priority from this build step, or one of its parent.
                    var buildStepPriority = buildStep;
                    while (buildStepPriority != null)
                    {
                        if (buildStepPriority.Priority.HasValue)
                        {
                            microThread.Priority = buildStepPriority.Priority.Value;
                            break;
                        }

                        buildStepPriority = buildStepPriority.Parent;
                    }

                    buildStep.ExecutionId = microThread.Id;

                    foreach (var threadMonitor in threadMonitors)
                    {
                        threadMonitor.RegisterBuildStep(buildStep, ((BuildStepLogger)executeContext.Logger).StepLogger);
                    }

                    microThread.Name = buildStep.ToString();

                    // Default:
                    // Schedule continuations as early as possible to help EnumerableBuildStep finish when all its task are finished.
                    // Otherwise, it would wait for all leaf to finish first before finishing parent EnumerableBuildStep.
                    // This should also reduce memory usage, and might improve cache coherency as well.
                    microThread.ScheduleMode = ScheduleMode.First;

                    microThread.Start(async () =>
                    {
                        // Wait for prerequisites
                        await Task.WhenAll(buildStep.PrerequisiteSteps.Select(x => x.ExecutedAsync()).ToArray());

                        // Check for failed prerequisites
                        var status = ResultStatus.NotProcessed;

                        if (buildStep.ArePrerequisitesSuccessful)
                        {
                            try
                            {
                                IndexFileCommand.MountDatabase(executeContext.GetOutputObjectsGroups());

                                // Execute
                                status = await buildStep.Execute(executeContext, builderContext);
                            }
                            catch (TaskCanceledException e)
                            {
                                // Benlitz: I'm NOT SURE this is the correct explanation, it might be a more subtle race condition, but I can't manage to reproduce it again
                                executeContext.Logger.Warning("A child task of build step " + buildStep + " triggered a TaskCanceledException that was not caught by the parent task. The command has not handled cancellation gracefully.");
                                executeContext.Logger.Warning(e.Message);
                                status = ResultStatus.Cancelled;
                            }
                            catch (Exception e)
                            {
                                executeContext.Logger.Error("Exception in command " + buildStep + ": " + e);
                                status = ResultStatus.Failed;
                            }
                            finally
                            {
                                IndexFileCommand.UnmountDatabase();
                                
                                // Ensure the command set at least the result status
                                if (status == ResultStatus.NotProcessed)
                                    throw new InvalidDataException("The build step " + buildStep + " returned ResultStatus.NotProcessed after completion.");
                            }
                            if (microThread.Exception != null)
                            {
                                executeContext.Logger.Error("Exception in command " + buildStep + ": " + microThread.Exception);
                                status = ResultStatus.Failed;
                            }
                        }
                        else
                        {
                            status = ResultStatus.NotTriggeredPrerequisiteFailed;
                        }

                        //if (completedTask.IsCanceled)
                        //{
                        //    completedStep.Status = ResultStatus.Cancelled;
                        //}
                        var logType = LogMessageType.Info;
                        string logText = null;
                        
                        switch (status)
                        {
                            case ResultStatus.Successful:
                                logType = LogMessageType.Verbose;
                                logText = "BuildStep {0} was successful.".ToFormat(buildStep.ToString());
                                break;
                            case ResultStatus.Failed:
                                logType = LogMessageType.Error;
                                logText = "BuildStep {0} failed.".ToFormat(buildStep.ToString());
                                break;
                            case ResultStatus.NotTriggeredPrerequisiteFailed:
                                logType = LogMessageType.Error;
                                logText = "BuildStep {0} failed of previous failed prerequisites.".ToFormat(buildStep.ToString());
                                break;
                            case ResultStatus.Cancelled:
                                logType = LogMessageType.Warning;
                                logText = "BuildStep {0} cancelled.".ToFormat(buildStep.ToString());
                                break;
                            case ResultStatus.NotTriggeredWasSuccessful:
                                logType = LogMessageType.Verbose;
                                logText = "BuildStep {0} is up-to-date and has been skipped".ToFormat(buildStep.ToString());
                                break;
                            case ResultStatus.NotProcessed:
                                throw new InvalidDataException("BuildStep has neither succeeded, failed, nor been cancelled");
                        }
                        if (logText != null)
                        {
                            var logMessage = new LogMessage(buildStep.Module, logType, logText);
                            Logger.Log(logMessage);
                        }

                        buildStep.RegisterResult(executeContext, status);
                        stepCounter.AddStepResult(status);
                    });
                }
                else
                {
                    buildStep.Clean(executeContext, builderContext, runMode == Mode.CleanAndDelete);
                }
            }
        }