// Copyright 1998-2016 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 { /// /// 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 { /// /// 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 (UEBuildModuleDistribution DistributionLevel = UEBuildModuleDistribution.NoRedist; DistributionLevel > UEBuildModuleDistribution.Public; DistributionLevel--) { string 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 string PS4FolderName = String.Format("{0}ps4{0}", Path.DirectorySeparatorChar); string 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 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 UHTModuleType 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; /// /// The binary the module will be linked into for the current target. Only set after UEBuildBinary.BindModules is called. /// public UEBuildBinary Binary = null; /// /// 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 RuntimeDependencyList RuntimeDependencies; /// /// Returns a list of this module's immediate dependencies. /// /// An enumerable containing the dependencies of the module. public IEnumerable GetDirectDependencyModules() { return PublicDependencyModules.Concat(PrivateDependencyModules).Concat(DynamicallyLoadedModules).Concat(PlatformSpecificDynamicallyLoadedModules); } public UEBuildModule( string InName, UHTModuleType InType, DirectoryReference InModuleDirectory, ModuleRules InRules, FileReference InRulesFile ) { 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 RuntimeDependencyList() : new RuntimeDependencyList(InRules.RuntimeDependencies); IsRedistributableOverride = InRules.IsRedistributableOverride; } /// /// Determines whether this module has a circular dependency on the given module /// public bool HasCircularDependencyOn(string ModuleName) { return Rules.CircularlyReferencedDependentModules.Contains(ModuleName); } /// /// Find all the modules which affect the public compile environment. Searches through /// /// /// /// protected void FindModulesInPublicCompileEnvironment(List Modules, Dictionary ModuleToIncludePathsOnlyFlag) { // bool bModuleIncludePathsOnly; if (!ModuleToIncludePathsOnlyFlag.TryGetValue(this, out bModuleIncludePathsOnly)) { Modules.Add(this); } else if (!bModuleIncludePathsOnly) { return; } ModuleToIncludePathsOnlyFlag[this] = false; foreach (UEBuildModule DependencyModule in PublicDependencyModules) { DependencyModule.FindModulesInPublicCompileEnvironment(Modules, ModuleToIncludePathsOnlyFlag); } // 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) { IncludePathModule.FindIncludePathModulesInPublicCompileEnvironment(Modules, ModuleToIncludePathsOnlyFlag); } } /// /// Find all the modules which affect the public compile environment. Searches through /// /// /// /// protected void FindIncludePathModulesInPublicCompileEnvironment(List Modules, Dictionary ModuleToIncludePathsOnlyFlag) { if (!ModuleToIncludePathsOnlyFlag.ContainsKey(this)) { // Add this module to the list Modules.Add(this); ModuleToIncludePathsOnlyFlag.Add(this, true); // Include any of its public include path modules in the compile environment too foreach (UEBuildModule IncludePathModule in PublicIncludePathModules) { IncludePathModule.FindIncludePathModulesInPublicCompileEnvironment(Modules, ModuleToIncludePathsOnlyFlag); } } } /// /// Sets up the environment for compiling any module that includes the public interface of this module. /// public void AddModuleToCompileEnvironment( UEBuildBinary SourceBinary, bool bIncludePathsOnly, HashSet IncludePaths, HashSet SystemIncludePaths, List Definitions, List AdditionalFrameworks ) { // 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 + "="); } } } // 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 (UEBuildFramework 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 (string Path in PathsToAdd) { string 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.IsGameModule()) { Definitions.Add("DEPRECATED_FORGAME=DEPRECATED"); } // Add this module's private include paths and definitions. AddIncludePathsWithChecks(IncludePaths, PrivateIncludePaths); // Find all the modules that are part of the public compile environment for this module. List Modules = new List(); Dictionary ModuleToIncludePathsOnlyFlag = new Dictionary(); FindModulesInPublicCompileEnvironment(Modules, ModuleToIncludePathsOnlyFlag); // Add in all the modules that are private dependencies foreach (UEBuildModule DependencyModule in PrivateDependencyModules) { DependencyModule.FindModulesInPublicCompileEnvironment(Modules, ModuleToIncludePathsOnlyFlag); } // And finally add in all the modules that are include path only dependencies foreach (UEBuildModule IncludePathModule in PrivateIncludePathModules) { IncludePathModule.FindIncludePathModulesInPublicCompileEnvironment(Modules, ModuleToIncludePathsOnlyFlag); } // Now set up the compile environment for the modules in the original order that we encountered them foreach (UEBuildModule Module in Modules) { Module.AddModuleToCompileEnvironment(Binary, ModuleToIncludePathsOnlyFlag[Module], IncludePaths, SystemIncludePaths, Definitions, AdditionalFrameworks); } } /// /// 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)) { // 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 (UEBuildFramework 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(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment); // Object interface. public override string ToString() { return Name; } /// /// Finds the modules referenced by this module which have not yet been bound to a binary /// /// List of unbound modules public List GetUnboundReferences() { List Modules = new List(); Modules.AddRange(PrivateDependencyModules.Where(x => x.Binary == null)); Modules.AddRange(PublicDependencyModules.Where(x => x.Binary == null)); return Modules; } [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. /// Hashset used to ignore modules which are already added to the list /// 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(List ReferencedModules, HashSet IgnoreReferencedModules, 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) { } /// /// Creates all the modules required for this target /// public void RecursivelyCreateModules(UEBuildTarget Target) { // 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(Target); 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( UHTModuleType InType, string InName, DirectoryReference InModuleDirectory, ModuleRules InRules, FileReference InRulesFile ) : base( InType: InType, InName: InName, InModuleDirectory: InModuleDirectory, InRules: InRules, InRulesFile: InRulesFile ) { } // UEBuildModule interface. public override List Compile(UEBuildTarget Target, 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; public int FindNumberOfGeneratedCppFiles() { return ((null == GeneratedCodeDirectory) || !GeneratedCodeDirectory.Exists()) ? 0 : (GeneratedCodeDirectory.EnumerateFileReferences("*.generated.*.cpp", SearchOption.AllDirectories).Count() + GeneratedCodeDirectory.EnumerateFileReferences("*.generated.cpp", SearchOption.AllDirectories).Count()); } /// /// Categorizes source files into per-extension buckets /// private static void CategorizeSourceFiles(IEnumerable 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); } } } public UEBuildModuleCPP( string InName, UHTModuleType InType, DirectoryReference InModuleDirectory, DirectoryReference InGeneratedCodeDirectory, IntelliSenseGatherer InIntelliSenseGatherer, IEnumerable InSourceFiles, ModuleRules InRules, bool bInBuildSourceFiles, FileReference InRulesFile ) : base( InName, InType, InModuleDirectory, InRules, InRulesFile ) { GeneratedCodeDirectory = InGeneratedCodeDirectory; IntelliSenseGatherer = InIntelliSenseGatherer; CategorizeSourceFiles(InSourceFiles, SourceFilesFound); if (bInBuildSourceFiles) { SourceFilesToBuild.CopyFrom(SourceFilesFound); } Definitions = HashSetFromOptionalEnumerableStringParameter(InRules.Definitions); foreach (string Def in Definitions) { Log.TraceVerbose("Compile Env {0}: {1}", Name, Def); } } // UEBuildModule interface. public override List Compile(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); List LinkInputFiles = new List(); if (ProjectFileGenerator.bGenerateProjectFiles && IntelliSenseGatherer == null) { // Nothing to do for IntelliSense, bail out early return LinkInputFiles; } CPPEnvironment ModuleCompileEnvironment = CreateModuleCompileEnvironment(Target, 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(Target, ModuleCompileEnvironment); // Make sure our RC files have cached includes. foreach (FileItem 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 bool IsPluginModule = ModuleDirectory.IsUnderDirectory(DirectoryReference.Combine(Target.ProjectDirectory, "Plugins")); bool 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 CPPEnvironment CPPCompileEnvironment = ModuleCompileEnvironment; // Precompiled header support. bool bWasModuleCodeCompiled = false; if (BuildPlatform.ShouldUsePCHFiles(CompileEnvironment.Config.Target.Platform, CompileEnvironment.Config.Target.Configuration)) { DateTime 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 List ReferencedModules = new List(); { this.GetAllDependencyModules(ReferencedModules, new HashSet(), bIncludeDynamicallyLoaded: false, bForceCircular: false, bOnlyDirectDependencies: true); } int LargestSharedPCHHeaderFileIndex = -1; foreach (UEBuildModule DependencyModule in ReferencedModules) { // These Shared PCHs are ordered from least complex to most complex. We'll start at the last one and search backwards. for (int SharedPCHHeaderFileIndex = GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1; SharedPCHHeaderFileIndex > LargestSharedPCHHeaderFileIndex; --SharedPCHHeaderFileIndex) { SharedPCHHeaderInfo 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 SharedPCHHeaderInfo LargestSharedPCHHeader = GlobalCompileEnvironment.SharedPCHHeaderFiles[LargestSharedPCHHeaderFileIndex]; ++LargestSharedPCHHeader.NumModulesUsingThisPCH; // Don't allow game modules to use engine PCHs in DebugGame - the optimization settings aren't correct. // @todo: we should be creating shared PCHs ahead of time, and only using them if our settings match. as it is, the first modules compiled // (which are currently plugins) get to call the shots for how the shared PCH gets built, and that might be a game plugin built in debug... if (Target.Configuration == UnrealTargetConfiguration.DebugGame && SharedPCHHeaderFile.Reference.IsUnderDirectory(UnrealBuildTool.EngineDirectory) && !RulesFile.IsUnderDirectory(UnrealBuildTool.EngineDirectory)) { SharedPCHModuleName = null; SharedPCHHeaderFile = null; } } 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 (FileItem 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 && SourceFilesToBuild.CPPFiles.Count > 0) { FileItem PCHHeaderFile = ProcessedDependencies.UniquePCHHeaderFile; string PCHModuleName = this.Name; if (SharedPCHHeaderFile != null) { PCHHeaderFile = SharedPCHHeaderFile; PCHModuleName = SharedPCHModuleName; } string 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. CPPEnvironment 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; } List 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, ModuleCompileEnvironment.Config.PCHOutputDirectory, Name, true); } else { UEBuildModuleCPP SharedPCHModule = (UEBuildModuleCPP)Target.FindOrCreateModuleByName(SharedPCHModuleName); CPPEnvironment SharedPCHCompileEnvironment = GlobalCompileEnvironment.DeepCopy(); SharedPCHCompileEnvironment.Config.bEnableShadowVariableWarning = SharedPCHModule.Rules.bEnableShadowVariableWarnings; List Modules = new List(); Dictionary ModuleToIncludePathsOnlyFlag = new Dictionary(); SharedPCHModule.FindModulesInPublicCompileEnvironment(Modules, ModuleToIncludePathsOnlyFlag); foreach (UEBuildModule Module in Modules) { Module.AddModuleToCompileEnvironment( Binary, ModuleToIncludePathsOnlyFlag[Module], SharedPCHCompileEnvironment.Config.CPPIncludeInfo.IncludePaths, SharedPCHCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths, SharedPCHCompileEnvironment.Config.Definitions, SharedPCHCompileEnvironment.Config.AdditionalFrameworks); } PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction( ToolChain, Target, CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, SharedPCHCompileEnvironment, DirectoryReference.Combine(CompileEnvironment.Config.OutputDirectory, "SharedPCHs"), (CompileEnvironment.Config.PCHOutputDirectory == null) ? null : DirectoryReference.Combine(CompileEnvironment.Config.PCHOutputDirectory, "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; } // In unity pass files through CompileUnityFilesWithToolChain which can opt adaptive unity files out of optimization if (bModuleUsesUnityBuild) { LinkInputFiles.AddRange(CompileUnityFilesWithToolChain(ToolChain, Target, CPPCompileEnvironment, CPPFilesToBuild, Name).ObjectFiles); } else { LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, CPPFilesToBuild, Name).ObjectFiles); } bWasModuleCodeCompiled = true; } if (BuildConfiguration.bPrintPerformanceInfo) { double PCHGenTime = (DateTime.UtcNow - PCHGenTimerStart).TotalSeconds; TotalPCHGenTime += PCHGenTime; } } if (!bWasModuleCodeCompiled && SourceFilesToBuild.CPPFiles.Count > 0) { List CPPFilesToCompile = SourceFilesToBuild.CPPFiles; if (bModuleUsesUnityBuild) { CPPFilesToCompile = Unity.GenerateUnityCPPs(ToolChain, Target, CPPFilesToCompile, CPPCompileEnvironment, Name); // In unity pass files through CompileUnityFilesWithToolChain which can opt adaptive unity files out of optimization LinkInputFiles.AddRange(CompileUnityFilesWithToolChain(ToolChain, Target, CPPCompileEnvironment, CPPFilesToCompile, Name).ObjectFiles); } else { 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) { FileItem GeneratedCppFileItem = FileItem.GetItemByPath(GeneratedFilename); CachePCHUsageForModuleSourceFile(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; } /// /// Compiles the provided CPP unity files. Will /// private CPPOutput CompileUnityFilesWithToolChain(UEToolChain ToolChain, UEBuildTarget Target, CPPEnvironment CompileEnvironment, List SourceFiles, string ModuleName) { List NormalFiles = new List(); List AdaptiveFiles = new List(); foreach (FileItem File in SourceFiles) { // Basic check as to whether something in this module is/isn't a unity file... if (File.ToString().StartsWith("Module.")) { NormalFiles.Add(File); } else { AdaptiveFiles.Add(File); } } CPPOutput OutputFiles = new CPPOutput(); if (NormalFiles.Count > 0) { OutputFiles = ToolChain.CompileCPPFiles(Target, CompileEnvironment, NormalFiles, Name); } if (AdaptiveFiles.Count > 0) { // Create an unoptmized compilation environment. Need to turn of PCH due to different // compiler settings CPPEnvironment UnoptimziedEnvironment = CompileEnvironment.DeepCopy(); UnoptimziedEnvironment.Config.OptimizeCode = ModuleRules.CodeOptimization.Never; UnoptimziedEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.None; // Compile the files CPPOutput AdaptiveOutput = ToolChain.CompileCPPFiles(Target, UnoptimziedEnvironment, AdaptiveFiles, Name); // Merge output OutputFiles.ObjectFiles.AddRange(AdaptiveOutput.ObjectFiles); OutputFiles.DebugDataFiles.AddRange(AdaptiveOutput.DebugDataFiles); } return OutputFiles; } 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 FileItem SharedPCHHeaderFileCopy = SharedPCHHeaderFile; PrecompileHeaderEnvironment 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) ModuleRules.CodeOptimization SharedPCHCodeOptimization = SharedPCHEnvironment.OptimizeCode; ModuleRules.CodeOptimization 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); } DateTime PCHCacheTimerStart = DateTime.UtcNow; UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(ModuleCompileEnvironment.Config.Target.Platform); List 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; FileItem PCHFile = CachePCHUsageForCPPFile(Target, CPPFile, BuildPlatform, IncludePathsToSearch, ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludeFileSearchDictionary); if (BuildConfiguration.bPrintPerformanceInfo) { double PCHCacheTime = (DateTime.UtcNow - PCHCacheTimerStart).TotalSeconds; TotalPCHCacheTime += PCHCacheTime; } return PCHFile; } public void CachePCHUsageForModuleSourceFiles(UEBuildTarget Target, CPPEnvironment ModuleCompileEnvironment) { if (ProcessedDependencies == null) { DateTime PCHCacheTimerStart = DateTime.UtcNow; UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(ModuleCompileEnvironment.Config.Target.Platform); 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! { // Build a single list of include paths to search. List 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. FileItem 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 Dictionary> UsageMapPCH = new Dictionary>(); foreach (FileItem 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 (KeyValuePair> 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> 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> 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}", Name, MostFilesAreIncludingPCH, FilesNotIncludingBestPCH); } } if (BuildConfiguration.bPrintPerformanceInfo) { double 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; } DependencyInclude 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(UEBuildTarget Target, CPPEnvironment BaseCompileEnvironment) { CPPEnvironment Result = BaseCompileEnvironment.DeepCopy(); if (Binary == null) { // Adding this check here as otherwise the call to Binary.Config.IntermediateDirectory will give an // unhandled exception throw new BuildException("UEBuildBinary not set up for module {0}", this.ToString()); } // 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(EGeneratedCodeVersion GeneratedCodeVersion) { if (UHTModuleInfoCache == null) { IEnumerable HeaderFilenames = Directory.GetFiles(ModuleDirectory.FullName, "*.h", SearchOption.AllDirectories); UHTModuleInfo Info = ExternalExecution.CreateUHTModuleInfo(HeaderFilenames, Name, ModuleDirectory, Type, GeneratedCodeVersion); UHTModuleInfoCache = new UHTModuleInfoCacheType(Info.PublicUObjectHeaders.Concat(Info.PublicUObjectClassesHeaders).Concat(Info.PrivateUObjectHeaders).Select(x => x.AbsolutePath).ToList(), Info); } return UHTModuleInfoCache; } public override void GetAllDependencyModules(List ReferencedModules, HashSet IgnoreReferencedModules, 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 (!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 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); } } } } /// /// 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( string InName, UHTModuleType InType, DirectoryReference InModuleDirectory, DirectoryReference InGeneratedCodeDirectory, IntelliSenseGatherer InIntelliSenseGatherer, IEnumerable InSourceFiles, ModuleRules InRules, bool bInBuildSourceFiles, FileReference InRulesFile ) : base(InName, InType, InModuleDirectory, InGeneratedCodeDirectory, InIntelliSenseGatherer, InSourceFiles, InRules, bInBuildSourceFiles, InRulesFile) { PrivateAssemblyReferences = HashSetFromOptionalEnumerableStringParameter(InRules.PrivateAssemblyReferences); } // UEBuildModule interface. public override List Compile(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { CPPEnvironment 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 (string PrivateAssemblyReference in PrivateAssemblyReferences) { ModuleCLREnvironment.AddPrivateAssembly(PrivateAssemblyReference); } // Pass the CLR compilation environment to the standard C++ module compilation code. return base.Compile(Target, 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, DirectoryReference PCHOutputDirectory, 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).ToUpperInvariant(); 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. CPPEnvironment ProjectPCHEnvironment = ProjectCPPEnvironment.DeepCopy(); ProjectPCHEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Create; ProjectPCHEnvironment.Config.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFilename.Reference; ProjectPCHEnvironment.Config.PCHHeaderNameInCode = PCHHeaderNameInCode; ProjectPCHEnvironment.Config.OutputDirectory = OutputDirectory; ProjectPCHEnvironment.Config.PCHOutputDirectory = PCHOutputDirectory; if (!bAllowDLLExports) { for (int CurDefinitionIndex = 0; CurDefinitionIndex < ProjectPCHEnvironment.Config.Definitions.Count; ++CurDefinitionIndex) { // We change DLLEXPORT to DLLIMPORT for "shared" PCH headers string 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); } } }