/// <summary> /// Validates the configuration. E.g. some options are mutually exclusive whereof some imply others. Also /// some functionality is not available on all platforms. /// @warning: the order of validation is important /// </summary> /// <param name="Configuration">Current configuration (e.g. development, debug, ...)</param> /// <param name="Platform">Current platform (e.g. Win32, PS3, ...)</param> /// <param name="bCreateDebugInfo">True if debug info should be created</param> public static void ValidateConfiguration(CPPTargetConfiguration Configuration, CPPTargetPlatform Platform, bool bCreateDebugInfo) { var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(Platform); // E&C support. if (bSupportEditAndContinue) { bUseIncrementalLinking = BuildPlatform.ShouldUseIncrementalLinking(Platform, Configuration); } // Incremental linking. if (bUseIncrementalLinking) { bUsePDBFiles = BuildPlatform.ShouldUsePDBFiles(Platform, Configuration, bCreateDebugInfo); } // Detailed stats if (bLogDetailedActionStats && bAllowXGE) { // Some build machines apparently have this turned on, so if you really want detailed stats, don't run with XGE bLogDetailedActionStats = false; } // PDB if (bUsePDBFiles) { // NOTE: Currently we allow XGE to run, even with PDBs, until we notice an issue with this bool bDisallowXGEWithPDBFiles = false; if (bDisallowXGEWithPDBFiles) { // Force local execution as we have one PDB for all files using the same PCH. This currently doesn't // scale well with XGE due to required networking bandwidth. Xoreax mentioned that this was going to // be fixed in a future version of the software. bAllowXGE = false; } } // Allow for the build platform to perform custom validation here... // NOTE: This CAN modify the static BuildConfiguration settings!!!! BuildPlatform.ValidateBuildConfiguration(Configuration, Platform, bCreateDebugInfo); if (!BuildPlatform.CanUseXGE()) { bAllowXGE = false; } if (!BuildPlatform.CanUseDistcc()) { bAllowDistcc = false; } if (!BuildPlatform.CanUseSNDBS()) { bAllowSNDBS = false; } }
public FileItem CachePCHUsageForCPPFile(FileItem CPPFile, CppIncludePaths IncludePaths, CppPlatform Platform) { // @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 <DependencyInclude> DirectIncludeFilenames = GetDirectIncludeDependencies(CPPFile, bOnlyCachedDependencies: false); if (UnrealBuildTool.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]; // 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. bUseIncludeDependencyResolveCache && // if we are testing the resolve cache, we force UBT to resolve every time to look for conflicts !bTestIncludeDependencyResolveCache) { CPPFile.PrecompiledHeaderIncludeFilename = FirstInclude.IncludeResolvedNameIfSuccessful; return(FileItem.GetItemByFileReference(CPPFile.PrecompiledHeaderIncludeFilename)); } // search the include paths to resolve the file. string FirstIncludeName = FirstInclude.IncludeName; UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(Platform); // convert back from relative to host path if needed if (!BuildPlatform.UseAbsolutePathsInUnityFiles()) { FirstIncludeName = RemoteExports.UnconvertPath(FirstIncludeName); } FileItem PrecompiledHeaderIncludeFile = CPPHeaders.FindIncludedFile(CPPFile.Reference, FirstIncludeName, IncludePaths); if (PrecompiledHeaderIncludeFile == null) { FirstIncludeName = RemoteExports.UnconvertPath(FirstInclude.IncludeName); 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, FirstIncludeName); } IncludeDependencyCache.CacheResolvedIncludeFullPath(CPPFile, 0, PrecompiledHeaderIncludeFile.Reference, bUseIncludeDependencyResolveCache, bTestIncludeDependencyResolveCache); CPPFile.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFile.Reference; return(PrecompiledHeaderIncludeFile); }
public override CPPOutput CompileRCFiles(UEBuildTarget Target, CPPEnvironment Environment, List <FileItem> RCFiles) { var EnvVars = VCEnvironment.SetEnvironment(Environment.Config.Target.Platform, false); CPPOutput Result = new CPPOutput(); var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(Environment.Config.Target.Platform); foreach (FileItem RCFile in RCFiles) { Action CompileAction = new Action(ActionType.Compile); CompileAction.CommandDescription = "Resource"; CompileAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory.FullName; CompileAction.CommandPath = EnvVars.ResourceCompilerPath; CompileAction.StatusDescription = Path.GetFileName(RCFile.AbsolutePath); // Suppress header spew CompileAction.CommandArguments += " /nologo"; // If we're compiling for 64-bit Windows, also add the _WIN64 definition to the resource // compiler so that we can switch on that in the .rc file using #ifdef. CompileAction.CommandArguments += " /D_WIN64"; // Language CompileAction.CommandArguments += " /l 0x409"; // Include paths. foreach (string IncludePath in Environment.Config.CPPIncludeInfo.IncludePaths) { CompileAction.CommandArguments += string.Format(" /i \"{0}\"", IncludePath); } // System include paths. foreach (var SystemIncludePath in Environment.Config.CPPIncludeInfo.SystemIncludePaths) { CompileAction.CommandArguments += string.Format(" /i \"{0}\"", SystemIncludePath); } // Preprocessor definitions. foreach (string Definition in Environment.Config.Definitions) { CompileAction.CommandArguments += string.Format(" /d \"{0}\"", Definition); } // Add the RES file to the produced item list. FileItem CompiledResourceFile = FileItem.GetItemByFileReference( FileReference.Combine( Environment.Config.OutputDirectory, Path.GetFileName(RCFile.AbsolutePath) + ".res" ) ); CompileAction.ProducedItems.Add(CompiledResourceFile); CompileAction.CommandArguments += string.Format(" /fo \"{0}\"", CompiledResourceFile.AbsolutePath); Result.ObjectFiles.Add(CompiledResourceFile); // Add the RC file as a prerequisite of the action. CompileAction.CommandArguments += string.Format(" \"{0}\"", RCFile.AbsolutePath); // Add the C++ source file and its included files to the prerequisite item list. AddPrerequisiteSourceFile(Target, BuildPlatform, Environment, RCFile, CompileAction.PrerequisiteItems); } return(Result); }
public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List <FileItem> SourceFiles, string ModuleName) { var EnvVars = VCEnvironment.SetEnvironment(CompileEnvironment.Config.Target.Platform, false); StringBuilder Arguments = new StringBuilder(); AppendCLArguments_Global(CompileEnvironment, EnvVars, Arguments); // Add include paths to the argument list. foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths) { string IncludePathRelative = Utils.CleanDirectorySeparators(Utils.MakePathRelativeTo(IncludePath, Path.Combine(ProjectFileGenerator.RootRelativePath, "Engine/Source")), '/'); Arguments.AppendFormat(" /I \"{0}\"", IncludePathRelative); } foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths) { string IncludePathRelative = Utils.CleanDirectorySeparators(Utils.MakePathRelativeTo(IncludePath, Path.Combine(ProjectFileGenerator.RootRelativePath, "Engine/Source")), '/'); Arguments.AppendFormat(" /I \"{0}\"", IncludePathRelative); } if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled) { // Add .NET framework assembly paths. This is needed so that C++/CLI projects // can reference assemblies with #using, without having to hard code a path in the // .cpp file to the assembly's location. foreach (string AssemblyPath in CompileEnvironment.Config.SystemDotNetAssemblyPaths) { Arguments.AppendFormat(" /AI \"{0}\"", AssemblyPath); } // Add explicit .NET framework assembly references foreach (string AssemblyName in CompileEnvironment.Config.FrameworkAssemblyDependencies) { Arguments.AppendFormat(" /FU \"{0}\"", AssemblyName); } // Add private assembly references foreach (PrivateAssemblyInfo CurAssemblyInfo in CompileEnvironment.PrivateAssemblyDependencies) { Arguments.AppendFormat(" /FU \"{0}\"", CurAssemblyInfo.FileItem.AbsolutePath); } } // Add preprocessor definitions to the argument list. foreach (string Definition in CompileEnvironment.Config.Definitions) { // Escape all quotation marks so that they get properly passed with the command line. var DefinitionArgument = Definition.Contains("\"") ? Definition.Replace("\"", "\\\"") : Definition; Arguments.AppendFormat(" /D\"{0}\"", DefinitionArgument); } var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); // Create a compile action for each source file. CPPOutput Result = new CPPOutput(); foreach (FileItem SourceFile in SourceFiles) { Action CompileAction = new Action(ActionType.Compile); CompileAction.CommandDescription = "Compile"; StringBuilder FileArguments = new StringBuilder(); bool bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C"; // Add the C++ source file and its included files to the prerequisite item list. AddPrerequisiteSourceFile(Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems); // If this is a CLR file then make sure our dependent assemblies are added as prerequisites if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled) { foreach (PrivateAssemblyInfo CurPrivateAssemblyDependency in CompileEnvironment.PrivateAssemblyDependencies) { CompileAction.PrerequisiteItems.Add(CurPrivateAssemblyDependency.FileItem); } } bool bEmitsObjectFile = true; if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { // Generate a CPP File that just includes the precompiled header. string PCHCPPFilename = "PCH." + ModuleName + "." + CompileEnvironment.Config.PrecompiledHeaderIncludeFilename.GetFileName() + ".cpp"; FileReference PCHCPPPath = FileReference.Combine(CompileEnvironment.Config.OutputDirectory, PCHCPPFilename); FileItem PCHCPPFile = FileItem.CreateIntermediateTextFile( PCHCPPPath, string.Format("#include \"{0}\"\r\n", CompileEnvironment.Config.PrecompiledHeaderIncludeFilename) ); // Make sure the original source directory the PCH header file existed in is added as an include // path -- it might be a private PCH header and we need to make sure that its found! string OriginalPCHHeaderDirectory = Path.GetDirectoryName(SourceFile.AbsolutePath); FileArguments.AppendFormat(" /I \"{0}\"", OriginalPCHHeaderDirectory); var PrecompiledFileExtension = UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.UWP).GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader); // Add the precompiled header file to the produced items list. FileItem PrecompiledHeaderFile = FileItem.GetItemByFileReference( FileReference.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + PrecompiledFileExtension ) ); CompileAction.ProducedItems.Add(PrecompiledHeaderFile); Result.PrecompiledHeaderFile = PrecompiledHeaderFile; // Add the parameters needed to compile the precompiled header file to the command-line. FileArguments.AppendFormat(" /Yc\"{0}\"", CompileEnvironment.Config.PrecompiledHeaderIncludeFilename); FileArguments.AppendFormat(" /Fp\"{0}\"", PrecompiledHeaderFile.AbsolutePath); // If we're creating a PCH that will be used to compile source files for a library, we need // the compiled modules to retain a reference to PCH's module, so that debugging information // will be included in the library. This is also required to avoid linker warning "LNK4206" // when linking an application that uses this library. if (CompileEnvironment.Config.bIsBuildingLibrary) { // NOTE: The symbol name we use here is arbitrary, and all that matters is that it is // unique per PCH module used in our library string FakeUniquePCHSymbolName = CompileEnvironment.Config.PrecompiledHeaderIncludeFilename.GetFileNameWithoutExtension(); FileArguments.AppendFormat(" /Yl{0}", FakeUniquePCHSymbolName); } FileArguments.AppendFormat(" \"{0}\"", PCHCPPFile.AbsolutePath); CompileAction.StatusDescription = PCHCPPFilename; } else { if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { CompileAction.bIsUsingPCH = true; CompileAction.PrerequisiteItems.Add(CompileEnvironment.PrecompiledHeaderFile); FileArguments.AppendFormat(" /Yu\"{0}\"", CompileEnvironment.Config.PCHHeaderNameInCode); FileArguments.AppendFormat(" /Fp\"{0}\"", CompileEnvironment.PrecompiledHeaderFile.AbsolutePath); // Is it unsafe to always force inclusion? Clang is doing it, and .generated.cpp files // won't work otherwise, because they're not located in the context of the module, // so they can't access the module's PCH without an absolute path. //if( CompileEnvironment.Config.bForceIncludePrecompiledHeader ) { // Force include the precompiled header file. This is needed because we may have selected a // precompiled header that is different than the first direct include in the C++ source file, but // we still need to make sure that our precompiled header is the first thing included! FileArguments.AppendFormat(" /FI\"{0}\"", CompileEnvironment.Config.PCHHeaderNameInCode); } } // Add the source file path to the command-line. FileArguments.AppendFormat(" \"{0}\"", SourceFile.AbsolutePath); CompileAction.StatusDescription = Path.GetFileName(SourceFile.AbsolutePath); } if (bEmitsObjectFile) { var ObjectFileExtension = UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.UWP).GetBinaryExtension(UEBuildBinaryType.Object); // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByFileReference( FileReference.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension ) ); CompileAction.ProducedItems.Add(ObjectFile); Result.ObjectFiles.Add(ObjectFile); FileArguments.AppendFormat(" /Fo\"{0}\"", ObjectFile.AbsolutePath); } // Create PDB files if we were configured to do that. // // Also, when debug info is off and XGE is enabled, force PDBs, otherwise it will try to share // a PDB file, which causes PCH creation to be serial rather than parallel (when debug info is disabled) // --> See https://udn.epicgames.com/lists/showpost.php?id=50619&list=unprog3 if (BuildConfiguration.bUsePDBFiles || (BuildConfiguration.bAllowXGE && !CompileEnvironment.Config.bCreateDebugInfo)) { string PDBFileName; bool bActionProducesPDB = false; // All files using the same PCH are required to share the same PDB that was used when compiling the PCH if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { PDBFileName = "PCH." + ModuleName + "." + CompileEnvironment.Config.PrecompiledHeaderIncludeFilename.GetFileName(); } // Files creating a PCH use a PDB per file. else if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { PDBFileName = "PCH." + ModuleName + "." + CompileEnvironment.Config.PrecompiledHeaderIncludeFilename.GetFileName(); bActionProducesPDB = true; } // Ungrouped C++ files use a PDB per file. else if (!bIsPlainCFile) { PDBFileName = Path.GetFileName(SourceFile.AbsolutePath); bActionProducesPDB = true; } // Group all plain C files that doesn't use PCH into the same PDB else { PDBFileName = "MiscPlainC"; } // Specify the PDB file that the compiler should write to. FileItem PDBFile = FileItem.GetItemByFileReference( FileReference.Combine( CompileEnvironment.Config.OutputDirectory, PDBFileName + ".pdb" ) ); FileArguments.AppendFormat(" /Fd\"{0}\"", PDBFile.AbsolutePath); // Only use the PDB as an output file if we want PDBs and this particular action is // the one that produces the PDB (as opposed to no debug info, where the above code // is needed, but not the output PDB, or when multiple files share a single PDB, so // only the action that generates it should count it as output directly) if (BuildConfiguration.bUsePDBFiles && bActionProducesPDB) { CompileAction.ProducedItems.Add(PDBFile); Result.DebugDataFiles.Add(PDBFile); } } // Add C or C++ specific compiler arguments. if (bIsPlainCFile) { AppendCLArguments_C(FileArguments); } else { AppendCLArguments_CPP(CompileEnvironment, FileArguments); } CompileAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory.FullName; CompileAction.CommandPath = EnvVars.CompilerPath; CompileAction.CommandArguments = Arguments.ToString() + FileArguments.ToString() + CompileEnvironment.Config.AdditionalArguments; if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { Log.TraceVerbose("Creating PCH: " + CompileEnvironment.Config.PrecompiledHeaderIncludeFilename); Log.TraceVerbose(" Command: " + CompileAction.CommandArguments); } else { Log.TraceVerbose(" Compiling: " + CompileAction.StatusDescription); Log.TraceVerbose(" Command: " + CompileAction.CommandArguments); } // VC++ always outputs the source file name being compiled, so we don't need to emit this ourselves CompileAction.bShouldOutputStatusDescription = false; // Don't farm out creation of precompiled headers as it is the critical path task. CompileAction.bCanExecuteRemotely = CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create || BuildConfiguration.bAllowRemotelyCompiledPCHs ; // @todo: XGE has problems remote compiling C++/CLI files that use .NET Framework 4.0 if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled) { CompileAction.bCanExecuteRemotely = false; } } return(Result); }
/// <summary> /// Given a set of C++ files, generates another set of C++ files that #include all the original /// files, the goal being to compile the same code in fewer translation units. /// The "unity" files are written to the CompileEnvironment's OutputDirectory. /// </summary> /// <param name="Target">The target we're building</param> /// <param name="CPPFiles">The C++ files to #include.</param> /// <param name="CompileEnvironment">The environment that is used to compile the C++ files.</param> /// <param name="BaseName">Base name to use for the Unity files</param> /// <returns>The "unity" C++ files.</returns> public static List <FileItem> GenerateUnityCPPs( UEBuildTarget Target, List <FileItem> CPPFiles, CPPEnvironment CompileEnvironment, string BaseName ) { var ToolChain = UEToolChain.GetPlatformToolChain(CompileEnvironment.Config.Target.Platform); var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); // Figure out size of all input files combined. We use this to determine whether to use larger unity threshold or not. long TotalBytesInCPPFiles = CPPFiles.Sum(F => F.Info.Length); // We have an increased threshold for unity file size if, and only if, all files fit into the same unity file. This // is beneficial when dealing with PCH files. The default PCH creation limit is X unity files so if we generate < X // this could be fairly slow and we'd rather bump the limit a bit to group them all into the same unity file. // // Optimization only makes sense if PCH files are enabled. bool bForceIntoSingleUnityFile = BuildConfiguration.bStressTestUnity || (TotalBytesInCPPFiles < BuildConfiguration.NumIncludedBytesPerUnityCPP * 2 && CompileEnvironment.ShouldUsePCHs()); // Build the list of unity files. List <FileCollection> AllUnityFiles; { var CPPUnityFileBuilder = new UnityFileBuilder(bForceIntoSingleUnityFile ? -1 : BuildConfiguration.NumIncludedBytesPerUnityCPP); foreach (var CPPFile in CPPFiles) { if (!bForceIntoSingleUnityFile && CPPFile.AbsolutePath.Contains(".GeneratedWrapper.")) { CPPUnityFileBuilder.EndCurrentUnityFile(); CPPUnityFileBuilder.AddFile(CPPFile); CPPUnityFileBuilder.EndCurrentUnityFile(); } else { CPPUnityFileBuilder.AddFile(CPPFile); } // Now that the CPPFile is part of this unity file, we will no longer need to treat it like a root level prerequisite for our // dependency cache, as it is now an "indirect include" from the unity file. We'll clear out the compile environment // attached to this file. This prevents us from having to cache all of the indirect includes from these files inside our // dependency cache, which speeds up iterative builds a lot! CPPFile.CachedCPPIncludeInfo = null; } AllUnityFiles = CPPUnityFileBuilder.GetUnityFiles(); } //THIS CHANGE WAS MADE TO FIX THE BUILD IN OUR BRANCH //DO NOT MERGE THIS BACK TO MAIN string PCHHeaderNameInCode = CPPFiles.Count > 0 ? CPPFiles[0].PCHHeaderNameInCode : ""; if (CompileEnvironment.Config.PrecompiledHeaderIncludeFilename != null) { PCHHeaderNameInCode = ToolChain.ConvertPath(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename); // Generated unity .cpp files always include the PCH using an absolute path, so we need to update // our compile environment's PCH header name to use this instead of the text it pulled from the original // C++ source files CompileEnvironment.Config.PCHHeaderNameInCode = PCHHeaderNameInCode; } // Create a set of CPP files that combine smaller CPP files into larger compilation units, along with the corresponding // actions to compile them. int CurrentUnityFileCount = 0; var UnityCPPFiles = new List <FileItem>(); foreach (var UnityFile in AllUnityFiles) { ++CurrentUnityFileCount; StringWriter OutputUnityCPPWriter = new StringWriter(); StringWriter OutputUnityCPPWriterExtra = null; // add an extra file for UBT to get the #include dependencies from if (BuildPlatform.RequiresExtraUnityCPPWriter() == true) { OutputUnityCPPWriterExtra = new StringWriter(); } OutputUnityCPPWriter.WriteLine("// This file is automatically generated at compile-time to include some subset of the user-created cpp files."); // Explicitly include the precompiled header first, since Visual C++ expects the first top-level #include to be the header file // that was used to create the PCH. if (CompileEnvironment.Config.PrecompiledHeaderIncludeFilename != null) { OutputUnityCPPWriter.WriteLine("#include \"{0}\"", PCHHeaderNameInCode); if (OutputUnityCPPWriterExtra != null) { OutputUnityCPPWriterExtra.WriteLine("#include \"{0}\"", PCHHeaderNameInCode); } } // Add source files to the unity file foreach (var CPPFile in UnityFile.Files) { OutputUnityCPPWriter.WriteLine("#include \"{0}\"", ToolChain.ConvertPath(CPPFile.AbsolutePath)); if (OutputUnityCPPWriterExtra != null) { OutputUnityCPPWriterExtra.WriteLine("#include \"{0}\"", CPPFile.AbsolutePath); } } // Determine unity file path name string UnityCPPFilePath; if (AllUnityFiles.Count > 1) { UnityCPPFilePath = string.Format("Module.{0}.{1}_of_{2}.cpp", BaseName, CurrentUnityFileCount, AllUnityFiles.Count); } else { UnityCPPFilePath = string.Format("Module.{0}.cpp", BaseName); } UnityCPPFilePath = Path.Combine(CompileEnvironment.Config.OutputDirectory, UnityCPPFilePath); // Write the unity file to the intermediate folder. FileItem UnityCPPFile = FileItem.CreateIntermediateTextFile(UnityCPPFilePath, OutputUnityCPPWriter.ToString()); if (OutputUnityCPPWriterExtra != null) { FileItem.CreateIntermediateTextFile(UnityCPPFilePath + ".ex", OutputUnityCPPWriterExtra.ToString()); } UnityCPPFile.RelativeCost = UnityFile.TotalLength; UnityCPPFile.PCHHeaderNameInCode = PCHHeaderNameInCode; UnityCPPFiles.Add(UnityCPPFile); // Cache information about the unity .cpp dependencies // @todo ubtmake urgent: Fails when building remotely for Mac because unity .cpp has an include for a PCH on the REMOTE machine UEBuildModuleCPP.CachePCHUsageForModuleSourceFile(Target, CompileEnvironment, UnityCPPFile); } return(UnityCPPFiles); }
public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName) { if (Arches.Count == 0) { throw new BuildException("At least one architecture (armv7, x86, etc) needs to be selected in the project settings to build"); } if (!bHasPrintedApiLevel) { Console.WriteLine("Compiling Native code with NDK API '{0}'", GetNdkApiLevel()); bHasPrintedApiLevel = true; } string BaseArguments = ""; if (CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create) { BaseArguments += " -Werror"; } // Directly added NDK files for NDK extensions ConditionallyAddNDKSourceFiles(SourceFiles, ModuleName); // Deal with dynamic modules removed by architecture GenerateEmptyLinkFunctionsForRemovedModules(SourceFiles, ModuleName, CompileEnvironment.Config.OutputDirectory); // Add preprocessor definitions to the argument list. foreach (string Definition in CompileEnvironment.Config.Definitions) { BaseArguments += string.Format(" -D \"{0}\"", Definition); } var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); var NDKRoot = Environment.GetEnvironmentVariable("NDKROOT").Replace("\\", "/"); string BasePCHName = ""; var PCHExtension = UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.Android).GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader); if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { BasePCHName = RemoveArchName(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath).Replace(PCHExtension, ""); } // Create a compile action for each source file. CPPOutput Result = new CPPOutput(); foreach (string Arch in Arches) { if (ShouldSkipModule(ModuleName, Arch)) { continue; } foreach (string GPUArchitecture in GPUArchitectures) { // which toolchain to use string Arguments = GetCLArguments_Global(CompileEnvironment, Arch) + BaseArguments; switch (Arch) { case "-armv7": Arguments += " -DPLATFORM_64BITS=0 -DPLATFORM_ANDROID_ARM=1"; break; case "-arm64": Arguments += " -DPLATFORM_64BITS=1 -DPLATFORM_ANDROID_ARM64=1"; break; case "-x86": Arguments += " -DPLATFORM_64BITS=0 -DPLATFORM_ANDROID_X86=1"; break; case "-x64": Arguments += " -DPLATFORM_64BITS=1 -DPLATFORM_ANDROID_X64=1"; break; default: Arguments += " -DPLATFORM_64BITS=0 -DPLATFORM_ANDROID_ARM=1"; break; } if (GPUArchitecture == "-esdeferred") { Arguments += " -DPLATFORM_ANDROIDESDEFERRED=1"; } // which PCH file to include string PCHArguments = ""; if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { // Add the precompiled header file's path to the include path so Clang can find it. // This needs to be before the other include paths to ensure Clang uses it instead of the source header file. PCHArguments += string.Format(" -include \"{0}\"", InlineArchName(BasePCHName, Arch, GPUArchitecture)); } // Add include paths to the argument list (filtered by architecture) foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths) { if (IsDirectoryForArch(IncludePath, Arch)) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } } foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths) { if (IsDirectoryForArch(IncludePath, Arch)) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } } foreach (FileItem SourceFile in SourceFiles) { Action CompileAction = new Action(ActionType.Compile); string FileArguments = ""; bool bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C"; bool bDisableShadowWarning = false; // should we disable optimizations on this file? // @todo android - We wouldn't need this if we could disable optimizations per function (via pragma) bool bDisableOptimizations = false;// SourceFile.AbsolutePath.ToUpperInvariant().IndexOf("\\SLATE\\") != -1; if (bDisableOptimizations && CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug) { Log.TraceWarning("Disabling optimizations on {0}", SourceFile.AbsolutePath); } bDisableOptimizations = bDisableOptimizations || CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug; // Add C or C++ specific compiler arguments. if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { FileArguments += GetCompileArguments_PCH(bDisableOptimizations); } else if (bIsPlainCFile) { FileArguments += GetCompileArguments_C(bDisableOptimizations); // remove shadow variable warnings for NDK files if (SourceFile.AbsolutePath.Replace("\\", "/").StartsWith(NDKRoot)) { bDisableShadowWarning = true; } } else { FileArguments += GetCompileArguments_CPP(bDisableOptimizations); // only use PCH for .cpp files FileArguments += PCHArguments; } // Add the C++ source file and its included files to the prerequisite item list. AddPrerequisiteSourceFile(Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems); if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { // Add the precompiled header file to the produced item list. FileItem PrecompiledHeaderFile = FileItem.GetItemByFileReference( FileReference.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(InlineArchName(SourceFile.AbsolutePath, Arch, GPUArchitecture) + PCHExtension) ) ); CompileAction.ProducedItems.Add(PrecompiledHeaderFile); Result.PrecompiledHeaderFile = PrecompiledHeaderFile; // Add the parameters needed to compile the precompiled header file to the command-line. FileArguments += string.Format(" -o \"{0}\"", PrecompiledHeaderFile.AbsolutePath, false); } else { if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { CompileAction.bIsUsingPCH = true; FileItem ArchPrecompiledHeaderFile = FileItem.GetItemByPath(InlineArchName(BasePCHName, Arch, GPUArchitecture) + PCHExtension); CompileAction.PrerequisiteItems.Add(ArchPrecompiledHeaderFile); } var ObjectFileExtension = UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.Android).GetBinaryExtension(UEBuildBinaryType.Object); // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByFileReference( FileReference.Combine( CompileEnvironment.Config.OutputDirectory, InlineArchName(Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension, Arch, GPUArchitecture) ) ); CompileAction.ProducedItems.Add(ObjectFile); Result.ObjectFiles.Add(ObjectFile); FileArguments += string.Format(" -o \"{0}\"", ObjectFile.AbsolutePath, false); } // Add the source file path to the command-line. FileArguments += string.Format(" \"{0}\"", SourceFile.AbsolutePath); // Build a full argument list string AllArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments; AllArguments = ActionThread.ExpandEnvironmentVariables(AllArguments); AllArguments = AllArguments.Replace("\\", "/"); // Remove shadow warning for this file if requested if (bDisableShadowWarning) { int WarningIndex = AllArguments.IndexOf(" -Wshadow"); if (WarningIndex > 0) { AllArguments = AllArguments.Remove(WarningIndex, 9); } } // Create the response file FileReference ResponseFileName = CompileAction.ProducedItems[0].Reference + "_" + AllArguments.GetHashCode().ToString("X") + ".response"; string ResponseArgument = string.Format("@\"{0}\"", ResponseFile.Create(ResponseFileName, new List<string> { AllArguments }).FullName); CompileAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory.FullName; CompileAction.CommandPath = ClangPath; CompileAction.CommandArguments = ResponseArgument; CompileAction.StatusDescription = string.Format("{0} [{1}-{2}]", Path.GetFileName(SourceFile.AbsolutePath), Arch.Replace("-", ""), GPUArchitecture.Replace("-", "")); // VC++ always outputs the source file name being compiled, so we don't need to emit this ourselves CompileAction.bShouldOutputStatusDescription = true; // Don't farm out creation of pre-compiled headers as it is the critical path task. CompileAction.bCanExecuteRemotely = CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create || BuildConfiguration.bAllowRemotelyCompiledPCHs; } } } return Result; }
/** * Whether to use PCH files with the current target * * @return true if PCH files should be used, false otherwise */ public bool ShouldUsePCHs() { return(UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(Config.Target.Platform).ShouldUsePCHFiles(Config.Target.Platform, Config.Target.Configuration)); }
/// <summary> /// Given a set of C++ files, generates another set of C++ files that #include all the original /// files, the goal being to compile the same code in fewer translation units. /// The "unity" files are written to the CompileEnvironment's OutputDirectory. /// </summary> /// <param name="Target">The target we're building</param> /// <param name="CPPFiles">The C++ files to #include.</param> /// <param name="CompileEnvironment">The environment that is used to compile the C++ files.</param> /// <param name="BaseName">Base name to use for the Unity files</param> /// <returns>The "unity" C++ files.</returns> public static List <FileItem> GenerateUnityCPPs( UEToolChain ToolChain, UEBuildTarget Target, List <FileItem> CPPFiles, CPPEnvironment CompileEnvironment, string BaseName ) { List <FileItem> NewCPPFiles = new List <FileItem>(); UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Platform); // Figure out size of all input files combined. We use this to determine whether to use larger unity threshold or not. long TotalBytesInCPPFiles = CPPFiles.Sum(F => F.Info.Length); // We have an increased threshold for unity file size if, and only if, all files fit into the same unity file. This // is beneficial when dealing with PCH files. The default PCH creation limit is X unity files so if we generate < X // this could be fairly slow and we'd rather bump the limit a bit to group them all into the same unity file. // When enabled, UnrealBuildTool will try to determine source files that you are actively iteratively changing, and break those files // out of their unity blobs so that you can compile them as individual translation units, much faster than recompiling the entire // unity blob each time. bool bUseAdaptiveUnityBuild = BuildConfiguration.bUseAdaptiveUnityBuild && !BuildConfiguration.bStressTestUnity; // Optimization only makes sense if PCH files are enabled. bool bForceIntoSingleUnityFile = BuildConfiguration.bStressTestUnity || (TotalBytesInCPPFiles < BuildConfiguration.NumIncludedBytesPerUnityCPP * 2 && CompileEnvironment.ShouldUsePCHs()); // Build the list of unity files. List <FileCollection> AllUnityFiles; { // Sort the incoming file paths alphabetically, so there will be consistency in unity blobs across multiple machines. // Note that we're relying on this not only sorting files within each directory, but also the directories // themselves, so the whole list of file paths is the same across computers. List <FileItem> SortedCPPFiles = CPPFiles.GetRange(0, CPPFiles.Count); { // Case-insensitive file path compare, because you never know what is going on with local file systems Comparison <FileItem> FileItemComparer = (FileA, FileB) => { return(FileA.AbsolutePath.ToLowerInvariant().CompareTo(FileB.AbsolutePath.ToLowerInvariant())); }; SortedCPPFiles.Sort(FileItemComparer); } // Figure out whether we REALLY want to use adaptive unity for this module. If nearly every file in the module appears in the working // set, we'll just go ahead and let unity build do its thing. if (bUseAdaptiveUnityBuild) { int CandidateWorkingSetSourceFileCount = 0; int WorkingSetSourceFileCount = 0; foreach (FileItem CPPFile in SortedCPPFiles) { // Don't include writable source files into unity blobs if (!CPPFile.Reference.IsUnderDirectory(Target.EngineIntermediateDirectory) && !CPPFile.Reference.IsUnderDirectory(Target.ProjectIntermediateDirectory)) { ++CandidateWorkingSetSourceFileCount; if (UnrealBuildTool.ShouldSourceFileBePartOfWorkingSet(CPPFile.AbsolutePath)) { ++WorkingSetSourceFileCount; // Mark this file as part of the working set. This will be saved into the UBT Makefile so that // the assembler can automatically invalidate the Makefile when the working set changes (allowing this // code to run again, to build up new unity blobs.) SourceFileWorkingSet.Add(CPPFile); } } } if (WorkingSetSourceFileCount >= CandidateWorkingSetSourceFileCount) { // Every single file in the module appears in the working set, so don't bother using adaptive unity for this // module. Otherwise it would make full builds really slow. bUseAdaptiveUnityBuild = false; } } UnityFileBuilder CPPUnityFileBuilder = new UnityFileBuilder(bForceIntoSingleUnityFile ? -1 : BuildConfiguration.NumIncludedBytesPerUnityCPP); StringBuilder AdaptiveUnityBuildInfoString = new StringBuilder(); foreach (FileItem CPPFile in SortedCPPFiles) { if (!bForceIntoSingleUnityFile && CPPFile.AbsolutePath.IndexOf(".GeneratedWrapper.", StringComparison.InvariantCultureIgnoreCase) != -1) { NewCPPFiles.Add(CPPFile); } // When adaptive unity is enabled, go ahead and exclude any source files that we're actively working with if (bUseAdaptiveUnityBuild && SourceFileWorkingSet.Contains(CPPFile)) { // Just compile this file normally, not as part of the unity blob NewCPPFiles.Add(CPPFile); // Let the unity file builder know about the file, so that we can retain the existing size of the unity blobs. // This won't actually make the source file part of the unity blob, but it will keep track of how big the // file is so that other existing unity blobs from the same module won't be invalidated. This prevents much // longer compile times the first time you build after your working file set changes. CPPUnityFileBuilder.AddVirtualFile(CPPFile); string CPPFileName = Path.GetFileName(CPPFile.AbsolutePath); if (AdaptiveUnityBuildInfoString.Length == 0) { AdaptiveUnityBuildInfoString.Append(String.Format("[Adaptive unity build] Excluded from {0} unity file: {1}", BaseName, CPPFileName)); } else { AdaptiveUnityBuildInfoString.Append(", " + CPPFileName); } } else { // If adaptive unity build is enabled for this module, add this source file to the set that will invalidate the makefile if (bUseAdaptiveUnityBuild) { CandidateSourceFilesForWorkingSet.Add(CPPFile); } // Compile this file as part of the unity blob CPPUnityFileBuilder.AddFile(CPPFile); // Now that the CPPFile is part of this unity file, we will no longer need to treat it like a root level prerequisite for our // dependency cache, as it is now an "indirect include" from the unity file. We'll clear out the compile environment // attached to this file. This prevents us from having to cache all of the indirect includes from these files inside our // dependency cache, which speeds up iterative builds a lot! CPPFile.CachedCPPIncludeInfo = null; } } if (AdaptiveUnityBuildInfoString.Length > 0) { Log.TraceInformation(AdaptiveUnityBuildInfoString.ToString()); } AllUnityFiles = CPPUnityFileBuilder.GetUnityFiles(); } // Create a set of CPP files that combine smaller CPP files into larger compilation units, along with the corresponding // actions to compile them. int CurrentUnityFileCount = 0; foreach (FileCollection UnityFile in AllUnityFiles) { ++CurrentUnityFileCount; StringWriter OutputUnityCPPWriter = new StringWriter(); StringWriter OutputUnityCPPWriterExtra = null; // add an extra file for UBT to get the #include dependencies from if (BuildPlatform.RequiresExtraUnityCPPWriter() == true) { OutputUnityCPPWriterExtra = new StringWriter(); } OutputUnityCPPWriter.WriteLine("// This file is automatically generated at compile-time to include some subset of the user-created cpp files."); // Add source files to the unity file foreach (FileItem CPPFile in UnityFile.Files) { OutputUnityCPPWriter.WriteLine("#include \"{0}\"", ToolChain.ConvertPath(CPPFile.AbsolutePath)); if (OutputUnityCPPWriterExtra != null) { OutputUnityCPPWriterExtra.WriteLine("#include \"{0}\"", CPPFile.AbsolutePath); } } // Determine unity file path name string UnityCPPFileName; if (AllUnityFiles.Count > 1) { UnityCPPFileName = string.Format("{0}{1}.{2}_of_{3}.cpp", ModulePrefix, BaseName, CurrentUnityFileCount, AllUnityFiles.Count); } else { UnityCPPFileName = string.Format("{0}{1}.cpp", ModulePrefix, BaseName); } FileReference UnityCPPFilePath = FileReference.Combine(CompileEnvironment.Config.OutputDirectory, UnityCPPFileName); // Write the unity file to the intermediate folder. FileItem UnityCPPFile = FileItem.CreateIntermediateTextFile(UnityCPPFilePath, OutputUnityCPPWriter.ToString()); if (OutputUnityCPPWriterExtra != null) { FileItem.CreateIntermediateTextFile(UnityCPPFilePath + ".ex", OutputUnityCPPWriterExtra.ToString()); } UnityCPPFile.RelativeCost = UnityFile.TotalLength; NewCPPFiles.Add(UnityCPPFile); // Cache information about the unity .cpp dependencies // @todo ubtmake urgent: Fails when building remotely for Mac because unity .cpp has an include for a PCH on the REMOTE machine UEBuildModuleCPP.CachePCHUsageForModuleSourceFile(CompileEnvironment, UnityCPPFile); } return(NewCPPFiles); }
public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List <FileItem> SourceFiles, string ModuleName) { if (Arches.Length == 0) { throw new BuildException("At least one architecture (armv7, x86, etc) needs to be selected in the project settings to build"); } if (!bHasPrintedApiLevel) { Console.WriteLine("Compiling with NDK API '{0}'", GetNdkApiLevel()); bHasPrintedApiLevel = true; } string BaseArguments = ""; if (CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create) { BaseArguments += " -Werror"; } // Directly added NDK files for NDK extensions if (!UnrealBuildTool.RunningRocket()) { ConditionallyAddNDKSourceFiles(SourceFiles); } // Add preprocessor definitions to the argument list. foreach (string Definition in CompileEnvironment.Config.Definitions) { BaseArguments += string.Format(" -D \"{0}\"", Definition); } var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); string BasePCHName = ""; var PCHExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Android].GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader); if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { BasePCHName = RemoveArchName(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath).Replace(PCHExtension, ""); } // Create a compile action for each source file. CPPOutput Result = new CPPOutput(); foreach (string Arch in Arches) { foreach (string GPUArchitecture in GPUArchitectures) { // which toolchain to use string Arguments = GetCLArguments_Global(CompileEnvironment, Arch) + BaseArguments; if (GPUArchitecture == "-gl4") { Arguments += " -DPLATFORM_ANDROIDGL4=1"; } else if (GPUArchitecture == "-es31") { Arguments += " -DPLATFORM_ANDROIDES31=1"; } // which PCH file to include string PCHArguments = ""; if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { // Add the precompiled header file's path to the include path so Clang can find it. // This needs to be before the other include paths to ensure Clang uses it instead of the source header file. PCHArguments += string.Format(" -include \"{0}\"", InlineArchName(BasePCHName, Arch, GPUArchitecture)); } // Add include paths to the argument list (filtered by architecture) foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths) { if (IsDirectoryForArch(IncludePath, Arch)) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } } foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths) { if (IsDirectoryForArch(IncludePath, Arch)) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } } foreach (FileItem SourceFile in SourceFiles) { Action CompileAction = new Action(ActionType.Compile); string FileArguments = ""; bool bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C"; // should we disable optimizations on this file? // @todo android - We wouldn't need this if we could disable optimizations per function (via pragma) bool bDisableOptimizations = false; // SourceFile.AbsolutePath.ToUpperInvariant().IndexOf("\\SLATE\\") != -1; if (bDisableOptimizations && CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug) { Log.TraceWarning("Disabling optimizations on {0}", SourceFile.AbsolutePath); } bDisableOptimizations = bDisableOptimizations || CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug; // Add C or C++ specific compiler arguments. if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { FileArguments += GetCompileArguments_PCH(bDisableOptimizations); } else if (bIsPlainCFile) { FileArguments += GetCompileArguments_C(bDisableOptimizations); } else { FileArguments += GetCompileArguments_CPP(bDisableOptimizations); // only use PCH for .cpp files FileArguments += PCHArguments; } // Add the C++ source file and its included files to the prerequisite item list. AddPrerequisiteSourceFile(Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems); if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { // Add the precompiled header file to the produced item list. FileItem PrecompiledHeaderFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(InlineArchName(SourceFile.AbsolutePath, Arch, GPUArchitecture) + PCHExtension) ) ); CompileAction.ProducedItems.Add(PrecompiledHeaderFile); Result.PrecompiledHeaderFile = PrecompiledHeaderFile; // Add the parameters needed to compile the precompiled header file to the command-line. FileArguments += string.Format(" -o \"{0}\"", PrecompiledHeaderFile.AbsolutePath, false); } else { if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { CompileAction.bIsUsingPCH = true; FileItem ArchPrecompiledHeaderFile = FileItem.GetItemByPath(InlineArchName(BasePCHName, Arch, GPUArchitecture) + PCHExtension); CompileAction.PrerequisiteItems.Add(ArchPrecompiledHeaderFile); } var ObjectFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Android].GetBinaryExtension(UEBuildBinaryType.Object); // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByPath( InlineArchName(Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension), Arch, GPUArchitecture ) ); CompileAction.ProducedItems.Add(ObjectFile); Result.ObjectFiles.Add(ObjectFile); FileArguments += string.Format(" -o \"{0}\"", ObjectFile.AbsolutePath, false); } // Add the source file path to the command-line. FileArguments += string.Format(" \"{0}\"", SourceFile.AbsolutePath); // Build a full argument list string AllArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments; AllArguments = ActionThread.ExpandEnvironmentVariables(AllArguments); AllArguments = AllArguments.Replace("\\", "/"); // Create the response file string ResponseFileName = CompileAction.ProducedItems[0].AbsolutePath + ".response"; string ResponseArgument = string.Format("@\"{0}\"", ResponseFile.Create(ResponseFileName, new List <string> { AllArguments })); CompileAction.WorkingDirectory = Path.GetFullPath("."); CompileAction.CommandPath = ClangPath; CompileAction.CommandArguments = ResponseArgument; CompileAction.StatusDescription = string.Format("{0} [{1}-{2}]", Path.GetFileName(SourceFile.AbsolutePath), Arch.Replace("-", ""), GPUArchitecture.Replace("-", "")); CompileAction.OutputEventHandler = new DataReceivedEventHandler(CompileOutputReceivedDataEventHandler); // VC++ always outputs the source file name being compiled, so we don't need to emit this ourselves CompileAction.bShouldOutputStatusDescription = true; // Don't farm out creation of pre-compiled headers as it is the critical path task. CompileAction.bCanExecuteRemotely = CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create || BuildConfiguration.bAllowRemotelyCompiledPCHs; } } } return(Result); }
public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List <FileItem> SourceFiles, string ModuleName) { string Arguments = GetCLArguments_Global(CompileEnvironment); // Add include paths to the argument list. foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths) { Arguments += string.Format(" /I \"{0}\"", IncludePath); } foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths) { Arguments += string.Format(" /I \"{0}\"", IncludePath); } if ((CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled) || (WinRTPlatform.ShouldCompileWinRT() == true)) { // Add .NET framework assembly paths. This is needed so that C++/CLI projects // can reference assemblies with #using, without having to hard code a path in the // .cpp file to the assembly's location. foreach (string AssemblyPath in CompileEnvironment.Config.SystemDotNetAssemblyPaths) { Arguments += string.Format(" /AI \"{0}\"", AssemblyPath); } // Add explicit .NET framework assembly references foreach (string AssemblyName in CompileEnvironment.Config.FrameworkAssemblyDependencies) { Arguments += string.Format(" /FU \"{0}\"", AssemblyName); } // Add private assembly references foreach (PrivateAssemblyInfo CurAssemblyInfo in CompileEnvironment.PrivateAssemblyDependencies) { Arguments += string.Format(" /FU \"{0}\"", CurAssemblyInfo.FileItem.AbsolutePath); } } else { foreach (string AssemblyPath in CompileEnvironment.Config.SystemDotNetAssemblyPaths) { Arguments += string.Format(" /AI \"{0}\"", AssemblyPath); } // Add explicit .NET framework assembly references foreach (string AssemblyName in CompileEnvironment.Config.FrameworkAssemblyDependencies) { Arguments += string.Format(" /FU \"{0}\"", AssemblyName); } } // Add preprocessor definitions to the argument list. foreach (string Definition in CompileEnvironment.Config.Definitions) { Arguments += string.Format(" /D \"{0}\"", Definition); } // Log.TraceInformation("Compile Arguments for {0}:", ModuleName); // Log.TraceInformation(Arguments); var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); // Create a compile action for each source file. CPPOutput Result = new CPPOutput(); foreach (FileItem SourceFile in SourceFiles) { Action CompileAction = new Action(ActionType.Compile); string FileArguments = ""; bool bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C"; // Add the C++ source file and its included files to the prerequisite item list. AddPrerequisiteSourceFile(Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems); // If this is a CLR file then make sure our dependent assemblies are added as prerequisites if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled) { foreach (PrivateAssemblyInfo CurPrivateAssemblyDependency in CompileEnvironment.PrivateAssemblyDependencies) { CompileAction.PrerequisiteItems.Add(CurPrivateAssemblyDependency.FileItem); } } if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { // Generate a CPP File that just includes the precompiled header. string PCHCPPFilename = "PCH." + Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename) + ".cpp"; string PCHCPPPath = Path.Combine(CompileEnvironment.Config.OutputDirectory, PCHCPPFilename); FileItem PCHCPPFile = FileItem.CreateIntermediateTextFile( PCHCPPPath, string.Format("#include \"{0}\"\r\n", CompileEnvironment.Config.PrecompiledHeaderIncludeFilename) ); // Make sure the original source directory the PCH header file existed in is added as an include // path -- it might be a private PCH header and we need to make sure that its found! string OriginalPCHHeaderDirectory = Path.GetDirectoryName(SourceFile.AbsolutePath); FileArguments += string.Format(" /I \"{0}\"", OriginalPCHHeaderDirectory); var PCHExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.WinRT].GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader); // Add the precompiled header file to the produced items list. FileItem PrecompiledHeaderFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + PCHExtension ) ); CompileAction.ProducedItems.Add(PrecompiledHeaderFile); Result.PrecompiledHeaderFile = PrecompiledHeaderFile; // Add the parameters needed to compile the precompiled header file to the command-line. FileArguments += string.Format(" /Yc\"{0}\"", CompileEnvironment.Config.PrecompiledHeaderIncludeFilename); FileArguments += string.Format(" /Fp\"{0}\"", PrecompiledHeaderFile.AbsolutePath); FileArguments += string.Format(" \"{0}\"", PCHCPPFile.AbsolutePath); // If we're creating a PCH that will be used to compile source files for a library, we need // the compiled modules to retain a reference to PCH's module, so that debugging information // will be included in the library. This is also required to avoid linker warning "LNK4206" // when linking an application that uses this library. if (CompileEnvironment.Config.bIsBuildingLibrary) { // NOTE: The symbol name we use here is arbitrary, and all that matters is that it is // unique per PCH module used in our library string FakeUniquePCHSymbolName = Path.GetFileNameWithoutExtension(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename); FileArguments += string.Format(" /Yl{0}", FakeUniquePCHSymbolName); } CompileAction.StatusDescription = PCHCPPFilename; } else { if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { CompileAction.bIsUsingPCH = true; CompileAction.PrerequisiteItems.Add(CompileEnvironment.PrecompiledHeaderFile); FileArguments += string.Format(" /Yu\"{0}\"", CompileEnvironment.Config.PCHHeaderNameInCode); FileArguments += string.Format(" /Fp\"{0}\"", CompileEnvironment.PrecompiledHeaderFile.AbsolutePath); // Is it unsafe to always force inclusion? Clang is doing it, and .generated.cpp files // won't work otherwise, because they're not located in the context of the module, // so they can't access the module's PCH without an absolute path. //if (CompileEnvironment.Config.bForceIncludePrecompiledHeader) { // Force include the precompiled header file. This is needed because we may have selected a // precompiled header that is different than the first direct include in the C++ source file, but // we still need to make sure that our precompiled header is the first thing included! FileArguments += string.Format(" /FI\"{0}\"", CompileEnvironment.Config.PCHHeaderNameInCode); } } // Add the source file path to the command-line. FileArguments += string.Format(" \"{0}\"", SourceFile.AbsolutePath); CompileAction.StatusDescription = Path.GetFileName(SourceFile.AbsolutePath); } var ObjectFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.WinRT].GetBinaryExtension(UEBuildBinaryType.Object); // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension ) ); CompileAction.ProducedItems.Add(ObjectFile); Result.ObjectFiles.Add(ObjectFile); FileArguments += string.Format(" /Fo\"{0}\"", ObjectFile.AbsolutePath); // create PDBs per-file when not using debug info, otherwise it will try to share a PDB file, which causes // PCH creation to be serial rather than parallel (when debug info is disabled) // See https://udn.epicgames.com/lists/showpost.php?id=50619&list=unprog3 if (!CompileEnvironment.Config.bCreateDebugInfo || BuildConfiguration.bUsePDBFiles) { string PDBFileName; bool bActionProducesPDB = false; // All files using the same PCH are required to share a PDB. if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { PDBFileName = Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename); } // Files creating a PCH or ungrouped C++ files use a PDB per file. else if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create || !bIsPlainCFile) { PDBFileName = Path.GetFileName(SourceFile.AbsolutePath); bActionProducesPDB = true; } // Group all plain C files that doesn't use PCH into the same PDB else { PDBFileName = "MiscPlainC"; } // Specify the PDB file that the compiler should write to. FileItem PDBFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, PDBFileName + ".pdb" ) ); FileArguments += string.Format(" /Fd\"{0}\"", PDBFile.AbsolutePath); // Only use the PDB as an output file if we want PDBs and this particular action is // the one that produces the PDB (as opposed to no debug info, where the above code // is needed, but not the output PDB, or when multiple files share a single PDB, so // only the action that generates it should count it as output directly) if (BuildConfiguration.bUsePDBFiles && bActionProducesPDB) { CompileAction.ProducedItems.Add(PDBFile); Result.DebugDataFiles.Add(PDBFile); } } // Add C or C++ specific compiler arguments. if (bIsPlainCFile) { FileArguments += GetCLArguments_C(); } else { FileArguments += GetCLArguments_CPP(CompileEnvironment); } CompileAction.WorkingDirectory = Path.GetFullPath("."); CompileAction.CommandPath = GetVCToolPath(CompileEnvironment.Config.Target.Platform, CompileEnvironment.Config.Target.Configuration, "cl"); CompileAction.CommandArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments; CompileAction.StatusDescription = string.Format("{0}", Path.GetFileName(SourceFile.AbsolutePath)); // Don't farm out creation of precomputed headers as it is the critical path task. CompileAction.bCanExecuteRemotely = CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create; // @todo: XGE has problems remote compiling C++/CLI files that use .NET Framework 4.0 if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled) { CompileAction.bCanExecuteRemotely = false; } } return(Result); }
public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List <FileItem> SourceFiles, string ModuleName) { string Arguments = GetCLArguments_Global(CompileEnvironment); string PCHArguments = ""; if (CompileEnvironment.Config.bIsBuildingDLL) { Arguments += " -fPIC"; } if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { // Add the precompiled header file's path to the include path so Clang can find it. // This needs to be before the other include paths to ensure Clang uses it instead of the source header file. var PrecompiledFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Linux].GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader); PCHArguments += string.Format(" -include \"{0}\"", CompileEnvironment.PrecompiledHeaderFile.AbsolutePath.Replace(PrecompiledFileExtension, "")); } // Add include paths to the argument list. foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } // Add preprocessor definitions to the argument list. foreach (string Definition in CompileEnvironment.Config.Definitions) { Arguments += string.Format(" -D \"{0}\"", Definition); } var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); // Create a compile action for each source file. CPPOutput Result = new CPPOutput(); foreach (FileItem SourceFile in SourceFiles) { Action CompileAction = new Action(ActionType.Compile); string FileArguments = ""; string Extension = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant(); // Add C or C++ specific compiler arguments. if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { FileArguments += GetCompileArguments_PCH(); } else if (Extension == ".C") { // Compile the file as C code. FileArguments += GetCompileArguments_C(); } else if (Extension == ".CC") { // Compile the file as C++ code. FileArguments += GetCompileArguments_CPP(); } else if (Extension == ".MM") { // Compile the file as Objective-C++ code. FileArguments += GetCompileArguments_MM(); } else if (Extension == ".M") { // Compile the file as Objective-C code. FileArguments += GetCompileArguments_M(); } else { FileArguments += GetCompileArguments_CPP(); // only use PCH for .cpp files FileArguments += PCHArguments; } // Add the C++ source file and its included files to the prerequisite item list. AddPrerequisiteSourceFile(Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems); if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { var PrecompiledFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Linux].GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader); // Add the precompiled header file to the produced item list. FileItem PrecompiledHeaderFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + PrecompiledFileExtension ) ); CompileAction.ProducedItems.Add(PrecompiledHeaderFile); Result.PrecompiledHeaderFile = PrecompiledHeaderFile; // Add the parameters needed to compile the precompiled header file to the command-line. FileArguments += string.Format(" -o \"{0}\"", PrecompiledHeaderFile.AbsolutePath, false); } else { if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { CompileAction.bIsUsingPCH = true; CompileAction.PrerequisiteItems.Add(CompileEnvironment.PrecompiledHeaderFile); } var ObjectFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Linux].GetBinaryExtension(UEBuildBinaryType.Object); // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension ) ); CompileAction.ProducedItems.Add(ObjectFile); Result.ObjectFiles.Add(ObjectFile); FileArguments += string.Format(" -o \"{0}\"", ObjectFile.AbsolutePath, false); } // Add the source file path to the command-line. FileArguments += string.Format(" \"{0}\"", SourceFile.AbsolutePath); CompileAction.WorkingDirectory = Path.GetFullPath("."); if (!UsingClang()) { CompileAction.CommandPath = GCCPath; } else { CompileAction.CommandPath = ClangPath; } CompileAction.CommandArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments; CompileAction.CommandDescription = "Compile"; CompileAction.StatusDescription = Path.GetFileName(SourceFile.AbsolutePath); CompileAction.bIsGCCCompiler = true; // Don't farm out creation of pre-compiled headers as it is the critical path task. CompileAction.bCanExecuteRemotely = CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create || BuildConfiguration.bAllowRemotelyCompiledPCHs; CompileAction.OutputEventHandler = new DataReceivedEventHandler(CompileOutputReceivedDataEventHandler); } return(Result); }
public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List <FileItem> SourceFiles, string ModuleName) { if (CompileEnvironment.Config.Target.Architecture == "-win32") { return(base.CompileCPPFiles(Target, CompileEnvironment, SourceFiles, ModuleName)); } string Arguments = GetCLArguments_Global(CompileEnvironment); CPPOutput Result = new CPPOutput(); // Add include paths to the argument list. foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } // Add preprocessor definitions to the argument list. foreach (string Definition in CompileEnvironment.Config.Definitions) { Arguments += string.Format(" -D{0}", Definition); } var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); foreach (FileItem SourceFile in SourceFiles) { Action CompileAction = new Action(ActionType.Compile); bool bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C"; // Add the C++ source file and its included files to the prerequisite item list. AddPrerequisiteSourceFile(Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems); // Add the source file path to the command-line. string FileArguments = string.Format(" \"{0}\"", SourceFile.AbsolutePath); var ObjectFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.HTML5].GetBinaryExtension(UEBuildBinaryType.Object); // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension ) ); CompileAction.ProducedItems.Add(ObjectFile); FileArguments += string.Format(" -o \"{0}\"", ObjectFile.AbsolutePath); // Add C or C++ specific compiler arguments. if (bIsPlainCFile) { FileArguments += GetCLArguments_C(CompileEnvironment.Config.Target.Architecture); } else { FileArguments += GetCLArguments_CPP(CompileEnvironment); } CompileAction.WorkingDirectory = Path.GetFullPath("."); CompileAction.CommandPath = PythonPath; CompileAction.CommandArguments = EMCCPath + " " + Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments; System.Console.WriteLine(CompileAction.CommandArguments); CompileAction.StatusDescription = Path.GetFileName(SourceFile.AbsolutePath); CompileAction.OutputEventHandler = new DataReceivedEventHandler(CompileOutputReceivedDataEventHandler); // Don't farm out creation of precomputed headers as it is the critical path task. CompileAction.bCanExecuteRemotely = CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create; // this is the final output of the compile step (a .abc file) Result.ObjectFiles.Add(ObjectFile); // VC++ always outputs the source file name being compiled, so we don't need to emit this ourselves CompileAction.bShouldOutputStatusDescription = true; // Don't farm out creation of precompiled headers as it is the critical path task. CompileAction.bCanExecuteRemotely = CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create || BuildConfiguration.bAllowRemotelyCompiledPCHs; } return(Result); }
/// <summary> /// Given a set of C++ files, generates another set of C++ files that #include all the original /// files, the goal being to compile the same code in fewer translation units. /// The "unity" files are written to the CompileEnvironment's OutputDirectory. /// </summary> /// <param name="Target">The target we're building</param> /// <param name="CPPFiles">The C++ files to #include.</param> /// <param name="CompileEnvironment">The environment that is used to compile the C++ files.</param> /// <param name="WorkingSet">Interface to query files which belong to the working set</param> /// <param name="BaseName">Base name to use for the Unity files</param> /// <param name="IntermediateDirectory">Intermediate directory for unity cpp files</param> /// <param name="Makefile">The makefile being built</param> /// <param name="SourceFileToUnityFile">Receives a mapping of source file to unity file</param> /// <returns>The "unity" C++ files.</returns> public static List <FileItem> GenerateUnityCPPs( ReadOnlyTargetRules Target, List <FileItem> CPPFiles, CppCompileEnvironment CompileEnvironment, ISourceFileWorkingSet WorkingSet, string BaseName, DirectoryReference IntermediateDirectory, TargetMakefile Makefile, Dictionary <FileItem, FileItem> SourceFileToUnityFile ) { List <FileItem> NewCPPFiles = new List <FileItem>(); UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Platform); // Figure out size of all input files combined. We use this to determine whether to use larger unity threshold or not. long TotalBytesInCPPFiles = CPPFiles.Sum(F => F.Length); // We have an increased threshold for unity file size if, and only if, all files fit into the same unity file. This // is beneficial when dealing with PCH files. The default PCH creation limit is X unity files so if we generate < X // this could be fairly slow and we'd rather bump the limit a bit to group them all into the same unity file. // When enabled, UnrealBuildTool will try to determine source files that you are actively iteratively changing, and break those files // out of their unity blobs so that you can compile them as individual translation units, much faster than recompiling the entire // unity blob each time. bool bUseAdaptiveUnityBuild = Target.bUseAdaptiveUnityBuild && !Target.bStressTestUnity; // Optimization only makes sense if PCH files are enabled. bool bForceIntoSingleUnityFile = Target.bStressTestUnity || (TotalBytesInCPPFiles < Target.NumIncludedBytesPerUnityCPP * 2 && Target.bUsePCHFiles); // Build the list of unity files. List <FileCollection> AllUnityFiles; { // Sort the incoming file paths alphabetically, so there will be consistency in unity blobs across multiple machines. // Note that we're relying on this not only sorting files within each directory, but also the directories // themselves, so the whole list of file paths is the same across computers. List <FileItem> SortedCPPFiles = CPPFiles.GetRange(0, CPPFiles.Count); { // Case-insensitive file path compare, because you never know what is going on with local file systems Comparison <FileItem> FileItemComparer = (FileA, FileB) => { return(FileA.AbsolutePath.ToLowerInvariant().CompareTo(FileB.AbsolutePath.ToLowerInvariant())); }; SortedCPPFiles.Sort(FileItemComparer); } // Figure out whether we REALLY want to use adaptive unity for this module. If nearly every file in the module appears in the working // set, we'll just go ahead and let unity build do its thing. if (bUseAdaptiveUnityBuild) { int CandidateWorkingSetSourceFileCount = 0; int WorkingSetSourceFileCount = 0; foreach (FileItem CPPFile in SortedCPPFiles) { ++CandidateWorkingSetSourceFileCount; // Don't include writable source files into unity blobs if (WorkingSet.Contains(CPPFile)) { ++WorkingSetSourceFileCount; // Mark this file as part of the working set. This will be saved into the UBT Makefile so that // the assembler can automatically invalidate the Makefile when the working set changes (allowing this // code to run again, to build up new unity blobs.) Makefile.WorkingSet.Add(CPPFile); } } if (WorkingSetSourceFileCount >= CandidateWorkingSetSourceFileCount) { // Every single file in the module appears in the working set, so don't bother using adaptive unity for this // module. Otherwise it would make full builds really slow. bUseAdaptiveUnityBuild = false; } } UnityFileBuilder CPPUnityFileBuilder = new UnityFileBuilder(bForceIntoSingleUnityFile ? -1 : Target.NumIncludedBytesPerUnityCPP); StringBuilder AdaptiveUnityBuildInfoString = new StringBuilder(); foreach (FileItem CPPFile in SortedCPPFiles) { if (!bForceIntoSingleUnityFile && CPPFile.AbsolutePath.IndexOf(".GeneratedWrapper.", StringComparison.InvariantCultureIgnoreCase) != -1) { NewCPPFiles.Add(CPPFile); } // When adaptive unity is enabled, go ahead and exclude any source files that we're actively working with if (bUseAdaptiveUnityBuild && Makefile.WorkingSet.Contains(CPPFile)) { // Just compile this file normally, not as part of the unity blob NewCPPFiles.Add(CPPFile); // Let the unity file builder know about the file, so that we can retain the existing size of the unity blobs. // This won't actually make the source file part of the unity blob, but it will keep track of how big the // file is so that other existing unity blobs from the same module won't be invalidated. This prevents much // longer compile times the first time you build after your working file set changes. CPPUnityFileBuilder.AddVirtualFile(CPPFile); string CPPFileName = Path.GetFileName(CPPFile.AbsolutePath); if (AdaptiveUnityBuildInfoString.Length == 0) { AdaptiveUnityBuildInfoString.Append(String.Format("[Adaptive unity build] Excluded from {0} unity file: {1}", BaseName, CPPFileName)); } else { AdaptiveUnityBuildInfoString.Append(", " + CPPFileName); } } else { // If adaptive unity build is enabled for this module, add this source file to the set that will invalidate the makefile if (bUseAdaptiveUnityBuild) { Makefile.CandidatesForWorkingSet.Add(CPPFile); } // Compile this file as part of the unity blob CPPUnityFileBuilder.AddFile(CPPFile); } } if (AdaptiveUnityBuildInfoString.Length > 0) { if (PrintedSettingsForTargets.Add(Target.Name)) { if (Target.bAdaptiveUnityCreatesDedicatedPCH) { Log.TraceInformation("[Adaptive unity build] Creating dedicated PCH for each excluded file. Set bAdaptiveUnityCreatesDedicatedPCH to false in BuildConfiguration.xml to change this behavior."); } else if (Target.bAdaptiveUnityDisablesPCH) { Log.TraceInformation("[Adaptive unity build] Disabling PCH for excluded files. Set bAdaptiveUnityDisablesPCH to false in BuildConfiguration.xml to change this behavior."); } if (Target.bAdaptiveUnityDisablesOptimizations) { Log.TraceInformation("[Adaptive unity build] Disabling optimizations for excluded files. Set bAdaptiveUnityDisablesOptimizations to false in BuildConfiguration.xml to change this behavior."); } if (Target.bAdaptiveUnityEnablesEditAndContinue) { Log.TraceInformation("[Adaptive unity build] Enabling Edit & Continue for excluded files. Set bAdaptiveUnityEnablesEditAndContinue to false in BuildConfiguration.xml to change this behavior."); } } Log.TraceInformation(AdaptiveUnityBuildInfoString.ToString()); } AllUnityFiles = CPPUnityFileBuilder.GetUnityFiles(); } // Create a set of CPP files that combine smaller CPP files into larger compilation units, along with the corresponding // actions to compile them. int CurrentUnityFileCount = 0; foreach (FileCollection UnityFile in AllUnityFiles) { ++CurrentUnityFileCount; StringWriter OutputUnityCPPWriter = new StringWriter(); OutputUnityCPPWriter.WriteLine("// This file is automatically generated at compile-time to include some subset of the user-created cpp files."); // Add source files to the unity file foreach (FileItem CPPFile in UnityFile.Files) { OutputUnityCPPWriter.WriteLine("#include \"{0}\"", CPPFile.AbsolutePath); } // Determine unity file path name string UnityCPPFileName; if (AllUnityFiles.Count > 1) { UnityCPPFileName = string.Format("{0}{1}.{2}_of_{3}.cpp", ModulePrefix, BaseName, CurrentUnityFileCount, AllUnityFiles.Count); } else { UnityCPPFileName = string.Format("{0}{1}.cpp", ModulePrefix, BaseName); } FileReference UnityCPPFilePath = FileReference.Combine(IntermediateDirectory, UnityCPPFileName); // Write the unity file to the intermediate folder. FileItem UnityCPPFile = FileItem.CreateIntermediateTextFile(UnityCPPFilePath, OutputUnityCPPWriter.ToString()); NewCPPFiles.Add(UnityCPPFile); // Store the mapping of source files to unity files in the makefile foreach (FileItem SourceFile in UnityFile.Files) { SourceFileToUnityFile[SourceFile] = UnityCPPFile; } foreach (FileItem SourceFile in UnityFile.VirtualFiles) { SourceFileToUnityFile[SourceFile] = UnityCPPFile; } } return(NewCPPFiles); }