Files
UnrealEngineUWP/Engine/Source/Programs/UnrealBuildTool/Configuration/UEBuildModuleCPP.cs
Jason Bestimt 17c16f57bb #DEV-VR - Manual merge of from DevMain @ CL 4166765
#RB:none
#CodeReview: nick.whiting

[CL 4169831 by Jason Bestimt in Dev-VR branch]
2018-06-28 11:08:34 -04:00

1442 lines
60 KiB
C#

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tools.DotNETCommon;
namespace UnrealBuildTool
{
/// <summary>
/// A module that is compiled from C++ code.
/// </summary>
class UEBuildModuleCPP : UEBuildModule
{
public class AutoGenerateCppInfoClass
{
public class BuildInfoClass
{
/// <summary>
/// The wildcard of the *.gen.cpp file which was generated for the module
/// </summary>
public readonly string FileWildcard;
public BuildInfoClass(string InWildcard)
{
Debug.Assert(InWildcard != null);
FileWildcard = InWildcard;
}
}
/// <summary>
/// Information about how to build the .gen.cpp files. If this is null, then we're not building .gen.cpp files for this module.
/// </summary>
public BuildInfoClass BuildInfo;
public AutoGenerateCppInfoClass(BuildInfoClass InBuildInfo)
{
BuildInfo = InBuildInfo;
}
}
/// <summary>
/// Information about the .gen.cpp file. If this is null then this module doesn't have any UHT-produced code.
/// </summary>
public AutoGenerateCppInfoClass AutoGenerateCppInfo = null;
public class SourceFilesClass
{
public readonly List<FileItem> MissingFiles = new List<FileItem>();
public readonly List<FileItem> CPPFiles = new List<FileItem>();
public readonly List<FileItem> CFiles = new List<FileItem>();
public readonly List<FileItem> CCFiles = new List<FileItem>();
public readonly List<FileItem> MMFiles = new List<FileItem>();
public readonly List<FileItem> RCFiles = new List<FileItem>();
public readonly List<FileItem> OtherFiles = new List<FileItem>();
public int Count
{
get
{
return MissingFiles.Count +
CPPFiles.Count +
CFiles.Count +
CCFiles.Count +
MMFiles.Count +
RCFiles.Count +
OtherFiles.Count;
}
}
/// <summary>
/// Copy from list to list helper.
/// </summary>
/// <param name="From">Source list.</param>
/// <param name="To">Destination list.</param>
private static void CopyFromListToList(List<FileItem> From, List<FileItem> To)
{
To.Clear();
To.AddRange(From);
}
/// <summary>
/// Copies file lists from other SourceFilesClass to this.
/// </summary>
/// <param name="Other">Source object.</param>
public void CopyFrom(SourceFilesClass Other)
{
CopyFromListToList(Other.MissingFiles, MissingFiles);
CopyFromListToList(Other.CPPFiles, CPPFiles);
CopyFromListToList(Other.CFiles, CFiles);
CopyFromListToList(Other.CCFiles, CCFiles);
CopyFromListToList(Other.MMFiles, MMFiles);
CopyFromListToList(Other.RCFiles, RCFiles);
CopyFromListToList(Other.OtherFiles, OtherFiles);
}
}
/// <summary>
/// Adds additional source cpp files for this module.
/// </summary>
/// <param name="Files">Files to add.</param>
public void AddAdditionalCPPFiles(IEnumerable<FileItem> Files)
{
SourceFiles.AddRange(Files);
SourceFilesToBuild.CPPFiles.AddRange(Files);
}
/// <summary>
/// All the source files for this module
/// </summary>
public readonly List<FileItem> SourceFiles = new List<FileItem>();
/// <summary>
/// A list of the absolute paths of source files to be built in this module.
/// </summary>
public readonly SourceFilesClass SourceFilesToBuild = new SourceFilesClass();
/// <summary>
/// A list of the source files that were found for the module.
/// </summary>
public readonly SourceFilesClass SourceFilesFound = new SourceFilesClass();
/// <summary>
/// The directory for this module's object files
/// </summary>
public readonly DirectoryReference IntermediateDirectory;
/// <summary>
/// The directory for this module's generated code
/// </summary>
public readonly DirectoryReference GeneratedCodeDirectory;
public List<DirectoryReference> IncludeSearchPaths = new List<DirectoryReference>();
public class ProcessedDependenciesClass
{
/// <summary>
/// The file, if any, which is used as the unique PCH for this module
/// </summary>
public FileItem UniquePCHHeaderFile = null;
}
/// <summary>
/// The processed dependencies for the class
/// </summary>
public ProcessedDependenciesClass ProcessedDependencies = null;
/// <summary>
/// List of invalid include directives. These are buffered up and output before we start compiling.
/// </summary>
public List<string> InvalidIncludeDirectiveMessages;
public IEnumerable<string> FindGeneratedCppFiles()
{
return ((null == GeneratedCodeDirectory) || !DirectoryReference.Exists(GeneratedCodeDirectory))
? Enumerable.Empty<string>()
: DirectoryReference.EnumerateFiles(GeneratedCodeDirectory, "*.gen.cpp", SearchOption.TopDirectoryOnly).Select((Dir) => Dir.FullName);
}
protected override void GetReferencedDirectories(HashSet<DirectoryReference> Directories)
{
base.GetReferencedDirectories(Directories);
foreach(FileItem SourceFile in SourceFiles)
{
Directories.Add(SourceFile.Location.Directory);
}
}
/// <summary>
/// Categorizes source files into per-extension buckets
/// </summary>
private static void CategorizeSourceFiles(IEnumerable<FileItem> InSourceFiles, SourceFilesClass OutSourceFiles)
{
foreach (FileItem SourceFile in InSourceFiles)
{
string Extension = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant();
if (!SourceFile.bExists)
{
OutSourceFiles.MissingFiles.Add(SourceFile);
}
else if (Extension == ".CPP")
{
OutSourceFiles.CPPFiles.Add(SourceFile);
}
else if (Extension == ".C")
{
OutSourceFiles.CFiles.Add(SourceFile);
}
else if (Extension == ".CC")
{
OutSourceFiles.CCFiles.Add(SourceFile);
}
else if (Extension == ".MM" || Extension == ".M")
{
OutSourceFiles.MMFiles.Add(SourceFile);
}
else if (Extension == ".RC")
{
OutSourceFiles.RCFiles.Add(SourceFile);
}
else
{
OutSourceFiles.OtherFiles.Add(SourceFile);
}
}
}
/// <summary>
/// List of whitelisted circular dependencies. Please do NOT add new modules here; refactor to allow the modules to be decoupled instead.
/// </summary>
static readonly KeyValuePair<string, string>[] WhitelistedCircularDependencies =
{
new KeyValuePair<string, string>("Engine", "Landscape"),
new KeyValuePair<string, string>("Engine", "UMG"),
new KeyValuePair<string, string>("Engine", "GameplayTags"),
new KeyValuePair<string, string>("Engine", "MaterialShaderQualitySettings"),
new KeyValuePair<string, string>("Engine", "UnrealEd"),
new KeyValuePair<string, string>("PacketHandler", "ReliabilityHandlerComponent"),
new KeyValuePair<string, string>("GameplayDebugger", "AIModule"),
new KeyValuePair<string, string>("GameplayDebugger", "GameplayTasks"),
new KeyValuePair<string, string>("Engine", "CinematicCamera"),
new KeyValuePair<string, string>("Engine", "CollisionAnalyzer"),
new KeyValuePair<string, string>("Engine", "LogVisualizer"),
new KeyValuePair<string, string>("Engine", "Kismet"),
new KeyValuePair<string, string>("Landscape", "UnrealEd"),
new KeyValuePair<string, string>("Landscape", "MaterialUtilities"),
new KeyValuePair<string, string>("LocalizationDashboard", "LocalizationService"),
new KeyValuePair<string, string>("LocalizationDashboard", "MainFrame"),
new KeyValuePair<string, string>("LocalizationDashboard", "TranslationEditor"),
new KeyValuePair<string, string>("Documentation", "SourceControl"),
new KeyValuePair<string, string>("UnrealEd", "GraphEditor"),
new KeyValuePair<string, string>("UnrealEd", "Kismet"),
new KeyValuePair<string, string>("UnrealEd", "AudioEditor"),
new KeyValuePair<string, string>("BlueprintGraph", "KismetCompiler"),
new KeyValuePair<string, string>("BlueprintGraph", "UnrealEd"),
new KeyValuePair<string, string>("BlueprintGraph", "GraphEditor"),
new KeyValuePair<string, string>("BlueprintGraph", "Kismet"),
new KeyValuePair<string, string>("BlueprintGraph", "CinematicCamera"),
new KeyValuePair<string, string>("ConfigEditor", "PropertyEditor"),
new KeyValuePair<string, string>("SourceControl", "UnrealEd"),
new KeyValuePair<string, string>("Kismet", "BlueprintGraph"),
new KeyValuePair<string, string>("Kismet", "UMGEditor"),
new KeyValuePair<string, string>("MovieSceneTools", "Sequencer"),
new KeyValuePair<string, string>("Sequencer", "MovieSceneTools"),
new KeyValuePair<string, string>("AIModule", "AITestSuite"),
new KeyValuePair<string, string>("GameplayTasks", "UnrealEd"),
new KeyValuePair<string, string>("AnimGraph", "UnrealEd"),
new KeyValuePair<string, string>("AnimGraph", "GraphEditor"),
new KeyValuePair<string, string>("MaterialUtilities", "Landscape"),
new KeyValuePair<string, string>("HierarchicalLODOutliner", "UnrealEd"),
new KeyValuePair<string, string>("PixelInspectorModule", "UnrealEd"),
new KeyValuePair<string, string>("GameplayAbilitiesEditor", "BlueprintGraph"),
new KeyValuePair<string, string>("UnrealEd", "ViewportInteraction"),
new KeyValuePair<string, string>("UnrealEd", "VREditor"),
new KeyValuePair<string, string>("LandscapeEditor", "ViewportInteraction"),
new KeyValuePair<string, string>("LandscapeEditor", "VREditor"),
new KeyValuePair<string, string>("FoliageEdit", "ViewportInteraction"),
new KeyValuePair<string, string>("FoliageEdit", "VREditor"),
new KeyValuePair<string, string>("MeshPaint", "ViewportInteraction"),
new KeyValuePair<string, string>("MeshPaint", "VREditor"),
new KeyValuePair<string, string>("MeshPaintMode", "ViewportInteraction"),
new KeyValuePair<string, string>("MeshPaintMode", "VREditor"),
new KeyValuePair<string, string>("Sequencer", "ViewportInteraction"),
new KeyValuePair<string, string>("NavigationSystem", "UnrealEd"),
};
public UEBuildModuleCPP(
string InName,
UHTModuleType InType,
DirectoryReference InModuleDirectory,
DirectoryReference InIntermediateDirectory,
DirectoryReference InGeneratedCodeDirectory,
IEnumerable<FileItem> InSourceFiles,
ModuleRules InRules,
bool bInBuildSourceFiles,
FileReference InRulesFile,
List<RuntimeDependency> InRuntimeDependencies
)
: base(
InName,
InType,
InModuleDirectory,
InRules,
InRulesFile,
InRuntimeDependencies
)
{
IntermediateDirectory = InIntermediateDirectory;
GeneratedCodeDirectory = InGeneratedCodeDirectory;
SourceFiles = InSourceFiles.ToList();
CategorizeSourceFiles(InSourceFiles, SourceFilesFound);
if (bInBuildSourceFiles)
{
SourceFilesToBuild.CopyFrom(SourceFilesFound);
}
foreach (string Def in PublicDefinitions)
{
Log.TraceVerbose("Compile Env {0}: {1}", Name, Def);
}
foreach (string Def in Rules.PrivateDefinitions)
{
Log.TraceVerbose("Compile Env {0}: {1}", Name, Def);
}
foreach(string CircularlyReferencedModuleName in Rules.CircularlyReferencedDependentModules)
{
if(CircularlyReferencedModuleName != "BlueprintContext" && !WhitelistedCircularDependencies.Any(x => x.Key == Name && x.Value == CircularlyReferencedModuleName))
{
Log.TraceWarning("Found reference between '{0}' and '{1}'. Support for circular references is being phased out; please do not introduce new ones.", Name, CircularlyReferencedModuleName);
}
}
AddDefaultIncludePaths();
}
/// <summary>
/// Add the default include paths for this module to its settings
/// </summary>
private void AddDefaultIncludePaths()
{
// Add the module's parent directory to the public include paths, so other modules may include headers from it explicitly.
PublicIncludePaths.Add(ModuleDirectory.ParentDirectory);
// Add the base directory to the legacy include paths.
LegacyPublicIncludePaths.Add(ModuleDirectory);
// Add the 'classes' directory, if it exists
DirectoryReference ClassesDirectory = DirectoryReference.Combine(ModuleDirectory, "Classes");
if (DirectoryLookupCache.DirectoryExists(ClassesDirectory))
{
PublicIncludePaths.Add(ClassesDirectory);
}
// Add all the public directories
DirectoryReference PublicDirectory = DirectoryReference.Combine(ModuleDirectory, "Public");
if (DirectoryLookupCache.DirectoryExists(PublicDirectory))
{
PublicIncludePaths.Add(PublicDirectory);
FileSystemName[] ExcludedFolderNames = UEBuildPlatform.GetBuildPlatform(Rules.Target.Platform).GetExcludedFolderNames();
foreach (DirectoryReference PublicSubDirectory in DirectoryLookupCache.EnumerateDirectoriesRecursively(PublicDirectory))
{
if(!PublicSubDirectory.ContainsAnyNames(ExcludedFolderNames, PublicDirectory))
{
LegacyPublicIncludePaths.Add(PublicSubDirectory);
}
}
}
// Add the base private directory for this module
DirectoryReference PrivateDirectory = DirectoryReference.Combine(ModuleDirectory, "Private");
if(DirectoryLookupCache.DirectoryExists(PrivateDirectory))
{
PrivateIncludePaths.Add(PrivateDirectory);
}
}
/// <summary>
/// Path to the precompiled manifest location
/// </summary>
public virtual FileReference PrecompiledManifestLocation
{
get { return FileReference.Combine(IntermediateDirectory, String.Format("{0}.precompiled", Name)); }
}
/// <summary>
/// Gathers intellisense data for the project file containing this module
/// </summary>
/// <param name="Target">The target being built</param>
/// <param name="BinaryCompileEnvironment">The inherited compile environment for this module</param>
/// <param name="ProjectFile">The project file containing this module</param>
public void GatherDataForProjectFile(ReadOnlyTargetRules Target, CppCompileEnvironment BinaryCompileEnvironment, ProjectFile ProjectFile)
{
CppCompileEnvironment ModuleCompileEnvironment = CreateModuleCompileEnvironment(Target, BinaryCompileEnvironment);
ProjectFile.AddIntelliSensePreprocessorDefinitions(ModuleCompileEnvironment.Definitions);
ProjectFile.AddInteliiSenseIncludePaths(ModuleCompileEnvironment.IncludePaths.SystemIncludePaths, true);
ProjectFile.AddInteliiSenseIncludePaths(ModuleCompileEnvironment.IncludePaths.UserIncludePaths, false);
}
/// <summary>
/// Sets up the environment for compiling any module that includes the public interface of this module.
/// </summary>
public override void AddModuleToCompileEnvironment(
UEBuildBinary SourceBinary,
HashSet<DirectoryReference> IncludePaths,
HashSet<DirectoryReference> SystemIncludePaths,
List<string> Definitions,
List<UEBuildFramework> AdditionalFrameworks,
bool bLegacyPublicIncludePaths
)
{
if(AutoGenerateCppInfo != null)
{
IncludePaths.Add(GeneratedCodeDirectory);
}
base.AddModuleToCompileEnvironment(SourceBinary, IncludePaths, SystemIncludePaths, Definitions, AdditionalFrameworks, bLegacyPublicIncludePaths);
}
// UEBuildModule interface.
public override List<FileItem> Compile(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment BinaryCompileEnvironment, List<PrecompiledHeaderTemplate> SharedPCHs, ISourceFileWorkingSet WorkingSet, ActionGraph ActionGraph)
{
UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(BinaryCompileEnvironment.Platform);
List<FileItem> LinkInputFiles = new List<FileItem>();
CppCompileEnvironment ModuleCompileEnvironment = CreateModuleCompileEnvironment(Target, BinaryCompileEnvironment);
IncludeSearchPaths = ModuleCompileEnvironment.IncludePaths.UserIncludePaths.ToList();
IncludeSearchPaths.AddRange(ModuleCompileEnvironment.IncludePaths.SystemIncludePaths.ToList());
// If the module is precompiled, read the object files from the manifest
if(Rules.bUsePrecompiled)
{
PrecompiledManifest Manifest = PrecompiledManifest.Read(PrecompiledManifestLocation);
foreach(FileReference OutputFile in Manifest.OutputFiles)
{
FileItem ObjectFile = FileItem.GetExistingItemByFileReference(OutputFile);
ToolChain.DoLocalToRemoteFileItem(ObjectFile);
LinkInputFiles.Add(ObjectFile);
}
return LinkInputFiles;
}
// Throw an error if the module's source file list referenced any non-existent files.
if (SourceFilesToBuild.MissingFiles.Count > 0)
{
throw new BuildException(
"UBT ERROR: Module \"{0}\" references non-existent files:\n{1} (perhaps a file was added to the project but not checked in)",
Name,
string.Join("\n", SourceFilesToBuild.MissingFiles.Select(M => M.AbsolutePath))
);
}
{
// Process all of the header file dependencies for this module
this.CachePCHUsageForModuleSourceFiles(Target, ModuleCompileEnvironment);
// Make sure our RC files have cached includes.
foreach (FileItem RCFile in SourceFilesToBuild.RCFiles)
{
// The default resource file (PCLaunch.rc) is created in a module-agnostic way, so we want to avoid overriding the include paths for it
if(RCFile.CachedIncludePaths == null)
{
RCFile.CachedIncludePaths = ModuleCompileEnvironment.IncludePaths;
}
}
}
// Should we force a precompiled header to be generated for this module? Usually, we only bother with a
// precompiled header if there are at least several source files in the module (after combining them for unity
// builds.) But for game modules, it can be convenient to always have a precompiled header to single-file
// changes to code is really quick to compile.
int MinFilesUsingPrecompiledHeader = Target.MinFilesUsingPrecompiledHeader;
if (Rules.MinFilesUsingPrecompiledHeaderOverride != 0)
{
MinFilesUsingPrecompiledHeader = Rules.MinFilesUsingPrecompiledHeaderOverride;
}
else if (!Rules.bTreatAsEngineModule && Target.bForcePrecompiledHeaderForGameModules)
{
// This is a game module with only a small number of source files, so go ahead and force a precompiled header
// to be generated to make incremental changes to source files as fast as possible for small projects.
MinFilesUsingPrecompiledHeader = 1;
}
// Engine modules will always use unity build mode unless MinSourceFilesForUnityBuildOverride is specified in
// the module rules file. By default, game modules only use unity of they have enough source files for that
// to be worthwhile. If you have a lot of small game modules, consider specifying MinSourceFilesForUnityBuildOverride=0
// in the modules that you don't typically iterate on source files in very frequently.
int MinSourceFilesForUnityBuild = 0;
if (Rules.MinSourceFilesForUnityBuildOverride != 0)
{
MinSourceFilesForUnityBuild = Rules.MinSourceFilesForUnityBuildOverride;
}
else if (Target.ProjectFile != null && RulesFile.IsUnderDirectory(DirectoryReference.Combine(Target.ProjectFile.Directory, "Source")))
{
// Game modules with only a small number of source files are usually better off having faster iteration times
// on single source file changes, so we forcibly disable unity build for those modules
MinSourceFilesForUnityBuild = Target.MinGameModuleSourceFilesForUnityBuild;
}
// Should we use unity build mode for this module?
bool bModuleUsesUnityBuild = false;
if (Target.bUseUnityBuild || Target.bForceUnityBuild)
{
if (Target.bForceUnityBuild)
{
Log.TraceVerbose("Module '{0}' using unity build mode (bForceUnityBuild enabled for this module)", this.Name);
bModuleUsesUnityBuild = true;
}
else if (Rules.bFasterWithoutUnity)
{
Log.TraceVerbose("Module '{0}' not using unity build mode (bFasterWithoutUnity enabled for this module)", this.Name);
bModuleUsesUnityBuild = false;
}
else if (SourceFilesToBuild.CPPFiles.Count < MinSourceFilesForUnityBuild)
{
Log.TraceVerbose("Module '{0}' not using unity build mode (module with fewer than {1} source files)", this.Name, MinSourceFilesForUnityBuild);
bModuleUsesUnityBuild = false;
}
else
{
Log.TraceVerbose("Module '{0}' using unity build mode", this.Name);
bModuleUsesUnityBuild = true;
}
}
else
{
Log.TraceVerbose("Module '{0}' not using unity build mode", this.Name);
}
// Set up the environment with which to compile the CPP files
CppCompileEnvironment CompileEnvironment = ModuleCompileEnvironment;
if (Target.bUsePCHFiles)
{
// If this module has an explicit PCH, use that
if(Rules.PrivatePCHHeaderFile != null)
{
PrecompiledHeaderInstance Instance = CreatePrivatePCH(ToolChain, FileItem.GetItemByFileReference(FileReference.Combine(ModuleDirectory, Rules.PrivatePCHHeaderFile)), CompileEnvironment, ActionGraph);
CompileEnvironment = new CppCompileEnvironment(CompileEnvironment);
CompileEnvironment.Definitions.Clear();
CompileEnvironment.PrecompiledHeaderAction = PrecompiledHeaderAction.Include;
CompileEnvironment.PrecompiledHeaderIncludeFilename = Instance.HeaderFile.Location;
CompileEnvironment.PrecompiledHeaderFile = Instance.Output.PrecompiledHeaderFile;
LinkInputFiles.AddRange(Instance.Output.ObjectFiles);
}
// Try to find a suitable shared PCH for this module
if (CompileEnvironment.PrecompiledHeaderFile == null && SharedPCHs.Count > 0 && !CompileEnvironment.bIsBuildingLibrary && Rules.PCHUsage != ModuleRules.PCHUsageMode.NoSharedPCHs)
{
// Find all the dependencies of this module
HashSet<UEBuildModule> ReferencedModules = new HashSet<UEBuildModule>();
GetAllDependencyModules(new List<UEBuildModule>(), ReferencedModules, bIncludeDynamicallyLoaded: false, bForceCircular: false, bOnlyDirectDependencies: true);
// Find the first shared PCH module we can use
PrecompiledHeaderTemplate Template = SharedPCHs.FirstOrDefault(x => ReferencedModules.Contains(x.Module));
if(Template != null && Template.IsValidFor(CompileEnvironment))
{
PrecompiledHeaderInstance Instance = FindOrCreateSharedPCH(ToolChain, Template, ModuleCompileEnvironment.bOptimizeCode, ModuleCompileEnvironment.bUseRTTI, ActionGraph);
FileReference PrivateDefinitionsFile = FileReference.Combine(IntermediateDirectory, String.Format("Definitions.{0}.h", Name));
FileItem PrivateDefinitionsFileItem;
using (StringWriter Writer = new StringWriter())
{
// Remove the module _API definition for cases where there are circular dependencies between the shared PCH module and modules using it
Writer.WriteLine("#undef {0}", ModuleApiDefine);
// Games may choose to use shared PCHs from the engine, so allow them to change the value of these macros
if(!Rules.bTreatAsEngineModule)
{
Writer.WriteLine("#undef UE_IS_ENGINE_MODULE");
Writer.WriteLine("#undef DEPRECATED_FORGAME");
Writer.WriteLine("#define DEPRECATED_FORGAME DEPRECATED");
}
WriteDefinitions(CompileEnvironment.Definitions, Writer);
PrivateDefinitionsFileItem = FileItem.CreateIntermediateTextFile(PrivateDefinitionsFile, Writer.ToString());
}
CompileEnvironment = new CppCompileEnvironment(CompileEnvironment);
CompileEnvironment.Definitions.Clear();
CompileEnvironment.ForceIncludeFiles.Add(PrivateDefinitionsFileItem);
CompileEnvironment.PrecompiledHeaderAction = PrecompiledHeaderAction.Include;
CompileEnvironment.PrecompiledHeaderIncludeFilename = Instance.HeaderFile.Location;
CompileEnvironment.PrecompiledHeaderFile = Instance.Output.PrecompiledHeaderFile;
LinkInputFiles.AddRange(Instance.Output.ObjectFiles);
}
}
// If there was one header that was included first by enough C++ files, use it as the precompiled header. Only use precompiled headers for projects with enough files to make the PCH creation worthwhile.
if (CompileEnvironment.PrecompiledHeaderFile == null && SourceFilesToBuild.CPPFiles.Count >= MinFilesUsingPrecompiledHeader && ProcessedDependencies != null)
{
PrecompiledHeaderInstance Instance = CreatePrivatePCH(ToolChain, ProcessedDependencies.UniquePCHHeaderFile, CompileEnvironment, ActionGraph);
CompileEnvironment = new CppCompileEnvironment(CompileEnvironment);
CompileEnvironment.Definitions.Clear();
CompileEnvironment.PrecompiledHeaderAction = PrecompiledHeaderAction.Include;
CompileEnvironment.PrecompiledHeaderIncludeFilename = Instance.HeaderFile.Location;
CompileEnvironment.PrecompiledHeaderFile = Instance.Output.PrecompiledHeaderFile;
LinkInputFiles.AddRange(Instance.Output.ObjectFiles);
}
}
// Write all the definitions to a separate file
CreateHeaderForDefinitions(CompileEnvironment, IntermediateDirectory, null);
// Compile CPP files
List<FileItem> CPPFilesToCompile = SourceFilesToBuild.CPPFiles;
if (bModuleUsesUnityBuild)
{
CPPFilesToCompile = Unity.GenerateUnityCPPs(Target, CPPFilesToCompile, CompileEnvironment, WorkingSet, Rules.ShortName ?? Name, IntermediateDirectory);
LinkInputFiles.AddRange(CompileUnityFilesWithToolChain(Target, ToolChain, CompileEnvironment, ModuleCompileEnvironment, CPPFilesToCompile, ActionGraph).ObjectFiles);
}
else
{
LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(CompileEnvironment, CPPFilesToCompile, IntermediateDirectory, Name, ActionGraph).ObjectFiles);
}
// Compile all the generated CPP files
if (AutoGenerateCppInfo != null && AutoGenerateCppInfo.BuildInfo != null && !CompileEnvironment.bHackHeaderGenerator)
{
string[] GeneratedFiles = Directory.GetFiles(Path.GetDirectoryName(AutoGenerateCppInfo.BuildInfo.FileWildcard), Path.GetFileName(AutoGenerateCppInfo.BuildInfo.FileWildcard));
if(GeneratedFiles.Length > 0)
{
// Create a compile environment for the generated files. We can disable creating debug info here to improve link times.
CppCompileEnvironment GeneratedCPPCompileEnvironment = CompileEnvironment;
if(GeneratedCPPCompileEnvironment.bCreateDebugInfo && Target.bDisableDebugInfoForGeneratedCode)
{
GeneratedCPPCompileEnvironment = new CppCompileEnvironment(GeneratedCPPCompileEnvironment);
GeneratedCPPCompileEnvironment.bCreateDebugInfo = false;
}
// Compile all the generated files
List<FileItem> GeneratedFileItems = new List<FileItem>();
foreach (string GeneratedFilename in GeneratedFiles)
{
FileItem GeneratedCppFileItem = FileItem.GetItemByPath(GeneratedFilename);
CachePCHUsageForModuleSourceFile(CompileEnvironment, GeneratedCppFileItem);
// @todo ubtmake: Check for ALL other places where we might be injecting .cpp or .rc files for compiling without caching CachedCPPIncludeInfo first (anything platform specific?)
GeneratedFileItems.Add(GeneratedCppFileItem);
}
if (bModuleUsesUnityBuild)
{
GeneratedFileItems = Unity.GenerateUnityCPPs(Target, GeneratedFileItems, GeneratedCPPCompileEnvironment, WorkingSet, (Rules.ShortName ?? Name) + ".gen", IntermediateDirectory);
LinkInputFiles.AddRange(CompileUnityFilesWithToolChain(Target, ToolChain, GeneratedCPPCompileEnvironment, ModuleCompileEnvironment, GeneratedFileItems, ActionGraph).ObjectFiles);
}
else
{
LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(GeneratedCPPCompileEnvironment, GeneratedFileItems, IntermediateDirectory, Name, ActionGraph).ObjectFiles);
}
}
}
// Compile C files directly. Do not use a PCH here, because a C++ PCH is not compatible with C source files.
if(SourceFilesToBuild.CFiles.Count > 0)
{
LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(ModuleCompileEnvironment, SourceFilesToBuild.CFiles, IntermediateDirectory, Name, ActionGraph).ObjectFiles);
}
// Compile CC files directly.
if(SourceFilesToBuild.CCFiles.Count > 0)
{
LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(CompileEnvironment, SourceFilesToBuild.CCFiles, IntermediateDirectory, Name, ActionGraph).ObjectFiles);
}
// Compile MM files directly.
if(SourceFilesToBuild.MMFiles.Count > 0)
{
LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(CompileEnvironment, SourceFilesToBuild.MMFiles, IntermediateDirectory, Name, ActionGraph).ObjectFiles);
}
// Compile RC files. The resource compiler does not work with response files, and using the regular compile environment can easily result in the
// command line length exceeding the OS limit. Use the binary compile environment to keep the size down, and require that all include paths
// must be specified relative to the resource file itself or Engine/Source.
if(SourceFilesToBuild.RCFiles.Count > 0)
{
CppCompileEnvironment ResourceCompileEnvironment = new CppCompileEnvironment(BinaryCompileEnvironment);
LinkInputFiles.AddRange(ToolChain.CompileRCFiles(ResourceCompileEnvironment, SourceFilesToBuild.RCFiles, IntermediateDirectory, ActionGraph).ObjectFiles);
}
// Write the compiled manifest
if(Rules.bPrecompile)
{
DirectoryReference.CreateDirectory(PrecompiledManifestLocation.Directory);
PrecompiledManifest Manifest = new PrecompiledManifest();
Manifest.OutputFiles.AddRange(LinkInputFiles.Select(x => x.Location));
Manifest.Write(PrecompiledManifestLocation);
}
return LinkInputFiles;
}
/// <summary>
/// Create a shared PCH template for this module, which allows constructing shared PCH instances in the future
/// </summary>
/// <param name="Target">The target which owns this module</param>
/// <param name="BaseCompileEnvironment">Base compile environment for this target</param>
/// <returns>Template for shared PCHs</returns>
public PrecompiledHeaderTemplate CreateSharedPCHTemplate(UEBuildTarget Target, CppCompileEnvironment BaseCompileEnvironment)
{
CppCompileEnvironment CompileEnvironment = CreateSharedPCHCompileEnvironment(Target, BaseCompileEnvironment);
FileItem HeaderFile = FileItem.GetItemByFileReference(FileReference.Combine(ModuleDirectory, Rules.SharedPCHHeaderFile));
HeaderFile.CachedIncludePaths = CompileEnvironment.IncludePaths;
DirectoryReference PrecompiledHeaderDir;
if(Target.Rules.bUsePrecompiled)
{
PrecompiledHeaderDir = DirectoryReference.Combine(Target.ProjectIntermediateDirectory, Name);
}
else
{
PrecompiledHeaderDir = IntermediateDirectory;
}
return new PrecompiledHeaderTemplate(this, CompileEnvironment, HeaderFile, PrecompiledHeaderDir);
}
/// <summary>
/// Creates a precompiled header action to generate a new pch file
/// </summary>
/// <param name="ToolChain">The toolchain to generate the PCH</param>
/// <param name="HeaderFile"></param>
/// <param name="ModuleCompileEnvironment"></param>
/// <param name="ActionGraph">Graph containing build actions</param>
/// <returns>The created PCH instance.</returns>
private PrecompiledHeaderInstance CreatePrivatePCH(UEToolChain ToolChain, FileItem HeaderFile, CppCompileEnvironment ModuleCompileEnvironment, ActionGraph ActionGraph)
{
// Cache the header file include paths. This file could have been a shared PCH too, so ignore if the include paths are already set.
if(HeaderFile.CachedIncludePaths == null)
{
HeaderFile.CachedIncludePaths = ModuleCompileEnvironment.IncludePaths;
}
// Create the wrapper file, which sets all the definitions needed to compile it
FileReference WrapperLocation = FileReference.Combine(IntermediateDirectory, String.Format("PCH.{0}.h", Name));
FileItem WrapperFile = CreatePCHWrapperFile(WrapperLocation, ModuleCompileEnvironment.Definitions, HeaderFile);
// Create a new C++ environment that is used to create the PCH.
CppCompileEnvironment CompileEnvironment = new CppCompileEnvironment(ModuleCompileEnvironment);
CompileEnvironment.Definitions.Clear();
CompileEnvironment.PrecompiledHeaderAction = PrecompiledHeaderAction.Create;
CompileEnvironment.PrecompiledHeaderIncludeFilename = WrapperFile.Location;
CompileEnvironment.bOptimizeCode = ModuleCompileEnvironment.bOptimizeCode;
// Create the action to compile the PCH file.
CPPOutput Output = ToolChain.CompileCPPFiles(CompileEnvironment, new List<FileItem>() { WrapperFile }, IntermediateDirectory, Name, ActionGraph);
return new PrecompiledHeaderInstance(WrapperFile, CompileEnvironment.bOptimizeCode, CompileEnvironment.bUseRTTI, Output);
}
/// <summary>
/// Generates a precompiled header instance from the given template, or returns an existing one if it already exists
/// </summary>
/// <param name="ToolChain">The toolchain being used to build this module</param>
/// <param name="Template">The PCH template</param>
/// <param name="bOptimizeCode">Whether optimization should be enabled for this PCH</param>
/// <param name="bUseRTTI">Whether to enable RTTI for this PCH</param>
/// <param name="ActionGraph">Graph containing build actions</param>
/// <returns>Instance of a PCH</returns>
public PrecompiledHeaderInstance FindOrCreateSharedPCH(UEToolChain ToolChain, PrecompiledHeaderTemplate Template, bool bOptimizeCode, bool bUseRTTI, ActionGraph ActionGraph)
{
PrecompiledHeaderInstance Instance = Template.Instances.Find(x => x.bOptimizeCode == bOptimizeCode && x.bUseRTTI == bUseRTTI);
if(Instance == null)
{
// Create a suffix to distinguish this shared PCH variant from any others. Currently only optimized and non-optimized shared PCHs are supported.
string Variant = "";
if(bOptimizeCode != Template.BaseCompileEnvironment.bOptimizeCode)
{
if(bOptimizeCode)
{
Variant += ".Optimized";
}
else
{
Variant += ".NonOptimized";
}
}
if(bUseRTTI != Template.BaseCompileEnvironment.bUseRTTI)
{
if (bUseRTTI)
{
Variant += ".RTTI";
}
else
{
Variant += ".NonRTTI";
}
}
// Create the wrapper file, which sets all the definitions needed to compile it
FileReference WrapperLocation = FileReference.Combine(Template.OutputDir, String.Format("SharedPCH.{0}{1}.h", Template.Module.Name, Variant));
FileItem WrapperFile = CreatePCHWrapperFile(WrapperLocation, Template.BaseCompileEnvironment.Definitions, Template.HeaderFile);
// Create the compile environment for this PCH
CppCompileEnvironment CompileEnvironment = new CppCompileEnvironment(Template.BaseCompileEnvironment);
CompileEnvironment.Definitions.Clear();
CompileEnvironment.PrecompiledHeaderAction = PrecompiledHeaderAction.Create;
CompileEnvironment.PrecompiledHeaderIncludeFilename = WrapperFile.Location;
CompileEnvironment.bOptimizeCode = bOptimizeCode;
CompileEnvironment.bUseRTTI = bUseRTTI;
// Create the PCH
CPPOutput Output = ToolChain.CompileCPPFiles(CompileEnvironment, new List<FileItem>() { WrapperFile }, Template.OutputDir, "Shared", ActionGraph);
Instance = new PrecompiledHeaderInstance(WrapperFile, bOptimizeCode, bUseRTTI, Output);
Template.Instances.Add(Instance);
}
return Instance;
}
/// <summary>
/// Compiles the provided CPP unity files. Will
/// </summary>
private CPPOutput CompileUnityFilesWithToolChain(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, CppCompileEnvironment ModuleCompileEnvironment, List<FileItem> SourceFiles, ActionGraph ActionGraph)
{
List<FileItem> NormalFiles = new List<FileItem>();
List<FileItem> AdaptiveFiles = new List<FileItem>();
bool bAdaptiveUnityDisablesPCH = (Target.bAdaptiveUnityDisablesPCH && Rules.PCHUsage == ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs);
if ((Target.bAdaptiveUnityDisablesOptimizations || bAdaptiveUnityDisablesPCH) && !Target.bStressTestUnity)
{
foreach (FileItem File in SourceFiles)
{
// Basic check as to whether something in this module is/isn't a unity file...
if (File.Location.GetFileName().StartsWith(Unity.ModulePrefix))
{
NormalFiles.Add(File);
}
else
{
AdaptiveFiles.Add(File);
}
}
}
else
{
NormalFiles.AddRange(SourceFiles);
}
CPPOutput OutputFiles = new CPPOutput();
if (NormalFiles.Count > 0)
{
OutputFiles = ToolChain.CompileCPPFiles(CompileEnvironment, NormalFiles, IntermediateDirectory, Name, ActionGraph);
}
if (AdaptiveFiles.Count > 0)
{
// Create the new compile environment. Always turn off PCH due to different compiler settings.
CppCompileEnvironment AdaptiveUnityEnvironment = new CppCompileEnvironment(ModuleCompileEnvironment);
if(Target.bAdaptiveUnityDisablesOptimizations)
{
AdaptiveUnityEnvironment.bOptimizeCode = false;
}
AdaptiveUnityEnvironment.PrecompiledHeaderAction = PrecompiledHeaderAction.None;
// Write all the definitions out to a separate file
CreateHeaderForDefinitions(AdaptiveUnityEnvironment, IntermediateDirectory, "Adaptive");
// Compile the files
CPPOutput AdaptiveOutput = ToolChain.CompileCPPFiles(AdaptiveUnityEnvironment, AdaptiveFiles, IntermediateDirectory, Name, ActionGraph);
// Merge output
OutputFiles.ObjectFiles.AddRange(AdaptiveOutput.ObjectFiles);
OutputFiles.DebugDataFiles.AddRange(AdaptiveOutput.DebugDataFiles);
}
return OutputFiles;
}
/// <summary>
/// Creates a header file containing all the preprocessor definitions for a compile environment, and force-include it. We allow a more flexible syntax for preprocessor definitions than
/// is typically allowed on the command line (allowing function macros or double-quote characters, for example). Ensuring all definitions are specified in a header files ensures consistent
/// behavior.
/// </summary>
/// <param name="CompileEnvironment">The compile environment</param>
/// <param name="IntermediateDirectory">Directory to create the intermediate file</param>
/// <param name="HeaderSuffix">Suffix for the included file</param>
static void CreateHeaderForDefinitions(CppCompileEnvironment CompileEnvironment, DirectoryReference IntermediateDirectory, string HeaderSuffix)
{
if(CompileEnvironment.Definitions.Count > 0)
{
StringBuilder PrivateDefinitionsName = new StringBuilder("Definitions");
if(!String.IsNullOrEmpty(HeaderSuffix))
{
PrivateDefinitionsName.Append('.');
PrivateDefinitionsName.Append(HeaderSuffix);
}
PrivateDefinitionsName.Append(".h");
FileReference PrivateDefinitionsFile = FileReference.Combine(IntermediateDirectory, PrivateDefinitionsName.ToString());
using (StringWriter Writer = new StringWriter())
{
WriteDefinitions(CompileEnvironment.Definitions, Writer);
CompileEnvironment.Definitions.Clear();
FileItem PrivateDefinitionsFileItem = FileItem.CreateIntermediateTextFile(PrivateDefinitionsFile, Writer.ToString());
CompileEnvironment.ForceIncludeFiles.Add(PrivateDefinitionsFileItem);
}
}
}
/// <summary>
/// Create a header file containing the module definitions, which also includes the PCH itself. Including through another file is necessary on
/// Clang, since we get warnings about #pragma once otherwise, but it also allows us to consistently define the preprocessor state on all
/// platforms.
/// </summary>
/// <param name="OutputFile">The output file to create</param>
/// <param name="Definitions">Definitions required by the PCH</param>
/// <param name="IncludedFile">The PCH file to include</param>
/// <returns>FileItem for the created file</returns>
static FileItem CreatePCHWrapperFile(FileReference OutputFile, IEnumerable<string> Definitions, FileItem IncludedFile)
{
// Build the contents of the wrapper file
StringBuilder WrapperContents = new StringBuilder();
using (StringWriter Writer = new StringWriter(WrapperContents))
{
Writer.WriteLine("// PCH for {0}", IncludedFile.AbsolutePath);
WriteDefinitions(Definitions, Writer);
Writer.WriteLine("#include \"{0}\"", IncludedFile.AbsolutePath);
}
// Create the item
FileItem WrapperFile = FileItem.CreateIntermediateTextFile(OutputFile, WrapperContents.ToString());
WrapperFile.CachedIncludePaths = IncludedFile.CachedIncludePaths;
// Touch it if the included file is newer, to make sure our timestamp dependency checking is accurate.
if (IncludedFile.LastWriteTime > WrapperFile.LastWriteTime)
{
File.SetLastWriteTimeUtc(WrapperFile.AbsolutePath, DateTime.UtcNow);
WrapperFile.ResetFileInfo();
}
return WrapperFile;
}
/// <summary>
/// Write a list of macro definitions to an output file
/// </summary>
/// <param name="Definitions">List of definitions</param>
/// <param name="Writer">Writer to receive output</param>
static void WriteDefinitions(IEnumerable<string> Definitions, TextWriter Writer)
{
foreach(string Definition in Definitions)
{
int EqualsIdx = Definition.IndexOf('=');
if(EqualsIdx == -1)
{
Writer.WriteLine("#define {0} 1", Definition);
}
else
{
Writer.WriteLine("#define {0} {1}", Definition.Substring(0, EqualsIdx), Definition.Substring(EqualsIdx + 1));
}
}
}
public static FileItem CachePCHUsageForModuleSourceFile(CppCompileEnvironment ModuleCompileEnvironment, FileItem CPPFile)
{
if (!CPPFile.bExists)
{
throw new BuildException("Required source file not found: " + CPPFile.AbsolutePath);
}
DateTime PCHCacheTimerStart = DateTime.UtcNow;
// Store the module compile environment along with the .cpp file. This is so that we can use it later on when looking
// for header dependencies
CPPFile.CachedIncludePaths = ModuleCompileEnvironment.IncludePaths;
FileItem PCHFile = ModuleCompileEnvironment.Headers.CachePCHUsageForCPPFile(CPPFile, ModuleCompileEnvironment.IncludePaths, ModuleCompileEnvironment.Platform);
if (UnrealBuildTool.bPrintPerformanceInfo)
{
double PCHCacheTime = (DateTime.UtcNow - PCHCacheTimerStart).TotalSeconds;
TotalPCHCacheTime += PCHCacheTime;
}
return PCHFile;
}
public void CachePCHUsageForModuleSourceFiles(ReadOnlyTargetRules Target, CppCompileEnvironment ModuleCompileEnvironment)
{
if(Rules == null || Rules.PCHUsage == ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs || Rules.PrivatePCHHeaderFile != null)
{
if(InvalidIncludeDirectiveMessages == null)
{
// Find all the source files in this module
List<FileReference> ModuleFiles = SourceFileSearch.FindModuleSourceFiles(RulesFile);
// Find headers used by the source file.
Dictionary<string, FileReference> NameToHeaderFile = new Dictionary<string, FileReference>();
foreach(FileReference ModuleFile in ModuleFiles)
{
if(ModuleFile.HasExtension(".h"))
{
NameToHeaderFile[ModuleFile.GetFileNameWithoutExtension()] = ModuleFile;
}
}
// Store the module compile environment along with the .cpp file. This is so that we can use it later on when looking for header dependencies
foreach (FileItem CPPFile in SourceFilesFound.CPPFiles)
{
CPPFile.CachedIncludePaths = ModuleCompileEnvironment.IncludePaths;
}
// Find the directly included files for each source file, and make sure it includes the matching header if possible
InvalidIncludeDirectiveMessages = new List<string>();
if (Rules != null && Rules.bEnforceIWYU && Target.bEnforceIWYU)
{
foreach (FileItem CPPFile in SourceFilesFound.CPPFiles)
{
List<DependencyInclude> DirectIncludeFilenames = ModuleCompileEnvironment.Headers.GetDirectIncludeDependencies(CPPFile, bOnlyCachedDependencies: false);
if (DirectIncludeFilenames.Count > 0)
{
string IncludeName = Path.GetFileNameWithoutExtension(DirectIncludeFilenames[0].IncludeName);
string ExpectedName = CPPFile.Location.GetFileNameWithoutExtension();
if (String.Compare(IncludeName, ExpectedName, StringComparison.InvariantCultureIgnoreCase) != 0)
{
FileReference HeaderFile;
if (NameToHeaderFile.TryGetValue(ExpectedName, out HeaderFile) && !IgnoreMismatchedHeader(ExpectedName))
{
InvalidIncludeDirectiveMessages.Add(String.Format("{0}(1): error: Expected {1} to be first header included.", CPPFile.Location, HeaderFile.GetFileName()));
}
}
}
}
}
}
}
else
{
if (ProcessedDependencies == null)
{
DateTime PCHCacheTimerStart = DateTime.UtcNow;
bool bFoundAProblemWithPCHs = false;
FileItem UniquePCH = null;
foreach (FileItem CPPFile in SourceFilesFound.CPPFiles) // @todo ubtmake: We're not caching CPPEnvironments for .c/.mm files, etc. Even though they don't use PCHs, they still have #includes! This can break dependency checking!
{
// Store the module compile environment along with the .cpp file. This is so that we can use it later on when looking
// for header dependencies
CPPFile.CachedIncludePaths = ModuleCompileEnvironment.IncludePaths;
// Find headers used by the source file.
FileItem PCH = ModuleCompileEnvironment.Headers.CachePCHUsageForCPPFile(CPPFile, ModuleCompileEnvironment.IncludePaths, ModuleCompileEnvironment.Platform);
if (PCH == null)
{
throw new BuildException("Source file \"{0}\" is not including any headers. We expect all modules to include a header file for precompiled header generation. Please add an #include statement.", CPPFile.AbsolutePath);
}
if (UniquePCH == null)
{
UniquePCH = PCH;
}
else if (!UniquePCH.Info.Name.Equals(PCH.Info.Name, StringComparison.InvariantCultureIgnoreCase)) // @todo ubtmake: We do a string compare on the file name (not path) here, because sometimes the include resolver will pick an Intermediate copy of a PCH header file and throw off our comparisons
{
// OK, looks like we have multiple source files including a different header file first. We'll keep track of this and print out
// helpful information afterwards.
bFoundAProblemWithPCHs = true;
}
}
ProcessedDependencies = new ProcessedDependenciesClass { UniquePCHHeaderFile = UniquePCH };
if (bFoundAProblemWithPCHs)
{
// Map from pch header string to the source files that use that PCH
Dictionary<FileReference, List<FileItem>> UsageMapPCH = new Dictionary<FileReference, List<FileItem>>();
foreach (FileItem CPPFile in SourceFilesToBuild.CPPFiles)
{
// Create a new entry if not in the pch usage map
List<FileItem> Files;
if (!UsageMapPCH.TryGetValue(CPPFile.PrecompiledHeaderIncludeFilename, out Files))
{
Files = new List<FileItem>();
UsageMapPCH.Add(CPPFile.PrecompiledHeaderIncludeFilename, Files);
}
Files.Add(CPPFile);
}
if (UnrealBuildTool.bPrintDebugInfo)
{
Log.TraceVerbose("{0} PCH files for module {1}:", UsageMapPCH.Count, Name);
int MostFilesIncluded = 0;
foreach (KeyValuePair<FileReference, List<FileItem>> CurPCH in UsageMapPCH)
{
if (CurPCH.Value.Count > MostFilesIncluded)
{
MostFilesIncluded = CurPCH.Value.Count;
}
Log.TraceVerbose(" {0} ({1} files including it: {2}, ...)", CurPCH.Key, CurPCH.Value.Count, CurPCH.Value[0].AbsolutePath);
}
}
if (UsageMapPCH.Count > 1)
{
// Keep track of the PCH file that is most used within this module
FileReference MostFilesAreIncludingPCH = null;
int MostFilesIncluded = 0;
foreach (KeyValuePair<FileReference, List<FileItem>> CurPCH in UsageMapPCH.Where(PCH => PCH.Value.Count > MostFilesIncluded))
{
MostFilesAreIncludingPCH = CurPCH.Key;
MostFilesIncluded = CurPCH.Value.Count;
}
// Find all of the files that are not including our "best" PCH header
StringBuilder FilesNotIncludingBestPCH = new StringBuilder();
foreach (KeyValuePair<FileReference, List<FileItem>> CurPCH in UsageMapPCH.Where(PCH => PCH.Key != MostFilesAreIncludingPCH))
{
foreach (FileItem SourceFile in CurPCH.Value)
{
FilesNotIncludingBestPCH.AppendFormat("{0} (including {1})\n", SourceFile.AbsolutePath, CurPCH.Key);
}
}
// Bail out and let the user know which source files may need to be fixed up
throw new BuildException(
"All source files in module \"{0}\" must include the same precompiled header first. Currently \"{1}\" is included by most of the source files. The following source files are not including \"{1}\" as their first include:\n\n{2}\n\nTo compile this module without implicit precompiled headers, add \"PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;\" to {0}.build.cs.",
Name,
MostFilesAreIncludingPCH,
FilesNotIncludingBestPCH);
}
}
if (UnrealBuildTool.bPrintPerformanceInfo)
{
double PCHCacheTime = (DateTime.UtcNow - PCHCacheTimerStart).TotalSeconds;
TotalPCHCacheTime += PCHCacheTime;
}
}
}
}
private bool IgnoreMismatchedHeader(string ExpectedName)
{
switch(ExpectedName)
{
case "Stats2":
case "DynamicRHI":
case "RHICommandList":
case "RHIUtilities":
return true;
}
switch(Name)
{
case "D3D11RHI":
case "D3D12RHI":
case "VulkanRHI":
case "OpenGLDrv":
case "MetalRHI":
case "PS4RHI":
case "Gnmx":
case "OnlineSubsystemIOS":
case "OnlineSubsystemLive":
return true;
}
return false;
}
/// <summary>
/// Determine whether optimization should be enabled for a given target
/// </summary>
/// <param name="Setting">The optimization setting from the rules file</param>
/// <param name="Configuration">The active target configuration</param>
/// <param name="bIsEngineModule">Whether the current module is an engine module</param>
/// <returns>True if optimization should be enabled</returns>
public static bool ShouldEnableOptimization(ModuleRules.CodeOptimization Setting, UnrealTargetConfiguration Configuration, bool bIsEngineModule)
{
switch(Setting)
{
case ModuleRules.CodeOptimization.Never:
return false;
case ModuleRules.CodeOptimization.Default:
case ModuleRules.CodeOptimization.InNonDebugBuilds:
return (Configuration == UnrealTargetConfiguration.Debug)? false : (Configuration != UnrealTargetConfiguration.DebugGame || bIsEngineModule);
case ModuleRules.CodeOptimization.InShippingBuildsOnly:
return (Configuration == UnrealTargetConfiguration.Shipping);
default:
return true;
}
}
/// <summary>
/// Creates a compile environment from a base environment based on the module settings.
/// </summary>
/// <param name="Target">Rules for the target being built</param>
/// <param name="BaseCompileEnvironment">An existing environment to base the module compile environment on.</param>
/// <returns>The new module compile environment.</returns>
public CppCompileEnvironment CreateModuleCompileEnvironment(ReadOnlyTargetRules Target, CppCompileEnvironment BaseCompileEnvironment)
{
CppCompileEnvironment Result = new CppCompileEnvironment(BaseCompileEnvironment);
// Override compile environment
Result.bFasterWithoutUnity = Rules.bFasterWithoutUnity;
Result.bOptimizeCode = ShouldEnableOptimization(Rules.OptimizeCode, Target.Configuration, Rules.bTreatAsEngineModule);
Result.bUseRTTI |= Rules.bUseRTTI;
Result.bUseAVX = Rules.bUseAVX;
Result.bEnableBufferSecurityChecks = Rules.bEnableBufferSecurityChecks;
Result.MinSourceFilesForUnityBuildOverride = Rules.MinSourceFilesForUnityBuildOverride;
Result.MinFilesUsingPrecompiledHeaderOverride = Rules.MinFilesUsingPrecompiledHeaderOverride;
Result.bBuildLocallyWithSNDBS = Rules.bBuildLocallyWithSNDBS;
Result.bEnableExceptions |= Rules.bEnableExceptions;
Result.bEnableObjCExceptions |= Rules.bEnableObjCExceptions;
Result.bEnableShadowVariableWarnings = Rules.bEnableShadowVariableWarnings;
Result.bEnableUndefinedIdentifierWarnings = Rules.bEnableUndefinedIdentifierWarnings;
// Set the macro used to check whether monolithic headers can be used
if (Rules.bTreatAsEngineModule && (!Rules.bEnforceIWYU || !Target.bEnforceIWYU))
{
Result.Definitions.Add("SUPPRESS_MONOLITHIC_HEADER_WARNINGS=1");
}
// Add a macro for when we're compiling an engine module, to enable additional compiler diagnostics through code.
if (Rules.bTreatAsEngineModule)
{
Result.Definitions.Add("UE_IS_ENGINE_MODULE=1");
}
else
{
Result.Definitions.Add("UE_IS_ENGINE_MODULE=0");
}
// Switch the optimization flag if we're building a game module. Also pass the definition for building in DebugGame along (see ModuleManager.h for notes).
if (!Rules.bTreatAsEngineModule)
{
if (Target.Configuration == UnrealTargetConfiguration.DebugGame)
{
Result.Definitions.Add("UE_BUILD_DEVELOPMENT_WITH_DEBUGGAME=1");
}
else
{
Result.Definitions.Add("UE_BUILD_DEVELOPMENT_WITH_DEBUGGAME=0");
}
}
// For game modules, set the define for the project name. This will be used by the IMPLEMENT_PRIMARY_GAME_MODULE macro.
if (!Rules.bTreatAsEngineModule)
{
// Make sure we don't set any define for a non-engine module that's under the engine directory (eg. UE4Game)
if (Target.ProjectFile != null && RulesFile.IsUnderDirectory(Target.ProjectFile.Directory))
{
string ProjectName = Target.ProjectFile.GetFileNameWithoutExtension();
Result.Definitions.Add(String.Format("UE_PROJECT_NAME={0}", ProjectName));
}
}
// Add the module's public and private definitions.
Result.Definitions.AddRange(PublicDefinitions);
Result.Definitions.AddRange(Rules.PrivateDefinitions);
// Add the project definitions
if(!Rules.bTreatAsEngineModule)
{
Result.Definitions.AddRange(Rules.Target.ProjectDefinitions);
}
// Setup the compile environment for the module.
SetupPrivateCompileEnvironment(Result.IncludePaths.UserIncludePaths, Result.IncludePaths.SystemIncludePaths, Result.Definitions, Result.AdditionalFrameworks, (Rules != null)? Rules.bLegacyPublicIncludePaths.Value : true);
return Result;
}
/// <summary>
/// Creates a compile environment for a shared PCH from a base environment based on the module settings.
/// </summary>
/// <param name="Target">The target being built</param>
/// <param name="BaseCompileEnvironment">An existing environment to base the module compile environment on.</param>
/// <returns>The new shared PCH compile environment.</returns>
public CppCompileEnvironment CreateSharedPCHCompileEnvironment(UEBuildTarget Target, CppCompileEnvironment BaseCompileEnvironment)
{
CppCompileEnvironment CompileEnvironment = new CppCompileEnvironment(BaseCompileEnvironment);
// Use the default optimization setting for
CompileEnvironment.bOptimizeCode = ShouldEnableOptimization(ModuleRules.CodeOptimization.Default, Target.Configuration, Rules.bTreatAsEngineModule);
// Override compile environment
CompileEnvironment.bIsBuildingDLL = !Target.ShouldCompileMonolithic();
CompileEnvironment.bIsBuildingLibrary = false;
// Add a macro for when we're compiling an engine module, to enable additional compiler diagnostics through code.
if (Rules.bTreatAsEngineModule)
{
CompileEnvironment.Definitions.Add("UE_IS_ENGINE_MODULE=1");
}
else
{
CompileEnvironment.Definitions.Add("UE_IS_ENGINE_MODULE=0");
}
// Switch the optimization flag if we're building a game module. Also pass the definition for building in DebugGame along (see ModuleManager.h for notes).
if (!Rules.bTreatAsEngineModule)
{
if (Target.Configuration == UnrealTargetConfiguration.DebugGame)
{
CompileEnvironment.Definitions.Add("UE_BUILD_DEVELOPMENT_WITH_DEBUGGAME=1");
}
else
{
CompileEnvironment.Definitions.Add("UE_BUILD_DEVELOPMENT_WITH_DEBUGGAME=0");
}
}
// Add the module's private definitions.
CompileEnvironment.Definitions.AddRange(PublicDefinitions);
// Find all the modules that are part of the public compile environment for this module.
Dictionary<UEBuildModule, bool> ModuleToIncludePathsOnlyFlag = new Dictionary<UEBuildModule, bool>();
FindModulesInPublicCompileEnvironment(ModuleToIncludePathsOnlyFlag);
// Now set up the compile environment for the modules in the original order that we encountered them
foreach (UEBuildModule Module in ModuleToIncludePathsOnlyFlag.Keys)
{
Module.AddModuleToCompileEnvironment(null, CompileEnvironment.IncludePaths.UserIncludePaths, CompileEnvironment.IncludePaths.SystemIncludePaths, CompileEnvironment.Definitions, CompileEnvironment.AdditionalFrameworks, (Rules != null)? Rules.bLegacyPublicIncludePaths.Value : true);
}
return CompileEnvironment;
}
public class UHTModuleInfoCacheType
{
public UHTModuleInfoCacheType(IEnumerable<string> InHeaderFilenames, UHTModuleInfo InInfo)
{
HeaderFilenames = InHeaderFilenames;
Info = InInfo;
}
public IEnumerable<string> HeaderFilenames = null;
public UHTModuleInfo Info = null;
}
private UHTModuleInfoCacheType UHTModuleInfoCache = null;
/// Total time spent generating PCHs for modules (not actually compiling, but generating the PCH's input data)
public static double TotalPCHGenTime = 0.0;
/// Time spent caching which PCH header is included by each module and source file
public static double TotalPCHCacheTime = 0.0;
/// <summary>
/// If any of this module's source files contain UObject definitions, this will return those header files back to the caller
/// </summary>
/// <returns></returns>
public UHTModuleInfoCacheType GetCachedUHTModuleInfo(EGeneratedCodeVersion GeneratedCodeVersion)
{
if (UHTModuleInfoCache == null)
{
List<FileReference> HeaderFiles = new List<FileReference>();
FileSystemName[] ExcludedFolders = UEBuildPlatform.GetBuildPlatform(Rules.Target.Platform, true).GetExcludedFolderNames();
FindHeaders(new DirectoryInfo(ModuleDirectory.FullName), ExcludedFolders, HeaderFiles);
UHTModuleInfo Info = ExternalExecution.CreateUHTModuleInfo(HeaderFiles, Name, RulesFile, ModuleDirectory, Type, GeneratedCodeVersion);
UHTModuleInfoCache = new UHTModuleInfoCacheType(Info.PublicUObjectHeaders.Concat(Info.PublicUObjectClassesHeaders).Concat(Info.PrivateUObjectHeaders).Select(x => x.AbsolutePath).ToList(), Info);
}
return UHTModuleInfoCache;
}
/// <summary>
/// Find all the headers under the given base directory, excluding any other platform folders.
/// </summary>
/// <param name="BaseDir">Base directory to search</param>
/// <param name="ExcludeFolders">Array of folders to exclude</param>
/// <param name="Headers">Receives the list of headers that was found</param>
static void FindHeaders(DirectoryInfo BaseDir, FileSystemName[] ExcludeFolders, List<FileReference> Headers)
{
if (!ExcludeFolders.Any(x => x.DisplayName.Equals(BaseDir.Name, StringComparison.InvariantCultureIgnoreCase)))
{
foreach (DirectoryInfo SubDir in BaseDir.EnumerateDirectories())
{
FindHeaders(SubDir, ExcludeFolders, Headers);
}
foreach (FileInfo File in BaseDir.EnumerateFiles("*.h"))
{
Headers.Add(new FileReference(File));
}
}
}
public override void GetAllDependencyModules(List<UEBuildModule> ReferencedModules, HashSet<UEBuildModule> IgnoreReferencedModules, bool bIncludeDynamicallyLoaded, bool bForceCircular, bool bOnlyDirectDependencies)
{
List<UEBuildModule> AllDependencyModules = new List<UEBuildModule>();
AllDependencyModules.AddRange(PrivateDependencyModules);
AllDependencyModules.AddRange(PublicDependencyModules);
if (bIncludeDynamicallyLoaded)
{
AllDependencyModules.AddRange(DynamicallyLoadedModules);
}
foreach (UEBuildModule DependencyModule in AllDependencyModules)
{
if (!IgnoreReferencedModules.Contains(DependencyModule))
{
// Don't follow circular back-references!
bool bIsCircular = HasCircularDependencyOn(DependencyModule.Name);
if (bForceCircular || !bIsCircular)
{
IgnoreReferencedModules.Add(DependencyModule);
if (!bOnlyDirectDependencies)
{
// Recurse into dependent modules first
DependencyModule.GetAllDependencyModules(ReferencedModules, IgnoreReferencedModules, bIncludeDynamicallyLoaded, bForceCircular, bOnlyDirectDependencies);
}
ReferencedModules.Add(DependencyModule);
}
}
}
}
public override void RecursivelyAddPrecompiledModules(List<UEBuildModule> Modules)
{
if (!Modules.Contains(this))
{
Modules.Add(this);
// Get the dependent modules
List<UEBuildModule> DependentModules = new List<UEBuildModule>();
if (PrivateDependencyModules != null)
{
DependentModules.AddRange(PrivateDependencyModules);
}
if (PublicDependencyModules != null)
{
DependentModules.AddRange(PublicDependencyModules);
}
if (DynamicallyLoadedModules != null)
{
DependentModules.AddRange(DynamicallyLoadedModules);
}
// Find modules for each of them, and add their dependencies too
foreach (UEBuildModule DependentModule in DependentModules)
{
DependentModule.RecursivelyAddPrecompiledModules(Modules);
}
}
}
}
}