// UEBuildModule interface. public override List<FileItem> Compile(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); List<FileItem> LinkInputFiles = new List<FileItem>(); 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<UEBuildModule> ReferencedModules = new List<UEBuildModule>(); { this.GetAllDependencyModules(ReferencedModules, new HashSet<UEBuildModule>(), 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<FileItem> 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<UEBuildModule> Modules = new List<UEBuildModule>(); Dictionary<UEBuildModule, bool> ModuleToIncludePathsOnlyFlag = new Dictionary<UEBuildModule, bool>(); 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; } 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<FileItem> 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) { 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<FileItem> { 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; }
/// <summary> /// Creates a precompiled header action to generate a new pch file /// </summary> /// <param name="PCHHeaderNameInCode">The precompiled header name as it appeared in an #include statement</param> /// <param name="PrecompiledHeaderIncludeFilename">Name of the header used for pch.</param> /// <param name="ProjectCPPEnvironment">The environment the C/C++ files in the project are compiled with.</param> /// <param name="OutputDirectory">The folder to save the generated PCH file to</param> /// <param name="ModuleName">Name of the module this PCH is being generated for</param> /// <param name="bAllowDLLExports">True if we should allow DLLEXPORT definitions for this PCH</param> /// <returns>the compilation output result of the created pch.</returns> 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<FileItem>() { DummyPCH }, ModuleName); } throw new BuildException("Couldn't find PCH file \"{0}\".", PrecompiledHeaderIncludeFilename); }