// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Diagnostics; using System.Runtime.Serialization; namespace UnrealBuildTool { /// /// Information about a target, passed along when creating a module descriptor /// [Serializable] public class TargetInfo : ISerializable { /// Target platform public readonly UnrealTargetPlatform Platform; /// Target architecture (or empty string if not needed) public readonly string Architecture; /// Target build configuration public readonly UnrealTargetConfiguration Configuration; /// Target type (if known) public readonly TargetRules.TargetType? Type; /// Whether the target is monolithic (if known) public readonly bool? bIsMonolithic; public TargetInfo(SerializationInfo Info, StreamingContext Context) { Platform = (UnrealTargetPlatform)Info.GetInt32("pl"); Architecture = Info.GetString("ar"); Configuration = (UnrealTargetConfiguration)Info.GetInt32("co"); if (Info.GetBoolean("t?")) { Type = (TargetRules.TargetType)Info.GetInt32("tt"); } if (Info.GetBoolean("m?")) { bIsMonolithic = Info.GetBoolean("mo"); } } public void GetObjectData(SerializationInfo Info, StreamingContext Context) { Info.AddValue("pl", (int)Platform); Info.AddValue("ar", Architecture); Info.AddValue("co", (int)Configuration); Info.AddValue("t?", Type.HasValue); if (Type.HasValue) { Info.AddValue("tt", (int)Type.Value); } Info.AddValue("m?", bIsMonolithic.HasValue); if (bIsMonolithic.HasValue) { Info.AddValue("mo", bIsMonolithic); } } /// /// Constructs a TargetInfo /// /// Target platform /// Target build configuration public TargetInfo(UnrealTargetPlatform InitPlatform, UnrealTargetConfiguration InitConfiguration) { Platform = InitPlatform; Configuration = InitConfiguration; // get the platform's architecture var BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform); Architecture = BuildPlatform.GetActiveArchitecture(); } /// /// Constructs a TargetInfo /// /// Target platform /// Target build configuration /// Target type /// Whether the target is monolithic public TargetInfo(UnrealTargetPlatform InitPlatform, UnrealTargetConfiguration InitConfiguration, TargetRules.TargetType InitType, bool bInitIsMonolithic) : this(InitPlatform, InitConfiguration) { Type = InitType; bIsMonolithic = bInitIsMonolithic; } /// /// True if the target type is a cooked game. /// public bool IsCooked { get { if (!Type.HasValue) { throw new BuildException("Trying to access TargetInfo.IsCooked when TargetInfo.Type is not set. Make sure IsCooked is used only in ModuleRules."); } return Type == TargetRules.TargetType.Client || Type == TargetRules.TargetType.Game || Type == TargetRules.TargetType.Server; } } /// /// True if the target type is a monolithic binary /// public bool IsMonolithic { get { if (!bIsMonolithic.HasValue) { throw new BuildException("Trying to access TargetInfo.IsMonolithic when bIsMonolithic is not set. Make sure IsMonolithic is used only in ModuleRules."); } return bIsMonolithic.Value; } } } /// /// ModuleRules is a data structure that contains the rules for defining a module /// public class ModuleRules { /// Type of module public enum ModuleType { /// C++ CPlusPlus, /// CLR module (mixed C++ and C++/CLI) CPlusPlusCLR, /// External (third-party) External, } /// Code optimization settings public enum CodeOptimization { /// Code should never be optimized if possible. Never, /// Code should only be optimized in non-debug builds (not in Debug). InNonDebugBuilds, /// Code should only be optimized in shipping builds (not in Debug, DebugGame, Development) InShippingBuildsOnly, /// Code should always be optimized if possible. Always, /// Default: 'InNonDebugBuilds' for game modules, 'Always' otherwise. Default, } /// Type of module public ModuleType Type = ModuleType.CPlusPlus; /// Subfolder of Binaries/PLATFORM folder to put this module in when building DLLs /// This should only be used by modules that are found via searching like the /// TargetPlatform or ShaderFormat modules. /// If FindModules is not used to track them down, the modules will not be found. public string BinariesSubFolder = ""; /// When this module's code should be optimized. public CodeOptimization OptimizeCode = CodeOptimization.Default; /// Header file name for a shared PCH provided by this module. Must be a valid relative path to a public C++ header file. /// This should only be set for header files that are included by a significant number of other C++ modules. public string SharedPCHHeaderFile = String.Empty; public enum PCHUsageMode { /// Default: Engine modules use shared PCHs, game modules do not Default, /// Never use shared PCHs. Always generate a unique PCH for this module if appropriate NoSharedPCHs, /// Shared PCHs are OK! UseSharedPCHs } /// Precompiled header usage for this module public PCHUsageMode PCHUsage = PCHUsageMode.Default; /// /// Use run time type information /// public bool bUseRTTI = false; /// /// Use AVX instructions /// public bool bUseAVX = false; /// /// Enable buffer security checks. This should usually be enabled as it prevents severe security risks. /// public bool bEnableBufferSecurityChecks = true; /// /// Enable exception handling /// public bool bEnableExceptions = false; /// /// Enable warnings for shadowed variables /// public bool bEnableShadowVariableWarnings = true; /// /// If true and unity builds are enabled, this module will build without unity. /// public bool bFasterWithoutUnity = false; /// /// The number of source files in this module before unity build will be activated for that module. If set to /// anything besides -1, will override the default setting which is controlled by MinGameModuleSourceFilesForUnityBuild /// public int MinSourceFilesForUnityBuildOverride = 0; /// /// Overrides BuildConfiguration.MinFilesUsingPrecompiledHeader if non-zero. /// public int MinFilesUsingPrecompiledHeaderOverride = 0; /// /// Module uses a #import so must be built locally when compiling with SN-DBS /// public bool bBuildLocallyWithSNDBS = false; /// /// List of modules with header files that our module's public headers needs access to, but we don't need to "import" or link against. /// public List PublicIncludePathModuleNames = new List(); /// /// List of public dependency module names. These are modules that are required by our public source files. /// public List PublicDependencyModuleNames = new List(); /// /// List of modules with header files that our module's private code files needs access to, but we don't need to "import" or link against. /// public List PrivateIncludePathModuleNames = new List(); /// /// List of private dependency module names. These are modules that our private code depends on but nothing in our public /// include files depend on. /// public List PrivateDependencyModuleNames = new List(); /// /// List of module dependencies that should be treated as circular references. This modules must have already been added to /// either the public or private dependent module list. /// public List CircularlyReferencedDependentModules = new List(); /// /// System include paths. These are public stable header file directories that are not checked when resolving header dependencies. /// public List PublicSystemIncludePaths = new List(); /// /// List of all paths to include files that are exposed to other modules /// public List PublicIncludePaths = new List(); /// /// List of all paths to this module's internal include files, not exposed to other modules /// public List PrivateIncludePaths = new List(); /// /// List of library paths - typically used for External (third party) modules /// public List PublicLibraryPaths = new List(); /// /// List of addition libraries - typically used for External (third party) modules /// public List PublicAdditionalLibraries = new List(); /// // List of frameworks /// public List PublicFrameworks = new List(); /// // List of weak frameworks (for OS version transitions) /// public List PublicWeakFrameworks = new List(); /// /// List of addition frameworks - typically used for External (third party) modules on Mac and iOS /// public List PublicAdditionalFrameworks = new List(); /// /// List of addition resources that should be copied to the app bundle for Mac or iOS /// public List AdditionalBundleResources = new List(); /// /// For builds that execute on a remote machine (e.g. iOS), this list contains additional files that /// need to be copied over in order for the app to link successfully. Source/header files and PCHs are /// automatically copied. Usually this is simply a list of precompiled third party library dependencies. /// public List PublicAdditionalShadowFiles = new List(); /// /// List of delay load DLLs - typically used for External (third party) modules /// public List PublicDelayLoadDLLs = new List(); /// /// Additional compiler definitions for this module /// public List Definitions = new List(); /// /// CLR modules only: The assemblies referenced by the module's private implementation. /// public List PrivateAssemblyReferences = new List(); /// /// Addition modules this module may require at run-time /// public List DynamicallyLoadedModuleNames = new List(); /// /// Extra modules this module may require at run time, that are on behalf of another platform (i.e. shader formats and the like) /// public List PlatformSpecificDynamicallyLoadedModuleNames = new List(); /// /// List of files which this module depends on at runtime. These files will be staged along with the target. /// public List RuntimeDependencies = new List(); /// /// Property for the directory containing this module. Useful for adding paths to third party dependencies. /// public string ModuleDirectory { get { return Path.GetDirectoryName(RulesCompiler.GetFileNameFromType(GetType())); } } /// /// Add the given ThirdParty modules as static private dependencies /// Statically linked to this module, meaning they utilize exports from the other module /// Private, meaning the include paths for the included modules will not be exposed when giving this modules include paths /// NOTE: There is no AddThirdPartyPublicStaticDependencies function. /// /// The names of the modules to add public void AddThirdPartyPrivateStaticDependencies(TargetInfo Target, params string[] InModuleNames) { if (UnrealBuildTool.RunningRocket() == false || Target.Type == TargetRules.TargetType.Game) { PrivateDependencyModuleNames.AddRange(InModuleNames); } } /// /// Add the given ThirdParty modules as dynamic private dependencies /// Dynamically linked to this module, meaning they do not utilize exports from the other module /// Private, meaning the include paths for the included modules will not be exposed when giving this modules include paths /// NOTE: There is no AddThirdPartyPublicDynamicDependencies function. /// /// The names of the modules to add public void AddThirdPartyPrivateDynamicDependencies(TargetInfo Target, params string[] InModuleNames) { if (UnrealBuildTool.RunningRocket() == false || Target.Type == TargetRules.TargetType.Game) { PrivateIncludePathModuleNames.AddRange(InModuleNames); DynamicallyLoadedModuleNames.AddRange(InModuleNames); } } /// /// Setup this module for PhysX/APEX support (based on the settings in UEBuildConfiguration) /// public void SetupModulePhysXAPEXSupport(TargetInfo Target) { if (UEBuildConfiguration.bCompilePhysX == true) { AddThirdPartyPrivateStaticDependencies(Target, "PhysX"); Definitions.Add("WITH_PHYSX=1"); if (UEBuildConfiguration.bCompileAPEX == true) { AddThirdPartyPrivateStaticDependencies(Target, "APEX"); Definitions.Add("WITH_APEX=1"); } else { Definitions.Add("WITH_APEX=0"); } } else { Definitions.Add("WITH_PHYSX=0"); Definitions.Add("WITH_APEX=0"); } if (UEBuildConfiguration.bRuntimePhysicsCooking == true) { Definitions.Add("WITH_RUNTIME_PHYSICS_COOKING"); } } /// /// Setup this module for Box2D support (based on the settings in UEBuildConfiguration) /// public void SetupModuleBox2DSupport(TargetInfo Target) { //@TODO: This need to be kept in sync with RulesCompiler.cs for now bool bSupported = false; if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32)) { bSupported = true; } bSupported = bSupported && UEBuildConfiguration.bCompileBox2D; if (bSupported) { AddThirdPartyPrivateStaticDependencies(Target, "Box2D"); } // Box2D included define (required because pointer types may be in public exported structures) Definitions.Add(string.Format("WITH_BOX2D={0}", bSupported ? 1 : 0)); } /// /// Redistribution override flag for this module. /// public bool? IsRedistributableOverride { get; set; } } /// /// TargetRules is a data structure that contains the rules for defining a target (application/executable) /// public abstract class TargetRules { /// Type of target [Serializable] public enum TargetType { /// Cooked monolithic game executable (GameName.exe). Also used for a game-agnostic engine executable (UE4Game.exe or RocketGame.exe) Game, /// Uncooked modular editor executable and DLLs (UE4Editor.exe, UE4Editor*.dll, GameName*.dll) Editor, /// Cooked monolithic game client executable (GameNameClient.exe, but no server code) Client, /// Cooked monolithic game server executable (GameNameServer.exe, but no client code) Server, /// Program (standalone program, e.g. ShaderCompileWorker.exe, can be modular or monolithic depending on the program) Program, } /// /// The name of the game, this is set up by the rules compiler after it compiles and constructs this /// public string TargetName = null; /// /// Whether the target uses Steam (todo: substitute with more generic functionality) /// public bool bUsesSteam; /// /// Whether the target uses CEF3 /// public bool bUsesCEF3; /// /// Whether the project uses visual Slate UI (as opposed to the low level windowing/messaging which is always used) /// public bool bUsesSlate = true; /// /// Hack for legacy game styling isses. No new project should ever set this to true /// Whether the project uses the Slate editor style in game. /// public bool bUsesSlateEditorStyle = false; /// /// Forces linking against the static CRT. This is not supported across the engine due to the need for allocator implementations to be shared (for example), and TPS /// libraries to be consistent with each other, but can be used for utility programs. /// public bool bUseStaticCRT = false; /// /// Allow a target to specify a preferred sub-platform. /// Can be used to target a build using sub platform specifics. /// public string PreferredSubPlatform = String.Empty; /// /// By default we use the Release C++ Runtime (CRT), even when compiling Debug builds. This is because the Debug C++ /// Runtime isn't very useful when debugging Unreal Engine projects, and linking against the Debug CRT libraries forces /// our third party library dependencies to also be compiled using the Debug CRT (and often perform more slowly.) Often /// it can be inconvenient to require a separate copy of the debug versions of third party static libraries simply /// so that you can debug your program's code. /// public bool bDebugBuildsActuallyUseDebugCRT = false; /// /// Whether the output from this target can be publicly distributed, even if it has /// dependencies on modules that are not (i.e. CarefullyRedist, NotForLicensees, NoRedist). /// This should be used when you plan to release binaries but not source. /// public bool bOutputPubliclyDistributable = false; /// /// Specifies the configuration whose binaries do not require a "-Platform-Configuration" suffix. /// public UnrealTargetConfiguration UndecoratedConfiguration = UnrealTargetConfiguration.Development; /// /// Build all the plugins that we can find, even if they're not enabled. This is particularly useful for content-only projects, /// where you're building the UE4Editor target but running it with a game that enables a plugin. /// public bool bBuildAllPlugins = false; /// /// A list of additional plugins which need to be included in this target. This allows referencing non-optional plugin modules /// which cannot be disabled, and allows building against specific modules in program targets which do not fit the categories /// in ModuleHostType. /// public List AdditionalPlugins = new List(); /// /// Is the given type a 'game' type (Game/Editor/Server) wrt building? /// /// The target type of interest /// true if it *is* a 'game' type static public bool IsGameType(TargetType InType) { return ( (InType == TargetType.Game) || (InType == TargetType.Editor) || (InType == TargetType.Client) || (InType == TargetType.Server) ); } /// /// Is the given type a game? /// /// The target type of interest /// true if it *is* a game static public bool IsAGame(TargetType InType) { return ( (InType == TargetType.Game) || (InType == TargetType.Client) ); } /// /// Is the given type an 'editor' type with regard to building? /// /// The target type of interest /// true if it *is* an 'editor' type static public bool IsEditorType(TargetType InType) { return (InType == TargetType.Editor); } /// /// Type of target /// public TargetType Type = TargetType.Game; /// /// The name of this target's 'configuration' within the development IDE. No project may have more than one target with the same configuration name. /// If no configuration name is set, then it defaults to the TargetType name /// public string ConfigurationName { get { if (String.IsNullOrEmpty(ConfigurationNameVar)) { return Type.ToString(); } else { return ConfigurationNameVar; } } set { ConfigurationNameVar = value; } } private string ConfigurationNameVar = String.Empty; /// /// Allows a Program Target to specify it's own solution folder path /// public string SolutionDirectory = String.Empty; /// /// If true, the built target goes into the Engine/Binaries/ folder /// public bool bOutputToEngineBinaries = false; /// /// Sub folder where the built target goes: Engine/Binaries// /// public string ExeBinariesSubFolder = String.Empty; /// /// Whether this target should be compiled in monolithic mode /// /// The platform being built /// The configuration being built /// true if it should, false if not public virtual bool ShouldCompileMonolithic(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration) { // By default, only Editor types compile non-monolithic if (IsEditorType(Type) == false) { // You can build a modular game/program/server via passing '-modular' to UBT if (UnrealBuildTool.CommandLineContains("-modular") == false) { return true; } } if (UnrealBuildTool.CommandLineContains("-monolithic")) { return true; } return false; } /// /// Get the supported platforms for this target /// /// The list of platforms supported /// true if successful, false if not public virtual bool GetSupportedPlatforms(ref List OutPlatforms) { if (Type == TargetType.Program) { // By default, all programs are desktop only. return UnrealBuildTool.GetAllDesktopPlatforms(ref OutPlatforms, false); } else if (IsEditorType(Type)) { return UnrealBuildTool.GetAllEditorPlatforms(ref OutPlatforms, false); } else if (TargetRules.IsGameType(Type)) { // By default all games support all platforms return UnrealBuildTool.GetAllPlatforms(ref OutPlatforms, false); } return false; } public bool SupportsPlatform(UnrealTargetPlatform InPlatform) { List SupportedPlatforms = new List(); if (GetSupportedPlatforms(ref SupportedPlatforms) == true) { return SupportedPlatforms.Contains(InPlatform); } return false; } /// /// Get the supported configurations for this target /// /// The list of configurations supported /// true if successful, false if not public virtual bool GetSupportedConfigurations(ref List OutConfigurations, bool bIncludeTestAndShippingConfigs) { if (Type == TargetType.Program) { // By default, programs are Debug and Development only. OutConfigurations.Add(UnrealTargetConfiguration.Debug); OutConfigurations.Add(UnrealTargetConfiguration.Development); } else { // By default all games support all configurations foreach (UnrealTargetConfiguration Config in Enum.GetValues(typeof(UnrealTargetConfiguration))) { if (Config != UnrealTargetConfiguration.Unknown) { // Some configurations just don't make sense for the editor if (IsEditorType(Type) && (Config == UnrealTargetConfiguration.Shipping || Config == UnrealTargetConfiguration.Test)) { // We don't currently support a "shipping" editor config } else if (!bIncludeTestAndShippingConfigs && (Config == UnrealTargetConfiguration.Shipping || Config == UnrealTargetConfiguration.Test)) { // User doesn't want 'Test' or 'Shipping' configs in their project files } else { OutConfigurations.Add(Config); } } } } return (OutConfigurations.Count > 0) ? true : false; } /// /// Setup the binaries associated with this target. /// /// The target information - such as platform and configuration /// Output list of binaries to generated /// Output list of extra modules that this target could utilize public virtual void SetupBinaries( TargetInfo Target, ref List OutBuildBinaryConfigurations, ref List OutExtraModuleNames ) { } /// /// Setup the global environment for building this target /// IMPORTANT: Game targets will *not* have this function called if they use the shared build environment. /// See ShouldUseSharedBuildEnvironment(). /// /// The target information - such as platform and configuration /// Output link environment settings /// Output compile environment settings public virtual void SetupGlobalEnvironment( TargetInfo Target, ref LinkEnvironmentConfiguration OutLinkEnvironmentConfiguration, ref CPPEnvironmentConfiguration OutCPPEnvironmentConfiguration ) { } /// /// Allows a target to choose whether to use the shared build environment for a given configuration. Using /// the shared build environment allows binaries to be reused between targets, but prevents customizing the /// compile environment through SetupGlobalEnvironment(). /// /// Information about the target /// True if the target should use the shared build environment public virtual bool ShouldUseSharedBuildEnvironment(TargetInfo Target) { return UnrealBuildTool.RunningRocket() || (Target.Type != TargetType.Program && !Target.IsMonolithic); } /// /// Allows the target to specify modules which can be precompiled with the -Precompile/-UsePrecompiled arguments to UBT. /// All dependencies of the specified modules will be included. /// /// The target information, such as platform and configuration /// List which receives module names to precompile public virtual void GetModulesToPrecompile(TargetInfo Target, List ModuleNames) { } /// /// Return true if this target should always be built with the base editor. Usually programs like shadercompilerworker. /// /// true if this target should always be built with the base editor. public virtual bool GUBP_AlwaysBuildWithBaseEditor() { return false; } /// /// Return true if this target should always be built with the tools. Usually programs like unrealpak. /// If this is set to true, the program will get its own node /// /// true if this target should always be built with the base editor. [Obsolete] public virtual bool GUBP_AlwaysBuildWithTools(UnrealTargetPlatform InHostPlatform, out bool bInternalToolOnly, out bool SeparateNode) { bInternalToolOnly = false; SeparateNode = false; return false; } /// /// Return true if this target should always be built with the tools. Usually programs like unrealpak. /// If this is set to true, the program will get its own node /// /// true if this target should always be built with the base editor. public virtual bool GUBP_AlwaysBuildWithTools(UnrealTargetPlatform InHostPlatform, out bool bInternalToolOnly, out bool SeparateNode, out bool CrossCompile) { bInternalToolOnly = false; SeparateNode = false; CrossCompile = false; return false; } /// /// Return a list of platforms to build a tool for /// /// a list of platforms to build a tool for public virtual List GUBP_ToolPlatforms(UnrealTargetPlatform InHostPlatform) { return new List { InHostPlatform }; } /// /// Return a list of configs to build a tool for /// /// a list of configs to build a tool for public virtual List GUBP_ToolConfigs(UnrealTargetPlatform InHostPlatform) { return new List { UnrealTargetConfiguration.Development }; } /// /// Return true if target should include a NonUnity test /// /// true if this target should include a NonUnity test public virtual bool GUBP_IncludeNonUnityToolTest() { return false; } /// /// Return true if this target should use a platform specific pass /// /// true if this target should use a platform specific pass public virtual bool GUBP_NeedsPlatformSpecificDLLs() { return false; } /// ///Returns true if XP monolithics are required for a game /// /// true if this target needs to be compiled for Windows XP public virtual bool GUBP_BuildWindowsXPMonolithics() { return false; } /// /// Return a list of target platforms for the monolithic /// /// a list of target platforms for the monolithic public virtual List GUBP_GetPlatforms_MonolithicOnly(UnrealTargetPlatform HostPlatform) { var Result = new List { HostPlatform }; // hack to set up the templates without adding anything to their .targets.cs files if (!String.IsNullOrEmpty(TargetName) && TargetName.StartsWith("TP_")) { if (HostPlatform == UnrealTargetPlatform.Win64) { Result.Add(UnrealTargetPlatform.IOS); Result.Add(UnrealTargetPlatform.Android); } else if (HostPlatform == UnrealTargetPlatform.Mac) { Result.Add(UnrealTargetPlatform.IOS); } } return Result; } /// /// Return a list of target platforms for the monolithic without cook /// /// a list of target platforms for the monolithic without cook public virtual List GUBP_GetBuildOnlyPlatforms_MonolithicOnly(UnrealTargetPlatform HostPlatform) { var Result = new List { }; return Result; } /// /// Return a list of configs for target platforms for the monolithic /// /// a list of configs for a target platforms for the monolithic public virtual List GUBP_GetConfigs_MonolithicOnly(UnrealTargetPlatform HostPlatform, UnrealTargetPlatform Platform) { return new List { UnrealTargetConfiguration.Development }; } /// /// Return a list of configs which are precompiled for the given target platform /// /// a list of configs for a target platforms for the monolithic public virtual List GUBP_GetConfigsForPrecompiledBuilds_MonolithicOnly(UnrealTargetPlatform HostPlatform, UnrealTargetPlatform Platform) { return new List(); } /// /// Return a list of configs for target platforms for formal builds /// /// a list of configs for a target platforms for the monolithic [Obsolete] public virtual List GUBP_GetConfigsForFormalBuilds_MonolithicOnly(UnrealTargetPlatform HostPlatform, UnrealTargetPlatform Platform) { return new List(); } public class GUBPFormalBuild { public UnrealTargetPlatform TargetPlatform = UnrealTargetPlatform.Unknown; public UnrealTargetConfiguration TargetConfig = UnrealTargetConfiguration.Unknown; public bool bTest = false; public bool bBeforeTrigger = false; public GUBPFormalBuild(UnrealTargetPlatform InTargetPlatform, UnrealTargetConfiguration InTargetConfig, bool bInTest = false, bool bInBeforeTrigger = false) { TargetPlatform = InTargetPlatform; TargetConfig = InTargetConfig; bTest = bInTest; bBeforeTrigger = bInBeforeTrigger; } } /// /// Return a list of formal builds /// /// a list of formal builds public virtual List GUBP_GetConfigsForFormalBuilds_MonolithicOnly(UnrealTargetPlatform HostPlatform) { return new List(); } /// /// Return true if this target should be included in a promotion and indicate shared or not /// /// if this target should be included in a promotion. public class GUBPProjectOptions { public bool bIsPromotable = false; public bool bBuildAnyway = false; public bool bSeparateGamePromotion = false; public bool bTestWithShared = false; public bool bIsMassive = false; public bool bCustomWorkflowForPromotion = false; public bool bIsNonCode = false; public bool bPromoteEditorOnly = true; public string GroupName = null; } public virtual GUBPProjectOptions GUBP_IncludeProjectInPromotedBuild_EditorTypeOnly(UnrealTargetPlatform HostPlatform) { var Result = new GUBPProjectOptions(); // hack to set up the templates without adding anything to their .targets.cs files // tweaked to include FP_ folders too - which are temporary if (!String.IsNullOrEmpty(TargetName) && (TargetName.StartsWith("TP_") || TargetName.StartsWith("FP_"))) { Result.bTestWithShared = true; Result.GroupName = "Templates"; } return Result; } /// /// Return a list of the non-code projects to test /// /// a list of the non-code projects to build cook and test public virtual Dictionary> GUBP_NonCodeProjects_BaseEditorTypeOnly(UnrealTargetPlatform HostPlatform) { return new Dictionary>(); } /// /// Return a list of the non-code projects to make formal builds for /// /// a list of the non-code projects to build cook and test [Obsolete] public virtual Dictionary>> GUBP_NonCodeFormalBuilds_BaseEditorTypeOnly() { return new Dictionary>>(); } /// /// Return a list of the non-code projects to make formal builds for /// /// a list of the non-code projects to build cook and test public virtual Dictionary> GUBP_GetNonCodeFormalBuilds_BaseEditorTypeOnly() { return new Dictionary>(); } /// /// Return a list of "test name", "UAT command" pairs for testing the editor /// public virtual Dictionary GUBP_GetEditorTests_EditorTypeOnly(UnrealTargetPlatform HostPlatform) { var MacOption = HostPlatform == UnrealTargetPlatform.Mac ? " -Mac" : ""; var Result = new Dictionary(); Result.Add("EditorTest", "BuildCookRun -run -editortest -unattended -nullrhi -NoP4" + MacOption); Result.Add("GameTest", "BuildCookRun -run -unattended -nullrhi -NoP4" + MacOption); Result.Add("EditorAutomationTest", "BuildCookRun -run -editortest -RunAutomationTests -unattended -nullrhi -NoP4" + MacOption); Result.Add("GameAutomationTest", "BuildCookRun -run -RunAutomationTests -unattended -nullrhi -NoP4" + MacOption); return Result; } /// /// Allow the platform to setup emails for the GUBP for folks that care about node failures relating to this platform /// Obsolete. Included to avoid breaking existing projects. /// /// p4 root of the branch we are running [Obsolete] public virtual string GUBP_GetGameFailureEMails_EditorTypeOnly(string Branch) { return ""; } /// /// Allow the Game to set up emails for Promotable and Promotion /// Obsolete. Included to avoid breaking existing projects. /// [Obsolete] public virtual string GUBP_GetPromotionEMails_EditorTypeOnly(string Branch) { return ""; } /// /// Return a list of "test name", "UAT command" pairs for testing a monolithic /// public virtual Dictionary GUBP_GetGameTests_MonolithicOnly(UnrealTargetPlatform HostPlatform, UnrealTargetPlatform AltHostPlatform, UnrealTargetPlatform Platform) { var Result = new Dictionary(); if ((Platform == HostPlatform || Platform == AltHostPlatform) && Type == TargetType.Game) // for now, we will only run these for the dev config of the host platform { Result.Add("CookedGameTest", "BuildCookRun -run -skipcook -stage -pak -deploy -unattended -nullrhi -NoP4 -platform=" + Platform.ToString()); Result.Add("CookedGameAutomationTest", "BuildCookRun -run -skipcook -stage -pak -deploy -RunAutomationTests -unattended -nullrhi -NoP4 -platform=" + Platform.ToString()); } return Result; } /// /// Return a list of "test name", "UAT command" pairs for testing a monolithic /// public virtual Dictionary GUBP_GetClientServerTests_MonolithicOnly(UnrealTargetPlatform HostPlatform, UnrealTargetPlatform AltHostPlatform, UnrealTargetPlatform ServerPlatform, UnrealTargetPlatform ClientPlatform) { var Result = new Dictionary(); #if false // needs work if ((ServerPlatform == HostPlatform || ServerPlatform == AltHostPlatform) && (ClientPlatform == HostPlatform || ClientPlatform == AltHostPlatform) && Type == TargetType.Game) // for now, we will only run these for the dev config of the host platform and only the game executable, not sure how to deal with a client only executable { Result.Add("CookedNetTest", "BuildCookRun -run -skipcook -stage -pak -deploy -unattended -server -nullrhi -NoP4 -addcmdline=\"-nosteam\" -platform=" + ClientPlatform.ToString() + " -serverplatform=" + ServerPlatform.ToString()); } #endif return Result; } /// /// Return additional parameters to cook commandlet /// public virtual string GUBP_AdditionalCookParameters(UnrealTargetPlatform HostPlatform, string Platform) { return ""; } /// /// Return additional parameters to package commandlet /// public virtual string GUBP_AdditionalPackageParameters(UnrealTargetPlatform HostPlatform, UnrealTargetPlatform Platform) { return ""; } /// /// Allow Cook Platform Override from a target file /// public virtual string GUBP_AlternateCookPlatform(UnrealTargetPlatform HostPlatform, string Platform) { return ""; } /// /// Allow target module to override UHT code generation version. /// public virtual EGeneratedCodeVersion GetGeneratedCodeVersion() { return EGeneratedCodeVersion.None; } } public class RulesAssembly { /// /// The compiled assembly /// private Assembly CompiledAssembly; /// /// All the plugins included in this assembly /// private IReadOnlyList Plugins; /// /// Maps module names to their actual xxx.Module.cs file on disk /// private Dictionary ModuleNameToModuleFile = new Dictionary(StringComparer.InvariantCultureIgnoreCase); /// /// Maps target names to their actual xxx.Target.cs file on disk /// private Dictionary TargetNameToTargetFile = new Dictionary(StringComparer.InvariantCultureIgnoreCase); /// /// Mapping from module file to its plugin info. /// private Dictionary ModuleFileToPluginInfo; /// /// Cache for whether a module has source code /// private Dictionary ModuleHasSource = new Dictionary(); /// /// The parent rules assembly that this assembly inherits. Game assemblies inherit the engine assembly, and the engine assembly inherits nothing. /// private RulesAssembly Parent; /// /// Constructor. Compiles a rules assembly from the given source files. /// /// All the plugins included in this assembly /// List of module files to compile /// List of target files to compile /// Mapping of module file to the plugin that contains it /// The output path for the compiled assembly /// The parent rules assembly public RulesAssembly(IReadOnlyList Plugins, List ModuleFiles, List TargetFiles, Dictionary ModuleFileToPluginInfo, FileReference AssemblyFileName, RulesAssembly Parent) { this.Plugins = Plugins; this.ModuleFileToPluginInfo = ModuleFileToPluginInfo; this.Parent = Parent; // Find all the source files List AssemblySourceFiles = new List(); AssemblySourceFiles.AddRange(ModuleFiles); AssemblySourceFiles.AddRange(TargetFiles); // Compile the assembly if (AssemblySourceFiles.Count > 0) { CompiledAssembly = DynamicCompilation.CompileAndLoadAssembly(AssemblyFileName, AssemblySourceFiles); } // Setup the module map foreach (FileReference ModuleFile in ModuleFiles) { string ModuleName = ModuleFile.GetFileNameWithoutAnyExtensions(); if (!ModuleNameToModuleFile.ContainsKey(ModuleName)) { ModuleNameToModuleFile.Add(ModuleName, ModuleFile); } } // Setup the target map foreach (FileReference TargetFile in TargetFiles) { string TargetName = TargetFile.GetFileNameWithoutAnyExtensions(); if (!TargetNameToTargetFile.ContainsKey(TargetName)) { TargetNameToTargetFile.Add(TargetName, TargetFile); } } } /// /// Tries to get the filename that declared the given type /// /// /// /// True if the type was found, false otherwise public bool TryGetFileNameFromType(Type ExistingType, out FileReference File) { if (ExistingType.Assembly == CompiledAssembly) { string Name = ExistingType.Name; if (ModuleNameToModuleFile.TryGetValue(Name, out File)) { return true; } if (TargetNameToTargetFile.TryGetValue(Name, out File)) { return true; } } else { if (Parent != null && Parent.TryGetFileNameFromType(ExistingType, out File)) { return true; } } File = null; return false; } /// /// Gets the source file containing rules for the given module /// /// The name of the module /// The filename containing rules for this module, or an empty string if not found public FileReference GetModuleFileName(string ModuleName) { FileReference ModuleFile; if (ModuleNameToModuleFile.TryGetValue(ModuleName, out ModuleFile)) { return ModuleFile; } else { return (Parent == null) ? null : Parent.GetModuleFileName(ModuleName); } } /// /// Gets the source file containing rules for the given target /// /// The name of the target /// The filename containing rules for this target, or an empty string if not found public FileReference GetTargetFileName(string TargetName) { FileReference TargetFile; if (TargetNameToTargetFile.TryGetValue(TargetName, out TargetFile)) { return TargetFile; } else { return (Parent == null) ? null : Parent.GetTargetFileName(TargetName); } } /// /// Creates an instance of a module rules descriptor object for the specified module name /// /// Name of the module /// Information about the target associated with this module /// Compiled module rule info public ModuleRules CreateModuleRules(string ModuleName, TargetInfo Target) { FileReference ModuleFileName; return CreateModuleRules(ModuleName, Target, out ModuleFileName); } /// /// Creates an instance of a module rules descriptor object for the specified module name /// /// Name of the module /// Information about the target associated with this module /// The original source file name for the Module.cs file for this module /// Compiled module rule info public ModuleRules CreateModuleRules(string ModuleName, TargetInfo Target, out FileReference ModuleFileName) { // Currently, we expect the user's rules object type name to be the same as the module name var ModuleTypeName = ModuleName; // Make sure the module file is known to us if (!ModuleNameToModuleFile.TryGetValue(ModuleName, out ModuleFileName)) { if (Parent == null) { throw new MissingModuleException(ModuleName); } else { return Parent.CreateModuleRules(ModuleName, Target, out ModuleFileName); } } UnrealTargetPlatform LocalPlatform = Target.Platform; UnrealTargetConfiguration LocalConfiguration = Target.Configuration; TargetInfo LocalTarget = new TargetInfo(LocalPlatform, LocalConfiguration, Target.Type.Value, Target.bIsMonolithic.Value); // The build module must define a type named 'Rules' that derives from our 'ModuleRules' type. var RulesObjectType = CompiledAssembly.GetType(ModuleName); if (RulesObjectType == null) { // Temporary hack to avoid System namespace collisions // @todo projectfiles: Make rules assemblies require namespaces. RulesObjectType = CompiledAssembly.GetType("UnrealBuildTool.Rules." + ModuleName); } if (RulesObjectType == null) { throw new BuildException("Expecting to find a type to be declared in a module rules named '{0}' in {1}. This type must derive from the 'ModuleRules' type defined by Unreal Build Tool.", ModuleTypeName, CompiledAssembly.FullName); } // Create an instance of the module's rules object ModuleRules RulesObject; try { RulesObject = (ModuleRules)Activator.CreateInstance(RulesObjectType, LocalTarget); } catch (Exception Ex) { throw new BuildException(Ex, "Unable to instantiate instance of '{0}' object type from compiled assembly '{1}'. Unreal Build Tool creates an instance of your module's 'Rules' object in order to find out about your module's requirements. The CLR exception details may provide more information: {2}", ModuleTypeName, CompiledAssembly.FullName, Ex.ToString()); } // Update the run-time dependencies path to remove $(PluginDir) and replace with a full path. When the receipt is saved it'll be converted to a $(ProjectDir) or $(EngineDir) equivalent. foreach (var Dependency in RulesObject.RuntimeDependencies) { const string PluginDirVariable = "$(PluginDir)"; if (Dependency.Path.StartsWith(PluginDirVariable, StringComparison.InvariantCultureIgnoreCase)) { PluginInfo Plugin; if (ModuleFileToPluginInfo.TryGetValue(ModuleFileName, out Plugin)) { Dependency.Path = Plugin.Directory + Dependency.Path.Substring(PluginDirVariable.Length); } } } return RulesObject; } /// /// Determines whether the given module name is a game module (as opposed to an engine module) /// public bool IsGameModule(string InModuleName) { FileReference ModuleFileName = GetModuleFileName(InModuleName); return (ModuleFileName != null && !ModuleFileName.IsUnderDirectory(UnrealBuildTool.EngineDirectory)); } protected bool GetTargetTypeAndRulesInstance(string InTargetName, TargetInfo InTarget, out System.Type OutRulesObjectType, out TargetRules OutRulesObject) { // The build module must define a type named 'Target' that derives from our 'TargetRules' type. OutRulesObjectType = CompiledAssembly.GetType(InTargetName); if (OutRulesObjectType == null) { throw new BuildException( "Expecting to find a type to be declared in a target rules named '{0}'. This type must derive from the 'TargetRules' type defined by Unreal Build Tool.", InTargetName); } // Create an instance of the module's rules object try { OutRulesObject = (TargetRules)Activator.CreateInstance(OutRulesObjectType, InTarget); } catch (Exception Ex) { var AssemblyFileName = Path.GetFileNameWithoutExtension(CompiledAssembly.Location); throw new BuildException(Ex, "Unable to instantiate instance of '{0}' object type from compiled assembly '{1}'. Unreal Build Tool creates an instance of your module's 'Rules' object in order to find out about your module's requirements. The CLR exception details may provide more information: {2}", InTargetName, AssemblyFileName, Ex.ToString()); } OutRulesObject.TargetName = InTargetName; return true; } /// /// Creates a target rules object for the specified target name. /// /// Name of the target /// Information about the target associated with this target /// The build target rules for the specified target public TargetRules CreateTargetRules(string TargetName, TargetInfo Target, bool bInEditorRecompile) { FileReference TargetFileName; return CreateTargetRules(TargetName, Target, bInEditorRecompile, out TargetFileName); } /// /// Creates a target rules object for the specified target name. /// /// Name of the target /// Information about the target associated with this target /// The original source file name of the Target.cs file for this target /// The build target rules for the specified target public TargetRules CreateTargetRules(string TargetName, TargetInfo Target, bool bInEditorRecompile, out FileReference TargetFileName) { // Make sure the target file is known to us bool bFoundTargetName = TargetNameToTargetFile.ContainsKey(TargetName); if (bFoundTargetName == false) { if (UnrealBuildTool.RunningRocket()) { //@todo Rocket: Remove this when full game support is implemented // If we are Rocket, they will currently only have an editor target. // See if that exists bFoundTargetName = TargetNameToTargetFile.ContainsKey(TargetName + "Editor"); if (bFoundTargetName) { TargetName += "Editor"; } } } if (bFoundTargetName == false) { if (Parent == null) { // throw new BuildException("Couldn't find target rules file for target '{0}' in rules assembly '{1}'.", TargetName, RulesAssembly.FullName); string ExceptionMessage = "Couldn't find target rules file for target '"; ExceptionMessage += TargetName; ExceptionMessage += "' in rules assembly '"; ExceptionMessage += CompiledAssembly.FullName; ExceptionMessage += "'." + Environment.NewLine; ExceptionMessage += "Location: " + CompiledAssembly.Location + Environment.NewLine; ExceptionMessage += "Target rules found:" + Environment.NewLine; foreach (KeyValuePair entry in TargetNameToTargetFile) { ExceptionMessage += "\t" + entry.Key + " - " + entry.Value + Environment.NewLine; } throw new BuildException(ExceptionMessage); } else { return Parent.CreateTargetRules(TargetName, Target, bInEditorRecompile, out TargetFileName); } } // Return the target file name to the caller TargetFileName = TargetNameToTargetFile[TargetName]; // Currently, we expect the user's rules object type name to be the same as the module name + 'Target' string TargetTypeName = TargetName + "Target"; // The build module must define a type named 'Target' that derives from our 'TargetRules' type. System.Type RulesObjectType; TargetRules RulesObject; GetTargetTypeAndRulesInstance(TargetTypeName, Target, out RulesObjectType, out RulesObject); if (bInEditorRecompile) { // Make sure this is an editor module. if (RulesObject != null) { if (RulesObject.Type != TargetRules.TargetType.Editor) { // Not the editor... determine the editor project string TargetSourceFolderString = TargetFileName.FullName; Int32 SourceFolderIndex = -1; if (Utils.IsRunningOnMono) { TargetSourceFolderString = TargetSourceFolderString.Replace("\\", "/"); SourceFolderIndex = TargetSourceFolderString.LastIndexOf("/Source/", StringComparison.InvariantCultureIgnoreCase); } else { TargetSourceFolderString = TargetSourceFolderString.Replace("/", "\\"); SourceFolderIndex = TargetSourceFolderString.LastIndexOf("\\Source\\", StringComparison.InvariantCultureIgnoreCase); } if (SourceFolderIndex != -1) { DirectoryReference TargetSourceFolder = new DirectoryReference(TargetSourceFolderString.Substring(0, SourceFolderIndex + 7)); foreach (KeyValuePair CheckEntry in TargetNameToTargetFile) { if (CheckEntry.Value.IsUnderDirectory(TargetSourceFolder)) { if (CheckEntry.Key.Equals(TargetName, StringComparison.InvariantCultureIgnoreCase) == false) { // We have found a target in the same source folder that is not the original target found. // See if it is the editor project string CheckTargetTypeName = CheckEntry.Key + "Target"; System.Type CheckRulesObjectType; TargetRules CheckRulesObject; GetTargetTypeAndRulesInstance(CheckTargetTypeName, Target, out CheckRulesObjectType, out CheckRulesObject); if (CheckRulesObject != null) { if (CheckRulesObject.Type == TargetRules.TargetType.Editor) { // Found it // NOTE: This prevents multiple Editor targets from co-existing... RulesObject = CheckRulesObject; break; } } } } } } } } } return RulesObject; } /// /// Enumerates all the plugins that are available /// /// public IEnumerable EnumeratePlugins() { if (Parent == null) { return Plugins; } else { return Plugins.Concat(Parent.EnumeratePlugins()); } } /// /// Tries to find the PluginInfo associated with a given module file /// /// The module to search for /// The matching plugin info, or null. /// True if the module belongs to a plugin public bool TryGetPluginForModule(FileReference ModuleFile, out PluginInfo Plugin) { if (ModuleFileToPluginInfo.TryGetValue(ModuleFile, out Plugin)) { return true; } else { return (Parent == null) ? false : Parent.TryGetPluginForModule(ModuleFile, out Plugin); } } /// /// Determines if a module in this rules assembly has source code. /// /// Name of the module to check /// True if the module has source files, false if the module was not found, or does not have source files. public bool DoesModuleHaveSource(string ModuleName) { FileReference ModuleFile; if (ModuleNameToModuleFile.TryGetValue(ModuleName, out ModuleFile)) { bool HasSource; if (!ModuleHasSource.TryGetValue(ModuleFile, out HasSource)) { foreach (string FileName in Directory.EnumerateFiles(ModuleFile.Directory.FullName, "*.cpp", SearchOption.AllDirectories)) { HasSource = true; break; } ModuleHasSource.Add(ModuleFile, HasSource); } return HasSource; } return (Parent == null) ? false : Parent.DoesModuleHaveSource(ModuleName); } } public class RulesCompiler { /// /// Enum for types of rules files. Should match extensions in RulesFileExtensions. /// public enum RulesFileType { Module, Target, Automation, AutomationModule } /// /// Cached list of rules files in each directory of each type /// class RulesFileCache { public List ModuleRules = new List(); public List TargetRules = new List(); public List AutomationModules = new List(); } /// Map of root folders to a cached list of all UBT-related source files in that folder or any of its sub-folders. /// We cache these file names so we can avoid searching for them later on. static Dictionary RootFolderToRulesFileCache = new Dictionary(); public static List FindAllRulesSourceFiles(RulesFileType RulesFileType, List GameFolders, List ForeignPlugins, List AdditionalSearchPaths, bool bIncludeEngine = true) { List Folders = new List(); // Add all engine source (including third party source) if (bIncludeEngine) { Folders.Add(UnrealBuildTool.EngineSourceDirectory); } // @todo plugin: Disallow modules from including plugin modules as dependency modules? (except when the module is part of that plugin) // Get all the root folders for plugins List RootFolders = new List(); if (bIncludeEngine) { RootFolders.Add(UnrealBuildTool.EngineDirectory); } if (GameFolders != null) { RootFolders.AddRange(GameFolders); } // Find all the plugin source directories foreach (DirectoryReference RootFolder in RootFolders) { DirectoryReference PluginsFolder = DirectoryReference.Combine(RootFolder, "Plugins"); foreach (FileReference PluginFile in Plugins.EnumeratePlugins(PluginsFolder)) { Folders.Add(DirectoryReference.Combine(PluginFile.Directory, "Source")); } } // Add all the extra plugin folders if (ForeignPlugins != null) { foreach (FileReference ForeignPlugin in ForeignPlugins) { Folders.Add(DirectoryReference.Combine(ForeignPlugin.Directory, "Source")); } } // Add in the game folders to search if (GameFolders != null) { foreach (DirectoryReference GameFolder in GameFolders) { DirectoryReference GameSourceFolder = DirectoryReference.Combine(GameFolder, "Source"); Folders.Add(GameSourceFolder); DirectoryReference GameIntermediateSourceFolder = DirectoryReference.Combine(GameFolder, "Intermediate", "Source"); Folders.Add(GameIntermediateSourceFolder); } } // Process the additional search path, if sent in if (AdditionalSearchPaths != null) { foreach (var AdditionalSearchPath in AdditionalSearchPaths) { if (AdditionalSearchPath != null) { if (AdditionalSearchPath.Exists()) { Folders.Add(AdditionalSearchPath); } else { throw new BuildException("Couldn't find AdditionalSearchPath for rules source files '{0}'", AdditionalSearchPath); } } } } // Iterate over all the folders to check List SourceFiles = new List(); HashSet UniqueSourceFiles = new HashSet(); foreach (DirectoryReference Folder in Folders) { IReadOnlyList SourceFilesForFolder = FindAllRulesFiles(Folder, RulesFileType); foreach (FileReference SourceFile in SourceFilesForFolder) { if (UniqueSourceFiles.Add(SourceFile)) { SourceFiles.Add(SourceFile); } } } return SourceFiles; } private static IReadOnlyList FindAllRulesFiles(DirectoryReference Directory, RulesFileType Type) { // Check to see if we've already cached source files for this folder RulesFileCache Cache; if (!RootFolderToRulesFileCache.TryGetValue(Directory, out Cache)) { Cache = new RulesFileCache(); FindAllRulesFilesRecursively(Directory, Cache); RootFolderToRulesFileCache[Directory] = Cache; } // Get the list of files of the type we're looking for if (Type == RulesCompiler.RulesFileType.Module) { return Cache.ModuleRules; } else if (Type == RulesCompiler.RulesFileType.Target) { return Cache.TargetRules; } else if (Type == RulesCompiler.RulesFileType.AutomationModule) { return Cache.AutomationModules; } else { throw new BuildException("Unhandled rules type: {0}", Type); } } private static void FindAllRulesFilesRecursively(DirectoryReference Directory, RulesFileCache Cache) { // Scan all the files in this directory bool bSearchSubFolders = true; foreach (FileReference File in DirectoryLookupCache.EnumerateFiles(Directory)) { if (File.HasExtension(".build.cs")) { Cache.ModuleRules.Add(File); bSearchSubFolders = false; } else if (File.HasExtension(".target.cs")) { Cache.TargetRules.Add(File); } else if (File.HasExtension(".automation.csproj")) { Cache.AutomationModules.Add(File); bSearchSubFolders = false; } } // If we didn't find anything to stop the search, search all the subdirectories too if (bSearchSubFolders) { foreach (DirectoryReference SubDirectory in DirectoryLookupCache.EnumerateDirectories(Directory)) { FindAllRulesFilesRecursively(SubDirectory, Cache); } } } /// /// The cached rules assembly for engine modules and targets. /// private static RulesAssembly EngineRulesAssembly; /// Map of assembly names we've already compiled and loaded to their Assembly and list of game folders. This is used to prevent /// trying to recompile the same assembly when ping-ponging between different types of targets private static Dictionary LoadedAssemblyMap = new Dictionary(); /// /// Creates the engine rules assembly /// /// List of plugins to include in this assembly /// New rules assembly public static RulesAssembly CreateEngineRulesAssembly() { if (EngineRulesAssembly == null) { // Find all the rules files List ModuleFiles = new List(FindAllRulesFiles(UnrealBuildTool.EngineSourceDirectory, RulesFileType.Module)); List TargetFiles = new List(FindAllRulesFiles(UnrealBuildTool.EngineSourceDirectory, RulesFileType.Target)); // Add all the plugin modules too IReadOnlyList EnginePlugins = Plugins.ReadEnginePlugins(UnrealBuildTool.EngineDirectory); Dictionary ModuleFileToPluginInfo = new Dictionary(); FindModuleRulesForPlugins(EnginePlugins, ModuleFiles, ModuleFileToPluginInfo); // Create a path to the assembly that we'll either load or compile FileReference AssemblyFileName = FileReference.Combine(UnrealBuildTool.EngineDirectory, BuildConfiguration.BaseIntermediateFolder, "BuildRules", "UE4Rules.dll"); EngineRulesAssembly = new RulesAssembly(EnginePlugins, ModuleFiles, TargetFiles, ModuleFileToPluginInfo, AssemblyFileName, null); } return EngineRulesAssembly; } /// /// Creates a rules assembly with the given parameters. /// /// The project file to create rules for. Null for the engine. /// List of foreign plugin folders to include in the assembly. May be null. public static RulesAssembly CreateProjectRulesAssembly(FileReference ProjectFileName) { // Check if there's an existing assembly for this project RulesAssembly ProjectRulesAssembly; if (!LoadedAssemblyMap.TryGetValue(ProjectFileName, out ProjectRulesAssembly)) { // Create the engine rules assembly RulesAssembly Parent = CreateEngineRulesAssembly(); // Find all the rules under the project source directory DirectoryReference ProjectDirectory = ProjectFileName.Directory; DirectoryReference ProjectSourceDirectory = DirectoryReference.Combine(ProjectDirectory, "Source"); List ModuleFiles = new List(FindAllRulesFiles(ProjectSourceDirectory, RulesFileType.Module)); List TargetFiles = new List(FindAllRulesFiles(ProjectSourceDirectory, RulesFileType.Target)); // Find all the project plugins IReadOnlyList ProjectPlugins = Plugins.ReadProjectPlugins(ProjectFileName.Directory); Dictionary ModuleFileToPluginInfo = new Dictionary(); FindModuleRulesForPlugins(ProjectPlugins, ModuleFiles, ModuleFileToPluginInfo); // Add the games project's intermediate source folder DirectoryReference ProjectIntermediateSourceDirectory = DirectoryReference.Combine(ProjectDirectory, "Intermediate", "Source"); if (ProjectIntermediateSourceDirectory.Exists()) { TargetFiles.AddRange(FindAllRulesFiles(ProjectIntermediateSourceDirectory, RulesFileType.Target)); } // Compile the assembly FileReference AssemblyFileName = FileReference.Combine(ProjectDirectory, BuildConfiguration.BaseIntermediateFolder, "BuildRules", ProjectFileName.GetFileNameWithoutExtension() + "ModuleRules.dll"); ProjectRulesAssembly = new RulesAssembly(ProjectPlugins, ModuleFiles, TargetFiles, ModuleFileToPluginInfo, AssemblyFileName, Parent); LoadedAssemblyMap.Add(ProjectFileName, ProjectRulesAssembly); } return ProjectRulesAssembly; } /// /// Creates a rules assembly with the given parameters. /// /// The project file to create rules for. Null for the engine. /// List of foreign plugin folders to include in the assembly. May be null. public static RulesAssembly CreatePluginRulesAssembly(FileReference PluginFileName, RulesAssembly Parent) { // Check if there's an existing assembly for this project RulesAssembly PluginRulesAssembly; if (!LoadedAssemblyMap.TryGetValue(PluginFileName, out PluginRulesAssembly)) { // Find all the rules source files List ModuleFiles = new List(); List TargetFiles = new List(); // Create a list of plugins for this assembly. If it already exists in the parent assembly, just create an empty assembly. List ForeignPlugins = new List(); if (Parent == null || !Parent.EnumeratePlugins().Any(x => x.File == PluginFileName)) { ForeignPlugins.Add(new PluginInfo(PluginFileName, PluginLoadedFrom.GameProject)); } // Find all the modules Dictionary ModuleFileToPluginInfo = new Dictionary(); FindModuleRulesForPlugins(ForeignPlugins, ModuleFiles, ModuleFileToPluginInfo); // Compile the assembly FileReference AssemblyFileName = FileReference.Combine(PluginFileName.Directory, BuildConfiguration.BaseIntermediateFolder, "BuildRules", Path.GetFileNameWithoutExtension(PluginFileName.FullName) + "ModuleRules.dll"); PluginRulesAssembly = new RulesAssembly(ForeignPlugins, ModuleFiles, TargetFiles, ModuleFileToPluginInfo, AssemblyFileName, Parent); LoadedAssemblyMap.Add(PluginFileName, PluginRulesAssembly); } return PluginRulesAssembly; } /// /// Finds all the module rules for plugins under the given directory. /// /// The directory to search /// List of module files to be populated /// Dictionary which is filled with mappings from the module file to its corresponding plugin file private static void FindModuleRulesForPlugins(IReadOnlyList Plugins, List ModuleFiles, Dictionary ModuleFileToPluginInfo) { foreach (PluginInfo Plugin in Plugins) { IReadOnlyList PluginModuleFiles = FindAllRulesFiles(DirectoryReference.Combine(Plugin.Directory, "Source"), RulesFileType.Module); foreach (FileReference ModuleFile in PluginModuleFiles) { ModuleFiles.Add(ModuleFile); ModuleFileToPluginInfo[ModuleFile] = Plugin; } } } /// /// Gets the filename that declares the given type. /// /// The type to search for. /// The filename that declared the given type, or null public static string GetFileNameFromType(Type ExistingType) { FileReference FileName; if (EngineRulesAssembly != null && EngineRulesAssembly.TryGetFileNameFromType(ExistingType, out FileName)) { return FileName.FullName; } foreach (RulesAssembly RulesAssembly in LoadedAssemblyMap.Values) { if (RulesAssembly.TryGetFileNameFromType(ExistingType, out FileName)) { return FileName.FullName; } } return null; } } }