Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
Builder.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using SiliconStudio.Core;
4 using SiliconStudio.Core.Storage;
5 using System;
6 using System.Collections.Generic;
7 using System.IO;
8 using System.Linq;
9 using System.Threading;
10 using System.Threading.Tasks;
11 using SiliconStudio.Core.Diagnostics;
12 using SiliconStudio.Core.MicroThreading;
13 using SiliconStudio.Core.Serialization.Assets;
14 using SiliconStudio.Core.IO;
15 
16 using System.Reflection;
17 
18 namespace SiliconStudio.BuildEngine
19 {
20  public class StepCounter
21  {
22  private readonly int[] stepResults;
23  public int Total { get; private set; }
24 
25  public StepCounter()
26  {
27  stepResults = new int[Enum.GetValues(typeof(ResultStatus)).Length];
28  }
29 
30  public void AddStepResult(ResultStatus result)
31  {
32  lock (stepResults)
33  {
34  ++Total;
35  ++stepResults[(int)result];
36  }
37  }
38 
39  public int Get(ResultStatus result)
40  {
41  lock (stepResults)
42  {
43  return stepResults[(int)result];
44  }
45  }
46 
47  public void Clear()
48  {
49  lock (stepResults)
50  {
51  Total = 0;
52  foreach (var value in Enum.GetValues(typeof(ResultStatus)))
53  stepResults[(int)value] = 0;
54  }
55  }
56  }
57 
58  public class Builder
59  {
60  public const int ExpectedVersion = 3;
61  public static readonly string DoNotPackTag = "DoNotPack";
62  public static readonly string DoNotCompressTag = "DoNotCompress";
63 
64  #region Public Members
65 
66  /// <summary>
67  /// Indicate which mode to use with this builder
68  /// </summary>
69  public enum Mode
70  {
71  /// <summary>
72  /// Build the script
73  /// </summary>
74  Build,
75  /// <summary>
76  /// Clean the command cache used to determine wheither a command has already been triggered.
77  /// </summary>
78  Clean,
79  /// <summary>
80  /// Clean the command cache and delete every output objects
81  /// </summary>
82  CleanAndDelete,
83  }
84 
85  /// <summary>
86  /// Logger used by the builder and the commands
87  /// </summary>
88  public ILogger Logger { get; private set; }
89 
90  /// <summary>
91  /// Builder name
92  /// </summary>
93  public string BuilderName { get; set; }
94 
95  /// <summary>
96  /// The <see cref="Guid"/> assigned to the builder.
97  /// </summary>
98  public Guid BuilderId { get; private set; }
99 
100  /// <summary>
101  /// The build path for spawned slave processes.
102  /// </summary>
103  public string SlaveBuilderPath { get; set; }
104 
105  /// <summary>
106  /// Number of working threads to create
107  /// </summary>
108  public int ThreadCount {
109  get { return threadCount; }
110  set { threadCount = value; if (MaxParallelProcesses > value) MaxParallelProcesses = value; }
111  }
112  private int threadCount;
113 
114  /// <summary>
115  /// Max number of processes that can be executed for remote commands
116  /// </summary>
117  public int MaxParallelProcesses {
118  get { return maxParallelProcesses; }
119  set { maxParallelProcesses = value; if (value > ThreadCount) throw new InvalidOperationException("MaxParallelProcesses can't be greater than ThreadCount."); }
120  }
121  private int maxParallelProcesses;
122 
123  /// <summary>
124  /// The root build step of the builder defining the builds to perform.
125  /// </summary>
126  public ListBuildStep Root { get; private set; }
127 
128  /// <summary>
129  /// Indicate whether this builder is currently running.
130  /// </summary>
131  public bool IsRunning { get; protected set; }
132 
133  /// <summary>
134  /// Indicate whether the build has been canceled
135  /// </summary>
136  public bool Cancelled { get; protected set; }
137 
138  public List<string> MonitorPipeNames { get; private set; }
139 
140  public const string MonitorPipeName = "net.pipe://localhost/Paradox.BuildEngine.Monitor";
141 
142  public IDictionary<string, string> InitialVariables { get; private set; }
143 
144  public string MetadataDatabaseDirectory { get; set; }
145 
146  public readonly ISet<ObjectId> DisableCompressionIds = new HashSet<ObjectId>();
147 
148  #endregion Public Members
149  #region Private Members
150 
151  /// <summary>
152  /// The name on the disk of the index file name.
153  /// </summary>
154  private readonly string indexFilename;
155 
156  /// <summary>
157  /// The name on the disk of the file caching the input file hashes
158  /// </summary>
159  private readonly string inputHashesFilename;
160 
161  /// <summary>
162  /// The path on the disk where to perform the build
163  /// </summary>
164  private readonly string buildPath;
165 
166  /// <summary>
167  /// The build profile
168  /// </summary>
169  private readonly string buildProfile;
170 
171  /// <summary>
172  /// The path of the data base from the build path
173  /// </summary>
174  private const string DatabasePath = "/data/db/";
175 
176  /// <summary>
177  /// Cancellation token source used for cancellation.
178  /// </summary>
179  private CancellationTokenSource cancellationTokenSource;
180 
181  private Scheduler scheduler;
182 
183  private readonly CommandIOMonitor ioMonitor;
184  private readonly List<BuildThreadMonitor> threadMonitors = new List<BuildThreadMonitor>();
185 
186  /// <summary>
187  /// A map containing results of each commands, indexed by command hashes. When the builder is running, this map if filled with the result of the commands of the current execution.
188  /// </summary>
189  private ObjectDatabase resultMap;
190 
191  private readonly DateTime startTime;
192 
193  private readonly StepCounter stepCounter = new StepCounter();
194 
195  /// <summary>
196  /// The build mode of the current run execution
197  /// </summary>
198  private Mode runMode;
199 
200  #endregion Private Members
201 
202  /// <summary>
203  /// The full path of the index file from the build directory.
204  /// </summary>
205  private string IndexFileFullPath
206  {
207  get { return DatabasePath + indexFilename; }
208  }
209 
210  /// <summary>
211  /// The full path of the input hashes file from the build directory.
212  /// </summary>
213  private string InputHashesFileFullPath
214  {
215  get { return DatabasePath + inputHashesFilename; }
216  }
217 
218  public Builder(string buildPath, string buildProfile, string indexFilename, string inputHashesFilename, ILogger logger)
219  {
220  if (buildPath == null) throw new ArgumentNullException("buildPath");
221  if (indexFilename == null) throw new ArgumentNullException("indexFilename");
222  if (inputHashesFilename == null) throw new ArgumentNullException("inputHashesFilename");
223 
224  MonitorPipeNames = new List<string>();
225  startTime = DateTime.Now;
226  this.buildProfile = buildProfile;
227  this.indexFilename = indexFilename;
228  var entryAssembly = Assembly.GetEntryAssembly();
229  SlaveBuilderPath = entryAssembly != null ? entryAssembly.Location : "";
230  Logger = logger;
231  this.inputHashesFilename = inputHashesFilename;
232  this.buildPath = buildPath;
233  Root = new ListBuildStep();
234  ioMonitor = new CommandIOMonitor(Logger);
235  ThreadCount = Environment.ProcessorCount;
236  MaxParallelProcesses = ThreadCount;
237  BuilderId = Guid.NewGuid();
238  InitialVariables = new Dictionary<string, string>();
239 
240  SetupBuildPath(buildPath);
241 
242  var objectDatabase = IndexFileCommand.ObjectDatabase;
243 
244  // Check current database version, and erase it if too old
245  int currentVersion = 0;
246  var versionFile = Path.Combine(VirtualFileSystem.GetAbsolutePath(DatabasePath), "version");
247  if (File.Exists(versionFile))
248  {
249  try
250  {
251  var versionText = File.ReadAllText(versionFile);
252  currentVersion = int.Parse(versionText);
253  }
254  catch (Exception)
255  {
256  }
257  }
258 
259  if (currentVersion != ExpectedVersion)
260  {
261  var looseObjects = objectDatabase.EnumerateLooseObjects().ToArray();
262 
263  if (looseObjects.Length > 0)
264  {
265  Logger.Info("Database version number has been updated from {0} to {1}, erasing all objects...", currentVersion, ExpectedVersion);
266 
267  // Database version has been updated, let's clean it
268  foreach (var objectId in looseObjects)
269  {
270  try
271  {
272  objectDatabase.Delete(objectId);
273  }
274  catch (IOException)
275  {
276  }
277  }
278  }
279 
280  // Create directory
281  File.WriteAllText(versionFile, ExpectedVersion.ToString());
282  }
283 
284  // Prepare data base directories
285  AssetManager.GetFileProvider = () => IndexFileCommand.DatabaseFileProvider.Value;
286  var databasePathSplits = DatabasePath.Split('/');
287  var accumulatorPath = "/";
288  foreach (var pathPart in databasePathSplits.Where(x=>x!=""))
289  {
290  accumulatorPath += pathPart + "/";
291  VirtualFileSystem.CreateDirectory(accumulatorPath);
292 
293  accumulatorPath += "";
294  }
295  }
296 
297  public static void SetupBuildPath(string buildPath)
298  {
299  // Mount build path
300  ((FileSystemProvider)VirtualFileSystem.ApplicationData).ChangeBasePath(buildPath);
301  if (IndexFileCommand.ObjectDatabase == null)
302  IndexFileCommand.ObjectDatabase = new ObjectDatabase(DatabasePath, loadDefaultBundle: false); // note: this has to be done after VFS.ChangeBasePath
303  }
304 
305  private class ExecuteContext : IExecuteContext
306  {
307  private readonly BuilderContext builderContext;
308  private readonly BuildStep buildStep;
309  private readonly BuildTransaction buildTransaction;
310  private readonly Logger logger;
311  private readonly Builder builder;
312 
313  public ExecuteContext(Builder builder, BuilderContext builderContext, BuildStep buildStep)
314  {
315  logger = new BuildStepLogger(builder.Logger, builder.startTime);
316  this.builderContext = builderContext;
317  this.builder = builder;
318  this.buildStep = buildStep;
319  buildTransaction = new BuildTransaction(buildStep.GetOutputObjectsGroups());
320  }
321 
322  public Logger Logger { get { return logger; } }
323 
324  public ObjectDatabase ResultMap { get { return builder.resultMap; } }
325 
326  public CancellationTokenSource CancellationTokenSource { get { return builder.cancellationTokenSource; } }
327 
328  public Dictionary<string, string> Variables { get; set; }
329 
330  public IMetadataProvider MetadataProvider { get { return builderContext.MetadataProvider; } }
331 
332  public void ScheduleBuildStep(BuildStep step)
333  {
334  builder.ScheduleBuildStep(builderContext, buildStep, step, Variables);
335  }
336 
337  public IEnumerable<IDictionary<ObjectUrl, OutputObject>> GetOutputObjectsGroups()
338  {
339  return buildStep.GetOutputObjectsGroups();
340  }
341 
342  public ObjectId ComputeInputHash(UrlType type, string filePath)
343  {
344  var hash = ObjectId.Empty;
345 
346  switch (type)
347  {
348  case UrlType.File:
349  hash = builderContext.InputHashes.ComputeFileHash(filePath);
350  break;
351  case UrlType.Internal:
352  if (!buildTransaction.TryGetValue(filePath, out hash))
353  Logger.Warning("Location " + filePath + " does not exist currently and is required to compute the current command hash. The build cache will not work for this command!");
354  break;
355  case UrlType.Virtual:
356  var providerResult = VirtualFileSystem.ResolveProvider(filePath, true);
357  var dbProvider = providerResult.Provider as DatabaseFileProvider;
358  var microProvider = providerResult.Provider as MicroThreadFileProvider;
359  if (microProvider != null)
360  {
361  dbProvider = microProvider.ThreadLocal.Value as DatabaseFileProvider;
362  }
363 
364  if (dbProvider != null)
365  {
366  dbProvider.AssetIndexMap.TryGetValue(providerResult.Path, out hash);
367  }
368  break;
369  }
370 
371  return hash;
372  }
373 
374  public CommandBuildStep IsCommandCurrentlyRunning(ObjectId commandHash)
375  {
376  lock (builderContext.CommandsInProgress)
377  {
378  CommandBuildStep step;
379  builderContext.CommandsInProgress.TryGetValue(commandHash, out step);
380  return step;
381  }
382  }
383 
384  public void NotifyCommandBuildStepStarted(CommandBuildStep commandBuildStep, ObjectId commandHash)
385  {
386  lock (builderContext.CommandsInProgress)
387  {
388  if (!builderContext.CommandsInProgress.ContainsKey(commandHash))
389  builderContext.CommandsInProgress.Add(commandHash, commandBuildStep);
390 
391  builder.ioMonitor.CommandStarted(commandBuildStep);
392  }
393  }
394 
395  public void NotifyCommandBuildStepFinished(CommandBuildStep commandBuildStep, ObjectId commandHash)
396  {
397  lock (builderContext.CommandsInProgress)
398  {
399  builderContext.CommandsInProgress.Remove(commandHash);
400  builder.ioMonitor.CommandEnded(commandBuildStep);
401  }
402  }
403  }
404 
405  private void ScheduleBuildStep(BuilderContext builderContext, BuildStep instigator, BuildStep buildStep, IDictionary<string, string> variables)
406  {
407  if (buildStep.ExecutionId == 0)
408  {
409  if (buildStep.Parent != null && buildStep.Parent != instigator)
410  throw new InvalidOperationException("Scheduling a BuildStep with a different instigator that its parent");
411  if (buildStep.Parent == null)
412  {
413  buildStep.Parent = instigator;
414  }
415 
416  var executeContext = new ExecuteContext(this, builderContext, buildStep) { Variables = new Dictionary<string, string>(variables) };
417  //buildStep.ExpandStrings(executeContext);
418 
419  if (runMode == Mode.Build)
420  {
421  MicroThread microThread = scheduler.Create();
422 
423  // Find priority from this build step, or one of its parent.
424  var buildStepPriority = buildStep;
425  while (buildStepPriority != null)
426  {
427  if (buildStepPriority.Priority.HasValue)
428  {
429  microThread.Priority = buildStepPriority.Priority.Value;
430  break;
431  }
432 
433  buildStepPriority = buildStepPriority.Parent;
434  }
435 
436  buildStep.ExecutionId = microThread.Id;
437 
438  foreach (var threadMonitor in threadMonitors)
439  {
440  threadMonitor.RegisterBuildStep(buildStep, ((BuildStepLogger)executeContext.Logger).StepLogger);
441  }
442 
443  microThread.Name = buildStep.ToString();
444 
445  // Default:
446  // Schedule continuations as early as possible to help EnumerableBuildStep finish when all its task are finished.
447  // Otherwise, it would wait for all leaf to finish first before finishing parent EnumerableBuildStep.
448  // This should also reduce memory usage, and might improve cache coherency as well.
449  microThread.ScheduleMode = ScheduleMode.First;
450 
451  microThread.Start(async () =>
452  {
453  // Wait for prerequisites
454  await Task.WhenAll(buildStep.PrerequisiteSteps.Select(x => x.ExecutedAsync()).ToArray());
455 
456  // Check for failed prerequisites
457  var status = ResultStatus.NotProcessed;
458 
459  if (buildStep.ArePrerequisitesSuccessful)
460  {
461  try
462  {
463  IndexFileCommand.MountDatabases(executeContext);
464 
465  // Execute
466  status = await buildStep.Execute(executeContext, builderContext);
467  }
468  catch (TaskCanceledException e)
469  {
470  // 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
471  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.");
472  executeContext.Logger.Warning(e.Message);
473  status = ResultStatus.Cancelled;
474  }
475  catch (Exception e)
476  {
477  executeContext.Logger.Error("Exception in command " + buildStep + ": " + e);
478  status = ResultStatus.Failed;
479  }
480  finally
481  {
482  IndexFileCommand.UnmountDatabases(executeContext);
483 
484  // Ensure the command set at least the result status
485  if (status == ResultStatus.NotProcessed)
486  throw new InvalidDataException("The build step " + buildStep + " returned ResultStatus.NotProcessed after completion.");
487  }
488  if (microThread.Exception != null)
489  {
490  executeContext.Logger.Error("Exception in command " + buildStep + ": " + microThread.Exception);
491  status = ResultStatus.Failed;
492  }
493  }
494  else
495  {
496  status = ResultStatus.NotTriggeredPrerequisiteFailed;
497  }
498 
499  buildStep.RegisterResult(executeContext, status);
500  stepCounter.AddStepResult(status);
501 
502  //if (completedTask.IsCanceled)
503  //{
504  // completedStep.Status = ResultStatus.Cancelled;
505  //}
506  var logType = LogMessageType.Info;
507  string logText = null;
508 
509  switch (buildStep.Status)
510  {
511  case ResultStatus.Successful:
512  logType = LogMessageType.Info;
513  logText = "BuildStep {0} was successful.".ToFormat(buildStep.ToString());
514  break;
515  case ResultStatus.Failed:
516  logType = LogMessageType.Error;
517  logText = "BuildStep {0} failed.".ToFormat(buildStep.ToString());
518  break;
519  case ResultStatus.Cancelled:
520  logType = LogMessageType.Warning;
521  logText = "BuildStep {0} cancelled.".ToFormat(buildStep.ToString());
522  break;
523  case ResultStatus.NotProcessed:
524  throw new InvalidDataException("BuildStep has neither succeeded, failed, nor been cancelled");
525  }
526  if (logText != null)
527  {
528  var logMessage = new LogMessage(buildStep.Module, logType, logText);
529  executeContext.Logger.Log(logMessage);
530  }
531  });
532  }
533  else
534  {
535  buildStep.Clean(executeContext, builderContext, runMode == Mode.CleanAndDelete);
536  }
537  }
538  }
539 
540  /// <summary>
541  /// Cancel the currently executing build.
542  /// </summary>
543  public void CancelBuild()
544  {
545  if (IsRunning)
546  {
547  Cancelled = true;
548  cancellationTokenSource.Cancel();
549  }
550  }
551 
552  public void RunUntilEnd()
553  {
554  foreach (var threadMonitor in threadMonitors)
555  threadMonitor.RegisterThread(Thread.CurrentThread.ManagedThreadId);
556 
557  while (true)
558  {
559  scheduler.Run();
560 
561  // Exit loop if no more micro threads
562  lock (scheduler.MicroThreads)
563  {
564  if (!scheduler.MicroThreads.Any())
565  break;
566  }
567 
568  // TODO: improve how we wait for work. Thread.Sleep(0) uses too much CPU.
569  Thread.Sleep(1);
570  }
571  }
572 
573  /// <summary>
574  /// Discard the current <see cref="Root"/> build step and initialize a new empty one.
575  /// </summary>
576  public void Reset()
577  {
578  Root = new ListBuildStep();
579  stepCounter.Clear();
580  }
581 
582  /// <summary>
583  /// Write the generated objects into the index map file.
584  /// </summary>
585  /// <param name="mergeWithCurrentIndexFile">Indicate if old values must be deleted or merged</param>
586  public void WriteIndexFile(bool mergeWithCurrentIndexFile)
587  {
588  if (!mergeWithCurrentIndexFile)
589  {
590  try
591  {
592  VirtualFileSystem.FileDelete(IndexFileFullPath);
593  }
594  catch (IOException)
595  {
596  }
597  }
598 
599  using (var indexFile = AssetIndexMap.NewTool(indexFilename))
600  {
601  // Filter database Location
602  indexFile.AddValues(
603  Root.OutputObjects.Where(x => x.Key.Type == UrlType.Internal)
604  .Select(x => new KeyValuePair<string, ObjectId>(x.Key.Path, x.Value.ObjectId)));
605 
606  foreach (var x in Root.OutputObjects)
607  {
608  if(x.Key.Type != UrlType.Internal)
609  continue;
610 
611  if (x.Value.Tags.Contains(DoNotCompressTag))
612  DisableCompressionIds.Add(x.Value.ObjectId);
613  }
614  }
615  }
616 
617  /// <summary>
618  /// Runs this instance.
619  /// </summary>
620  public BuildResultCode Run(Mode mode, bool writeIndexFile = true, bool enableMonitor = true)
621  {
622  runMode = mode;
623 
624  if (IsRunning)
625  throw new InvalidOperationException("An instance of this Builder is already running.");
626 
627  // reset build cache from previous build run
628  var parameters = new BuildParameterCollection();
629  cancellationTokenSource = new CancellationTokenSource();
630  Cancelled = false;
631  IsRunning = true;
632  DisableCompressionIds.Clear();
633 
634  // Reseting result map
635  var inputHashes = FileVersionTracker.GetDefault();
636  {
637  var builderContext = new BuilderContext(buildPath, buildProfile, inputHashes, parameters, MaxParallelProcesses, SlaveBuilderPath);
638  if (!string.IsNullOrWhiteSpace(MetadataDatabaseDirectory))
639  {
640  var metadataProvider = new QueryMetadataProvider();
641  if (metadataProvider.Open(Path.Combine(MetadataDatabaseDirectory, QueryMetadataProvider.DefaultDatabaseFilename), false))
642  {
643  builderContext.MetadataProvider = metadataProvider;
644  }
645  }
646 
647  resultMap = IndexFileCommand.ObjectDatabase;
648 
649  scheduler = new Scheduler();
650  if (enableMonitor)
651  {
652  threadMonitors.Add(new BuildThreadMonitor(scheduler, BuilderId));
653  foreach (var monitorPipeName in MonitorPipeNames)
654  threadMonitors.Add(new BuildThreadMonitor(scheduler, BuilderId, monitorPipeName));
655 
656  foreach (var threadMonitor in threadMonitors)
657  threadMonitor.Start();
658  }
659 
660  ScheduleBuildStep(builderContext, null, Root, InitialVariables);
661 
662  // Create threads
663  var threads = Enumerable.Range(0, ThreadCount).Select(x => new Thread(SafeAction.Wrap(RunUntilEnd)) { IsBackground = true }).ToArray();
664 
665  // Start threads
666  int threadId = 0;
667  foreach (var thread in threads)
668  {
669  thread.Name = "Builder thread " + (++threadId);
670  thread.Start();
671  }
672 
673  // Wait for all threads to finish
674  foreach (var thread in threads)
675  {
676  thread.Join();
677  }
678 
679  foreach (var threadMonitor in threadMonitors)
680  threadMonitor.Finish();
681 
682  foreach (var threadMonitor in threadMonitors)
683  threadMonitor.Join();
684  }
685 
686  threadMonitors.Clear();
687  BuildResultCode result;
688 
689  if (runMode == Mode.Build)
690  {
691  Logger.Info("");
692  if (cancellationTokenSource.IsCancellationRequested)
693  {
694  Logger.Error("Build cancelled.");
695  result = BuildResultCode.Cancelled;
696 
697  }
698  else if (stepCounter.Get(ResultStatus.Failed) > 0 || stepCounter.Get(ResultStatus.NotTriggeredPrerequisiteFailed) > 0)
699  {
700  Logger.Error("Build finished in {0} steps. Command results: {1} succeeded, {2} up-to-date, {3} failed, {4} not triggered due to previous failure.",
701  stepCounter.Total, stepCounter.Get(ResultStatus.Successful), stepCounter.Get(ResultStatus.NotTriggeredWasSuccessful),
702  stepCounter.Get(ResultStatus.Failed), stepCounter.Get(ResultStatus.NotTriggeredPrerequisiteFailed));
703 
704  Logger.Error("Build failed.");
705  result = BuildResultCode.BuildError;
706  }
707  else
708  {
709  Logger.Info("Build finished in {0} steps. Command results: {1} succeeded, {2} up-to-date, {3} failed, {4} not triggered due to previous failure.",
710  stepCounter.Total, stepCounter.Get(ResultStatus.Successful), stepCounter.Get(ResultStatus.NotTriggeredWasSuccessful),
711  stepCounter.Get(ResultStatus.Failed), stepCounter.Get(ResultStatus.NotTriggeredPrerequisiteFailed));
712 
713  Logger.Info("Build is successful.");
714  result = BuildResultCode.Successful;
715  }
716  }
717  else
718  {
719  // Clean input hashes file
720  if (VirtualFileSystem.FileExists(InputHashesFileFullPath))
721  {
722  try
723  {
724  VirtualFileSystem.FileDelete(InputHashesFileFullPath);
725  }
726  catch (IOException)
727  {
728  return BuildResultCode.BuildError;
729  }
730  }
731  string modeName;
732  switch (runMode)
733  {
734  case Mode.Clean:
735  modeName = "Clean";
736  break;
737  case Mode.CleanAndDelete:
738  modeName = "Clean-and-delete";
739  break;
740  default:
741  throw new InvalidOperationException("Builder executed in unknown mode.");
742  }
743 
744  if (cancellationTokenSource.IsCancellationRequested)
745  {
746  Logger.Error(modeName + " has been cancelled.");
747  result = BuildResultCode.Cancelled;
748 
749  }
750  else if (stepCounter.Get(ResultStatus.Failed) > 0 || stepCounter.Get(ResultStatus.NotTriggeredPrerequisiteFailed) > 0)
751  {
752  Logger.Error(modeName + " has failed.");
753  result = BuildResultCode.BuildError;
754  }
755  else
756  {
757  Logger.Error(modeName + " has been successfully completed.");
758  result = BuildResultCode.Successful;
759  }
760  }
761  scheduler = null;
762  resultMap = null;
763  IsRunning = false;
764 
765  return result;
766  }
767  }
768 }
Virtual abstraction over a file system. It handles access to files, http, packages, path rewrite, etc...
IEnumerable< IDictionary< ObjectUrl, OutputObject > > GetOutputObjectsGroups()
Definition: BuildStep.cs:179
Represents an execution context managed by a Scheduler, that can cooperatively yield execution to ano...
Definition: MicroThread.cs:16
A file system implementation for IVirtualFileProvider.
void CancelBuild()
Cancel the currently executing build.
Definition: Builder.cs:543
static ThreadStart Wrap(ThreadStart action, [CallerFilePath] string sourceFilePath="", [CallerMemberName] string memberName="", [CallerLineNumber] int sourceLineNumber=0)
Definition: SafeAction.cs:13
void AddStepResult(ResultStatus result)
Definition: Builder.cs:30
static bool FileExists(string path)
Checks the existence of a file.
ResultStatus
Status of a command.
Definition: ResultStatus.cs:8
int Get(ResultStatus result)
Definition: Builder.cs:39
Gives access to the object database.
Base implementation for ILogger.
Definition: Logger.cs:10
Exception Exception
Gets the exception that was thrown by this MicroThread.
Definition: MicroThread.cs:133
ILogger Logger
Logger used by the builder and the commands
Definition: Builder.cs:88
System.IO.File File
Mode
Indicate which mode to use with this builder
Definition: Builder.cs:69
static readonly IVirtualFileProvider ApplicationData
The application data file provider.
static MicroThreadLocal< DatabaseFileProvider > DatabaseFileProvider
A Command that reads and/or writes to the index file.
static void SetupBuildPath(string buildPath)
Definition: Builder.cs:297
A hash to uniquely identify data.
Definition: ObjectId.cs:13
A base log message used by the logging infrastructure.
Definition: LogMessage.cs:13
BuildResultCode Run(Mode mode, bool writeIndexFile=true, bool enableMonitor=true)
Runs this instance.
Definition: Builder.cs:620
void WriteIndexFile(bool mergeWithCurrentIndexFile)
Write the generated objects into the index map file.
Definition: Builder.cs:586
Builder(string buildPath, string buildProfile, string indexFilename, string inputHashesFilename, ILogger logger)
Definition: Builder.cs:218
Scheduler that manage a group of cooperating MicroThread.
Definition: Scheduler.cs:20
void Reset()
Discard the current Root build step and initialize a new empty one.
Definition: Builder.cs:576
void Warning(string message, Exception exception, CallerInfo callerInfo=null)
Logs the specified warning message with an exception.
Interface for logging.
Definition: ILogger.cs:8