Files
UnrealEngineUWP/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildTarget.cs
Robert Manuszewski aa11e3bbbf Merging UE4-Pretest @ 2042161 to UE4
Change 1996384 by Andrew Brown:
	322252 - EDITOR: Asset picker displays incorrect text when there are no filter results.
Change 1996385 by Andrew Brown:
	321858 - CRASH: Assertion failed: (Index >= 0) Function: STransformViewportToolBar::GetLocationGridLabel() STextBlock::CacheDesiredSize()
Change 1996977 by Andrew Brown:
	309685 - UE4: Adding an event/renaming an event on an event track in Matinee does not update the MatineeActor node in blueprint
Change 2034873 by Jaroslaw Palczynski:
	More robust VS installation detection.
Change 2039693 by Jaroslaw Palczynski:
	327268 - RocketGDC: POSTLAUNCH: DEV: Make engine more robust against bad Visual Studio environment variables
Change 1978978 by Jaroslaw Surowiec:
	- Removed obsolete AllowEliminatingReferences from the FArchive
Change 2020326 by Maciej Mroz:
	pretest BP K2Node: RemovePinsFromOldPins function moved from K2Node to RemovePinsFromOldPins
Change 2017608 by Maciej Mroz:
	pretest Some changes in SFortMissionEventSelector caused by FPinTypeTreeInfo
Change 2017463 by Maciej Mroz:
	PinTypeSelector can lins unloaded UDStructs
Change 2019979 by Maciej Mroz:
	pretest BP: Crash when performing Diff against Depot with blueprints containing Format Text nodes
Change 2024469 by Maciej Mroz:
	MemberReference variable added to PinType. It's necessary for delegate's signature.
Change 2024049 by Maciej Mroz:
	HasExternalBlueprintDependencies added to UK2Node_DynamicCast
Change 2024586 by Maciej Mroz:
	FillSimpleMemberReference fix
Change 2024472 by Maciej Mroz:
	workaround for delegates signature in pintype removed.
Change 2023997 by Maciej Mroz:
	BP, UDStruc: Class UserDefinedStructEditorData added. It fixes many problems with undo/redo.
Change 2021934 by Maciej Mroz:
	typo in a comment
Change 2020355 by Maciej Mroz:
	Back out changelist 2020342
Change 2022178 by Maciej Mroz:
	CRASH: PRETEST: EDITOR: UDS: Crash when undo then redo new variable in struct that is used by blueprint
Change 2021958 by Maciej Mroz:
	CRASH: PRETEST: EDITOR: UDS: Crash using variable of a type of copied struct in blueprint
Change 1986247 by Maciej Mroz:
	User Defined Structures: circle dependency fixed. Early version.
Change 1985107 by Maciej Mroz:
	UserDefinedStruct cannot have a field of a non-native type
Change 1986278 by Maciej Mroz:
	pretest ensureMsgf in Struct::link
Change 1986250 by Maciej Mroz:
	User Defined Struct: Non native classes are accepted types od values in structures.
Change 1980955 by Maciej Mroz:
	Using AssetPtr and LazyPtr as UFunction parameter (intput or return) is explicitly disallowed.
Change 2041215 by Maciej Mroz:
	ttp331249 BLOCKER: PRETEST: UI: Survive the Storm is missing the Mission HUD.
Change 1984316 by Maciej Mroz:
	New User Defined Structure. WIP - there are still problems with circular dependencies.
Change 2011616 by Maciej Mroz:
	UserDefinedStructures - various problems fixed.
Change 2011609 by Maciej Mroz:
	more robust HasExternalBlueprintDependencies implementation
Change 2016697 by Maciej Mroz:
	pretest BP: UDStruct - default value propagation in cooked build
Change 2016288 by Maciej Mroz:
	pretest BP: UDStruct: Renaming variables wont break links from make/break nodes
Change 1987637 by Maciej Mroz:
	CustomStruct icons placeholders
Change 1987422 by Maciej Mroz:
	Better tooltips for variables in MyBlueprint
Change 1991387 by Maciej Mroz:
	UDStructures fixes:
Change 2029165 by Maciej Mroz:
	BP: better comment for incomatible pins
Change 2030016 by Maciej Mroz:
	8PRETEST: EDITOR: UDS: Defaults values aren't updated in struct type variables in blueprints
Change 2030017 by Maciej Mroz:
	Unused UDStructure code removed (PPF_UseDefaultsForUDStructures)
Change 2028856 by Maciej Mroz:
	BP: Pins with PC_Struct type are compatible only with exactly the same structure. (No derived structures are not handled as compatible).
Change 2026701 by Maciej Mroz:
	k2: odd error on an add item node within a function (see attached image in details)
Change 2028160 by Maciej Mroz:
	PRETEST: EDITOR: UDS: When deleting structures just after creating there is always some references in the memory
Change 2028165 by Maciej Mroz:
	BP: BreakHitResult function has proper icon.
Change 2033340 by Maciej Mroz:
	ttp330786 PRETEST: EDITOR: UDS: Changes of default values aren't apllied to breeak nodes for text type of variables
Change 2034255 by Maciej Mroz:
	EDITOR: UDS: Changes of default values aren't apllied to make nodes for text type of variables ttp#330620
Change 2037682 by Maciej Mroz:
	ttp331309 BLOCKER: PRETEST: CRASH: EDITOR: Crash occurs when performing Diff Against Depot on any Blueprint
Change 2033142 by Maciej Mroz:
	CreateDelegate Node uses internally FMemberReference. Refactor.
Change 2032329 by Maciej Mroz:
	ttp330608 CRASH: PRETEST: EDITOR: UDS: Crash when trying to use struct named 'Color' in blueprint
Change 2032420 by Maciej Mroz:
	ttp330620 PRETEST: EDITOR: UDS: Changes of default values aren't apllied to make nodes for text type of variables
Change 2033139 by Maciej Mroz:
	Functions generated from CustomEvents can be also identified by GUID
Change 2026631 by Maciej Mroz:
	BP. UDStruct: Invalid structs are handled better.
Change 2025344 by Maciej Mroz:
	UDStruct enabled by default
Change 2026672 by Maciej Mroz:
	EDITOR: BP: Can't easily remove 'pass-by-reference' pins on ReturnNodes
Change 2026411 by Maciej Mroz:
	ExposeOnSpawn updated, it supports UDStructs, custom native Structs, and it throws compiler error.
Change 2025342 by Maciej Mroz:
	GenerateBlueprintSkeleton moved from BLueprint::Serialize to RegenerateBlueprintClass, because SkeletonClass compilation requires all external dependencies to be loaded and linked.
Change 2025570 by Steve Robb:
	Moved dependency processing to its own function.
Change 2033235 by Steve Robb:
	String improvements
Change 2035830 by Steve Robb:
	Workaround for FriendsAndChat crash in Fortnite.
Change 2035115 by Steve Robb:
	UBT build time regression fixes.
Change 2034162 by Steve Robb:
	312775: UObject improvement: Ensure that *.generated.inl is included somewhere
Change 2034181 by Steve Robb:
	Removal of any references to .generated.inl
Change 2020165 by Steve Robb:
	BuildPublicAndPrivateUObjectHeaders factored out into its own function.
Change 2020187 by Steve Robb:
	CreateModuleCompileEnvironment function factored out.
Change 2020055 by Steve Robb:
	Refactoring of Unity.cs to remove complex and duplicate iteration.
Change 2020083 by Steve Robb:
	Another use of dictionary utilities.
Change 2031049 by Steve Robb:
	312775: UObject improvement: Ensure that *.generated.inl is included somewhere
Change 2025728 by Steve Robb:
	Refactored the application of a shared PCH file to multiple file into a single ApplySharedPCH function.
Change 2020068 by Steve Robb:
	A couple of helpful utility functions for populating dictionaries.
Change 2032307 by Steve Robb:
	312775: UObject improvement: Ensure that *.generated.inl is included somewhere

[CL 2054495 by Robert Manuszewski in Main branch]
2014-04-23 20:18:55 -04:00

2624 lines
102 KiB
C#

// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Xml;
namespace UnrealBuildTool
{
public enum UnrealTargetPlatform
{
Unknown,
Win32,
Win64,
Mac,
XboxOne,
PS4,
IOS,
Android,
WinRT,
WinRT_ARM,
HTML5,
Linux,
}
public enum UnrealPlatformGroup
{
Windows, // this group is just to lump Win32 and Win64 into Windows directories, removing the special Windows logic in MakeListOfUnsupportedPlatforms
Microsoft,
Apple,
Unix,
Android,
Sony,
/*
* These two groups can be further used to conditionally compile files for a given platform. e.g
* Core/Private/HTML5/Simulator/<VC tool chain files>
* Core/Private/HTML5/Device/<emscripten toolchain files>.
* Note: There's no default group - if the platform is not registered as device or simulator - both are rejected.
*/
Device,
Simulator,
}
public enum UnrealTargetConfiguration
{
Unknown,
Debug,
DebugGame,
Development,
Shipping,
Test,
}
public enum UnrealProjectType
{
CPlusPlus, // C++ or C++/CLI
CSharp, // C#
}
public enum ErrorMode
{
Ignore,
Suppress,
Check,
}
/** Helper class for holding project name, platform and config together. */
public class UnrealBuildConfig
{
/** Project to build. */
public string Name;
/** Platform to build the project for. */
public UnrealTargetPlatform Platform;
/** Config to build the project with. */
public UnrealTargetConfiguration Config;
/** Constructor */
public UnrealBuildConfig(string InName, UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfig)
{
Name = InName;
Platform = InPlatform;
Config = InConfig;
}
/** Overriden ToString() to make this class esier to read when debugging. */
public override string ToString()
{
return String.Format("{0}, {1}, {2}", Name, Platform, Config);
}
};
delegate UEBuildTarget TargetCreationDelegate();
/// <summary>
/// A container for a binary files (dll, exe) with its associated debug info.
/// </summary>
public class FileManifest
{
public readonly List<string> FileManifestItems = new List<string>();
public FileManifest()
{
}
public void AddFileName( string FilePath )
{
FileManifestItems.Add( Path.GetFullPath( FilePath ) );
}
public void AddBinaryNames(string OutputFilePath, string DebugInfoExtension)
{
// Only add unique files to the manifest. Multiple games have many shared binaries.
if( !FileManifestItems.Any( x => Path.GetFullPath( OutputFilePath ) == x ) )
{
string FullPath = Path.GetFullPath(OutputFilePath);
FileManifestItems.Add( FullPath );
if (!string.IsNullOrEmpty(DebugInfoExtension))
{
FileManifestItems.Add(Path.ChangeExtension(FullPath, DebugInfoExtension));
}
}
}
}
public class OnlyModule
{
public OnlyModule(string InitOnlyModuleName)
{
OnlyModuleName = InitOnlyModuleName;
OnlyModuleSuffix = String.Empty;
}
public OnlyModule(string InitOnlyModuleName, string InitOnlyModuleSuffix)
{
OnlyModuleName = InitOnlyModuleName;
OnlyModuleSuffix = InitOnlyModuleSuffix;
}
/** If building only a single module, this is the module name to build */
public readonly string OnlyModuleName;
/** When building only a single module, the optional suffix for the module file name */
public readonly string OnlyModuleSuffix;
}
public class UEBuildTarget
{
public string GetAppName()
{
return AppName;
}
public string GetTargetName()
{
return !String.IsNullOrEmpty(GameName) ? GameName : AppName;
}
public static UnrealTargetPlatform CPPTargetPlatformToUnrealTargetPlatform(CPPTargetPlatform InCPPPlatform)
{
switch (InCPPPlatform)
{
case CPPTargetPlatform.Win32: return UnrealTargetPlatform.Win32;
case CPPTargetPlatform.Win64: return UnrealTargetPlatform.Win64;
case CPPTargetPlatform.Mac: return UnrealTargetPlatform.Mac;
case CPPTargetPlatform.XboxOne: return UnrealTargetPlatform.XboxOne;
case CPPTargetPlatform.PS4: return UnrealTargetPlatform.PS4;
case CPPTargetPlatform.Android: return UnrealTargetPlatform.Android;
case CPPTargetPlatform.WinRT: return UnrealTargetPlatform.WinRT;
case CPPTargetPlatform.WinRT_ARM: return UnrealTargetPlatform.WinRT_ARM;
case CPPTargetPlatform.IOS: return UnrealTargetPlatform.IOS;
case CPPTargetPlatform.HTML5: return UnrealTargetPlatform.HTML5;
case CPPTargetPlatform.Linux: return UnrealTargetPlatform.Linux;
}
throw new BuildException("CPPTargetPlatformToUnrealTargetPlatform: Unknown CPPTargetPlatform {0}", InCPPPlatform.ToString());
}
public static UEBuildTarget CreateTarget(string[] SourceArguments)
{
TargetCreationDelegate TargetCreationDelegate = null;
var AdditionalDefinitions = new List<string>();
var Platform = UnrealTargetPlatform.Unknown;
var Configuration = UnrealTargetConfiguration.Unknown;
string RemoteRoot = null;
var OnlyModules = new List<OnlyModule>();
// Combine the two arrays of arguments
List<string> Arguments = new List<string>(SourceArguments.Length);
Arguments.AddRange(SourceArguments);
// If true, the recompile was launched by the editor.
// This means we actually have to
bool bIsEditorRecompile = false;
List<string> PossibleTargetNames = new List<string>();
for (int ArgumentIndex = 0; ArgumentIndex < Arguments.Count; ArgumentIndex++)
{
UnrealTargetPlatform ParsedPlatform = UEBuildPlatform.ConvertStringToPlatform(Arguments[ArgumentIndex]);
if (ParsedPlatform != UnrealTargetPlatform.Unknown)
{
Platform = ParsedPlatform;
}
else
{
switch (Arguments[ArgumentIndex].ToUpperInvariant())
{
case "-MODULE":
// Specifies a module to recompile. Can be specified more than once on the command-line to compile multiple specific modules.
{
if (ArgumentIndex + 1 >= Arguments.Count)
{
throw new BuildException("Expected module name after -Module argument, but found nothing.");
}
var OnlyModuleName = Arguments[++ArgumentIndex];
OnlyModules.Add(new OnlyModule(OnlyModuleName));
}
break;
case "-MODULEWITHSUFFIX":
{
// Specifies a module name to compile along with a suffix to append to the DLL file name. Can be specified more than once on the command-line to compile multiple specific modules.
if (ArgumentIndex + 2 >= Arguments.Count)
{
throw new BuildException("Expected module name and module suffix -ModuleWithSuffix argument");
}
var OnlyModuleName = Arguments[++ArgumentIndex];
var OnlyModuleSuffix = Arguments[++ArgumentIndex];
OnlyModules.Add(new OnlyModule(OnlyModuleName, OnlyModuleSuffix));
}
break;
// Configuration names:
case "DEBUG":
Configuration = UnrealTargetConfiguration.Debug;
break;
case "DEBUGGAME":
Configuration = UnrealTargetConfiguration.DebugGame;
break;
case "DEVELOPMENT":
Configuration = UnrealTargetConfiguration.Development;
break;
case "SHIPPING":
Configuration = UnrealTargetConfiguration.Shipping;
break;
case "TEST":
Configuration = UnrealTargetConfiguration.Test;
break;
// -Define <definition> adds a definition to the global C++ compilation environment.
case "-DEFINE":
if (ArgumentIndex + 1 >= Arguments.Count)
{
throw new BuildException("Expected path after -define argument, but found nothing.");
}
ArgumentIndex++;
AdditionalDefinitions.Add(Arguments[ArgumentIndex]);
break;
// -RemoteRoot <RemoteRoot> sets where the generated binaries are CookerSynced.
case "-REMOTEROOT":
if (ArgumentIndex + 1 >= Arguments.Count)
{
throw new BuildException("Expected path after -RemoteRoot argument, but found nothing.");
}
ArgumentIndex++;
if (Arguments[ArgumentIndex].StartsWith("xe:\\") == true)
{
RemoteRoot = Arguments[ArgumentIndex].Substring("xe:\\".Length);
}
else if (Arguments[ArgumentIndex].StartsWith("devkit:\\") == true)
{
RemoteRoot = Arguments[ArgumentIndex].Substring("devkit:\\".Length);
}
break;
// Disable editor support
case "-NOEDITOR":
UEBuildConfiguration.bBuildEditor = false;
break;
case "-NOEDITORONLYDATA":
UEBuildConfiguration.bBuildWithEditorOnlyData = false;
break;
case "-DEPLOY":
// Does nothing at the moment...
break;
case "-PROJECTFILES":
{
// Force platform to Win64 for building IntelliSense files
Platform = UnrealTargetPlatform.Win64;
// Force configuration to Development for IntelliSense
Configuration = UnrealTargetConfiguration.Development;
}
break;
case "-XCODEPROJECTFILE":
{
// @todo Mac: Don't want to force a platform/config for generated projects, in case they affect defines/includes (each project's individual configuration should be generated with correct settings)
// Force platform to Mac for building IntelliSense files
Platform = UnrealTargetPlatform.Mac;
// Force configuration to Development for IntelliSense
Configuration = UnrealTargetConfiguration.Development;
}
break;
case "-MAKEFILE":
{
// Force platform to Linux for building IntelliSense files
Platform = UnrealTargetPlatform.Linux;
// Force configuration to Development for IntelliSense
Configuration = UnrealTargetConfiguration.Development;
}
break;
case "-EDITORRECOMPILE":
{
bIsEditorRecompile = true;
}
break;
default:
PossibleTargetNames.Add(Arguments[ArgumentIndex]);
break;
}
}
}
if (Platform == UnrealTargetPlatform.Unknown)
{
throw new BuildException("Couldn't find platform name.");
}
if (Configuration == UnrealTargetConfiguration.Unknown)
{
throw new BuildException("Couldn't determine configuration name.");
}
if (TargetCreationDelegate != null)
{
return TargetCreationDelegate();
}
if (PossibleTargetNames.Count > 0)
{
// We have possible targets!
UEBuildTarget BuildTarget = null;
foreach (string PossibleTargetName in PossibleTargetNames)
{
// If running Rocket, the PossibleTargetName could contain a path
var TargetName = PossibleTargetName;
// If a project file was not specified see if we can find one
string CheckProjectFile = UProjectInfo.GetProjectForTarget(TargetName);
if (string.IsNullOrEmpty(CheckProjectFile) == false)
{
Log.TraceVerbose("Found project file for {0} - {1}", TargetName, CheckProjectFile);
if (UnrealBuildTool.HasUProjectFile() == false)
{
string NewProjectFilename = CheckProjectFile;
if (Path.IsPathRooted(NewProjectFilename) == false)
{
NewProjectFilename = Path.GetFullPath(NewProjectFilename);
}
NewProjectFilename = NewProjectFilename.Replace("\\", "/");
UnrealBuildTool.SetProjectFile(NewProjectFilename);
}
}
if (UnrealBuildTool.HasUProjectFile())
{
if( TargetName.Contains( "/" ) || TargetName.Contains( "\\" ) )
{
// Parse off the path
TargetName = Path.GetFileNameWithoutExtension( TargetName );
}
}
if( !ProjectFileGenerator.bGenerateProjectFiles )
{
// Configure the rules compiler
string PossibleAssemblyName = TargetName;
if (bIsEditorRecompile == true)
{
PossibleAssemblyName += "_EditorRecompile";
}
// Scan the disk to find source files for all known targets and modules, and generate "in memory" project
// file data that will be used to determine what to build
RulesCompiler.SetAssemblyNameAndGameFolders( PossibleAssemblyName, UEBuildTarget.DiscoverAllGameFolders() );
}
// Try getting it from the RulesCompiler
UEBuildTarget Target = RulesCompiler.CreateTarget(
TargetName:TargetName,
Target:new TargetInfo(Platform, Configuration),
InAdditionalDefinitions:AdditionalDefinitions,
InRemoteRoot:RemoteRoot,
InOnlyModules:OnlyModules,
bInEditorRecompile:bIsEditorRecompile);
if (Target == null)
{
if (UEBuildConfiguration.bCleanProject)
{
return null;
}
throw new BuildException( "Couldn't find target name {0}.", TargetName );
}
else
{
BuildTarget = Target;
break;
}
}
return BuildTarget;
}
throw new BuildException("No target name was specified on the command-line.");
}
/// Parses only the target platform and configuration from the specified command-line argument list
public static void ParsePlatformAndConfiguration(string[] SourceArguments,
out UnrealTargetPlatform Platform, out UnrealTargetConfiguration Configuration,
bool bThrowExceptionOnFailure = true)
{
Platform = UnrealTargetPlatform.Unknown;
Configuration = UnrealTargetConfiguration.Unknown;
foreach (var CurArgument in SourceArguments)
{
UnrealTargetPlatform ParsedPlatform = UEBuildPlatform.ConvertStringToPlatform(CurArgument);
if (ParsedPlatform != UnrealTargetPlatform.Unknown)
{
Platform = ParsedPlatform;
}
else
{
switch (CurArgument.ToUpperInvariant())
{
// Configuration names:
case "DEBUG":
Configuration = UnrealTargetConfiguration.Debug;
break;
case "DEBUGGAME":
Configuration = UnrealTargetConfiguration.DebugGame;
break;
case "DEVELOPMENT":
Configuration = UnrealTargetConfiguration.Development;
break;
case "SHIPPING":
Configuration = UnrealTargetConfiguration.Shipping;
break;
case "TEST":
Configuration = UnrealTargetConfiguration.Test;
break;
case "-PROJECTFILES":
// Force platform to Win64 and configuration to Development for building IntelliSense files
Platform = UnrealTargetPlatform.Win64;
Configuration = UnrealTargetConfiguration.Development;
break;
case "-XCODEPROJECTFILE":
// @todo Mac: Don't want to force a platform/config for generated projects, in case they affect defines/includes (each project's individual configuration should be generated with correct settings)
// Force platform to Mac and configuration to Development for building IntelliSense files
Platform = UnrealTargetPlatform.Mac;
Configuration = UnrealTargetConfiguration.Development;
break;
case "-MAKEFILE":
// Force platform to Linux and configuration to Development for building IntelliSense files
Platform = UnrealTargetPlatform.Linux;
Configuration = UnrealTargetConfiguration.Development;
break;
}
}
}
if (bThrowExceptionOnFailure == true)
{
if (Platform == UnrealTargetPlatform.Unknown)
{
throw new BuildException("Couldn't find platform name.");
}
if (Configuration == UnrealTargetConfiguration.Unknown)
{
throw new BuildException("Couldn't determine configuration name.");
}
}
}
/**
* Look for all folders ending in "Game" in the branch root that have a "Source" subfolder
* This is defined as a valid game
*/
public static List<string> DiscoverAllGameFolders()
{
List<string> AllGameFolders = new List<string>();
// Add all the normal game folders. The UProjectInfo list is already filtered for projects specified on the command line.
List<UProjectInfo> GameProjects = UProjectInfo.FilterGameProjects(true, null);
foreach (UProjectInfo GameProject in GameProjects)
{
AllGameFolders.Add(GameProject.Folder);
}
// @todo: Perversely, if we're not running rocket we need to add the Rocket root folder so that the Rocket build automation script can be found...
if (!UnrealBuildTool.RunningRocket())
{
string RocketFolder = "../../Rocket";
if (Directory.Exists(RocketFolder))
{
AllGameFolders.Add(RocketFolder);
}
}
return AllGameFolders;
}
/// <summary>
///
/// </summary>
/// <param name="InGameName"></param>
/// <param name="InRulesObject"></param>
/// <param name="InPlatform"></param>
/// <param name="InConfiguration"></param>
/// <returns></returns>
public static string GetBinaryBaseName(string InGameName, TargetRules InRulesObject, UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration, string InNameAppend)
{
//@todo. Allow failure to get build platform here?
bool bPlatformRequiresMonolithic = false;
UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(InPlatform, true);
if (BuildPlatform != null)
{
bPlatformRequiresMonolithic = BuildPlatform.ShouldCompileMonolithicBinary(InPlatform);
}
if (InRulesObject.ShouldCompileMonolithic(InPlatform, InConfiguration) || bPlatformRequiresMonolithic || (InRulesObject.Type == TargetRules.TargetType.RocketGame))
{
return InGameName;
}
else
{
return "UE4" + InNameAppend;
}
}
/** The target rules */
public TargetRules Rules = null;
/** The name of the application the target is part of. */
public string AppName;
/** The name of the game the target is part of - can be empty */
public string GameName;
/** Platform as defined by the VCProject and passed via the command line. Not the same as internal config names. */
public UnrealTargetPlatform Platform;
/** Target as defined by the VCProject and passed via the command line. Not necessarily the same as internal name. */
public UnrealTargetConfiguration Configuration;
/** Root directory for the active project. Typically contains the .uproject file, or the engine root. */
public string ProjectDirectory;
/** Default directory for intermediate files. Typically underneath ProjectDirectory. */
public string ProjectIntermediateDirectory;
/** Directory for engine intermediates. For an agnostic editor/game executable, this will be under the engine directory. For monolithic executables this will be the same as the project intermediate directory. */
public string EngineIntermediateDirectory;
/** Output path of final executable. */
public string OutputPath;
/** Remote path of the binary if it is to be synced with CookerSync */
public string RemoteRoot;
/** The C++ environment that all the environments used to compile UE-based modules are derived from. */
public CPPEnvironment GlobalCompileEnvironment = new CPPEnvironment();
/** The link environment all binary link environments are derived from. */
public LinkEnvironment GlobalLinkEnvironment = new LinkEnvironment();
/** All plugins enabled for this target */
public List<PluginInfo> EnabledPlugins = new List<PluginInfo>();
/** All application binaries; may include binaries not built by this target. */
public List<UEBuildBinary> AppBinaries = new List<UEBuildBinary>();
/** Extra engine module names to either include in the binary (monolithic) or create side-by-side DLLs for (modular) */
public List<string> ExtraModuleNames = new List<string>();
/** If building only a specific set of modules, these are the modules to build */
protected List<OnlyModule> OnlyModules = new List<OnlyModule>();
/** true if target should be compiled in monolithic mode, false if not */
protected bool bCompileMonolithic = false;
/** Used to keep track of all modules by name. */
private Dictionary<string, UEBuildModule> Modules = new Dictionary<string, UEBuildModule>(StringComparer.InvariantCultureIgnoreCase);
/// <summary>
/// Whether this target should be compiled in monolithic mode
/// </summary>
/// <returns>true if it should, false if it shouldn't</returns>
public bool ShouldCompileMonolithic()
{
return bCompileMonolithic;
}
/**
* @param InAppName - The name of the application being built, which is used to scope all intermediate and output file names.
* @param InGameName - The name of the game being build - can be empty
* @param InPlatform - The platform the target is being built for.
* @param InConfiguration - The configuration the target is being built for.
* @param InAdditionalDefinitions - Additional definitions provided on the UBT command-line for the target.
* @param InRemoteRoot - The remote path that the build output is synced to.
*/
public UEBuildTarget(
string InAppName,
string InGameName,
UnrealTargetPlatform InPlatform,
UnrealTargetConfiguration InConfiguration,
TargetRules InRulesObject,
List<string> InAdditionalDefinitions,
string InRemoteRoot,
List<OnlyModule> InOnlyModules)
{
AppName = InAppName;
GameName = InGameName;
Platform = InPlatform;
Configuration = InConfiguration;
Rules = InRulesObject;
{
bCompileMonolithic = (Rules != null) ? Rules.ShouldCompileMonolithic(InPlatform, InConfiguration) : false;
// Platforms may *require* monolithic compilation...
bCompileMonolithic |= UEBuildPlatform.PlatformRequiresMonolithicBuilds(InPlatform, InConfiguration);
// Force monolithic or modular mode if we were asked to
if( UnrealBuildTool.CommandLineContains("-Monolithic") ||
UnrealBuildTool.CommandLineContains("MONOLITHIC_BUILD=1") )
{
bCompileMonolithic = true;
}
else if( UnrealBuildTool.CommandLineContains( "-Modular" ) )
{
bCompileMonolithic = false;
}
}
// Figure out what the project directory is. If we have a uproject file, use that. Otherwise use the engine directory.
if (UnrealBuildTool.HasUProjectFile())
{
ProjectDirectory = Path.GetFullPath(UnrealBuildTool.GetUProjectPath());
}
else
{
ProjectDirectory = Path.GetFullPath(BuildConfiguration.RelativeEnginePath);
}
// Build the project intermediate directory
ProjectIntermediateDirectory = Path.GetFullPath(Path.Combine(ProjectDirectory, BuildConfiguration.PlatformIntermediateFolder, GetTargetName(), Configuration.ToString()));
// Build the engine intermediate directory. If we're building agnostic engine binaries, we can use the engine intermediates folder. Otherwise we need to use the project intermediates directory.
if (ShouldCompileMonolithic())
{
EngineIntermediateDirectory = ProjectIntermediateDirectory;
}
else if(Configuration == UnrealTargetConfiguration.DebugGame)
{
EngineIntermediateDirectory = Path.GetFullPath(Path.Combine(BuildConfiguration.RelativeEnginePath, BuildConfiguration.PlatformIntermediateFolder, AppName, UnrealTargetConfiguration.Development.ToString()));
}
else
{
EngineIntermediateDirectory = Path.GetFullPath(Path.Combine(BuildConfiguration.RelativeEnginePath, BuildConfiguration.PlatformIntermediateFolder, AppName, Configuration.ToString()));
}
RemoteRoot = InRemoteRoot;
OnlyModules = InOnlyModules;
bool bIsRocketGame = (InRulesObject != null) ? (InRulesObject.Type == TargetRules.TargetType.RocketGame) : false;
// Construct the output path based on configuration, platform, game if not specified.
TargetRules.TargetType? TargetType = (Rules != null) ? Rules.Type : (TargetRules.TargetType?)null;
OutputPath = Path.GetFullPath(MakeBinaryPath("", AppName, UEBuildBinaryType.Executable, TargetType, bIsRocketGame, null, InAppName));
if (bCompileMonolithic && TargetRules.IsGameType(InRulesObject.Type))
{
// For Rocket, UE4Game.exe and UE4Editor.exe still go into Engine/Binaries/<PLATFORM>
if (!InRulesObject.bOutputToEngineBinaries)
{
// We are compiling a monolithic game...
// We want the output to go into the <GAME>\Binaries folder
if (UnrealBuildTool.HasUProjectFile() == false)
{
OutputPath = OutputPath.Replace(Path.Combine("Engine", "Binaries"), Path.Combine(InGameName,"Binaries"));
}
else
{
string EnginePath = Path.GetFullPath(Path.Combine(ProjectFileGenerator.EngineRelativePath, "Binaries"));
string UProjectPath = UnrealBuildTool.GetUProjectPath();
if (Path.IsPathRooted(UProjectPath) == false)
{
string FilePath = UProjectInfo.GetProjectForTarget(InGameName);
string FullPath = Path.GetFullPath(FilePath);
UProjectPath = Path.GetDirectoryName(FullPath);
}
string ProjectPath = Path.GetFullPath(Path.Combine(UProjectPath, "Binaries"));
OutputPath = OutputPath.Replace(EnginePath, ProjectPath);
}
}
}
// handle some special case defines (so build system can pass -DEFINE as normal instead of needing
// to know about special parameters)
foreach (string Define in InAdditionalDefinitions)
{
switch (Define)
{
case "WITH_EDITOR=0":
UEBuildConfiguration.bBuildEditor = false;
break;
case "WITH_EDITORONLY_DATA=0":
UEBuildConfiguration.bBuildWithEditorOnlyData = false;
break;
// Memory profiler doesn't work if frame pointers are omitted
case "USE_MALLOC_PROFILER=1":
BuildConfiguration.bOmitFramePointers = false;
break;
case "WITH_LEAN_AND_MEAN_UE=1":
UEBuildConfiguration.bCompileLeanAndMeanUE = true;
break;
}
}
// Add the definitions specified on the command-line.
GlobalCompileEnvironment.Config.Definitions.AddRange(InAdditionalDefinitions);
}
/// <summary>
/// Attempts to delete a file. Will retry a few times before failing.
/// </summary>
/// <param name="Filename"></param>
void CleanFile(string Filename)
{
const int RetryDelayStep = 200;
int RetryDelay = 1000;
int RetryCount = 10;
bool bResult = false;
do
{
try
{
File.Delete(Filename);
bResult = true;
}
catch (Exception Ex)
{
// This happens mostly because some other stale process is still locking this file
Log.TraceVerbose(Ex.Message);
if (--RetryCount < 0)
{
throw Ex;
}
System.Threading.Thread.Sleep(RetryDelay);
// Try with a slightly longer delay next time
RetryDelay += RetryDelayStep;
}
}
while (!bResult);
}
/// <summary>
/// Attempts to delete a directory. Will retry a few times before failing.
/// </summary>
/// <param name="DirectoryPath"></param>
void CleanDirectory(string DirectoryPath)
{
const int RetryDelayStep = 200;
int RetryDelay = 1000;
int RetryCount = 10;
bool bResult = false;
do
{
try
{
Directory.Delete(DirectoryPath, true);
bResult = true;
}
catch (Exception Ex)
{
// This happens mostly because some other stale process is still locking this file
Log.TraceVerbose(Ex.Message);
if (--RetryCount < 0)
{
throw Ex;
}
System.Threading.Thread.Sleep(RetryDelay);
// Try with a slightly longer delay next time
RetryDelay += RetryDelayStep;
}
}
while (!bResult);
}
/// <summary>
/// Cleans UnralHeaderTool
/// </summary>
private void CleanUnrealHeaderTool()
{
if (!UnrealBuildTool.RunningRocket())
{
var UBTArguments = new StringBuilder();
UBTArguments.Append("UnrealHeaderTool");
// Which desktop platform do we need to clean UHT for?
UBTArguments.Append(" " + ExternalExecution.GetRuntimePlatform().ToString());
UBTArguments.Append(" " + UnrealTargetConfiguration.Development.ToString());
// NOTE: We disable mutex when launching UBT from within UBT to clean UHT
UBTArguments.Append(" -NoMutex -Clean");
ExternalExecution.RunExternalExecutable(UnrealBuildTool.GetUBTPath(), UBTArguments.ToString());
}
}
/// <summary>
/// Cleans all target intermediate files. May also clean UHT if the target uses UObjects.
/// </summary>
/// <param name="Binaries">Target binaries</param>
/// <param name="Platform">Tareet platform</param>
/// <param name="Manifest">Manifest</param>
protected void CleanTarget(List<UEBuildBinary> Binaries, CPPTargetPlatform Platform, FileManifest Manifest)
{
if (Rules != null)
{
var TargetFilename = RulesCompiler.GetTargetFilename(GameName);
var LocalTargetName = (Rules.Type == TargetRules.TargetType.Program) ? AppName : GameName;
Log.TraceVerbose("Cleaning target {0} - AppName {1}", LocalTargetName, AppName);
Log.TraceVerbose("\tTargetFilename {0}", TargetFilename);
var TargetFolder = "";
if (String.IsNullOrEmpty(TargetFilename) == false)
{
var TargetInfo = new FileInfo(TargetFilename);
TargetFolder = TargetInfo.Directory.FullName;
var SourceIdx = TargetFolder.LastIndexOf("\\Source");
if (SourceIdx != -1)
{
TargetFolder = TargetFolder.Substring(0, SourceIdx + 1);
}
}
// Collect all files to delete.
var AdditionalFileExtensions = new string[] { ".lib", ".exp", ".dll.response" };
var AllFilesToDelete = new List<string>(Manifest.FileManifestItems);
foreach (var FileManifestItem in Manifest.FileManifestItems)
{
var FileExt = Path.GetExtension(FileManifestItem);
if (FileExt == ".dll" || FileExt == ".exe")
{
var ManifestFileWithoutExtension = Utils.GetPathWithoutExtension(FileManifestItem);
foreach (var AdditionalExt in AdditionalFileExtensions)
{
var AdditionalFileToDelete = ManifestFileWithoutExtension + AdditionalExt;
AllFilesToDelete.Add(AdditionalFileToDelete);
}
}
}
//@todo. This does not clean up files that are no longer built by the target...
// Delete all output files listed in the manifest as well as any additional files.
foreach (var FileToDelete in AllFilesToDelete)
{
if (File.Exists(FileToDelete))
{
Log.TraceVerbose("\t\tDeleting " + FileToDelete);
CleanFile(FileToDelete);
}
}
// Generate a list of all the modules of each AppBinaries entry
var ModuleList = new List<string>();
var bTargetUsesUObjectModule = false;
foreach (var AppBin in AppBinaries)
{
UEBuildBinaryCPP AppBinCPP = AppBin as UEBuildBinaryCPP;
if (AppBinCPP != null)
{
// Collect all modules used by this binary.
Log.TraceVerbose("\tProcessing AppBinary " + AppBin.Config.OutputFilePath);
foreach (string ModuleName in AppBinCPP.ModuleNames)
{
if (ModuleList.Contains(ModuleName) == false)
{
Log.TraceVerbose("\t\tModule: " + ModuleName);
ModuleList.Add(ModuleName);
if (ModuleName == "CoreUObject")
{
bTargetUsesUObjectModule = true;
}
}
}
}
else
{
Log.TraceVerbose("\t********* Skipping " + AppBin.Config.OutputFilePath);
}
}
var BaseEngineBuildDataFolder = Path.GetFullPath(BuildConfiguration.BaseIntermediatePath).Replace("\\", "/");
var PlatformEngineBuildDataFolder = BuildConfiguration.BaseIntermediatePath;
// Delete generated header files
foreach (var ModuleName in ModuleList)
{
var Module = GetModuleByName(ModuleName);
var ModuleIncludeDir = UEBuildModuleCPP.GetGeneratedCodeDirectoryForModule(this, Module.ModuleDirectory, ModuleName).Replace("\\", "/");
if (!UnrealBuildTool.RunningRocket() || !ModuleIncludeDir.StartsWith(BaseEngineBuildDataFolder, StringComparison.InvariantCultureIgnoreCase))
{
if (Directory.Exists(ModuleIncludeDir))
{
Log.TraceVerbose("\t\tDeleting " + ModuleIncludeDir);
CleanDirectory(ModuleIncludeDir);
}
}
}
//
{
var AppEnginePath = Path.Combine(PlatformEngineBuildDataFolder, LocalTargetName, Configuration.ToString());
if (Directory.Exists(AppEnginePath))
{
CleanDirectory(AppEnginePath);
}
}
// Clean the intermediate directory
if (!UnrealBuildTool.RunningRocket())
{
// This is always under Rocket installation folder
if (Directory.Exists(GlobalLinkEnvironment.Config.IntermediateDirectory))
{
Log.TraceVerbose("\tDeleting " + GlobalLinkEnvironment.Config.IntermediateDirectory);
CleanDirectory(GlobalLinkEnvironment.Config.IntermediateDirectory);
}
}
else if (ShouldCompileMonolithic())
{
// Only in monolithic, otherwise it's pointing to Rocket installation folder
if (Directory.Exists(GlobalLinkEnvironment.Config.OutputDirectory))
{
Log.TraceVerbose("\tDeleting " + GlobalLinkEnvironment.Config.OutputDirectory);
CleanDirectory(GlobalCompileEnvironment.Config.OutputDirectory);
}
}
// Delete the dependency cache
{
var DependencyCacheFilename = DependencyCache.GetDependencyCachePathForTarget(this);
if (File.Exists(DependencyCacheFilename))
{
Log.TraceVerbose("\tDeleting " + DependencyCacheFilename);
CleanFile(DependencyCacheFilename);
}
}
// Delete the action history
{
var ActionHistoryFilename = ActionHistory.GeneratePathForTarget(this);
if (File.Exists(ActionHistoryFilename))
{
Log.TraceVerbose("\tDeleting " + ActionHistoryFilename);
CleanFile(ActionHistoryFilename);
}
}
// Finally clean UnrealHeaderTool if this target uses CoreUObject modules and we're not cleaning UHT already
// and we want UHT to be cleaned.
if (!UEBuildConfiguration.bDoNotBuildUHT && bTargetUsesUObjectModule && GetTargetName() != "UnrealHeaderTool")
{
CleanUnrealHeaderTool();
}
}
else
{
Log.TraceVerbose("Cannot clean target with no Rules object: {0}", GameName);
}
}
/** Generates a public manifest file for writing out */
public void GenerateManifest(List<UEBuildBinary> Binaries, CPPTargetPlatform Platform, List<string> SpecialRocketLibFilesThatAreBuildProducts)
{
string ManifestPath;
if (UnrealBuildTool.RunningRocket())
{
ManifestPath = Path.Combine(UnrealBuildTool.GetUProjectPath(), BuildConfiguration.BaseIntermediateFolder, "Manifest.xml");
}
else
{
ManifestPath = "../Intermediate/Build/Manifest.xml";
}
FileManifest Manifest = new FileManifest();
if (UEBuildConfiguration.bMergeManifests)
{
// Load in existing manifest (if any)
Manifest = Utils.ReadClass<FileManifest>(ManifestPath);
}
UnrealTargetPlatform TargetPlatform = CPPTargetPlatformToUnrealTargetPlatform( Platform );
UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform( TargetPlatform );
// Iterate over all the binaries, and add the relevant info to the manifest
foreach( UEBuildBinary Binary in Binaries )
{
// Get the platform specific extension for debug info files
// Don't add static library files to the manifest as we do not check them into perforce.
// However, add them to the manifest when cleaning the project as we do want to delete
// them in that case.
if (UEBuildConfiguration.bCleanProject == false)
{
if (Binary.Config.Type == UEBuildBinaryType.StaticLibrary)
{
continue;
}
}
string DebugInfoExtension = BuildPlatform.GetDebugInfoExtension(Binary.Config.Type);
// Create and add the binary and associated debug info
Manifest.AddBinaryNames(Binary.Config.OutputFilePath, DebugInfoExtension);
if (Binary.Config.Type == UEBuildBinaryType.Executable &&
GlobalLinkEnvironment.Config.CanProduceAdditionalConsoleApp &&
UEBuildConfiguration.bBuildEditor)
{
Manifest.AddBinaryNames(UEBuildBinary.GetAdditionalConsoleAppPath(Binary.Config.OutputFilePath), DebugInfoExtension);
}
if (TargetPlatform == UnrealTargetPlatform.Mac)
{
// Add all the resources and third party dylibs stored in app bundle
MacToolChain.AddAppBundleContentsToManifest(ref Manifest, Binary);
}
else if (TargetPlatform == UnrealTargetPlatform.IOS)
{
IOSToolChain.AddStubToManifest(ref Manifest, Binary);
}
// ok, this is pretty awful, we want the import libraries that go with the editor, only on the PC
else if (UnrealBuildTool.BuildingRocket() &&
Path.GetFileNameWithoutExtension(Binary.Config.OutputFilePath).StartsWith("UE4Editor-", StringComparison.InvariantCultureIgnoreCase) &&
Path.GetExtension(Binary.Config.OutputFilePath).EndsWith("dll", StringComparison.InvariantCultureIgnoreCase) &&
Binary.Config.Type == UEBuildBinaryType.DynamicLinkLibrary)
{
// ok, this is pretty awful, we want the import libraries that go with the editor, only on the PC
Manifest.AddBinaryNames(Path.Combine(Binary.Config.IntermediateDirectory, Path.GetFileNameWithoutExtension(Binary.Config.OutputFilePath) + ".lib"), "");
}
}
{
string DebugInfoExtension = BuildPlatform.GetDebugInfoExtension(UEBuildBinaryType.StaticLibrary);
foreach (var RedistLib in SpecialRocketLibFilesThatAreBuildProducts)
{
Manifest.AddBinaryNames(RedistLib, DebugInfoExtension);
}
}
if (UEBuildConfiguration.bCleanProject)
{
CleanTarget(Binaries, Platform, Manifest);
}
if (UEBuildConfiguration.bGenerateManifest)
{
Utils.WriteClass<FileManifest>(Manifest, ManifestPath, "");
}
}
/** Builds the target, appending list of output files and returns building result. */
public ECompilationResult Build(List<FileItem> OutputItems)
{
// Set up the global compile and link environment in GlobalCompileEnvironment and GlobalLinkEnvironment.
SetupGlobalEnvironment();
// Setup the target's modules.
SetupModules();
// Setup the target's binaries.
SetupBinaries();
// Setup the target's plugins
SetupPlugins();
var SpecialRocketLibFilesThatAreBuildProducts = new List<string>();
// Add the enabled plugins to the build
foreach (PluginInfo Plugin in EnabledPlugins)
{
SpecialRocketLibFilesThatAreBuildProducts.AddRange(AddPlugin(Plugin));
}
// Describe what's being built.
Log.TraceVerbose("Building {0} - {1} - {2} - {3}", AppName, GameName, Platform, Configuration);
// Put the non-executable output files (PDB, import library, etc) in the intermediate directory.
GlobalLinkEnvironment.Config.IntermediateDirectory = Path.GetFullPath(GlobalCompileEnvironment.Config.OutputDirectory);
GlobalLinkEnvironment.Config.OutputDirectory = GlobalLinkEnvironment.Config.IntermediateDirectory;
// By default, shadow source files for this target in the root OutputDirectory
GlobalLinkEnvironment.Config.LocalShadowDirectory = GlobalLinkEnvironment.Config.OutputDirectory;
// Add all of the extra modules, including game modules, that need to be compiled along
// with this app. These modules aren't necessarily statically linked against, but may
// still be required at runtime in order for the application to load and function properly!
AddExtraModules();
// Bind modules for all app binaries.
foreach (var Binary in AppBinaries)
{
Binary.BindModules();
}
// Process all referenced modules and create new binaries for DLL dependencies if needed
var NewBinaries = new List<UEBuildBinary>();
foreach (var Binary in AppBinaries)
{
// Add binaries for all of our dependent modules
var FoundBinaries = Binary.ProcessUnboundModules();
if (FoundBinaries != null)
{
NewBinaries.AddRange(FoundBinaries);
}
}
AppBinaries.AddRange( NewBinaries );
// On Mac AppBinaries paths for non-console targets need to be adjusted to be inside the app bundle
if (GlobalLinkEnvironment.Config.TargetPlatform == CPPTargetPlatform.Mac && !GlobalLinkEnvironment.Config.bIsBuildingConsoleApplication)
{
MacToolChain.FixBundleBinariesPaths( this, AppBinaries );
}
// If we're building a single module, then find the binary for that module and add it to our target
if (OnlyModules.Count > 0)
{
var FilteredBinaries = new List<UEBuildBinary>();
var AnyBinariesAdded = false;
foreach (var DLLBinary in AppBinaries)
{
var FoundOnlyModule = DLLBinary.FindOnlyModule(OnlyModules);
if (FoundOnlyModule != null)
{
FilteredBinaries.Add( DLLBinary );
AnyBinariesAdded = true;
if (!String.IsNullOrEmpty(FoundOnlyModule.OnlyModuleSuffix))
{
var Appendage = "-" + FoundOnlyModule.OnlyModuleSuffix;
var MatchPos = DLLBinary.Config.OutputFilePath.LastIndexOf(FoundOnlyModule.OnlyModuleName, StringComparison.InvariantCultureIgnoreCase);
if (MatchPos < 0)
{
throw new BuildException("Failed to find module name \"{0}\" specified on the command line inside of the output filename \"{1}\" to add appendage.", FoundOnlyModule.OnlyModuleName, DLLBinary.Config.OutputFilePath);
}
DLLBinary.Config.OriginalOutputFilePath = DLLBinary.Config.OutputFilePath;
DLLBinary.Config.OutputFilePath = DLLBinary.Config.OutputFilePath.Insert(MatchPos + FoundOnlyModule.OnlyModuleName.Length, Appendage);
}
}
}
// Copy the result into the final list
AppBinaries = FilteredBinaries;
if (!AnyBinariesAdded)
{
throw new BuildException("One or more of the modules specified using the '-module' argument could not be found.");
}
}
// Filter out binaries that were already built and are just used for linking. We will not build these binaries but we need them for link information
{
var FilteredBinaries = new List<UEBuildBinary>();
foreach (var DLLBinary in AppBinaries)
{
if ( DLLBinary.Config.bAllowCompilation )
{
FilteredBinaries.Add(DLLBinary);
}
}
// Copy the result into the final list
AppBinaries = FilteredBinaries;
if (AppBinaries.Count == 0)
{
throw new BuildException("No modules found to build. All requested binaries were already built.");
}
}
// If we are only interested in platform specific binaries, filter everything else out now
if (UnrealBuildTool.GetOnlyPlatformSpecificFor() != UnrealTargetPlatform.Unknown)
{
var FilteredBinaries = new List<UEBuildBinary>();
var OtherThingsWeNeedToBuild = new List<OnlyModule>();
foreach (var DLLBinary in AppBinaries)
{
if (DLLBinary.Config.bIsCrossTarget)
{
FilteredBinaries.Add(DLLBinary);
bool bIncludeDynamicallyLoaded = false;
var AllReferencedModules = DLLBinary.GetAllDependencyModules(bIncludeDynamicallyLoaded, bForceCircular: true);
foreach (var Other in AllReferencedModules)
{
OtherThingsWeNeedToBuild.Add(new OnlyModule(Other.Name));
}
}
}
foreach (var DLLBinary in AppBinaries)
{
if (!FilteredBinaries.Contains(DLLBinary) && DLLBinary.FindOnlyModule(OtherThingsWeNeedToBuild) != null)
{
if (!File.Exists(DLLBinary.Config.OutputFilePath))
{
throw new BuildException("Module {0} is potentially needed for the {1} platform to work, but it isn't actually compiled. This either needs to be marked as platform specific or made a dynamic dependency of something compiled with the base editor.", DLLBinary.Config.OutputFilePath, UnrealBuildTool.GetOnlyPlatformSpecificFor().ToString());
}
}
}
// Copy the result into the final list
AppBinaries = FilteredBinaries;
}
//@todo.Rocket: Will users be able to rebuild UnrealHeaderTool? NO
if (UnrealBuildTool.RunningRocket() && AppName != "UnrealHeaderTool")
{
var FilteredBinaries = new List<UEBuildBinary>();
// Have to do absolute here as this could be a project that is under the root
string FullUProjectPath = Path.GetFullPath(UnrealBuildTool.GetUProjectPath());
// We only want to build rocket projects...
foreach (var DLLBinary in AppBinaries)
{
if (Utils.IsFileUnderDirectory( DLLBinary.Config.OutputFilePath, FullUProjectPath ))
{
FilteredBinaries.Add(DLLBinary);
}
}
// Copy the result into the final list
AppBinaries = FilteredBinaries;
if (AppBinaries.Count == 0)
{
throw new BuildException("Rocket: No modules found to build?");
}
}
// If we're compiling monolithic, make sure the executable knows about all referenced modules
if (ShouldCompileMonolithic())
{
var ExecutableBinary = AppBinaries[0];
bool bIncludeDynamicallyLoaded = true;
var AllReferencedModules = ExecutableBinary.GetAllDependencyModules( bIncludeDynamicallyLoaded, bForceCircular:true );
foreach (var CurModule in AllReferencedModules)
{
if ( CurModule.Binary == null || CurModule.Binary == ExecutableBinary )
{
ExecutableBinary.AddModule(CurModule.Name);
}
}
bool IsCurrentPlatform;
if (Utils.IsRunningOnMono)
{
IsCurrentPlatform = Platform == UnrealTargetPlatform.Mac;
}
else
{
IsCurrentPlatform = Platform == UnrealTargetPlatform.Win64 || Platform == UnrealTargetPlatform.Win32;
}
if (TargetRules.IsAGame(Rules.Type) && IsCurrentPlatform)
{
// The hardcoded engine directory needs to be a relative path to match the normal EngineDir format. Not doing so breaks the network file system (TTP#315861).
string EnginePath = Utils.CleanDirectorySeparators(Utils.MakePathRelativeTo(ProjectFileGenerator.EngineRelativePath, Path.GetDirectoryName(ExecutableBinary.Config.OutputFilePath)), '/');
if (EnginePath.EndsWith("/") == false)
{
EnginePath += "/";
}
GlobalCompileEnvironment.Config.Definitions.Add("UE_ENGINE_DIRECTORY=" + EnginePath);
}
// Generate static libraries for monolithic games in Rocket
if ((UnrealBuildTool.BuildingRocket() || UnrealBuildTool.RunningRocket()) && TargetRules.IsAGame(Rules.Type))
{
List<UEBuildModule> Modules = ExecutableBinary.GetAllDependencyModules(true, false);
foreach (UEBuildModuleCPP Module in Modules.OfType<UEBuildModuleCPP>())
{
if(Utils.IsFileUnderDirectory(Module.ModuleDirectory, BuildConfiguration.RelativeEnginePath) && Module.Binary == ExecutableBinary)
{
UnrealTargetConfiguration LibraryConfiguration = (Configuration == UnrealTargetConfiguration.DebugGame)? UnrealTargetConfiguration.Development : Configuration;
Module.RedistStaticLibraryPath = MakeBinaryPath("", "UE4Game-Redist-" + Module.Name, Platform, LibraryConfiguration, UEBuildBinaryType.StaticLibrary, Rules.Type, false, null, AppName);
Module.bBuildingRedistStaticLibrary = UnrealBuildTool.BuildingRocket();
if (Module.bBuildingRedistStaticLibrary)
{
SpecialRocketLibFilesThatAreBuildProducts.Add(Module.RedistStaticLibraryPath);
}
}
}
}
}
if (ShouldCompileMonolithic() && !ProjectFileGenerator.bGenerateProjectFiles && Rules != null && Rules.Type != TargetRules.TargetType.Program)
{
// All non-program monolithic binaries implicitly depend on all static plugin libraries so they are always linked appropriately
// In order to do this, we create a new module here with a cpp file we emit that invokes an empty function in each library.
// If we do not do this, there will be no static initialization for libs if no symbols are referenced in them.
CreateLinkerFixupsCPPFile();
}
// On Mac we have actions that should be executed after all the binaries are created
if (GlobalLinkEnvironment.Config.TargetPlatform == CPPTargetPlatform.Mac)
{
MacToolChain.SetupBundleDependencies(AppBinaries, GameName);
}
// If we're only generating the manifest, return now
if (UEBuildConfiguration.bGenerateManifest || UEBuildConfiguration.bCleanProject)
{
GenerateManifest(AppBinaries, GlobalLinkEnvironment.Config.TargetPlatform, SpecialRocketLibFilesThatAreBuildProducts);
if (!BuildConfiguration.bXGEExport)
{
return ECompilationResult.Succeeded;
}
}
// We don't need to worry about shared PCH headers when only generating project files. This is
// just an optimization
if( !ProjectFileGenerator.bGenerateProjectFiles )
{
var SharedPCHHeaderFiles = FindSharedPCHHeaders();
GlobalCompileEnvironment.SharedPCHHeaderFiles = SharedPCHHeaderFiles;
}
if( (BuildConfiguration.bXGEExport && UEBuildConfiguration.bGenerateManifest) || (!UEBuildConfiguration.bGenerateManifest && !UEBuildConfiguration.bCleanProject && !ProjectFileGenerator.bGenerateProjectFiles) )
{
var ToolChain = UEToolChain.GetPlatformToolChain(GlobalLinkEnvironment.Config.TargetPlatform);
var UObjectModules = new List<UHTModuleInfo>();
// Figure out which modules have UObjects that we may need to generate headers for
foreach( var Binary in AppBinaries )
{
var DependencyModules = Binary.GetAllDependencyModules(bIncludeDynamicallyLoaded: false, bForceCircular: false);
foreach( var DependencyModuleCPP in DependencyModules.OfType<UEBuildModuleCPP>().Where( CPPModule => !UObjectModules.Any( Module => Module.ModuleName == CPPModule.Name ) ) )
{
if( !DependencyModuleCPP.bIncludedInTarget )
throw new BuildException( "Expecting module {0} to have bIncludeInTarget set", DependencyModuleCPP.Name );
var ModuleCompileEnvironment = DependencyModuleCPP.CreateModuleCompileEnvironment(GlobalCompileEnvironment);
DependencyModuleCPP.ProcessAllCppDependencies(ModuleCompileEnvironment);
var UHTModuleInfo = DependencyModuleCPP.GetUHTModuleInfo(ModuleCompileEnvironment);
if( UHTModuleInfo.PublicUObjectClassesHeaders.Count == 0 && UHTModuleInfo.PrivateUObjectHeaders.Count == 0 && UHTModuleInfo.PublicUObjectHeaders.Count == 0 )
continue;
UHTModuleInfo.PCH = ToolChain.ConvertPath( DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile.AbsolutePath );
UHTModuleInfo.GeneratedCPPFilenameBase = Path.Combine( UEBuildModuleCPP.GetGeneratedCodeDirectoryForModule(this, UHTModuleInfo.ModuleDirectory, UHTModuleInfo.ModuleName), UHTModuleInfo.ModuleName ) + ".generated";
DependencyModuleCPP.AutoGenerateInlInfo = new UEBuildModuleCPP.AutoGenerateInlInfoClass( UHTModuleInfo.GeneratedCPPFilenameBase + ".cpp" );
UObjectModules.Add( UHTModuleInfo );
Log.TraceVerbose( "Detected UObject module: " + DependencyModuleCPP.Name );
}
}
if( UObjectModules.Count > 0 )
{
// Execute the header tool
var TargetName = !String.IsNullOrEmpty( GameName ) ? GameName : AppName;
string ModuleInfoFileName = Path.GetFullPath( Path.Combine( ProjectIntermediateDirectory, "UnrealHeaderTool.manifest" ) );
ECompilationResult UHTResult = ECompilationResult.OtherCompilationError;
if (!ExternalExecution.ExecuteHeaderToolIfNecessary(this, UObjectModules, ModuleInfoFileName, ref UHTResult))
{
Log.TraceInformation("UnrealHeaderTool failed for target '" + TargetName + "' (platform: " + Platform.ToString() + ", module info: " + ModuleInfoFileName + ").");
return UHTResult;
}
}
}
// Build the target's binaries.
foreach (var Binary in AppBinaries)
{
OutputItems.AddRange(Binary.Build(GlobalCompileEnvironment, GlobalLinkEnvironment));
}
if (BuildConfiguration.WriteTargetInfoPath != null)
{
Log.TraceInformation("Writing build environment to " + BuildConfiguration.WriteTargetInfoPath + "...");
XmlWriterSettings Settings = new XmlWriterSettings();
Settings.Indent = true;
using (XmlWriter Writer = XmlWriter.Create(BuildConfiguration.WriteTargetInfoPath, Settings))
{
Writer.WriteStartElement("target");
Writer.WriteAttributeString("name", AppName);
foreach (UEBuildBinary Binary in AppBinaries)
{
Binary.WriteBuildEnvironment(GlobalCompileEnvironment, GlobalLinkEnvironment, Writer);
}
Writer.WriteEndElement();
}
OutputItems.Clear();
}
return ECompilationResult.Succeeded;
}
/// <summary>
/// All non-program monolithic binaries implicitly depend on all static plugin libraries so they are always linked appropriately
/// In order to do this, we create a new module here with a cpp file we emit that invokes an empty function in each library.
/// If we do not do this, there will be no static initialization for libs if no symbols are referenced in them.
/// </summary>
private void CreateLinkerFixupsCPPFile()
{
var ExecutableBinary = AppBinaries[0];
List<string> PrivateDependencyModuleNames = new List<string>();
UEBuildBinaryCPP BinaryCPP = ExecutableBinary as UEBuildBinaryCPP;
if (BinaryCPP != null)
{
TargetInfo CurrentTarget = new TargetInfo(Platform, Configuration, (Rules != null) ? Rules.Type : (TargetRules.TargetType?)null);
foreach (var TargetModuleName in BinaryCPP.ModuleNames)
{
string UnusedFilename;
ModuleRules CheckRules = RulesCompiler.CreateModuleRules(TargetModuleName, CurrentTarget, out UnusedFilename);
if ((CheckRules != null) && (CheckRules.Type != ModuleRules.ModuleType.External))
{
PrivateDependencyModuleNames.Add(TargetModuleName);
}
}
}
foreach (PluginInfo Plugin in EnabledPlugins)
{
foreach (PluginInfo.PluginModuleInfo Module in Plugin.Modules)
{
if(ShouldIncludePluginModule(Plugin, Module))
{
PrivateDependencyModuleNames.Add(Module.Name);
}
}
}
// We ALWAYS have to write this file as the IMPLEMENT_PRIMARY_GAME_MODULE function depends on the UELinkerFixups function existing.
{
List<string> LinkerFixupsFileContents = new List<string>();
string LinkerFixupsName = "UELinkerFixups";
// Include an empty header so UEBuildModule.Compile does not complain about a lack of PCH
string HeaderFilename = LinkerFixupsName + "Name.h";
LinkerFixupsFileContents.Add("#include \"" + HeaderFilename + "\"");
// Add a function that is not referenced by anything that invokes all the empty functions in the different static libraries
LinkerFixupsFileContents.Add("void " + LinkerFixupsName + "()");
LinkerFixupsFileContents.Add("{");
// Fill out the body of the function with the empty function calls. This is what causes the static libraries to be considered relevant
foreach (var DependencyModuleName in PrivateDependencyModuleNames)
{
LinkerFixupsFileContents.Add(" extern void EmptyLinkFunctionForStaticInitialization" + DependencyModuleName + "();");
LinkerFixupsFileContents.Add(" EmptyLinkFunctionForStaticInitialization" + DependencyModuleName + "();");
}
// End the function body that was started above
LinkerFixupsFileContents.Add("}");
// Create the cpp file
string LinkerFixupCPPFilename = Path.Combine(GlobalCompileEnvironment.Config.OutputDirectory, LinkerFixupsName + ".cpp");
// Determine if the file changed. Write it if it either doesn't exist or the contents are different.
bool bShouldWriteFile = true;
if (File.Exists(LinkerFixupCPPFilename))
{
string[] ExistingFixupText = File.ReadAllLines(LinkerFixupCPPFilename);
string JoinedNewContents = string.Join("", LinkerFixupsFileContents.ToArray());
string JoinedOldContents = string.Join("", ExistingFixupText);
bShouldWriteFile = (JoinedNewContents != JoinedOldContents);
}
// If we determined that we should write the file, write it now.
if (bShouldWriteFile)
{
string LinkerFixupHeaderFilenameWithPath = Path.Combine(GlobalCompileEnvironment.Config.OutputDirectory, HeaderFilename);
ResponseFile.Create(LinkerFixupHeaderFilenameWithPath, new List<string>());
ResponseFile.Create(LinkerFixupCPPFilename, LinkerFixupsFileContents);
}
// Create the source file list (just the one cpp file)
List<FileItem> SourceFiles = new List<FileItem>();
SourceFiles.Add(FileItem.GetItemByPath(LinkerFixupCPPFilename));
// Create the CPP module
var FakeModuleDirectory = Path.GetDirectoryName( LinkerFixupCPPFilename );
UEBuildModuleCPP NewModule = new UEBuildModuleCPP(
InTarget: this,
InName: LinkerFixupsName,
InType: UEBuildModuleType.GameModule,
InModuleDirectory:FakeModuleDirectory,
InOutputDirectory: GlobalCompileEnvironment.Config.OutputDirectory,
InIntelliSenseGatherer: null,
InSourceFiles: SourceFiles,
InPublicIncludePaths: new List<string>(),
InPublicSystemIncludePaths: new List<string>(),
InDefinitions: new List<string>(),
InPublicIncludePathModuleNames: new List<string>(),
InPublicDependencyModuleNames: new List<string>(),
InPublicDelayLoadDLLs: new List<string>(),
InPublicAdditionalLibraries: new List<string>(),
InPublicFrameworks: new List<string>(),
InPublicAdditionalFrameworks: new List<string>(),
InPrivateIncludePaths: new List<string>(),
InPrivateIncludePathModuleNames: new List<string>(),
InPrivateDependencyModuleNames: PrivateDependencyModuleNames,
InCircularlyReferencedDependentModules: new List<string>(),
InDynamicallyLoadedModuleNames: new List<string>(),
InPlatformSpecificDynamicallyLoadedModuleNames: new List<string>(),
InOptimizeCode: ModuleRules.CodeOptimization.Default,
InAllowSharedPCH: false,
InSharedPCHHeaderFile: "",
InUseRTTI: false,
InEnableBufferSecurityChecks: true,
InFasterWithoutUnity: true,
InMinFilesUsingPrecompiledHeaderOverride: 0,
InEnableExceptions: false,
InEnableInlining: true
);
// Now bind this new module to the executable binary so it will link the plugin libs correctly
NewModule.Binary = ExecutableBinary;
NewModule.bIncludedInTarget = true;
ExecutableBinary.AddModule(NewModule.Name);
}
}
/// <summary>
/// For any dependent module that has "SharedPCHHeaderFile" set in its module rules, we gather these headers
/// and sort them in order from least-dependent to most-dependent such that larger, more complex PCH headers
/// appear last in the list
/// </summary>
/// <returns>List of shared PCH headers to use</returns>
private List<SharedPCHHeaderInfo> FindSharedPCHHeaders()
{
// List of modules, with all of the dependencies of that module
var SharedPCHHeaderFiles = new List<SharedPCHHeaderInfo>();
// Build up our list of modules with "shared PCH headers". The list will be in dependency order, with modules
// that depend on previous modules appearing later in the list
foreach( var Binary in AppBinaries )
{
var CPPBinary = Binary as UEBuildBinaryCPP;
if( CPPBinary != null )
{
foreach( var ModuleName in CPPBinary.ModuleNames )
{
var CPPModule = GetModuleByName( ModuleName ) as UEBuildModuleCPP;
if( CPPModule != null )
{
if( !String.IsNullOrEmpty( CPPModule.SharedPCHHeaderFile ) )
{
// @todo SharedPCH: Ideally we could figure the PCH header name automatically, and simply use a boolean in the module
// definition to opt into exposing a shared PCH. Unfortunately we don't determine which private PCH header "goes with"
// a module until a bit later in the process. It shouldn't be hard to change that though.
var SharedPCHHeaderFilePath = ProjectFileGenerator.RootRelativePath + "/Engine/Source/" + CPPModule.SharedPCHHeaderFile;
var SharedPCHHeaderFileItem = FileItem.GetExistingItemByPath( SharedPCHHeaderFilePath );
if( SharedPCHHeaderFileItem != null )
{
var ModuleDependencies = new Dictionary<string, UEBuildModule>();
var ModuleList = new List<UEBuildModule>();
bool bIncludeDynamicallyLoaded = false;
CPPModule.GetAllDependencyModules(ref ModuleDependencies, ref ModuleList, bIncludeDynamicallyLoaded, bForceCircular: false);
// Figure out where to insert the shared PCH into our list, based off the module dependency ordering
int InsertAtIndex = SharedPCHHeaderFiles.Count;
for( var ExistingModuleIndex = SharedPCHHeaderFiles.Count - 1; ExistingModuleIndex >= 0; --ExistingModuleIndex )
{
var ExistingModule = SharedPCHHeaderFiles[ ExistingModuleIndex ].Module;
var ExistingModuleDependencies = SharedPCHHeaderFiles[ ExistingModuleIndex ].Dependencies;
// If the module to add to the list is dependent on any modules already in our header list, that module
// must be inserted after any of those dependencies in the list
foreach( var ExistingModuleDependency in ExistingModuleDependencies )
{
if( ExistingModuleDependency.Value == CPPModule )
{
// Make sure we're not a circular dependency of this module. Circular dependencies always
// point "upstream". That is, modules like Engine point to UnrealEd in their
// CircularlyReferencedDependentModules array, but the natural dependency order is
// that UnrealEd depends on Engine. We use this to avoid having modules such as UnrealEd
// appear before Engine in our shared PCH list.
// @todo SharedPCH: This is not very easy for people to discover. Luckily we won't have many shared PCHs in total.
if( !ExistingModule.CircularlyReferencedDependentModules.Contains( CPPModule.Name ) )
{
// We are at least dependent on this module. We'll keep searching the list to find
// further-descendant modules we might be dependent on
InsertAtIndex = ExistingModuleIndex;
break;
}
}
}
}
var NewSharedPCHHeaderInfo = new SharedPCHHeaderInfo();
NewSharedPCHHeaderInfo.PCHHeaderFile = SharedPCHHeaderFileItem;
NewSharedPCHHeaderInfo.Module = CPPModule;
NewSharedPCHHeaderInfo.Dependencies = ModuleDependencies;
SharedPCHHeaderFiles.Insert( InsertAtIndex, NewSharedPCHHeaderInfo );
}
else
{
throw new BuildException( "Could not locate the specified SharedPCH header file '{0}' for module '{1}'.", SharedPCHHeaderFilePath, CPPModule.Name );
}
}
}
}
}
}
if( SharedPCHHeaderFiles.Count > 0 )
{
Log.TraceVerbose("Detected {0} shared PCH headers (listed in dependency order):", SharedPCHHeaderFiles.Count);
foreach( var CurSharedPCH in SharedPCHHeaderFiles )
{
Log.TraceVerbose(" " + CurSharedPCH.PCHHeaderFile.AbsolutePath + " (module: " + CurSharedPCH.Module.Name + ")");
}
}
else
{
Log.TraceVerbose("Did not detect any shared PCH headers");
}
return SharedPCHHeaderFiles;
}
/// <summary>
/// Include the given plugin in the target. It may be included as a separate binary, or compiled into a monolithic executable.
/// </summary>
public List<string> AddPlugin(PluginInfo Plugin)
{
var SpecialRocketLibFilesThatAreBuildProducts = new List<string>();
foreach(PluginInfo.PluginModuleInfo Module in Plugin.Modules)
{
if (ShouldIncludePluginModule(Plugin, Module))
{
SpecialRocketLibFilesThatAreBuildProducts.AddRange(AddPluginModule(Plugin, Module));
}
}
return SpecialRocketLibFilesThatAreBuildProducts;
}
/// <summary>
/// Include the given plugin module in the target. Will be built in the appropriate subfolder under the plugin directory.
/// </summary>
public List<string> AddPluginModule(PluginInfo Plugin, PluginInfo.PluginModuleInfo Module)
{
var SpecialRocketLibFilesThatAreBuildProducts = new List<string>();
// Is this a Rocket module?
bool bIsRocketModule = RulesCompiler.IsRocketProjectModule(Module.Name);
// Get the binary type to build
UEBuildBinaryType BinaryType = ShouldCompileMonolithic()? UEBuildBinaryType.StaticLibrary : UEBuildBinaryType.DynamicLinkLibrary;
// Make the plugin intermediate path
string IntermediateDirectory = Path.Combine(Plugin.Directory, BuildConfiguration.PlatformIntermediateFolder, Plugins.GetPluginSubfolderName(BinaryType, AppName), Configuration.ToString());
// Get the output path. Don't prefix the app name for Rocket
string OutputFilePath;
if ((UnrealBuildTool.BuildingRocket() || UnrealBuildTool.RunningRocket()) && BinaryType == UEBuildBinaryType.StaticLibrary)
{
OutputFilePath = MakeBinaryPath(Module.Name, Module.Name, BinaryType, Rules.Type, bIsRocketModule, Plugin, AppName);
if (UnrealBuildTool.BuildingRocket())
{
SpecialRocketLibFilesThatAreBuildProducts.Add(OutputFilePath);
}
}
else
{
OutputFilePath = MakeBinaryPath(Module.Name, GetAppName() + "-" + Module.Name, BinaryType, Rules.Type, bIsRocketModule, Plugin, AppName);
}
// Try to determine if we have the rules file
var ModuleFilename = RulesCompiler.GetModuleFilename(Module.Name);
var bHasModuleRules = String.IsNullOrEmpty(ModuleFilename) == false;
// Figure out whether we should build it from source
var ModuleSourceFolder = bHasModuleRules ? Path.GetDirectoryName(RulesCompiler.GetModuleFilename(Module.Name)) : ModuleFilename;
bool bShouldBeBuiltFromSource = bHasModuleRules && Directory.GetFiles(ModuleSourceFolder, "*.cpp", SearchOption.AllDirectories).Length > 0;
// Create the binary
UEBuildBinaryConfiguration Config = new UEBuildBinaryConfiguration( InType: BinaryType,
InOutputFilePath: OutputFilePath,
InIntermediateDirectory: IntermediateDirectory,
bInAllowExports: true,
bInAllowCompilation: bShouldBeBuiltFromSource,
bInHasModuleRules : bHasModuleRules,
InModuleNames: new List<string> { Module.Name } );
AppBinaries.Add(new UEBuildBinaryCPP(this, Config));
return SpecialRocketLibFilesThatAreBuildProducts;
}
/// When building a target, this is called to add any additional modules that should be compiled along
/// with the main target. If you override this in a derived class, remember to call the base implementation!
protected virtual void AddExtraModules()
{
// Add extra modules that will either link into the main binary (monolithic), or be linked into separate DLL files (modular)
foreach (var ModuleName in ExtraModuleNames)
{
if (ShouldCompileMonolithic())
{
// Add this module to the executable's list of included modules
var ExecutableBinary = AppBinaries[0];
ExecutableBinary.AddModule(ModuleName);
}
else
{
// Is this a Rocket module?
bool bIsRocketModule = RulesCompiler.IsRocketProjectModule(ModuleName);
// Create a DLL binary for this module
string OutputFilePath = MakeBinaryPath(ModuleName, GetAppName() + "-" + ModuleName, UEBuildBinaryType.DynamicLinkLibrary, Rules.Type, bIsRocketModule, null, AppName);
UEBuildBinaryConfiguration Config = new UEBuildBinaryConfiguration( InType: UEBuildBinaryType.DynamicLinkLibrary,
InOutputFilePath: OutputFilePath,
InIntermediateDirectory: RulesCompiler.IsGameModule(ModuleName)? ProjectIntermediateDirectory : EngineIntermediateDirectory,
bInAllowExports: true,
InModuleNames: new List<string> { ModuleName } );
// Tell the target about this new binary
AppBinaries.Add( new UEBuildBinaryCPP( this, Config ) );
}
}
}
/**
* @return true if debug information is created, false otherwise.
*/
public bool IsCreatingDebugInfo()
{
return GlobalCompileEnvironment.Config.bCreateDebugInfo;
}
/**
* @return The overall output directory of actions for this target.
*/
public string GetOutputDirectory()
{
return GlobalCompileEnvironment.Config.OutputDirectory;
}
/**
* @return true if we are building for dedicated server, false otherwise.
*/
public bool IsBuildingDedicatedServer()
{
return UEBuildConfiguration.bBuildDedicatedServer;
}
/** Given a UBT-built binary name (e.g. "Core"), returns a relative path to the binary for the current build configuration (e.g. "../Binaries/Win64/Core-Win64-Debug.lib") */
public static string MakeBinaryPath(string ModuleName, string BinaryName, UnrealTargetPlatform Platform,
UnrealTargetConfiguration Configuration, UEBuildBinaryType BinaryType, TargetRules.TargetType? TargetType, bool bIsRocketModule, PluginInfo PluginInfo, string AppName)
{
// Determine the binary extension for the platform and binary type.
UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform);
string BinaryExtension = BuildPlatform.GetBinaryExtension(BinaryType);
UnrealTargetConfiguration LocalConfig = Configuration;
if(Configuration == UnrealTargetConfiguration.DebugGame && !String.IsNullOrEmpty(ModuleName) && !RulesCompiler.IsGameModule(ModuleName))
{
LocalConfig = UnrealTargetConfiguration.Development;
}
string ModuleBinariesSubDir = "";
if ((BinaryType == UEBuildBinaryType.DynamicLinkLibrary) && (string.IsNullOrEmpty(ModuleName) == false))
{
// Allow for modules to specify sub-folders in the Binaries folder
var RulesFilename = RulesCompiler.GetModuleFilename(ModuleName);
// Plugins can be binary-only and can have no rules object
if (PluginInfo == null || !String.IsNullOrEmpty(RulesFilename))
{
ModuleRules ModuleRulesObj = RulesCompiler.CreateModuleRules(ModuleName, new TargetInfo(Platform, Configuration, TargetType), out RulesFilename);
if (ModuleRulesObj != null)
{
ModuleBinariesSubDir = ModuleRulesObj.BinariesSubFolder;
}
}
}
//@todo.Rocket: This just happens to work since exp and lib files go into intermediate...
// Build base directory string ("../Binaries/<Platform>/")
string BinariesDirName;
if( PluginInfo != null )
{
BinariesDirName = Path.Combine( PluginInfo.Directory, "Binaries" );
}
else
{
BinariesDirName = Path.Combine( "..", "Binaries" );
}
var BaseDirectory = Path.Combine( BinariesDirName, Platform.ToString());
if (ModuleBinariesSubDir.Length > 0)
{
BaseDirectory = Path.Combine(BaseDirectory, ModuleBinariesSubDir);
}
string BinarySuffix = "";
if ((PluginInfo != null) && (BinaryType != UEBuildBinaryType.DynamicLinkLibrary))
{
BinarySuffix = "-Static";
}
// append the architecture to the end of the binary name
BinarySuffix += BuildPlatform.GetActiveArchitecture();
string OutBinaryPath = "";
// Append binary file name
if (LocalConfig == UnrealTargetConfiguration.Development)
{
OutBinaryPath = Path.Combine(BaseDirectory, String.Format("{0}{1}{2}", BinaryName, BinarySuffix, BinaryExtension));
}
else
{
OutBinaryPath = Path.Combine(BaseDirectory, String.Format("{0}-{1}-{2}{3}{4}",
BinaryName, Platform.ToString(), LocalConfig.ToString(), BinarySuffix, BinaryExtension));
}
return OutBinaryPath;
}
/** Given a UBT-built binary name (e.g. "Core"), returns a relative path to the binary for the current build configuration (e.g. "../../Binaries/Win64/Core-Win64-Debug.lib") */
public string MakeBinaryPath(string ModuleName, string BinaryName, UEBuildBinaryType BinaryType, TargetRules.TargetType? TargetType, bool bIsRocketModule, PluginInfo PluginInfo, string AppName )
{
if (String.IsNullOrEmpty(ModuleName) && Configuration == UnrealTargetConfiguration.DebugGame && !bCompileMonolithic)
{
return MakeBinaryPath(ModuleName, BinaryName, Platform, UnrealTargetConfiguration.Development, BinaryType, TargetType, bIsRocketModule, PluginInfo, AppName);
}
else
{
return MakeBinaryPath(ModuleName, BinaryName, Platform, Configuration, BinaryType, TargetType, bIsRocketModule, PluginInfo, AppName);
}
}
/// <summary>
/// Determines whether the given plugin module is part of the current build.
/// </summary>
private bool ShouldIncludePluginModule(PluginInfo Plugin, PluginInfo.PluginModuleInfo Module)
{
// Check it can be built for this platform...
if (Module.Platforms.Contains(Platform))
{
// ...and that it's appropriate for the current build environment.
switch (Module.Type)
{
case PluginInfo.PluginModuleType.Runtime:
case PluginInfo.PluginModuleType.RuntimeNoCommandlet:
return true;
case PluginInfo.PluginModuleType.Developer:
return UEBuildConfiguration.bBuildDeveloperTools;
case PluginInfo.PluginModuleType.Editor:
case PluginInfo.PluginModuleType.EditorNoCommandlet:
return UEBuildConfiguration.bBuildEditor;
}
}
return false;
}
/** Sets up the modules for the target. */
protected virtual void SetupModules()
{
UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform);
List<string> PlatformExtraModules = new List<string>();
BuildPlatform.GetExtraModules(new TargetInfo(Platform, Configuration, Rules.Type), this, ref PlatformExtraModules);
ExtraModuleNames.AddRange(PlatformExtraModules);
}
/** Sets up the plugins for this target */
protected virtual void SetupPlugins()
{
if (!UEBuildConfiguration.bExcludePlugins)
{
// Filter the plugins list by the current project
List<PluginInfo> ValidPlugins = new List<PluginInfo>();
foreach(PluginInfo Plugin in Plugins.AllPlugins)
{
if(Plugin.LoadedFrom != PluginInfo.LoadedFromType.GameProject || Plugin.Directory.StartsWith(ProjectDirectory, StringComparison.InvariantCultureIgnoreCase))
{
ValidPlugins.Add(Plugin);
}
}
// Build the enabled plugin list
if (ShouldCompileMonolithic() || Rules.Type == TargetRules.TargetType.Program)
{
List<string> FilterPluginNames = new List<string>(Rules.AdditionalPlugins);
if (UEBuildConfiguration.bCompileAgainstEngine)
{
EngineConfiguration.ReadArray(ProjectDirectory, Platform, "Engine", "Plugins", "EnabledPlugins", FilterPluginNames);
}
EnabledPlugins.AddRange(ValidPlugins.Where(x => FilterPluginNames.Contains(x.Name)));
}
else
{
EnabledPlugins.AddRange(ValidPlugins);
}
}
}
/** Sets up the binaries for the target. */
protected virtual void SetupBinaries()
{
if (Rules != null)
{
List<UEBuildBinaryConfiguration> RulesBuildBinaryConfigurations = new List<UEBuildBinaryConfiguration>();
List<string> RulesExtraModuleNames = new List<string>();
Rules.SetupBinaries(
new TargetInfo(Platform, Configuration, Rules.Type),
ref RulesBuildBinaryConfigurations,
ref RulesExtraModuleNames
);
foreach (UEBuildBinaryConfiguration BinaryConfig in RulesBuildBinaryConfigurations)
{
BinaryConfig.OutputFilePath = OutputPath;
BinaryConfig.IntermediateDirectory = ProjectIntermediateDirectory;
if (BinaryConfig.ModuleNames.Count > 0)
{
AppBinaries.Add(new UEBuildBinaryCPP( this, BinaryConfig ));
}
else
{
AppBinaries.Add(new UEBuildBinaryCSDLL( this, BinaryConfig ));
}
}
ExtraModuleNames.AddRange(RulesExtraModuleNames);
}
}
public virtual void SetupDefaultGlobalEnvironment(
TargetInfo Target,
ref LinkEnvironmentConfiguration OutLinkEnvironmentConfiguration,
ref CPPEnvironmentConfiguration OutCPPEnvironmentConfiguration
)
{
// Default does nothing
}
/** Sets up the global compile and link environment for the target. */
public virtual void SetupGlobalEnvironment()
{
UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform);
UEToolChain ToolChain = UEToolChain.GetPlatformToolChain(BuildPlatform.GetCPPTargetPlatform(Platform));
ToolChain.SetUpGlobalEnvironment();
// Allow each target type (Game/Editor/Server) to set a default global environment state
{
LinkEnvironmentConfiguration RulesLinkEnvConfig = GlobalLinkEnvironment.Config;
CPPEnvironmentConfiguration RulesCPPEnvConfig = GlobalCompileEnvironment.Config;
SetupDefaultGlobalEnvironment(
new TargetInfo(Platform, Configuration, Rules.Type),
ref RulesLinkEnvConfig,
ref RulesCPPEnvConfig
);
// Copy them back...
GlobalLinkEnvironment.Config = RulesLinkEnvConfig;
GlobalCompileEnvironment.Config = RulesCPPEnvConfig;
}
// If there is a Rules object present, let it set things up before validation.
// This mirrors the behavior of the UEBuild<TARGET> setup, where the
// SetupGlobalEnvironment call would first set the target-specific values,
// and then call Base.SetupGlobalEnvironment
if (Rules != null)
{
LinkEnvironmentConfiguration RulesLinkEnvConfig = GlobalLinkEnvironment.Config;
CPPEnvironmentConfiguration RulesCPPEnvConfig = GlobalCompileEnvironment.Config;
// Don't let games override the global environment...
// This will potentially cause problems due to them generating a game-agnostic exe.
// RocketEditor is a special case.
//
bool bAllowGameOverride = !TargetRules.IsGameType(Rules.Type);
if (bAllowGameOverride ||
(Rules.ToString() == "UE4EditorTarget" && UnrealBuildTool.BuildingRocket()) || // @todo Rocket: Hard coded target name hack
Rules.ShouldCompileMonolithic(Platform, Configuration))
{
Rules.SetupGlobalEnvironment(
new TargetInfo(Platform, Configuration, Rules.Type),
ref RulesLinkEnvConfig,
ref RulesCPPEnvConfig
);
// Copy them back...
GlobalLinkEnvironment.Config = RulesLinkEnvConfig;
GlobalCompileEnvironment.Config = RulesCPPEnvConfig;
}
GlobalCompileEnvironment.Config.Definitions.Add(String.Format("IS_PROGRAM={0}", Rules.Type == TargetRules.TargetType.Program ? "1" : "0"));
}
// Validate UE configuration - needs to happen before setting any environment mojo and after argument parsing.
BuildPlatform.ValidateUEBuildConfiguration();
UEBuildConfiguration.ValidateConfiguration();
// Add the 'Engine/Source' path as a global include path for all modules
var EngineSourceDirectory = Path.GetFullPath( Path.Combine( "..", "..", "Engine", "Source" ) );
if( !Directory.Exists( EngineSourceDirectory ) )
{
throw new BuildException( "Couldn't find Engine/Source directory using relative path" );
}
GlobalCompileEnvironment.Config.IncludePaths.Add( EngineSourceDirectory );
//@todo.PLATFORM: Do any platform specific tool chain initialization here if required
if (BuildConfiguration.bUseMallocProfiler)
{
BuildConfiguration.bOmitFramePointers = false;
GlobalCompileEnvironment.Config.Definitions.Add("USE_MALLOC_PROFILER=1");
}
string OutputAppName = GetAppName();
// Mark it as a Rocket build
if (UnrealBuildTool.BuildingRocket() || UnrealBuildTool.RunningRocket())
{
GlobalCompileEnvironment.Config.Definitions.Add("UE_ROCKET=1");
}
else
{
GlobalCompileEnvironment.Config.Definitions.Add("UE_ROCKET=0");
}
// Rocket intermediates go to the project's intermediate folder. Rocket never writes to the engine intermediate folder. (Those files are immutable)
// Also, when compiling in monolithic, all intermediates go to the project's folder. This is because a project can change definitions that affects all engine translation
// units too, so they can't be shared between different targets. They are effectively project-specific engine intermediates.
if( UnrealBuildTool.RunningRocket() || ( UnrealBuildTool.HasUProjectFile() && ShouldCompileMonolithic() ) )
{
//@todo SAS: Add a true Debug mode!
var IntermediateConfiguration = Configuration;
if( UnrealBuildTool.RunningRocket() )
{
// Only Development and Shipping are supported for engine modules
if( IntermediateConfiguration != UnrealTargetConfiguration.Development && IntermediateConfiguration != UnrealTargetConfiguration.Shipping )
{
IntermediateConfiguration = UnrealTargetConfiguration.Development;
}
}
GlobalCompileEnvironment.Config.OutputDirectory = Path.Combine(BuildConfiguration.PlatformIntermediatePath, OutputAppName, IntermediateConfiguration.ToString());
if (ShouldCompileMonolithic())
{
GlobalCompileEnvironment.Config.OutputDirectory = Path.Combine(UnrealBuildTool.GetUProjectPath(), BuildConfiguration.PlatformIntermediateFolder, OutputAppName, IntermediateConfiguration.ToString());
}
}
else
{
GlobalCompileEnvironment.Config.OutputDirectory = Path.Combine(BuildConfiguration.PlatformIntermediatePath, OutputAppName, Configuration.ToString());
}
// By default, shadow source files for this target in the root OutputDirectory
GlobalCompileEnvironment.Config.LocalShadowDirectory = GlobalCompileEnvironment.Config.OutputDirectory;
GlobalCompileEnvironment.Config.Definitions.Add("UNICODE");
GlobalCompileEnvironment.Config.Definitions.Add("_UNICODE");
GlobalCompileEnvironment.Config.Definitions.Add("__UNREAL__");
GlobalCompileEnvironment.Config.Definitions.Add(String.Format("IS_MONOLITHIC={0}", ShouldCompileMonolithic() ? "1" : "0"));
if (UEBuildConfiguration.bCompileAgainstEngine)
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_ENGINE=1");
GlobalCompileEnvironment.Config.Definitions.Add(
String.Format("WITH_UNREAL_DEVELOPER_TOOLS={0}",UEBuildConfiguration.bBuildDeveloperTools ? "1" : "0"));
// Automatically include CoreUObject
UEBuildConfiguration.bCompileAgainstCoreUObject = true;
}
else
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_ENGINE=0");
// Can't have developer tools w/out engine
GlobalCompileEnvironment.Config.Definitions.Add("WITH_UNREAL_DEVELOPER_TOOLS=0");
}
if (UEBuildConfiguration.bCompileAgainstCoreUObject)
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_COREUOBJECT=1");
}
else
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_COREUOBJECT=0");
}
if (UEBuildConfiguration.bCompileWithStatsWithoutEngine)
{
GlobalCompileEnvironment.Config.Definitions.Add("USE_STATS_WITHOUT_ENGINE=1");
}
else
{
GlobalCompileEnvironment.Config.Definitions.Add("USE_STATS_WITHOUT_ENGINE=0");
}
if (UEBuildConfiguration.bCompileWithPluginSupport)
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_PLUGIN_SUPPORT=1");
}
else
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_PLUGIN_SUPPORT=0");
}
if (UEBuildConfiguration.bUseLoggingInShipping)
{
GlobalCompileEnvironment.Config.Definitions.Add("USE_LOGGING_IN_SHIPPING=1");
}
else
{
GlobalCompileEnvironment.Config.Definitions.Add("USE_LOGGING_IN_SHIPPING=0");
}
// Propagate whether we want a lean and mean build to the C++ code.
if (UEBuildConfiguration.bCompileLeanAndMeanUE)
{
GlobalCompileEnvironment.Config.Definitions.Add("UE_BUILD_MINIMAL=1");
}
else
{
GlobalCompileEnvironment.Config.Definitions.Add("UE_BUILD_MINIMAL=0");
}
// Disable editor when its not needed
if (BuildPlatform.ShouldNotBuildEditor(Platform, Configuration) == true)
{
UEBuildConfiguration.bBuildEditor = false;
}
// Disable the DDC and a few other things related to preparing assets
if (BuildPlatform.BuildRequiresCookedData(Platform, Configuration) == true)
{
UEBuildConfiguration.bBuildRequiresCookedData = true;
}
// bBuildEditor has now been set appropriately for all platforms, so this is here to make sure the #define
if (UEBuildConfiguration.bBuildEditor)
{
// Must have editor only data if building the editor.
UEBuildConfiguration.bBuildWithEditorOnlyData = true;
GlobalCompileEnvironment.Config.Definitions.Add("WITH_EDITOR=1");
}
else if (!GlobalCompileEnvironment.Config.Definitions.Contains("WITH_EDITOR=0"))
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_EDITOR=0");
}
if (UEBuildConfiguration.bBuildWithEditorOnlyData == false)
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_EDITORONLY_DATA=0");
}
// Check if server-only code should be compiled out.
if (UEBuildConfiguration.bWithServerCode == true)
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_SERVER_CODE=1");
}
else
{
GlobalCompileEnvironment.Config.Definitions.Add("WITH_SERVER_CODE=0");
}
// tell the compiled code the name of the UBT platform (this affects folder on disk, etc that the game may need to know)
GlobalCompileEnvironment.Config.Definitions.Add("UBT_COMPILED_PLATFORM=" + Platform.ToString());
// Initialize the compile and link environments for the platform and configuration.
SetUpPlatformEnvironment();
SetUpConfigurationEnvironment();
// Validates current settings and updates if required.
BuildConfiguration.ValidateConfiguration(
GlobalCompileEnvironment.Config.TargetConfiguration,
GlobalCompileEnvironment.Config.TargetPlatform,
GlobalCompileEnvironment.Config.bCreateDebugInfo);
}
void SetUpPlatformEnvironment()
{
UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(Platform);
CPPTargetPlatform MainCompilePlatform = BuildPlatform.GetCPPTargetPlatform(Platform);
GlobalLinkEnvironment.Config.TargetPlatform = MainCompilePlatform;
GlobalCompileEnvironment.Config.TargetPlatform = MainCompilePlatform;
string ActiveArchitecture = BuildPlatform.GetActiveArchitecture();
GlobalCompileEnvironment.Config.TargetArchitecture = ActiveArchitecture;
GlobalLinkEnvironment.Config.TargetArchitecture = ActiveArchitecture;
// Set up the platform-specific environment.
BuildPlatform.SetUpEnvironment(this);
}
void SetUpConfigurationEnvironment()
{
UEBuildPlatform.GetBuildPlatform(Platform).SetUpConfigurationEnvironment(this);
// Check to see if we're compiling a library or not
bool bIsBuildingDLL = Path.GetExtension(OutputPath).ToUpperInvariant() == ".DLL";
GlobalCompileEnvironment.Config.bIsBuildingDLL = bIsBuildingDLL;
GlobalLinkEnvironment.Config.bIsBuildingDLL = bIsBuildingDLL;
bool bIsBuildingLibrary = Path.GetExtension(OutputPath).ToUpperInvariant() == ".LIB";
GlobalCompileEnvironment.Config.bIsBuildingLibrary = bIsBuildingLibrary;
GlobalLinkEnvironment.Config.bIsBuildingLibrary = bIsBuildingLibrary;
}
/** Constructs a TargetInfo object for this target. */
public TargetInfo GetTargetInfo()
{
if(Rules == null)
{
return new TargetInfo(Platform, Configuration);
}
else
{
return new TargetInfo(Platform, Configuration, Rules.Type);
}
}
/** Registers a module with this target. */
public void RegisterModule(UEBuildModule Module)
{
Debug.Assert(Module.Target == this);
Modules.Add(Module.Name, Module);
}
/** Finds a module given its name. Throws an exception if the module couldn't be found. */
public UEBuildModule FindOrCreateModuleByName(string ModuleName)
{
UEBuildModule Module;
if (!Modules.TryGetValue(ModuleName, out Module))
{
TargetInfo TargetInfo = GetTargetInfo();
// Create the module! (It will be added to our hash table in its constructor)
// Figure out whether we need to build this module
bool bBuildFiles = OnlyModules.Count == 0 || OnlyModules.Any(x => x.OnlyModuleName == ModuleName);
// @todo projectfiles: Cross-platform modules can appear here during project generation, but they may have already
// been filtered out by the project generator. This causes the projects to not be added to directories properly.
string ModuleFileName;
var RulesObject = RulesCompiler.CreateModuleRules(ModuleName, GetTargetInfo(), out ModuleFileName);
var ModuleDirectory = Path.GetDirectoryName(ModuleFileName);
// Making an assumption here that any project file path that contains '../../'
// is NOT from the engine and therefore must be an application-specific module.
bool IsGameModule = false;
string ApplicationOutputPath = "";
var ModuleFileRelativeToEngineDirectory = Utils.MakePathRelativeTo(ModuleFileName, ProjectFileGenerator.EngineRelativePath);
if (ModuleFileRelativeToEngineDirectory.StartsWith("..") || Path.IsPathRooted(ModuleFileRelativeToEngineDirectory))
{
ApplicationOutputPath = ModuleFileName;
int SourceIndex = ApplicationOutputPath.LastIndexOf(Path.DirectorySeparatorChar + "Source" + Path.DirectorySeparatorChar);
if (SourceIndex != -1)
{
ApplicationOutputPath = ApplicationOutputPath.Substring(0, SourceIndex + 1);
IsGameModule = true;
}
else
{
throw new BuildException("Unable to parse application directory for module '{0}' ({1}). Is it in '../../<APP>/Source'?",
ModuleName, ModuleFileName);
}
}
// Get the base directory for paths referenced by the module. If the module's under the UProject source directory use that, otherwise leave it relative to the Engine source directory.
string ProjectSourcePath = UnrealBuildTool.GetUProjectSourcePath();
if (ProjectSourcePath != null)
{
string FullProjectSourcePath = Path.GetFullPath(ProjectSourcePath);
if (Utils.IsFileUnderDirectory(ModuleFileName, FullProjectSourcePath))
{
RulesObject.PublicIncludePaths = CombinePathList(ProjectSourcePath, RulesObject.PublicIncludePaths);
RulesObject.PrivateIncludePaths = CombinePathList(ProjectSourcePath, RulesObject.PrivateIncludePaths);
RulesObject.PublicLibraryPaths = CombinePathList(ProjectSourcePath, RulesObject.PublicLibraryPaths);
RulesObject.PublicAdditionalShadowFiles = CombinePathList(ProjectSourcePath, RulesObject.PublicAdditionalShadowFiles);
}
}
// Don't generate include paths for third party modules. They don't follow our conventions.
if (RulesObject.Type != ModuleRules.ModuleType.External)
{
// Add the default include paths to the module rules, if they exist.
RulesCompiler.AddDefaultIncludePathsToModuleRules(this, ModuleName, ModuleFileName, ModuleFileRelativeToEngineDirectory, IsGameModule: IsGameModule, RulesObject: ref RulesObject);
}
IntelliSenseGatherer IntelliSenseGatherer = null;
List<FileItem> ModuleSourceFiles = new List<FileItem>();
if (RulesObject.Type == ModuleRules.ModuleType.CPlusPlus || RulesObject.Type == ModuleRules.ModuleType.CPlusPlusCLR)
{
ProjectFile ProjectFile = null;
if (ProjectFileGenerator.bGenerateProjectFiles && ProjectFileGenerator.ModuleToProjectFileMap.TryGetValue(ModuleName, out ProjectFile))
{
IntelliSenseGatherer = ProjectFile;
}
if (!ProjectFileGenerator.bGenerateProjectFiles && bBuildFiles) // We don't care about actual source files when generating projects, as these are discovered separately
{
// So all we care about are the game module and/or plugins.
//@todo Rocket: This assumes plugins that have source will be under the game folder...
if (!UnrealBuildTool.RunningRocket() || Utils.IsFileUnderDirectory(ModuleFileName, UnrealBuildTool.GetUProjectPath()))
{
var SourceFilePaths = new List<string>();
if (ProjectFile != null)
{
foreach (var SourceFile in ProjectFile.SourceFiles)
{
SourceFilePaths.Add(SourceFile.FilePath);
}
}
else
{
// Don't have a project file for this module with the source file names cached already, so find the source files ourselves
SourceFilePaths = SourceFileSearch.FindModuleSourceFiles(
ModuleRulesFile: ModuleFileName,
ExcludeNoRedistFiles: false);
}
ModuleSourceFiles = GetCPlusPlusFilesToBuild(SourceFilePaths, ModuleDirectory, Platform);
}
}
}
// Get the type of module we're creating
UEBuildModuleType ModuleType = IsGameModule ? UEBuildModuleType.GameModule : UEBuildModuleType.EngineModule;
// Now, go ahead and create the module builder instance
switch (RulesObject.Type)
{
case ModuleRules.ModuleType.CPlusPlus:
{
Module = new UEBuildModuleCPP(
InTarget: this,
InName: ModuleName,
InType: ModuleType,
InModuleDirectory: ModuleDirectory,
InOutputDirectory: ApplicationOutputPath,
InIntelliSenseGatherer: IntelliSenseGatherer,
InSourceFiles: ModuleSourceFiles,
InPublicSystemIncludePaths: RulesObject.PublicSystemIncludePaths,
InPublicIncludePaths: RulesObject.PublicIncludePaths,
InDefinitions: RulesObject.Definitions,
InPublicIncludePathModuleNames: RulesObject.PublicIncludePathModuleNames,
InPublicDependencyModuleNames: RulesObject.PublicDependencyModuleNames,
InPublicDelayLoadDLLs: RulesObject.PublicDelayLoadDLLs,
InPublicAdditionalLibraries: RulesObject.PublicAdditionalLibraries,
InPublicFrameworks: RulesObject.PublicFrameworks,
InPublicAdditionalFrameworks: RulesObject.PublicAdditionalFrameworks,
InPrivateIncludePaths: RulesObject.PrivateIncludePaths,
InPrivateIncludePathModuleNames: RulesObject.PrivateIncludePathModuleNames,
InPrivateDependencyModuleNames: RulesObject.PrivateDependencyModuleNames,
InCircularlyReferencedDependentModules: RulesObject.CircularlyReferencedDependentModules,
InDynamicallyLoadedModuleNames: RulesObject.DynamicallyLoadedModuleNames,
InPlatformSpecificDynamicallyLoadedModuleNames: RulesObject.PlatformSpecificDynamicallyLoadedModuleNames,
InOptimizeCode: RulesObject.OptimizeCode,
InAllowSharedPCH: (RulesObject.PCHUsage == ModuleRules.PCHUsageMode.NoSharedPCHs) ? false : true,
InSharedPCHHeaderFile: RulesObject.SharedPCHHeaderFile,
InUseRTTI: RulesObject.bUseRTTI,
InEnableBufferSecurityChecks: RulesObject.bEnableBufferSecurityChecks,
InFasterWithoutUnity: RulesObject.bFasterWithoutUnity,
InMinFilesUsingPrecompiledHeaderOverride: RulesObject.MinFilesUsingPrecompiledHeaderOverride,
InEnableExceptions: RulesObject.bEnableExceptions,
InEnableInlining: RulesObject.bEnableInlining
);
}
break;
case ModuleRules.ModuleType.CPlusPlusCLR:
{
Module = new UEBuildModuleCPPCLR(
InTarget: this,
InName: ModuleName,
InType: ModuleType,
InModuleDirectory: ModuleDirectory,
InOutputDirectory: ApplicationOutputPath,
InIntelliSenseGatherer: IntelliSenseGatherer,
InSourceFiles: ModuleSourceFiles,
InDefinitions: RulesObject.Definitions,
InPublicSystemIncludePaths: RulesObject.PublicSystemIncludePaths,
InPublicIncludePaths: RulesObject.PublicIncludePaths,
InPublicIncludePathModuleNames: RulesObject.PublicIncludePathModuleNames,
InPublicDependencyModuleNames: RulesObject.PublicDependencyModuleNames,
InPublicDelayLoadDLLs: RulesObject.PublicDelayLoadDLLs,
InPublicAdditionalLibraries: RulesObject.PublicAdditionalLibraries,
InPublicFrameworks: RulesObject.PublicFrameworks,
InPublicAdditionalFrameworks: RulesObject.PublicAdditionalFrameworks,
InPrivateIncludePaths: RulesObject.PrivateIncludePaths,
InPrivateIncludePathModuleNames: RulesObject.PrivateIncludePathModuleNames,
InPrivateDependencyModuleNames: RulesObject.PrivateDependencyModuleNames,
InPrivateAssemblyReferences: RulesObject.PrivateAssemblyReferences,
InCircularlyReferencedDependentModules: RulesObject.CircularlyReferencedDependentModules,
InDynamicallyLoadedModuleNames: RulesObject.DynamicallyLoadedModuleNames,
InPlatformSpecificDynamicallyLoadedModuleNames: RulesObject.PlatformSpecificDynamicallyLoadedModuleNames,
InOptimizeCode: RulesObject.OptimizeCode,
InAllowSharedPCH: (RulesObject.PCHUsage == ModuleRules.PCHUsageMode.NoSharedPCHs) ? false : true,
InSharedPCHHeaderFile: RulesObject.SharedPCHHeaderFile,
InUseRTTI: RulesObject.bUseRTTI,
InEnableBufferSecurityChecks: RulesObject.bEnableBufferSecurityChecks,
InFasterWithoutUnity: RulesObject.bFasterWithoutUnity,
InMinFilesUsingPrecompiledHeaderOverride: RulesObject.MinFilesUsingPrecompiledHeaderOverride,
InEnableExceptions: RulesObject.bEnableExceptions,
InEnableInlining: RulesObject.bEnableInlining
);
}
break;
case ModuleRules.ModuleType.External:
Module = new UEBuildExternalModule(
InTarget: this,
InName: ModuleName,
InType: ModuleType,
InModuleDirectory: ModuleDirectory,
InOutputDirectory: ApplicationOutputPath,
InPublicDefinitions: RulesObject.Definitions,
InPublicSystemIncludePaths: RulesObject.PublicSystemIncludePaths,
InPublicIncludePaths: RulesObject.PublicIncludePaths,
InPublicLibraryPaths: RulesObject.PublicLibraryPaths,
InPublicAdditionalLibraries: RulesObject.PublicAdditionalLibraries,
InPublicFrameworks: RulesObject.PublicFrameworks,
InPublicAdditionalFrameworks: RulesObject.PublicAdditionalFrameworks,
InPublicAdditionalShadowFiles: RulesObject.PublicAdditionalShadowFiles,
InPublicDependencyModuleNames: RulesObject.PublicDependencyModuleNames,
InPublicDelayLoadDLLs: RulesObject.PublicDelayLoadDLLs);
break;
default:
throw new BuildException("Unrecognized module type specified by 'Rules' object {0}", RulesObject.ToString());
}
UnrealTargetPlatform Only = UnrealBuildTool.GetOnlyPlatformSpecificFor();
if (Only == UnrealTargetPlatform.Unknown && UnrealBuildTool.SkipNonHostPlatforms())
{
Only = Platform;
}
// Allow all build platforms to 'adjust' the module setting.
// This will allow undisclosed platforms to make changes without
// exposing information about the platform in publicly accessible
// locations.
UEBuildPlatform.PlatformModifyNewlyLoadedModule(Module, TargetInfo, Only);
}
return Module;
}
/** Finds a module given its name. Throws an exception if the module couldn't be found. */
public UEBuildModule GetModuleByName(string Name)
{
UEBuildModule Result;
if (Modules.TryGetValue(Name, out Result))
{
return Result;
}
else
{
throw new BuildException("Couldn't find referenced module '{0}'.", Name);
}
}
/// <summary>
/// Combines a list of paths with a base path.
/// </summary>
/// <param name="BasePath">Base path to combine with. May be null or empty.</param>
/// <param name="PathList">List of input paths to combine with. May be null.</param>
/// <returns>List of paths relative The build module object for the specified build rules source file</returns>
private static List<string> CombinePathList(string BasePath, List<string> PathList)
{
List<string> NewPathList = PathList;
if (PathList != null && !String.IsNullOrEmpty(BasePath))
{
NewPathList = new List<string>();
foreach (string Path in PathList)
{
NewPathList.Add(System.IO.Path.Combine(BasePath, Path));
}
}
return NewPathList;
}
/// <summary>
/// Given a list of source files for a module, filters them into a list of files that should actually be included in a build
/// </summary>
/// <param name="SourceFiles">Original list of files, which may contain non-source</param>
/// <param name="SourceFilesBaseDirectory">Directory that the source files are in</param>
/// <param name="TargetPlatform">The platform we're going to compile for</param>
/// <returns>The list of source files to actually compile</returns>
static List<FileItem> GetCPlusPlusFilesToBuild(List<string> SourceFiles, string SourceFilesBaseDirectory, UnrealTargetPlatform TargetPlatform)
{
// Make a list of all platform name strings that we're *not* currently compiling, to speed
// up file path comparisons later on
var SupportedPlatforms = new List<UnrealTargetPlatform>();
SupportedPlatforms.Add(TargetPlatform);
var OtherPlatformNameStrings = Utils.MakeListOfUnsupportedPlatforms(SupportedPlatforms);
// @todo projectfiles: Consider saving out cached list of source files for modules so we don't need to harvest these each time
var FilteredFileItems = new List<FileItem>();
FilteredFileItems.Capacity = SourceFiles.Count;
// @todo projectfiles: hard-coded source file set. Should be made extensible by platform tool chains.
var CompilableSourceFileTypes = new string[]
{
".cpp",
".c",
".cc",
".mm",
".m",
".rc",
".manifest"
};
// When generating project files, we have no file to extract source from, so we'll locate the code files manually
foreach (var SourceFilePath in SourceFiles)
{
// We're only able to compile certain types of files
bool IsCompilableSourceFile = false;
foreach (var CurExtension in CompilableSourceFileTypes)
{
if (SourceFilePath.EndsWith(CurExtension, StringComparison.InvariantCultureIgnoreCase))
{
IsCompilableSourceFile = true;
break;
}
}
if (IsCompilableSourceFile)
{
if (SourceFilePath.StartsWith(SourceFilesBaseDirectory + Path.DirectorySeparatorChar))
{
// Store the path as relative to the project file
var RelativeFilePath = Utils.MakePathRelativeTo(SourceFilePath, SourceFilesBaseDirectory);
// All compiled files should always be in a sub-directory under the project file directory. We enforce this here.
if (Path.IsPathRooted(RelativeFilePath) || RelativeFilePath.StartsWith(".."))
{
throw new BuildException("Error: Found source file {0} in project whose path was not relative to the base directory of the source files", RelativeFilePath);
}
// Check for source files that don't belong to the platform we're currently compiling. We'll filter
// those source files out
bool IncludeThisFile = true;
foreach (var CurPlatformName in OtherPlatformNameStrings)
{
if (RelativeFilePath.IndexOf(Path.DirectorySeparatorChar + CurPlatformName + Path.DirectorySeparatorChar, StringComparison.InvariantCultureIgnoreCase) != -1
|| RelativeFilePath.StartsWith(CurPlatformName + Path.DirectorySeparatorChar))
{
IncludeThisFile = false;
break;
}
}
if (IncludeThisFile)
{
FilteredFileItems.Add(FileItem.GetItemByFullPath(SourceFilePath));
}
}
}
}
// @todo projectfiles: Consider enabling this error but changing it to a warning instead. It can fire for
// projects that are being digested for IntelliSense (because the module was set as a cross-
// platform dependency), but all of their source files were filtered out due to platform masking
// in the project generator
bool AllowEmptyProjects = true;
if (!AllowEmptyProjects)
{
if (FilteredFileItems.Count == 0)
{
throw new BuildException("Could not find any valid source files for base directory {0}. Project has {1} files in it", SourceFilesBaseDirectory, SourceFiles.Count);
}
}
return FilteredFileItems;
}
}
}