// UEBuildModule interface. public override List<FileItem> Compile(CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); var LinkInputFiles = new List<FileItem>(); if( ProjectFileGenerator.bGenerateProjectFiles && IntelliSenseGatherer == null) { // Nothing to do for IntelliSense, bail out early return LinkInputFiles; } var ModuleCompileEnvironment = CreateModuleCompileEnvironment(CompileEnvironment); IncludeSearchPaths = ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludePaths.ToList(); IncludeSearchPaths.AddRange(ModuleCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths.ToList()); if( IntelliSenseGatherer != null ) { // Update project file's set of preprocessor definitions and include paths IntelliSenseGatherer.AddIntelliSensePreprocessorDefinitions( ModuleCompileEnvironment.Config.Definitions ); IntelliSenseGatherer.AddInteliiSenseIncludePaths( ModuleCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths, bAddingSystemIncludes: true ); IntelliSenseGatherer.AddInteliiSenseIncludePaths( ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludePaths, bAddingSystemIncludes: false ); // Bail out. We don't need to actually compile anything while generating project files. return LinkInputFiles; } // Throw an error if the module's source file list referenced any non-existent files. if (SourceFilesToBuild.MissingFiles.Count > 0) { throw new BuildException( "UBT ERROR: Module \"{0}\" references non-existent files:\n{1} (perhaps a file was added to the project but not checked in)", Name, string.Join("\n", SourceFilesToBuild.MissingFiles.Select(M => M.AbsolutePath)) ); } // For an executable or a static library do not use the default RC file - // If the executable wants it, it will be in their source list anyway. // The issue here is that when making a monolithic game, the processing // of the other game modules will stomp the game-specific rc file. if (Binary.Config.Type == UEBuildBinaryType.DynamicLinkLibrary) { // Add default PCLaunch.rc file if this module has no own resource file specified if (SourceFilesToBuild.RCFiles.Count <= 0) { string DefRC = Utils.CleanDirectorySeparators( Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "Runtime/Launch/Resources/Windows/PCLaunch.rc")) ); FileItem Item = FileItem.GetItemByFullPath(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. string ModuleVersionRC = Utils.CleanDirectorySeparators( Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "Runtime/Core/Resources/Windows/ModuleVersionResource.rc.inl")) ); FileItem ModuleVersionItem = FileItem.GetItemByFullPath(ModuleVersionRC); if( !SourceFilesToBuild.RCFiles.Contains(ModuleVersionItem) ) { SourceFilesToBuild.RCFiles.Add(ModuleVersionItem); } } { // Process all of the header file dependencies for this module this.CachePCHUsageForModuleSourceFiles( ModuleCompileEnvironment ); // Make sure our RC files have cached includes. foreach( var RCFile in SourceFilesToBuild.RCFiles ) { RCFile.CachedCPPIncludeInfo = ModuleCompileEnvironment.Config.CPPIncludeInfo; } } // Check to see if this is an Engine module (including program or plugin modules). That is, the module is located under the "Engine" folder var IsGameModule = !Utils.IsFileUnderDirectory( this.ModuleDirectory, Path.Combine( ProjectFileGenerator.EngineRelativePath ) ); // 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( MinFilesUsingPrecompiledHeaderOverride != 0 ) { MinFilesUsingPrecompiledHeader = 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; } // Should we use unity build mode for this module? bool bModuleUsesUnityBuild = BuildConfiguration.bUseUnityBuild || BuildConfiguration.bForceUnityBuild; if (!BuildConfiguration.bForceUnityBuild) { if (bFasterWithoutUnity) { bModuleUsesUnityBuild = false; } else if (IsGameModule && SourceFilesToBuild.CPPFiles.Count < BuildConfiguration.MinGameModuleSourceFilesForUnityBuild) { // 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 bModuleUsesUnityBuild = false; } } // The environment with which to compile the CPP files var CPPCompileEnvironment = ModuleCompileEnvironment; // Precompiled header support. bool bWasModuleCodeCompiled = false; if (BuildPlatform.ShouldUsePCHFiles(CompileEnvironment.Config.Target.Platform, CompileEnvironment.Config.Target.Configuration)) { var PCHGenTimerStart = DateTime.UtcNow; // The code below will figure out whether this module will either use a "unique PCH" (private PCH that will only be included by // this module's code files), or a "shared PCH" (potentially included by many code files in many modules.) Only one or the other // will be used. FileItem SharedPCHHeaderFile = null; // In the case of a shared PCH, we also need to keep track of which module that PCH's header file is a member of string SharedPCHModuleName = String.Empty; if( BuildConfiguration.bUseSharedPCHs && CompileEnvironment.Config.bIsBuildingLibrary ) { Log.TraceVerbose("Module '{0}' was not allowed to use Shared PCHs, because we're compiling to a library", this.Name ); } bool bUseSharedPCHFiles = BuildConfiguration.bUseSharedPCHs && !CompileEnvironment.Config.bIsBuildingLibrary && GlobalCompileEnvironment.SharedPCHHeaderFiles.Count > 0; if( bUseSharedPCHFiles ) { string SharingPCHHeaderFilePath = null; bool bIsASharedPCHModule = bUseSharedPCHFiles && GlobalCompileEnvironment.SharedPCHHeaderFiles.Any( PCH => PCH.Module == this ); if( bIsASharedPCHModule ) { SharingPCHHeaderFilePath = Path.GetFullPath( Path.Combine( ProjectFileGenerator.RootRelativePath, "Engine", "Source", this.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 bCanModuleUseOwnSharedPCH = bAllowSharedPCH && bIsASharedPCHModule && !Binary.Config.bAllowExports && ProcessedDependencies.UniquePCHHeaderFile.AbsolutePath.Equals( SharingPCHHeaderFilePath, StringComparison.InvariantCultureIgnoreCase ); if( bAllowSharedPCH && ( !bIsASharedPCHModule || bCanModuleUseOwnSharedPCH ) ) { // Figure out which shared PCH tier we're in var ReferencedModules = new CaselessDictionary<ModuleIndexPair>(); { this.GetAllDependencyModules( ReferencedModules, bIncludeDynamicallyLoaded:false, bForceCircular:false, bOnlyDirectDependencies:true ); } int LargestSharedPCHHeaderFileIndex = -1; foreach( var DependencyModule in ReferencedModules.Values.OrderBy(P => P.Index).Select(P => P.Module) ) { // These Shared PCHs are ordered from least complex to most complex. We'll start at the last one and search backwards. for( var SharedPCHHeaderFileIndex = GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1; SharedPCHHeaderFileIndex > LargestSharedPCHHeaderFileIndex; --SharedPCHHeaderFileIndex ) { var CurSharedPCHHeaderFile = GlobalCompileEnvironment.SharedPCHHeaderFiles[ SharedPCHHeaderFileIndex ]; if( DependencyModule == CurSharedPCHHeaderFile.Module || ( bIsASharedPCHModule && CurSharedPCHHeaderFile.Module == this ) ) // If we ourselves are a shared PCH module, always at least use our own module as our shared PCH header if we can't find anything better { SharedPCHModuleName = CurSharedPCHHeaderFile.Module.Name; SharedPCHHeaderFile = CurSharedPCHHeaderFile.PCHHeaderFile; LargestSharedPCHHeaderFileIndex = SharedPCHHeaderFileIndex; break; } } if( LargestSharedPCHHeaderFileIndex == GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1 ) { // We've determined that the module is using our most complex PCH header, so we can early-out break; } } // Did we not find a shared PCH header that is being included by this module? This could happen if the module is not including Core.h, even indirectly. if( String.IsNullOrEmpty( SharedPCHModuleName ) ) { throw new BuildException( "Module {0} doesn't use a Shared PCH! Please add a dependency on a Shared PCH module to this module's dependency list", this.Name); } // Keep track of how many modules make use of this PCH for performance diagnostics var LargestSharedPCHHeader = GlobalCompileEnvironment.SharedPCHHeaderFiles[ LargestSharedPCHHeaderFileIndex ]; ++LargestSharedPCHHeader.NumModulesUsingThisPCH; } else { Log.TraceVerbose("Module '{0}' cannot create or use Shared PCHs, because it needs its own private PCH", this.Name); } } // The precompiled header environment for all source files in this module that use a precompiled header, if we even need one PrecompileHeaderEnvironment ModulePCHEnvironment = null; // If there was one header that was included first by enough C++ files, use it as the precompiled header. // Only use precompiled headers for projects with enough files to make the PCH creation worthwhile. if( SharedPCHHeaderFile != null || SourceFilesToBuild.CPPFiles.Count >= MinFilesUsingPrecompiledHeader ) { FileItem PCHToUse; if( SharedPCHHeaderFile != null ) { ModulePCHEnvironment = ApplySharedPCH(GlobalCompileEnvironment, CompileEnvironment, ModuleCompileEnvironment, SourceFilesToBuild.CPPFiles, ref SharedPCHHeaderFile); if (ModulePCHEnvironment != null) { // @todo SharedPCH: Ideally we would exhaustively check for a compatible compile environment (definitions, imports/exports, etc) // Currently, it's possible for the shared PCH to be compiled differently depending on which module UBT happened to have // include it first during the build phase. This could create problems with deterministic builds, or turn up compile // errors unexpectedly due to compile environment differences. Log.TraceVerbose("Module " + Name + " uses existing Shared PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "' (from module " + ModulePCHEnvironment.ModuleName + ")"); } PCHToUse = SharedPCHHeaderFile; } else { PCHToUse = ProcessedDependencies.UniquePCHHeaderFile; } if (PCHToUse != null) { // Update all CPPFiles to point to the PCH foreach (var CPPFile in SourceFilesToBuild.CPPFiles) { CPPFile.PCHHeaderNameInCode = PCHToUse.AbsolutePath; CPPFile.PrecompiledHeaderIncludeFilename = PCHToUse.AbsolutePath; } } // A shared PCH was not already set up for us, so set one up. if( ModulePCHEnvironment == null ) { var PCHHeaderFile = ProcessedDependencies.UniquePCHHeaderFile; var PCHModuleName = this.Name; if( SharedPCHHeaderFile != null ) { PCHHeaderFile = SharedPCHHeaderFile; PCHModuleName = SharedPCHModuleName; } var PCHHeaderNameInCode = SourceFilesToBuild.CPPFiles[ 0 ].PCHHeaderNameInCode; ModulePCHEnvironment = new PrecompileHeaderEnvironment( PCHModuleName, PCHHeaderNameInCode, PCHHeaderFile, ModuleCompileEnvironment.Config.CLRMode, ModuleCompileEnvironment.Config.OptimizeCode ); if( SharedPCHHeaderFile != null ) { // Add to list of shared PCH environments GlobalCompileEnvironment.SharedPCHEnvironments.Add( ModulePCHEnvironment ); Log.TraceVerbose( "Module " + Name + " uses new Shared PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'" ); } else { Log.TraceVerbose( "Module " + Name + " uses a Unique PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'" ); } } } else { Log.TraceVerbose( "Module " + Name + " doesn't use a Shared PCH, and only has " + SourceFilesToBuild.CPPFiles.Count.ToString() + " source file(s). No Unique PCH will be generated." ); } // Compile the C++ source or the unity C++ files that use a PCH environment. if( ModulePCHEnvironment != null ) { // Setup a new compile environment for this module's source files. It's pretty much the exact same as the // module's compile environment, except that it will include a PCH file. var ModulePCHCompileEnvironment = ModuleCompileEnvironment.DeepCopy(); ModulePCHCompileEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Include; ModulePCHCompileEnvironment.Config.PrecompiledHeaderIncludeFilename = ModulePCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath; ModulePCHCompileEnvironment.Config.PCHHeaderNameInCode = ModulePCHEnvironment.PCHHeaderNameInCode; if( SharedPCHHeaderFile != null ) { // Shared PCH headers need to be force included, because we're basically forcing the module to use // the precompiled header that we want, instead of the "first include" in each respective .cpp file ModulePCHCompileEnvironment.Config.bForceIncludePrecompiledHeader = true; } var CPPFilesToBuild = SourceFilesToBuild.CPPFiles; if (bModuleUsesUnityBuild) { // unity files generated for only the set of files which share the same PCH environment CPPFilesToBuild = Unity.GenerateUnityCPPs( 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( Target, CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, ModuleCompileEnvironment, ModuleCompileEnvironment.Config.OutputDirectory, Name, true ); } else { UEBuildModuleCPP SharedPCHModule = (UEBuildModuleCPP)Target.FindOrCreateModuleByName(SharedPCHModuleName); CPPEnvironment SharedPCHCompileEnvironment = GlobalCompileEnvironment.DeepCopy(); SharedPCHCompileEnvironment.Config.bEnableShadowVariableWarning = SharedPCHModule.bEnableShadowVariableWarnings; SharedPCHModule.SetupPublicCompileEnvironment( Binary, false, SharedPCHCompileEnvironment.Config.CPPIncludeInfo.IncludePaths, SharedPCHCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths, SharedPCHCompileEnvironment.Config.Definitions, SharedPCHCompileEnvironment.Config.AdditionalFrameworks, new Dictionary<UEBuildModule,bool>()); PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction( Target, CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, SharedPCHCompileEnvironment, Path.Combine( CompileEnvironment.Config.OutputDirectory, "SharedPCHs" ), "Shared", false ); } ModulePCHEnvironment.PrecompiledHeaderFile = PCHOutput.PrecompiledHeaderFile; ModulePCHEnvironment.OutputObjectFiles.Clear(); ModulePCHEnvironment.OutputObjectFiles.AddRange( PCHOutput.ObjectFiles ); } else if( CPPFilesToBuild.Count < MinFilesUsingPrecompiledHeader ) { Log.TraceVerbose( "Module " + Name + " doesn't use a Shared PCH, and only has " + CPPFilesToBuild.Count.ToString() + " unity source file(s). No Unique PCH will be generated." ); } } if( ModulePCHEnvironment.PrecompiledHeaderFile != null ) { // Link in the object files produced by creating the precompiled header. LinkInputFiles.AddRange( ModulePCHEnvironment.OutputObjectFiles ); // if pch action was generated for the environment then use pch ModulePCHCompileEnvironment.PrecompiledHeaderFile = ModulePCHEnvironment.PrecompiledHeaderFile; // Use this compile environment from now on CPPCompileEnvironment = ModulePCHCompileEnvironment; } LinkInputFiles.AddRange( CPPCompileEnvironment.CompileFiles( Target, CPPFilesToBuild, Name ).ObjectFiles ); bWasModuleCodeCompiled = true; } if( BuildConfiguration.bPrintPerformanceInfo ) { var PCHGenTime = ( DateTime.UtcNow - PCHGenTimerStart ).TotalSeconds; TotalPCHGenTime += PCHGenTime; } } if( !bWasModuleCodeCompiled && SourceFilesToBuild.CPPFiles.Count > 0 ) { var CPPFilesToCompile = SourceFilesToBuild.CPPFiles; if (bModuleUsesUnityBuild) { CPPFilesToCompile = Unity.GenerateUnityCPPs( Target, CPPFilesToCompile, CPPCompileEnvironment, Name ); } LinkInputFiles.AddRange( CPPCompileEnvironment.CompileFiles( Target, CPPFilesToCompile, Name ).ObjectFiles ); } if (AutoGenerateCppInfo != null && AutoGenerateCppInfo.BuildInfo != null && !CPPCompileEnvironment.bHackHeaderGenerator) { string[] GeneratedFiles = Directory.GetFiles(Path.GetDirectoryName(AutoGenerateCppInfo.BuildInfo.FileWildcard), Path.GetFileName(AutoGenerateCppInfo.BuildInfo.FileWildcard)); foreach (string GeneratedFilename in GeneratedFiles) { var GeneratedCppFileItem = FileItem.GetItemByPath(GeneratedFilename); CachePCHUsageForModuleSourceFile(this.Target, CPPCompileEnvironment, GeneratedCppFileItem); // @todo ubtmake: Check for ALL other places where we might be injecting .cpp or .rc files for compiling without caching CachedCPPIncludeInfo first (anything platform specific?) LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles(Target, new List<FileItem> { GeneratedCppFileItem }, Name).ObjectFiles); } } // Compile C files directly. LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles( Target, SourceFilesToBuild.CFiles, Name).ObjectFiles); // Compile CC files directly. LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles( Target, SourceFilesToBuild.CCFiles, Name).ObjectFiles); // Compile MM files directly. LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles( Target, SourceFilesToBuild.MMFiles, Name).ObjectFiles); // Compile RC files. LinkInputFiles.AddRange(CPPCompileEnvironment.CompileRCFiles(Target, SourceFilesToBuild.RCFiles).ObjectFiles); return LinkInputFiles; }
// UEBuildModule interface. public override List<FileItem> Compile(CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment, bool bCompileMonolithic) { UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.TargetPlatform); var LinkInputFiles = new List<FileItem>(); if( ProjectFileGenerator.bGenerateProjectFiles && IntelliSenseGatherer == null) { // Nothing to do for IntelliSense, bail out early return LinkInputFiles; } if(RedistStaticLibraryPath != null && !bBuildingRedistStaticLibrary) { // The redist static library will be added in SetupPrivateLinkEnvironment return LinkInputFiles; } var ModuleCompileEnvironment = new CPPEnvironment(CompileEnvironment); // Override compile environment ModuleCompileEnvironment.Config.OptimizeCode = OptimizeCode; ModuleCompileEnvironment.Config.bUseRTTI = bUseRTTI; ModuleCompileEnvironment.Config.bFasterWithoutUnity = bFasterWithoutUnity; ModuleCompileEnvironment.Config.OptimizeCode = OptimizeCode; ModuleCompileEnvironment.Config.bUseRTTI = bUseRTTI; ModuleCompileEnvironment.Config.bFasterWithoutUnity = bFasterWithoutUnity; ModuleCompileEnvironment.Config.OptimizeCode = OptimizeCode; ModuleCompileEnvironment.Config.bUseRTTI = bUseRTTI; ModuleCompileEnvironment.Config.bEnableBufferSecurityChecks = bEnableBufferSecurityChecks; ModuleCompileEnvironment.Config.bFasterWithoutUnity = bFasterWithoutUnity; ModuleCompileEnvironment.Config.MinFilesUsingPrecompiledHeaderOverride = MinFilesUsingPrecompiledHeaderOverride; ModuleCompileEnvironment.Config.bEnableExceptions = bEnableExceptions; ModuleCompileEnvironment.Config.bEnableInlining = bEnableInlining; ModuleCompileEnvironment.Config.OutputDirectory = Path.Combine(Binary.Config.IntermediateDirectory, Name); // Switch the optimization flag if we're building a game module if(Target.Configuration == UnrealTargetConfiguration.DebugGame && Type == UEBuildModuleType.GameModule) { ModuleCompileEnvironment.Config.TargetConfiguration = CPPTargetConfiguration.Debug; } // Add the module's private definitions. ModuleCompileEnvironment.Config.Definitions.AddRange(Definitions); // Setup the compile environment for the module. SetupPrivateCompileEnvironment( ref ModuleCompileEnvironment.Config.IncludePaths, ref ModuleCompileEnvironment.Config.SystemIncludePaths, ref ModuleCompileEnvironment.Config.Definitions ); if( IntelliSenseGatherer != null ) { // Update project file's set of preprocessor definitions and include paths IntelliSenseGatherer.AddIntelliSensePreprocessorDefinitions( ModuleCompileEnvironment.Config.Definitions ); IntelliSenseGatherer.AddInteliiSenseIncludePaths( ModuleCompileEnvironment.Config.SystemIncludePaths ); IntelliSenseGatherer.AddInteliiSenseIncludePaths( ModuleCompileEnvironment.Config.IncludePaths ); // Bail out. We don't need to actually compile anything while generating project files. return LinkInputFiles; } // Gather a list of CPP files in the project. var CPPFiles = new List<FileItem>(); var CFiles = new List<FileItem>(); var CCFiles = new List<FileItem>(); var MMFiles = new List<FileItem>(); var RCFiles = new List<FileItem>(); var MissingFiles = new List<FileItem>(); foreach (var SourceFile in SourceFiles) { string Extension = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant(); if (SourceFile.bExists) { if (Extension == ".CPP") { CPPFiles.Add(SourceFile); } else if (Extension == ".C") { CFiles.Add(SourceFile); } else if (Extension == ".CC") { CCFiles.Add(SourceFile); } else if (Extension == ".MM" || Extension == ".M") { MMFiles.Add(SourceFile); } else if (Extension == ".RC") { RCFiles.Add(SourceFile); } } else { MissingFiles.Add(SourceFile); } } // 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 (RCFiles.Count <= 0) { string DefRC = Path.Combine(Directory.GetCurrentDirectory(), "Runtime/Launch/Resources/Windows/PCLaunch.rc"); FileItem Item = FileItem.GetItemByFullPath(DefRC); RCFiles.Add(Item); } } // Throw an error if the module's source file list referenced any non-existent files. if (MissingFiles.Count > 0) { string FileList = ""; foreach (var MissingFile in MissingFiles) { FileList += "\n"; FileList += MissingFile.AbsolutePath; } throw new BuildException( "UBT ERROR: Module \"{0}\" references non-existent files:{1} (perhaps a file was added to the project but not checked in)", Name, FileList ); } // Clear out any references to precompiled header usage for source files.. foreach (var CPPFile in CPPFiles) { CPPFile.PCHHeaderNameInCode = null; CPPFile.PrecompiledHeaderIncludeFilename = null; } // Check to see if this is an Engine module (including program or plugin modules). That is, the module is located under the "Engine" folder var IsGameModule = !Utils.IsFileUnderDirectory( this.ModuleDirectory, Path.Combine( ProjectFileGenerator.EngineRelativePath ) ); // 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( MinFilesUsingPrecompiledHeaderOverride != 0 ) { MinFilesUsingPrecompiledHeader = 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; } // Should we use unity build mode for this module? bool bModuleUsesUnityBuild = BuildConfiguration.bUseUnityBuild; if( bFasterWithoutUnity ) { bModuleUsesUnityBuild = false; } else if( !BuildConfiguration.bForceUnityBuild && IsGameModule && CPPFiles != null && CPPFiles.Count < BuildConfiguration.MinGameModuleSourceFilesForUnityBuild ) { // 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 bModuleUsesUnityBuild = false; } // Precompiled header support. bool bWasModuleCodeCompiled = false; if (BuildPlatform.ShouldUsePCHFiles(CompileEnvironment.Config.TargetPlatform, CompileEnvironment.Config.TargetConfiguration)) { var PCHTimerStart = 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 UniquePCHHeaderFile = null; 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; // The precompiled header environment for all source files in this module that use a precompiled header, if we even need one PrecompileHeaderEnvironment ModulePCHEnvironment = null; bool bDisableSharedPCHFiles = (Binary.Config.bCompileMonolithic && CompileEnvironment.Config.bIsBuildingLibrary); if( BuildConfiguration.bUseSharedPCHs && bDisableSharedPCHFiles ) { Log.TraceVerbose("Module '{0}' was not allowed to use SharedPCHs, because we're compiling to a library in monolithic mode", this.Name ); } bool bUseSharedPCHFiles = BuildConfiguration.bUseSharedPCHs && (bDisableSharedPCHFiles == false); bool bIsASharedPCHModule = false; if (bUseSharedPCHFiles) { foreach( var CurSharedPCHHeaderFile in GlobalCompileEnvironment.SharedPCHHeaderFiles ) { if( this == CurSharedPCHHeaderFile.Module ) { bIsASharedPCHModule = true; break; } } } // Map from pch header string to the source files that use that PCH var UsageMapPCH = new Dictionary<string, List<FileItem>>( StringComparer.InvariantCultureIgnoreCase ); // Determine what potential precompiled header is used by each source file. double SharedPCHTotalTime = 0.0; foreach( var CPPFile in CPPFiles ) { if (bUseSharedPCHFiles) { var SharedPCHStartTime = DateTime.UtcNow; // When compiling in modular mode, 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. // @todo SharedPCH: If we ever have SharedPCH headers that themselves belong to modules which never use DLL Exports, we can avoid // generating TWO PCH files by checking for that here. For now, we always assume that SharedPCH headers have exports when // compiling in modular mode. if( bAllowSharedPCH && ( !bIsASharedPCHModule || bCompileMonolithic ) ) { // Figure out which shared PCH tier we're in int LargestSharedPCHHeaderFileIndex = -1; { var AllIncludedFiles = ModuleCompileEnvironment.GetIncludeDependencies( CPPFile ); foreach( var IncludedFile in AllIncludedFiles ) { // These PCHs are ordered from least complex to most complex. We'll start at the last one and search backwards. for( var SharedPCHHeaderFileIndex = GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1; SharedPCHHeaderFileIndex > LargestSharedPCHHeaderFileIndex; --SharedPCHHeaderFileIndex ) { var CurSharedPCHHeaderFile = GlobalCompileEnvironment.SharedPCHHeaderFiles[ SharedPCHHeaderFileIndex ]; if( IncludedFile == 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; } } } if( LargestSharedPCHHeaderFileIndex > -1 ) { var LargestIncludedSharedPCHHeaderFile = GlobalCompileEnvironment.SharedPCHHeaderFiles[LargestSharedPCHHeaderFileIndex]; if( SharedPCHHeaderFile == null ) { SharedPCHModuleName = LargestIncludedSharedPCHHeaderFile.Module.Name; SharedPCHHeaderFile = LargestIncludedSharedPCHHeaderFile.PCHHeaderFile; } else { if( SharedPCHHeaderFile != LargestIncludedSharedPCHHeaderFile.PCHHeaderFile ) { // @todo UBT perf: It is fairly costly to perform this test, as we could easily early-out after we have SharedPCHHeaderFile and not bother testing the rest of the files in the module. But this can be useful to find abusive modules that include a shared PCH header in the bowels of a non-PCH private source file. Console.WriteLine( "WARNING: File '{0}' doesn't use same 'shared' precompiled header as other files in this module: '{1}' vs '{2}'. This can greatly impact compile times. Make sure that your module's private PCH header includes the 'largest' shared PCH header that your module uses", CPPFile.AbsolutePath, SharedPCHHeaderFile, LargestIncludedSharedPCHHeaderFile.PCHHeaderFile ); } } } else { Log.TraceVerbose("File {0} doesn't use a SharedPCH!", CPPFile.AbsolutePath); } SharedPCHTotalTime += ( DateTime.UtcNow - SharedPCHStartTime ).TotalSeconds; } else { Log.TraceVerbose("File '{0}' cannot create or use SharedPCHs, because its module '{1}' needs its own private PCH", CPPFile.AbsolutePath, this.Name); } } { // Find headers used by the source file. List<DependencyInclude> DirectIncludeFilenames = CPPEnvironment.GetDirectIncludeDependencies( CPPFile, ModuleCompileEnvironment.Config.TargetPlatform, ModuleCompileEnvironment.bHackHeaderGenerator ); if( BuildConfiguration.bPrintDebugInfo ) { var IncludedFileNames = new StringBuilder(); foreach( var CurInclude in DirectIncludeFilenames ) { if( IncludedFileNames.Length > 0 ) { IncludedFileNames.Append( ", " ); } IncludedFileNames.Append( Path.GetFileName( CurInclude.IncludeName ) ); } Log.TraceVerbose( "Found direct includes for {0}: {1}", Path.GetFileName( CPPFile.AbsolutePath ), IncludedFileNames ); } if( DirectIncludeFilenames.Count > 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 = DirectIncludeFilenames[ 0 ].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 (string.IsNullOrEmpty(DirectIncludeFilenames[0].IncludeResolvedName) || // 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) { string SourceFilesDirectory = Path.GetDirectoryName(CPPFile.AbsolutePath); // search the include paths to resolve the file. FileItem PrecompiledHeaderIncludeFile = ModuleCompileEnvironment.FindIncludedFile(CPPFile.PCHHeaderNameInCode, !BuildConfiguration.bCheckExternalHeadersForModification, SourceFilesDirectory); if (PrecompiledHeaderIncludeFile != null) { CPPEnvironment.IncludeDependencyCache.CacheResolvedIncludeFullPath(CPPFile, 0, PrecompiledHeaderIncludeFile.AbsolutePath); CPPFile.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFile.AbsolutePath; if (UniquePCHHeaderFile == null) { UniquePCHHeaderFile = PrecompiledHeaderIncludeFile; } } else { throw new BuildException("The first include statement in source file '{0}' is trying to include the file '{1}' as the precompiled header for module '{2}', but that file could not be located in any of the module's include search paths.", CPPFile.AbsolutePath, CPPFile.PCHHeaderNameInCode, this.Name); } } else { CPPFile.PrecompiledHeaderIncludeFilename = DirectIncludeFilenames[0].IncludeResolvedName; if (UniquePCHHeaderFile == null) { UniquePCHHeaderFile = FileItem.GetItemByFullPath(CPPFile.PrecompiledHeaderIncludeFilename); } } } } if( CPPFile.PrecompiledHeaderIncludeFilename == null ) { throw new BuildException( "No PCH usage for file \"{0}\" . Missing #include header?", CPPFile.AbsolutePath ); } // Create a new entry if not in the pch usage map List<FileItem> PCHSourceFiles = null; if( !UsageMapPCH.TryGetValue( CPPFile.PrecompiledHeaderIncludeFilename, out PCHSourceFiles ) ) { PCHSourceFiles = new List<FileItem>(); UsageMapPCH.Add( CPPFile.PrecompiledHeaderIncludeFilename, PCHSourceFiles ); } PCHSourceFiles.Add( CPPFile ); } if( BuildConfiguration.bPrintDebugInfo ) { Log.TraceVerbose( "{0} PCH files for module {1}:", UsageMapPCH.Count, Name ); int MostFilesIncluded = 0; foreach( var CurPCH in UsageMapPCH ) { if( CurPCH.Value.Count > MostFilesIncluded ) { MostFilesIncluded = CurPCH.Value.Count; } Log.TraceVerbose(" {0} ({1} files including it: {2}, ...)", CurPCH.Key, CurPCH.Value.Count, CurPCH.Value[0].AbsolutePath); } } if( UsageMapPCH.Count > 1 ) { // Keep track of the PCH file that is most used within this module string MostFilesAreIncludingPCH = string.Empty; int MostFilesIncluded = 0; foreach( var CurPCH in UsageMapPCH ) { if( CurPCH.Value.Count > MostFilesIncluded ) { MostFilesAreIncludingPCH = CurPCH.Key; MostFilesIncluded = CurPCH.Value.Count; } } // Find all of the files that are not including our "best" PCH header var FilesNotIncludingBestPCH = new StringBuilder(); foreach( var CurPCH in UsageMapPCH ) { if( CurPCH.Key != MostFilesAreIncludingPCH ) { foreach( var SourceFile in CurPCH.Value ) { FilesNotIncludingBestPCH.AppendFormat( "{0} (including {1})\n", SourceFile.AbsolutePath, CurPCH.Key ); } } } // Bail out and let the user know which source files may need to be fixed up throw new BuildException( "All source files in module \"{0}\" must include the same precompiled header first. Currently \"{1}\" is included by most of the source files. The following source files are not including \"{1}\" as their first include:\n\n{2}", Name, MostFilesAreIncludingPCH, FilesNotIncludingBestPCH ); } // If 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( CPPFiles != null && ( SharedPCHHeaderFile != null || CPPFiles.Count >= MinFilesUsingPrecompiledHeader ) ) { if( SharedPCHHeaderFile != null ) { // Check to see if we have a PCH header already setup that we can use foreach( var SharedPCHEnvironment in GlobalCompileEnvironment.SharedPCHEnvironments ) { if( SharedPCHEnvironment.PrecompiledHeaderIncludeFilename == SharedPCHHeaderFile ) { // Don't mix CLR modes if( SharedPCHEnvironment.CLRMode == ModuleCompileEnvironment.Config.CLRMode ) { // Don't mix non-optimized code with optimized code (PCHs won't be compatible) bool bIsDebugBuild = CompileEnvironment.Config.TargetConfiguration == CPPTargetConfiguration.Debug; var SharedPCHCodeOptimization = SharedPCHEnvironment.OptimizeCode; if( SharedPCHCodeOptimization == ModuleRules.CodeOptimization.InNonDebugBuilds && !bIsDebugBuild ) { SharedPCHCodeOptimization = ModuleRules.CodeOptimization.Always; } var ModuleCodeOptimization = ModuleCompileEnvironment.Config.OptimizeCode; if( ModuleCodeOptimization == ModuleRules.CodeOptimization.InNonDebugBuilds && !bIsDebugBuild ) { ModuleCodeOptimization = ModuleRules.CodeOptimization.Always; } if( SharedPCHCodeOptimization.Equals( ModuleCodeOptimization ) ) { // Found a valid shared PCH header to use! // @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 determinstic builds, or turn up compile // errors unexpectedly due to compile environment differences. ModulePCHEnvironment = SharedPCHEnvironment; Log.TraceVerbose( "Module " + Name + " uses existing SharedPCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "' (from module " + ModulePCHEnvironment.ModuleName + ")" ); // Update all CPPFiles to point to the shared PCH foreach( var CPPFile in CPPFiles ) { CPPFile.PCHHeaderNameInCode = SharedPCHHeaderFile.AbsolutePath; CPPFile.PrecompiledHeaderIncludeFilename = SharedPCHHeaderFile.AbsolutePath; } } else { Log.TraceVerbose( "Module {0} cannot use existing SharedPCH '{1}' (from module '{2}') because optimization levels don't match", Name, SharedPCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath, SharedPCHEnvironment.ModuleName ); SharedPCHHeaderFile = null; } } else { Log.TraceVerbose( "Module {0} cannot use existing SharedPCH '{1}' (from module '{2}') because CLR modes don't match", Name, SharedPCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath, SharedPCHEnvironment.ModuleName ); SharedPCHHeaderFile = null; } break; } } } // A shared PCH was not already set up for us, so set one up. if( ModulePCHEnvironment == null ) { var PCHHeaderFile = UniquePCHHeaderFile; var PCHModuleName = this.Name; if( SharedPCHHeaderFile != null ) { PCHHeaderFile = SharedPCHHeaderFile; PCHModuleName = SharedPCHModuleName; } var PCHHeaderNameInCode = CPPFiles[ 0 ].PCHHeaderNameInCode; ModulePCHEnvironment = new PrecompileHeaderEnvironment( PCHModuleName, PCHHeaderNameInCode, PCHHeaderFile, ModuleCompileEnvironment.Config.CLRMode, ModuleCompileEnvironment.Config.OptimizeCode ); Log.TraceVerbose( "PCH file \"{0}\" generated for module \"{1}\" .", PCHHeaderFile.AbsolutePath, Name ); if( SharedPCHHeaderFile != null ) { // Add to list of shared PCH environments GlobalCompileEnvironment.SharedPCHEnvironments.Add( ModulePCHEnvironment ); Log.TraceVerbose( "Module " + Name + " uses new SharedPCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'" ); } else { Log.TraceVerbose( "Module " + Name + " uses a unique PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'" ); } } } // Compile the C++ source or the unity C++ files that use a PCH environment. if( ModulePCHEnvironment != null ) { // Setup a new compile environment for this module's source files. It's pretty much the exact same as the // module's compile environment, except that it will include a PCH file. var ModulePCHCompileEnvironment = new CPPEnvironment( ModuleCompileEnvironment ); { ModulePCHCompileEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Include; ModulePCHCompileEnvironment.Config.PrecompiledHeaderIncludeFilename = ModulePCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath; ModulePCHCompileEnvironment.Config.PCHHeaderNameInCode = ModulePCHEnvironment.PCHHeaderNameInCode; if( SharedPCHHeaderFile != null ) { // Shared PCH headers need to be force included, because we're basically forcing the module to use // the precompiled header that we want, instead of the "first include" in each respective .cpp file ModulePCHCompileEnvironment.Config.bForceIncludePrecompiledHeader = true; } } var CPPFilesToBuild = CPPFiles; if (bModuleUsesUnityBuild) { // unity files generated for only the set of files which share the same PCH environment CPPFilesToBuild = Unity.GenerateUnityCPPs( 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 && ( SharedPCHHeaderFile != null || CPPFilesToBuild.Count >= MinFilesUsingPrecompiledHeader ) ) { bool bAllowDLLExports = true; var PCHOutputDirectory = ModuleCompileEnvironment.Config.OutputDirectory; var PCHModuleName = this.Name; if( SharedPCHHeaderFile != null ) { // Disallow DLLExports when generating shared PCHs. These headers aren't able to export anything, because they're potentially shared between many modules. bAllowDLLExports = false; // Save shared PCHs to a specific folder PCHOutputDirectory = Path.Combine( CompileEnvironment.Config.OutputDirectory, "SharedPCHs" ); // Use a fake module name for "shared" PCHs. It may be used by many modules, so we don't want to use this module's name. PCHModuleName = "Shared"; } var PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction( CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, ModuleCompileEnvironment, PCHOutputDirectory, PCHModuleName, bAllowDLLExports ); ModulePCHEnvironment.PrecompiledHeaderFile = PCHOutput.PrecompiledHeaderFile; ModulePCHEnvironment.OutputObjectFiles.Clear(); ModulePCHEnvironment.OutputObjectFiles.AddRange( PCHOutput.ObjectFiles ); } 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; LinkInputFiles.AddRange( ModulePCHCompileEnvironment.CompileFiles( CPPFilesToBuild, Name ).ObjectFiles ); } else { // otherwise, compile non-pch LinkInputFiles.AddRange( ModuleCompileEnvironment.CompileFiles( CPPFilesToBuild, Name ).ObjectFiles ); } bWasModuleCodeCompiled = true; } if( BuildConfiguration.bPrintPerformanceInfo ) { var TotalPCHTime = DateTime.UtcNow - PCHTimerStart; Trace.TraceInformation( "PCH time for " + Name + " is " + TotalPCHTime.TotalSeconds + "s (shared PCHs: " + SharedPCHTotalTime + "s)" ); } } if( !bWasModuleCodeCompiled ) { if( CPPFiles.Count > 0 ) { var CPPFilesToCompile = CPPFiles; if (bModuleUsesUnityBuild) { CPPFilesToCompile = Unity.GenerateUnityCPPs( CPPFilesToCompile, ModuleCompileEnvironment, Name ); } LinkInputFiles.AddRange( ModuleCompileEnvironment.CompileFiles( CPPFilesToCompile, Name ).ObjectFiles ); } } // Compile C files directly. LinkInputFiles.AddRange(ModuleCompileEnvironment.CompileFiles(CFiles, Name).ObjectFiles); // Compile CC files directly. LinkInputFiles.AddRange(ModuleCompileEnvironment.CompileFiles(CCFiles, Name).ObjectFiles); // Compile MM files directly. LinkInputFiles.AddRange(ModuleCompileEnvironment.CompileFiles(MMFiles, Name).ObjectFiles); // If we're building Rocket, generate a static library for this module if(RedistStaticLibraryPath != null) { // Create a link environment for it LinkEnvironment RedistLinkEnvironment = new LinkEnvironment(); RedistLinkEnvironment.InputFiles.AddRange(LinkInputFiles); RedistLinkEnvironment.Config.TargetArchitecture = CompileEnvironment.Config.TargetArchitecture; RedistLinkEnvironment.Config.TargetConfiguration = CompileEnvironment.Config.TargetConfiguration; RedistLinkEnvironment.Config.TargetPlatform = CompileEnvironment.Config.TargetPlatform; RedistLinkEnvironment.Config.bIsBuildingDLL = false; RedistLinkEnvironment.Config.bIsBuildingLibrary = true; RedistLinkEnvironment.Config.IntermediateDirectory = Binary.Config.IntermediateDirectory; RedistLinkEnvironment.Config.OutputFilePath = RedistStaticLibraryPath; // Replace the items built so far with the library FileItem RedistLibrary = RedistLinkEnvironment.LinkExecutable(false); LinkInputFiles.Clear(); } // Compile RC files. LinkInputFiles.AddRange(ModuleCompileEnvironment.CompileRCFiles(RCFiles).ObjectFiles); // Keep track of this module's public and private UObject source files, so that we can pass those off to UHT if needed { string ModuleSourceFolder = Path.GetFullPath( this.ModuleDirectory ); var ModuleClassesSourceFolder = Path.Combine( ModuleSourceFolder, "Classes" ); // @todo uht: Want to deprecate this eventually foreach( var SourceFile in SourceFiles ) { // Will always be a cache hit (we did this earlier during Compile()) var IncludedFiles = ModuleCompileEnvironment.GetIncludeDependencies( SourceFile ); // Also check for intrinsic classes like "Object.h", which are special cases because they are never included in compiled code and exist only for UHT to parse { // Runtime/CoreUObject/Classes/Object.h { var IntrinsicFileItem = FileItem.GetExistingItemByPath( Path.Combine( ProjectFileGenerator.EngineRelativePath, "Source", "Runtime", Path.Combine( "CoreUObject", "Classes", "Object.h" ) ) ); // @todo uht: In Classes folder right now if( !IntrinsicFileItem.bExists ) { throw new BuildException( "Expecting " + IntrinsicFileItem.AbsolutePath + " to exist" ); } IntrinsicFileItem.HasUObjects = true; IncludedFiles.Add( IntrinsicFileItem ); } // Runtime/Engine/Classes/Model.h { var IntrinsicFileItem = FileItem.GetExistingItemByPath( Path.Combine( ProjectFileGenerator.EngineRelativePath, "Source", "Runtime", Path.Combine( "Engine", "Classes", "Intrinsic", "Model.h" ) ) ); // @todo uht: In Classes folder right now if( !IntrinsicFileItem.bExists ) { throw new BuildException( "Expecting " + IntrinsicFileItem.AbsolutePath + " to exist" ); } IntrinsicFileItem.HasUObjects = true; IncludedFiles.Add( IntrinsicFileItem ); } } // @todo uht: Could check SourceFile.HasUObjects if we want to include .cpps with USTRUCT/UCLASSES here foreach( var IncludedFile in IncludedFiles ) { if( IncludedFile.HasUObjects ) { if( IncludedFile.AbsolutePath.StartsWith( ModuleSourceFolder + Path.DirectorySeparatorChar ) ) { // Is it private or public? bool bIsPublic = false; { // Get the part of the path that is relative to the module source folder var RelativeSourceFilePath = IncludedFile.AbsolutePath.Substring( ModuleSourceFolder.Length + 1 ); if( RelativeSourceFilePath.StartsWith( "Classes" + Path.DirectorySeparatorChar, StringComparison.InvariantCultureIgnoreCase ) ) { // Code files under the legacy 'Classes' directory are always considered public bIsPublic = true; } else if( RelativeSourceFilePath.StartsWith( "Public" + Path.DirectorySeparatorChar, StringComparison.InvariantCultureIgnoreCase ) || ( RelativeSourceFilePath.IndexOf( Path.DirectorySeparatorChar + "Public" + Path.DirectorySeparatorChar, StringComparison.InvariantCultureIgnoreCase ) != -1 ) ) { // Code is under a 'Public' subdirectory somewhere in the hierarchy bIsPublic = true; } } if( bIsPublic ) { PublicUObjectHeaders.Add( IncludedFile ); } else { PrivateUObjectHeaders.Add( IncludedFile ); } } else { // Not from this module, so we don't need to worry about UObjects } } } } } return LinkInputFiles; }