// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Xml; using Tools.DotNETCommon.CaselessDictionary; namespace UnrealBuildTool { /// /// Type of module. Mirrored in UHT as EBuildModuleType /// public enum UEBuildModuleType { Unknown, Runtime, Developer, Editor, ThirdParty, Program, Game, } public static class UEBuildModuleTypeExtensions { public static bool IsEngineModule(this UEBuildModuleType ModuleType) { return ModuleType != UEBuildModuleType.Game; } } /// /// Distribution level of module. /// Note: The name of each entry is used to search for/create folders /// public enum UEBuildModuleDistribution { /// Binaries can be distributed to everyone Public, /// Can be used by UE4 but not required CarefullyRedist, /// Epic Employees and Contractors NotForLicensees, /// Epic Employees only NoRedist, } /// /// A unit of code compilation and linking. /// public abstract class UEBuildModule { /// /// Known module folders /// public static readonly string RuntimeFolder = String.Format("{0}Runtime{0}", Path.DirectorySeparatorChar); public static readonly string DeveloperFolder = String.Format("{0}Developer{0}", Path.DirectorySeparatorChar); public static readonly string EditorFolder = String.Format("{0}Editor{0}", Path.DirectorySeparatorChar); public static readonly string ProgramsFolder = String.Format("{0}Programs{0}", Path.DirectorySeparatorChar); public static UEBuildModuleType GetModuleTypeFromDescriptor(ModuleDescriptor Module) { switch (Module.Type) { case ModuleHostType.Developer: return UEBuildModuleType.Developer; case ModuleHostType.Editor: case ModuleHostType.EditorNoCommandlet: return UEBuildModuleType.Editor; case ModuleHostType.Program: return UEBuildModuleType.Program; case ModuleHostType.Runtime: case ModuleHostType.RuntimeNoCommandlet: return UEBuildModuleType.Runtime; default: throw new BuildException("Unhandled module type {0}", Module.Type.ToString()); } } public static UEBuildModuleType GetEngineModuleTypeBasedOnLocation(string ModuleName, UEBuildModuleType ModuleType, FileReference ModuleFileName) { string ModuleFileRelativeToEngineDirectory = ModuleFileName.MakeRelativeTo(UnrealBuildTool.EngineDirectory); if (ModuleFileRelativeToEngineDirectory.IndexOf(UEBuildModule.RuntimeFolder, StringComparison.InvariantCultureIgnoreCase) >= 0) { ModuleType = UEBuildModuleType.Runtime; } else if (ModuleFileRelativeToEngineDirectory.IndexOf(UEBuildModule.DeveloperFolder, StringComparison.InvariantCultureIgnoreCase) >= 0) { ModuleType = UEBuildModuleType.Developer; } else if (ModuleFileRelativeToEngineDirectory.IndexOf(UEBuildModule.EditorFolder, StringComparison.InvariantCultureIgnoreCase) >= 0) { ModuleType = UEBuildModuleType.Editor; } else if (ModuleFileRelativeToEngineDirectory.IndexOf(UEBuildModule.ProgramsFolder, StringComparison.InvariantCultureIgnoreCase) >= 0) { ModuleType = UEBuildModuleType.Program; } return ModuleType; } /// /// Checks what distribution level a particular path should have by checking for key folders anywhere in it /// public static UEBuildModuleDistribution GetModuleDistributionLevelBasedOnLocation(string FilePath) { // Get full path to ensure all folder separators are the same FilePath = Path.GetFullPath(FilePath); // Check from highest to lowest (don't actually need to check 'Everyone') for (var DistributionLevel = UEBuildModuleDistribution.NoRedist; DistributionLevel > UEBuildModuleDistribution.Public; DistributionLevel--) { var DistributionFolderName = String.Format("{0}{1}{0}", Path.DirectorySeparatorChar, DistributionLevel.ToString()); if (FilePath.IndexOf(DistributionFolderName, StringComparison.InvariantCultureIgnoreCase) >= 0) { return DistributionLevel; } if (DistributionLevel == UEBuildModuleDistribution.NotForLicensees) { // Extra checks for PS4 and XboxOne folders, which are equivalent to NotForLicensees var PS4FolderName = String.Format("{0}ps4{0}", Path.DirectorySeparatorChar); var XboxFolderName = String.Format("{0}xboxone{0}", Path.DirectorySeparatorChar); if (FilePath.IndexOf(PS4FolderName, StringComparison.InvariantCultureIgnoreCase) >= 0 || FilePath.IndexOf(XboxFolderName, StringComparison.InvariantCultureIgnoreCase) >= 0) { return UEBuildModuleDistribution.NotForLicensees; } } } return UEBuildModuleDistribution.Public; } /// /// Determines the distribution level of a module based on its directory and includes. /// public UEBuildModuleDistribution DetermineDistributionLevel() { List PathsToCheck = new List(); PathsToCheck.Add(ModuleDirectory.FullName); PathsToCheck.AddRange(PublicIncludePaths); PathsToCheck.AddRange(PrivateIncludePaths); // Not sure if these two are necessary as paths will usually be in basic includes too PathsToCheck.AddRange(PublicSystemIncludePaths); PathsToCheck.AddRange(PublicLibraryPaths); UEBuildModuleDistribution DistributionLevel = UEBuildModuleDistribution.Public; if (!Rules.bOutputPubliclyDistributable) { // Keep checking as long as we haven't reached the maximum level for (int PathIndex = 0; PathIndex < PathsToCheck.Count && DistributionLevel != UEBuildModuleDistribution.NoRedist; ++PathIndex) { DistributionLevel = Utils.Max(DistributionLevel, UEBuildModule.GetModuleDistributionLevelBasedOnLocation(PathsToCheck[PathIndex])); } } return DistributionLevel; } /// /// Converts an optional string list parameter to a well-defined hash set. /// protected static HashSet HashSetFromOptionalEnumerableStringParameter(IEnumerable InEnumerableStrings) { return InEnumerableStrings == null ? new HashSet() : new HashSet(InEnumerableStrings); } /// /// The target which owns this module. /// public readonly UEBuildTarget Target; /// /// The name that uniquely identifies the module. /// public readonly string Name; /// /// The type of module being built. Used to switch between debug/development and precompiled/source configurations. /// public UEBuildModuleType Type; /// /// The rules for this module /// public ModuleRules Rules; /// /// Path to the module directory /// public readonly DirectoryReference ModuleDirectory; /// /// Is this module allowed to be redistributed. /// private readonly bool? IsRedistributableOverride; /// /// The name of the .Build.cs file this module was created from, if any /// public FileReference RulesFile; /// /// Tells if this module can be redistributed. /// public bool IsRedistributable() { return IsRedistributableOverride.HasValue ? IsRedistributableOverride.Value : (Type != UEBuildModuleType.Developer && Type != UEBuildModuleType.Editor); } /// /// The binary the module will be linked into for the current target. Only set after UEBuildBinary.BindModules is called. /// public UEBuildBinary Binary = null; /// /// Whether this module is included in the current target. Only set after UEBuildBinary.BindModules is called. /// public bool bIncludedInTarget = false; /// /// Include path for this module's base directory, relative to the Engine/Source directory /// protected string NormalizedModuleIncludePath; /// /// The name of the _API define for this module /// protected readonly string ModuleApiDefine; protected readonly HashSet PublicDefinitions; protected readonly HashSet PublicIncludePaths; protected readonly HashSet PrivateIncludePaths; protected readonly HashSet PublicSystemIncludePaths; protected readonly HashSet PublicLibraryPaths; protected readonly HashSet PublicAdditionalLibraries; protected readonly HashSet PublicFrameworks; protected readonly HashSet PublicWeakFrameworks; protected readonly HashSet PublicAdditionalFrameworks; protected readonly HashSet PublicAdditionalShadowFiles; protected readonly HashSet PublicAdditionalBundleResources; /// /// Names of modules with header files that this module's public interface needs access to. /// protected List PublicIncludePathModules; /// /// Names of modules that this module's public interface depends on. /// protected List PublicDependencyModules; /// /// Names of DLLs that this module should delay load /// protected HashSet PublicDelayLoadDLLs; /// /// Names of modules with header files that this module's private implementation needs access to. /// protected List PrivateIncludePathModules; /// /// Names of modules that this module's private implementation depends on. /// protected List PrivateDependencyModules; /// /// Extra modules this module may require at run time /// protected List DynamicallyLoadedModules; /// /// Extra modules this module may require at run time, that are on behalf of another platform (i.e. shader formats and the like) /// protected List PlatformSpecificDynamicallyLoadedModules; /// /// Files which this module depends on at runtime. /// public List RuntimeDependencies; public UEBuildModule( UEBuildTarget InTarget, string InName, UEBuildModuleType InType, DirectoryReference InModuleDirectory, ModuleRules InRules, FileReference InRulesFile ) { Target = InTarget; Name = InName; Type = InType; ModuleDirectory = InModuleDirectory; Rules = InRules; RulesFile = InRulesFile; NormalizedModuleIncludePath = Utils.CleanDirectorySeparators(ModuleDirectory.MakeRelativeTo(UnrealBuildTool.EngineSourceDirectory), '/'); ModuleApiDefine = Name.ToUpperInvariant() + "_API"; PublicDefinitions = HashSetFromOptionalEnumerableStringParameter(InRules.Definitions); PublicIncludePaths = HashSetFromOptionalEnumerableStringParameter(InRules.PublicIncludePaths); PublicSystemIncludePaths = HashSetFromOptionalEnumerableStringParameter(InRules.PublicSystemIncludePaths); PublicLibraryPaths = HashSetFromOptionalEnumerableStringParameter(InRules.PublicLibraryPaths); PublicAdditionalLibraries = HashSetFromOptionalEnumerableStringParameter(InRules.PublicAdditionalLibraries); PublicFrameworks = HashSetFromOptionalEnumerableStringParameter(InRules.PublicFrameworks); PublicWeakFrameworks = HashSetFromOptionalEnumerableStringParameter(InRules.PublicWeakFrameworks); PublicAdditionalFrameworks = InRules.PublicAdditionalFrameworks == null ? new HashSet() : new HashSet(InRules.PublicAdditionalFrameworks); PublicAdditionalShadowFiles = HashSetFromOptionalEnumerableStringParameter(InRules.PublicAdditionalShadowFiles); PublicAdditionalBundleResources = InRules.AdditionalBundleResources == null ? new HashSet() : new HashSet(InRules.AdditionalBundleResources); PublicDelayLoadDLLs = HashSetFromOptionalEnumerableStringParameter(InRules.PublicDelayLoadDLLs); PrivateIncludePaths = HashSetFromOptionalEnumerableStringParameter(InRules.PrivateIncludePaths); RuntimeDependencies = (InRules.RuntimeDependencies == null) ? new List() : new List(InRules.RuntimeDependencies); IsRedistributableOverride = InRules.IsRedistributableOverride; Target.RegisterModule(this); } /// /// Determines whether this module has a circular dependency on the given module /// public bool HasCircularDependencyOn(string ModuleName) { return Rules.CircularlyReferencedDependentModules.Contains(ModuleName); } /// /// Sets up the environment for compiling any module that includes the public interface of this module. /// protected virtual void SetupPublicCompileEnvironment( UEBuildBinary SourceBinary, bool bIncludePathsOnly, HashSet IncludePaths, HashSet SystemIncludePaths, List Definitions, List AdditionalFrameworks, HashSet VisitedModules ) { // There may be circular dependencies in compile dependencies, so we need to avoid reentrance. if (VisitedModules.Add(this)) { // Add this module's public include paths and definitions. AddIncludePathsWithChecks(IncludePaths, PublicIncludePaths); AddIncludePathsWithChecks(SystemIncludePaths, PublicSystemIncludePaths); Definitions.AddRange(PublicDefinitions); // If this module is being built into a DLL or EXE, set up an IMPORTS or EXPORTS definition for it. if (Binary == null) { // If we're referencing include paths for a module that's not being built, we don't actually need to import anything from it, but we need to avoid barfing when // the compiler encounters an _API define. We also want to avoid changing the compile environment in cases where the module happens to be compiled because it's a dependency // of something else, which cause a fall-through to the code below and set up an empty _API define. if (bIncludePathsOnly) { Log.TraceVerbose("{0}: Include paths only for {1} (no binary)", SourceBinary.Config.OutputFilePaths[0].GetFileNameWithoutExtension(), Name); Definitions.Add(ModuleApiDefine + "="); } } else { FileReference BinaryPath = Binary.Config.OutputFilePaths[0]; FileReference SourceBinaryPath = SourceBinary.Config.OutputFilePaths[0]; if (ProjectFileGenerator.bGenerateProjectFiles || (Binary.Config.Type == UEBuildBinaryType.StaticLibrary)) { // When generating IntelliSense files, never add dllimport/dllexport specifiers as it // simply confuses the compiler Definitions.Add(ModuleApiDefine + "="); } else if (Binary == SourceBinary) { if (Binary.Config.bAllowExports) { Log.TraceVerbose("{0}: Exporting {1} from {2}", SourceBinaryPath.GetFileNameWithoutExtension(), Name, BinaryPath.GetFileNameWithoutExtension()); Definitions.Add(ModuleApiDefine + "=DLLEXPORT"); } else { Log.TraceVerbose("{0}: Not importing/exporting {1} (binary: {2})", SourceBinaryPath.GetFileNameWithoutExtension(), Name, BinaryPath.GetFileNameWithoutExtension()); Definitions.Add(ModuleApiDefine + "="); } } else { // @todo SharedPCH: Public headers included from modules that are not importing the module of that public header, seems invalid. // Those public headers have no business having APIs in them. OnlineSubsystem has some public headers like this. Without changing // this, we need to suppress warnings at compile time. if (bIncludePathsOnly) { Log.TraceVerbose("{0}: Include paths only for {1} (binary: {2})", SourceBinaryPath.GetFileNameWithoutExtension(), Name, BinaryPath.GetFileNameWithoutExtension()); Definitions.Add(ModuleApiDefine + "="); } else if (Binary.Config.bAllowExports) { Log.TraceVerbose("{0}: Importing {1} from {2}", SourceBinaryPath.GetFileNameWithoutExtension(), Name, BinaryPath.GetFileNameWithoutExtension()); Definitions.Add(ModuleApiDefine + "=DLLIMPORT"); } else { Log.TraceVerbose("{0}: Not importing/exporting {1} (binary: {2})", SourceBinaryPath.GetFileNameWithoutExtension(), Name, BinaryPath.GetFileNameWithoutExtension()); Definitions.Add(ModuleApiDefine + "="); } } } if (!bIncludePathsOnly) { // Recurse on this module's public dependencies. foreach (UEBuildModule DependencyModule in PublicDependencyModules) { DependencyModule.SetupPublicCompileEnvironment(SourceBinary, bIncludePathsOnly, IncludePaths, SystemIncludePaths, Definitions, AdditionalFrameworks, VisitedModules); } } // Now add an include paths from modules with header files that we need access to, but won't necessarily be importing foreach (UEBuildModule IncludePathModule in PublicIncludePathModules) { bool bInnerIncludePathsOnly = true; IncludePathModule.SetupPublicCompileEnvironment(SourceBinary, bInnerIncludePathsOnly, IncludePaths, SystemIncludePaths, Definitions, AdditionalFrameworks, VisitedModules); } // Add the module's directory to the include path, so we can root #includes to it IncludePaths.Add(NormalizedModuleIncludePath); // Add the additional frameworks so that the compiler can know about their #include paths AdditionalFrameworks.AddRange(PublicAdditionalFrameworks); // Remember the module so we can refer to it when needed foreach (var Framework in PublicAdditionalFrameworks) { Framework.OwningModule = this; } } } static Regex VCMacroRegex = new Regex(@"\$\([A-Za-z0-9_]+\)"); /// /// Checks if path contains a VC macro /// protected bool DoesPathContainVCMacro(string Path) { return VCMacroRegex.IsMatch(Path); } /// /// Adds PathsToAdd to IncludePaths, performing path normalization and ignoring duplicates. /// protected void AddIncludePathsWithChecks(HashSet IncludePaths, HashSet PathsToAdd) { if (ProjectFileGenerator.bGenerateProjectFiles) { // Extra checks are switched off for IntelliSense generation as they provide // no additional value and cause performance impact. IncludePaths.UnionWith(PathsToAdd); } else { foreach (var Path in PathsToAdd) { var NormalizedPath = Path.TrimEnd('/'); // If path doesn't exist, it may contain VC macro (which is passed directly to and expanded by compiler). if (Directory.Exists(NormalizedPath) || DoesPathContainVCMacro(NormalizedPath)) { IncludePaths.Add(NormalizedPath); } } } } /// /// Sets up the environment for compiling this module. /// protected virtual void SetupPrivateCompileEnvironment( HashSet IncludePaths, HashSet SystemIncludePaths, List Definitions, List AdditionalFrameworks ) { HashSet VisitedModules = new HashSet(); if (this.Type == UEBuildModuleType.Game) { Definitions.Add("DEPRECATED_FORGAME=DEPRECATED"); } // Add this module's private include paths and definitions. AddIncludePathsWithChecks(IncludePaths, PrivateIncludePaths); // Allow the module's public dependencies to modify the compile environment. bool bIncludePathsOnly = false; SetupPublicCompileEnvironment(Binary, bIncludePathsOnly, IncludePaths, SystemIncludePaths, Definitions, AdditionalFrameworks, VisitedModules); // Also allow the module's private dependencies to modify the compile environment. foreach (UEBuildModule DependencyModule in PrivateDependencyModules) { DependencyModule.SetupPublicCompileEnvironment(Binary, bIncludePathsOnly, IncludePaths, SystemIncludePaths, Definitions, AdditionalFrameworks, VisitedModules); } // Add include paths from modules with header files that our private files need access to, but won't necessarily be importing foreach (UEBuildModule IncludePathModule in PrivateIncludePathModules) { bool bInnerIncludePathsOnly = true; IncludePathModule.SetupPublicCompileEnvironment(Binary, bInnerIncludePathsOnly, IncludePaths, SystemIncludePaths, Definitions, AdditionalFrameworks, VisitedModules); } } /// /// Sets up the environment for linking any module that includes the public interface of this module. /// protected virtual void SetupPublicLinkEnvironment( UEBuildBinary SourceBinary, List LibraryPaths, List AdditionalLibraries, List Frameworks, List WeakFrameworks, List AdditionalFrameworks, List AdditionalShadowFiles, List AdditionalBundleResources, List DelayLoadDLLs, List BinaryDependencies, HashSet VisitedModules ) { // There may be circular dependencies in compile dependencies, so we need to avoid reentrance. if (VisitedModules.Add(this)) { // Only process modules that are included in the current target. if (bIncludedInTarget) { // Add this module's binary to the binary dependencies. if (Binary != null && Binary != SourceBinary && !BinaryDependencies.Contains(Binary)) { BinaryDependencies.Add(Binary); } // If this module belongs to a static library that we are not currently building, recursively add the link environment settings for all of its dependencies too. // Keep doing this until we reach a module that is not part of a static library (or external module, since they have no associated binary). // Static libraries do not contain the symbols for their dependencies, so we need to recursively gather them to be linked into other binary types. bool bIsBuildingAStaticLibrary = (SourceBinary != null && SourceBinary.Config.Type == UEBuildBinaryType.StaticLibrary); bool bIsModuleBinaryAStaticLibrary = (Binary != null && Binary.Config.Type == UEBuildBinaryType.StaticLibrary); if (!bIsBuildingAStaticLibrary && bIsModuleBinaryAStaticLibrary) { // Gather all dependencies and recursively call SetupPublicLinkEnvironmnet List AllDependencyModules = new List(); AllDependencyModules.AddRange(PrivateDependencyModules); AllDependencyModules.AddRange(PublicDependencyModules); foreach (UEBuildModule DependencyModule in AllDependencyModules) { bool bIsExternalModule = (DependencyModule as UEBuildExternalModule != null); bool bIsInStaticLibrary = (DependencyModule.Binary != null && DependencyModule.Binary.Config.Type == UEBuildBinaryType.StaticLibrary); if (bIsExternalModule || bIsInStaticLibrary) { DependencyModule.SetupPublicLinkEnvironment(SourceBinary, LibraryPaths, AdditionalLibraries, Frameworks, WeakFrameworks, AdditionalFrameworks, AdditionalShadowFiles, AdditionalBundleResources, DelayLoadDLLs, BinaryDependencies, VisitedModules); } } } // Add this module's public include library paths and additional libraries. LibraryPaths.AddRange(PublicLibraryPaths); AdditionalLibraries.AddRange(PublicAdditionalLibraries); Frameworks.AddRange(PublicFrameworks); WeakFrameworks.AddRange(PublicWeakFrameworks); AdditionalBundleResources.AddRange(PublicAdditionalBundleResources); // Remember the module so we can refer to it when needed foreach (var Framework in PublicAdditionalFrameworks) { Framework.OwningModule = this; } AdditionalFrameworks.AddRange(PublicAdditionalFrameworks); AdditionalShadowFiles.AddRange(PublicAdditionalShadowFiles); DelayLoadDLLs.AddRange(PublicDelayLoadDLLs); } } } /// /// Sets up the environment for linking this module. /// public virtual void SetupPrivateLinkEnvironment( UEBuildBinary SourceBinary, LinkEnvironment LinkEnvironment, List BinaryDependencies, HashSet VisitedModules ) { // Allow the module's public dependencies to add library paths and additional libraries to the link environment. SetupPublicLinkEnvironment(SourceBinary, LinkEnvironment.Config.LibraryPaths, LinkEnvironment.Config.AdditionalLibraries, LinkEnvironment.Config.Frameworks, LinkEnvironment.Config.WeakFrameworks, LinkEnvironment.Config.AdditionalFrameworks, LinkEnvironment.Config.AdditionalShadowFiles, LinkEnvironment.Config.AdditionalBundleResources, LinkEnvironment.Config.DelayLoadDLLs, BinaryDependencies, VisitedModules); // Also allow the module's public and private dependencies to modify the link environment. List AllDependencyModules = new List(); AllDependencyModules.AddRange(PrivateDependencyModules); AllDependencyModules.AddRange(PublicDependencyModules); foreach (UEBuildModule DependencyModule in AllDependencyModules) { DependencyModule.SetupPublicLinkEnvironment(SourceBinary, LinkEnvironment.Config.LibraryPaths, LinkEnvironment.Config.AdditionalLibraries, LinkEnvironment.Config.Frameworks, LinkEnvironment.Config.WeakFrameworks, LinkEnvironment.Config.AdditionalFrameworks, LinkEnvironment.Config.AdditionalShadowFiles, LinkEnvironment.Config.AdditionalBundleResources, LinkEnvironment.Config.DelayLoadDLLs, BinaryDependencies, VisitedModules); } } /// /// Compiles the module, and returns a list of files output by the compiler. /// public abstract List Compile(UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment); // Object interface. public override string ToString() { return Name; } [DebuggerDisplay("{Index}: {Module}")] public class ModuleIndexPair { public UEBuildModule Module; public int Index; } /// /// Gets all of the modules referenced by this module /// /// Hash of all referenced modules with their addition index. /// True if dynamically loaded modules (and all of their dependent modules) should be included. /// True if circular dependencies should be processed /// True to return only this module's direct dependencies public virtual void GetAllDependencyModules(CaselessDictionary ReferencedModules, bool bIncludeDynamicallyLoaded, bool bForceCircular, bool bOnlyDirectDependencies) { } /// /// Gets all of the modules precompiled along with this module /// /// Set of all the precompiled modules public virtual void RecursivelyAddPrecompiledModules(List Modules) { } /// /// Gathers and binds binaries for this module and all of it's dependent modules /// public virtual void RecursivelyProcessUnboundModules() { } /// /// Creates all the modules required for this target /// public void RecursivelyCreateModules() { // Create all the include path modules. These modules may not be added to the target (and we don't process their dependencies), but they need // to be created to set up their compile environment. RecursivelyCreateIncludePathModulesByName(Target, Rules.PublicIncludePathModuleNames, ref PublicIncludePathModules); RecursivelyCreateIncludePathModulesByName(Target, Rules.PrivateIncludePathModuleNames, ref PrivateIncludePathModules); // Create all the dependency modules RecursivelyCreateModulesByName(Target, Rules.PublicDependencyModuleNames, ref PublicDependencyModules); RecursivelyCreateModulesByName(Target, Rules.PrivateDependencyModuleNames, ref PrivateDependencyModules); RecursivelyCreateModulesByName(Target, Rules.DynamicallyLoadedModuleNames, ref DynamicallyLoadedModules); RecursivelyCreateModulesByName(Target, Rules.PlatformSpecificDynamicallyLoadedModuleNames, ref PlatformSpecificDynamicallyLoadedModules); } private static void RecursivelyCreateModulesByName(UEBuildTarget Target, List ModuleNames, ref List Modules) { // Check whether the module list is already set. We set this immediately (via the ref) to avoid infinite recursion. if (Modules == null) { Modules = new List(); foreach (string ModuleName in ModuleNames) { UEBuildModule Module = Target.FindOrCreateModuleByName(ModuleName); if (!Modules.Contains(Module)) { Module.RecursivelyCreateModules(); Modules.Add(Module); } } } } private static void RecursivelyCreateIncludePathModulesByName(UEBuildTarget Target, List ModuleNames, ref List Modules) { // Check whether the module list is already set. We set this immediately (via the ref) to avoid infinite recursion. if (Modules == null) { Modules = new List(); foreach (string ModuleName in ModuleNames) { UEBuildModule Module = Target.FindOrCreateModuleByName(ModuleName); RecursivelyCreateIncludePathModulesByName(Target, Module.Rules.PublicIncludePathModuleNames, ref Module.PublicIncludePathModules); Modules.Add(Module); } } } }; /// /// A module that is never compiled by us, and is only used to group include paths and libraries into a dependency unit. /// class UEBuildExternalModule : UEBuildModule { public UEBuildExternalModule( UEBuildTarget InTarget, UEBuildModuleType InType, string InName, DirectoryReference InModuleDirectory, ModuleRules InRules, FileReference InRulesFile ) : base( InTarget: InTarget, InType: InType, InName: InName, InModuleDirectory: InModuleDirectory, InRules: InRules, InRulesFile: InRulesFile ) { bIncludedInTarget = true; } // UEBuildModule interface. public override List Compile(UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { return new List(); } }; /// /// A module that is compiled from C++ code. /// public class UEBuildModuleCPP : UEBuildModule { public class AutoGenerateCppInfoClass { public class BuildInfoClass { /// /// The wildcard of the *.generated.cpp file which was generated for the module /// public readonly string FileWildcard; public BuildInfoClass(string InWildcard) { Debug.Assert(InWildcard != null); FileWildcard = InWildcard; } } /// /// Information about how to build the .generated.cpp files. If this is null, then we're not building .generated.cpp files for this module. /// public BuildInfoClass BuildInfo; public AutoGenerateCppInfoClass(BuildInfoClass InBuildInfo) { BuildInfo = InBuildInfo; } } /// /// Information about the .generated.cpp file. If this is null then this module doesn't have any UHT-produced code. /// public AutoGenerateCppInfoClass AutoGenerateCppInfo = null; public class SourceFilesClass { public readonly List MissingFiles = new List(); public readonly List CPPFiles = new List(); public readonly List CFiles = new List(); public readonly List CCFiles = new List(); public readonly List MMFiles = new List(); public readonly List RCFiles = new List(); public readonly List OtherFiles = new List(); public int Count { get { return MissingFiles.Count + CPPFiles.Count + CFiles.Count + CCFiles.Count + MMFiles.Count + RCFiles.Count + OtherFiles.Count; } } /// /// Copy from list to list helper. /// /// Source list. /// Destination list. private static void CopyFromListToList(List From, List To) { To.Clear(); To.AddRange(From); } /// /// Copies file lists from other SourceFilesClass to this. /// /// Source object. 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); } } /// /// Adds additional source cpp files for this module. /// /// Files to add. public void AddAdditionalCPPFiles(IEnumerable Files) { SourceFilesToBuild.CPPFiles.AddRange(Files); } /// /// A list of the absolute paths of source files to be built in this module. /// public readonly SourceFilesClass SourceFilesToBuild = new SourceFilesClass(); /// /// A list of the source files that were found for the module. /// public readonly SourceFilesClass SourceFilesFound = new SourceFilesClass(); /// /// The directory for this module's generated code /// public readonly DirectoryReference GeneratedCodeDirectory; /// /// The preprocessor definitions used to compile this module's private implementation. /// HashSet Definitions; /// When set, allows this module to report compiler definitions and include paths for Intellisense IntelliSenseGatherer IntelliSenseGatherer; public List IncludeSearchPaths = new List(); public class ProcessedDependenciesClass { /// /// The file, if any, which is used as the unique PCH for this module /// public FileItem UniquePCHHeaderFile = null; } /// /// The processed dependencies for the class /// public ProcessedDependenciesClass ProcessedDependencies = null; /// /// Hack to skip adding definitions to compile environment. They will be baked into source code by external code. /// public bool bSkipDefinitionsForCompileEnvironment = false; /// /// Categorizes source files into per-extension buckets /// private static void CategorizeSourceFiles(IEnumerable InSourceFiles, SourceFilesClass OutSourceFiles) { foreach (var 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); } } } public UEBuildModuleCPP( UEBuildTarget InTarget, string InName, UEBuildModuleType InType, DirectoryReference InModuleDirectory, DirectoryReference InGeneratedCodeDirectory, IntelliSenseGatherer InIntelliSenseGatherer, IEnumerable InSourceFiles, ModuleRules InRules, bool bInBuildSourceFiles, FileReference InRulesFile ) : base(InTarget, InName, InType, InModuleDirectory, InRules, InRulesFile ) { GeneratedCodeDirectory = InGeneratedCodeDirectory; IntelliSenseGatherer = InIntelliSenseGatherer; CategorizeSourceFiles(InSourceFiles, SourceFilesFound); if (bInBuildSourceFiles) { SourceFilesToBuild.CopyFrom(SourceFilesFound); } Definitions = HashSetFromOptionalEnumerableStringParameter(InRules.Definitions); foreach (var Def in Definitions) { Log.TraceVerbose("Compile Env {0}: {1}", Name, Def); } } // UEBuildModule interface. public override List Compile(UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); var LinkInputFiles = new List(); if (ProjectFileGenerator.bGenerateProjectFiles && IntelliSenseGatherer == null) { // Nothing to do for IntelliSense, bail out early return LinkInputFiles; } var ModuleCompileEnvironment = CreateModuleCompileEnvironment(CompileEnvironment); IncludeSearchPaths = ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludePaths.ToList(); IncludeSearchPaths.AddRange(ModuleCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths.ToList()); if (IntelliSenseGatherer != null) { // Update project file's set of preprocessor definitions and include paths IntelliSenseGatherer.AddIntelliSensePreprocessorDefinitions(ModuleCompileEnvironment.Config.Definitions); IntelliSenseGatherer.AddInteliiSenseIncludePaths(ModuleCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths, bAddingSystemIncludes: true); IntelliSenseGatherer.AddInteliiSenseIncludePaths(ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludePaths, bAddingSystemIncludes: false); // Bail out. We don't need to actually compile anything while generating project files. 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)) ); } // For an executable or a static library do not use the default RC file - // If the executable wants it, it will be in their source list anyway. // The issue here is that when making a monolithic game, the processing // of the other game modules will stomp the game-specific rc file. if (Binary.Config.Type == UEBuildBinaryType.DynamicLinkLibrary) { // Add default PCLaunch.rc file if this module has no own resource file specified if (SourceFilesToBuild.RCFiles.Count <= 0) { FileReference DefRC = FileReference.Combine(UnrealBuildTool.EngineSourceDirectory, "Runtime", "Launch", "Resources", "Windows", "PCLaunch.rc"); FileItem Item = FileItem.GetItemByFileReference(DefRC); SourceFilesToBuild.RCFiles.Add(Item); } // Always compile in the API version resource separately. This is required for the module manager to detect compatible API versions. FileReference ModuleVersionRC = FileReference.Combine(UnrealBuildTool.EngineSourceDirectory, "Runtime", "Core", "Resources", "Windows", "ModuleVersionResource.rc.inl"); FileItem ModuleVersionItem = FileItem.GetItemByFileReference(ModuleVersionRC); if (!SourceFilesToBuild.RCFiles.Contains(ModuleVersionItem)) { SourceFilesToBuild.RCFiles.Add(ModuleVersionItem); } } { // Process all of the header file dependencies for this module this.CachePCHUsageForModuleSourceFiles(ModuleCompileEnvironment); // Make sure our RC files have cached includes. foreach (var RCFile in SourceFilesToBuild.RCFiles) { RCFile.CachedCPPIncludeInfo = ModuleCompileEnvironment.Config.CPPIncludeInfo; } } // Check to see if this is an Engine module (including program or plugin modules). That is, the module is located under the "Engine" folder var IsPluginModule = ModuleDirectory.IsUnderDirectory(DirectoryReference.Combine(Target.ProjectDirectory, "Plugins")); var IsGameModule = !IsPluginModule && !ModuleDirectory.IsUnderDirectory(UnrealBuildTool.EngineDirectory); // 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 = BuildConfiguration.MinFilesUsingPrecompiledHeader; if (Rules.MinFilesUsingPrecompiledHeaderOverride != 0) { MinFilesUsingPrecompiledHeader = Rules.MinFilesUsingPrecompiledHeaderOverride; } else if (IsGameModule && BuildConfiguration.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 (IsGameModule) { // 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 = BuildConfiguration.MinGameModuleSourceFilesForUnityBuild; } // Should we use unity build mode for this module? bool bModuleUsesUnityBuild = false; if (BuildConfiguration.bUseUnityBuild || BuildConfiguration.bForceUnityBuild) { if (BuildConfiguration.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 (enabled in BuildConfiguration)", this.Name); bModuleUsesUnityBuild = true; } } else { Log.TraceVerbose("Module '{0}' not using unity build mode (disabled in BuildConfiguration)", this.Name); } // The environment with which to compile the CPP files var CPPCompileEnvironment = ModuleCompileEnvironment; // Precompiled header support. bool bWasModuleCodeCompiled = false; if (BuildPlatform.ShouldUsePCHFiles(CompileEnvironment.Config.Target.Platform, CompileEnvironment.Config.Target.Configuration)) { var PCHGenTimerStart = DateTime.UtcNow; // The code below will figure out whether this module will either use a "unique PCH" (private PCH that will only be included by // this module's code files), or a "shared PCH" (potentially included by many code files in many modules.) Only one or the other // will be used. FileItem SharedPCHHeaderFile = null; // In the case of a shared PCH, we also need to keep track of which module that PCH's header file is a member of string SharedPCHModuleName = String.Empty; if (BuildConfiguration.bUseSharedPCHs && CompileEnvironment.Config.bIsBuildingLibrary) { Log.TraceVerbose("Module '{0}' was not allowed to use Shared PCHs, because we're compiling to a library", this.Name); } bool bUseSharedPCHFiles = BuildConfiguration.bUseSharedPCHs && !CompileEnvironment.Config.bIsBuildingLibrary && GlobalCompileEnvironment.SharedPCHHeaderFiles.Count > 0; if (bUseSharedPCHFiles) { FileReference SharingPCHHeaderFilePath = null; bool bIsASharedPCHModule = bUseSharedPCHFiles && GlobalCompileEnvironment.SharedPCHHeaderFiles.Any(PCH => PCH.Module == this); if (bIsASharedPCHModule) { SharingPCHHeaderFilePath = FileReference.Combine(UnrealBuildTool.EngineSourceDirectory, Rules.SharedPCHHeaderFile); } // We can't use a shared PCH file when compiling a module // with exports, because the shared PCH can only have imports in it to work correctly. bool bAllowSharedPCH = (Rules.PCHUsage == ModuleRules.PCHUsageMode.NoSharedPCHs) ? false : true; bool bCanModuleUseOwnSharedPCH = bAllowSharedPCH && bIsASharedPCHModule && !Binary.Config.bAllowExports && ProcessedDependencies.UniquePCHHeaderFile.Reference == SharingPCHHeaderFilePath; if (bAllowSharedPCH && (!bIsASharedPCHModule || bCanModuleUseOwnSharedPCH)) { // Figure out which shared PCH tier we're in var ReferencedModules = new CaselessDictionary(); { this.GetAllDependencyModules(ReferencedModules, bIncludeDynamicallyLoaded: false, bForceCircular: false, bOnlyDirectDependencies: true); } int LargestSharedPCHHeaderFileIndex = -1; foreach (var DependencyModule in ReferencedModules.Values.OrderBy(P => P.Index).Select(P => P.Module)) { // These Shared PCHs are ordered from least complex to most complex. We'll start at the last one and search backwards. for (var SharedPCHHeaderFileIndex = GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1; SharedPCHHeaderFileIndex > LargestSharedPCHHeaderFileIndex; --SharedPCHHeaderFileIndex) { var CurSharedPCHHeaderFile = GlobalCompileEnvironment.SharedPCHHeaderFiles[SharedPCHHeaderFileIndex]; if (DependencyModule == CurSharedPCHHeaderFile.Module || (bIsASharedPCHModule && CurSharedPCHHeaderFile.Module == this)) // If we ourselves are a shared PCH module, always at least use our own module as our shared PCH header if we can't find anything better { SharedPCHModuleName = CurSharedPCHHeaderFile.Module.Name; SharedPCHHeaderFile = CurSharedPCHHeaderFile.PCHHeaderFile; LargestSharedPCHHeaderFileIndex = SharedPCHHeaderFileIndex; break; } } if (LargestSharedPCHHeaderFileIndex == GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1) { // We've determined that the module is using our most complex PCH header, so we can early-out break; } } // Did we not find a shared PCH header that is being included by this module? This could happen if the module is not including Core.h, even indirectly. if (String.IsNullOrEmpty(SharedPCHModuleName)) { throw new BuildException("Module {0} doesn't use a Shared PCH! Please add a dependency on a Shared PCH module to this module's dependency list", this.Name); } // Keep track of how many modules make use of this PCH for performance diagnostics var LargestSharedPCHHeader = GlobalCompileEnvironment.SharedPCHHeaderFiles[LargestSharedPCHHeaderFileIndex]; ++LargestSharedPCHHeader.NumModulesUsingThisPCH; } else { Log.TraceVerbose("Module '{0}' cannot create or use Shared PCHs, because it needs its own private PCH", this.Name); } } // The precompiled header environment for all source files in this module that use a precompiled header, if we even need one PrecompileHeaderEnvironment ModulePCHEnvironment = null; // 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 (SharedPCHHeaderFile != null || SourceFilesToBuild.CPPFiles.Count >= MinFilesUsingPrecompiledHeader) { FileItem PCHToUse; if (SharedPCHHeaderFile != null) { ModulePCHEnvironment = ApplySharedPCH(GlobalCompileEnvironment, CompileEnvironment, ModuleCompileEnvironment, SourceFilesToBuild.CPPFiles, ref SharedPCHHeaderFile); if (ModulePCHEnvironment != null) { // @todo SharedPCH: Ideally we would exhaustively check for a compatible compile environment (definitions, imports/exports, etc) // Currently, it's possible for the shared PCH to be compiled differently depending on which module UBT happened to have // include it first during the build phase. This could create problems with deterministic builds, or turn up compile // errors unexpectedly due to compile environment differences. Log.TraceVerbose("Module " + Name + " uses existing Shared PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "' (from module " + ModulePCHEnvironment.ModuleName + ")"); } PCHToUse = SharedPCHHeaderFile; } else { PCHToUse = ProcessedDependencies.UniquePCHHeaderFile; } if (PCHToUse != null) { // Update all CPPFiles to point to the PCH foreach (var CPPFile in SourceFilesToBuild.CPPFiles) { CPPFile.PCHHeaderNameInCode = PCHToUse.AbsolutePath; CPPFile.PrecompiledHeaderIncludeFilename = PCHToUse.Reference; } } // A shared PCH was not already set up for us, so set one up. if (ModulePCHEnvironment == null) { var PCHHeaderFile = ProcessedDependencies.UniquePCHHeaderFile; var PCHModuleName = this.Name; if (SharedPCHHeaderFile != null) { PCHHeaderFile = SharedPCHHeaderFile; PCHModuleName = SharedPCHModuleName; } var PCHHeaderNameInCode = SourceFilesToBuild.CPPFiles[0].PCHHeaderNameInCode; ModulePCHEnvironment = new PrecompileHeaderEnvironment(PCHModuleName, PCHHeaderNameInCode, PCHHeaderFile, ModuleCompileEnvironment.Config.CLRMode, ModuleCompileEnvironment.Config.OptimizeCode); if (SharedPCHHeaderFile != null) { // Add to list of shared PCH environments GlobalCompileEnvironment.SharedPCHEnvironments.Add(ModulePCHEnvironment); Log.TraceVerbose("Module " + Name + " uses new Shared PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'"); } else { Log.TraceVerbose("Module " + Name + " uses a Unique PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'"); } } } else { Log.TraceVerbose("Module " + Name + " doesn't use a Shared PCH, and only has " + SourceFilesToBuild.CPPFiles.Count.ToString() + " source file(s). No Unique PCH will be generated."); } // Compile the C++ source or the unity C++ files that use a PCH environment. if (ModulePCHEnvironment != null) { // Setup a new compile environment for this module's source files. It's pretty much the exact same as the // module's compile environment, except that it will include a PCH file. var ModulePCHCompileEnvironment = ModuleCompileEnvironment.DeepCopy(); ModulePCHCompileEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Include; ModulePCHCompileEnvironment.Config.PrecompiledHeaderIncludeFilename = ModulePCHEnvironment.PrecompiledHeaderIncludeFilename.Reference; ModulePCHCompileEnvironment.Config.PCHHeaderNameInCode = ModulePCHEnvironment.PCHHeaderNameInCode; if (SharedPCHHeaderFile != null) { // Shared PCH headers need to be force included, because we're basically forcing the module to use // the precompiled header that we want, instead of the "first include" in each respective .cpp file ModulePCHCompileEnvironment.Config.bForceIncludePrecompiledHeader = true; } var CPPFilesToBuild = SourceFilesToBuild.CPPFiles; if (bModuleUsesUnityBuild) { // unity files generated for only the set of files which share the same PCH environment CPPFilesToBuild = Unity.GenerateUnityCPPs(ToolChain, Target, CPPFilesToBuild, ModulePCHCompileEnvironment, Name); } // Check if there are enough unity files to warrant pch generation (and we haven't already generated the shared one) if (ModulePCHEnvironment.PrecompiledHeaderFile == null) { if (SharedPCHHeaderFile != null || CPPFilesToBuild.Count >= MinFilesUsingPrecompiledHeader) { CPPOutput PCHOutput; if (SharedPCHHeaderFile == null) { PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction( ToolChain, Target, CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, ModuleCompileEnvironment, ModuleCompileEnvironment.Config.OutputDirectory, Name, true); } else { UEBuildModuleCPP SharedPCHModule = (UEBuildModuleCPP)Target.FindOrCreateModuleByName(SharedPCHModuleName); CPPEnvironment SharedPCHCompileEnvironment = GlobalCompileEnvironment.DeepCopy(); SharedPCHCompileEnvironment.Config.bEnableShadowVariableWarning = SharedPCHModule.Rules.bEnableShadowVariableWarnings; SharedPCHModule.SetupPublicCompileEnvironment( Binary, false, SharedPCHCompileEnvironment.Config.CPPIncludeInfo.IncludePaths, SharedPCHCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths, SharedPCHCompileEnvironment.Config.Definitions, SharedPCHCompileEnvironment.Config.AdditionalFrameworks, new HashSet()); PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction( ToolChain, Target, CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, SharedPCHCompileEnvironment, DirectoryReference.Combine(CompileEnvironment.Config.OutputDirectory, "SharedPCHs"), "Shared", false); } ModulePCHEnvironment.PrecompiledHeaderFile = PCHOutput.PrecompiledHeaderFile; ModulePCHEnvironment.OutputObjectFiles.Clear(); ModulePCHEnvironment.OutputObjectFiles.AddRange(PCHOutput.ObjectFiles); } else if (CPPFilesToBuild.Count < MinFilesUsingPrecompiledHeader) { Log.TraceVerbose("Module " + Name + " doesn't use a Shared PCH, and only has " + CPPFilesToBuild.Count.ToString() + " unity source file(s). No Unique PCH will be generated."); } } if (ModulePCHEnvironment.PrecompiledHeaderFile != null) { // Link in the object files produced by creating the precompiled header. LinkInputFiles.AddRange(ModulePCHEnvironment.OutputObjectFiles); // if pch action was generated for the environment then use pch ModulePCHCompileEnvironment.PrecompiledHeaderFile = ModulePCHEnvironment.PrecompiledHeaderFile; // Use this compile environment from now on CPPCompileEnvironment = ModulePCHCompileEnvironment; } LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, CPPFilesToBuild, Name).ObjectFiles); bWasModuleCodeCompiled = true; } if (BuildConfiguration.bPrintPerformanceInfo) { var PCHGenTime = (DateTime.UtcNow - PCHGenTimerStart).TotalSeconds; TotalPCHGenTime += PCHGenTime; } } if (!bWasModuleCodeCompiled && SourceFilesToBuild.CPPFiles.Count > 0) { var CPPFilesToCompile = SourceFilesToBuild.CPPFiles; if (bModuleUsesUnityBuild) { CPPFilesToCompile = Unity.GenerateUnityCPPs(ToolChain, Target, CPPFilesToCompile, CPPCompileEnvironment, Name); } LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, CPPFilesToCompile, Name).ObjectFiles); } if (AutoGenerateCppInfo != null && AutoGenerateCppInfo.BuildInfo != null && !CPPCompileEnvironment.bHackHeaderGenerator) { string[] GeneratedFiles = Directory.GetFiles(Path.GetDirectoryName(AutoGenerateCppInfo.BuildInfo.FileWildcard), Path.GetFileName(AutoGenerateCppInfo.BuildInfo.FileWildcard)); foreach (string GeneratedFilename in GeneratedFiles) { var GeneratedCppFileItem = FileItem.GetItemByPath(GeneratedFilename); CachePCHUsageForModuleSourceFile(this.Target, CPPCompileEnvironment, 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?) LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, new List { GeneratedCppFileItem }, Name).ObjectFiles); } } // Compile C files directly. LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, SourceFilesToBuild.CFiles, Name).ObjectFiles); // Compile CC files directly. LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, SourceFilesToBuild.CCFiles, Name).ObjectFiles); // Compile MM files directly. LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, SourceFilesToBuild.MMFiles, Name).ObjectFiles); // Compile RC files. LinkInputFiles.AddRange(ToolChain.CompileRCFiles(Target, CPPCompileEnvironment, SourceFilesToBuild.RCFiles).ObjectFiles); return LinkInputFiles; } private PrecompileHeaderEnvironment ApplySharedPCH(CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment, CPPEnvironment ModuleCompileEnvironment, List CPPFiles, ref FileItem SharedPCHHeaderFile) { // Check to see if we have a PCH header already setup that we can use var SharedPCHHeaderFileCopy = SharedPCHHeaderFile; var SharedPCHEnvironment = GlobalCompileEnvironment.SharedPCHEnvironments.Find(Env => Env.PrecompiledHeaderIncludeFilename == SharedPCHHeaderFileCopy); if (SharedPCHEnvironment == null) { return null; } // Don't mix CLR modes if (SharedPCHEnvironment.CLRMode != ModuleCompileEnvironment.Config.CLRMode) { Log.TraceVerbose("Module {0} cannot use existing Shared PCH '{1}' (from module '{2}') because CLR modes don't match", Name, SharedPCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath, SharedPCHEnvironment.ModuleName); SharedPCHHeaderFile = null; return null; } // Don't mix RTTI modes if (Rules.bUseRTTI) { Log.TraceVerbose("Module {0} cannot use existing Shared PCH '{1}' (from module '{2}') because RTTI modes don't match", Name, SharedPCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath, SharedPCHEnvironment.ModuleName); SharedPCHHeaderFile = null; return null; } // Don't mix non-optimized code with optimized code (PCHs won't be compatible) var SharedPCHCodeOptimization = SharedPCHEnvironment.OptimizeCode; var ModuleCodeOptimization = ModuleCompileEnvironment.Config.OptimizeCode; if (CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug) { if (SharedPCHCodeOptimization == ModuleRules.CodeOptimization.InNonDebugBuilds) { SharedPCHCodeOptimization = ModuleRules.CodeOptimization.Always; } if (ModuleCodeOptimization == ModuleRules.CodeOptimization.InNonDebugBuilds) { ModuleCodeOptimization = ModuleRules.CodeOptimization.Always; } } if (SharedPCHCodeOptimization != ModuleCodeOptimization) { Log.TraceVerbose("Module {0} cannot use existing Shared PCH '{1}' (from module '{2}') because optimization levels don't match", Name, SharedPCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath, SharedPCHEnvironment.ModuleName); SharedPCHHeaderFile = null; return null; } return SharedPCHEnvironment; } public static FileItem CachePCHUsageForModuleSourceFile(UEBuildTarget Target, CPPEnvironment ModuleCompileEnvironment, FileItem CPPFile) { if (!CPPFile.bExists) { throw new BuildException("Required source file not found: " + CPPFile.AbsolutePath); } var PCHCacheTimerStart = DateTime.UtcNow; var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(ModuleCompileEnvironment.Config.Target.Platform); var IncludePathsToSearch = ModuleCompileEnvironment.Config.CPPIncludeInfo.GetIncludesPathsToSearch(CPPFile); // 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.CachedCPPIncludeInfo = ModuleCompileEnvironment.Config.CPPIncludeInfo; var PCHFile = CachePCHUsageForCPPFile(Target, CPPFile, BuildPlatform, IncludePathsToSearch, ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludeFileSearchDictionary); if (BuildConfiguration.bPrintPerformanceInfo) { var PCHCacheTime = (DateTime.UtcNow - PCHCacheTimerStart).TotalSeconds; TotalPCHCacheTime += PCHCacheTime; } return PCHFile; } public void CachePCHUsageForModuleSourceFiles(CPPEnvironment ModuleCompileEnvironment) { if (ProcessedDependencies == null) { var PCHCacheTimerStart = DateTime.UtcNow; var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(ModuleCompileEnvironment.Config.Target.Platform); bool bFoundAProblemWithPCHs = false; FileItem UniquePCH = null; foreach (var 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! { // Build a single list of include paths to search. var IncludePathsToSearch = ModuleCompileEnvironment.Config.CPPIncludeInfo.GetIncludesPathsToSearch(CPPFile); // 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.CachedCPPIncludeInfo = ModuleCompileEnvironment.Config.CPPIncludeInfo; // Find headers used by the source file. var PCH = UEBuildModuleCPP.CachePCHUsageForCPPFile(Target, CPPFile, BuildPlatform, IncludePathsToSearch, ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludeFileSearchDictionary); 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 var UsageMapPCH = new Dictionary>(); foreach (var CPPFile in SourceFilesToBuild.CPPFiles) { // Create a new entry if not in the pch usage map UsageMapPCH.GetOrAddNew(CPPFile.PrecompiledHeaderIncludeFilename).Add(CPPFile); } if (BuildConfiguration.bPrintDebugInfo) { Log.TraceVerbose("{0} PCH files for module {1}:", UsageMapPCH.Count, Name); int MostFilesIncluded = 0; foreach (var 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 (var 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 var FilesNotIncludingBestPCH = new StringBuilder(); foreach (var CurPCH in UsageMapPCH.Where(PCH => PCH.Key != MostFilesAreIncludingPCH)) { foreach (var 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}", Name, MostFilesAreIncludingPCH, FilesNotIncludingBestPCH); } } if (BuildConfiguration.bPrintPerformanceInfo) { var PCHCacheTime = (DateTime.UtcNow - PCHCacheTimerStart).TotalSeconds; TotalPCHCacheTime += PCHCacheTime; } } } private static FileItem CachePCHUsageForCPPFile(UEBuildTarget Target, FileItem CPPFile, UEBuildPlatform BuildPlatform, List IncludePathsToSearch, Dictionary IncludeFileSearchDictionary) { // @todo ubtmake: We don't really need to scan every file looking for PCH headers, just need one. The rest is just for error checking. // @todo ubtmake: We don't need all of the direct includes either. We just need the first, unless we want to check for errors. List DirectIncludeFilenames = CPPEnvironment.GetDirectIncludeDependencies(Target, CPPFile, BuildPlatform, bOnlyCachedDependencies: false); if (BuildConfiguration.bPrintDebugInfo) { Log.TraceVerbose("Found direct includes for {0}: {1}", Path.GetFileName(CPPFile.AbsolutePath), string.Join(", ", DirectIncludeFilenames.Select(F => F.IncludeName))); } if (DirectIncludeFilenames.Count == 0) { return null; } var FirstInclude = DirectIncludeFilenames[0]; // The pch header should always be the first include in the source file. // NOTE: This is not an absolute path. This is just the literal include string from the source file! CPPFile.PCHHeaderNameInCode = FirstInclude.IncludeName; // Resolve the PCH header to an absolute path. // Check NullOrEmpty here because if the file could not be resolved we need to throw an exception if (FirstInclude.IncludeResolvedNameIfSuccessful != null && // ignore any preexisting resolve cache if we are not configured to use it. BuildConfiguration.bUseIncludeDependencyResolveCache && // if we are testing the resolve cache, we force UBT to resolve every time to look for conflicts !BuildConfiguration.bTestIncludeDependencyResolveCache) { CPPFile.PrecompiledHeaderIncludeFilename = FirstInclude.IncludeResolvedNameIfSuccessful; return FileItem.GetItemByFileReference(CPPFile.PrecompiledHeaderIncludeFilename); } // search the include paths to resolve the file. FileItem PrecompiledHeaderIncludeFile = CPPEnvironment.FindIncludedFile(CPPFile.PCHHeaderNameInCode, !BuildConfiguration.bCheckExternalHeadersForModification, IncludePathsToSearch, IncludeFileSearchDictionary); if (PrecompiledHeaderIncludeFile == null) { throw new BuildException("The first include statement in source file '{0}' is trying to include the file '{1}' as the precompiled header, but that file could not be located in any of the module's include search paths.", CPPFile.AbsolutePath, CPPFile.PCHHeaderNameInCode); } CPPEnvironment.IncludeDependencyCache[Target].CacheResolvedIncludeFullPath(CPPFile, 0, PrecompiledHeaderIncludeFile.Reference); CPPFile.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFile.Reference; return PrecompiledHeaderIncludeFile; } /// /// Creates a compile environment from a base environment based on the module settings. /// /// An existing environment to base the module compile environment on. /// The new module compile environment. public CPPEnvironment CreateModuleCompileEnvironment(CPPEnvironment BaseCompileEnvironment) { var Result = BaseCompileEnvironment.DeepCopy(); // Override compile environment Result.Config.bFasterWithoutUnity = Rules.bFasterWithoutUnity; Result.Config.OptimizeCode = Rules.OptimizeCode; Result.Config.bUseRTTI = Rules.bUseRTTI; Result.Config.bUseAVX = Rules.bUseAVX; Result.Config.bEnableBufferSecurityChecks = Rules.bEnableBufferSecurityChecks; Result.Config.MinSourceFilesForUnityBuildOverride = Rules.MinSourceFilesForUnityBuildOverride; Result.Config.MinFilesUsingPrecompiledHeaderOverride = Rules.MinFilesUsingPrecompiledHeaderOverride; Result.Config.bBuildLocallyWithSNDBS = Rules.bBuildLocallyWithSNDBS; Result.Config.bEnableExceptions = Rules.bEnableExceptions; Result.Config.bEnableShadowVariableWarning = Rules.bEnableShadowVariableWarnings; Result.Config.bUseStaticCRT = (Target.Rules != null && Target.Rules.bUseStaticCRT); Result.Config.OutputDirectory = DirectoryReference.Combine(Binary.Config.IntermediateDirectory, Name); // 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 (Target.Configuration == UnrealTargetConfiguration.DebugGame) { if (!ModuleDirectory.IsUnderDirectory(UnrealBuildTool.EngineDirectory)) { Result.Config.Target.Configuration = CPPTargetConfiguration.Debug; Result.Config.Definitions.Add("UE_BUILD_DEVELOPMENT_WITH_DEBUGGAME=1"); } } // Add the module's private definitions. Result.Config.Definitions.AddRange(Definitions); // Setup the compile environment for the module. SetupPrivateCompileEnvironment(Result.Config.CPPIncludeInfo.IncludePaths, Result.Config.CPPIncludeInfo.SystemIncludePaths, Result.Config.Definitions, Result.Config.AdditionalFrameworks); // @hack to skip adding definitions to compile environment, they will be baked into source code files if (bSkipDefinitionsForCompileEnvironment) { Result.Config.Definitions.Clear(); Result.Config.CPPIncludeInfo.IncludePaths = new HashSet(BaseCompileEnvironment.Config.CPPIncludeInfo.IncludePaths); } return Result; } public class UHTModuleInfoCacheType { public UHTModuleInfoCacheType(IEnumerable InHeaderFilenames, UHTModuleInfo InInfo) { HeaderFilenames = InHeaderFilenames; Info = InInfo; } public IEnumerable 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; /// /// If any of this module's source files contain UObject definitions, this will return those header files back to the caller /// /// public UHTModuleInfoCacheType GetCachedUHTModuleInfo() { if (UHTModuleInfoCache == null) { IEnumerable HeaderFilenames = Directory.GetFiles(ModuleDirectory.FullName, "*.h", SearchOption.AllDirectories); UHTModuleInfo Info = ExternalExecution.CreateUHTModuleInfo(HeaderFilenames, Target, Name, ModuleDirectory, Type); UHTModuleInfoCache = new UHTModuleInfoCacheType(Info.PublicUObjectHeaders.Concat(Info.PublicUObjectClassesHeaders).Concat(Info.PrivateUObjectHeaders).Select(x => x.AbsolutePath).ToList(), Info); } return UHTModuleInfoCache; } public override void GetAllDependencyModules(CaselessDictionary ReferencedModules, bool bIncludeDynamicallyLoaded, bool bForceCircular, bool bOnlyDirectDependencies) { List AllDependencyModules = new List(); AllDependencyModules.AddRange(PrivateDependencyModules); AllDependencyModules.AddRange(PublicDependencyModules); if (bIncludeDynamicallyLoaded) { AllDependencyModules.AddRange(DynamicallyLoadedModules); AllDependencyModules.AddRange(PlatformSpecificDynamicallyLoadedModules); } foreach (UEBuildModule DependencyModule in AllDependencyModules) { if (!ReferencedModules.ContainsKey(DependencyModule.Name)) { // Don't follow circular back-references! bool bIsCircular = HasCircularDependencyOn(DependencyModule.Name); if (bForceCircular || !bIsCircular) { ReferencedModules[DependencyModule.Name] = null; if (!bOnlyDirectDependencies) { // Recurse into dependent modules first DependencyModule.GetAllDependencyModules(ReferencedModules, bIncludeDynamicallyLoaded, bForceCircular, bOnlyDirectDependencies); } ReferencedModules[DependencyModule.Name] = new ModuleIndexPair { Module = DependencyModule, Index = ReferencedModules.Where(x => x.Value != null).Count() - 1 }; } } } } public override void RecursivelyAddPrecompiledModules(List Modules) { if (!Modules.Contains(this)) { Modules.Add(this); // Get the dependent modules List DependentModules = new List(); if (PrivateDependencyModules != null) { DependentModules.AddRange(PrivateDependencyModules); } if (PublicDependencyModules != null) { DependentModules.AddRange(PublicDependencyModules); } if (DynamicallyLoadedModules != null) { DependentModules.AddRange(DynamicallyLoadedModules); } if (PlatformSpecificDynamicallyLoadedModules != null) { DependentModules.AddRange(PlatformSpecificDynamicallyLoadedModules); } // Find modules for each of them, and add their dependencies too foreach (UEBuildModule DependentModule in DependentModules) { DependentModule.RecursivelyAddPrecompiledModules(Modules); } } } public override void RecursivelyProcessUnboundModules() { try { // Make sure this module is bound to a binary if (!bIncludedInTarget) { throw new BuildException("Module '{0}' should already have been bound to a binary!", Name); } List AllDependencyModules = new List(); AllDependencyModules.AddRange(PrivateDependencyModules); AllDependencyModules.AddRange(PublicDependencyModules); AllDependencyModules.AddRange(DynamicallyLoadedModules); AllDependencyModules.AddRange(PlatformSpecificDynamicallyLoadedModules); foreach (UEBuildModule DependencyModule in AllDependencyModules) { // Skip modules that are included with the target (externals) if (!DependencyModule.bIncludedInTarget) { bool bIsCrossTarget = Rules.PlatformSpecificDynamicallyLoadedModuleNames.Contains(DependencyModule.Name) && !Rules.DynamicallyLoadedModuleNames.Contains(DependencyModule.Name); // Get the binary that this module should be bound to UEBuildBinary BinaryToBindTo = Target.FindOrAddBinaryForModule(DependencyModule, bIsCrossTarget); // Bind this module DependencyModule.Binary = BinaryToBindTo; DependencyModule.bIncludedInTarget = true; // Also add binaries for this module's dependencies DependencyModule.RecursivelyProcessUnboundModules(); } if (Target.ShouldCompileMonolithic() == false) { // Check to see if there is a circular relationship between the module and it's referencer if (DependencyModule.Binary != null) { if (Rules.CircularlyReferencedDependentModules.Contains(DependencyModule.Name)) { DependencyModule.Binary.SetCreateImportLibrarySeparately(true); } } } } } catch (System.Exception ex) { throw new ModuleProcessingException(this, ex); } } } /// /// A module that is compiled from C++ CLR code. /// class UEBuildModuleCPPCLR : UEBuildModuleCPP { /// /// The assemblies referenced by the module's private implementation. /// HashSet PrivateAssemblyReferences; public UEBuildModuleCPPCLR( UEBuildTarget InTarget, string InName, UEBuildModuleType InType, DirectoryReference InModuleDirectory, DirectoryReference InGeneratedCodeDirectory, IntelliSenseGatherer InIntelliSenseGatherer, IEnumerable InSourceFiles, ModuleRules InRules, bool bInBuildSourceFiles, FileReference InRulesFile ) : base(InTarget, InName, InType, InModuleDirectory, InGeneratedCodeDirectory, InIntelliSenseGatherer, InSourceFiles, InRules, bInBuildSourceFiles, InRulesFile) { PrivateAssemblyReferences = HashSetFromOptionalEnumerableStringParameter(InRules.PrivateAssemblyReferences); } // UEBuildModule interface. public override List Compile(UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { var ModuleCLREnvironment = CompileEnvironment.DeepCopy(); // Setup the module environment for the project CLR mode ModuleCLREnvironment.Config.CLRMode = CPPCLRMode.CLREnabled; // Add the private assembly references to the compile environment. foreach (var PrivateAssemblyReference in PrivateAssemblyReferences) { ModuleCLREnvironment.AddPrivateAssembly(PrivateAssemblyReference); } // Pass the CLR compilation environment to the standard C++ module compilation code. return base.Compile(ToolChain, GlobalCompileEnvironment, ModuleCLREnvironment); } public override void SetupPrivateLinkEnvironment( UEBuildBinary SourceBinary, LinkEnvironment LinkEnvironment, List BinaryDependencies, HashSet VisitedModules ) { base.SetupPrivateLinkEnvironment(SourceBinary, LinkEnvironment, BinaryDependencies, VisitedModules); // Setup the link environment for linking a CLR binary. LinkEnvironment.Config.CLRMode = CPPCLRMode.CLREnabled; } } public class UEBuildFramework { public UEBuildFramework(string InFrameworkName) { FrameworkName = InFrameworkName; } public UEBuildFramework(string InFrameworkName, string InFrameworkZipPath) { FrameworkName = InFrameworkName; FrameworkZipPath = InFrameworkZipPath; } public UEBuildFramework(string InFrameworkName, string InFrameworkZipPath, string InCopyBundledAssets) { FrameworkName = InFrameworkName; FrameworkZipPath = InFrameworkZipPath; CopyBundledAssets = InCopyBundledAssets; } public UEBuildModule OwningModule = null; public string FrameworkName = null; public string FrameworkZipPath = null; public string CopyBundledAssets = null; } public class UEBuildBundleResource { public UEBuildBundleResource(string InResourcePath, string InBundleContentsSubdir = "Resources", bool bInShouldLog = true) { ResourcePath = InResourcePath; BundleContentsSubdir = InBundleContentsSubdir; bShouldLog = bInShouldLog; } public string ResourcePath = null; public string BundleContentsSubdir = null; public bool bShouldLog = true; } public class PrecompileHeaderEnvironment { /// /// The name of the module this PCH header is a member of /// public readonly string ModuleName; /// /// PCH header file name as it appears in an #include statement in source code (might include partial, or no relative path.) /// This is needed by some compilers to use PCH features. /// public string PCHHeaderNameInCode; /// /// The source header file that this precompiled header will be generated for /// public readonly FileItem PrecompiledHeaderIncludeFilename; /// /// Whether this precompiled header will be built with CLR features enabled. We won't mix and match CLR PCHs with non-CLR PCHs /// public readonly CPPCLRMode CLRMode; /// /// Whether this precompiled header will be built with code optimization enabled. /// public readonly ModuleRules.CodeOptimization OptimizeCode; /// /// The PCH file we're generating /// public FileItem PrecompiledHeaderFile = null; /// /// Object files emitted from the compiler when generating this precompiled header. These will be linked into modules that /// include this PCH /// public readonly List OutputObjectFiles = new List(); public PrecompileHeaderEnvironment(string InitModuleName, string InitPCHHeaderNameInCode, FileItem InitPrecompiledHeaderIncludeFilename, CPPCLRMode InitCLRMode, ModuleRules.CodeOptimization InitOptimizeCode) { ModuleName = InitModuleName; PCHHeaderNameInCode = InitPCHHeaderNameInCode; PrecompiledHeaderIncludeFilename = InitPrecompiledHeaderIncludeFilename; CLRMode = InitCLRMode; OptimizeCode = InitOptimizeCode; } /// /// Creates a precompiled header action to generate a new pch file /// /// The precompiled header name as it appeared in an #include statement /// Name of the header used for pch. /// The environment the C/C++ files in the project are compiled with. /// The folder to save the generated PCH file to /// Name of the module this PCH is being generated for /// True if we should allow DLLEXPORT definitions for this PCH /// the compilation output result of the created pch. public static CPPOutput GeneratePCHCreationAction(UEToolChain ToolChain, UEBuildTarget Target, string PCHHeaderNameInCode, FileItem PrecompiledHeaderIncludeFilename, CPPEnvironment ProjectCPPEnvironment, DirectoryReference OutputDirectory, string ModuleName, bool bAllowDLLExports) { // Find the header file to be precompiled. Don't skip external headers if (PrecompiledHeaderIncludeFilename.bExists) { // Create a Dummy wrapper around the PCH to avoid problems with #pragma once on clang string PCHGuardDefine = Path.GetFileNameWithoutExtension(PrecompiledHeaderIncludeFilename.AbsolutePath).ToUpper(); string LocalPCHHeaderNameInCode = ToolChain.ConvertPath(PrecompiledHeaderIncludeFilename.AbsolutePath); string TmpPCHHeaderContents = String.Format("#ifndef __AUTO_{0}_H__\n#define __AUTO_{0}_H__\n//Last Write: {2}\n#include \"{1}\"\n#endif//__AUTO_{0}_H__", PCHGuardDefine, LocalPCHHeaderNameInCode, PrecompiledHeaderIncludeFilename.LastWriteTime); FileReference DummyPath = FileReference.Combine( ProjectCPPEnvironment.Config.OutputDirectory, Path.GetFileName(PrecompiledHeaderIncludeFilename.AbsolutePath)); FileItem DummyPCH = FileItem.CreateIntermediateTextFile(DummyPath, TmpPCHHeaderContents); // Create a new C++ environment that is used to create the PCH. var ProjectPCHEnvironment = ProjectCPPEnvironment.DeepCopy(); ProjectPCHEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Create; ProjectPCHEnvironment.Config.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFilename.Reference; ProjectPCHEnvironment.Config.PCHHeaderNameInCode = PCHHeaderNameInCode; ProjectPCHEnvironment.Config.OutputDirectory = OutputDirectory; if (!bAllowDLLExports) { for (var CurDefinitionIndex = 0; CurDefinitionIndex < ProjectPCHEnvironment.Config.Definitions.Count; ++CurDefinitionIndex) { // We change DLLEXPORT to DLLIMPORT for "shared" PCH headers var OldDefinition = ProjectPCHEnvironment.Config.Definitions[CurDefinitionIndex]; if (OldDefinition.EndsWith("=DLLEXPORT")) { ProjectPCHEnvironment.Config.Definitions[CurDefinitionIndex] = OldDefinition.Replace("DLLEXPORT", "DLLIMPORT"); } } } // Cache our CPP environment so that we can check for outdatedness quickly. Only files that have includes need this. DummyPCH.CachedCPPIncludeInfo = ProjectPCHEnvironment.Config.CPPIncludeInfo; Log.TraceVerbose("Found PCH file \"{0}\".", PrecompiledHeaderIncludeFilename); // Create the action to compile the PCH file. return ToolChain.CompileCPPFiles(Target, ProjectPCHEnvironment, new List() { DummyPCH }, ModuleName); } throw new BuildException("Couldn't find PCH file \"{0}\".", PrecompiledHeaderIncludeFilename); } } }