Nuke.Unreal
Build Unreal apps in Style.
Loading...
Searching...
No Matches
Unreal.cs
1using System;
2using System.Linq;
3using System.IO;
4using System.Collections.Generic;
5using Nuke.Common.IO;
6using System.Runtime.InteropServices;
7using Nuke.Common.Tooling;
8using Newtonsoft.Json;
9using Serilog;
10using System.Text;
12using Nuke.Cola.Tooling;
13using Nuke.Common;
14using Nuke.Common.Utilities;
15using Newtonsoft.Json.Converters;
16
17namespace Nuke.Unreal;
18
19/// <summary>
20/// A collection of utilities around basic functions regarding the environment of the Engine
21/// we're working with.
22/// </summary>
23public static class Unreal
24{
25 /// <summary>
26 /// Frankly this is not really relevant anymore
27 /// </summary>
28 public static readonly HashSet<AbsolutePath>? EngineSearchPaths;
29
30 /// <summary>
31 /// Once the Engine location is found for the current session, it ain't gonna move around,
32 /// so we cache it.
33 /// </summary>
34 public static AbsolutePath? EnginePathCache = null;
35
36 static Unreal()
37 {
38 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
39 {
40 // Use UnrealLocator
41 return;
42 }
43
44 // TODO: Use UnrealLocator on other platforms as well
45 if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
46 {
47 EngineSearchPaths = new()
48 {
49 (AbsolutePath) @"/Users/Shared/Epic Games",
50 };
51 return;
52 }
53 if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
54 {
55 // TODO: build and use UnrealLocator on linux
56 EngineSearchPaths = new()
57 {
58 (AbsolutePath) @"/Users/Shared/Epic Games",
59 };
60 return;
61 }
62
63 throw new Exception("Attempting to build on an unsupported platform");
64 }
65
66 /// <summary>
67 /// Common `JsonSerializerSettings` for Unreal conventions of JSON format
68 /// </summary>
69 public static readonly JsonSerializerSettings JsonReadSettings = new()
70 {
71 MissingMemberHandling = MissingMemberHandling.Ignore,
72 DefaultValueHandling = DefaultValueHandling.Populate,
73 NullValueHandling = NullValueHandling.Include,
74 Converters = {
75 new StringEnumConverter(false)
76 }
77 };
78
79 /// <summary>
80 /// Write data in JSON with Unreal conventions of JSON format
81 /// </summary>
82 public static void WriteJson(object input, AbsolutePath path)
83 {
84 var sb = new StringBuilder();
85 var sw = new StringWriter(sb);
86
87 using var jtw = new JsonTextWriter(sw)
88 {
89 Formatting = Formatting.Indented,
90 Indentation = 1,
91 IndentChar = '\t'
92 };
93
94 var serializer = new JsonSerializer()
95 {
96 NullValueHandling = NullValueHandling.Ignore,
97 Formatting = Formatting.Indented,
98 Converters = {
99 new StringEnumConverter(false)
100 }
101 };
102 serializer.Serialize(jtw, input);
103 File.WriteAllText(path, sb.ToString());
104 }
105
106 /// <summary>
107 /// In the rare and unlikely case that the Engine location may have changed during one
108 /// session
109 /// </summary>
110 public static void InvalidateEnginePathCache() => EnginePathCache = null;
111
112 /// <summary>
113 /// Get the Unreal Engine path based on an input association text.
114 /// (version, GUID or absolute path)
115 /// </summary>
116 public static AbsolutePath GetEnginePath(string engineAssociation, bool ignoreCache = false)
117 {
118 if (!ignoreCache && EnginePathCache != null) return EnginePathCache;
119
120 IUnrealLocator locator = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
122 : new GenericUnrealLocator();
123
124 Log.Debug("Looking for Unreal Engine installation {0}", engineAssociation);
125
126 EnginePathCache = locator.GetEngine(engineAssociation);
127 Assert.NotNull(EnginePathCache, "Couldn't find Unreal Engine with that association");
128
129 Log.Debug("Found at: {0}", EnginePathCache!);
130 return EnginePathCache!;
131 }
132
133 /// <summary>
134 /// Get high-level version of currently used Engine
135 /// </summary>
136 public static EngineVersion Version(IUnrealBuild build) => build.GetEngineVersionFromProject();
137
138 /// <summary>
139 /// Create a compatibility flag mask which indicates that a feature is available un-broken
140 /// in given and the following versions of Unreal Engine
141 /// </summary>
142 public static UnrealCompatibility AndLater(this UnrealCompatibility compatibility)
143 => ~(compatibility - 1);
144
145 /// <summary>
146 /// Are we working with UE4
147 /// </summary>
148 public static bool Is4(IUnrealBuild build) => Version(build).SemanticalVersion.Major == 4;
149
150 /// <summary>
151 /// Are we working with UE5
152 /// </summary>
153 public static bool Is5(IUnrealBuild build) => Version(build).SemanticalVersion.Major == 5;
154
155 /// <summary>
156 /// Is given path a vanilla engine most probably installed via the Marketplace?
157 /// </summary>
158 public static bool IsInstalled(AbsolutePath enginePath)
159 => (enginePath / "Engine" / "Build" / "InstalledBuild.txt").FileExists();
160
161 /// <summary>
162 /// Are we working with a vanilla engine most probably installed via the Marketplace?
163 /// </summary>
164 public static bool IsInstalled(EngineVersion ofVersion)
165 => IsInstalled(ofVersion.EnginePath);
166
167 /// <summary>
168 /// Are we working with a vanilla engine most probably installed via the Marketplace?
169 /// </summary>
170 public static bool IsInstalled(IUnrealBuild build)
171 => IsInstalled(GetEnginePath(build));
172
173 /// <summary>
174 /// Is given path an engine built from source?
175 /// </summary>
176 public static bool IsSource(AbsolutePath enginePath)
177 => !IsInstalled(enginePath);
178
179 /// <summary>
180 /// Are we working with an engine built from source?
181 /// </summary>
182 public static bool IsSource(EngineVersion ofVersion)
183 => IsSource(ofVersion.EnginePath);
184
185 /// <summary>
186 /// Are we working with an engine built from source?
187 /// </summary>
188 public static bool IsSource(IUnrealBuild build)
189 => IsSource(GetEnginePath(build));
190
191 public static AbsolutePath GetEnginePath(IUnrealBuild build)
192 => Version(build).EnginePath;
193
194 /// <summary>
195 /// Get the current development platform flag Nuke.Unreal is ran on.
196 /// </summary>
198 {
199 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
200 return UnrealPlatformFlag.Win64;
201
202 if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
203 return UnrealPlatformFlag.Mac;
204
205 if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
206 return UnrealPlatformFlag.Linux;
207
208 throw new Exception("Attempting to build on an unsupported platform");
209 }
210
211 /// <summary>
212 /// Get the current development platform Nuke.Unreal is ran on.
213 /// </summary>
215
216 /// <summary>
217 /// On Mac many Unreal tools need the Mono bootstrap.
218 /// </summary>
219 public static AbsolutePath MacRunMono(EngineVersion ofVersion) =>
220 ofVersion.EnginePath / "Engine" / "Build" / "BatchFiles" / "Mac" / "RunMono.sh";
221
222 /// <summary>
223 /// Exit handler which throws an exception on error which doesn't include the entire process output, which tends
224 /// to be biblical amount in case of Unreal
225 /// </summary>
226 public static Action<IProcess> UnrealToolExitHandler(bool repeatErrorsOnFailure = false)
227 => p => p.AssertZeroExitCodeNoLog(repeatErrorsOnFailure);
228
229 /// <summary>Prepare invocation for UBT</summary>
230 /// <returns>A Tool delegate for UBT</returns>
231 public static ToolEx BuildTool(EngineVersion ofVersion)
232 {
233 var ubtPath = ofVersion.SemanticalVersion.Major >= 5
234 ? ofVersion.EnginePath / "Engine" / "Binaries" / "DotNET" / "UnrealBuildTool" / "UnrealBuildTool.exe"
235 : ofVersion.EnginePath / "Engine" / "Binaries" / "DotNET" / "UnrealBuildTool.exe";
236
237 return ToolExResolver.GetTool(ubtPath)
238 .WithSemanticLogging()
239 .With(exitHandler: UnrealToolExitHandler())
240 ;
241
242 // TODO: MacOS: "sh", $"\"{MacRunMono(ofVersion)}\" \"{ubtPath}\" " + arguments
243 // TODO: Linux: "mono", $"\"{ubtPath}\" " + arguments
244 }
245
246 /// <summary>
247 /// Prepare invocation for UBT with extra fluent-API configuration
248 /// </summary>
249 /// <param name="ofVersion"></param>
250 /// <param name="config">
251 /// Auto-generated Configuration facilities mirroring UBT arguments
252 /// </param>
253 /// <returns>A Tool delegate for UBT</returns>
254 public static ToolEx BuildTool(EngineVersion ofVersion, Action<UbtConfig> config)
255 {
256 var toolConfig = new UbtConfig();
257 config.Invoke(toolConfig);
258 return BuildTool(ofVersion).With(
259 arguments: toolConfig.Gather(ofVersion),
260 workingDirectory: ofVersion.EnginePath / "Engine" / "Source",
261 logInvocation: true
262 );
263 }
264
265 /// <summary>Prepare invocation for UBT</summary>
266 /// <returns>A Tool delegate for UBT</returns>
267 public static ToolEx BuildTool(IUnrealBuild build) => BuildTool(Version(build));
268
269 /// <summary>
270 /// Prepare invocation for UBT with extra fluent-API configuration
271 /// </summary>
272 /// <param name="build"></param>
273 /// <param name="config">
274 /// Auto-generated Configuration facilities mirroring UBT arguments
275 /// </param>
276 /// <returns>A Tool delegate for UBT</returns>
277 public static ToolEx BuildTool(IUnrealBuild build, Action<UbtConfig> config) => BuildTool(Version(build), config);
278
279 /// <summary>Prepare invocation for UAT</summary>
280 /// <returns>A Tool delegate for UAT</returns>
281 public static ToolEx AutomationTool(EngineVersion ofVersion)
282 {
283 var scriptExt = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "bat" : "sh";
284 return ToolExResolver.GetTool(ofVersion.EnginePath / "Engine" / "Build" / "BatchFiles" / $"RunUAT.{scriptExt}")
285 .WithSemanticLogging(filter: l =>
286 !(l.Contains("Reading chunk manifest") && l.Contains("which contains 0 entries"))
287 )
288 .With(exitHandler: UnrealToolExitHandler())
289 ;
290 }
291
292 /// <summary>
293 /// Prepare invocation for UAT with extra fluent-API configuration
294 /// </summary>
295 /// <param name="ofVersion"></param>
296 /// <param name="config">
297 /// Auto-generated Configuration facilities mirroring UAT arguments
298 /// </param>
299 /// <returns>A Tool delegate for UAT</returns>
300 public static ToolEx AutomationTool(EngineVersion ofVersion, Action<UatConfig> config)
301 {
302 var toolConfig = new UatConfig();
303 config?.Invoke(toolConfig);
304 return AutomationTool(ofVersion).With(
305 arguments: $"{toolConfig.Gather(ofVersion):nq}",
306 workingDirectory: ofVersion.EnginePath / "Engine" / "Source",
307 logInvocation: true
308 );
309 }
310
311 /// <summary>Prepare invocation for UAT</summary>
312 /// <returns>A Tool delegate for UAT</returns>
313 public static ToolEx AutomationTool(IUnrealBuild build) => AutomationTool(Version(build));
314
315 /// <summary>
316 /// Prepare invocation for UAT with extra fluent-API configuration
317 /// </summary>
318 /// <param name="build"></param>
319 /// <param name="config">
320 /// Auto-generated Configuration facilities mirroring UAT arguments
321 /// </param>
322 /// <returns>A Tool delegate for UAT</returns>
323 public static ToolEx AutomationTool(IUnrealBuild build, Action<UatConfig> config) => AutomationTool(Version(build), config);
324
325 /// <summary>
326 /// Clear intermediate folders of Unreal from a given folder
327 /// </summary>
328 public static void ClearFolder(AbsolutePath folder)
329 {
330 (folder / "Intermediate").ExistingDirectory()?.DeleteDirectory();
331 (folder / "Binaries").ExistingDirectory()?.DeleteDirectory();
332 (folder / "DerivedDataCache").ExistingDirectory()?.DeleteDirectory();
333 }
334
335 /// <summary>
336 /// Read copyright info from the project's `DefaultGame.ini`
337 /// </summary>
338 public static string ReadCopyrightFromProject(AbsolutePath projectFolder)
339 {
340 var configPath = projectFolder / "Config" / "DefaultGame.ini";
341 if (!File.Exists(configPath)) return "Fill in Copyright info...";
342
343 var crLine = File.ReadAllLines(configPath)
344 .FirstOrDefault(l => l.StartsWith("CopyrightNotice="));
345
346 if (string.IsNullOrWhiteSpace(crLine)) return "Fill in Copyright info...";
347
348 var crEntry = crLine.Split('=', 2, StringSplitOptions.TrimEntries);
349 if (crEntry.Length < 2) return "Fill in Copyright info...";
350
351 return crEntry[1];
352 }
353
354 /// <summary>
355 /// Get a native binary tool from `Engine/Binaries` folder. Unreal tools written in C# or
356 /// stored in other folders/sub-folders are not supported. You can omit the `Unreal` part
357 /// of the tool name.
358 /// </summary>
359 /// <param name="build"></param>
360 /// <param name="name">You can omit the `Unreal` part of the tool name.</param>
361 /// <returns>A Tool delegate for selected Unreal tool</returns>
362 public static ToolEx GetTool(IUnrealBuild build, string name)
363 {
364 var binaries = GetEnginePath(build) / "Engine" / "Binaries" / GetHostPlatformFlag().ToString();
365 var ext = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "";
366 var path = binaries / (name + ext);
367
368 if (!path.FileExists())
369 path = binaries / ("Unreal" + name + ext);
370
371 Assert.FileExists(path, $"Requested tool {name} doesn't exist.");
372 return ToolExResolver.GetTool(path)
373 .With(exitHandler: UnrealToolExitHandler())
374 ;
375 }
376}
High level representation of an Unreal Engine version.
Version SemanticalVersion
Semantical version representation of the given Unreal Engine.
AbsolutePath EnginePath
Cached engine path.
An empty Unreal Locator implementation which doesn't know about installed instances,...
Unreal Automation Tool is a vast collection of scripts solving all aspects of deploying a program mad...
Definition UatConfig.cs:13
Unreal Build Tool defines the Unreal project structure and provides unified source building utilities...
Definition UbtConfig.cs:13
High level representation of common platforms supported by Unreal Engine (NDA ones excluded) and extr...
static UnrealPlatform FromFlag(UnrealPlatformFlag flag)
Get the high-level platform from a bit-field platform flag.
A collection of utilities around basic functions regarding the environment of the Engine we're workin...
Definition Unreal.cs:24
static bool IsSource(EngineVersion ofVersion)
Are we working with an engine built from source?
static Action< IProcess > UnrealToolExitHandler(bool repeatErrorsOnFailure=false)
Exit handler which throws an exception on error which doesn't include the entire process output,...
static void WriteJson(object input, AbsolutePath path)
Write data in JSON with Unreal conventions of JSON format.
Definition Unreal.cs:82
static EngineVersion Version(IUnrealBuild build)
Get high-level version of currently used Engine.
static ToolEx AutomationTool(EngineVersion ofVersion)
Prepare invocation for UAT.
Definition Unreal.cs:281
static ToolEx AutomationTool(IUnrealBuild build, Action< UatConfig > config)
Prepare invocation for UAT with extra fluent-API configuration.
static bool IsSource(IUnrealBuild build)
Are we working with an engine built from source?
static UnrealPlatform GetHostPlatform()
Get the current development platform Nuke.Unreal is ran on.
static ToolEx AutomationTool(IUnrealBuild build)
Prepare invocation for UAT.
static bool IsInstalled(IUnrealBuild build)
Are we working with a vanilla engine most probably installed via the Marketplace?
static ToolEx GetTool(IUnrealBuild build, string name)
Get a native binary tool from Engine/Binaries folder. Unreal tools written in C# or stored in other f...
Definition Unreal.cs:362
static AbsolutePath MacRunMono(EngineVersion ofVersion)
On Mac many Unreal tools need the Mono bootstrap.
static ToolEx AutomationTool(EngineVersion ofVersion, Action< UatConfig > config)
Prepare invocation for UAT with extra fluent-API configuration.
Definition Unreal.cs:300
static UnrealPlatformFlag GetHostPlatformFlag()
Get the current development platform flag Nuke.Unreal is ran on.
Definition Unreal.cs:197
static ToolEx BuildTool(EngineVersion ofVersion)
Prepare invocation for UBT.
Definition Unreal.cs:231
static void InvalidateEnginePathCache()
In the rare and unlikely case that the Engine location may have changed during one session.
static bool IsSource(AbsolutePath enginePath)
Is given path an engine built from source?
static string ReadCopyrightFromProject(AbsolutePath projectFolder)
Read copyright info from the project's DefaultGame.ini
Definition Unreal.cs:338
static ToolEx BuildTool(IUnrealBuild build)
Prepare invocation for UBT.
static UnrealCompatibility AndLater(this UnrealCompatibility compatibility)
Create a compatibility flag mask which indicates that a feature is available un-broken in given and t...
static bool Is5(IUnrealBuild build)
Are we working with UE5.
static readonly JsonSerializerSettings JsonReadSettings
Common JsonSerializerSettings for Unreal conventions of JSON format.
Definition Unreal.cs:69
static bool Is4(IUnrealBuild build)
Are we working with UE4.
static bool IsInstalled(AbsolutePath enginePath)
Is given path a vanilla engine most probably installed via the Marketplace?
static ? AbsolutePath EnginePathCache
Once the Engine location is found for the current session, it ain't gonna move around,...
Definition Unreal.cs:34
static bool IsInstalled(EngineVersion ofVersion)
Are we working with a vanilla engine most probably installed via the Marketplace?
static ToolEx BuildTool(EngineVersion ofVersion, Action< UbtConfig > config)
Prepare invocation for UBT with extra fluent-API configuration.
Definition Unreal.cs:254
static void ClearFolder(AbsolutePath folder)
Clear intermediate folders of Unreal from a given folder.
Definition Unreal.cs:328
static readonly? HashSet< AbsolutePath > EngineSearchPaths
Frankly this is not really relevant anymore.
Definition Unreal.cs:28
static AbsolutePath GetEnginePath(string engineAssociation, bool ignoreCache=false)
Get the Unreal Engine path based on an input association text. (version, GUID or absolute path)
Definition Unreal.cs:116
static ToolEx BuildTool(IUnrealBuild build, Action< UbtConfig > config)
Prepare invocation for UBT with extra fluent-API configuration.
Base interface for build components which require an UnrealBuild main class.
Common interface for locating Unreal Engine instances in different environments.
AbsolutePath? GetEngine(string name)
Get the path to an installed engine by its name or its absolute path.
UnrealPlatformFlag
Bit-field representation of Unreal platforms and platform-families.
UnrealCompatibility
A flag enum representation for checking the Unreal version compatibility of various features....