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
121
122 Log.Debug("Looking for Unreal Engine installation {0}", engineAssociation);
123
124 EnginePathCache = locator.GetEngine(engineAssociation);
125 Assert.NotNull(EnginePathCache, "Couldn't find Unreal Engine with that association");
126
127 Log.Debug("Found at: {0}", EnginePathCache!);
128 return EnginePathCache!;
129 }
130
131 /// <summary>
132 /// Get high-level version of currently used Engine
133 /// </summary>
134 public static EngineVersion Version(IUnrealBuild build) => build.GetEngineVersionFromProject();
135
136 /// <summary>
137 /// Create a compatibility flag mask which indicates that a feature is available un-broken
138 /// in given and the following versions of Unreal Engine
139 /// </summary>
140 public static UnrealCompatibility AndLater(this UnrealCompatibility compatibility)
141 => ~(compatibility - 1);
142
143 /// <summary>
144 /// Are we working with UE4
145 /// </summary>
146 public static bool Is4(IUnrealBuild build) => Version(build).SemanticalVersion.Major == 4;
147
148 /// <summary>
149 /// Are we working with UE5
150 /// </summary>
151 public static bool Is5(IUnrealBuild build) => Version(build).SemanticalVersion.Major == 5;
152
153 /// <summary>
154 /// Is given path a vanilla engine most probably installed via the Marketplace?
155 /// </summary>
156 public static bool IsInstalled(AbsolutePath enginePath)
157 => (enginePath / "Engine" / "Build" / "InstalledBuild.txt").FileExists();
158
159 /// <summary>
160 /// Gets the parsed content of Build.version file of an engine instance
161 /// </summary>
162 public static UnrealBuildVersion GetBuildVersion(AbsolutePath enginePath)
163 {
164 var buildVersionPath = enginePath / "Engine" / "Build" / "Build.version";
165 Assert.FileExists(buildVersionPath, $"Specified path was not an Unreal Engine instance ({buildVersionPath})");
166 return buildVersionPath.ReadJson<UnrealBuildVersion>(JsonReadSettings);
167 }
168
169 /// <summary>
170 /// Are we working with a vanilla engine most probably installed via the Marketplace?
171 /// </summary>
172 public static bool IsInstalled(EngineVersion ofVersion)
173 => IsInstalled(ofVersion.EnginePath);
174
175 /// <summary>
176 /// Are we working with a vanilla engine most probably installed via the Marketplace?
177 /// </summary>
178 public static bool IsInstalled(IUnrealBuild build)
179 => IsInstalled(GetEnginePath(build));
180
181 /// <summary>
182 /// Is given path an engine built from source?
183 /// </summary>
184 public static bool IsSource(AbsolutePath enginePath)
185 => !IsInstalled(enginePath);
186
187 /// <summary>
188 /// Are we working with an engine built from source?
189 /// </summary>
190 public static bool IsSource(EngineVersion ofVersion)
191 => IsSource(ofVersion.EnginePath);
192
193 /// <summary>
194 /// Are we working with an engine built from source?
195 /// </summary>
196 public static bool IsSource(IUnrealBuild build)
197 => IsSource(GetEnginePath(build));
198
199 public static AbsolutePath GetEnginePath(IUnrealBuild build)
200 => Version(build).EnginePath;
201
202 /// <summary>
203 /// Get the current development platform flag Nuke.Unreal is ran on.
204 /// </summary>
206 {
207 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
208 return UnrealPlatformFlag.Win64;
209
210 if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
211 return UnrealPlatformFlag.Mac;
212
213 if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
214 return UnrealPlatformFlag.Linux;
215
216 throw new Exception("Attempting to build on an unsupported platform");
217 }
218
219 /// <summary>
220 /// Get the current development platform Nuke.Unreal is ran on.
221 /// </summary>
223
224 /// <summary>
225 /// On Mac many Unreal tools need the Mono bootstrap.
226 /// </summary>
227 public static AbsolutePath MacRunMono(EngineVersion ofVersion) =>
228 ofVersion.EnginePath / "Engine" / "Build" / "BatchFiles" / "Mac" / "RunMono.sh";
229
230 /// <summary>
231 /// Exit handler which throws an exception on error which doesn't include the entire process output, which tends
232 /// to be biblical amount in case of Unreal
233 /// </summary>
234 public static Action<IProcess> UnrealToolExitHandler(bool repeatErrorsOnFailure = false)
235 => p => p.AssertZeroExitCodeNoLog(repeatErrorsOnFailure);
236
237 /// <summary>Prepare invocation for UBT</summary>
238 /// <returns>A Tool delegate for UBT</returns>
239 public static ToolEx BuildTool(EngineVersion ofVersion)
240 {
241 var ubtPath = ofVersion.SemanticalVersion.Major >= 5
242 ? ofVersion.EnginePath / "Engine" / "Binaries" / "DotNET" / "UnrealBuildTool" / "UnrealBuildTool.exe"
243 : ofVersion.EnginePath / "Engine" / "Binaries" / "DotNET" / "UnrealBuildTool.exe";
244
245 return ToolExResolver.GetTool(ubtPath)
246 .WithSemanticLogging()
247 .With(exitHandler: UnrealToolExitHandler())
248 ;
249
250 // TODO: MacOS: "sh", $"\"{MacRunMono(ofVersion)}\" \"{ubtPath}\" " + arguments
251 // TODO: Linux: "mono", $"\"{ubtPath}\" " + arguments
252 }
253
254 /// <summary>
255 /// Prepare invocation for UBT with extra fluent-API configuration
256 /// </summary>
257 /// <param name="ofVersion"></param>
258 /// <param name="config">
259 /// Auto-generated Configuration facilities mirroring UBT arguments
260 /// </param>
261 /// <returns>A Tool delegate for UBT</returns>
262 public static ToolEx BuildTool(EngineVersion ofVersion, Action<UbtConfig> config)
263 {
264 var toolConfig = new UbtConfig();
265 config.Invoke(toolConfig);
266 return BuildTool(ofVersion).With(
267 arguments: toolConfig.Gather(ofVersion),
268 workingDirectory: ofVersion.EnginePath / "Engine" / "Source",
269 logInvocation: true
270 );
271 }
272
273 /// <summary>Prepare invocation for UBT</summary>
274 /// <returns>A Tool delegate for UBT</returns>
275 public static ToolEx BuildTool(IUnrealBuild build) => BuildTool(Version(build));
276
277 /// <summary>
278 /// Prepare invocation for UBT with extra fluent-API configuration
279 /// </summary>
280 /// <param name="build"></param>
281 /// <param name="config">
282 /// Auto-generated Configuration facilities mirroring UBT arguments
283 /// </param>
284 /// <returns>A Tool delegate for UBT</returns>
285 public static ToolEx BuildTool(IUnrealBuild build, Action<UbtConfig> config) => BuildTool(Version(build), config);
286
287 /// <summary>Prepare invocation for UAT</summary>
288 /// <returns>A Tool delegate for UAT</returns>
289 public static ToolEx AutomationTool(EngineVersion ofVersion)
290 {
291 var scriptExt = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "bat" : "sh";
292 return ToolExResolver.GetTool(ofVersion.EnginePath / "Engine" / "Build" / "BatchFiles" / $"RunUAT.{scriptExt}")
293 .WithSemanticLogging(filter: l =>
294 !(l.Contains("Reading chunk manifest") && l.Contains("which contains 0 entries"))
295 )
296 .With(exitHandler: UnrealToolExitHandler())
297 ;
298 }
299
300 /// <summary>
301 /// Prepare invocation for UAT with extra fluent-API configuration
302 /// </summary>
303 /// <param name="ofVersion"></param>
304 /// <param name="config">
305 /// Auto-generated Configuration facilities mirroring UAT arguments
306 /// </param>
307 /// <returns>A Tool delegate for UAT</returns>
308 public static ToolEx AutomationTool(EngineVersion ofVersion, Action<UatConfig> config)
309 {
310 var toolConfig = new UatConfig();
311 config?.Invoke(toolConfig);
312 return AutomationTool(ofVersion).With(
313 arguments: $"{toolConfig.Gather(ofVersion):nq}",
314 workingDirectory: ofVersion.EnginePath / "Engine" / "Source",
315 logInvocation: true
316 );
317 }
318
319 /// <summary>Prepare invocation for UAT</summary>
320 /// <returns>A Tool delegate for UAT</returns>
321 public static ToolEx AutomationTool(IUnrealBuild build) => AutomationTool(Version(build));
322
323 /// <summary>
324 /// Prepare invocation for UAT with extra fluent-API configuration
325 /// </summary>
326 /// <param name="build"></param>
327 /// <param name="config">
328 /// Auto-generated Configuration facilities mirroring UAT arguments
329 /// </param>
330 /// <returns>A Tool delegate for UAT</returns>
331 public static ToolEx AutomationTool(IUnrealBuild build, Action<UatConfig> config) => AutomationTool(Version(build), config);
332
333 /// <summary>
334 /// Clear intermediate folders of Unreal from a given folder
335 /// </summary>
336 public static void ClearFolder(AbsolutePath folder)
337 {
338 (folder / "Intermediate").ExistingDirectory()?.DeleteDirectory();
339 (folder / "Binaries").ExistingDirectory()?.DeleteDirectory();
340 (folder / "DerivedDataCache").ExistingDirectory()?.DeleteDirectory();
341 }
342
343 /// <summary>
344 /// Read copyright info from the project's `DefaultGame.ini`
345 /// </summary>
346 public static string ReadCopyrightFromProject(AbsolutePath projectFolder)
347 {
348 var configPath = projectFolder / "Config" / "DefaultGame.ini";
349 if (!File.Exists(configPath)) return "Fill in Copyright info...";
350
351 var crLine = File.ReadAllLines(configPath)
352 .FirstOrDefault(l => l.StartsWith("CopyrightNotice="));
353
354 if (string.IsNullOrWhiteSpace(crLine)) return "Fill in Copyright info...";
355
356 var crEntry = crLine.Split('=', 2, StringSplitOptions.TrimEntries);
357 if (crEntry.Length < 2) return "Fill in Copyright info...";
358
359 return crEntry[1];
360 }
361
362 /// <summary>
363 /// Get a native binary tool from `Engine/Binaries` folder. Unreal tools written in C# or
364 /// stored in other folders/sub-folders are not supported. You can omit the `Unreal` part
365 /// of the tool name.
366 /// </summary>
367 /// <param name="build"></param>
368 /// <param name="name">You can omit the `Unreal` part of the tool name.</param>
369 /// <returns>A Tool delegate for selected Unreal tool</returns>
370 public static ToolEx GetTool(IUnrealBuild build, string name)
371 {
372 var binaries = GetEnginePath(build) / "Engine" / "Binaries" / GetHostPlatformFlag().ToString();
373 var ext = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : "";
374 var path = binaries / (name + ext);
375
376 if (!path.FileExists())
377 path = binaries / ("Unreal" + name + ext);
378
379 Assert.FileExists(path, $"Requested tool {name} doesn't exist.");
380 return ToolExResolver.GetTool(path)
381 .With(exitHandler: UnrealToolExitHandler())
382 ;
383 }
384}
High level representation of an Unreal Engine version.
Version SemanticalVersion
Semantical version representation of the given Unreal Engine.
AbsolutePath EnginePath
Cached engine path.
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
Static functions and common utilities for locating Unreal Engine.
static IUnrealLocator Make()
Get the current platform implementation of an Unreal Locator.
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:289
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 UnrealBuildVersion GetBuildVersion(AbsolutePath enginePath)
Gets the parsed content of Build.version file of an engine instance.
Definition Unreal.cs:162
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:370
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:308
static UnrealPlatformFlag GetHostPlatformFlag()
Get the current development platform flag Nuke.Unreal is ran on.
Definition Unreal.cs:205
static ToolEx BuildTool(EngineVersion ofVersion)
Prepare invocation for UBT.
Definition Unreal.cs:239
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:346
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:262
static void ClearFolder(AbsolutePath folder)
Clear intermediate folders of Unreal from a given folder.
Definition Unreal.cs:336
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....
record class UnrealBuildVersion(int MajorVersion, int MinorVersion, int PatchVersion, ulong? Changelist, ulong? CompatibleChangelist, int? IsLicenseeVersion, int? IsPromotedBuild, string? BranchName)
Represents a Build.version file in an engine instance.