Module is the basic element that constitutes Unreal. Each Module encapsulates and implements a set of functions, which can be used by other Modules. The entire Unreal Engine is driven by the combination of various Modules, and even the game project that we create is a separate Module.
So how does UE create and build these Modules? This is the main purpose of writing this article, to study Unreal’s build system and the various properties they support (Target and Module).
It is recommended to read my previous article before looking at this one: Build flow of the Unreal Engine4 project, which mainly outlines the build process of UE; this article is merely one part of the UE build system.
Anyone familiar with UE projects knows that when a C++ game project is created using UE, a Source folder is created under the project path, which by default contains the following files:
| 1 | Example\GWorld\Source>tree /a /f | 
Among them, *.Target.cs and *.Build.cs are the actual controllers of the Unreal build system. UBT determines the entire compilation environment by scanning these two files, and they are the focus of this article. Their responsibilities differ:
- *.Target.cscontrols the external compilation environment of the generated executable, known as the Target. For example, what- Type(Game/Client/Server/Editor/Program) is generated, whether to enable RTTI (- bForceEnableRTTI), how CRT is linked (- bUseStaticCRT), etc.
- *.Build.cscontrols the Module compilation process, managing dependencies, file inclusions, linking, macro definitions, and other related operations for the associated Module.- *.Build.csinforms the UE build system that it is a Module and specifies what actions to take during compilation.
In short: Anything related to the external compilation environment falls under *.target.cs and anything related to the Module itself falls under *.build.cs.
As a side note, the actual execution logic of the Module is defined in GWorld.h and GWorld.cpp, using IMPLEMENT_MODULE. All Modules in UE inherit from IModuleInterface and have the following interface:
| 1 | class IModuleInterface | 
The IModuleInterface drives the startup and shutdown of Modules, but generally, Game Module does not use this to control the game flow. Detailed content on this can be found in my previous article: UE4 Modules: Load and Startup
Target
Each project based on Unreal has a Tergat.cs, which contains a class definition inheriting from TargetRules; it is also recommended to associate this with a Module definition of the same name (though not strictly necessary) to avoid compilation errors for undefined Modules. Its implication is to compile the specified Module into the Target:
| 1 | UnrealBuildTool : error : Could not find definition for module 'GWorld' (referenced via GWorld.Target.cs) | 
The name of the Module associated with Target can be specified through ExtraModuleNames:
| 1 | public class GWorldTarget : TargetRules | 
The above specifies GWorld, so when UBT parses it, it will look for the definition of the GWorld Module in the GWorld.build.cs file, which contains the GWorld class definition. If it’s not found, the aforementioned Module undefined error will occur.
Note: The Module associated with
Targetis not just a simple specified name; all code that usesXXXX_APIis related to the Module’s name.
If I make the following change: ExtraModuleNames.AddRange( new string[] { "GWorldAAA" } );, then the changes required in all source files in the project are:
- Rename the existing GWorld.build.csfile toGWorldAAA.build.csand replace all occurrences ofGWorldin the file content withGWorldAAA;
- Rename all GWORLD_APIin the project’s header files toGWORLDAAA_API, because theXXX_APIexport symbols depend on theModuleName;
This is indeed a considerable amount of work, so it is advisable to keep the name specified in ExtraModuleNames the same as the Game Module.
Through the above, we understand how Target.cs is linked to Build.cs. In fact, the Game/Server/Client/Editor Targets can share the same Module; just set their ExtraModuleNames to be the same (if you want to write them separately for each Target type, that’s fine too).
The code for TargetRules can be found in UnrealBuildTools/Configuration/ModuleRules (and ReadOnlyTargetRules is defined there as well), where you can see the default values for supported parameters; UE’s documentation describing properties for Target: Targets.
However, the official documentation from UE only includes comments from the code, and some descriptions are a bit hard to grasp after reading; I will analyze the meanings of some TargetRule properties later on, so let’s leave that for now.
Type(TargetType)
The property Type in TargetRules is of type TargetType, defined in TargetRules.cs, which specifies what program the project is to build.
- Game - A standalone game which requires cooked data to run.
- Client - Same as Game, but does not include any server code. Useful for networked games.
- Server - Same as Game, but does not include any client code. Useful for dedicated servers in networked games.
- Editor - A target which extends the Unreal Editor.
- Program - A standalone utility program built on top of the Unreal Engine.
LinkType(TargetLinkType)
The LinkType in TargetRules is of type TargetLinkType, defined in TargetRules.cs, specifying the linking type of the project.
TargetLinkType has three enumeration values:
| 1 | /// <summary> | 
- TargetLinkType.Defaultis the default value for- LinkType; in this state, if the current- Target‘s- Typeis- Editor, it uses the- Modulartype, linking all modules as dynamic link libraries.
- TargetLinkType.Modular: Links Modules as dynamic link libraries.
- TargetLinkType.Monolithic: Links all modules into a single file (static link).
You can modify LinkType.
| 1 | /// <summary> | 
Name(string)
The name of the Target is a read-only property, coming from the project name.
Platform(UnrealTargetPlatform)
The Platform is of type UnrealTargetPlatform, which is an enumeration defined in UnrealBuildTool\Configuration\UEBuildTarget.cs.
It records the current Target’s platform information, such as Win32/Win64, and currently, the supported platforms in version UE_4.22 are:
| 1 | public enum UnrealTargetPlatform | 
In build.cs or target.cs, we can perform different actions based on the Platform.
For example:
| 1 | if(Target.Platform != UnrealTargetPlatform.Win32 && Target.Platform != UnrealTargetPlatform.Win64) | 
IsInPlatformGroup
This is a function bool IsInPlatformGroup(UnrealPlatformGroup Group), defined in TargetRules.cs, which is used to determine whether the current Platform is part of a specific group.
The parameter required is of the enumeration type UnrealTargetformGroup, which is defined in UEBuildTarget.cs:
| 1 | /// <summary> | 
Configuration(UnrealTargetConfiguration)
The current compilation configuration is of type UnrealTargetConfiguration, defined in UEBuildTarget.cs and constructed from the Configuration in VS, such as:
- Development
- Shipping
- DebugGame
- Debug
- Test
- Unknow
It is through this setting that UBT adds the following macros in the compilation environment:
| 1 | public override void SetUpConfigurationEnvironment(ReadOnlyTargetRules Target, CppCompileEnvironment GlobalCompileEnvironment, LinkEnvironment GlobalLinkEnvironment) | 
Architecture(string)
The architecture information of the platform on which it runs: x86/arm, etc.
CppStandard(CppStandardVersion)
Used to specify the C++ standard version used when compiling the project (available only in newer engine versions (4.23)).CppStandardVersion:
- Latast
- Cpp17
- Cpp14
This option essentially adds /std:c++xxx to the VS compilation options.
| 1 | void AppendCLArguments_CPP(CppCompileEnvironment CompileEnvironment, List<string> Arguments) | 
bUseDebugCRT(bool)
Used to control whether the output Runtime Library type is MT or MD; it also controls the addition of _DEBUG and NODEBUG macros:
| 1 | public override void SetUpConfigurationEnvironment(ReadOnlyTargetRules Target, CppCompileEnvironment GlobalCompileEnvironment, LinkEnvironment GlobalLinkEnvironment) | 
ProjectDefinitions(List) 
Macro definitions added for the current project, available throughout the project.
GlobalDefinitions(List) 
Macro definitions that can be used throughout the entire Target.
bShouldCompileAsDLL(bool)
Compilers the Target as a DLL, requiring LinkType to be Monolithic when true.
| 1 | /// <summary> | 
AdditionalCompilerArguments(String)
Arguments passed to the compiler.
AdditionalLinkerArguments(String)
Arguments passed to the linker.
bUsesSlate(bool)
Controls whether Slate related image resources are packaged into the pak during packaging.
bUseInlining(bool)
Whether to enable inline optimization; inlining essentially reduces function call overhead by expanding functions.
However, there may be some issues, such as in the engine’s Engine module, the FStreamingLevelsToConsider function, which does not export the symbol ENGINE_API.
In the Engine/World.h, the function IsStreamingLevelBeingConsidered calls it as defined in the header file:
| 1 | /** Returns true if StreamingLevel is part of the levels being considered for update */ | 
The function defined in the header file is implicitly inlined by the compiler, and if the call to World->IsStreamingLevelBeingConsidered is inlined in other modules, it leads to a linking error:
| 1 | Undefined symbols for architecture x86_64: | 
Because inlining expands to directly execute at the calling site, with the FStreamingLevelsToConsider symbol not being exported, it leads to a linking error.
To resolve such issues, you can either modify the engine to export the symbol or disable inlining in the target.cs:
| 1 | bOverrideBuildEnvironment = true; | 
In the UBT code, there is a check for its value, then actual compilation parameters will be added:
| 1 | // | 
Module
Similar to Target, each Unreal Module has a dedicated ModuleName.Build.cs where a dedicated ModuleName class is defined, inheriting from ModuleRules. The operations we perform during the module build are controlled through it.
Note: Whether it’s a Game Module or a Plugin Module, as long as it’s a project-dependent Module, it will receive the current
Targetinformation during compilation.
The code for ModuleRules can be found in UnrealBuildTools/Configuration/ModuleRules, and you can also look at the default values for supported properties there; UE’s official documentation describing Modules: Modules only contains comments in the code without actual examples. I will analyze some common properties in Build.cs in practical projects.
In *.Build.cs, you can retrieve property information from *.Target.cs through the ReadOnlyTargetRules Target parameter.
| 1 | using UnrealBuildTool; | 
Through the Target object, you can manage operations on the Module based on different platforms (Platform), architectures (Architecture), and other options (such as defining different macros, including different ThirdParty libraries, linking different libraries, etc.).
ModuleDirectory
- string ModuleDirectory: The absolute path to the project source path- PROJECT_NAME/Source/PROJECT_NAME.
EngineDirectory
- string EngineDirectory: The absolute path of the engine directory- Engine/in the current environment.
PublicAdditionalLibraries
Adding static link library files (note the difference from PublicLibraryPaths), generally used for linking third-party libraries.
| 1 | PublicAdditionalLibraries.AddRange( | 
For detailed information, you can read: Linking Static Libraries Using The Build System
It can also be used for DLL import libraries, in combination with
PublicDelayLoadDLLsandRuntimeDependencies.
PublicAdditionalShadowFiles
When executing remote compilation, specify the files that need to be copied to the remote server for the current module to ensure a successful link.
For instance, when remotely packaging for iOS platforms, you need to add the statically linked libraries that the current module depends on (e.g., the Game module depends on a plugin’s External module).
RuntimeDependencies
- list<RuntimeDependency> RuntimeDependencies: Files (- .so/- .dll, etc.) that the Module depends on at runtime, which will be copied to the storage directory during packaging.
When packaging for Windows, the files will be copied directly to the corresponding directory, but on Android, the files will be placed in the Apk package’s main.obb.webp.

PublicDelayLoadDLLs
- List<string> PublicDelayLoadDLLs: A list of DLLs to be loaded lazily, usually used for third-party libraries.
| 1 | // build.cs | 
The meaning is that the DLLs in the list are not loaded immediately upon program startup; instead, they are loaded once their symbols are needed for the first time. This allows them to be loaded in the StartupModule method as needed, without placing the DLL next to the executable.
| 1 | FString AbsPath = FileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*MyLibPath); | 
PS: For DLL usage with import libraries, the process is as follows:
- Use PublicAdditionalLibrariesto add the lib.
- Add the DLL to PublicDelayLoadDLLs.
- Use RuntimeDependenciesto copy the DLL during packaging.
- If the copied location is not the exe path, AddDllDirectoryandPushDllDirectoryshould be executed inStartupModuleto add the DLL’s path there.PublicDelayLoadDLLsonly needs the DLL name (xxxx.dll), no path is required.
I tested using DLL + import library and placed the DLL in a non-exe directory in GoogleInstantPreview. Here are some examples: ue4-plugin-GoogleInstanceIns.7z
PublicDefinitions
- List<string> PublicDefinitions: Adds public macro definitions to the current Module, equivalent to adding a preprocessor macro in traditional VS project settings.
Once analyzed by UBT, it produces a Definitions.PROJECT_NAME.h header file, which defines various macros.
| 1 | Intermediate\Build\Win64\UE4Editor\Development\ReflectionExample\Definitions.ReflectionExample.h | 
PublicSystemIncludePaths
- List<string> PublicSystemIncludePaths: The documentation states it is for adding system include paths, but unlike- PublicIncludePaths, it skips header file resolution checks (however, my testing shows that code included this way still detects the following errors (UE_4.20)):
| 1 | error : Expected mpack-platform.h to be first header included. | 
Note: If the paths are not specified, the default IncludePath is
Engine/Source.
For example:
| 1 | PublicSystemIncludePaths.AddRange( | 
This indicates the path is:
| 1 | D:\UnrealEngine\Epic\UE_4.21\Engine\Source\TEST_LIB | 
The default path for any specified *IncludePaths in *.build.cs is Engine/Source.
PrivateRuntimeLibraryPaths
- List<string> PrivateRuntimeLibraryPaths: The search path for runtime libraries, e.g.,- .soor- .dll.
PublicRuntimeLibraryPaths
- List<string> PublicRuntimeLibraryPaths: The search path for runtime libraries, e.g.,- .soor- .dll.
Because the default search paths for dynamic link libraries are:
- The system PATH;
- The current directory of the executable;
If our dynamic link library resides elsewhere, runtime errors will occur. We can add the library directories using PublicRuntimeLibraryPaths or PrivateRuntimeLibraryPaths.
PublicLibraryPaths
To add the path for linking library files, such as using in source code:
| 1 | 
You can use PublicLibraryPaths to add the dependent Lib.
DynamicallyLoadedModuleNames
- List<string> DynamicallyLoadedModuleNames: Adds Modules that need to be dynamically loaded at runtime, using functions like- FModuleManager::LoadModuleChecked<MODULE_TYPE>(TEXT("MODULE_NAME"))to start.
| 1 | // e.g | 
PublicDependencyModuleNames
- List<string> PublicDependencyModuleNames: Adds the source file dependencies of the executing Module, automatically adding the- Publicand- Privatesource file inclusions of the dependent Modules.
PrivateDependencyModuleNames
- List<string> PrivateDependencyModuleNames: Unlike- PublicDependencyModuleNames, it implies that the source files in the dependent Module can only be used in- Private.
For instance, if there is a Module A and another Module B, both structured with UE's Module/Public and Module/Private file systems.
- If B depends on A and the dependency was added using PrivateDependencyModuleNames, then A’s source files can only be used in B’sPrivatefiles, leading toNo such file or directoryerrors when used in B’sPublicfiles.
- Conversely, if A is added through PublicDependencyModuleNames, its files are available in both B’sPublicandPrivatedirectories.
In addition to the difference, it also affects modules dependent on B. When a module C depends on B, it can only access classes exposed from B’s PublicDependencyModule.
For example, C depends on B, and B depends on A; if C wants to access classes in A, there are two options:
- Add A module to C’s dependencies.
- Ensure B’s dependency on A is added to PublicDependencyModuleNames, allowing C indirect access to A.
Testing reveals that for Game Modules (PROJECT_NAME/Source/PROJECT_NAME.target.cs), it doesn’t significantly matter whether the dependent Module is added through PublicDependencyModuleNames or PrivateDependencyModuleNames; headers from the Module can still be used in Game Modules’ Public directory under certain conditions (unless the dependent module is set with bUsePrecompiled, which alters accessibility).
Relevant discussions:
- What is the difference between PublicDependencyModuleNames and PrivateDependencyModuleNames
- Explanation of Source Code folder structure?
bPrecompile and bUsePrecompiled
| 1 | /// <summary> | 
These two properties need to be used together.
Consider this scenario:
If we want to provide a Module A to others for use, but do not wish to disclose the complete code, what should we do?
In the traditional C++ domain, we would say: compile the code into a DLL and only release the headers and DLL to the users.
Exactly! The bPrecompile and bUsePrecompiled serve a similar purpose.
When compiling Module A, we add to its *.build.cs:
| 1 | public class A : ModuleRules | 
After completing the compilation, delete the Source/Private folder in Module A (ensure you back it up first), and remove the Intermediate folder while keeping the Binaries. Finally, open Module A’s A.build.cs, delete bPrecompile=true;, and add:
| 1 | public class A : ModuleRules | 
With this, our objective is achieved: the implementation code (Private) is not released, and pre-compiled binaries are provided, but this does not allow for static linking. If it’s only exposed for Blueprint usage, that can work, but using its symbols in other Modules will lead to undefined symbol errors.
OptimizeCode(CodeOptimization)
This property is used to control whether to enable code optimization for the current module. When debugging in VS, you may sometimes see “Variable has been optimized away and is therefore unavailable,” which is due to optimization.
It can be used to disable optimization:
| 1 | // build.cs | 
CodeOptimization supports several values, with the default being Default, enabling optimization:
- Never
- Default
- InNonDebugBuilds
- InShippingBuildsOnly
Related code:
| 1 | // UnrealBuildTool/Configutation/UEBuildModuleCPP.cs | 
This function is called in UEBuildModuleCPP.cs‘s CreateModuleCompileEnvironment, and the result is assigned to CppCompileEnvironment.bOptimizeCode, which is subsequently used in VCToolChain.cs:
| 1 | UnrealBuildTool\Platform\Windows\VCToolChain.cs | 
As seen, optimization is disabled by default in the Debug environment. In non-Debug configurations, whether to enable optimization is determined by the value of CompileEnvironment.bOptimizeCode.
Debugging results:
When using the default (with OptimizeCode = CodeOptimization.Default;):
When code optimization is disabled (OptimizeCode = CodeOptimization.Never;):
It is recommended to use OptimizeCode = CodeOptimization.InShippingBuildsOnly;.
Note: This option is the same as the setting for
Properties-Configuration-C/C++-Optimization-Optimizationin a regular C++ project in VS.
 ### bEnableUndefinedIdentifierWarnings (bool)
### bEnableUndefinedIdentifierWarnings (bool)
Whether to enable warnings for the use of undefined identifiers in preprocessed code #if.
| 1 | 
If this macro is undefined, enabling bEnableUndefinedIdentifierWarnings will produce the C4688 error.
The related code is defined in UBT’s code:
| 1 | // Source\Programs\UnrealBuildTool\Platform\Windows\VCToolChain.cs | 
bUseRTTI (bool)
UE4 disables RTTI by default, so when writing code with constructs like typeid, the following error will occur:
| 1 | In file included from C:\UnrealProject_\Source\GWorld\Private\Modules\Flibs\FLibIniConfigHelper.cpp:10: | 
The only solutions are to either remove the rtti related code or set bUseRTTI to true in the current Module‘s build.cs.
 
                      
                    