Nuke.Unreal
Build Unreal apps in Style.
Loading...
Searching...
No Matches
UnrealPlugin.cs
1
2using System;
3using System.Collections.Generic;
4using System.IO;
5using System.Linq;
6using System.Runtime.InteropServices;
7using EverythingSearchClient;
8using Newtonsoft.Json.Linq;
9using Nuke.Cola;
10using Nuke.Cola.FolderComposition;
11using Nuke.Cola.Search;
12using Nuke.Common;
13using Nuke.Common.IO;
14using Nuke.Common.Utilities;
15using Nuke.Common.Utilities.Collections;
17using Serilog;
18
19namespace Nuke.Unreal.Plugins;
20
21/// <summary>
22/// Options for creating a distributable version of this plugin, usually for making it
23/// independent of Nuke.
24/// </summary>
25/// <param name="OutputSubfolder">
26/// Relative path to the output folder indicated by `UnrealBuild.GetOutput()`.
27/// Default is `Plugins/{Name}/Distribution`
28/// </param>
29/// <param name="OutputOverride">
30/// If set, this will disregard both `UnrealBuild.GetOutput()` and `OutputSubfolder`, and use this
31/// as the output of plugin distribution tasks
32/// </param>
33/// <param name="GenerateFilterPluginIni">
34/// Generate a FilterPlugin.ini in the plugin's Config folder, so Unreal tools will not ignore
35/// extra files handled with Nuke.
36/// </param>
37/// <param name="UPluginAssociateEngineVersion">
38/// When set to true, distributing sources of plugins will write engine version to UPlugin file.
39/// </param>
40/// <param name="UPluginIsInstalled">
41/// Mark plugin as Installed when distributing. Default is true
42/// </param>
43/// <param name="UPluginConfig">
44/// Modify plugin descriptor for the distributed plugin after all other configuration is done.
45/// </param>
46public record class PluginDistributionOptions(
47 RelativePath? OutputSubfolder = null,
48 AbsolutePath? OutputOverride = null,
49 bool GenerateFilterPluginIni = true,
50 bool UPluginAssociateEngineVersion = true,
51 bool UPluginIsInstalled = true,
52 Func<PluginDescriptor, PluginDescriptor>? UPluginConfig = null
53);
54
55/// <summary>
56/// Method of packaging a plugin.
57/// </summary>
59{
60 /// <summary>
61 /// Use vanilla UAT BuildPlugin feature
62 /// </summary>
63 UAT,
64
65 /// <summary>
66 /// <para>
67 /// Nuke.Unreal will use UBT directly in a similar way than UAT does, but with extra
68 /// features/bugfixes that UAT doesn't have, for example automatically handle plugin
69 /// dependencies, but in a customizable way. This is the default method.
70 /// </para>
71 /// </summary>
72 UBT
73}
74
75/// <summary>
76/// Options for packaging plugins for binary distribution..
77/// </summary>
78/// <param name="OutputSubfolder">
79/// Relative path to the output folder indicated by `UnrealBuild.GetOutput()`.
80/// Default is `Plugins/{Name}/Build`
81/// </param>
82/// <param name="OutputOverride">
83/// If set, this will disregard both `UnrealBuild.GetOutput()` and `OutputSubfolder`, and use this
84/// as the output of plugin distribution tasks
85/// </param>
86/// <param name="UseDistributedPlugin">
87/// If set to true, first make a distributed copy of this plugin and then package it with UAT
88/// from that.
89/// </param>
90/// <param name="Method">
91/// Select the method of packaging the plugin. If you have problems with duplicate types in your
92/// module rules, then set this to PluginBuildMethod.UBT.
93/// </param>
94/// <param name="Platforms">
95/// Extra platforms to build this plugin for. UnrealBuild.Platform is always added to this list.
96/// </param>
97public record class PluginBuildOptions(
98 RelativePath? OutputSubfolder = null,
99 AbsolutePath? OutputOverride = null,
100 bool UseDistributedPlugin = true,
102 UnrealPlatform[]? Platforms = null
103);
104
105/// <summary>
106/// Arguments for BuildPlugin
107/// </summary>
108public record class PluginBuildArguments(
109 Func<UatConfig, UatConfig>? UatConfig = null,
110 Func<UbtConfig, UbtConfig>? UbtConfig = null,
111 PluginBuildOptions? BuildOptions = null,
112 PluginDistributionOptions? DistOptions = null,
113 Func<UnrealPlugin, PluginBuildArguments, PluginBuildArguments?>? DependencyHandler = null
114);
115
116/// <summary>
117/// A class encapsulating information and tasks around one Unreal plugin.
118/// </summary>
119public class UnrealPlugin
120{
121 public static readonly Dictionary<AbsolutePath, UnrealPlugin> Instances = [];
122 internal static string RelativePathDistinction<T>(T path)
123 => path!.ToString()!.Trim().Trim('/');
124
125 /// <summary>
126 /// <para>
127 /// Get a `PluginInfo` instance from the path to its `.uplugin` file, or a file/folder
128 /// belonging to that plugin (in its subtree of files and folders). If a PluginInfo
129 /// doesn't exist yet, one will be created given that the associated plugin exists.
130 /// </para>
131 /// <para>
132 /// For example:
133 /// </para>
134 /// <code>
135 /// // In a local `MyPlugin.nuke.cs`
136 /// PluginInfo.Get(this.ScriptFolder()); // Gets or creates a `PluginInfo` about `MyPlugin.uplugin`
137 /// </code>
138 /// </summary>
139 /// <remarks>
140 /// Referring to non-existing plugins is a runtime error.
141 /// </remarks>
142 /// <param name="from"></param>
143 /// <returns></returns>
144 public static UnrealPlugin Get(AbsolutePath from)
145 {
146 var uplugin = from.FileExists() && from.HasExtension(".uplugin") ? from : from.GetOwningPlugin();
147 Assert.NotNull(uplugin, $"Given plugin context {from} didn't contain an Unreal plugin");
148 if (!Instances.ContainsKey(uplugin!))
149 {
150 Log.Debug("Handling plugin: {0}", uplugin!.NameWithoutExtension);
151 Instances.Add(uplugin!, new (uplugin!));
152 }
153 return Instances[uplugin!];
154 }
155
156 internal UnrealPlugin(AbsolutePath uplugin)
157 {
158 PluginPath = uplugin;
160 if (FilterPluginIni.FileExists())
161 {
162 Log.Debug("Considering FilterPlugin.ini of {0}", Name);
163 var inFiles = FilterPluginIni.ReadAllLines()
164 .Where(l => !l.Contains("[FilterPlugin]"))
165 .Where(l => !string.IsNullOrWhiteSpace(l))
166 .Where(l => !l.StartsWith(';'))
167 .Select(l => (RelativePath)l.Trim())
168 .DistinctBy(RelativePathDistinction)
169 ;
170 AddExplicitPluginFiles(inFiles);
171 }
172 }
173
174 /// <summary>
175 /// Path to the `.uplugin` file
176 /// </summary>
177 public AbsolutePath PluginPath { get; private set; }
178
179 /// <summary>
180 /// "Immutable" C# representation of the uplugin file
181 /// </summary>
182 public PluginDescriptor Descriptor { get; private set; }
183
184 /// <summary>
185 /// Path to folder containing the `.uplugin` file
186 /// </summary>
187 public AbsolutePath Folder => PluginPath.Parent;
188
189 public AbsolutePath ConfigFolder => Folder / "Config";
190 public AbsolutePath FilterPluginIni => ConfigFolder / "FilterPlugin.ini";
191
192 /// <summary>
193 /// Short name of the plugin
194 /// </summary>
195 public string Name => PluginPath.NameWithoutExtension;
196
197 private Version? _versionCache = null;
198
199 /// <summary>
200 /// Semantic version of the plugin. Parsed from the `uplugin` file. If set, it will modify the
201 /// `.uplugin` file as well
202 /// </summary>
204 {
205 get => _versionCache ?? (
206 Version.TryParse(Descriptor.VersionName ?? "", out var version)
207 ? version
208 : new()
209 );
210 set
211 {
212 Descriptor = Descriptor with { VersionName = value.ToString(3) };
213 _versionCache = value;
215 }
216 }
217
218 private List<UnixRelativePath> _explicitPluginFiles = [];
219
220 /// <summary>
221 /// Return list of files which may need extra mention for Unreal/Epic tools. This is also
222 /// usually the contents of FilterPlugin.ini
223 /// </summary>
224 public IEnumerable<AbsolutePath> ExplicitPluginFiles => _explicitPluginFiles
225 .Select(f => Folder / f);
226
227 /// <summary>
228 /// Add explicit plugin files to be listed later in FilterPlugin.ini
229 /// </summary>
230 /// <param name="files">
231 /// Absolute paths to the files. Relative paths will be calculated based on plugin root.
232 /// </param>
233 public void AddExplicitPluginFiles(IEnumerable<AbsolutePath> files)
234 => AddExplicitPluginFiles(files.Select(f => Folder.GetRelativePathTo(f)));
235
236 /// <summary>
237 /// Add explicit plugin files to be listed later in FilterPlugin.ini
238 /// </summary>
239 public void AddExplicitPluginFiles(IEnumerable<RelativePath> pluginRelativePaths)
240 => _explicitPluginFiles = [..
241 _explicitPluginFiles
242 .UnionBy(
243 pluginRelativePaths.Select(f => f.ToUnixRelativePath()),
244 RelativePathDistinction
245 )
246 ];
247
248 private PluginDistributionOptions _distributionOptionsCache = new();
249 private RelativePath _defaultDistSubdir => (RelativePath) $"Plugins/{Name}/Distribution";
250
251 /// <summary>
252 /// Gets the output folder for distribution
253 /// </summary>
254 /// <param name="build"></param>
255 /// <param name="options">
256 /// Optional argument to get the output from. If another functionality has cached this
257 /// before, this is not needed.
258 /// </param>
259 public AbsolutePath GetDistributionOutput(UnrealBuild build, PluginDistributionOptions? options = null)
260 {
261 options ??= _distributionOptionsCache;
262 _distributionOptionsCache = options;
263
264 var outputSubfolder = options.OutputSubfolder ?? _defaultDistSubdir;
265 return options.OutputOverride ?? (build.GetOutput() / outputSubfolder);
266 }
267
268 private PluginBuildOptions _buildOptionsCache = new();
269 private RelativePath _defaultBuildSubdir => (RelativePath) $"Plugins/{Name}/Build";
270
271 /// <summary>
272 /// Gets the output folder for packaging this plugin
273 /// </summary>
274 /// <param name="build"></param>
275 /// <param name="options">
276 /// Optional argument to get the output from. If another functionality has cached this
277 /// before, this is not needed.
278 /// </param>
279 public AbsolutePath GetBuildOutput(UnrealBuild build, PluginBuildOptions? options = null)
280 {
281 options ??= _buildOptionsCache;
282 _buildOptionsCache = options;
283
284 var outputSubfolder = options.OutputSubfolder ?? _defaultBuildSubdir;
285 return options.OutputOverride ?? (build.GetOutput() / outputSubfolder);
286 }
287
288 /// <summary>
289 /// Generate the `FilterPlugin.ini` file after all components have submitted their
290 /// explicit files.
291 /// </summary>
293 {
294 AddDefaultExplicitPluginFiles(build);
295
296 var configPath = FilterPluginIni;
297 Log.Debug("Generating FilterPlugin.ini: {0}", configPath);
298
299 var lines = _explicitPluginFiles
300 .Select(f => ("/" + f.ToString()).Replace("//", "/"))
301 .Distinct()
302 ;
303 if (!lines.IsEmpty())
304 configPath.WriteAllLines(lines.Prepend("[FilterPlugin]"));
305 else
306 Log.Debug("There were no files to list in FilterPlugin.ini");
307 }
308
309 private static readonly ExportManifest _filterExportManifest = new()
310 {
311 Copy = { new() { File = "Config/**/*.ini" , Not = {"FilterPlugin.ini"}} },
312 Not = {
313 "PluginFiles.yml",
314 "*.nuke.cs",
315 "*.nuke.csx",
316 "Nuke.Targets",
317 ".Nuke",
318 ".git",
319 ".gitignore",
320 ".p4ignore",
321 },
322 Use = {
323 new() { File = "**/PluginFiles.yml" }
324 }
325 };
326
327 private IEnumerable<AbsolutePath> GetDefaultExplicitPluginFiles(UnrealBuild build)
328 => build.ImportFolder((Folder, Folder.Parent / "ShouldntExist", "PluginFiles.yml"), new(
329 Pretend: true,
330 UseSubfolder: false,
331 ForceCopyLinks: true,
332 AddToMain: [_filterExportManifest]
333 )).WithFilesExpanded().Select(f => f.From);
334
335 private void AddDefaultExplicitPluginFiles(UnrealBuild build)
336 => AddExplicitPluginFiles(GetDefaultExplicitPluginFiles(build));
337
338 private bool InjectBinaryPlumbing(PluginDescriptor descriptor, out PluginDescriptor result)
339 {
340 var plumbingRootRelative = (RelativePath) "Source" / "ThirdParty" / "BinaryPlumbing";
341 var plumbingRoot = Folder / plumbingRootRelative;
342 if (!plumbingRoot.DirectoryExists())
343 {
344 result = descriptor;
345 return false;
346 }
347
348 Log.Information("{0} seemingly requires binary plumbing", Name);
349 Log.Debug("Because {0} exists", plumbingRoot);
350
351 result = descriptor with {
352 PreBuildSteps = descriptor.PreBuildSteps?.ToDictionary() ?? []
353 };
354 result.PreBuildSteps.EnsureDevelopmentPlatforms();
355
356 foreach (var platform in UnrealPlatform.DevelopmentPlatforms)
357 {
358 result.PreBuildSteps[platform] = [..
359 result.PreBuildSteps[platform]
360 .FilterBuildStepBlock("BinaryPlumbing")
361 .StartBuildStepBlock("BinaryPlumbing"),
362 $"echo Plumbing runtime dependencies for {Name} plugin",
363 ];
364 }
365
366 foreach (var subdir in plumbingRoot.GetDirectories())
367 {
368 Log.Debug("Plumbing from {0}", subdir);
369
370 result.PreBuildSteps[UnrealPlatform.Win64].AddRange([
371 $"echo Copying {subdir.Name} runtime dependencies",
372 $"robocopy /s /v /njh /njs /np /ndl \"$(PluginDir)\\{plumbingRootRelative.ToWinRelativePath()}\\{subdir.Name}\" \"$(PluginDir)\\Binaries\"",
373 "if %ERRORLEVEL% LSS 8 (exit /B 0) else (exit /B %ERRORLEVEL%)"
374 ]);
375
376 result.PreBuildSteps[UnrealPlatform.Linux].AddRange([
377 $"echo Copying {subdir.Name} runtime dependencies",
378 $"cp -vnpR \"$(PluginDir)/{plumbingRootRelative.ToUnixRelativePath()}/{subdir.Name}\" \"$(PluginDir)/Binaries\"",
379 "chmod -R +x \"$(PluginDir)/Binaries\""
380 ]);
381
382 result.PreBuildSteps[UnrealPlatform.Mac].AddRange([
383 $"echo Copying {subdir.Name} runtime dependencies",
384 $"cp -vnpR \"$(PluginDir)/{plumbingRootRelative.ToUnixRelativePath()}/{subdir.Name}\" \"$(PluginDir)/Binaries\"",
385 "chmod -R +x \"$(PluginDir)/Binaries\""
386 ]);
387 }
388
389 return true;
390 }
391
392 /// <summary>
393 /// Create a copy of this plugin which can be distributed to other developers or other tools
394 /// who shouldn't require extra non-unreal related steps to work with it.
395 /// </summary>
396 /// <param name="build"></param>
397 /// <param name="options">Optional arguments for distribution</param>
398 /// <param name="pretend">
399 /// Do not have side effects on files, just return a list of files which may be affected
400 /// by this operation.
401 /// </param>
402 /// <returns>
403 /// <list type="bullet">
404 /// <item>result: List of files which has been copied</item>
405 /// <item>output: Output folder of distribution</item>
406 /// </list>
407 /// </returns>
408 public (IEnumerable<ImportedItem> result, AbsolutePath output) DistributeSource(
409 UnrealBuild build,
410 PluginDistributionOptions? options = null,
411 bool pretend = false
412 ) {
413 var outFolder = GetDistributionOutput(build, options);
414 options ??= _distributionOptionsCache;
415
416 if (options.GenerateFilterPluginIni && !pretend)
418
419 var result = build.ImportFolder((Folder, outFolder, "PluginFiles.yml"), new(
420 UseSubfolder: false,
421 ForceCopyLinks: true,
422 Pretend: pretend,
423 AddToMain: [_filterExportManifest, new()
424 {
425 Copy = {
426 new() { File = "Content/**/*" , Not = { "PluginFiles.yml" }},
427 new() { File = "Shaders/**/*" , Not = { "PluginFiles.yml" }},
428 new() { File = "Source/**/*" , Not = { "PluginFiles.yml" }},
429 new() { File = "Resources/**/*" , Not = { "PluginFiles.yml" }},
430 new() { File = "Tests/**/*" , Not = { "PluginFiles.yml" }},
431 new() { File = "*.uplugin" , Not = { "PluginFiles.yml" }},
432 },
433 }]
434 ));
435
436 var outUPlugin = outFolder / PluginPath.Name;
437
438 var descriptor = Descriptor with {};
439
440 if (!pretend && options.UPluginAssociateEngineVersion)
441 {
442 descriptor = descriptor with { EngineVersion = Unreal.Version(build).VersionMinor + ".0" };
443 Unreal.WriteJson(descriptor, outUPlugin);
444 }
445
446 if (!pretend && InjectBinaryPlumbing(descriptor, out descriptor))
447 {
448 Unreal.WriteJson(descriptor, outUPlugin);
449 }
450
451 if (!pretend && options.UPluginIsInstalled)
452 {
453 descriptor = descriptor with { Installed = true };
454 Unreal.WriteJson(descriptor, outUPlugin);
455 }
456
457 if (!pretend && options.UPluginConfig != null)
458 {
459 descriptor = options.UPluginConfig(descriptor);
460 Unreal.WriteJson(descriptor, outUPlugin);
461 }
462
463 return (result.WithFilesExpanded(), outFolder);
464 }
465
466 /// <summary>
467 /// Get the project plugin dependencies of this plugin as UnrealPlugin objects.
468 /// </summary>
469 /// <param name="build"></param>
470 /// <returns></returns>
471 IEnumerable<UnrealPlugin> GetProjectPluginDependencies(UnrealBuild build)
472 => Descriptor.Plugins?
473 .Where(p => p.Enabled ?? false)
474 .Select(p =>
475 {
476 var depFIle = build.PluginsFolder.SearchFiles($"**/{p.Name}.uplugin").FirstOrDefault();
477 return depFIle != null ? Get(depFIle) : null;
478 })
479 .Where(p => p != null)
480 .Select(p => p!)
481 ?? []
482 ;
483
484 private AbsolutePath? _buildOutput = null;
485
486 /// <summary>
487 /// Make a prebuilt release of this plugin for end-users. Globally set UAT and UBT arguments
488 /// are used from the input UnrealBuild
489 /// </summary>
490 /// <param name="build"></param>
491 /// <param name="uatConfig">Configurator for UAT</param>
492 /// <param name="ubtConfig">Configurator for UBT (only when UBT method is used)</param>
493 /// <param name="buildOptions">Optional arguments for packaging</param>
494 /// <param name="distOptions">Optional arguments for distribution</param>
495 /// <param name="dependencyHandler">Customize the build option for plugin dependencies</param>
496 /// <returns>Output folder of the packaged plugin</returns>
497 public AbsolutePath BuildPlugin(
498 UnrealBuild build,
499 Func<UatConfig, UatConfig>? uatConfig = null,
500 Func<UbtConfig, UbtConfig>? ubtConfig = null,
501 PluginBuildOptions? buildOptions = null,
502 PluginDistributionOptions? distOptions = null,
503 Func<UnrealPlugin, PluginBuildArguments, PluginBuildArguments?>? dependencyHandler = null
504 ) {
505 var outFolder = GetBuildOutput(build, buildOptions);
506 buildOptions ??= _buildOptionsCache;
507 uatConfig ??= _ => _;
508 ubtConfig ??= _ => _;
509
510 var sourceFolder = Folder;
511 if (buildOptions.UseDistributedPlugin || buildOptions.Method == PluginBuildMethod.UBT)
512 {
513 var (_, distFolder) = DistributeSource(build, distOptions);
514 sourceFolder = distFolder;
515 }
516
517 var hostProjectDir = build.GetOutput() / "Plugins" / Name / "HostProject";
518 outFolder.CreateOrCleanDirectory();
519 hostProjectDir.ExistingDirectory()?.DeleteDirectory();
520
521 var platforms = (buildOptions.Platforms ?? []).Union([build.Platform]);
522
523 var dependencies = GetProjectPluginDependencies(build).ToList();
524 var thisArgs = new PluginBuildArguments(
525 uatConfig,
526 ubtConfig,
527 buildOptions,
528 distOptions,
529 dependencyHandler
530 );
531
532 foreach (var unbuiltDep in dependencies.Where(d => d._buildOutput == null))
533 {
534 Log.Information("Building dependency plugin {0}", unbuiltDep.Name);
535 var args = dependencyHandler?.Invoke(unbuiltDep, thisArgs) ?? thisArgs;
536 unbuiltDep.BuildPlugin(
537 build,
538 args.UatConfig,
539 args.UbtConfig,
540 args.BuildOptions,
541 args.DistOptions,
542 args.DependencyHandler
543 );
544 }
545 var enginePluginsDir = build.UnrealEnginePath / "Engine" / "Plugins" / "Marketplace";
546 foreach (var dep in dependencies)
547 {
548 Log.Information("Copying dependency plugin {0} to {1}", dep.Name, enginePluginsDir);
549 dep._buildOutput.Copy(enginePluginsDir / dep.Name);
550 }
551
552 switch (buildOptions.Method)
553 {
554
555 case PluginBuildMethod.UAT:
556 var shortSource = sourceFolder.Shorten();
557 var shortOut = outFolder.Shorten();
558 try
559 {
560 Unreal.AutomationTool(build, _ => _
561 .BuildPlugin(_ => _
562 .Plugin(shortSource / PluginPath.Name)
563 .Package(shortOut)
564 .TargetPlatforms(string.Join('+', platforms))
565 .If(Unreal.Is4(build), _ => _
566 .VS2019()
567 )
568 .Unversioned()
569 )
570 .Apply(build.UatGlobal)
571 .Apply(uatConfig)
572 )("");
573 _buildOutput = outFolder;
574 }
575 catch (Exception) { throw; }
576 finally
577 {
578 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
579 {
580 shortSource.DeleteDirectory();
581 shortOut.AsLinkInfo()?.Delete();
582 }
583 }
584
585 break;
586
587 case PluginBuildMethod.UBT:
588
589 hostProjectDir.CreateDirectory();
590 var hostPluginDir = hostProjectDir / "Plugins" / Name;
591 var hostProject = new ProjectDescriptor(
592 EngineAssociation: build.ProjectDescriptor.EngineAssociation,
593 Plugins: [
594 new (Name: Name, Enabled: true),
595 ..
596 dependencies.Select(d => new PluginReferenceDescriptor(
597 Name: d.Name,
598 Enabled: true
599 ))
600 ]
601 );
602 Unreal.WriteJson(hostProject, hostProjectDir / "HostProject.uproject");
603 sourceFolder.Copy(hostPluginDir);
604
605 var shortHostProjectDir = hostProjectDir.Shorten();
606 var shortPluginDir = shortHostProjectDir / "Plugins" / Name;
607 try
608 {
609 UbtConfig Common(UbtConfig _) => _
610 .NoHotReload()
611 .Apply(build.UbtGlobal)
612 .Apply(ubtConfig)
613 ;
614 UbtConfig CommonProject(UbtConfig _) => _
615 .Project(shortHostProjectDir / "HostProject.uproject")
616 .Apply(Common)
617 ;
618 UbtConfig CommonPlugin(UbtConfig _) => _
619 .Plugin(shortPluginDir / PluginPath.Name)
620 .Apply(Common)
621 ;
622
623 foreach(var platform in platforms)
624 {
625 Log.Information("Building UnrealGame binaries from UProject for {0} @ {1}", Name, platform);
626 Unreal.BuildTool(build, _ => _
627 .Target("UnrealGame", platform,
628 [
629 UnrealConfig.Development,
630 UnrealConfig.Shipping
631 ])
632 .Apply(CommonProject)
633 )("");
634 if (platform.IsDevelopment)
635 {
636 Log.Information("Building UnrealEditor binaries for UProject {0} @ {1}", Name, platform);
637 Unreal.BuildTool(build, _ => _
638 .Target("UnrealEditor", platform, [UnrealConfig.Development])
639 .Apply(CommonProject)
640 )("");
641 }
642 Log.Information("Building UnrealGame binaries from UPlugin for {0} @ {1}", Name, platform);
643 Unreal.BuildTool(build, _ => _
644 .Target("UnrealGame", platform,
645 [
646 UnrealConfig.Development,
647 UnrealConfig.Shipping
648 ])
649 .Apply(CommonPlugin)
650 )("");
651 if (platform.IsDevelopment)
652 {
653 Log.Information("Building UnrealEditor binaries for UPlugin {0} @ {1}", Name, platform);
654 Unreal.BuildTool(build, _ => _
655 .Target("UnrealEditor", platform, [UnrealConfig.Development])
656 .Apply(CommonPlugin)
657 )("");
658 }
659 }
660 hostPluginDir.Copy(outFolder, ExistsPolicy.MergeAndOverwrite);
661 hostProjectDir.DeleteDirectory();
662 _buildOutput = outFolder;
663 }
664 catch(Exception) { throw; }
665 finally
666 {
667 if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
668 shortPluginDir.AsLinkInfo()?.Delete();
669
670 foreach (var dep in dependencies)
671 {
672 Log.Debug("Deleting temporary installed plugin {0}", enginePluginsDir / dep.Name);
673 (enginePluginsDir / dep.Name).ExistingDirectory()?.DeleteDirectory();
674 }
675 }
676 break;
677
678 }
679
680 return outFolder;
681 }
682}
683
684public static class UnrealPluginExtensions
685{
686 internal static IEnumerable<string> FilterBuildStepBlock(this IEnumerable<string> self, string name)
687 => self
688 .TakeUntil(a => a == "echo GENERATED BUILD STEPS".AppendNonEmpty(name))
689 .Concat(
690 self.SkipUntil(a => a == "echo GENERATED BUILD STEPS".AppendNonEmpty(name))
691 .Skip(1)
692 .SkipUntil(a => a.StartsWith("echo GENERATED BUILD STEPS"))
693 );
694
695 internal static IEnumerable<string> StartBuildStepBlock(this IEnumerable<string> self, string name)
696 => self.Append("echo GENERATED BUILD STEPS".AppendNonEmpty(name));
697
698 internal static void EnsureDevelopmentPlatforms(this Dictionary<UnrealPlatform, List<string>> self)
699 {
700 foreach (var platform in UnrealPlatform.DevelopmentPlatforms)
701 {
702 if (!self.ContainsKey(platform)) self.Add(platform, []);
703 }
704 }
705
706 public static UbtConfig StrictIncludes(this UbtConfig _) => _
707 .NoPCH()
708 .NoSharedPCH()
709 .DisableUnity();
710}
High level representation of an Unreal Engine version.
string VersionMinor
Only the Major.Minor version components, with extra information trimmed.
A class encapsulating information and tasks around one Unreal plugin.
IEnumerable< ImportedItem > result
Create a copy of this plugin which can be distributed to other developers or other tools who shouldn'...
void AddExplicitPluginFiles(IEnumerable< AbsolutePath > files)
Add explicit plugin files to be listed later in FilterPlugin.ini.
Version Version
Semantic version of the plugin. Parsed from the uplugin file. If set, it will modify the ....
AbsolutePath Folder
Path to folder containing the .uplugin file.
PluginDescriptor Descriptor
"Immutable" C# representation of the uplugin file
void GenerateFilterPluginIni(UnrealBuild build)
Generate the FilterPlugin.ini file after all components have submitted their explicit files.
AbsolutePath GetBuildOutput(UnrealBuild build, PluginBuildOptions? options=null)
Gets the output folder for packaging this plugin.
AbsolutePath GetDistributionOutput(UnrealBuild build, PluginDistributionOptions? options=null)
Gets the output folder for distribution.
void AddExplicitPluginFiles(IEnumerable< RelativePath > pluginRelativePaths)
Add explicit plugin files to be listed later in FilterPlugin.ini.
static UnrealPlugin Get(AbsolutePath from)
AbsolutePath PluginPath
Path to the .uplugin file.
IEnumerable< UnrealPlugin > GetProjectPluginDependencies(UnrealBuild build)
Get the project plugin dependencies of this plugin as UnrealPlugin objects.
string Name
Short name of the plugin.
IEnumerable< AbsolutePath > ExplicitPluginFiles
Return list of files which may need extra mention for Unreal/Epic tools. This is also usually the con...
AbsolutePath BuildPlugin(UnrealBuild build, Func< UatConfig, UatConfig >? uatConfig=null, Func< UbtConfig, UbtConfig >? ubtConfig=null, PluginBuildOptions? buildOptions=null, PluginDistributionOptions? distOptions=null, Func< UnrealPlugin, PluginBuildArguments, PluginBuildArguments?>? dependencyHandler=null)
Make a prebuilt release of this plugin for end-users. Globally set UAT and UBT arguments are used fro...
Unreal Automation Tool is a vast collection of scripts solving all aspects of deploying a program mad...
Definition UatConfig.cs:13
virtual UbtConfig NoSharedPCH(bool present=true)
Enables "Shared PCHs", a feature which significantly speeds up compile times by attempting to share c...
virtual UbtConfig NoHotReload(HotReloadMode? val=null)
Whether to perform hot reload for this target.
virtual UbtConfig DisableUnity(bool present=true)
Whether to unify C++ code into larger files for faster compilation.
virtual UbtConfig Plugin(object val=null)
Foreign plugin to compile against this target.
virtual UbtConfig NoPCH(bool present=true)
Whether PCH files should be used.
Unreal Build Tool defines the Unreal project structure and provides unified source building utilities...
Definition UbtConfig.cs:13
UbtConfig Project(string project, bool withKey=false)
Specify Project file without -Project
Definition UbtConfig.cs:20
The main build class Unreal projects using Nuke.Unreal should inherit from. This class contains all b...
ProjectDescriptor ProjectDescriptor
"Immutable" C# representation of the .uproject contents
virtual UbtConfig UbtGlobal(UbtConfig _)
UBT arguments to be applied globally for all UBT invocations. Override this function in your main bui...
virtual AbsolutePath GetOutput()
Get an output folder where the targets should store their artifacts. Override this function in your m...
virtual UatConfig UatGlobal(UatConfig _)
UAT arguments to be applied globally for all UAT invocations. Override this function in your main bui...
Build configurations UBT supports.
High level representation of common platforms supported by Unreal Engine (NDA ones excluded) and extr...
static readonly UnrealPlatform Win64
Any platform name containing 'Windows' is also mapped to this platform.
static readonly UnrealPlatform Mac
Any platform name containing 'Mac' is also mapped to this platform.
static readonly List< UnrealPlatform > DevelopmentPlatforms
List of platforms which can support the Unreal Editor.
A collection of utilities around basic functions regarding the environment of the Engine we're workin...
Definition Unreal.cs:24
static void WriteJson(object input, AbsolutePath path)
Write data in JSON with Unreal conventions of JSON format.
Definition Unreal.cs:82
static bool Is4(UnrealBuild build)
Are we working with UE4.
static Tool AutomationTool(EngineVersion ofVersion)
Prepare invocation for UAT.
Definition Unreal.cs:266
static Tool BuildTool(EngineVersion ofVersion)
Prepare invocation for UBT.
Definition Unreal.cs:219
static readonly JsonSerializerSettings JsonReadSettings
Common JsonSerializerSettings for Unreal conventions of JSON format.
Definition Unreal.cs:69
static EngineVersion Version(UnrealBuild build)
Get high-level version of currently used Engine.
record class PluginReferenceDescriptor(string? Name=null, bool? Enabled=null, bool? Optional=null, string? Description=null, string? MarketplaceURL=null, List< string >? PlatformAllowList=null, List< string >? PlatformDenyList=null, List< UnrealConfig >? TargetConfigurationAllowList=null, List< UnrealConfig >? TargetConfigurationDenyList=null, List< UnrealTargetType >? TargetAllowList=null, List< UnrealTargetType >? TargetDenyList=null, List< string >? SupportedTargetPlatforms=null, bool? HasExplicitPlatforms=null, int? RequestedVersion=null)
Representation of a reference to a plugin from a project file.
record class PluginDescriptor(int FileVersion=3, int Version=1, string? VersionName=null, string? FriendlyName=null, string? Description=null, string? Category=null, string? CreatedBy=null, string? CreatedByURL=null, string? DocsURL=null, string? MarketplaceURL=null, string? SupportURL=null, string? EngineVersion=null, bool? IsPluginExtension=null, List< UnrealPlatform >? SupportedTargetPlatforms=null, List< string >? SupportedPrograms=null, List< ModuleDescriptor >? Modules=null, List< LocalizationTargetDescriptor >? LocalizationTargets=null, bool? EnabledByDefault=null, bool? CanContainContent=null, bool? CanContainVerse=null, bool? IsBetaVersion=null, bool? IsExperimentalVersion=null, bool? Installed=null, bool? RequiresBuildPlatform=null, bool? IsSealed=null, bool? NoCode=null, bool? ExplicitlyLoaded=null, bool? HasExplicitPlatforms=null, Dictionary< UnrealPlatform, List< string > >? PreBuildSteps=null, Dictionary< UnrealPlatform, List< string > >? PostBuildSteps=null, List< PluginReferenceDescriptor >? Plugins=null, List< string >? DisallowedPlugins=null)
In-memory representation of a .uplugin file.
record class PluginDistributionOptions(RelativePath? OutputSubfolder=null, AbsolutePath? OutputOverride=null, bool GenerateFilterPluginIni=true, bool UPluginAssociateEngineVersion=true, bool UPluginIsInstalled=true, Func< PluginDescriptor, PluginDescriptor >? UPluginConfig=null)
Options for creating a distributable version of this plugin, usually for making it independent of Nuk...
PluginBuildMethod
Method of packaging a plugin.
@ UAT
Use vanilla UAT BuildPlugin feature.
record class PluginBuildArguments(Func< UatConfig, UatConfig >? UatConfig=null, Func< UbtConfig, UbtConfig >? UbtConfig=null, PluginBuildOptions? BuildOptions=null, PluginDistributionOptions? DistOptions=null, Func< UnrealPlugin, PluginBuildArguments, PluginBuildArguments?>? DependencyHandler=null)
Arguments for BuildPlugin.
record class PluginBuildOptions(RelativePath? OutputSubfolder=null, AbsolutePath? OutputOverride=null, bool UseDistributedPlugin=true, PluginBuildMethod Method=PluginBuildMethod.UBT, UnrealPlatform[]? Platforms=null)
Options for packaging plugins for binary distribution..
record class ProjectDescriptor(int FileVersion=3, string? EngineAssociation=null, string? Category=null, string? Description=null, List< ModuleDescriptor >? Modules=null, List< PluginReferenceDescriptor >? Plugins=null, List< string >? AdditionalRootDirectories=null, List< string >? AdditionalPluginDirectories=null, List< UnrealPlatform >? TargetPlatforms=null, uint? EpicSampleNameHash=null, Dictionary< UnrealPlatform, List< string > >? InitSteps=null, Dictionary< UnrealPlatform, List< string > >? PreBuildSteps=null, Dictionary< UnrealPlatform, List< string > >? PostBuildSteps=null, bool? IsEnterpriseProject=null, bool? DisableEnginePluginsByDefault=null)
In-memory representation of a .uproject file.