public override CPPOutput CompileCPPFiles(CPPEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName) { if (CompileEnvironment.Config.TargetArchitecture == "-win32") { return base.CompileCPPFiles(CompileEnvironment, SourceFiles, ModuleName); } string Arguments = GetCLArguments_Global(CompileEnvironment); string BaseSDKPath = Environment.GetEnvironmentVariable("EMSCRIPTEN"); CPPOutput Result = new CPPOutput(); // Add include paths to the argument list. foreach (string IncludePath in CompileEnvironment.Config.IncludePaths) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } foreach (string IncludePath in CompileEnvironment.Config.SystemIncludePaths) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } if ( ModuleName == "Launch" ) Arguments += string.Format(" -I\"{0}\"", BaseSDKPath + "/system/lib/libcxxabi/include" ); // Add preprocessor definitions to the argument list. foreach (string Definition in CompileEnvironment.Config.Definitions) { Arguments += string.Format(" -D{0}", Definition); } // Create a compile action for each source file. if (ModuleName == "Launch") SourceFiles.Add(FileItem.GetItemByPath(BaseSDKPath + "/system/lib/libcxxabi/src/cxa_demangle.cpp")); 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. CompileAction.PrerequisiteItems.Add(SourceFile); foreach (FileItem IncludedFile in CompileEnvironment.GetIncludeDependencies(SourceFile)) { CompileAction.PrerequisiteItems.Add(IncludedFile); } // Add the source file path to the command-line. string bfastlinkstring = bEnableFastIteration ? "" : " -c "; string FileArguments = string.Format(bfastlinkstring + " \"{0}\"", SourceFile.AbsolutePath); // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + (bEnableFastIteration ? ".js" : ".o") ) ); 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.TargetArchitecture); } else { FileArguments += GetCLArguments_CPP(CompileEnvironment); } CompileAction.WorkingDirectory = Path.GetFullPath("."); CompileAction.CommandPath = PythonPath; string fastlinkString = SourceFile.Info.FullName.Contains("Launch") ? " -s MAIN_MODULE=1 " : "-s SIDE_MODULE=1"; CompileAction.CommandArguments = EMCCPath + Arguments + (bEnableFastIteration ? fastlinkString : "" )+ FileArguments + CompileEnvironment.Config.AdditionalArguments; System.Console.WriteLine(CompileAction.CommandArguments); CompileAction.StatusDescription = Path.GetFileName(SourceFile.AbsolutePath); CompileAction.StatusDetailedDescription = SourceFile.Description; 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; }
public override CPPOutput CompileCPPFiles(CPPEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName) { string Arguments = GetCompileArguments_Global(CompileEnvironment); string PCHArguments = ""; if (CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create) { Arguments += " -Werror"; } if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { // Add the precompiled header file's path to the include path so GCC can find it. // This needs to be before the other include paths to ensure GCC uses it instead of the source header file. PCHArguments += string.Format(" -include \"{0}\"", CompileEnvironment.PrecompiledHeaderFile.AbsolutePath.Replace(".gch", "")); } // Add include paths to the argument list. List<string> AllIncludes = CompileEnvironment.Config.IncludePaths; AllIncludes.AddRange(CompileEnvironment.Config.SystemIncludePaths); foreach (string IncludePath in AllIncludes) { Arguments += string.Format(" -I\"{0}\"", ConvertPath(Path.GetFullPath(IncludePath))); if (ExternalExecution.GetRuntimePlatform() != UnrealTargetPlatform.Mac) { // sync any third party headers we may need if (IncludePath.Contains("ThirdParty")) { string[] FileList = Directory.GetFiles(IncludePath, "*.h", SearchOption.AllDirectories); foreach (string File in FileList) { FileItem ExternalDependency = FileItem.GetItemByPath(File); LocalToRemoteFileItem(ExternalDependency, true); } FileList = Directory.GetFiles(IncludePath, "*.cpp", SearchOption.AllDirectories); foreach (string File in FileList) { FileItem ExternalDependency = FileItem.GetItemByPath(File); LocalToRemoteFileItem(ExternalDependency, true); } } } } foreach (string Definition in CompileEnvironment.Config.Definitions) { Arguments += string.Format(" -D\"{0}\"", Definition); } CPPOutput Result = new CPPOutput(); // Create a compile action for each source file. foreach (FileItem SourceFile in SourceFiles) { Action CompileAction = new Action(ActionType.Compile); string FileArguments = ""; string Extension = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant(); if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { // Compile the file as a C++ PCH. 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 { // Compile the file as C++ code. 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. CompileAction.PrerequisiteItems.Add(SourceFile); if (ExternalExecution.GetRuntimePlatform() != UnrealTargetPlatform.Mac) { QueueFileForBatchUpload(SourceFile); } foreach (FileItem IncludedFile in CompileEnvironment.GetIncludeDependencies(SourceFile)) { if (ExternalExecution.GetRuntimePlatform() != UnrealTargetPlatform.Mac) { QueueFileForBatchUpload(IncludedFile); } CompileAction.PrerequisiteItems.Add(IncludedFile); } 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(SourceFile.AbsolutePath) + ".gch" ) ); FileItem RemotePrecompiledHeaderFile = LocalToRemoteFileItem(PrecompiledHeaderFile, false); CompileAction.ProducedItems.Add(RemotePrecompiledHeaderFile); Result.PrecompiledHeaderFile = RemotePrecompiledHeaderFile; // Add the parameters needed to compile the precompiled header file to the command-line. FileArguments += string.Format(" -o \"{0}\"", RemotePrecompiledHeaderFile.AbsolutePath, false); } else { if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { CompileAction.bIsUsingPCH = true; CompileAction.PrerequisiteItems.Add(CompileEnvironment.PrecompiledHeaderFile); } // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ".o" ) ); FileItem RemoteObjectFile = LocalToRemoteFileItem(ObjectFile, false); CompileAction.ProducedItems.Add(RemoteObjectFile); Result.ObjectFiles.Add(RemoteObjectFile); FileArguments += string.Format(" -o \"{0}\"", RemoteObjectFile.AbsolutePath, false); } // Add the source file path to the command-line. FileArguments += string.Format(" \"{0}\"", ConvertPath(SourceFile.AbsolutePath), false); if (ExternalExecution.GetRuntimePlatform() != UnrealTargetPlatform.Mac) { CompileAction.ActionHandler = new Action.BlockingActionHandler(RPCUtilHelper.RPCActionHandler); } CompileAction.WorkingDirectory = GetMacDevSrcRoot(); CompileAction.CommandPath = "xcrun"; CompileAction.CommandArguments = MacCompiler + Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments; CompileAction.StatusDescription = string.Format("{0}", Path.GetFileName(SourceFile.AbsolutePath)); CompileAction.StatusDetailedDescription = SourceFile.Description; CompileAction.bIsGCCCompiler = true; // We're already distributing the command by execution on Mac. CompileAction.bCanExecuteRemotely = false; CompileAction.OutputEventHandler = new DataReceivedEventHandler(RemoteOutputReceivedEventHandler); } return Result; }
public override CPPOutput CompileCPPFiles(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.IncludePaths) { Arguments += string.Format(" /I \"{0}\"", IncludePath); } foreach (string IncludePath in CompileEnvironment.Config.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); // 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. CompileAction.PrerequisiteItems.Add(SourceFile); foreach (FileItem IncludedFile in CompileEnvironment.GetIncludeDependencies(SourceFile)) { CompileAction.PrerequisiteItems.Add(IncludedFile); } // 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." + ModuleName + "." + 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); // Add the precompiled header file to the produced items list. FileItem PrecompiledHeaderFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ".pch" ) ); 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); 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); } // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ".obj" ) ); 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.TargetPlatform, CompileEnvironment.Config.TargetConfiguration, "cl"); CompileAction.bIsVCCompiler = true; CompileAction.CommandArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments; CompileAction.StatusDescription = string.Format("{0}", Path.GetFileName(SourceFile.AbsolutePath)); CompileAction.StatusDetailedDescription = SourceFile.Description; // 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(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.IncludePaths) { Arguments += string.Format(" /I \"{0}\"", IncludePath); } foreach (string IncludePath in CompileEnvironment.Config.SystemIncludePaths) { if( WindowsPlatform.bCompileWithClang ) { // @todo Clang: Clang uses a special command-line syntax for system headers. This is used for two reasons. The first is that Clang will automatically // suppress compiler warnings in headers found in these directories, such as the DirectX SDK headers. The other reason this is important is in the case // where there the same header include path is passed as both a regular include path and a system include path (extracted from INCLUDE environment). In // this case Clang will ignore any earlier occurrence of the include path, preventing a system header include path from overriding a different system // include path set later on by a module. NOTE: When passing "-Xclang", these options will always appear at the end of the command-line string, meaning // they will be forced to appear *after* all environment-variable-extracted includes. This is technically okay though. Arguments += string.Format(" -Xclang -internal-isystem -Xclang \"{0}\"", IncludePath); } else { Arguments += string.Format(" /I \"{0}\"", IncludePath); } } 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 += 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 ); } } // Add preprocessor definitions to the argument list. foreach (string Definition in CompileEnvironment.Config.Definitions) { Arguments += string.Format(" /D \"{0}\"", Definition); } // 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. CompileAction.PrerequisiteItems.Add(SourceFile); var IncludeDependencies = CompileEnvironment.GetIncludeDependencies( SourceFile ); foreach (FileItem IncludedFile in IncludeDependencies) { CompileAction.PrerequisiteItems.Add(IncludedFile); } // 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 + "." + 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); // Add the precompiled header file to the produced items list. FileItem PrecompiledHeaderFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ".pch" ) ); CompileAction.ProducedItems.Add(PrecompiledHeaderFile); Result.PrecompiledHeaderFile = PrecompiledHeaderFile; // @todo clang: Ideally clang-cl would support the regular MSVC arguments for PCH files, and we wouldn't need to do this manually if( WindowsPlatform.bCompileWithClang ) { // Tell Clang to generate a PCH header FileArguments += " -Xclang -x -Xclang c++-header"; // @todo clang: Doesn't work do to Clang-cl overriding us at the end of the command-line (see -### option output) FileArguments += string.Format(" /Fo\"{0}\"", PrecompiledHeaderFile.AbsolutePath); // Clang PCH generation doesn't create an .obj file to link in, unlike MSVC bEmitsObjectFile = false; } else { // 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); // 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); } } FileArguments += string.Format(" \"{0}\"", PCHCPPFile.AbsolutePath); CompileAction.StatusDescription = PCHCPPFilename; } else { if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include) { CompileAction.bIsUsingPCH = true; CompileAction.PrerequisiteItems.Add(CompileEnvironment.PrecompiledHeaderFile); if( WindowsPlatform.bCompileWithClang ) { // NOTE: With Clang, PCH headers are ALWAYS forcibly included! // NOTE: This needs to be before the other include paths to ensure Clang uses it instead of the source header file. FileArguments += string.Format(" /FI\"{0}\"", Path.ChangeExtension(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath, null)); } else { FileArguments += string.Format(" /Yu\"{0}\"", CompileEnvironment.Config.PCHHeaderNameInCode); FileArguments += string.Format(" /Fp\"{0}\"", CompileEnvironment.PrecompiledHeaderFile.AbsolutePath); 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 ); } if( bEmitsObjectFile ) { // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ".obj" ) ); CompileAction.ProducedItems.Add(ObjectFile); Result.ObjectFiles.Add(ObjectFile); FileArguments += string.Format(" /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 + "." + Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename); } // Files creating a PCH use a PDB per file. else if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create) { PDBFileName = "PCH." + ModuleName + "." + Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename); 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"; } // @todo Clang: Clang doesn't emit PDB files even when debugging is enabled if( !WindowsPlatform.bCompileWithClang ) { // 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.TargetPlatform, CompileEnvironment.Config.TargetConfiguration, "cl"); if( !WindowsPlatform.bCompileWithClang ) { CompileAction.bIsVCCompiler = true; } CompileAction.CommandArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments; CompileAction.StatusDetailedDescription = SourceFile.Description; 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); } if( WindowsPlatform.bCompileWithClang ) { // Clang doesn't print the file names by default, so we'll do it ourselves CompileAction.bShouldOutputStatusDescription = true; } else { // 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; }
public override CPPOutput CompileRCFiles(CPPEnvironment Environment, List<FileItem> RCFiles) { CPPOutput Result = new CPPOutput(); foreach (FileItem RCFile in RCFiles) { Action CompileAction = new Action(ActionType.Compile); CompileAction.WorkingDirectory = Path.GetFullPath("."); CompileAction.CommandPath = GetVCToolPath(Environment.Config.TargetPlatform, Environment.Config.TargetConfiguration, "rc"); 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. if (Environment.Config.TargetPlatform == CPPTargetPlatform.Win64) { CompileAction.CommandArguments += " /D _WIN64"; } // When targeting Windows XP with Visual Studio 2012+, we need to tell the compiler to use the older Windows SDK that works // with Windows XP (http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx) if (WindowsPlatform.SupportWindowsXP) { if (WindowsPlatform.Compiler == WindowsCompiler.VisualStudio2013) { CompileAction.CommandArguments += " /D_USING_V120_SDK71_"; } else if (WindowsPlatform.Compiler == WindowsCompiler.VisualStudio2012) { CompileAction.CommandArguments += " /D_USING_V110_SDK71_"; } } // Language CompileAction.CommandArguments += " /l 0x409"; // Include paths. foreach (string IncludePath in Environment.Config.IncludePaths) { CompileAction.CommandArguments += string.Format( " /i \"{0}\"", IncludePath ); } // System include paths. foreach( var SystemIncludePath in Environment.Config.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.GetItemByPath( Path.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.PrerequisiteItems.Add(RCFile); CompileAction.CommandArguments += string.Format(" \"{0}\"", RCFile.AbsolutePath); // Add the files included by the RC file as prerequisites of the action. foreach (FileItem IncludedFile in Environment.GetIncludeDependencies(RCFile)) { CompileAction.PrerequisiteItems.Add(IncludedFile); } } return Result; }
// 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; }
public override CPPOutput CompileCPPFiles(CPPEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName) { if (!bHasPrintedApiLevel) { Console.WriteLine("Compiling with NDK API '{0}'", GetNdkApiLevel()); bHasPrintedApiLevel = true; } string Arguments = GetCLArguments_Global(CompileEnvironment); string PCHArguments = ""; if (CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create) { Arguments += " -Werror"; } 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}\"", CompileEnvironment.PrecompiledHeaderFile.AbsolutePath.Replace(".gch", "")); } // Add include paths to the argument list. foreach (string IncludePath in CompileEnvironment.Config.SystemIncludePaths) { Arguments += string.Format(" -I\"{0}\"", IncludePath); } foreach (string IncludePath in CompileEnvironment.Config.IncludePaths) { // we make this absolute because there are some edge cases when a code-based rocket project on the same dtive // as the engine will make relative paths that make clang fail to compile. Absolute will succeeed. Arguments += string.Format(" -I\"{0}\"", Path.GetFullPath(IncludePath)); } // 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) { Arguments += string.Format(" -D \"{0}\"", Definition); } // 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"; // 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.TargetConfiguration != CPPTargetConfiguration.Debug) { Log.TraceWarning("Disabling optimizations on {0}", SourceFile.AbsolutePath); } bDisableOptimizations = bDisableOptimizations || CompileEnvironment.Config.TargetConfiguration == 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. CompileAction.PrerequisiteItems.Add(SourceFile); foreach (FileItem IncludedFile in CompileEnvironment.GetIncludeDependencies(SourceFile)) { CompileAction.PrerequisiteItems.Add(IncludedFile); } 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(SourceFile.AbsolutePath) + ".gch" ) ); 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); } // Add the object file to the produced item list. FileItem ObjectFile = FileItem.GetItemByPath( Path.Combine( CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ".o" ) ); 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}", Path.GetFileName(SourceFile.AbsolutePath)); CompileAction.StatusDetailedDescription = SourceFile.Description; 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; }