// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Diagnostics; using System.Linq; using EpicGames.Core; namespace UnrealBuildTool { /// /// The type of host that can load a module /// public enum ModuleHostType { /// /// /// Default, /// /// Any target using the UE4 runtime /// Runtime, /// /// Any target except for commandlet /// RuntimeNoCommandlet, /// /// Any target or program /// RuntimeAndProgram, /// /// Loaded only in cooked builds /// CookedOnly, /// /// Loaded only in uncooked builds /// UncookedOnly, /// /// Loaded only when the engine has support for developer tools enabled /// Developer, /// /// Loads on any targets where bBuildDeveloperTools is enabled /// DeveloperTool, /// /// Loaded only by the editor /// Editor, /// /// Loaded only by the editor, except when running commandlets /// EditorNoCommandlet, /// /// Loaded by the editor or program targets /// EditorAndProgram, /// /// Loaded only by programs /// Program, /// /// Loaded only by servers /// ServerOnly, /// /// Loaded only by clients, and commandlets, and editor.... /// ClientOnly, /// /// Loaded only by clients and editor (editor can run PIE which is kinda a commandlet) /// ClientOnlyNoCommandlet, } /// /// Indicates when the engine should attempt to load this module /// public enum ModuleLoadingPhase { /// /// Loaded at the default loading point during startup (during engine init, after game modules are loaded.) /// Default, /// /// Right after the default phase /// PostDefault, /// /// Right before the default phase /// PreDefault, /// /// Loaded as soon as plugins can possibly be loaded (need GConfig) /// EarliestPossible, /// /// Loaded before the engine is fully initialized, immediately after the config system has been initialized. Necessary only for very low-level hooks /// PostConfigInit, /// /// The first screen to be rendered after system splash screen /// PostSplashScreen, /// /// After PostConfigInit and before coreUobject initialized. used for early boot loading screens before the uobjects are initialized /// PreEarlyLoadingScreen, /// /// Loaded before the engine is fully initialized for modules that need to hook into the loading screen before it triggers /// PreLoadingScreen, /// /// After the engine has been initialized /// PostEngineInit, /// /// Do not automatically load this module /// None, } /// /// Class containing information about a code module /// [DebuggerDisplay("Name={Name}")] public class ModuleDescriptor { /// /// Name of this module /// public readonly string Name; /// /// Usage type of module /// public ModuleHostType Type; /// /// When should the module be loaded during the startup sequence? This is sort of an advanced setting. /// public ModuleLoadingPhase LoadingPhase = ModuleLoadingPhase.Default; /// /// List of allowed platforms /// public List? WhitelistPlatforms; /// /// List of disallowed platforms /// public List? BlacklistPlatforms; /// /// List of allowed targets /// public TargetType[]? WhitelistTargets; /// /// List of disallowed targets /// public TargetType[]? BlacklistTargets; /// /// List of allowed target configurations /// public UnrealTargetConfiguration[]? WhitelistTargetConfigurations; /// /// List of disallowed target configurations /// public UnrealTargetConfiguration[]? BlacklistTargetConfigurations; /// /// List of allowed programs /// public string[]? WhitelistPrograms; /// /// List of disallowed programs /// public string[]? BlacklistPrograms; /// /// List of additional dependencies for building this module. /// public string[]? AdditionalDependencies; /// /// Constructor /// /// Name of the module /// Type of target that can host this module public ModuleDescriptor(string InName, ModuleHostType InType) { Name = InName; Type = InType; } /// /// Constructs a ModuleDescriptor from a Json object /// /// /// The new module descriptor public static ModuleDescriptor FromJsonObject(JsonObject InObject) { ModuleDescriptor Module = new ModuleDescriptor(InObject.GetStringField("Name"), InObject.GetEnumField("Type")); ModuleLoadingPhase LoadingPhase; if (InObject.TryGetEnumField("LoadingPhase", out LoadingPhase)) { Module.LoadingPhase = LoadingPhase; } try { string[] WhitelistPlatforms; // it's important we default to null, and don't have an empty whitelist by default, because that will indicate that no // platforms should be compiled (see IsCompiledInConfiguration(), it only checks for null, not length) Module.WhitelistPlatforms = null; if (InObject.TryGetStringArrayField("WhitelistPlatforms", out WhitelistPlatforms)) { Module.WhitelistPlatforms = new List(); foreach (string TargetPlatformName in WhitelistPlatforms) { UnrealTargetPlatform Platform; if (UnrealTargetPlatform.TryParse(TargetPlatformName, out Platform)) { Module.WhitelistPlatforms.Add(Platform); } else { Log.TraceWarning("Unknown platform {0} while parsing whitelist for module descriptor {1}", TargetPlatformName, Module.Name); } } } string[] BlacklistPlatforms; if (InObject.TryGetStringArrayField("BlacklistPlatforms", out BlacklistPlatforms)) { Module.BlacklistPlatforms = new List(); foreach (string TargetPlatformName in BlacklistPlatforms) { UnrealTargetPlatform Platform; if (UnrealTargetPlatform.TryParse(TargetPlatformName, out Platform)) { Module.BlacklistPlatforms.Add(Platform); } else { Log.TraceWarning("Unknown platform {0} while parsing blacklist for module descriptor {1}", TargetPlatformName, Module.Name); } } } } catch (BuildException Ex) { ExceptionUtils.AddContext(Ex, "while parsing module descriptor '{0}'", Module.Name); throw; } TargetType[] WhitelistTargets; if (InObject.TryGetEnumArrayField("WhitelistTargets", out WhitelistTargets)) { Module.WhitelistTargets = WhitelistTargets; } TargetType[] BlacklistTargets; if (InObject.TryGetEnumArrayField("BlacklistTargets", out BlacklistTargets)) { Module.BlacklistTargets = BlacklistTargets; } UnrealTargetConfiguration[] WhitelistTargetConfigurations; if (InObject.TryGetEnumArrayField("WhitelistTargetConfigurations", out WhitelistTargetConfigurations)) { Module.WhitelistTargetConfigurations = WhitelistTargetConfigurations; } UnrealTargetConfiguration[] BlacklistTargetConfigurations; if (InObject.TryGetEnumArrayField("BlacklistTargetConfigurations", out BlacklistTargetConfigurations)) { Module.BlacklistTargetConfigurations = BlacklistTargetConfigurations; } string[] WhitelistPrograms; if (InObject.TryGetStringArrayField("WhitelistPrograms", out WhitelistPrograms)) { Module.WhitelistPrograms = WhitelistPrograms; } string[] BlacklistPrograms; if (InObject.TryGetStringArrayField("BlacklistPrograms", out BlacklistPrograms)) { Module.BlacklistPrograms = BlacklistPrograms; } string[] AdditionalDependencies; if (InObject.TryGetStringArrayField("AdditionalDependencies", out AdditionalDependencies)) { Module.AdditionalDependencies = AdditionalDependencies; } return Module; } /// /// Write this module to a JsonWriter /// /// Writer to output to void Write(JsonWriter Writer) { Writer.WriteObjectStart(); Writer.WriteValue("Name", Name); Writer.WriteValue("Type", Type.ToString()); Writer.WriteValue("LoadingPhase", LoadingPhase.ToString()); // important note: we don't check the length of the whitelist platforms, because if an unknown platform was read in, but was not valid, the // list will exist but be empty. We don't want to remove the whitelist completely, because that would allow this module on all platforms, // which will not be the desired effect if (WhitelistPlatforms != null) { Writer.WriteArrayStart("WhitelistPlatforms"); foreach (UnrealTargetPlatform WhitelistPlatform in WhitelistPlatforms) { Writer.WriteValue(WhitelistPlatform.ToString()); } Writer.WriteArrayEnd(); } if (BlacklistPlatforms != null && BlacklistPlatforms.Count > 0) { Writer.WriteArrayStart("BlacklistPlatforms"); foreach (UnrealTargetPlatform BlacklistPlatform in BlacklistPlatforms) { Writer.WriteValue(BlacklistPlatform.ToString()); } Writer.WriteArrayEnd(); } if (WhitelistTargets != null && WhitelistTargets.Length > 0) { Writer.WriteArrayStart("WhitelistTargets"); foreach (TargetType WhitelistTarget in WhitelistTargets) { Writer.WriteValue(WhitelistTarget.ToString()); } Writer.WriteArrayEnd(); } if (BlacklistTargets != null && BlacklistTargets.Length > 0) { Writer.WriteArrayStart("BlacklistTargets"); foreach (TargetType BlacklistTarget in BlacklistTargets) { Writer.WriteValue(BlacklistTarget.ToString()); } Writer.WriteArrayEnd(); } if (WhitelistTargetConfigurations != null && WhitelistTargetConfigurations.Length > 0) { Writer.WriteArrayStart("WhitelistTargetConfigurations"); foreach (UnrealTargetConfiguration WhitelistTargetConfiguration in WhitelistTargetConfigurations) { Writer.WriteValue(WhitelistTargetConfiguration.ToString()); } Writer.WriteArrayEnd(); } if (BlacklistTargetConfigurations != null && BlacklistTargetConfigurations.Length > 0) { Writer.WriteArrayStart("BlacklistTargetConfigurations"); foreach (UnrealTargetConfiguration BlacklistTargetConfiguration in BlacklistTargetConfigurations) { Writer.WriteValue(BlacklistTargetConfiguration.ToString()); } Writer.WriteArrayEnd(); } if(WhitelistPrograms != null && WhitelistPrograms.Length > 0) { Writer.WriteStringArrayField("WhitelistPrograms", WhitelistPrograms); } if(BlacklistPrograms != null && BlacklistPrograms.Length > 0) { Writer.WriteStringArrayField("BlacklistPrograms", BlacklistPrograms); } if (AdditionalDependencies != null && AdditionalDependencies.Length > 0) { Writer.WriteArrayStart("AdditionalDependencies"); foreach (string AdditionalDependency in AdditionalDependencies) { Writer.WriteValue(AdditionalDependency); } Writer.WriteArrayEnd(); } Writer.WriteObjectEnd(); } /// /// Write an array of module descriptors /// /// The Json writer to output to /// Name of the array /// Array of modules public static void WriteArray(JsonWriter Writer, string Name, ModuleDescriptor[]? Modules) { if (Modules != null && Modules.Length > 0) { Writer.WriteArrayStart(Name); foreach (ModuleDescriptor Module in Modules) { Module.Write(Writer); } Writer.WriteArrayEnd(); } } /// /// Produces any warnings and errors for the module settings /// /// File containing the module declaration public void Validate(FileReference File) { if(Type == ModuleHostType.Developer) { Log.TraceWarningOnce("The 'Developer' module type has been deprecated in 4.24. Use 'DeveloperTool' for modules that can be loaded by game/client/server targets in non-shipping configurations, or 'UncookedOnly' for modules that should only be loaded by uncooked editor and program targets (eg. modules containing blueprint nodes)"); Log.TraceWarningOnce(File, "The 'Developer' module type has been deprecated in 4.24."); } } /// /// Determines whether the given plugin module is part of the current build. /// /// The platform being compiled for /// The target configuration being compiled for /// Name of the target being built /// The type of the target being compiled /// Whether the configuration includes developer tools (typically UEBuildConfiguration.bBuildDeveloperTools for UBT callers) /// Whether the configuration requires cooked content (typically UEBuildConfiguration.bBuildRequiresCookedData for UBT callers) public bool IsCompiledInConfiguration(UnrealTargetPlatform Platform, UnrealTargetConfiguration Configuration, string TargetName, TargetType TargetType, bool bBuildDeveloperTools, bool bBuildRequiresCookedData) { // Check the platform is whitelisted // important note: we don't check the length of the whitelist platforms, because if an unknown platform was read in, but was not valid, the // list will exist but be empty. In this case, we need to disallow all platforms from building, otherwise, build errors will occur when // it starts compiling for _all_ platforms if (WhitelistPlatforms != null && !WhitelistPlatforms.Contains(Platform)) { return false; } // Check the platform is not blacklisted if (BlacklistPlatforms != null && BlacklistPlatforms.Contains(Platform)) { return false; } // Check the target is whitelisted if (WhitelistTargets != null && WhitelistTargets.Length > 0 && !WhitelistTargets.Contains(TargetType)) { return false; } // Check the target is not blacklisted if (BlacklistTargets != null && BlacklistTargets.Contains(TargetType)) { return false; } // Check the target configuration is whitelisted if (WhitelistTargetConfigurations != null && WhitelistTargetConfigurations.Length > 0 && !WhitelistTargetConfigurations.Contains(Configuration)) { return false; } // Check the target configuration is not blacklisted if (BlacklistTargetConfigurations != null && BlacklistTargetConfigurations.Contains(Configuration)) { return false; } // Special checks just for programs if(TargetType == TargetType.Program) { // Check the program name is whitelisted. Note that this behavior is slightly different to other whitelist/blacklist checks; we will whitelist a module of any type if it's explicitly allowed for this program. if(WhitelistPrograms != null && WhitelistPrograms.Length > 0) { return WhitelistPrograms.Contains(TargetName); } // Check the program name is not blacklisted if(BlacklistPrograms != null && BlacklistPrograms.Contains(TargetName)) { return false; } } // Check the module is compatible with this target. switch (Type) { case ModuleHostType.Runtime: case ModuleHostType.RuntimeNoCommandlet: return TargetType != TargetType.Program; case ModuleHostType.RuntimeAndProgram: return true; case ModuleHostType.CookedOnly: return bBuildRequiresCookedData; case ModuleHostType.UncookedOnly: return !bBuildRequiresCookedData; case ModuleHostType.Developer: return TargetType == TargetType.Editor || TargetType == TargetType.Program; case ModuleHostType.DeveloperTool: return bBuildDeveloperTools; case ModuleHostType.Editor: case ModuleHostType.EditorNoCommandlet: return TargetType == TargetType.Editor; case ModuleHostType.EditorAndProgram: return TargetType == TargetType.Editor || TargetType == TargetType.Program; case ModuleHostType.Program: return TargetType == TargetType.Program; case ModuleHostType.ServerOnly: return TargetType != TargetType.Program && TargetType != TargetType.Client; case ModuleHostType.ClientOnly: case ModuleHostType.ClientOnlyNoCommandlet: return TargetType != TargetType.Program && TargetType != TargetType.Server; } return false; } } }