Nuke.Unreal
Build Unreal apps in Style.
Loading...
Searching...
No Matches
AutoRuntimeDependencies.cs
1using System;
2using System.Collections.Generic;
3using Nuke.Common;
4using System.Xml.Serialization;
5using System.IO;
6using System.Linq;
7using Nuke.Common.IO;
8using Nuke.Common.Tooling;
9using System.Runtime.CompilerServices;
10using Nuke.Cola.FolderComposition;
11using Nuke.Common.Utilities;
13using System.Collections;
14using Serilog;
15
17
18public enum RuntimeDependencyConfig
19{
20 Debug,
21 Release,
22 All
23}
24
26{
27 public required RelativePath Path;
29 public RuntimeDependencyConfig Config = RuntimeDependencyConfig.All;
30}
31
33{
34 public string Value = "";
35 public string Origin = "";
37 public RuntimeDependencyConfig Config = RuntimeDependencyConfig.All;
38};
39
41{
42 public string ModuleName = "";
43 public List<RuntimeDependency> RuntimeLibraryPath = [];
44 public List<RuntimeDependency> Files = [];
45 public List<RuntimeDependency> Dlls = [];
46}
47
49{
50 public void Generate(AbsolutePath templatesPath, AbsolutePath currentFolder, RuntimeDependencies model)
51 {
52 var templateSubFolder = "AutoRuntimeDependency";
53 if (!(templatesPath / templateSubFolder).DirectoryExists())
54 templatesPath = DefaultTemplateFolder;
55
56 var templateDir = templatesPath / templateSubFolder;
57 RenderFolder(templateDir, currentFolder, model);
58 }
59}
60
62{
63 /// <summary>
64 /// <para>
65 /// Prepare and transform a group of files for neatly be packaged as runtime
66 /// dependencies in conventional locations. This aims to alleviate some chore regarding
67 /// working with elaborate third-party/prebuilt libraries. Files are copied by
68 /// Nuke.Unreal but let UBT do it.
69 /// </para>
70 /// <para>
71 /// The result of this function is a partial module rule C# file which will list runtime
72 /// dependencies in a partial method called `SetupRuntimeDependencies`. Please take care
73 /// that the main module rules class for the target module is also partial.
74 /// </para>
75 /// <para>
76 /// Here's the overview of the usage of this function.
77 /// </para>
78 /// <list type="number">
79 /// <item>
80 /// Provide a source folder for the runtime dependencies
81 /// </item>
82 /// <item>
83 /// Provide a set of locations in runtime, relative to the destination folder serving
84 /// as runtime library paths (ideally one for each supported platforms). This may be
85 /// used as base folders to load DLL's from.
86 /// </item>
87 /// <item>
88 /// If applicable provide pattern matching functions to determine the platform and
89 /// configuration for individual files
90 /// </item>
91 /// <item>
92 /// To control the destination folder structure PrepareRuntimeDependencies may pick
93 /// up a folder composition manifest file called "RuntimeDeps.yml" (by default this
94 /// can be also overridden)
95 /// </item>
96 /// <item>
97 /// If no such manifest is available one can be passed directly in C#
98 /// </item>
99 /// </list>
100 /// <para>
101 /// The module rule will copy output files on building the project to
102 /// `&lt;plugin-directory&gt;/Binaries/&lt;binariesSubfolder&gt;/&lt;moduleName&gt;`.
103 /// Where `binariesSubfolder` is "ThirdParty" by default.
104 /// </para>
105 /// <para>
106 /// For example:
107 /// </para>
108 /// <code>
109 /// [ImplicitBuildInterface]
110 /// public interface IMyPluginTargets : INukeBuild
111 /// {
112 /// Target PrepareMyPlugin =&gt; _ =&gt; _
113 /// .Executes(() =&gt;
114 /// {
115 /// this.PrepareRuntimeDependencies(
116 /// this.ScriptFolder(),
117 /// [ new() {Path = (RelativePath)&quot;Win64&quot; } ],
118 /// determinePlatform: f =&gt; UnrealPlatform.Win64,
119 /// customManifest: new()
120 /// {
121 /// Copy = [
122 /// new() {Directory = &quot;tools&quot;},
123 /// new() {
124 /// Directory = &quot;sdk/windows-desktop/amd64/release/bin&quot;,
125 /// As = &quot;Win64&quot;
126 /// },
127 /// ]
128 /// }
129 /// );
130 /// });
131 /// }
132 /// </code>
133 /// </summary>
134 /// <remarks>
135 /// This will create a "copy" of the binaries subfolder in Plugin sources, so Fab
136 /// compatibility can be ensured. This "copy" is just done with symlinks, not actual copies
137 /// of binaries. This wisdom has been bestowed upon us by the author of CefView
138 /// https://forums.unrealengine.com/t/copy-3rd-party-dlls-to-binaries-folder-does-not-work-for-engine-plugin-how-to-solve/738255/6
139 /// </remarks>
140 /// <param name="self"></param>
141 /// <param name="sourceFolder">
142 /// Required. The root folder of the third-party library. Ideally the same folder as the
143 /// external module representing the library for Unreal.
144 /// </param>
145 /// <param name="runtimeLibraryPaths">
146 /// Required. A a set of locations in runtime, relative to the destination folder serving as
147 /// runtime library paths (ideally one for each supported platforms). This may be used as
148 /// base folders to load DLL's from.
149 /// </param>
150 /// <param name="determineConfig">
151 /// Optional. Determin a configuration (either debug or release) from a given runtime
152 /// dependency file. If the file is needed in all configurations return
153 /// `RuntimeDependencyConfig.All`. This predicate is also applied to DLL's, there are no
154 /// assumptions about them, solely based on their file paths. By default every file is
155 /// assumed for all configurations.
156 /// </param>
157 /// <param name="determinePlatform">
158 /// Optional. Determin a target platform from a given runtime dependency file. If the file
159 /// is needed on all platforms return `UnrealPlatform.Independent`. This predicate is also
160 /// applied to DLL's, there are no assumptions about them, solely based on their file paths.
161 /// By default every file is assumed for all platforms.
162 /// </param>
163 /// <param name="moduleName">
164 /// Optional. By default derived from the folder name provided with `sourceFolder`
165 /// </param>
166 /// <param name="pluginFolder">
167 /// Optional. By default it's the owning plugin of the `sourceFolder`
168 /// </param>
169 /// <param name="customManifest">
170 /// Optional. Provide a folder composition manifest if the target file-folder structure is
171 /// different than the input one. Default is empty (just copy folder structure as-is)
172 /// </param>
173 /// <param name="manifestFilePattern">
174 /// Optional. The file name of an optional folder composition manifest which may reside in
175 /// `sourceFolder`. Default is "RuntimeDeps.y*ml"
176 /// </param>
177 /// <param name="binariesSubfolder">
178 /// Optional. The subfolder name in the plugin Binaries where everything else is copied into.
179 /// Default is "ThirdParty".
180 /// </param>
181 /// <param name="moduleRuleOutput">
182 /// Optional. A custom folder output for the generated module rule file.
183 /// Default is `sourceFolder`.
184 /// </param>
185 /// <param name="setFilterPlugin">
186 /// Enqueue referred files and their destination locations in the owning plugin instance.
187 /// Default is true.
188 /// </param>
189 /// <param name="pretend">
190 /// Optional. If true only list affected files, but do not modify files or generate a module
191 /// rule. Default is false.
192 /// </param>
193 public static IEnumerable<ImportedItem> PrepareRuntimeDependencies(
194 this UnrealBuild self,
195 AbsolutePath sourceFolder,
196 IEnumerable<RuntimeLibraryPath> runtimeLibraryPaths,
197 Func<AbsolutePath, RuntimeDependencyConfig>? determineConfig = null,
198 Func<AbsolutePath, UnrealPlatform>? determinePlatform = null,
199 string? moduleName = null,
200 AbsolutePath? pluginFolder = null,
201 ExportManifest? customManifest = null,
202 string manifestFilePattern = "RuntimeDeps.y*ml",
203 string binariesSubfolder = "ThirdParty",
204 AbsolutePath? moduleRuleOutput = null,
205 bool setFilterPlugin = true,
206 bool pretend = false
207 ) {
208 pluginFolder ??= sourceFolder.GetOwningPlugin()!.Parent;
209 determineConfig ??= p => RuntimeDependencyConfig.All;
210 determinePlatform ??= p => UnrealPlatform.Independent;
211 moduleName ??= sourceFolder.Name;
212 moduleRuleOutput ??= sourceFolder;
213
214 Log.Information(
215 """
216 Preparing runtime dependencies:
217 For plugin: {0}
218 folder: {1}
219 For module: {2}
220 folder: {3}
221 Result .build.cs: {4}
222 """,
223 pluginFolder.Name, pluginFolder,
224 moduleName, sourceFolder,
225 moduleRuleOutput
226 );
227
228 var dstFolder = pluginFolder / "Binaries" / binariesSubfolder;
229 var options = new ImportOptions(
230 Pretend: pretend
231 );
232 var deps = (
233 customManifest == null
234 ? self.ImportFolder((sourceFolder, dstFolder, manifestFilePattern), options)
235 : self.ImportFolder((sourceFolder, dstFolder, customManifest, manifestFilePattern), options)
236 ).WithFilesExpanded().ToList();
237
238 if (setFilterPlugin)
239 {
240 var thisPlugin = UnrealPlugin.Get(pluginFolder);
241 thisPlugin.AddExplicitPluginFiles(deps.Select(d => d.To));
242
243 if (!pretend)
244 {
245 Log.Debug("Generating FilterPlugin.ini for {0}", thisPlugin.Name);
246 thisPlugin.GenerateFilterPluginIni(self);
247 }
248 }
249
250 if (!pretend)
251 {
252 var binaryPlumbingTemporary = pluginFolder
253 / "Source"
254 / "ThirdParty"
255 / "BinaryPlumbing"
256 / moduleName
257 / binariesSubfolder
258 / sourceFolder.Name
259 ;
260 Log.Debug("For marketplace compatibility these binaries will be linked within the Source folder as well.");
261 Log.Debug("Plugin distribution will ship these binaries in the Source folder, not in Binaries folder for Fab compatibility.");
262 Log.Information("Binary-plumbing folder: {0}", binaryPlumbingTemporary);
263 self.ImportFolder((dstFolder / sourceFolder.Name, binaryPlumbingTemporary), new(UseSubfolder: false));
264 }
265
266 var dllDeps = deps
267 .Where(d => UnrealPlatform.Platforms.Any(
268 p => d.From.Extension.EqualsOrdinalIgnoreCase("." + p.DllExtension)
269 ));
270
271 Log.Debug("DLL's handled: {0}", dllDeps.Select(d => d.To.Name));
272
273 var model = new RuntimeDependencies
274 {
275 ModuleName = moduleName,
276 Files = [.. deps.Select(d => new RuntimeDependency
277 {
278 Value = pluginFolder.GetRelativePathTo(d.To).ToUnixRelativePath(),
279 Origin = pluginFolder.GetRelativePathTo(d.From).ToUnixRelativePath(),
280 Config = determineConfig(d.From),
281 Platform = determinePlatform(d.From)
282 })],
283
284 Dlls = [.. dllDeps.Select(d => new RuntimeDependency
285 {
286 Value = d.To.Name,
287 Config = determineConfig(d.From),
288 Platform = determinePlatform(d.From)
289 })],
290
291 RuntimeLibraryPath = [.. runtimeLibraryPaths.Select(p => new RuntimeDependency
292 {
293 Value = $"Binaries/{binariesSubfolder}/{p.Path.ToUnixRelativePath()}",
294 Config = p.Config,
295 Platform = p.Platform
296 })]
297 };
298
299 if (!pretend)
300 new AutoRuntimeDependencyGenerator().Generate(self.TemplatesPath, moduleRuleOutput, model);
301
302 return deps;
303 }
304}
static void RenderFolder(AbsolutePath templateRoot, AbsolutePath destinationFolder, object model, AbsolutePath? currentFolder=null)
Render scriban templated scaffoldings and files to destination folder.
static IEnumerable< ImportedItem > PrepareRuntimeDependencies(this UnrealBuild self, AbsolutePath sourceFolder, IEnumerable< RuntimeLibraryPath > runtimeLibraryPaths, Func< AbsolutePath, RuntimeDependencyConfig >? determineConfig=null, Func< AbsolutePath, UnrealPlatform >? determinePlatform=null, string? moduleName=null, AbsolutePath? pluginFolder=null, ExportManifest? customManifest=null, string manifestFilePattern="RuntimeDeps.y*ml", string binariesSubfolder="ThirdParty", AbsolutePath? moduleRuleOutput=null, bool setFilterPlugin=true, bool pretend=false)
A class encapsulating information and tasks around one Unreal plugin.
void AddExplicitPluginFiles(IEnumerable< AbsolutePath > files)
Add explicit plugin files to be listed later in FilterPlugin.ini.
static UnrealPlugin Get(AbsolutePath from)
The main build class Unreal projects using Nuke.Unreal should inherit from. This class contains all b...
High level representation of common platforms supported by Unreal Engine (NDA ones excluded) and extr...
static readonly UnrealPlatform Independent
Meta-platform indicating that its context of operation is platform-independent or has (should have) t...
static readonly List< UnrealPlatform > Platforms
List of all "real" platforms.