// 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;
}
}
}