Repository navigation
Expand file tree
/
Copy pathBuildManager.cs
More file actions
123 lines (104 loc) · 4.37 KB
/
Copy pathBuildManager.cs
File metadata and controls
123 lines (104 loc) · 4.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using System.Text;
using Fallout.Build.Execution;
using Fallout.Common.Tooling;
using Fallout.Common.Utilities;
using Fallout.Common.Utilities.Collections;
using Microsoft.Extensions.DependencyModel;
using Serilog;
#pragma warning disable CA2255
namespace Fallout.Common.Execution;
internal static class BuildManager
{
private const int ErrorExitCode = -1;
// Facade over the active BuildContext (FT-2): callers register/unregister during a run; the
// context owns the handler list and discards it on dispose.
public static event Action CancellationHandler
{
add => BuildContext.Current?.RegisterCancellationHandler(value);
remove => BuildContext.Current?.UnregisterCancellationHandler(value);
}
[ModuleInitializer]
public static void Initialize()
{
DependencyContext.Default?.GetRuntimeAssemblyNames(string.Empty)
.Where(x => x.FullName.StartsWith("Fallout."))
.ForEach(x => AppDomain.CurrentDomain.Load(x));
}
public static int Execute<T>(Expression<Func<T, Target>>[] defaultTargetExpressions)
where T : FalloutBuild, new()
{
Console.OutputEncoding = Encoding.UTF8;
Console.InputEncoding = Encoding.UTF8;
// The per-run scope owns the global subscriptions + teardown (FT-2 / #307). Disposed at
// method exit, so re-invocation in the same process starts clean even if `new T()` throws.
using var context = BuildContext.Activate();
var build = new T();
try
{
Logging.Configure(build);
build.ExecutableTargets = ExecutableTargetFactory.CreateAll(build, defaultTargetExpressions);
build.ExecuteExtension<IOnBuildCreated>(x => x.OnBuildCreated(build.ExecutableTargets));
NuGetToolPathResolver.EmbeddedPackagesDirectory = build.EmbeddedPackagesDirectory;
NuGetToolPathResolver.NuGetPackagesConfigFile = build.NuGetPackagesConfigFile;
NuGetToolPathResolver.NuGetAssetsConfigFile = build.NuGetAssetsConfigFile;
NpmToolPathResolver.NpmPackageJsonFile = build.NpmPackageJsonFile;
if (!build.NoLogo)
{
build.WriteLogo();
}
// TODO: move InvokedTargets to ExecutableTargetFactory
build.ExecutionPlan = ExecutionPlanner.GetExecutionPlan(
build.ExecutableTargets,
ParameterService.GetParameter<string[]>(() => build.InvokedTargets));
ToolRequirementService.EnsureToolRequirements(build, build.ExecutionPlan);
build.ExecuteExtension<IOnBuildInitialized>(x => x.OnBuildInitialized(build.ExecutableTargets, build.ExecutionPlan));
CancellationHandler += Finish;
BuildExecutor.Execute(
build,
ParameterService.GetParameter<string[]>(() => build.SkippedTargets));
return build.ExitCode ??= build.IsSucceeding ? 0 : ErrorExitCode;
}
catch (Exception exception)
{
exception = exception.Unwrap();
if (exception is not TargetExecutionException)
{
Log.Verbose(exception, "Target-unrelated exception was thrown");
Host.Error(exception.Message);
}
return build.ExitCode ??= ErrorExitCode;
}
finally
{
Finish();
Log.CloseAndFlush();
// Per-run teardown (handler unsubscription + state reset) is owned by the BuildContext,
// run when `context` is disposed at method exit.
}
void Finish()
{
if (build.ExecutionPlan == null)
{
return;
}
foreach (var target in build.ExecutionPlan)
{
target.Stopwatch.Stop();
target.Status = target.Status switch
{
ExecutionStatus.Running => ExecutionStatus.Aborted,
ExecutionStatus.Scheduled => ExecutionStatus.NotRun,
_ => target.Status
};
}
build.WriteErrorsAndWarnings();
build.WriteTargetOutcome();
build.WriteBuildOutcome();
build.ExecuteExtension<IOnBuildFinished>(x => x.OnBuildFinished());
}
}
}