using System; using System.IO; using System.Linq; using System.Runtime.ExceptionServices; using JD.Core.ProjectManaging; using Packager.BuildConfigurations; using Packager.Parameters; using Packager.Tasks; using LogCategory = JD.LogService.LogCategory; namespace Packager { /// /// Builder takes in the build configuration's tasklist and executes each task in the list sequentially. /// public class Builder : IDisposable { private readonly TaskList _buildTasks; private readonly BuildConfiguration _buildProfile; private engineWrapper _engine; private cookerWrapper _cooker; private readonly bool _stopOnErrors; private bool _exit; public MessageLogger Logger { get; } = new MessageLogger(nameof(Builder)); public Builder(BuildConfiguration profile, bool stopOnErrors) { _buildProfile = profile; _buildTasks = _buildProfile.Tasks; _stopOnErrors = stopOnErrors; } public void BuildTaskList(int startTask = 0) { var taskNameString = string.Join(";", _buildTasks.Select(task => task.InstanceName)); Logger.LogInfo("[Tasklist]" + taskNameString); foreach (var task in _buildTasks.Skip(startTask)) { if (_exit) { Logger.LogTaskMessage(task, "Aborting the build as StopOnError is on", LogCategory.Error); break; } Logger.LogInfo($"{task.InstanceName} task was created, its log file can be found at \"{task.Logger.LogFilePath}\"."); ExecuteCurrentTask(task); CheckTaskErrors(task); TaskFinishCleanUp(task); } } [HandleProcessCorruptedStateExceptions] private void ExecuteCurrentTask(Task task) { try { string outputPath = task.TaskOutputFolder; if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); } Logger.LogStartedTask(task); TaskUtility.ValidateTaskInput(task); if (task.NeedCooker) { _engine = new engineWrapper("Packager"); _cooker = new cookerWrapper(); _cooker.init(DataPath.Value); Logger.AttachCookerWrapper(_cooker); } task.Start(_buildProfile, _cooker); } catch (AccessViolationException ex) { HandleAccessViolationError(task, ex); } catch (Exception ex) { HandleLaunchError(task, ex); } } private void CheckTaskErrors(Task task) { if (task.ErrorCode == 0) { return; } Logger.LogTaskMessage(task, $"Returns error {task.ErrorCode}", LogCategory.Error); CheckToStopExecution(); } private void HandleLaunchError(Task task, Exception ex) { var message = $"When launching the task \"{task.InstanceName}\": {ex.Message}. Details: {ex}"; Logger.LogTaskMessage(task, message, ex is EmptyInputException ? LogCategory.Warning : LogCategory.Error); CheckToStopExecution(); } private void HandleAccessViolationError(Task task, Exception ex) { var message = $"Access Violation Exception when launching the task \"{task.InstanceName}\". If this error occurs in ITF.EngineSignature.computeEngineSignature(), please check with the engine and ggp teams if changes were made in serializable classes. Please check {Logger.LogFilePath} for the stacktrace. Details: {ex}"; Logger.LogTaskMessage(task, message, LogCategory.Error); //Force stop execution because an access violation error is not recoverable _exit = true; } private void CheckToStopExecution() { if (_stopOnErrors) { _exit = true; } } public void TaskFinishCleanUp(Task task) { if (_cooker != null) { Logger.DetachCookerWrapper(_cooker); _cooker.destroy(); _cooker = null; } if (_engine != null) { _engine.destroy(); _engine = null; } SaveTaskOutput(task); Logger.LogEndedTask(task); } private void SaveTaskOutput(Task task) { try { task.SaveOutputs(task.TaskOutputFile); } catch (Exception ex) { Logger.LogTaskMessage(task, $"Error while saving the task outputs: {ex.Message}", LogCategory.Error); } } public void Dispose() { _cooker?.Dispose(); } } }