2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.IO ;
using System.Collections.Generic ;
using System.Linq ;
using System.Reflection ;
using System.Diagnostics ;
2015-08-04 11:20:50 -04:00
using System.Runtime.Serialization ;
2014-03-14 14:13:41 -04:00
namespace UnrealBuildTool
{
/// <summary>
/// Information about a target, passed along when creating a module descriptor
/// </summary>
2015-04-13 10:01:26 -04:00
[Serializable]
2015-08-04 11:20:50 -04:00
public class TargetInfo : ISerializable
2014-03-14 14:13:41 -04:00
{
/// 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 ;
2015-04-12 13:49:25 -04:00
/// Whether the target is monolithic (if known)
public readonly bool? bIsMonolithic ;
2015-08-04 11:20:50 -04:00
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 ) ;
}
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Constructs a TargetInfo
/// </summary>
/// <param name="InitPlatform">Target platform</param>
/// <param name="InitConfiguration">Target build configuration</param>
2015-04-12 13:49:25 -04:00
public TargetInfo ( UnrealTargetPlatform InitPlatform , UnrealTargetConfiguration InitConfiguration )
2014-03-14 14:13:41 -04:00
{
Platform = InitPlatform ;
Configuration = InitConfiguration ;
// get the platform's architecture
2014-05-21 06:09:37 -04:00
var BuildPlatform = UEBuildPlatform . GetBuildPlatform ( Platform ) ;
2014-03-14 14:13:41 -04:00
Architecture = BuildPlatform . GetActiveArchitecture ( ) ;
}
2015-04-12 13:49:25 -04:00
/// <summary>
/// Constructs a TargetInfo
/// </summary>
/// <param name="InitPlatform">Target platform</param>
/// <param name="InitConfiguration">Target build configuration</param>
/// <param name="InitType">Target type</param>
/// <param name="bInitIsMonolithic">Whether the target is monolithic</param>
public TargetInfo ( UnrealTargetPlatform InitPlatform , UnrealTargetConfiguration InitConfiguration , TargetRules . TargetType InitType , bool bInitIsMonolithic )
: this ( InitPlatform , InitConfiguration )
{
Type = InitType ;
bIsMonolithic = bInitIsMonolithic ;
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// True if the target type is a cooked game.
/// </summary>
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 | |
2014-09-29 09:35:05 -04:00
Type = = TargetRules . TargetType . Server ;
2014-03-14 14:13:41 -04:00
}
}
2014-07-02 16:24:26 -04:00
/// <summary>
/// True if the target type is a monolithic binary
/// </summary>
public bool IsMonolithic
{
get
{
2015-04-12 13:49:25 -04:00
if ( ! bIsMonolithic . HasValue )
2014-07-02 16:24:26 -04:00
{
2015-04-12 13:49:25 -04:00
throw new BuildException ( "Trying to access TargetInfo.IsMonolithic when bIsMonolithic is not set. Make sure IsMonolithic is used only in ModuleRules." ) ;
2014-07-02 16:24:26 -04:00
}
2015-04-12 13:49:25 -04:00
return bIsMonolithic . Value ;
2014-07-02 16:24:26 -04:00
}
}
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// ModuleRules is a data structure that contains the rules for defining a module
/// </summary>
2015-08-28 08:39:42 -04:00
public class ModuleRules
2014-03-14 14:13:41 -04:00
{
/// 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 ,
2015-03-23 09:00:18 -04:00
/// Code should only be optimized in shipping builds (not in Debug, DebugGame, Development)
InShippingBuildsOnly ,
2014-03-14 14:13:41 -04:00
/// 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
}
2014-06-09 12:05:40 -04:00
/// Precompiled header usage for this module
2014-03-14 14:13:41 -04:00
public PCHUsageMode PCHUsage = PCHUsageMode . Default ;
/** Use run time type information */
public bool bUseRTTI = false ;
2015-06-29 12:24:33 -04:00
/** Use AVX instructions */
public bool bUseAVX = false ;
2014-03-14 14:13:41 -04:00
/** 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 ;
2015-05-12 08:03:21 -04:00
/** Enable warnings for shadowed variables */
public bool bEnableShadowVariableWarnings = true ;
2014-03-14 14:13:41 -04:00
/** If true and unity builds are enabled, this module will build without unity. */
public bool bFasterWithoutUnity = false ;
2015-08-07 13:58:49 -04:00
/ * * 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 ;
2014-03-14 14:13:41 -04:00
/** Overrides BuildConfiguration.MinFilesUsingPrecompiledHeader if non-zero. */
public int MinFilesUsingPrecompiledHeaderOverride = 0 ;
2015-07-03 05:23:01 -04:00
/** Module uses a #import so must be built locally when compiling with SN-DBS */
public bool bBuildLocallyWithSNDBS = false ;
2014-03-14 14:13:41 -04:00
/// 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 < string > PublicIncludePathModuleNames = new List < string > ( ) ;
/// List of public dependency module names. These are modules that are required by our public source files.
public List < string > PublicDependencyModuleNames = new List < string > ( ) ;
/// 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 < string > PrivateIncludePathModuleNames = new List < string > ( ) ;
/// 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 < string > PrivateDependencyModuleNames = new List < string > ( ) ;
/// 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 < string > CircularlyReferencedDependentModules = new List < string > ( ) ;
/// System include paths. These are public stable header file directories that are not checked when resolving header dependencies.
public List < string > PublicSystemIncludePaths = new List < string > ( ) ;
/// List of all paths to include files that are exposed to other modules
public List < string > PublicIncludePaths = new List < string > ( ) ;
/// List of all paths to this module's internal include files, not exposed to other modules
public List < string > PrivateIncludePaths = new List < string > ( ) ;
/// List of library paths - typically used for External (third party) modules
public List < string > PublicLibraryPaths = new List < string > ( ) ;
/// List of addition libraries - typically used for External (third party) modules
public List < string > PublicAdditionalLibraries = new List < string > ( ) ;
// List of frameworks
public List < string > PublicFrameworks = new List < string > ( ) ;
2014-06-18 15:57:40 -04:00
// List of weak frameworks (for OS version transitions)
public List < string > PublicWeakFrameworks = new List < string > ( ) ;
2014-03-14 14:13:41 -04:00
/// List of addition frameworks - typically used for External (third party) modules on Mac and iOS
Support for third party iOS framework bundled assets
* Work in progress, works with RPC utility, local mac support incoming
* Changed AdditionalPublicFrameworks from storing just string, to storing the frame work name, zip name, and bundled asset name that needs to be copied
* We also now store the module that added this framework, so we can derive the module project path, etc when we need it for when we create intermediate directories
#Codereview Josh.Adams, Peter.Sauerbrei, Michael.Noland, Gil.Gribb, Robert.Manuszewski
[CL 2068603 by John Pollard in Main branch]
2014-05-09 16:38:26 -04:00
public List < UEBuildFramework > PublicAdditionalFrameworks = new List < UEBuildFramework > ( ) ;
2014-03-14 14:13:41 -04:00
2014-07-28 14:55:48 -04:00
/// List of addition resources that should be copied to the app bundle for Mac or iOS
public List < UEBuildBundleResource > AdditionalBundleResources = new List < UEBuildBundleResource > ( ) ;
2014-03-14 14:13:41 -04:00
/// 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 < string > PublicAdditionalShadowFiles = new List < string > ( ) ;
/// List of delay load DLLs - typically used for External (third party) modules
public List < string > PublicDelayLoadDLLs = new List < string > ( ) ;
/// Additional compiler definitions for this module
public List < string > Definitions = new List < string > ( ) ;
/** CLR modules only: The assemblies referenced by the module's private implementation. */
public List < string > PrivateAssemblyReferences = new List < string > ( ) ;
/// Addition modules this module may require at run-time
public List < string > DynamicallyLoadedModuleNames = new List < string > ( ) ;
/// 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 < string > PlatformSpecificDynamicallyLoadedModuleNames = new List < string > ( ) ;
2015-04-03 10:25:57 -04:00
/// List of files which this module depends on at runtime. These files will be staged along with the target.
public List < RuntimeDependency > RuntimeDependencies = new List < RuntimeDependency > ( ) ;
2015-01-13 11:41:17 -05:00
/// <summary>
/// Property for the directory containing this module. Useful for adding paths to third party dependencies.
/// </summary>
public string ModuleDirectory
{
get
{
2015-08-31 10:33:16 -04:00
return Path . GetDirectoryName ( RulesCompiler . GetFileNameFromType ( GetType ( ) ) ) ;
2015-01-13 11:41:17 -05:00
}
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// 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.
/// </summary>
/// <param name="ModuleNames">The names of the modules to add</param>
public void AddThirdPartyPrivateStaticDependencies ( TargetInfo Target , params string [ ] InModuleNames )
{
2014-09-29 09:35:05 -04:00
if ( UnrealBuildTool . RunningRocket ( ) = = false | | Target . Type = = TargetRules . TargetType . Game )
2014-03-14 14:13:41 -04:00
{
PrivateDependencyModuleNames . AddRange ( InModuleNames ) ;
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="ModuleNames">The names of the modules to add</param>
public void AddThirdPartyPrivateDynamicDependencies ( TargetInfo Target , params string [ ] InModuleNames )
{
2014-09-29 09:35:05 -04:00
if ( UnrealBuildTool . RunningRocket ( ) = = false | | Target . Type = = TargetRules . TargetType . Game )
2014-03-14 14:13:41 -04:00
{
PrivateIncludePathModuleNames . AddRange ( InModuleNames ) ;
DynamicallyLoadedModuleNames . AddRange ( InModuleNames ) ;
}
}
/// <summary>
/// Setup this module for PhysX/APEX support (based on the settings in UEBuildConfiguration)
/// </summary>
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" ) ;
}
2014-09-09 16:30:29 -04:00
if ( UEBuildConfiguration . bRuntimePhysicsCooking = = true )
{
Definitions . Add ( "WITH_RUNTIME_PHYSICS_COOKING" ) ;
}
2014-03-14 14:13:41 -04:00
}
2014-05-30 13:28:52 -04:00
/// <summary>
/// Setup this module for Box2D support (based on the settings in UEBuildConfiguration)
/// </summary>
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 ) ) ;
}
2014-07-21 04:28:22 -04:00
/** Redistribution override flag for this module. */
public bool? IsRedistributableOverride { get ; set ; }
2015-03-17 09:34:18 -04:00
/ * *
* Reads additional dependencies array for project module from project file and fills PrivateDependencyModuleNames .
*
* @param ProjectFile A path to the . uproject file .
* @param ModuleName Name of the module .
* /
2015-09-03 08:47:24 -04:00
public void ReadAdditionalDependencies ( FileReference ProjectFile , string ModuleName )
2015-03-17 09:34:18 -04:00
{
// Create a case-insensitive dictionary of the contents
2015-09-03 08:47:24 -04:00
Dictionary < string , object > Descriptor = fastJSON . JSON . Instance . ToObject < Dictionary < string , object > > ( File . ReadAllText ( ProjectFile . FullName ) ) ;
2015-03-17 09:34:18 -04:00
Descriptor = new Dictionary < string , object > ( Descriptor , StringComparer . InvariantCultureIgnoreCase ) ;
// Get the list of plugins
object ModulesObject ;
if ( Descriptor . TryGetValue ( "Modules" , out ModulesObject ) )
{
foreach ( var ModuleObject in ( ModulesObject as object [ ] ) . Cast < Dictionary < string , object > > ( ) )
{
object NameObject ;
object AdditionalDependenciesObject ;
if ( ! ModuleObject . TryGetValue ( "Name" , out NameObject )
| | ! ( NameObject as string ) . Equals ( ModuleName )
| | ! ModuleObject . TryGetValue ( "AdditionalDependencies" , out AdditionalDependenciesObject ) )
{
continue ;
}
foreach ( var AdditionalDependency in ( AdditionalDependenciesObject as object [ ] ) . Cast < string > ( ) )
{
if ( ! PrivateDependencyModuleNames . Contains ( AdditionalDependency ) )
{
PrivateDependencyModuleNames . Add ( AdditionalDependency ) ;
}
}
break ;
}
}
}
2014-03-14 14:13:41 -04:00
}
/// <summary>
/// TargetRules is a data structure that contains the rules for defining a target (application/executable)
/// </summary>
public abstract class TargetRules
{
/// Type of target
2014-08-29 15:46:20 -04:00
[Serializable]
2014-03-14 14:13:41 -04:00
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 ,
}
/// <summary>
/// The name of the game, this is set up by the rules compiler after it compiles and constructs this
/// </summary>
public string TargetName = null ;
/// <summary>
/// Whether the target uses Steam (todo: substitute with more generic functionality)
/// </summary>
public bool bUsesSteam ;
2015-02-18 09:18:51 -05:00
/// <summary>
/// Whether the target uses CEF3
/// </summary>
public bool bUsesCEF3 ;
2014-03-14 14:13:41 -04:00
/// <summary>
2014-04-23 18:04:33 -04:00
/// Whether the project uses visual Slate UI (as opposed to the low level windowing/messaging which is always used)
2014-03-14 14:13:41 -04:00
/// </summary>
public bool bUsesSlate = true ;
/// <summary>
/// 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.
/// </summary>
public bool bUsesSlateEditorStyle = false ;
/// <summary>
2014-09-23 13:55:06 -04:00
/// 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.
/// </summary>
public bool bUseStaticCRT = false ;
2015-05-14 12:40:00 -04:00
/// <summary>
/// Allow a target to specify a preferred sub-platform.
/// Can be used to target a build using sub platform specifics.
/// </summary>
public string PreferredSubPlatform = String . Empty ;
2014-09-23 13:55:06 -04:00
/// <summary>
/// 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.
2014-03-14 14:13:41 -04:00
/// </summary>
public bool bDebugBuildsActuallyUseDebugCRT = false ;
2015-03-27 07:15:32 -04:00
/// <summary>
/// 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.
/// </summary>
public bool bOutputPubliclyDistributable = false ;
2015-04-12 10:51:07 -04:00
/// <summary>
/// Specifies the configuration whose binaries do not require a "-Platform-Configuration" suffix.
/// </summary>
public UnrealTargetConfiguration UndecoratedConfiguration = UnrealTargetConfiguration . Development ;
2014-03-14 14:13:41 -04:00
/// <summary>
2015-06-18 08:41:17 -04:00
/// 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.
/// </summary>
public bool bBuildAllPlugins = false ;
/// <summary>
2015-04-21 21:22:15 -04:00
/// 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.
2014-03-14 14:13:41 -04:00
/// </summary>
public List < string > AdditionalPlugins = new List < string > ( ) ;
/// <summary>
/// Is the given type a 'game' type (Game/Editor/Server) wrt building?
/// </summary>
/// <param name="InType">The target type of interest</param>
/// <returns>true if it *is* a 'game' type</returns>
static public bool IsGameType ( TargetType InType )
{
return (
( InType = = TargetType . Game ) | |
( InType = = TargetType . Editor ) | |
( InType = = TargetType . Client ) | |
( InType = = TargetType . Server )
) ;
}
/// <summary>
/// Is the given type a game?
/// </summary>
/// <param name="InType">The target type of interest</param>
/// <returns>true if it *is* a game</returns>
static public bool IsAGame ( TargetType InType )
{
return (
( InType = = TargetType . Game ) | |
2014-09-29 09:35:05 -04:00
( InType = = TargetType . Client )
2014-03-14 14:13:41 -04:00
) ;
}
/// <summary>
/// Is the given type an 'editor' type with regard to building?
/// </summary>
/// <param name="InType">The target type of interest</param>
/// <returns>true if it *is* an 'editor' type</returns>
static public bool IsEditorType ( TargetType InType )
{
return ( InType = = TargetType . Editor ) ;
}
/// <summary>
/// Type of target
/// </summary>
2014-04-23 18:18:51 -04:00
public TargetType Type = TargetType . Game ;
2014-03-14 14:13:41 -04:00
/// <summary>
/// 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
/// </summary>
public string ConfigurationName
{
get
{
if ( String . IsNullOrEmpty ( ConfigurationNameVar ) )
{
return Type . ToString ( ) ;
}
else
{
return ConfigurationNameVar ;
}
}
set
{
ConfigurationNameVar = value ;
}
}
private string ConfigurationNameVar = String . Empty ;
2015-04-20 14:30:37 -04:00
/// <summary>
/// Allows a Program Target to specify it's own solution folder path
/// </summary>
public string SolutionDirectory = String . Empty ;
2014-03-14 14:13:41 -04:00
/// <summary>
/// If true, the built target goes into the Engine/Binaries/<PLATFORM> folder
/// </summary>
public bool bOutputToEngineBinaries = false ;
2014-07-18 10:10:24 -04:00
/// <summary>
/// Sub folder where the built target goes: Engine/Binaries/<PLATFORM>/<SUBDIR>
/// </summary>
public string ExeBinariesSubFolder = String . Empty ;
2014-03-14 14:13:41 -04:00
/// <summary>
/// Whether this target should be compiled in monolithic mode
/// </summary>
/// <param name="InPlatform">The platform being built</param>
/// <param name="InConfiguration">The configuration being built</param>
/// <returns>true if it should, false if not</returns>
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 ;
}
/// <summary>
/// Get the supported platforms for this target
/// </summary>
/// <param name="OutPlatforms">The list of platforms supported</param>
/// <returns>true if successful, false if not</returns>
2015-03-13 10:52:15 -04:00
public virtual bool GetSupportedPlatforms ( ref List < UnrealTargetPlatform > OutPlatforms )
2014-03-14 14:13:41 -04:00
{
2014-04-30 09:43:00 -04:00
if ( Type = = TargetType . Program )
2014-03-14 14:13:41 -04:00
{
// By default, all programs are desktop only.
2015-03-13 10:52:15 -04:00
return UnrealBuildTool . GetAllDesktopPlatforms ( ref OutPlatforms , false ) ;
2014-03-14 14:13:41 -04:00
}
2014-04-30 09:43:00 -04:00
else if ( IsEditorType ( Type ) )
{
2015-03-13 10:52:15 -04:00
return UnrealBuildTool . GetAllEditorPlatforms ( ref OutPlatforms , false ) ;
2014-04-30 09:43:00 -04:00
}
2014-03-14 14:13:41 -04:00
else if ( TargetRules . IsGameType ( Type ) )
{
// By default all games support all platforms
2015-03-13 10:52:15 -04:00
return UnrealBuildTool . GetAllPlatforms ( ref OutPlatforms , false ) ;
2014-03-14 14:13:41 -04:00
}
return false ;
}
public bool SupportsPlatform ( UnrealTargetPlatform InPlatform )
{
List < UnrealTargetPlatform > SupportedPlatforms = new List < UnrealTargetPlatform > ( ) ;
2015-03-13 10:52:15 -04:00
if ( GetSupportedPlatforms ( ref SupportedPlatforms ) = = true )
2014-03-14 14:13:41 -04:00
{
return SupportedPlatforms . Contains ( InPlatform ) ;
}
return false ;
}
/// <summary>
/// Get the supported configurations for this target
/// </summary>
/// <param name="OutConfigurations">The list of configurations supported</param>
/// <returns>true if successful, false if not</returns>
public virtual bool GetSupportedConfigurations ( ref List < UnrealTargetConfiguration > 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 ;
}
/// <summary>
/// Setup the binaries associated with this target.
/// </summary>
/// <param name="Target">The target information - such as platform and configuration</param>
/// <param name="OutBuildBinaryConfigurations">Output list of binaries to generated</param>
/// <param name="OutExtraModuleNames">Output list of extra modules that this target could utilize</param>
public virtual void SetupBinaries (
TargetInfo Target ,
ref List < UEBuildBinaryConfiguration > OutBuildBinaryConfigurations ,
ref List < string > OutExtraModuleNames
)
{
}
/// <summary>
/// Setup the global environment for building this target
2015-04-16 11:55:56 -04:00
/// IMPORTANT: Game targets will *not* have this function called if they use the shared build environment.
/// See ShouldUseSharedBuildEnvironment().
2014-03-14 14:13:41 -04:00
/// </summary>
/// <param name="Target">The target information - such as platform and configuration</param>
/// <param name="OutLinkEnvironmentConfiguration">Output link environment settings</param>
/// <param name="OutCPPEnvironmentConfiguration">Output compile environment settings</param>
public virtual void SetupGlobalEnvironment (
TargetInfo Target ,
ref LinkEnvironmentConfiguration OutLinkEnvironmentConfiguration ,
ref CPPEnvironmentConfiguration OutCPPEnvironmentConfiguration
)
{
}
2015-03-20 08:25:23 -04:00
2015-04-16 11:55:56 -04:00
/// <summary>
/// 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().
/// </summary>
/// <param name="Target">Information about the target</param>
/// <returns>True if the target should use the shared build environment</returns>
public virtual bool ShouldUseSharedBuildEnvironment ( TargetInfo Target )
{
return UnrealBuildTool . RunningRocket ( ) | | ( Target . Type ! = TargetType . Program & & ! Target . IsMonolithic ) ;
}
2015-03-20 08:25:23 -04:00
/// <summary>
2015-03-27 09:13:06 -04:00
/// Allows the target to specify modules which can be precompiled with the -Precompile/-UsePrecompiled arguments to UBT.
2015-03-20 08:25:23 -04:00
/// All dependencies of the specified modules will be included.
/// </summary>
/// <param name="Target">The target information, such as platform and configuration</param>
/// <param name="ModuleNames">List which receives module names to precompile</param>
public virtual void GetModulesToPrecompile ( TargetInfo Target , List < string > ModuleNames )
{
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Return true if this target should always be built with the base editor. Usually programs like shadercompilerworker.
/// </summary>
/// <returns>true if this target should always be built with the base editor.</returns>
public virtual bool GUBP_AlwaysBuildWithBaseEditor ( )
{
return false ;
}
/// <summary>
/// Return true if this target should always be built with the tools. Usually programs like unrealpak.
2014-04-23 17:51:55 -04:00
/// <param name="SeparateNode">If this is set to true, the program will get its own node</param>
2014-03-14 14:13:41 -04:00
/// </summary>
2014-07-22 09:55:11 -04:00
/// <returns>true if this target should always be built with the base editor.</returns>
[Obsolete]
2014-04-23 17:51:55 -04:00
public virtual bool GUBP_AlwaysBuildWithTools ( UnrealTargetPlatform InHostPlatform , out bool bInternalToolOnly , out bool SeparateNode )
2014-03-14 14:13:41 -04:00
{
bInternalToolOnly = false ;
2015-01-06 10:24:25 -05:00
SeparateNode = false ;
2014-03-14 14:13:41 -04:00
return false ;
}
/// <summary>
2014-07-21 18:06:25 -04:00
/// Return true if this target should always be built with the tools. Usually programs like unrealpak.
/// <param name="SeparateNode">If this is set to true, the program will get its own node</param>
/// </summary>
/// <returns>true if this target should always be built with the base editor.</returns>
2015-03-12 08:29:35 -04:00
public virtual bool GUBP_AlwaysBuildWithTools ( UnrealTargetPlatform InHostPlatform , out bool bInternalToolOnly , out bool SeparateNode , out bool CrossCompile )
2014-07-21 18:06:25 -04:00
{
bInternalToolOnly = false ;
SeparateNode = false ;
2015-01-06 10:24:25 -05:00
CrossCompile = false ;
2014-07-21 18:06:25 -04:00
return false ;
}
/// <summary>
2014-03-14 14:13:41 -04:00
/// Return a list of platforms to build a tool for
/// </summary>
/// <returns>a list of platforms to build a tool for</returns>
public virtual List < UnrealTargetPlatform > GUBP_ToolPlatforms ( UnrealTargetPlatform InHostPlatform )
{
return new List < UnrealTargetPlatform > { InHostPlatform } ;
}
/// <summary>
/// Return a list of configs to build a tool for
/// </summary>
/// <returns>a list of configs to build a tool for</returns>
public virtual List < UnrealTargetConfiguration > GUBP_ToolConfigs ( UnrealTargetPlatform InHostPlatform )
{
return new List < UnrealTargetConfiguration > { UnrealTargetConfiguration . Development } ;
}
2014-09-22 12:13:56 -04:00
/// <summary>
/// Return true if target should include a NonUnity test
/// </summary>
/// <returns>true if this target should include a NonUnity test
public virtual bool GUBP_IncludeNonUnityToolTest ( )
{
return false ;
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Return true if this target should use a platform specific pass
/// </summary>
/// <returns>true if this target should use a platform specific pass
public virtual bool GUBP_NeedsPlatformSpecificDLLs ( )
{
return false ;
}
2015-01-28 08:26:51 -05:00
///<summary>
///Returns true if XP monolithics are required for a game
/// </summary>
/// <returns>true if this target needs to be compiled for Windows XP</returns>
public virtual bool GUBP_BuildWindowsXPMonolithics ( )
{
return false ;
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Return a list of target platforms for the monolithic
/// </summary>
/// <returns>a list of target platforms for the monolithic</returns>
public virtual List < UnrealTargetPlatform > GUBP_GetPlatforms_MonolithicOnly ( UnrealTargetPlatform HostPlatform )
{
var Result = new List < UnrealTargetPlatform > { HostPlatform } ;
// hack to set up the templates without adding anything to their .targets.cs files
if ( ! String . IsNullOrEmpty ( TargetName ) & & TargetName . StartsWith ( "TP_" ) )
{
2014-09-05 21:50:16 -04:00
if ( HostPlatform = = UnrealTargetPlatform . Win64 )
{
Result . Add ( UnrealTargetPlatform . IOS ) ;
Result . Add ( UnrealTargetPlatform . Android ) ;
}
else if ( HostPlatform = = UnrealTargetPlatform . Mac )
2014-03-14 14:13:41 -04:00
{
2014-09-05 21:50:16 -04:00
Result . Add ( UnrealTargetPlatform . IOS ) ;
}
2014-03-14 14:13:41 -04:00
}
return Result ;
}
/// <summary>
2014-08-18 13:29:39 -04:00
/// Return a list of target platforms for the monolithic without cook
/// </summary>
/// <returns>a list of target platforms for the monolithic without cook</returns>
public virtual List < UnrealTargetPlatform > GUBP_GetBuildOnlyPlatforms_MonolithicOnly ( UnrealTargetPlatform HostPlatform )
{
var Result = new List < UnrealTargetPlatform > { } ;
return Result ;
}
/// <summary>
2014-03-14 14:13:41 -04:00
/// Return a list of configs for target platforms for the monolithic
/// </summary>
/// <returns>a list of configs for a target platforms for the monolithic</returns>
public virtual List < UnrealTargetConfiguration > GUBP_GetConfigs_MonolithicOnly ( UnrealTargetPlatform HostPlatform , UnrealTargetPlatform Platform )
{
return new List < UnrealTargetConfiguration > { UnrealTargetConfiguration . Development } ;
}
2014-05-01 07:33:06 -04:00
/// <summary>
2015-03-21 15:21:14 -04:00
/// Return a list of configs which are precompiled for the given target platform
/// </summary>
/// <returns>a list of configs for a target platforms for the monolithic</returns>
public virtual List < UnrealTargetConfiguration > GUBP_GetConfigsForPrecompiledBuilds_MonolithicOnly ( UnrealTargetPlatform HostPlatform , UnrealTargetPlatform Platform )
{
return new List < UnrealTargetConfiguration > ( ) ;
}
/// <summary>
2014-05-01 07:33:06 -04:00
/// Return a list of configs for target platforms for formal builds
/// </summary>
/// <returns>a list of configs for a target platforms for the monolithic</returns>
2014-06-03 10:37:01 -04:00
[Obsolete]
2014-05-01 07:33:06 -04:00
public virtual List < UnrealTargetConfiguration > GUBP_GetConfigsForFormalBuilds_MonolithicOnly ( UnrealTargetPlatform HostPlatform , UnrealTargetPlatform Platform )
{
return new List < UnrealTargetConfiguration > ( ) ;
}
2014-03-14 14:13:41 -04:00
2014-06-03 10:37:01 -04:00
public class GUBPFormalBuild
{
public UnrealTargetPlatform TargetPlatform = UnrealTargetPlatform . Unknown ;
public UnrealTargetConfiguration TargetConfig = UnrealTargetConfiguration . Unknown ;
public bool bTest = false ;
2015-03-13 09:50:42 -04:00
public bool bBeforeTrigger = false ;
public GUBPFormalBuild ( UnrealTargetPlatform InTargetPlatform , UnrealTargetConfiguration InTargetConfig , bool bInTest = false , bool bInBeforeTrigger = false )
2014-06-03 10:37:01 -04:00
{
TargetPlatform = InTargetPlatform ;
TargetConfig = InTargetConfig ;
bTest = bInTest ;
2015-03-13 09:50:42 -04:00
bBeforeTrigger = bInBeforeTrigger ;
2014-06-03 10:37:01 -04:00
}
}
/// <summary>
/// Return a list of formal builds
/// </summary>
/// <returns>a list of formal builds</returns>
public virtual List < GUBPFormalBuild > GUBP_GetConfigsForFormalBuilds_MonolithicOnly ( UnrealTargetPlatform HostPlatform )
{
return new List < GUBPFormalBuild > ( ) ;
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Return true if this target should be included in a promotion and indicate shared or not
/// </summary>
/// <returns>if this target should be included in a promotion.</returns>
public class GUBPProjectOptions
{
public bool bIsPromotable = false ;
2015-06-09 13:21:22 -04:00
public bool bBuildAnyway = false ;
2014-03-14 14:13:41 -04:00
public bool bSeparateGamePromotion = false ;
public bool bTestWithShared = false ;
public bool bIsMassive = false ;
2014-04-23 18:40:36 -04:00
public bool bCustomWorkflowForPromotion = false ;
2015-04-16 13:30:21 -04:00
public bool bIsNonCode = false ;
2015-04-17 08:13:57 -04:00
public bool bPromoteEditorOnly = true ;
2015-04-17 08:15:17 -04:00
public string GroupName = null ;
2015-04-17 08:13:57 -04:00
}
2014-03-14 14:13:41 -04:00
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
2015-01-08 13:30:35 -05:00
// tweaked to include FP_ folders too - which are temporary
if ( ! String . IsNullOrEmpty ( TargetName ) & & ( TargetName . StartsWith ( "TP_" ) | | TargetName . StartsWith ( "FP_" ) ) )
2014-03-14 14:13:41 -04:00
{
Result . bTestWithShared = true ;
2015-04-17 08:15:17 -04:00
Result . GroupName = "Templates" ;
2014-03-14 14:13:41 -04:00
}
return Result ;
}
/// <summary>
/// Return a list of the non-code projects to test
/// </summary>
/// <returns>a list of the non-code projects to build cook and test</returns>
public virtual Dictionary < string , List < UnrealTargetPlatform > > GUBP_NonCodeProjects_BaseEditorTypeOnly ( UnrealTargetPlatform HostPlatform )
{
return new Dictionary < string , List < UnrealTargetPlatform > > ( ) ;
}
/// <summary>
2014-04-23 18:18:41 -04:00
/// Return a list of the non-code projects to make formal builds for
/// </summary>
/// <returns>a list of the non-code projects to build cook and test</returns>
2014-06-03 10:37:01 -04:00
[Obsolete]
2014-04-23 18:18:41 -04:00
public virtual Dictionary < string , List < KeyValuePair < UnrealTargetPlatform , UnrealTargetConfiguration > > > GUBP_NonCodeFormalBuilds_BaseEditorTypeOnly ( )
{
return new Dictionary < string , List < KeyValuePair < UnrealTargetPlatform , UnrealTargetConfiguration > > > ( ) ;
}
2014-06-03 10:37:01 -04:00
/// <summary>
/// Return a list of the non-code projects to make formal builds for
/// </summary>
/// <returns>a list of the non-code projects to build cook and test</returns>
public virtual Dictionary < string , List < GUBPFormalBuild > > GUBP_GetNonCodeFormalBuilds_BaseEditorTypeOnly ( )
{
return new Dictionary < string , List < GUBPFormalBuild > > ( ) ;
}
2014-04-23 18:18:41 -04:00
/// <summary>
2014-03-14 14:13:41 -04:00
/// Return a list of "test name", "UAT command" pairs for testing the editor
/// </summary>
public virtual Dictionary < string , string > GUBP_GetEditorTests_EditorTypeOnly ( UnrealTargetPlatform HostPlatform )
{
var MacOption = HostPlatform = = UnrealTargetPlatform . Mac ? " -Mac" : "" ;
var Result = new Dictionary < string , string > ( ) ;
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 ;
}
2014-05-29 17:24:55 -04:00
/// <summary>
/// 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.
/// </summary>
/// <param name="Branch">p4 root of the branch we are running</param>
[Obsolete]
public virtual string GUBP_GetGameFailureEMails_EditorTypeOnly ( string Branch )
{
return "" ;
}
/// <summary>
/// Allow the Game to set up emails for Promotable and Promotion
/// Obsolete. Included to avoid breaking existing projects.
/// </summary>
[Obsolete]
public virtual string GUBP_GetPromotionEMails_EditorTypeOnly ( string Branch )
{
return "" ;
}
2014-03-14 14:13:41 -04:00
/// <summary>
/// Return a list of "test name", "UAT command" pairs for testing a monolithic
/// </summary>
public virtual Dictionary < string , string > GUBP_GetGameTests_MonolithicOnly ( UnrealTargetPlatform HostPlatform , UnrealTargetPlatform AltHostPlatform , UnrealTargetPlatform Platform )
{
var Result = new Dictionary < string , string > ( ) ;
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 ;
}
/// <summary>
/// Return a list of "test name", "UAT command" pairs for testing a monolithic
/// </summary>
public virtual Dictionary < string , string > GUBP_GetClientServerTests_MonolithicOnly ( UnrealTargetPlatform HostPlatform , UnrealTargetPlatform AltHostPlatform , UnrealTargetPlatform ServerPlatform , UnrealTargetPlatform ClientPlatform )
{
var Result = new Dictionary < string , string > ( ) ;
2014-06-16 08:19:58 -04:00
#if false // needs work
2014-03-14 14:13:41 -04:00
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 ( ) ) ;
}
2014-06-16 08:19:58 -04:00
#endif
2014-03-14 14:13:41 -04:00
return Result ;
}
2014-09-25 10:11:15 -04:00
/// <summary>
/// Return additional parameters to cook commandlet
/// </summary>
public virtual string GUBP_AdditionalCookParameters ( UnrealTargetPlatform HostPlatform , string Platform )
{
return "" ;
}
/// <summary>
/// Return additional parameters to package commandlet
/// </summary>
public virtual string GUBP_AdditionalPackageParameters ( UnrealTargetPlatform HostPlatform , UnrealTargetPlatform Platform )
{
return "" ;
}
2014-10-08 10:49:02 -04:00
/// <summary>
/// Allow Cook Platform Override from a target file
/// </summary>
public virtual string GUBP_AlternateCookPlatform ( UnrealTargetPlatform HostPlatform , string Platform )
{
return "" ;
}
2015-04-10 06:02:22 -04:00
/// <summary>
/// Allow target module to override UHT code generation version.
/// </summary>
public virtual EGeneratedCodeVersion GetGeneratedCodeVersion ( )
{
return EGeneratedCodeVersion . None ;
}
2014-03-14 14:13:41 -04:00
}
2015-08-28 16:02:15 -04:00
public class RulesAssembly
{
2015-08-31 12:39:51 -04:00
/// <summary>
/// The parent rules assembly that this assembly inherits. Game assemblies inherit the engine assembly, and the engine assembly inherits nothing.
/// </summary>
private RulesAssembly Parent ;
/// <summary>
/// The compiled assembly
/// </summary>
2015-08-28 16:02:15 -04:00
private Assembly CompiledAssembly ;
2015-09-08 07:59:23 -04:00
/// <summary>
/// All the plugins included in this assembly
/// </summary>
private IReadOnlyList < PluginInfo > Plugins ;
2015-08-31 12:39:51 -04:00
/// <summary>
2015-08-28 16:02:15 -04:00
/// Maps module names to their actual xxx.Module.cs file on disk
2015-08-31 12:39:51 -04:00
/// </summary>
2015-09-08 07:59:23 -04:00
private Dictionary < string , FileReference > ModuleNameToModuleFile = new Dictionary < string , FileReference > ( StringComparer . InvariantCultureIgnoreCase ) ;
2015-08-28 16:02:15 -04:00
2015-08-31 12:39:51 -04:00
/// <summary>
2015-08-28 16:02:15 -04:00
/// Maps target names to their actual xxx.Target.cs file on disk
2015-08-31 12:39:51 -04:00
/// </summary>
2015-09-08 07:59:23 -04:00
private Dictionary < string , FileReference > TargetNameToTargetFile = new Dictionary < string , FileReference > ( StringComparer . InvariantCultureIgnoreCase ) ;
/// <summary>
/// Mapping from module file to its plugin info.
/// </summary>
private Dictionary < FileReference , PluginInfo > ModuleFileToPluginInfo ;
2015-08-28 16:02:15 -04:00
2015-09-09 10:47:39 -04:00
/// <summary>
/// Cache for whether a module has source code
/// </summary>
private Dictionary < FileReference , bool > ModuleHasSource = new Dictionary < FileReference , bool > ( ) ;
2015-08-28 16:02:15 -04:00
/// <summary>
/// Constructor. Compiles a rules assembly from the given source files.
/// </summary>
2015-09-08 07:59:23 -04:00
/// <param name="Plugins">All the plugins included in this assembly</param>
/// <param name="ModuleFiles">List of module files to compile</param>
/// <param name="TargetFiles">List of target files to compile</param>
/// <param name="ModuleFileToPluginInfo">Mapping of module file to the plugin that contains it</param>
2015-08-28 16:02:15 -04:00
/// <param name="AssemblyFileName">The output path for the compiled assembly</param>
2015-09-08 07:59:23 -04:00
/// <param name="Parent">The parent rules assembly</param>
public RulesAssembly ( IReadOnlyList < PluginInfo > Plugins , List < FileReference > ModuleFiles , List < FileReference > TargetFiles , Dictionary < FileReference , PluginInfo > ModuleFileToPluginInfo , FileReference AssemblyFileName , RulesAssembly Parent )
2015-08-28 16:02:15 -04:00
{
2015-09-08 07:59:23 -04:00
this . Plugins = Plugins ;
this . ModuleFileToPluginInfo = ModuleFileToPluginInfo ;
this . Parent = Parent ;
2015-08-31 12:39:51 -04:00
2015-08-28 16:02:15 -04:00
// Find all the source files
2015-09-03 08:47:24 -04:00
List < FileReference > AssemblySourceFiles = new List < FileReference > ( ) ;
2015-09-08 07:59:23 -04:00
AssemblySourceFiles . AddRange ( ModuleFiles ) ;
AssemblySourceFiles . AddRange ( TargetFiles ) ;
2015-08-28 16:02:15 -04:00
// Compile the assembly
2015-08-31 16:44:02 -04:00
if ( AssemblySourceFiles . Count > 0 )
{
CompiledAssembly = DynamicCompilation . CompileAndLoadAssembly ( AssemblyFileName , AssemblySourceFiles ) ;
}
2015-08-28 16:02:15 -04:00
// Setup the module map
2015-09-08 07:59:23 -04:00
foreach ( FileReference ModuleFile in ModuleFiles )
2015-08-28 16:02:15 -04:00
{
2015-09-08 07:59:23 -04:00
string ModuleName = ModuleFile . GetFileNameWithoutAnyExtensions ( ) ;
if ( ! ModuleNameToModuleFile . ContainsKey ( ModuleName ) )
2015-08-28 16:02:15 -04:00
{
2015-09-08 07:59:23 -04:00
ModuleNameToModuleFile . Add ( ModuleName , ModuleFile ) ;
2015-08-28 16:02:15 -04:00
}
}
// Setup the target map
2015-09-08 07:59:23 -04:00
foreach ( FileReference TargetFile in TargetFiles )
2015-08-28 16:02:15 -04:00
{
2015-09-08 07:59:23 -04:00
string TargetName = TargetFile . GetFileNameWithoutAnyExtensions ( ) ;
if ( ! TargetNameToTargetFile . ContainsKey ( TargetName ) )
2015-08-28 16:02:15 -04:00
{
2015-09-08 07:59:23 -04:00
TargetNameToTargetFile . Add ( TargetName , TargetFile ) ;
2015-08-28 16:02:15 -04:00
}
}
}
/// <summary>
2015-08-31 10:33:16 -04:00
/// Tries to get the filename that declared the given type
2015-08-28 16:02:15 -04:00
/// </summary>
2015-08-31 10:33:16 -04:00
/// <param name="ExistingType"></param>
/// <param name="FileName"></param>
/// <returns>True if the type was found, false otherwise</returns>
2015-09-08 07:59:23 -04:00
public bool TryGetFileNameFromType ( Type ExistingType , out FileReference File )
2015-08-28 16:02:15 -04:00
{
2015-08-31 10:33:16 -04:00
if ( ExistingType . Assembly = = CompiledAssembly )
2015-08-28 16:02:15 -04:00
{
2015-08-31 10:33:16 -04:00
string Name = ExistingType . Name ;
2015-09-08 07:59:23 -04:00
if ( ModuleNameToModuleFile . TryGetValue ( Name , out File ) )
2015-08-31 10:33:16 -04:00
{
return true ;
}
2015-09-08 07:59:23 -04:00
if ( TargetNameToTargetFile . TryGetValue ( Name , out File ) )
2015-08-31 10:33:16 -04:00
{
return true ;
}
2015-08-28 16:02:15 -04:00
}
2015-08-31 12:39:51 -04:00
else
{
2015-09-08 07:59:23 -04:00
if ( Parent ! = null & & Parent . TryGetFileNameFromType ( ExistingType , out File ) )
2015-08-31 12:39:51 -04:00
{
return true ;
}
}
2015-08-28 16:02:15 -04:00
2015-09-08 07:59:23 -04:00
File = null ;
2015-08-31 10:33:16 -04:00
return false ;
2015-08-28 16:02:15 -04:00
}
2015-08-31 10:33:16 -04:00
/// <summary>
/// Gets the source file containing rules for the given module
/// </summary>
/// <param name="ModuleName">The name of the module</param>
/// <returns>The filename containing rules for this module, or an empty string if not found</returns>
2015-09-03 08:47:24 -04:00
public FileReference GetModuleFileName ( string ModuleName )
2015-08-28 16:02:15 -04:00
{
2015-09-08 07:59:23 -04:00
FileReference ModuleFile ;
if ( ModuleNameToModuleFile . TryGetValue ( ModuleName , out ModuleFile ) )
2015-08-28 16:02:15 -04:00
{
2015-09-08 07:59:23 -04:00
return ModuleFile ;
2015-08-31 12:39:51 -04:00
}
else
{
2015-09-03 08:47:24 -04:00
return ( Parent = = null ) ? null : Parent . GetModuleFileName ( ModuleName ) ;
2015-08-28 16:02:15 -04:00
}
2015-08-31 10:33:16 -04:00
}
2015-08-28 16:02:15 -04:00
2015-08-31 10:33:16 -04:00
/// <summary>
/// Gets the source file containing rules for the given target
/// </summary>
/// <param name="TargetName">The name of the target</param>
/// <returns>The filename containing rules for this target, or an empty string if not found</returns>
2015-09-03 08:47:24 -04:00
public FileReference GetTargetFileName ( string TargetName )
2015-08-31 10:33:16 -04:00
{
2015-09-08 07:59:23 -04:00
FileReference TargetFile ;
if ( TargetNameToTargetFile . TryGetValue ( TargetName , out TargetFile ) )
2015-08-31 10:33:16 -04:00
{
2015-09-08 07:59:23 -04:00
return TargetFile ;
2015-08-31 12:39:51 -04:00
}
else
{
2015-09-03 08:47:24 -04:00
return ( Parent = = null ) ? null : Parent . GetTargetFileName ( TargetName ) ;
2015-08-31 10:33:16 -04:00
}
}
/// <summary>
/// Creates an instance of a module rules descriptor object for the specified module name
/// </summary>
/// <param name="ModuleName">Name of the module</param>
/// <param name="Target">Information about the target associated with this module</param>
/// <returns>Compiled module rule info</returns>
2015-09-03 08:47:24 -04:00
public ModuleRules CreateModuleRules ( string ModuleName , TargetInfo Target )
2015-08-31 10:33:16 -04:00
{
2015-09-03 08:47:24 -04:00
FileReference ModuleFileName ;
2015-08-31 10:33:16 -04:00
return CreateModuleRules ( ModuleName , Target , out ModuleFileName ) ;
2015-08-28 16:02:15 -04:00
}
/// <summary>
/// Creates an instance of a module rules descriptor object for the specified module name
/// </summary>
/// <param name="ModuleName">Name of the module</param>
/// <param name="Target">Information about the target associated with this module</param>
/// <param name="ModuleFileName">The original source file name for the Module.cs file for this module</param>
/// <returns>Compiled module rule info</returns>
2015-09-03 08:47:24 -04:00
public ModuleRules CreateModuleRules ( string ModuleName , TargetInfo Target , out FileReference ModuleFileName )
2015-08-28 16:02:15 -04:00
{
// 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
2015-09-08 07:59:23 -04:00
if ( ! ModuleNameToModuleFile . TryGetValue ( ModuleName , out ModuleFileName ) )
2015-08-28 16:02:15 -04:00
{
2015-08-31 12:39:51 -04:00
if ( Parent = = null )
{
throw new MissingModuleException ( ModuleName ) ;
}
else
{
return Parent . CreateModuleRules ( ModuleName , Target , out ModuleFileName ) ;
}
2015-08-28 16:02:15 -04:00
}
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 )
{
2015-09-08 16:42:08 -04:00
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 ( ) ) ;
2015-08-28 16:02:15 -04:00
}
// Have to do absolute here as this could be a project that is under the root
2015-09-03 08:47:24 -04:00
bool bProjectModule = UnrealBuildTool . HasUProjectFile ( ) & & ModuleFileName . IsUnderDirectory ( UnrealBuildTool . GetUProjectPath ( ) ) ;
2015-08-28 16:02:15 -04:00
if ( bProjectModule )
{
RulesObject . ReadAdditionalDependencies ( UnrealBuildTool . GetUProjectFile ( ) , ModuleName ) ;
}
// Validate rules object
{
if ( RulesObject . Type = = ModuleRules . ModuleType . CPlusPlus )
{
if ( RulesObject . PrivateAssemblyReferences . Count > 0 )
{
2015-09-08 16:42:08 -04:00
throw new BuildException ( "Module rules for '{0}' may not specify PrivateAssemblyReferences unless it is a CPlusPlusCLR module type." , CompiledAssembly . FullName ) ;
2015-08-28 16:02:15 -04:00
}
var InvalidDependencies = RulesObject . DynamicallyLoadedModuleNames . Intersect ( RulesObject . PublicDependencyModuleNames . Concat ( RulesObject . PrivateDependencyModuleNames ) ) . ToList ( ) ;
if ( InvalidDependencies . Count ! = 0 )
{
throw new BuildException ( "Module rules for '{0}' should not be dependent on modules which are also dynamically loaded: {1}" , ModuleName , String . Join ( ", " , InvalidDependencies ) ) ;
}
// Choose code optimization options based on module type (game/engine) if
// default optimization method is selected.
2015-09-03 08:47:24 -04:00
bool bIsEngineModule = ModuleFileName . IsUnderDirectory ( UnrealBuildTool . EngineDirectory ) ;
bool IsGamePluginModule = bProjectModule ? ModuleFileName . IsUnderDirectory ( DirectoryReference . Combine ( UnrealBuildTool . GetUProjectPath ( ) , "Plugins" ) ) : false ;
2015-08-28 16:02:15 -04:00
if ( RulesObject . OptimizeCode = = ModuleRules . CodeOptimization . Default )
{
// Engine/Source and Engine/Plugins are considered 'Engine' code...
if ( bIsEngineModule )
{
// Engine module - always optimize (except Debug).
RulesObject . OptimizeCode = ModuleRules . CodeOptimization . Always ;
}
else
{
// Game module - do not optimize in Debug and DebugGame builds.
RulesObject . OptimizeCode = ModuleRules . CodeOptimization . InNonDebugBuilds ;
}
}
// Disable shared PCHs for game modules by default (but not game plugins, since they won't depend on the game's PCH!)
if ( RulesObject . PCHUsage = = ModuleRules . PCHUsageMode . Default )
{
// Note that bIsEngineModule includes Engine/Plugins, so Engine/Plugins will use shared PCHs.
var IsProgramTarget = Target . Type ! = null & & Target . Type = = TargetRules . TargetType . Program ;
if ( bIsEngineModule | | IsProgramTarget | | IsGamePluginModule )
{
// Engine module or plugin module -- allow shared PCHs
RulesObject . PCHUsage = ModuleRules . PCHUsageMode . UseSharedPCHs ;
}
else
{
// Game module. Do not enable shared PCHs by default, because games usually have a large precompiled header of their own and compile times would suffer.
RulesObject . PCHUsage = ModuleRules . PCHUsageMode . NoSharedPCHs ;
}
}
}
2015-09-02 14:54:39 -04:00
2015-09-08 16:42:08 -04:00
// 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.
2015-09-02 14:54:39 -04:00
foreach ( var Dependency in RulesObject . RuntimeDependencies )
{
2015-09-08 16:42:08 -04:00
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 ) ;
}
}
2015-09-02 14:54:39 -04:00
}
2015-08-28 16:02:15 -04:00
}
return RulesObject ;
}
2015-08-31 10:33:16 -04:00
/// <summary>
/// Creates an instance of a module rules descriptor object for the specified module name
/// </summary>
/// <param name="ModuleName">Name of the module</param>
/// <param name="Target">Information about the target associated with this module</param>
/// <param name="Rules">Output </param>
/// <returns>Compiled module rule info</returns>
2015-09-03 08:47:24 -04:00
public bool TryCreateModuleRules ( string ModuleName , TargetInfo Target , out ModuleRules Rules )
2015-08-31 10:33:16 -04:00
{
2015-09-03 08:47:24 -04:00
if ( GetModuleFileName ( ModuleName ) = = null )
2015-08-31 10:33:16 -04:00
{
Rules = null ;
return false ;
}
else
{
Rules = CreateModuleRules ( ModuleName , Target ) ;
return true ;
}
}
/// <summary>
/// Determines whether the given module name is a game module (as opposed to an engine module)
/// </summary>
public bool IsGameModule ( string InModuleName )
{
2015-09-03 08:47:24 -04:00
FileReference ModuleFileName = GetModuleFileName ( InModuleName ) ;
return ( ModuleFileName ! = null & & ! ModuleFileName . IsUnderDirectory ( UnrealBuildTool . EngineDirectory ) ) ;
2015-08-31 10:33:16 -04:00
}
2015-08-28 16:02:15 -04:00
protected bool GetTargetTypeAndRulesInstance ( string InTargetName , TargetInfo InTarget , out System . Type OutRulesObjectType , out TargetRules OutRulesObject )
{
// The build module must define a type named '<TargetName>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 ;
}
2015-09-03 08:47:24 -04:00
/// <summary>
/// Creates a target rules object for the specified target name.
/// </summary>
/// <param name="TargetName">Name of the target</param>
/// <param name="Target">Information about the target associated with this target</param>
/// <returns>The build target rules for the specified target</returns>
public TargetRules CreateTargetRules ( string TargetName , TargetInfo Target , bool bInEditorRecompile )
{
FileReference TargetFileName ;
return CreateTargetRules ( TargetName , Target , bInEditorRecompile , out TargetFileName ) ;
}
2015-08-28 16:02:15 -04:00
/// <summary>
/// Creates a target rules object for the specified target name.
/// </summary>
/// <param name="TargetName">Name of the target</param>
/// <param name="Target">Information about the target associated with this target</param>
/// <param name="TargetFileName">The original source file name of the Target.cs file for this target</param>
/// <returns>The build target rules for the specified target</returns>
2015-09-03 08:47:24 -04:00
public TargetRules CreateTargetRules ( string TargetName , TargetInfo Target , bool bInEditorRecompile , out FileReference TargetFileName )
2015-08-28 16:02:15 -04:00
{
// Make sure the target file is known to us
2015-09-08 07:59:23 -04:00
bool bFoundTargetName = TargetNameToTargetFile . ContainsKey ( TargetName ) ;
2015-08-28 16:02:15 -04:00
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
2015-09-08 07:59:23 -04:00
bFoundTargetName = TargetNameToTargetFile . ContainsKey ( TargetName + "Editor" ) ;
2015-08-28 16:02:15 -04:00
if ( bFoundTargetName )
{
TargetName + = "Editor" ;
}
}
}
if ( bFoundTargetName = = false )
{
2015-08-31 12:39:51 -04:00
if ( Parent = = null )
2015-08-28 16:02:15 -04:00
{
2015-08-31 12:39:51 -04:00
// 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 ;
2015-08-28 16:02:15 -04:00
2015-08-31 12:39:51 -04:00
ExceptionMessage + = "Location: " + CompiledAssembly . Location + Environment . NewLine ;
ExceptionMessage + = "Target rules found:" + Environment . NewLine ;
2015-09-08 07:59:23 -04:00
foreach ( KeyValuePair < string , FileReference > entry in TargetNameToTargetFile )
2015-08-31 12:39:51 -04:00
{
ExceptionMessage + = "\t" + entry . Key + " - " + entry . Value + Environment . NewLine ;
}
throw new BuildException ( ExceptionMessage ) ;
}
else
{
return Parent . CreateTargetRules ( TargetName , Target , bInEditorRecompile , out TargetFileName ) ;
}
2015-08-28 16:02:15 -04:00
}
// Return the target file name to the caller
2015-09-08 07:59:23 -04:00
TargetFileName = TargetNameToTargetFile [ TargetName ] ;
2015-08-28 16:02:15 -04:00
// 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 '<TargetName>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
2015-09-03 08:47:24 -04:00
string TargetSourceFolderString = TargetFileName . FullName ;
2015-08-28 16:02:15 -04:00
Int32 SourceFolderIndex = - 1 ;
if ( Utils . IsRunningOnMono )
{
2015-09-03 08:47:24 -04:00
TargetSourceFolderString = TargetSourceFolderString . Replace ( "\\" , "/" ) ;
SourceFolderIndex = TargetSourceFolderString . LastIndexOf ( "/Source/" , StringComparison . InvariantCultureIgnoreCase ) ;
2015-08-28 16:02:15 -04:00
}
else
{
2015-09-03 08:47:24 -04:00
TargetSourceFolderString = TargetSourceFolderString . Replace ( "/" , "\\" ) ;
SourceFolderIndex = TargetSourceFolderString . LastIndexOf ( "\\Source\\" , StringComparison . InvariantCultureIgnoreCase ) ;
2015-08-28 16:02:15 -04:00
}
if ( SourceFolderIndex ! = - 1 )
{
2015-09-03 08:47:24 -04:00
DirectoryReference TargetSourceFolder = new DirectoryReference ( TargetSourceFolderString . Substring ( 0 , SourceFolderIndex + 8 ) ) ;
2015-09-08 07:59:23 -04:00
foreach ( KeyValuePair < string , FileReference > CheckEntry in TargetNameToTargetFile )
2015-08-28 16:02:15 -04:00
{
2015-09-03 08:47:24 -04:00
if ( CheckEntry . Value . IsUnderDirectory ( TargetSourceFolder ) )
2015-08-28 16:02:15 -04:00
{
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 ;
}
2015-09-08 07:59:23 -04:00
/// <summary>
/// Enumerates all the plugins that are available
/// </summary>
/// <returns></returns>
public IEnumerable < PluginInfo > EnumeratePlugins ( )
{
if ( Parent = = null )
{
return Plugins ;
}
else
{
return Plugins . Concat ( Parent . EnumeratePlugins ( ) ) ;
}
}
/// <summary>
/// Tries to find the PluginInfo associated with a given module file
/// </summary>
/// <param name="ModuleFile">The module to search for</param>
/// <param name="Plugin">The matching plugin info, or null.</param>
/// <returns>True if the module belongs to a plugin</returns>
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 ) ;
}
}
2015-09-09 10:47:39 -04:00
/// <summary>
/// Determines if a module in this rules assembly has source code.
/// </summary>
/// <param name="ModuleName">Name of the module to check</param>
/// <returns>True if the module has source files, false if the module was not found, or does not have source files.</returns>
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 ) ;
}
2015-08-28 16:02:15 -04:00
}
2014-03-14 14:13:41 -04:00
2014-12-11 03:13:57 -05:00
public class RulesCompiler
2014-03-14 14:13:41 -04:00
{
/// <summary>
2015-09-06 12:49:32 -04:00
/// Enum for types of rules files. Should match extensions in RulesFileExtensions.
2014-03-14 14:13:41 -04:00
/// </summary>
public enum RulesFileType
{
Module ,
Target ,
Automation ,
2014-12-11 03:13:57 -05:00
AutomationModule
2014-03-14 14:13:41 -04:00
}
2015-09-06 12:49:32 -04:00
/// <summary>
/// Cached list of rules files in each directory of each type
/// </summary>
2014-03-14 14:13:41 -04:00
class RulesFileCache
{
2015-09-06 12:49:32 -04:00
public List < FileReference > ModuleRules = new List < FileReference > ( ) ;
public List < FileReference > TargetRules = new List < FileReference > ( ) ;
public List < FileReference > AutomationModules = new List < FileReference > ( ) ;
2014-03-14 14:13:41 -04:00
}
/// 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.
2015-09-03 08:47:24 -04:00
static Dictionary < DirectoryReference , RulesFileCache > RootFolderToRulesFileCache = new Dictionary < DirectoryReference , RulesFileCache > ( ) ;
2014-03-14 14:13:41 -04:00
2015-09-03 08:47:24 -04:00
public static List < FileReference > FindAllRulesSourceFiles ( RulesFileType RulesFileType , List < DirectoryReference > GameFolders , List < FileReference > ForeignPlugins , List < DirectoryReference > AdditionalSearchPaths , bool bIncludeEngine = true )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
List < DirectoryReference > Folders = new List < DirectoryReference > ( ) ;
2014-03-14 14:13:41 -04:00
// Add all engine source (including third party source)
2015-08-31 12:39:51 -04:00
if ( bIncludeEngine )
{
2015-09-03 08:47:24 -04:00
Folders . Add ( UnrealBuildTool . EngineSourceDirectory ) ;
2015-08-31 12:39:51 -04:00
}
2014-03-14 14:13:41 -04:00
// @todo plugin: Disallow modules from including plugin modules as dependency modules? (except when the module is part of that plugin)
2015-04-22 09:37:04 -04:00
// Get all the root folders for plugins
2015-09-03 08:47:24 -04:00
List < DirectoryReference > RootFolders = new List < DirectoryReference > ( ) ;
2015-08-31 12:39:51 -04:00
if ( bIncludeEngine )
{
2015-09-03 08:47:24 -04:00
RootFolders . Add ( UnrealBuildTool . EngineDirectory ) ;
2015-08-31 12:39:51 -04:00
}
if ( GameFolders ! = null )
{
RootFolders . AddRange ( GameFolders ) ;
}
2015-04-22 09:37:04 -04:00
// Find all the plugin source directories
2015-09-03 08:47:24 -04:00
foreach ( DirectoryReference RootFolder in RootFolders )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
DirectoryReference PluginsFolder = DirectoryReference . Combine ( RootFolder , "Plugins" ) ;
foreach ( FileReference PluginFile in Plugins . EnumeratePlugins ( PluginsFolder ) )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
Folders . Add ( DirectoryReference . Combine ( PluginFile . Directory , "Source" ) ) ;
2014-03-14 14:13:41 -04:00
}
}
2015-05-01 10:58:14 -04:00
// Add all the extra plugin folders
if ( ForeignPlugins ! = null )
{
2015-09-03 08:47:24 -04:00
foreach ( FileReference ForeignPlugin in ForeignPlugins )
2015-05-01 10:58:14 -04:00
{
2015-09-03 08:47:24 -04:00
Folders . Add ( DirectoryReference . Combine ( ForeignPlugin . Directory , "Source" ) ) ;
2015-05-01 10:58:14 -04:00
}
}
2014-03-14 14:13:41 -04:00
// Add in the game folders to search
2015-08-31 10:33:16 -04:00
if ( GameFolders ! = null )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
foreach ( DirectoryReference GameFolder in GameFolders )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
DirectoryReference GameSourceFolder = DirectoryReference . Combine ( GameFolder , "Source" ) ;
Folders . Add ( GameSourceFolder ) ;
DirectoryReference GameIntermediateSourceFolder = DirectoryReference . Combine ( GameFolder , "Intermediate" , "Source" ) ;
2015-02-27 16:10:22 -05:00
Folders . Add ( GameIntermediateSourceFolder ) ;
2014-03-14 14:13:41 -04:00
}
}
// Process the additional search path, if sent in
if ( AdditionalSearchPaths ! = null )
{
foreach ( var AdditionalSearchPath in AdditionalSearchPaths )
{
2015-09-03 08:47:24 -04:00
if ( AdditionalSearchPath ! = null )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
if ( AdditionalSearchPath . Exists ( ) )
2014-03-14 14:13:41 -04:00
{
Folders . Add ( AdditionalSearchPath ) ;
}
else
{
throw new BuildException ( "Couldn't find AdditionalSearchPath for rules source files '{0}'" , AdditionalSearchPath ) ;
}
}
}
}
// Iterate over all the folders to check
2015-09-03 08:47:24 -04:00
List < FileReference > SourceFiles = new List < FileReference > ( ) ;
2015-09-06 12:49:32 -04:00
HashSet < FileReference > UniqueSourceFiles = new HashSet < FileReference > ( ) ;
2015-09-03 08:47:24 -04:00
foreach ( DirectoryReference Folder in Folders )
2014-03-14 14:13:41 -04:00
{
2015-09-08 07:59:23 -04:00
IReadOnlyList < FileReference > SourceFilesForFolder = FindAllRulesFiles ( Folder , RulesFileType ) ;
2015-09-06 12:49:32 -04:00
foreach ( FileReference SourceFile in SourceFilesForFolder )
{
if ( UniqueSourceFiles . Add ( SourceFile ) )
2014-03-14 14:13:41 -04:00
{
2015-09-06 12:49:32 -04:00
SourceFiles . Add ( SourceFile ) ;
2014-03-14 14:13:41 -04:00
}
}
}
return SourceFiles ;
}
2015-09-08 07:59:23 -04:00
private static IReadOnlyList < FileReference > 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 ) ;
}
}
2015-09-06 12:49:32 -04:00
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 ) ;
}
}
}
2015-08-31 12:39:51 -04:00
/// <summary>
/// The cached rules assembly for engine modules and targets.
/// </summary>
private static RulesAssembly EngineRulesAssembly ;
2015-05-08 15:19:00 -04:00
/// 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
2015-09-03 08:47:24 -04:00
private static Dictionary < FileReference , RulesAssembly > LoadedAssemblyMap = new Dictionary < FileReference , RulesAssembly > ( ) ;
2014-03-14 14:13:41 -04:00
2015-08-31 12:39:51 -04:00
/// <summary>
/// Creates the engine rules assembly
/// </summary>
/// <param name="ForeignPlugins">List of plugins to include in this assembly</param>
/// <returns>New rules assembly</returns>
public static RulesAssembly CreateEngineRulesAssembly ( )
{
if ( EngineRulesAssembly = = null )
{
// Find all the rules files
2015-09-08 07:59:23 -04:00
List < FileReference > ModuleFiles = new List < FileReference > ( FindAllRulesFiles ( UnrealBuildTool . EngineSourceDirectory , RulesFileType . Module ) ) ;
List < FileReference > TargetFiles = new List < FileReference > ( FindAllRulesFiles ( UnrealBuildTool . EngineSourceDirectory , RulesFileType . Target ) ) ;
// Add all the plugin modules too
IReadOnlyList < PluginInfo > EnginePlugins = Plugins . ReadEnginePlugins ( UnrealBuildTool . EngineDirectory ) ;
Dictionary < FileReference , PluginInfo > ModuleFileToPluginInfo = new Dictionary < FileReference , PluginInfo > ( ) ;
FindModuleRulesForPlugins ( EnginePlugins , ModuleFiles , ModuleFileToPluginInfo ) ;
2015-08-31 12:39:51 -04:00
// Create a path to the assembly that we'll either load or compile
2015-09-03 08:47:24 -04:00
FileReference AssemblyFileName = FileReference . Combine ( UnrealBuildTool . EngineDirectory , BuildConfiguration . BaseIntermediateFolder , "BuildRules" , "UE4Rules.dll" ) ;
2015-09-08 07:59:23 -04:00
EngineRulesAssembly = new RulesAssembly ( EnginePlugins , ModuleFiles , TargetFiles , ModuleFileToPluginInfo , AssemblyFileName , null ) ;
2015-08-31 12:39:51 -04:00
}
return EngineRulesAssembly ;
}
2015-08-31 10:33:16 -04:00
/// <summary>
/// Creates a rules assembly with the given parameters.
/// </summary>
/// <param name="ProjectFileName">The project file to create rules for. Null for the engine.</param>
/// <param name="ForeignPlugins">List of foreign plugin folders to include in the assembly. May be null.</param>
2015-09-03 08:47:24 -04:00
public static RulesAssembly CreateProjectRulesAssembly ( FileReference ProjectFileName )
2014-03-14 14:13:41 -04:00
{
2015-08-31 10:33:16 -04:00
// Check if there's an existing assembly for this project
2015-08-31 12:39:51 -04:00
RulesAssembly ProjectRulesAssembly ;
2015-09-03 08:47:24 -04:00
if ( ! LoadedAssemblyMap . TryGetValue ( ProjectFileName , out ProjectRulesAssembly ) )
2014-03-14 14:13:41 -04:00
{
2015-08-31 12:39:51 -04:00
// Create the engine rules assembly
RulesAssembly Parent = CreateEngineRulesAssembly ( ) ;
2014-03-14 14:13:41 -04:00
2015-09-08 07:59:23 -04:00
// Find all the rules under the project source directory
DirectoryReference ProjectDirectory = ProjectFileName . Directory ;
DirectoryReference ProjectSourceDirectory = DirectoryReference . Combine ( ProjectDirectory , "Source" ) ;
List < FileReference > ModuleFiles = new List < FileReference > ( FindAllRulesFiles ( ProjectSourceDirectory , RulesFileType . Module ) ) ;
List < FileReference > TargetFiles = new List < FileReference > ( FindAllRulesFiles ( ProjectSourceDirectory , RulesFileType . Target ) ) ;
// Find all the project plugins
IReadOnlyList < PluginInfo > ProjectPlugins = Plugins . ReadProjectPlugins ( ProjectFileName . Directory ) ;
Dictionary < FileReference , PluginInfo > ModuleFileToPluginInfo = new Dictionary < FileReference , PluginInfo > ( ) ;
FindModuleRulesForPlugins ( ProjectPlugins , ModuleFiles , ModuleFileToPluginInfo ) ;
2015-05-08 15:19:00 -04:00
// Add the games project's intermediate source folder
2015-09-08 07:59:23 -04:00
DirectoryReference ProjectIntermediateSourceDirectory = DirectoryReference . Combine ( ProjectDirectory , "Intermediate" , "Source" ) ;
2015-09-03 08:47:24 -04:00
if ( ProjectIntermediateSourceDirectory . Exists ( ) )
2015-05-08 15:19:00 -04:00
{
2015-09-08 07:59:23 -04:00
TargetFiles . AddRange ( FindAllRulesFiles ( ProjectIntermediateSourceDirectory , RulesFileType . Target ) ) ;
2015-05-08 15:19:00 -04:00
}
2015-08-31 12:39:51 -04:00
// Compile the assembly
2015-09-08 07:59:23 -04:00
FileReference AssemblyFileName = FileReference . Combine ( ProjectDirectory , BuildConfiguration . BaseIntermediateFolder , "BuildRules" , ProjectFileName . GetFileNameWithoutExtension ( ) + "ModuleRules.dll" ) ;
ProjectRulesAssembly = new RulesAssembly ( ProjectPlugins , ModuleFiles , TargetFiles , ModuleFileToPluginInfo , AssemblyFileName , Parent ) ;
2015-09-03 08:47:24 -04:00
LoadedAssemblyMap . Add ( ProjectFileName , ProjectRulesAssembly ) ;
2015-05-08 15:19:00 -04:00
}
2015-08-31 12:39:51 -04:00
return ProjectRulesAssembly ;
}
2015-08-31 10:33:16 -04:00
2015-08-31 12:39:51 -04:00
/// <summary>
/// Creates a rules assembly with the given parameters.
/// </summary>
/// <param name="ProjectFileName">The project file to create rules for. Null for the engine.</param>
/// <param name="ForeignPlugins">List of foreign plugin folders to include in the assembly. May be null.</param>
2015-09-03 08:47:24 -04:00
public static RulesAssembly CreatePluginRulesAssembly ( FileReference PluginFileName , RulesAssembly Parent )
2015-08-31 12:39:51 -04:00
{
// Check if there's an existing assembly for this project
RulesAssembly PluginRulesAssembly ;
2015-09-03 08:47:24 -04:00
if ( ! LoadedAssemblyMap . TryGetValue ( PluginFileName , out PluginRulesAssembly ) )
2015-08-31 12:39:51 -04:00
{
// Find all the rules source files
2015-09-08 07:59:23 -04:00
List < FileReference > ModuleFiles = new List < FileReference > ( ) ;
List < FileReference > TargetFiles = new List < FileReference > ( ) ;
// Create a list of plugins for this assembly. If it already exists in the parent assembly, just create an empty assembly.
List < PluginInfo > ForeignPlugins = new List < PluginInfo > ( ) ;
if ( Parent = = null | | ! Parent . EnumeratePlugins ( ) . Any ( x = > x . File = = PluginFileName ) )
{
ForeignPlugins . Add ( new PluginInfo ( PluginFileName , PluginLoadedFrom . GameProject ) ) ;
}
// Find all the modules
Dictionary < FileReference , PluginInfo > ModuleFileToPluginInfo = new Dictionary < FileReference , PluginInfo > ( ) ;
FindModuleRulesForPlugins ( ForeignPlugins , ModuleFiles , ModuleFileToPluginInfo ) ;
2015-05-08 15:19:00 -04:00
2015-08-31 12:39:51 -04:00
// Compile the assembly
2015-09-03 08:47:24 -04:00
FileReference AssemblyFileName = FileReference . Combine ( PluginFileName . Directory , BuildConfiguration . BaseIntermediateFolder , "BuildRules" , Path . GetFileNameWithoutExtension ( PluginFileName . FullName ) + "ModuleRules.dll" ) ;
2015-09-08 07:59:23 -04:00
PluginRulesAssembly = new RulesAssembly ( ForeignPlugins , ModuleFiles , TargetFiles , ModuleFileToPluginInfo , AssemblyFileName , Parent ) ;
2015-09-03 08:47:24 -04:00
LoadedAssemblyMap . Add ( PluginFileName , PluginRulesAssembly ) ;
2015-08-31 12:39:51 -04:00
}
return PluginRulesAssembly ;
2014-03-14 14:13:41 -04:00
}
2015-09-08 07:59:23 -04:00
/// <summary>
/// Finds all the module rules for plugins under the given directory.
/// </summary>
/// <param name="PluginsDirectory">The directory to search</param>
/// <param name="ModuleFiles">List of module files to be populated</param>
/// <param name="ModuleFileToPluginFile">Dictionary which is filled with mappings from the module file to its corresponding plugin file</param>
private static void FindModuleRulesForPlugins ( IReadOnlyList < PluginInfo > Plugins , List < FileReference > ModuleFiles , Dictionary < FileReference , PluginInfo > ModuleFileToPluginInfo )
{
foreach ( PluginInfo Plugin in Plugins )
{
IReadOnlyList < FileReference > PluginModuleFiles = FindAllRulesFiles ( DirectoryReference . Combine ( Plugin . Directory , "Source" ) , RulesFileType . Module ) ;
foreach ( FileReference ModuleFile in PluginModuleFiles )
{
ModuleFiles . Add ( ModuleFile ) ;
ModuleFileToPluginInfo [ ModuleFile ] = Plugin ;
}
}
}
2014-03-14 14:13:41 -04:00
/// <summary>
2015-08-31 10:33:16 -04:00
/// Gets the filename that declares the given type.
2014-03-14 14:13:41 -04:00
/// </summary>
2015-08-31 10:33:16 -04:00
/// <param name="ExistingType">The type to search for.</param>
/// <returns>The filename that declared the given type, or null</returns>
public static string GetFileNameFromType ( Type ExistingType )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
FileReference FileName ;
2015-08-31 12:59:09 -04:00
if ( EngineRulesAssembly ! = null & & EngineRulesAssembly . TryGetFileNameFromType ( ExistingType , out FileName ) )
{
2015-09-03 08:47:24 -04:00
return FileName . FullName ;
2015-08-31 12:59:09 -04:00
}
2015-08-31 10:33:16 -04:00
foreach ( RulesAssembly RulesAssembly in LoadedAssemblyMap . Values )
2014-03-14 14:13:41 -04:00
{
2015-08-31 10:33:16 -04:00
if ( RulesAssembly . TryGetFileNameFromType ( ExistingType , out FileName ) )
{
2015-09-03 08:47:24 -04:00
return FileName . FullName ;
2015-08-31 10:33:16 -04:00
}
2014-03-14 14:13:41 -04:00
}
2015-08-31 10:33:16 -04:00
return null ;
2014-03-14 14:13:41 -04:00
}
}
}