/// <summary> /// Builds the binary. /// </summary> /// <param name="Target">Rules for the target being built</param> /// <param name="ToolChain">The toolchain which to use for building</param> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <param name="SharedPCHs">List of templates for shared PCHs</param> /// <param name="WorkingSet">The working set of source files</param> /// <param name="ActionGraph">Graph to add build actions to</param> /// <returns>Set of built products</returns> public IEnumerable <FileItem> Build(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment, List <PrecompiledHeaderTemplate> SharedPCHs, ISourceFileWorkingSet WorkingSet, ActionGraph ActionGraph) { // Return nothing if we're using precompiled binaries if (bUsePrecompiled) { return(new List <FileItem>()); } // Setup linking environment. LinkEnvironment BinaryLinkEnvironment = SetupBinaryLinkEnvironment(Target, ToolChain, LinkEnvironment, CompileEnvironment, SharedPCHs, WorkingSet, ActionGraph); // If we're generating projects, we only need include paths and definitions, there is no need to run the linking logic. if (ProjectFileGenerator.bGenerateProjectFiles) { return(BinaryLinkEnvironment.InputFiles); } // If linking is disabled, our build products are just the compiled object files if (Target.bDisableLinking) { return(BinaryLinkEnvironment.InputFiles); } // Return linked files. return(SetupOutputFiles(ToolChain, CompileEnvironment, BinaryLinkEnvironment, ActionGraph)); }
public override void RegisterToolChain() { // Register this tool chain for WinRT Log.TraceVerbose(" Registered for {0}", CPPTargetPlatform.WinRT.ToString()); Log.TraceVerbose(" Registered for {0}", CPPTargetPlatform.WinRT_ARM.ToString()); UEToolChain.RegisterPlatformToolChain(CPPTargetPlatform.WinRT, this); UEToolChain.RegisterPlatformToolChain(CPPTargetPlatform.WinRT_ARM, this); }
protected void RegisterRemoteToolChain(UnrealTargetPlatform InPlatform, CPPTargetPlatform CPPPlatform) { RemoteToolChainPlatform = InPlatform; // Register this tool chain for IOS Log.TraceVerbose(" Registered for {0}", CPPPlatform.ToString()); UEToolChain.RegisterPlatformToolChain(CPPPlatform, this); }
public override void RegisterToolChain() { if (UWPPlatform.bEnableUWPSupport) { // Register this tool chain for UWP Log.TraceVerbose(" Registered for {0}", CPPTargetPlatform.UWP.ToString()); UEToolChain.RegisterPlatformToolChain(CPPTargetPlatform.UWP, this); } }
/// <summary> /// Gets all build products produced by this binary /// </summary> /// <param name="ToolChain">The platform toolchain</param> /// <param name="BuildProducts">Mapping of produced build product to type</param> public override void GetBuildProducts(UEToolChain ToolChain, Dictionary <FileReference, BuildProductType> BuildProducts) { base.GetBuildProducts(ToolChain, BuildProducts); // Add the compiled resource file if we're building a static library containing the launch module on Windows if (Config.Type == UEBuildBinaryType.StaticLibrary && Modules.Any(x => x.Name == "Launch") && (Target.Platform == UnrealTargetPlatform.Win32 || Target.Platform == UnrealTargetPlatform.Win64)) { FileReference ResourceFilePath = FileReference.Combine(Config.IntermediateDirectory, "Launch", "PCLaunch.rc.res"); BuildProducts.Add(ResourceFilePath, BuildProductType.StaticLibrary); } }
/// <summary> /// Compiles the module, and returns a list of files output by the compiler. /// </summary> public virtual List <FileItem> Compile(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, FileReference SingleFileToCompile, ISourceFileWorkingSet WorkingSet, IActionGraphBuilder Graph) { // Generate type libraries for Windows foreach (ModuleRules.TypeLibrary TypeLibrary in Rules.TypeLibraries) { FileReference OutputFile = FileReference.Combine(IntermediateDirectory, TypeLibrary.Header); ToolChain.GenerateTypeLibraryHeader(CompileEnvironment, TypeLibrary, OutputFile, Graph); } return(new List <FileItem>()); }
public static void RegisterPlatformToolChain(CPPTargetPlatform InPlatform, UEToolChain InToolChain) { if (CPPToolChainDictionary.ContainsKey(InPlatform) == true) { Log.TraceInformation("RegisterPlatformToolChain Warning: Registering tool chain {0} for {1} when it is already set to {2}", InToolChain.ToString(), InPlatform.ToString(), CPPToolChainDictionary[InPlatform].ToString()); CPPToolChainDictionary[InPlatform] = InToolChain; } else { CPPToolChainDictionary.Add(InPlatform, InToolChain); } }
// cache the location of SDK tools public override void RegisterToolChain() { if (HTML5SDKInfo.IsSDKInstalled()) { // set some environment variable we'll need //Environment.SetEnvironmentVariable("EMCC_DEBUG", "cache"); Environment.SetEnvironmentVariable("EMCC_CORES", "8"); Environment.SetEnvironmentVariable("EMCC_OPTIMIZE_NORMALLY", "1"); // finally register the toolchain that is now ready to go Log.TraceVerbose(" Registered for {0}", CPPTargetPlatform.HTML5.ToString()); UEToolChain.RegisterPlatformToolChain(CPPTargetPlatform.HTML5, this); } }
/// <summary> /// Builds the binary. /// </summary> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <returns></returns> public override IEnumerable <FileItem> Build(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment, List <PrecompiledHeaderTemplate> SharedPCHs, ActionGraph ActionGraph) { // UnrealCodeAnalyzer produces output files only for a specific module. if (BuildConfiguration.bRunUnrealCodeAnalyzer && !(Modules.Any(x => x.Name == BuildConfiguration.UCAModuleToAnalyze))) { return(new List <FileItem>()); } // Setup linking environment. LinkEnvironment BinaryLinkEnvironment = SetupBinaryLinkEnvironment(Target, ToolChain, LinkEnvironment, CompileEnvironment, SharedPCHs, ActionGraph); // Return linked files. return(SetupOutputFiles(ToolChain, ref BinaryLinkEnvironment, ActionGraph)); }
/// <summary> /// Builds the binary. /// </summary> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <returns></returns> public override IEnumerable <FileItem> Build(UEToolChain ToolChain, CPPEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment) { // UnrealCodeAnalyzer produces output files only for a specific module. if (BuildConfiguration.bRunUnrealCodeAnalyzer && !(Modules.Any(x => x.Name == BuildConfiguration.UCAModuleToAnalyze))) { return(new List <FileItem>()); } // Setup linking environment. var BinaryLinkEnvironment = SetupBinaryLinkEnvironment(ToolChain, LinkEnvironment, CompileEnvironment); // Return linked files. return(SetupOutputFiles(ToolChain, ref BinaryLinkEnvironment)); }
public override void RegisterToolChain() { if (!CrossCompiling()) { // use native linux toolchain ClangPath = Which("clang++"); GCCPath = Which("g++"); ArPath = Which("ar"); RanlibPath = Which("ranlib"); // if clang is available, zero out gcc (@todo: support runtime switching?) if (!String.IsNullOrEmpty(ClangPath)) { GCCPath = null; } } else { // use cross linux toolchain if LINUX_ROOT is specified BaseLinuxPath = Environment.GetEnvironmentVariable("LINUX_ROOT"); // don't register if we don't have an LINUX_ROOT specified if (String.IsNullOrEmpty(BaseLinuxPath)) { return; } BaseLinuxPath = BaseLinuxPath.Replace("\"", ""); // set up the path to our toolchains (FIXME: support switching per architecture) GCCPath = ""; ClangPath = Path.Combine(BaseLinuxPath, @"bin\Clang++.exe"); ArPath = Path.Combine(BaseLinuxPath, @"bin\x86_64-unknown-linux-gnu-ar.exe"); RanlibPath = Path.Combine(BaseLinuxPath, @"bin\x86_64-unknown-linux-gnu-ranlib.exe"); } if (!DetermineCompilerVersion()) { Console.WriteLine("Could not determine version of the compiler, not registering Linux toolchain"); return; } // Register this tool chain for both Linux if (BuildConfiguration.bPrintDebugInfo) { Console.WriteLine(" Registered for {0}", CPPTargetPlatform.Linux.ToString()); } UEToolChain.RegisterPlatformToolChain(CPPTargetPlatform.Linux, this); }
public override void RegisterToolChain() { // Make sure the SDK is installed // look up installed SDK. string BaseSDKPath = Environment.GetEnvironmentVariable("EMSCRIPTEN"); if (!String.IsNullOrEmpty(BaseSDKPath)) { BaseSDKPath = BaseSDKPath.Replace("\"", ""); if (!String.IsNullOrEmpty(BaseSDKPath)) { var EmscriptenSettings = ReadEmscriptenSettings(); EMCCPath = Path.Combine(BaseSDKPath, "emcc"); // also figure out where python lives (if no envvar, assume it's in the path) if (EmscriptenSettings.ContainsKey("PYTHON")) { PythonPath = EmscriptenSettings["PYTHON"]; } else { PythonPath = Environment.GetEnvironmentVariable("PYTHON"); } string PythonExeName = Utils.IsRunningOnMono ? "python" : "python.exe"; if (PythonPath == null) { PythonPath = PythonExeName; } else { if (!PythonPath.EndsWith(PythonExeName)) { PythonPath += "/" + PythonExeName; } } EMCCPath = "\"" + EMCCPath + "\""; // set some environment variable we'll need //Environment.SetEnvironmentVariable("EMCC_DEBUG", "cache"); Environment.SetEnvironmentVariable("EMCC_CORES", "8"); Environment.SetEnvironmentVariable("EMCC_FORCE_STDLIBS", "1"); Environment.SetEnvironmentVariable("EMCC_OPTIMIZE_NORMALLY", "1"); // finally register the toolchain that is now ready to go Log.TraceVerbose(" Registered for {0}", CPPTargetPlatform.HTML5.ToString()); UEToolChain.RegisterPlatformToolChain(CPPTargetPlatform.HTML5, this); } } }
// cache the location of SDK tools public override void RegisterToolChain() { if (HTML5SDKInfo.IsSDKInstalled() && HTML5SDKInfo.IsPythonInstalled()) { EMCCPath = "\"" + HTML5SDKInfo.EmscriptenCompiler() + "\""; PythonPath = HTML5SDKInfo.PythonPath(); // set some environment variable we'll need //Environment.SetEnvironmentVariable("EMCC_DEBUG", "cache"); Environment.SetEnvironmentVariable("EMCC_CORES", "8"); Environment.SetEnvironmentVariable("EMCC_FORCE_STDLIBS", "1"); Environment.SetEnvironmentVariable("EMCC_OPTIMIZE_NORMALLY", "1"); // finally register the toolchain that is now ready to go Log.TraceVerbose(" Registered for {0}", CPPTargetPlatform.HTML5.ToString()); UEToolChain.RegisterPlatformToolChain(CPPTargetPlatform.HTML5, this); } }
/// <summary> /// Gets all build products produced by this binary /// </summary> /// <param name="Target">The target being built</param> /// <param name="ToolChain">The platform toolchain</param> /// <param name="BuildProducts">Mapping of produced build product to type</param> /// <param name="bCreateDebugInfo">Whether debug info is enabled for this binary</param> public void GetBuildProducts(ReadOnlyTargetRules Target, UEToolChain ToolChain, Dictionary <FileReference, BuildProductType> BuildProducts, bool bCreateDebugInfo) { // Get the type of build products we're creating BuildProductType Type = BuildProductType.RequiredResource; switch (Config.Type) { case UEBuildBinaryType.Executable: Type = BuildProductType.Executable; break; case UEBuildBinaryType.DynamicLinkLibrary: Type = BuildProductType.DynamicLibrary; break; case UEBuildBinaryType.StaticLibrary: Type = BuildProductType.StaticLibrary; break; } // Add the primary build products string DebugExtension = UEBuildPlatform.GetBuildPlatform(Target.Platform).GetDebugInfoExtension(Target, Config.Type); foreach (FileReference OutputFilePath in Config.OutputFilePaths) { AddBuildProductAndDebugFile(OutputFilePath, Type, DebugExtension, BuildProducts, ToolChain, bCreateDebugInfo); } // Add the console app, if there is one if (Config.Type == UEBuildBinaryType.Executable && Config.bBuildAdditionalConsoleApp) { foreach (FileReference OutputFilePath in Config.OutputFilePaths) { AddBuildProductAndDebugFile(GetAdditionalConsoleAppPath(OutputFilePath), Type, DebugExtension, BuildProducts, ToolChain, bCreateDebugInfo); } } // Add any additional build products from the modules in this binary, including additional bundle resources/dylibs on Mac. List <string> Libraries = new List <string>(); List <UEBuildBundleResource> BundleResources = new List <UEBuildBundleResource>(); GatherAdditionalResources(Libraries, BundleResources); // Add any extra files from the toolchain ToolChain.ModifyBuildProducts(Target, this, Libraries, BundleResources, BuildProducts); }
private List <FileItem> SetupOutputFiles(UEToolChain ToolChain, CppCompileEnvironment BinaryCompileEnvironment, LinkEnvironment BinaryLinkEnvironment, ActionGraph ActionGraph) { // // Regular linking action. // List <FileItem> OutputFiles = new List <FileItem>(); if (bCreateImportLibrarySeparately) { // Mark the link environment as cross-referenced. BinaryLinkEnvironment.bIsCrossReferenced = true; if (BinaryLinkEnvironment.Platform != CppPlatform.Mac && BinaryLinkEnvironment.Platform != CppPlatform.Linux) { // Create the import library. OutputFiles.AddRange(ToolChain.LinkAllFiles(BinaryLinkEnvironment, true, ActionGraph)); } } BinaryLinkEnvironment.bIncludeDependentLibrariesInLibrary = bIncludeDependentLibrariesInLibrary; // Link the binary. FileItem[] Executables = ToolChain.LinkAllFiles(BinaryLinkEnvironment, false, ActionGraph); OutputFiles.AddRange(Executables); // Produce additional console app if requested if (Config.bBuildAdditionalConsoleApp) { // Produce additional binary but link it as a console app LinkEnvironment ConsoleAppLinkEvironment = new LinkEnvironment(BinaryLinkEnvironment); ConsoleAppLinkEvironment.bIsBuildingConsoleApplication = true; ConsoleAppLinkEvironment.WindowsEntryPointOverride = "WinMainCRTStartup"; // For WinMain() instead of "main()" for Launch module ConsoleAppLinkEvironment.OutputFilePaths = ConsoleAppLinkEvironment.OutputFilePaths.Select(Path => GetAdditionalConsoleAppPath(Path)).ToList(); // Link the console app executable OutputFiles.AddRange(ToolChain.LinkAllFiles(ConsoleAppLinkEvironment, false, ActionGraph)); } foreach (FileItem Executable in Executables) { OutputFiles.AddRange(ToolChain.PostBuild(Executable, BinaryLinkEnvironment, ActionGraph)); } return(OutputFiles); }
/// <summary> /// Builds the binary. /// </summary> /// <param name="ToolChain">The toolchain to use for building</param> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <returns></returns> public override IEnumerable <FileItem> Build(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment) { CSharpEnvironment ProjectCSharpEnviroment = new CSharpEnvironment(); if (LinkEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug) { ProjectCSharpEnviroment.TargetConfiguration = CSharpTargetConfiguration.Debug; } else { ProjectCSharpEnviroment.TargetConfiguration = CSharpTargetConfiguration.Development; } ProjectCSharpEnviroment.EnvironmentTargetPlatform = LinkEnvironment.Config.Target.Platform; ToolChain.CompileCSharpProject(ProjectCSharpEnviroment, Config.ProjectFilePath, Config.OutputFilePath); return(new FileItem[] { FileItem.GetItemByFileReference(Config.OutputFilePath) }); }
/// <summary> /// Builds the binary. /// </summary> /// <param name="Target">The target rules</param> /// <param name="ToolChain">The toolchain to use for building</param> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <param name="SharedPCHs">List of available shared PCHs</param> /// <param name="ActionGraph">The graph to add build actions to</param> /// <returns>Set of produced build artifacts</returns> public override IEnumerable <FileItem> Build(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment, List <PrecompiledHeaderTemplate> SharedPCHs, ActionGraph ActionGraph) { CSharpEnvironment ProjectCSharpEnviroment = new CSharpEnvironment(); if (LinkEnvironment.Configuration == CppConfiguration.Debug) { ProjectCSharpEnviroment.TargetConfiguration = CSharpTargetConfiguration.Debug; } else { ProjectCSharpEnviroment.TargetConfiguration = CSharpTargetConfiguration.Development; } ProjectCSharpEnviroment.EnvironmentTargetPlatform = LinkEnvironment.Platform; ToolChain.CompileCSharpProject(ProjectCSharpEnviroment, Config.ProjectFilePath, Config.OutputFilePath, ActionGraph); return(new FileItem[] { FileItem.GetItemByFileReference(Config.OutputFilePath) }); }
/// <summary> /// Builds the binary. /// </summary> /// <param name="ToolChain">The toolchain to use for building</param> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <returns></returns> public override IEnumerable <FileItem> Build(IUEToolChain ToolChain, CPPEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment) { var ProjectCSharpEnviroment = new CSharpEnvironment(); if (LinkEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug) { ProjectCSharpEnviroment.TargetConfiguration = CSharpTargetConfiguration.Debug; } else { ProjectCSharpEnviroment.TargetConfiguration = CSharpTargetConfiguration.Development; } ProjectCSharpEnviroment.EnvironmentTargetPlatform = LinkEnvironment.Config.Target.Platform; // Currently only supported by windows... UEToolChain.GetPlatformToolChain(CPPTargetPlatform.Win64).CompileCSharpProject( ProjectCSharpEnviroment, Config.ProjectFilePath, Config.OutputFilePath); return(new FileItem[] { FileItem.GetItemByPath(Config.OutputFilePath) }); }
/// <summary> /// Gets all build products produced by this binary /// </summary> /// <param name="ToolChain">The platform toolchain</param> /// <param name="BuildProducts">Mapping of produced build product to type</param> public virtual void GetBuildProducts(UEToolChain ToolChain, Dictionary <FileReference, BuildProductType> BuildProducts) { // Get the type of build products we're creating BuildProductType Type = BuildProductType.RequiredResource; switch (Config.Type) { case UEBuildBinaryType.Executable: Type = BuildProductType.Executable; break; case UEBuildBinaryType.DynamicLinkLibrary: Type = BuildProductType.DynamicLibrary; break; case UEBuildBinaryType.StaticLibrary: Type = BuildProductType.StaticLibrary; break; } // Add the primary build products string DebugExtension = UEBuildPlatform.GetBuildPlatform(Target.Platform).GetDebugInfoExtension(Config.Type); foreach (FileReference OutputFilePath in Config.OutputFilePaths) { AddBuildProductAndDebugFile(OutputFilePath, Type, DebugExtension, BuildProducts, ToolChain); } // Add the console app, if there is one if (Config.Type == UEBuildBinaryType.Executable && Config.bBuildAdditionalConsoleApp) { foreach (FileReference OutputFilePath in Config.OutputFilePaths) { AddBuildProductAndDebugFile(GetAdditionalConsoleAppPath(OutputFilePath), Type, DebugExtension, BuildProducts, ToolChain); } } // Add any extra files from the toolchain ToolChain.ModifyBuildProducts(this, BuildProducts); }
// UEBuildModule interface. public override List <FileItem> Compile(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, ISourceFileWorkingSet WorkingSet, TargetMakefile Makefile) { return(new List <FileItem>()); }
/// <summary> /// Builds the binary. /// </summary> /// <param name="ToolChain">The toolchain which to use for building</param> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <returns></returns> public abstract IEnumerable<FileItem> Build(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment);
/// <summary> /// Gets all build products produced by this binary /// </summary> /// <param name="ToolChain">The platform toolchain</param> /// <param name="BuildProducts">Mapping of produced build product to type</param> public virtual void GetBuildProducts(UEToolChain ToolChain, Dictionary<FileReference, BuildProductType> BuildProducts) { // Get the type of build products we're creating BuildProductType Type = BuildProductType.RequiredResource; switch (Config.Type) { case UEBuildBinaryType.Executable: Type = BuildProductType.Executable; break; case UEBuildBinaryType.DynamicLinkLibrary: Type = BuildProductType.DynamicLibrary; break; case UEBuildBinaryType.StaticLibrary: Type = BuildProductType.StaticLibrary; break; } // Add the primary build products string DebugExtension = UEBuildPlatform.GetBuildPlatform(Target.Platform).GetDebugInfoExtension(Config.Type); foreach (FileReference OutputFilePath in Config.OutputFilePaths) { AddBuildProductAndDebugFile(OutputFilePath, Type, DebugExtension, BuildProducts, ToolChain); } // Add the console app, if there is one if (Config.Type == UEBuildBinaryType.Executable && Config.bBuildAdditionalConsoleApp) { foreach (FileReference OutputFilePath in Config.OutputFilePaths) { AddBuildProductAndDebugFile(GetAdditionalConsoleAppPath(OutputFilePath), Type, DebugExtension, BuildProducts, ToolChain); } } // Add any extra files from the toolchain ToolChain.ModifyBuildProducts(this, BuildProducts); }
/// <summary> /// Adds a build product and its associated debug file to a receipt. /// </summary> /// <param name="OutputFile">Build product to add</param> /// <param name="DebugExtension">Extension for the matching debug file (may be null).</param> /// <param name="Receipt">Receipt to add to</param> static void AddBuildProductAndDebugFile(FileReference OutputFile, BuildProductType OutputType, string DebugExtension, Dictionary<FileReference, BuildProductType> BuildProducts, UEToolChain ToolChain) { BuildProducts.Add(OutputFile, OutputType); if (!String.IsNullOrEmpty(DebugExtension) && ToolChain.ShouldAddDebugFileToReceipt(OutputFile, OutputType)) { BuildProducts.Add(OutputFile.ChangeExtension(DebugExtension), BuildProductType.SymbolFile); } }
/// <summary> /// Builds the binary. /// </summary> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <returns></returns> public override IEnumerable<FileItem> Build(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment) { // UnrealCodeAnalyzer produces output files only for a specific module. if (BuildConfiguration.bRunUnrealCodeAnalyzer && !(Modules.Any(x => x.Name == BuildConfiguration.UCAModuleToAnalyze))) { return new List<FileItem>(); } // Setup linking environment. LinkEnvironment BinaryLinkEnvironment = SetupBinaryLinkEnvironment(Target, ToolChain, LinkEnvironment, CompileEnvironment); // Return linked files. return SetupOutputFiles(ToolChain, ref BinaryLinkEnvironment); }
/// <summary> /// Gets all build products produced by this binary /// </summary> /// <param name="ToolChain">The platform toolchain</param> /// <param name="BuildProducts">Mapping of produced build product to type</param> public override void GetBuildProducts(UEToolChain ToolChain, Dictionary<FileReference, BuildProductType> BuildProducts) { base.GetBuildProducts(ToolChain, BuildProducts); // Add the compiled resource file if we're building a static library containing the launch module on Windows if (Config.Type == UEBuildBinaryType.StaticLibrary && Modules.Any(x => x.Name == "Launch") && (Target.Platform == UnrealTargetPlatform.Win32 || Target.Platform == UnrealTargetPlatform.Win64)) { FileReference ResourceFilePath = FileReference.Combine(Config.IntermediateDirectory, "Launch", "PCLaunch.rc.res"); BuildProducts.Add(ResourceFilePath, BuildProductType.StaticLibrary); } }
/// <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); }
private LinkEnvironment SetupBinaryLinkEnvironment(UEBuildTarget Target, UEToolChain ToolChain, LinkEnvironment LinkEnvironment, CPPEnvironment CompileEnvironment) { LinkEnvironment BinaryLinkEnvironment = LinkEnvironment.DeepCopy(); HashSet<UEBuildModule> LinkEnvironmentVisitedModules = new HashSet<UEBuildModule>(); List<UEBuildBinary> BinaryDependencies = new List<UEBuildBinary>(); CompileEnvironment.Config.bIsBuildingDLL = IsBuildingDll(Config.Type); CompileEnvironment.Config.bIsBuildingLibrary = IsBuildingLibrary(Config.Type); CPPEnvironment BinaryCompileEnvironment = CompileEnvironment.DeepCopy(); // @Hack: This to prevent UHT from listing CoreUObject.generated.cpp as its dependency. // We flag the compile environment when we build UHT so that we don't need to check // this for each file when generating their dependencies. BinaryCompileEnvironment.bHackHeaderGenerator = (Target.GetAppName() == "UnrealHeaderTool"); // @todo: This should be in some Windows code somewhere... // Set the original file name macro; used in PCLaunch.rc to set the binary metadata fields. string OriginalFilename = (Config.OriginalOutputFilePaths != null) ? Config.OriginalOutputFilePaths[0].GetFileName() : Config.OutputFilePaths[0].GetFileName(); BinaryCompileEnvironment.Config.Definitions.Add("ORIGINAL_FILE_NAME=\"" + OriginalFilename + "\""); foreach (UEBuildModule Module in Modules) { List<FileItem> LinkInputFiles; if (Module.Binary == null || Module.Binary == this) { // Compile each module. Log.TraceVerbose("Compile module: " + Module.Name); LinkInputFiles = Module.Compile(Target, ToolChain, CompileEnvironment, BinaryCompileEnvironment); // NOTE: Because of 'Shared PCHs', in monolithic builds the same PCH file may appear as a link input // multiple times for a single binary. We'll check for that here, and only add it once. This avoids // a linker warning about redundant .obj files. foreach (FileItem LinkInputFile in LinkInputFiles) { if (!BinaryLinkEnvironment.InputFiles.Contains(LinkInputFile)) { BinaryLinkEnvironment.InputFiles.Add(LinkInputFile); } } } else { BinaryDependencies.Add(Module.Binary); } if (!BuildConfiguration.bRunUnrealCodeAnalyzer) { // Allow the module to modify the link environment for the binary. Module.SetupPrivateLinkEnvironment(this, BinaryLinkEnvironment, BinaryDependencies, LinkEnvironmentVisitedModules); } } // Allow the binary dependencies to modify the link environment. foreach (UEBuildBinary BinaryDependency in BinaryDependencies) { BinaryDependency.SetupDependentLinkEnvironment(BinaryLinkEnvironment); } // Remove the default resource file on Windows (PCLaunch.rc) if the user has specified their own if (BinaryLinkEnvironment.InputFiles.Select(Item => Path.GetFileName(Item.AbsolutePath).ToLower()).Any(Name => Name.EndsWith(".res") && !Name.EndsWith(".inl.res") && Name != "pclaunch.rc.res")) { BinaryLinkEnvironment.InputFiles.RemoveAll(x => Path.GetFileName(x.AbsolutePath).ToLower() == "pclaunch.rc.res"); } // Set the link output file. BinaryLinkEnvironment.Config.OutputFilePaths = Config.OutputFilePaths.ToList(); // Set whether the link is allowed to have exports. BinaryLinkEnvironment.Config.bHasExports = Config.bAllowExports; // Set the output folder for intermediate files BinaryLinkEnvironment.Config.IntermediateDirectory = Config.IntermediateDirectory; // Put the non-executable output files (PDB, import library, etc) in the same directory as the production BinaryLinkEnvironment.Config.OutputDirectory = Config.OutputFilePaths[0].Directory; // Setup link output type BinaryLinkEnvironment.Config.bIsBuildingDLL = IsBuildingDll(Config.Type); BinaryLinkEnvironment.Config.bIsBuildingLibrary = IsBuildingLibrary(Config.Type); BinaryLinkEnvironment.Config.ProjectFile = Target.ProjectFile; return BinaryLinkEnvironment; }
private List<FileItem> SetupOutputFiles(UEToolChain ToolChain, ref LinkEnvironment BinaryLinkEnvironment) { // Early exits first if (ProjectFileGenerator.bGenerateProjectFiles) { // We're generating projects. Since we only need include paths and definitions, there is no need // to go ahead and run through the linking logic. return BinaryLinkEnvironment.InputFiles; } if (BuildConfiguration.bDisableLinking) { // We don't need linked binaries return BinaryLinkEnvironment.InputFiles; } if (BuildConfiguration.bEnableCodeAnalysis) { // We're only analyzing code, so we won't actually link any executables. Instead, our output // files will simply be the .obj files that were compiled during static analysis. return BinaryLinkEnvironment.InputFiles; } if (BuildConfiguration.bRunUnrealCodeAnalyzer) { // // Create actions to analyze *.includes files and provide suggestions on how to modify PCH. // return CreateOutputFilesForUCA(BinaryLinkEnvironment); } if (!string.IsNullOrEmpty(BuildConfiguration.SingleFileToCompile)) { return BinaryLinkEnvironment.InputFiles; } // // Regular linking action. // List<FileItem> OutputFiles = new List<FileItem>(); if (bCreateImportLibrarySeparately) { // Mark the link environment as cross-referenced. BinaryLinkEnvironment.Config.bIsCrossReferenced = true; if (BinaryLinkEnvironment.Config.Target.Platform != CPPTargetPlatform.Mac && BinaryLinkEnvironment.Config.Target.Platform != CPPTargetPlatform.Linux) { // Create the import library. OutputFiles.AddRange(ToolChain.LinkAllFiles(BinaryLinkEnvironment, true)); } } BinaryLinkEnvironment.Config.bIncludeDependentLibrariesInLibrary = bIncludeDependentLibrariesInLibrary; // Link the binary. FileItem[] Executables = ToolChain.LinkAllFiles(BinaryLinkEnvironment, false); OutputFiles.AddRange(Executables); // Produce additional console app if requested if (Config.bBuildAdditionalConsoleApp) { // Produce additional binary but link it as a console app LinkEnvironment ConsoleAppLinkEvironment = BinaryLinkEnvironment.DeepCopy(); ConsoleAppLinkEvironment.Config.bIsBuildingConsoleApplication = true; ConsoleAppLinkEvironment.Config.WindowsEntryPointOverride = "WinMainCRTStartup"; // For WinMain() instead of "main()" for Launch module ConsoleAppLinkEvironment.Config.OutputFilePaths = ConsoleAppLinkEvironment.Config.OutputFilePaths.Select(Path => GetAdditionalConsoleAppPath(Path)).ToList(); // Link the console app executable OutputFiles.AddRange(ToolChain.LinkAllFiles(ConsoleAppLinkEvironment, false)); } foreach (FileItem Executable in Executables) { OutputFiles.AddRange(ToolChain.PostBuild(Executable, BinaryLinkEnvironment)); } return OutputFiles; }
/// <summary> /// Builds the binary. /// </summary> /// <param name="ToolChain">The toolchain to use for building</param> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <returns></returns> public override IEnumerable<FileItem> Build(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment) { CSharpEnvironment ProjectCSharpEnviroment = new CSharpEnvironment(); if (LinkEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug) { ProjectCSharpEnviroment.TargetConfiguration = CSharpTargetConfiguration.Debug; } else { ProjectCSharpEnviroment.TargetConfiguration = CSharpTargetConfiguration.Development; } ProjectCSharpEnviroment.EnvironmentTargetPlatform = LinkEnvironment.Config.Target.Platform; ToolChain.CompileCSharpProject(ProjectCSharpEnviroment, Config.ProjectFilePath, Config.OutputFilePath); return new FileItem[] { FileItem.GetItemByFileReference(Config.OutputFilePath) }; }
/// <summary> /// Builds the binary. /// </summary> /// <param name="Target">Rules for the target being built</param> /// <param name="ToolChain">The toolchain which to use for building</param> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <param name="SharedPCHs">List of templates for shared PCHs</param> /// <param name="WorkingSet">The working set of source files</param> /// <param name="ActionGraph">Graph to add build actions to</param> /// <returns>Set of built products</returns> public IEnumerable <FileItem> Build(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment, List <PrecompiledHeaderTemplate> SharedPCHs, ISourceFileWorkingSet WorkingSet, ActionGraph ActionGraph) { // Return nothing if we're using precompiled binaries if (bUsePrecompiled) { return(new List <FileItem>()); } // Setup linking environment. LinkEnvironment BinaryLinkEnvironment = SetupBinaryLinkEnvironment(Target, ToolChain, LinkEnvironment, CompileEnvironment, SharedPCHs, WorkingSet, ActionGraph); // If we're generating projects, we only need include paths and definitions, there is no need to run the linking logic. if (ProjectFileGenerator.bGenerateProjectFiles) { return(BinaryLinkEnvironment.InputFiles); } // If linking is disabled, our build products are just the compiled object files if (Target.bDisableLinking) { return(BinaryLinkEnvironment.InputFiles); } // Generate import libraries as a separate step List <FileItem> OutputFiles = new List <FileItem>(); if (bCreateImportLibrarySeparately) { // Mark the link environment as cross-referenced. BinaryLinkEnvironment.bIsCrossReferenced = true; if (BinaryLinkEnvironment.Platform != CppPlatform.Mac && BinaryLinkEnvironment.Platform != CppPlatform.Linux) { // Create the import library. OutputFiles.AddRange(ToolChain.LinkAllFiles(BinaryLinkEnvironment, true, ActionGraph)); } } // Link the binary. FileItem[] Executables = ToolChain.LinkAllFiles(BinaryLinkEnvironment, false, ActionGraph); OutputFiles.AddRange(Executables); // Produce additional console app if requested if (bBuildAdditionalConsoleApp) { // Produce additional binary but link it as a console app LinkEnvironment ConsoleAppLinkEvironment = new LinkEnvironment(BinaryLinkEnvironment); ConsoleAppLinkEvironment.bIsBuildingConsoleApplication = true; ConsoleAppLinkEvironment.WindowsEntryPointOverride = "WinMainCRTStartup"; // For WinMain() instead of "main()" for Launch module ConsoleAppLinkEvironment.OutputFilePaths = ConsoleAppLinkEvironment.OutputFilePaths.Select(Path => GetAdditionalConsoleAppPath(Path)).ToList(); // Link the console app executable OutputFiles.AddRange(ToolChain.LinkAllFiles(ConsoleAppLinkEvironment, false, ActionGraph)); } foreach (FileItem Executable in Executables) { OutputFiles.AddRange(ToolChain.PostBuild(Executable, BinaryLinkEnvironment, ActionGraph)); } return(OutputFiles); }
/// <summary> /// Adds a build product and its associated debug file to a receipt. /// </summary> /// <param name="OutputFile">Build product to add</param> /// <param name="OutputType">The type of built product</param> /// <param name="DebugExtensions">Extensions for the matching debug file (may be null).</param> /// <param name="BuildProducts">Map of build products to their type</param> /// <param name="ToolChain">The toolchain used to build these binaries</param> /// <param name="bCreateDebugInfo">Whether creating debug info is enabled</param> static void AddBuildProductAndDebugFiles(FileReference OutputFile, BuildProductType OutputType, string[] DebugExtensions, Dictionary <FileReference, BuildProductType> BuildProducts, UEToolChain ToolChain, bool bCreateDebugInfo) { BuildProducts.Add(OutputFile, OutputType); foreach (string DebugExtension in DebugExtensions) { if (!String.IsNullOrEmpty(DebugExtension) && ToolChain.ShouldAddDebugFileToReceipt(OutputFile, OutputType) && bCreateDebugInfo) { // @todo this could be cleaned up if we replaced Platform.GetDebugExtensions() with ToolChain.GetDebugFiles(OutputFile) // would need care in MacToolchain tho, so too risky for now BuildProducts.Add(ToolChain.GetDebugFile(OutputFile, DebugExtension), BuildProductType.SymbolFile); } } }
/// <summary> /// Builds the binary. /// </summary> /// <param name="Target">Rules for the target being built</param> /// <param name="ToolChain">The toolchain which to use for building</param> /// <param name="CompileEnvironment">The environment to compile the binary in</param> /// <param name="LinkEnvironment">The environment to link the binary in</param> /// <param name="SingleFileToCompile">If non-null, specifies a single cpp file to be compiled</param> /// <param name="WorkingSet">The working set of source files</param> /// <param name="ExeDir">Directory containing the output executable</param> /// <param name="Makefile">The makefile to add actions to</param> /// <returns>Set of built products</returns> public List <FileItem> Build(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment, FileReference SingleFileToCompile, ISourceFileWorkingSet WorkingSet, DirectoryReference ExeDir, TargetMakefile Makefile) { // Return nothing if we're using precompiled binaries. If we're not linking, we might want just one module to be compiled (eg. a foreign plugin), so allow any actions to run. if (bUsePrecompiled && !Target.bDisableLinking) { return(new List <FileItem>()); } // Setup linking environment. LinkEnvironment BinaryLinkEnvironment = SetupBinaryLinkEnvironment(Target, ToolChain, LinkEnvironment, CompileEnvironment, SingleFileToCompile, WorkingSet, ExeDir, Makefile); // If we're generating projects, we only need include paths and definitions, there is no need to run the linking logic. if (ProjectFileGenerator.bGenerateProjectFiles) { return(BinaryLinkEnvironment.InputFiles); } // If linking is disabled, our build products are just the compiled object files if (Target.bDisableLinking) { return(BinaryLinkEnvironment.InputFiles); } // Generate import libraries as a separate step List <FileItem> OutputFiles = new List <FileItem>(); if (bCreateImportLibrarySeparately) { // Mark the link environment as cross-referenced. BinaryLinkEnvironment.bIsCrossReferenced = true; if (BinaryLinkEnvironment.Platform != UnrealTargetPlatform.Mac && BinaryLinkEnvironment.Platform != UnrealTargetPlatform.Linux && BinaryLinkEnvironment.Platform != UnrealTargetPlatform.LinuxAArch64) { // Create the import library. OutputFiles.AddRange(ToolChain.LinkAllFiles(BinaryLinkEnvironment, true, Makefile.Actions)); } } // Link the binary. FileItem[] Executables = ToolChain.LinkAllFiles(BinaryLinkEnvironment, false, Makefile.Actions); OutputFiles.AddRange(Executables); // Save all the output items for this binary. This is used for hot-reload, and excludes any items added in PostBuild (such as additional files copied into the app). if (Target.LinkType == TargetLinkType.Modular) { Makefile.ModuleNameToOutputItems[PrimaryModule.Name] = OutputFiles.ToArray(); } // Produce additional console app if requested if (bBuildAdditionalConsoleApp) { // Produce additional binary but link it as a console app LinkEnvironment ConsoleAppLinkEvironment = new LinkEnvironment(BinaryLinkEnvironment); ConsoleAppLinkEvironment.bIsBuildingConsoleApplication = true; ConsoleAppLinkEvironment.WindowsEntryPointOverride = "WinMainCRTStartup"; // For WinMain() instead of "main()" for Launch module ConsoleAppLinkEvironment.OutputFilePaths = ConsoleAppLinkEvironment.OutputFilePaths.Select(Path => GetAdditionalConsoleAppPath(Path)).ToList(); // Link the console app executable FileItem[] ConsoleAppOutputFiles = ToolChain.LinkAllFiles(ConsoleAppLinkEvironment, false, Makefile.Actions); OutputFiles.AddRange(ConsoleAppOutputFiles); foreach (FileItem Executable in ConsoleAppOutputFiles) { OutputFiles.AddRange(ToolChain.PostBuild(Executable, ConsoleAppLinkEvironment, Makefile.Actions)); } } foreach (FileItem Executable in Executables) { OutputFiles.AddRange(ToolChain.PostBuild(Executable, BinaryLinkEnvironment, Makefile.Actions)); } return(OutputFiles); }
// UEBuildModule interface. public override List <FileItem> Compile(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, List <PrecompiledHeaderTemplate> SharedPCHs, ActionGraph ActionGraph) { return(new List <FileItem>()); }
/// <summary> /// Compiles the module, and returns a list of files output by the compiler. /// </summary> public abstract List <FileItem> Compile(ReadOnlyTargetRules Target, UEToolChain ToolChain, CppCompileEnvironment CompileEnvironment, List <PrecompiledHeaderTemplate> SharedPCHModules, ISourceFileWorkingSet WorkingSet, ActionGraph ActionGraph);
private LinkEnvironment SetupBinaryLinkEnvironment(ReadOnlyTargetRules Target, UEToolChain ToolChain, LinkEnvironment LinkEnvironment, CppCompileEnvironment CompileEnvironment, List <PrecompiledHeaderTemplate> SharedPCHs, ISourceFileWorkingSet WorkingSet, ActionGraph ActionGraph) { LinkEnvironment BinaryLinkEnvironment = new LinkEnvironment(LinkEnvironment); HashSet <UEBuildModule> LinkEnvironmentVisitedModules = new HashSet <UEBuildModule>(); List <UEBuildBinary> BinaryDependencies = new List <UEBuildBinary>(); CppCompileEnvironment BinaryCompileEnvironment = CreateBinaryCompileEnvironment(CompileEnvironment); foreach (UEBuildModule Module in Modules) { List <FileItem> LinkInputFiles; if (Module.Binary == null || Module.Binary == this) { // Compile each module. Log.TraceVerbose("Compile module: " + Module.Name); LinkInputFiles = Module.Compile(Target, ToolChain, BinaryCompileEnvironment, SharedPCHs, WorkingSet, ActionGraph); // NOTE: Because of 'Shared PCHs', in monolithic builds the same PCH file may appear as a link input // multiple times for a single binary. We'll check for that here, and only add it once. This avoids // a linker warning about redundant .obj files. foreach (FileItem LinkInputFile in LinkInputFiles) { if (!BinaryLinkEnvironment.InputFiles.Contains(LinkInputFile)) { BinaryLinkEnvironment.InputFiles.Add(LinkInputFile); } } } else { BinaryDependencies.Add(Module.Binary); } // Allow the module to modify the link environment for the binary. Module.SetupPrivateLinkEnvironment(this, BinaryLinkEnvironment, BinaryDependencies, LinkEnvironmentVisitedModules); } // Allow the binary dependencies to modify the link environment. foreach (UEBuildBinary BinaryDependency in BinaryDependencies) { BinaryDependency.SetupDependentLinkEnvironment(BinaryLinkEnvironment); } // Remove the default resource file on Windows (PCLaunch.rc) if the user has specified their own if (BinaryLinkEnvironment.InputFiles.Select(Item => Path.GetFileName(Item.AbsolutePath).ToLower()).Any(Name => Name.EndsWith(".res") && !Name.EndsWith(".inl.res") && Name != "pclaunch.rc.res")) { BinaryLinkEnvironment.InputFiles.RemoveAll(x => Path.GetFileName(x.AbsolutePath).ToLower() == "pclaunch.rc.res"); } // Set the link output file. BinaryLinkEnvironment.OutputFilePaths = OutputFilePaths.ToList(); // Set whether the link is allowed to have exports. BinaryLinkEnvironment.bHasExports = bAllowExports; // Set the output folder for intermediate files BinaryLinkEnvironment.IntermediateDirectory = IntermediateDirectory; // Put the non-executable output files (PDB, import library, etc) in the same directory as the production BinaryLinkEnvironment.OutputDirectory = OutputFilePaths[0].Directory; // Setup link output type BinaryLinkEnvironment.bIsBuildingDLL = IsBuildingDll(Type); BinaryLinkEnvironment.bIsBuildingLibrary = IsBuildingLibrary(Type); // If we don't have any resource file, use the default or compile a custom one for this module if (BinaryLinkEnvironment.Platform == CppPlatform.Win32 || BinaryLinkEnvironment.Platform == CppPlatform.Win64) { if (!BinaryLinkEnvironment.InputFiles.Any(x => x.Location.HasExtension(".res"))) { if (BinaryLinkEnvironment.DefaultResourceFiles.Count > 0) { // Use the default resource file if possible BinaryLinkEnvironment.InputFiles.AddRange(BinaryLinkEnvironment.DefaultResourceFiles); } else { // Create a compile environment for resource files CppCompileEnvironment ResourceCompileEnvironment = new CppCompileEnvironment(BinaryCompileEnvironment); // @todo: This should be in some Windows code somewhere... // Set the original file name macro; used in PCLaunch.rc to set the binary metadata fields. string OriginalFilename = (OriginalOutputFilePaths != null) ? OriginalOutputFilePaths[0].GetFileName() : OutputFilePaths[0].GetFileName(); ResourceCompileEnvironment.Definitions.Add("ORIGINAL_FILE_NAME=\"" + OriginalFilename + "\""); // Set the other version fields ResourceCompileEnvironment.Definitions.Add(String.Format("BUILT_FROM_CHANGELIST={0}", Target.Version.Changelist)); ResourceCompileEnvironment.Definitions.Add(String.Format("BUILD_VERSION={0}", Target.BuildVersion)); // Otherwise compile the default resource file per-binary, so that it gets the correct ORIGINAL_FILE_NAME macro. FileItem DefaultResourceFile = FileItem.GetItemByFileReference(FileReference.Combine(UnrealBuildTool.EngineSourceDirectory, "Runtime", "Launch", "Resources", "Windows", "PCLaunch.rc")); CPPOutput DefaultResourceOutput = ToolChain.CompileRCFiles(BinaryCompileEnvironment, new List <FileItem> { DefaultResourceFile }, ((UEBuildModuleCPP)Modules.First()).IntermediateDirectory, ActionGraph); BinaryLinkEnvironment.InputFiles.AddRange(DefaultResourceOutput.ObjectFiles); } } } // Add all the common resource files BinaryLinkEnvironment.InputFiles.AddRange(BinaryLinkEnvironment.CommonResourceFiles); return(BinaryLinkEnvironment); }
/// <summary> /// Adds a build product and its associated debug file to a receipt. /// </summary> /// <param name="OutputFile">Build product to add</param> /// <param name="OutputType">The type of built product</param> /// <param name="DebugExtensions">Extensions for the matching debug file (may be null).</param> /// <param name="BuildProducts">Map of build products to their type</param> /// <param name="ToolChain">The toolchain used to build these binaries</param> /// <param name="bCreateDebugInfo">Whether creating debug info is enabled</param> static void AddBuildProductAndDebugFiles(FileReference OutputFile, BuildProductType OutputType, string[] DebugExtensions, Dictionary <FileReference, BuildProductType> BuildProducts, UEToolChain ToolChain, bool bCreateDebugInfo) { BuildProducts.Add(OutputFile, OutputType); foreach (string DebugExtension in DebugExtensions) { if (!String.IsNullOrEmpty(DebugExtension) && ToolChain.ShouldAddDebugFileToReceipt(OutputFile, OutputType) && bCreateDebugInfo) { BuildProducts.Add(OutputFile.ChangeExtension(DebugExtension), BuildProductType.SymbolFile); } } }
// UEBuildModule interface. public override List<FileItem> Compile(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { CPPEnvironment ModuleCLREnvironment = CompileEnvironment.DeepCopy(); // Setup the module environment for the project CLR mode ModuleCLREnvironment.Config.CLRMode = CPPCLRMode.CLREnabled; // Add the private assembly references to the compile environment. foreach (string PrivateAssemblyReference in PrivateAssemblyReferences) { ModuleCLREnvironment.AddPrivateAssembly(PrivateAssemblyReference); } // Pass the CLR compilation environment to the standard C++ module compilation code. return base.Compile(Target, ToolChain, GlobalCompileEnvironment, ModuleCLREnvironment); }
/// <summary> /// Builds and runs the header tool and touches the header directories. /// Performs any early outs if headers need no changes, given the UObject modules, tool path, game name, and configuration /// </summary> public static bool ExecuteHeaderToolIfNecessary(UEToolChain ToolChain, UEBuildTarget Target, CPPEnvironment GlobalCompileEnvironment, List <UHTModuleInfo> UObjectModules, FileReference ModuleInfoFileName, ref ECompilationResult UHTResult) { if (ProgressWriter.bWriteMarkup) { Log.WriteLine(LogEventType.Console, "@progress push 5%"); } using (ProgressWriter Progress = new ProgressWriter("Generating code...", false)) { // We never want to try to execute the header tool when we're already trying to build it! var bIsBuildingUHT = Target.GetTargetName().Equals("UnrealHeaderTool", StringComparison.InvariantCultureIgnoreCase); var RootLocalPath = Path.GetFullPath(ProjectFileGenerator.RootRelativePath); // check if UHT is out of date DateTime HeaderToolTimestamp = DateTime.MaxValue; bool bHaveHeaderTool = !bIsBuildingUHT && GetHeaderToolTimestamp(out HeaderToolTimestamp); // ensure the headers are up to date bool bUHTNeedsToRun = (UEBuildConfiguration.bForceHeaderGeneration == true || !bHaveHeaderTool || AreGeneratedCodeFilesOutOfDate(UObjectModules, HeaderToolTimestamp)); if (bUHTNeedsToRun || UnrealBuildTool.IsGatheringBuild) { // Since code files are definitely out of date, we'll now finish computing information about the UObject modules for UHT. We // want to save this work until we know that UHT actually needs to be run to speed up best-case iteration times. if (UnrealBuildTool.IsGatheringBuild) // In assembler-only mode, PCH info is loaded from our UBTMakefile! { foreach (var UHTModuleInfo in UObjectModules) { // Only cache the PCH name if we don't already have one. When running in 'gather only' mode, this will have already been cached if (string.IsNullOrEmpty(UHTModuleInfo.PCH)) { UHTModuleInfo.PCH = ""; // We need to figure out which PCH header this module is including, so that UHT can inject an include statement for it into any .cpp files it is synthesizing var DependencyModuleCPP = (UEBuildModuleCPP)Target.GetModuleByName(UHTModuleInfo.ModuleName); var ModuleCompileEnvironment = DependencyModuleCPP.CreateModuleCompileEnvironment(GlobalCompileEnvironment); DependencyModuleCPP.CachePCHUsageForModuleSourceFiles(ModuleCompileEnvironment); if (DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile != null) { UHTModuleInfo.PCH = DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile.AbsolutePath; } } } } } // @todo ubtmake: Optimization: Ideally we could avoid having to generate this data in the case where UHT doesn't even need to run! Can't we use the existing copy? (see below use of Manifest) UHTManifest Manifest = new UHTManifest(Target, RootLocalPath, ToolChain.ConvertPath(RootLocalPath + '\\'), UObjectModules); if (!bIsBuildingUHT && bUHTNeedsToRun) { // Always build UnrealHeaderTool if header regeneration is required, unless we're running within a Rocket ecosystem or hot-reloading if (UnrealBuildTool.RunningRocket() == false && UEBuildConfiguration.bDoNotBuildUHT == false && UEBuildConfiguration.bHotReloadFromIDE == false && !(bHaveHeaderTool && !UnrealBuildTool.IsGatheringBuild && UnrealBuildTool.IsAssemblingBuild)) // If running in "assembler only" mode, we assume UHT is already up to date for much faster iteration! { // If it is out of date or not there it will be built. // If it is there and up to date, it will add 0.8 seconds to the build time. Log.TraceInformation("Building UnrealHeaderTool..."); var UBTArguments = new StringBuilder(); UBTArguments.Append("UnrealHeaderTool"); // Which desktop platform do we need to compile UHT for? UBTArguments.Append(" " + BuildHostPlatform.Current.Platform.ToString()); // NOTE: We force Development configuration for UHT so that it runs quickly, even when compiling debug UBTArguments.Append(" " + UnrealTargetConfiguration.Development.ToString()); // NOTE: We disable mutex when launching UBT from within UBT to compile UHT UBTArguments.Append(" -NoMutex"); if (UnrealBuildTool.CommandLineContains("-noxge")) { UBTArguments.Append(" -noxge"); } // Propagate command-line option if (UnrealBuildTool.CommandLineContains("-2015")) { UBTArguments.Append(" -2015"); } if (UnrealBuildTool.CommandLineContains("-2013")) { UBTArguments.Append(" -2013"); } // Add UHT plugins to UBT command line as external plugins if (Target.UnrealHeaderToolPlugins != null && Target.UnrealHeaderToolPlugins.Count > 0) { foreach (PluginInfo Plugin in Target.UnrealHeaderToolPlugins) { UBTArguments.Append(" -PLUGIN \"" + Plugin.File + "\""); } } if (RunExternalExecutable(UnrealBuildTool.GetUBTPath(), UBTArguments.ToString()) != 0) { return(false); } } Progress.Write(1, 3); var ActualTargetName = String.IsNullOrEmpty(Target.GetTargetName()) ? "UE4" : Target.GetTargetName(); Log.TraceInformation("Parsing headers for {0}", ActualTargetName); string HeaderToolPath = GetHeaderToolPath(); if (!File.Exists(HeaderToolPath)) { throw new BuildException("Unable to generate headers because UnrealHeaderTool binary was not found ({0}).", Path.GetFullPath(HeaderToolPath)); } // Disable extensions when serializing to remove the $type fields Directory.CreateDirectory(ModuleInfoFileName.Directory.FullName); System.IO.File.WriteAllText(ModuleInfoFileName.FullName, fastJSON.JSON.Instance.ToJSON(Manifest, new fastJSON.JSONParameters { UseExtensions = false })); string CmdLine = (Target.ProjectFile != null) ? "\"" + Target.ProjectFile.FullName + "\"" : Target.GetTargetName(); CmdLine += " \"" + ModuleInfoFileName + "\" -LogCmds=\"loginit warning, logexit warning, logdatabase error\" -Unattended -WarningsAsErrors"; if (UnrealBuildTool.IsEngineInstalled()) { CmdLine += " -installed"; } if (UEBuildConfiguration.bFailIfGeneratedCodeChanges) { CmdLine += " -FailIfGeneratedCodeChanges"; } if (!bInvalidateUHTMakefile && BuildConfiguration.bUseUHTMakefiles) { CmdLine += " -UseMakefiles"; } if (!UEBuildConfiguration.bCompileAgainstEngine) { CmdLine += " -NoEnginePlugins"; } Log.TraceInformation(" Running UnrealHeaderTool {0}", CmdLine); Stopwatch s = new Stopwatch(); s.Start(); UHTResult = (ECompilationResult)RunExternalExecutable(ExternalExecution.GetHeaderToolPath(), CmdLine); s.Stop(); if (UHTResult != ECompilationResult.Succeeded) { // On Linux and Mac, the shell will return 128+signal number exit codes if UHT gets a signal (e.g. crashes or is interrupted) if ((BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Linux || BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) && (int)(UHTResult) >= 128 ) { // SIGINT is 2, so 128 + SIGINT is 130 UHTResult = ((int)(UHTResult) == 130) ? ECompilationResult.Canceled : ECompilationResult.CrashOrAssert; } Log.TraceInformation("Error: Failed to generate code for {0} - error code: {2} ({1})", ActualTargetName, (int)UHTResult, UHTResult.ToString()); return(false); } Log.TraceInformation("Reflection code generated for {0} in {1} seconds", ActualTargetName, s.Elapsed.TotalSeconds); if (BuildConfiguration.bPrintPerformanceInfo) { Log.TraceInformation("UnrealHeaderTool took {1}", ActualTargetName, (double)s.ElapsedMilliseconds / 1000.0); } // Now that UHT has successfully finished generating code, we need to update all cached FileItems in case their last write time has changed. // Otherwise UBT might not detect changes UHT made. DateTime StartTime = DateTime.UtcNow; FileItem.ResetInfos(); double ResetDuration = (DateTime.UtcNow - StartTime).TotalSeconds; Log.TraceVerbose("FileItem.ResetInfos() duration: {0}s", ResetDuration); } else { Log.TraceVerbose("Generated code is up to date."); } Progress.Write(2, 3); // There will never be generated code if we're building UHT, so this should never be called. if (!bIsBuildingUHT) { // Allow generated code to be sync'd to remote machines if needed. This needs to be done even if UHT did not run because // generated headers include other generated headers using absolute paths which in case of building remotely are already // the remote machine absolute paths. Because of that parsing headers will not result in finding all includes properly. // @todo ubtmake: Need to figure out what this does in the assembler case, and whether we need to run it ToolChain.PostCodeGeneration(Manifest); } // touch the directories UpdateDirectoryTimestamps(UObjectModules); Progress.Write(3, 3); } if (ProgressWriter.bWriteMarkup) { Log.WriteLine(LogEventType.Console, "@progress pop"); } return(true); }
/// <summary> /// Creates a precompiled header action to generate a new pch file /// </summary> /// <param name="PCHHeaderNameInCode">The precompiled header name as it appeared in an #include statement</param> /// <param name="PrecompiledHeaderIncludeFilename">Name of the header used for pch.</param> /// <param name="ProjectCPPEnvironment">The environment the C/C++ files in the project are compiled with.</param> /// <param name="OutputDirectory">The folder to save the generated PCH file to</param> /// <param name="ModuleName">Name of the module this PCH is being generated for</param> /// <param name="bAllowDLLExports">True if we should allow DLLEXPORT definitions for this PCH</param> /// <returns>the compilation output result of the created pch.</returns> public static CPPOutput GeneratePCHCreationAction(UEToolChain ToolChain, UEBuildTarget Target, string PCHHeaderNameInCode, FileItem PrecompiledHeaderIncludeFilename, CPPEnvironment ProjectCPPEnvironment, DirectoryReference OutputDirectory, DirectoryReference PCHOutputDirectory, string ModuleName, bool bAllowDLLExports) { // Find the header file to be precompiled. Don't skip external headers if (PrecompiledHeaderIncludeFilename.bExists) { // Create a Dummy wrapper around the PCH to avoid problems with #pragma once on clang string PCHGuardDefine = Path.GetFileNameWithoutExtension(PrecompiledHeaderIncludeFilename.AbsolutePath).ToUpperInvariant(); string LocalPCHHeaderNameInCode = ToolChain.ConvertPath(PrecompiledHeaderIncludeFilename.AbsolutePath); string TmpPCHHeaderContents = String.Format("#ifndef __AUTO_{0}_H__\n#define __AUTO_{0}_H__\n//Last Write: {2}\n#include \"{1}\"\n#endif//__AUTO_{0}_H__", PCHGuardDefine, LocalPCHHeaderNameInCode, PrecompiledHeaderIncludeFilename.LastWriteTime); FileReference DummyPath = FileReference.Combine( ProjectCPPEnvironment.Config.OutputDirectory, Path.GetFileName(PrecompiledHeaderIncludeFilename.AbsolutePath)); FileItem DummyPCH = FileItem.CreateIntermediateTextFile(DummyPath, TmpPCHHeaderContents); // Create a new C++ environment that is used to create the PCH. CPPEnvironment ProjectPCHEnvironment = ProjectCPPEnvironment.DeepCopy(); ProjectPCHEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Create; ProjectPCHEnvironment.Config.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFilename.Reference; ProjectPCHEnvironment.Config.PCHHeaderNameInCode = PCHHeaderNameInCode; ProjectPCHEnvironment.Config.OutputDirectory = OutputDirectory; ProjectPCHEnvironment.Config.PCHOutputDirectory = PCHOutputDirectory; if (!bAllowDLLExports) { for (int CurDefinitionIndex = 0; CurDefinitionIndex < ProjectPCHEnvironment.Config.Definitions.Count; ++CurDefinitionIndex) { // We change DLLEXPORT to DLLIMPORT for "shared" PCH headers string OldDefinition = ProjectPCHEnvironment.Config.Definitions[CurDefinitionIndex]; if (OldDefinition.EndsWith("=DLLEXPORT")) { ProjectPCHEnvironment.Config.Definitions[CurDefinitionIndex] = OldDefinition.Replace("DLLEXPORT", "DLLIMPORT"); } } } // Cache our CPP environment so that we can check for outdatedness quickly. Only files that have includes need this. DummyPCH.CachedCPPIncludeInfo = ProjectPCHEnvironment.Config.CPPIncludeInfo; Log.TraceVerbose("Found PCH file \"{0}\".", PrecompiledHeaderIncludeFilename); // Create the action to compile the PCH file. return ToolChain.CompileCPPFiles(Target, ProjectPCHEnvironment, new List<FileItem>() { DummyPCH }, ModuleName); } throw new BuildException("Couldn't find PCH file \"{0}\".", PrecompiledHeaderIncludeFilename); }
/// <summary> /// Compiles the module, and returns a list of files output by the compiler. /// </summary> public abstract List<FileItem> Compile(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment);
// UEBuildModule interface. public override List<FileItem> Compile(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { return new List<FileItem>(); }
/// <summary> /// Gets all build products produced by this binary /// </summary> /// <param name="Target">The target being built</param> /// <param name="ToolChain">The platform toolchain</param> /// <param name="BuildProducts">Mapping of produced build product to type</param> /// <param name="bCreateDebugInfo">Whether debug info is enabled for this binary</param> public void GetBuildProducts(ReadOnlyTargetRules Target, UEToolChain ToolChain, Dictionary <FileReference, BuildProductType> BuildProducts, bool bCreateDebugInfo) { // Add all the precompiled outputs foreach (UEBuildModuleCPP Module in Modules.OfType <UEBuildModuleCPP>()) { if (Module.Rules.bPrecompile) { if (Module.GeneratedCodeDirectory != null && DirectoryReference.Exists(Module.GeneratedCodeDirectory)) { foreach (FileReference GeneratedCodeFile in DirectoryReference.EnumerateFiles(Module.GeneratedCodeDirectory)) { // Exclude timestamp files, since they're always updated and cause collisions between builds if (!GeneratedCodeFile.GetFileName().Equals("Timestamp", StringComparison.OrdinalIgnoreCase) && !GeneratedCodeFile.HasExtension(".cpp")) { BuildProducts.Add(GeneratedCodeFile, BuildProductType.BuildResource); } } } if (Target.LinkType == TargetLinkType.Monolithic) { FileReference PrecompiledManifestLocation = Module.PrecompiledManifestLocation; BuildProducts.Add(PrecompiledManifestLocation, BuildProductType.BuildResource); PrecompiledManifest ModuleManifest = PrecompiledManifest.Read(PrecompiledManifestLocation); foreach (FileReference OutputFile in ModuleManifest.OutputFiles) { if (!BuildProducts.ContainsKey(OutputFile)) { BuildProducts.Add(OutputFile, BuildProductType.BuildResource); } } } } } // Add all the binary outputs if (!Target.bDisableLinking) { // Get the type of build products we're creating BuildProductType OutputType = BuildProductType.RequiredResource; switch (Type) { case UEBuildBinaryType.Executable: OutputType = BuildProductType.Executable; break; case UEBuildBinaryType.DynamicLinkLibrary: OutputType = BuildProductType.DynamicLibrary; break; case UEBuildBinaryType.StaticLibrary: OutputType = BuildProductType.BuildResource; break; } // Add the primary build products string[] DebugExtensions = UEBuildPlatform.GetBuildPlatform(Target.Platform).GetDebugInfoExtensions(Target, Type); foreach (FileReference OutputFilePath in OutputFilePaths) { AddBuildProductAndDebugFiles(OutputFilePath, OutputType, DebugExtensions, BuildProducts, ToolChain, bCreateDebugInfo); } // Add the console app, if there is one if (Type == UEBuildBinaryType.Executable && bBuildAdditionalConsoleApp) { foreach (FileReference OutputFilePath in OutputFilePaths) { AddBuildProductAndDebugFiles(GetAdditionalConsoleAppPath(OutputFilePath), OutputType, DebugExtensions, BuildProducts, ToolChain, bCreateDebugInfo); } } // Add any additional build products from the modules in this binary, including additional bundle resources/dylibs on Mac. List <string> Libraries = new List <string>(); List <UEBuildBundleResource> BundleResources = new List <UEBuildBundleResource>(); GatherAdditionalResources(Libraries, BundleResources); // Add any extra files from the toolchain ToolChain.ModifyBuildProducts(Target, this, Libraries, BundleResources, BuildProducts); } }
// UEBuildModule interface. public override List<FileItem> Compile(UEBuildTarget Target, UEToolChain ToolChain, CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment) { UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform); List<FileItem> LinkInputFiles = new List<FileItem>(); if (ProjectFileGenerator.bGenerateProjectFiles && IntelliSenseGatherer == null) { // Nothing to do for IntelliSense, bail out early return LinkInputFiles; } CPPEnvironment ModuleCompileEnvironment = CreateModuleCompileEnvironment(Target, CompileEnvironment); IncludeSearchPaths = ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludePaths.ToList(); IncludeSearchPaths.AddRange(ModuleCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths.ToList()); if (IntelliSenseGatherer != null) { // Update project file's set of preprocessor definitions and include paths IntelliSenseGatherer.AddIntelliSensePreprocessorDefinitions(ModuleCompileEnvironment.Config.Definitions); IntelliSenseGatherer.AddInteliiSenseIncludePaths(ModuleCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths, bAddingSystemIncludes: true); IntelliSenseGatherer.AddInteliiSenseIncludePaths(ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludePaths, bAddingSystemIncludes: false); // Bail out. We don't need to actually compile anything while generating project files. return LinkInputFiles; } // Throw an error if the module's source file list referenced any non-existent files. if (SourceFilesToBuild.MissingFiles.Count > 0) { throw new BuildException( "UBT ERROR: Module \"{0}\" references non-existent files:\n{1} (perhaps a file was added to the project but not checked in)", Name, string.Join("\n", SourceFilesToBuild.MissingFiles.Select(M => M.AbsolutePath)) ); } // For an executable or a static library do not use the default RC file - // If the executable wants it, it will be in their source list anyway. // The issue here is that when making a monolithic game, the processing // of the other game modules will stomp the game-specific rc file. if (Binary.Config.Type == UEBuildBinaryType.DynamicLinkLibrary) { // Add default PCLaunch.rc file if this module has no own resource file specified if (SourceFilesToBuild.RCFiles.Count <= 0) { FileReference DefRC = FileReference.Combine(UnrealBuildTool.EngineSourceDirectory, "Runtime", "Launch", "Resources", "Windows", "PCLaunch.rc"); FileItem Item = FileItem.GetItemByFileReference(DefRC); SourceFilesToBuild.RCFiles.Add(Item); } // Always compile in the API version resource separately. This is required for the module manager to detect compatible API versions. FileReference ModuleVersionRC = FileReference.Combine(UnrealBuildTool.EngineSourceDirectory, "Runtime", "Core", "Resources", "Windows", "ModuleVersionResource.rc.inl"); FileItem ModuleVersionItem = FileItem.GetItemByFileReference(ModuleVersionRC); if (!SourceFilesToBuild.RCFiles.Contains(ModuleVersionItem)) { SourceFilesToBuild.RCFiles.Add(ModuleVersionItem); } } { // Process all of the header file dependencies for this module this.CachePCHUsageForModuleSourceFiles(Target, ModuleCompileEnvironment); // Make sure our RC files have cached includes. foreach (FileItem RCFile in SourceFilesToBuild.RCFiles) { RCFile.CachedCPPIncludeInfo = ModuleCompileEnvironment.Config.CPPIncludeInfo; } } // Check to see if this is an Engine module (including program or plugin modules). That is, the module is located under the "Engine" folder bool IsPluginModule = ModuleDirectory.IsUnderDirectory(DirectoryReference.Combine(Target.ProjectDirectory, "Plugins")); bool IsGameModule = !IsPluginModule && !ModuleDirectory.IsUnderDirectory(UnrealBuildTool.EngineDirectory); // Should we force a precompiled header to be generated for this module? Usually, we only bother with a // precompiled header if there are at least several source files in the module (after combining them for unity // builds.) But for game modules, it can be convenient to always have a precompiled header to single-file // changes to code is really quick to compile. int MinFilesUsingPrecompiledHeader = BuildConfiguration.MinFilesUsingPrecompiledHeader; if (Rules.MinFilesUsingPrecompiledHeaderOverride != 0) { MinFilesUsingPrecompiledHeader = Rules.MinFilesUsingPrecompiledHeaderOverride; } else if (IsGameModule && BuildConfiguration.bForcePrecompiledHeaderForGameModules) { // This is a game module with only a small number of source files, so go ahead and force a precompiled header // to be generated to make incremental changes to source files as fast as possible for small projects. MinFilesUsingPrecompiledHeader = 1; } // Engine modules will always use unity build mode unless MinSourceFilesForUnityBuildOverride is specified in // the module rules file. By default, game modules only use unity of they have enough source files for that // to be worthwhile. If you have a lot of small game modules, consider specifying MinSourceFilesForUnityBuildOverride=0 // in the modules that you don't typically iterate on source files in very frequently. int MinSourceFilesForUnityBuild = 0; if (Rules.MinSourceFilesForUnityBuildOverride != 0) { MinSourceFilesForUnityBuild = Rules.MinSourceFilesForUnityBuildOverride; } else if (IsGameModule) { // Game modules with only a small number of source files are usually better off having faster iteration times // on single source file changes, so we forcibly disable unity build for those modules MinSourceFilesForUnityBuild = BuildConfiguration.MinGameModuleSourceFilesForUnityBuild; } // Should we use unity build mode for this module? bool bModuleUsesUnityBuild = false; if (BuildConfiguration.bUseUnityBuild || BuildConfiguration.bForceUnityBuild) { if (BuildConfiguration.bForceUnityBuild) { Log.TraceVerbose("Module '{0}' using unity build mode (bForceUnityBuild enabled for this module)", this.Name); bModuleUsesUnityBuild = true; } else if (Rules.bFasterWithoutUnity) { Log.TraceVerbose("Module '{0}' not using unity build mode (bFasterWithoutUnity enabled for this module)", this.Name); bModuleUsesUnityBuild = false; } else if (SourceFilesToBuild.CPPFiles.Count < MinSourceFilesForUnityBuild) { Log.TraceVerbose("Module '{0}' not using unity build mode (module with fewer than {1} source files)", this.Name, MinSourceFilesForUnityBuild); bModuleUsesUnityBuild = false; } else { Log.TraceVerbose("Module '{0}' using unity build mode (enabled in BuildConfiguration)", this.Name); bModuleUsesUnityBuild = true; } } else { Log.TraceVerbose("Module '{0}' not using unity build mode (disabled in BuildConfiguration)", this.Name); } // The environment with which to compile the CPP files CPPEnvironment CPPCompileEnvironment = ModuleCompileEnvironment; // Precompiled header support. bool bWasModuleCodeCompiled = false; if (BuildPlatform.ShouldUsePCHFiles(CompileEnvironment.Config.Target.Platform, CompileEnvironment.Config.Target.Configuration)) { DateTime PCHGenTimerStart = DateTime.UtcNow; // The code below will figure out whether this module will either use a "unique PCH" (private PCH that will only be included by // this module's code files), or a "shared PCH" (potentially included by many code files in many modules.) Only one or the other // will be used. FileItem SharedPCHHeaderFile = null; // In the case of a shared PCH, we also need to keep track of which module that PCH's header file is a member of string SharedPCHModuleName = String.Empty; if (BuildConfiguration.bUseSharedPCHs && CompileEnvironment.Config.bIsBuildingLibrary) { Log.TraceVerbose("Module '{0}' was not allowed to use Shared PCHs, because we're compiling to a library", this.Name); } bool bUseSharedPCHFiles = BuildConfiguration.bUseSharedPCHs && !CompileEnvironment.Config.bIsBuildingLibrary && GlobalCompileEnvironment.SharedPCHHeaderFiles.Count > 0; if (bUseSharedPCHFiles) { FileReference SharingPCHHeaderFilePath = null; bool bIsASharedPCHModule = bUseSharedPCHFiles && GlobalCompileEnvironment.SharedPCHHeaderFiles.Any(PCH => PCH.Module == this); if (bIsASharedPCHModule) { SharingPCHHeaderFilePath = FileReference.Combine(UnrealBuildTool.EngineSourceDirectory, Rules.SharedPCHHeaderFile); } // We can't use a shared PCH file when compiling a module // with exports, because the shared PCH can only have imports in it to work correctly. bool bAllowSharedPCH = (Rules.PCHUsage == ModuleRules.PCHUsageMode.NoSharedPCHs) ? false : true; bool bCanModuleUseOwnSharedPCH = bAllowSharedPCH && bIsASharedPCHModule && !Binary.Config.bAllowExports && ProcessedDependencies.UniquePCHHeaderFile.Reference == SharingPCHHeaderFilePath; if (bAllowSharedPCH && (!bIsASharedPCHModule || bCanModuleUseOwnSharedPCH)) { // Figure out which shared PCH tier we're in List<UEBuildModule> ReferencedModules = new List<UEBuildModule>(); { this.GetAllDependencyModules(ReferencedModules, new HashSet<UEBuildModule>(), bIncludeDynamicallyLoaded: false, bForceCircular: false, bOnlyDirectDependencies: true); } int LargestSharedPCHHeaderFileIndex = -1; foreach (UEBuildModule DependencyModule in ReferencedModules) { // These Shared PCHs are ordered from least complex to most complex. We'll start at the last one and search backwards. for (int SharedPCHHeaderFileIndex = GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1; SharedPCHHeaderFileIndex > LargestSharedPCHHeaderFileIndex; --SharedPCHHeaderFileIndex) { SharedPCHHeaderInfo CurSharedPCHHeaderFile = GlobalCompileEnvironment.SharedPCHHeaderFiles[SharedPCHHeaderFileIndex]; if (DependencyModule == CurSharedPCHHeaderFile.Module || (bIsASharedPCHModule && CurSharedPCHHeaderFile.Module == this)) // If we ourselves are a shared PCH module, always at least use our own module as our shared PCH header if we can't find anything better { SharedPCHModuleName = CurSharedPCHHeaderFile.Module.Name; SharedPCHHeaderFile = CurSharedPCHHeaderFile.PCHHeaderFile; LargestSharedPCHHeaderFileIndex = SharedPCHHeaderFileIndex; break; } } if (LargestSharedPCHHeaderFileIndex == GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1) { // We've determined that the module is using our most complex PCH header, so we can early-out break; } } // Did we not find a shared PCH header that is being included by this module? This could happen if the module is not including Core.h, even indirectly. if (String.IsNullOrEmpty(SharedPCHModuleName)) { throw new BuildException("Module {0} doesn't use a Shared PCH! Please add a dependency on a Shared PCH module to this module's dependency list", this.Name); } // Keep track of how many modules make use of this PCH for performance diagnostics SharedPCHHeaderInfo LargestSharedPCHHeader = GlobalCompileEnvironment.SharedPCHHeaderFiles[LargestSharedPCHHeaderFileIndex]; ++LargestSharedPCHHeader.NumModulesUsingThisPCH; // Don't allow game modules to use engine PCHs in DebugGame - the optimization settings aren't correct. // @todo: we should be creating shared PCHs ahead of time, and only using them if our settings match. as it is, the first modules compiled // (which are currently plugins) get to call the shots for how the shared PCH gets built, and that might be a game plugin built in debug... if(Target.Configuration == UnrealTargetConfiguration.DebugGame && SharedPCHHeaderFile.Reference.IsUnderDirectory(UnrealBuildTool.EngineDirectory) && !RulesFile.IsUnderDirectory(UnrealBuildTool.EngineDirectory)) { SharedPCHModuleName = null; SharedPCHHeaderFile = null; } } else { Log.TraceVerbose("Module '{0}' cannot create or use Shared PCHs, because it needs its own private PCH", this.Name); } } // The precompiled header environment for all source files in this module that use a precompiled header, if we even need one PrecompileHeaderEnvironment ModulePCHEnvironment = null; // If there was one header that was included first by enough C++ files, use it as the precompiled header. // Only use precompiled headers for projects with enough files to make the PCH creation worthwhile. if (SharedPCHHeaderFile != null || SourceFilesToBuild.CPPFiles.Count >= MinFilesUsingPrecompiledHeader) { FileItem PCHToUse; if (SharedPCHHeaderFile != null) { ModulePCHEnvironment = ApplySharedPCH(GlobalCompileEnvironment, CompileEnvironment, ModuleCompileEnvironment, SourceFilesToBuild.CPPFiles, ref SharedPCHHeaderFile); if (ModulePCHEnvironment != null) { // @todo SharedPCH: Ideally we would exhaustively check for a compatible compile environment (definitions, imports/exports, etc) // Currently, it's possible for the shared PCH to be compiled differently depending on which module UBT happened to have // include it first during the build phase. This could create problems with deterministic builds, or turn up compile // errors unexpectedly due to compile environment differences. Log.TraceVerbose("Module " + Name + " uses existing Shared PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "' (from module " + ModulePCHEnvironment.ModuleName + ")"); } PCHToUse = SharedPCHHeaderFile; } else { PCHToUse = ProcessedDependencies.UniquePCHHeaderFile; } if (PCHToUse != null) { // Update all CPPFiles to point to the PCH foreach (FileItem CPPFile in SourceFilesToBuild.CPPFiles) { CPPFile.PCHHeaderNameInCode = PCHToUse.AbsolutePath; CPPFile.PrecompiledHeaderIncludeFilename = PCHToUse.Reference; } } // A shared PCH was not already set up for us, so set one up. if (ModulePCHEnvironment == null && SourceFilesToBuild.CPPFiles.Count > 0) { FileItem PCHHeaderFile = ProcessedDependencies.UniquePCHHeaderFile; string PCHModuleName = this.Name; if (SharedPCHHeaderFile != null) { PCHHeaderFile = SharedPCHHeaderFile; PCHModuleName = SharedPCHModuleName; } string PCHHeaderNameInCode = SourceFilesToBuild.CPPFiles[0].PCHHeaderNameInCode; ModulePCHEnvironment = new PrecompileHeaderEnvironment(PCHModuleName, PCHHeaderNameInCode, PCHHeaderFile, ModuleCompileEnvironment.Config.CLRMode, ModuleCompileEnvironment.Config.OptimizeCode); if (SharedPCHHeaderFile != null) { // Add to list of shared PCH environments GlobalCompileEnvironment.SharedPCHEnvironments.Add(ModulePCHEnvironment); Log.TraceVerbose("Module " + Name + " uses new Shared PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'"); } else { Log.TraceVerbose("Module " + Name + " uses a Unique PCH '" + ModulePCHEnvironment.PrecompiledHeaderIncludeFilename + "'"); } } } else { Log.TraceVerbose("Module " + Name + " doesn't use a Shared PCH, and only has " + SourceFilesToBuild.CPPFiles.Count.ToString() + " source file(s). No Unique PCH will be generated."); } // Compile the C++ source or the unity C++ files that use a PCH environment. if (ModulePCHEnvironment != null) { // Setup a new compile environment for this module's source files. It's pretty much the exact same as the // module's compile environment, except that it will include a PCH file. CPPEnvironment ModulePCHCompileEnvironment = ModuleCompileEnvironment.DeepCopy(); ModulePCHCompileEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Include; ModulePCHCompileEnvironment.Config.PrecompiledHeaderIncludeFilename = ModulePCHEnvironment.PrecompiledHeaderIncludeFilename.Reference; ModulePCHCompileEnvironment.Config.PCHHeaderNameInCode = ModulePCHEnvironment.PCHHeaderNameInCode; if (SharedPCHHeaderFile != null) { // Shared PCH headers need to be force included, because we're basically forcing the module to use // the precompiled header that we want, instead of the "first include" in each respective .cpp file ModulePCHCompileEnvironment.Config.bForceIncludePrecompiledHeader = true; } List<FileItem> CPPFilesToBuild = SourceFilesToBuild.CPPFiles; if (bModuleUsesUnityBuild) { // unity files generated for only the set of files which share the same PCH environment CPPFilesToBuild = Unity.GenerateUnityCPPs(ToolChain, Target, CPPFilesToBuild, ModulePCHCompileEnvironment, Name); } // Check if there are enough unity files to warrant pch generation (and we haven't already generated the shared one) if (ModulePCHEnvironment.PrecompiledHeaderFile == null) { if (SharedPCHHeaderFile != null || CPPFilesToBuild.Count >= MinFilesUsingPrecompiledHeader) { CPPOutput PCHOutput; if (SharedPCHHeaderFile == null) { PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction( ToolChain, Target, CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, ModuleCompileEnvironment, ModuleCompileEnvironment.Config.OutputDirectory, ModuleCompileEnvironment.Config.PCHOutputDirectory, Name, true); } else { UEBuildModuleCPP SharedPCHModule = (UEBuildModuleCPP)Target.FindOrCreateModuleByName(SharedPCHModuleName); CPPEnvironment SharedPCHCompileEnvironment = GlobalCompileEnvironment.DeepCopy(); SharedPCHCompileEnvironment.Config.bEnableShadowVariableWarning = SharedPCHModule.Rules.bEnableShadowVariableWarnings; List<UEBuildModule> Modules = new List<UEBuildModule>(); Dictionary<UEBuildModule, bool> ModuleToIncludePathsOnlyFlag = new Dictionary<UEBuildModule, bool>(); SharedPCHModule.FindModulesInPublicCompileEnvironment(Modules, ModuleToIncludePathsOnlyFlag); foreach (UEBuildModule Module in Modules) { Module.AddModuleToCompileEnvironment( Binary, ModuleToIncludePathsOnlyFlag[Module], SharedPCHCompileEnvironment.Config.CPPIncludeInfo.IncludePaths, SharedPCHCompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths, SharedPCHCompileEnvironment.Config.Definitions, SharedPCHCompileEnvironment.Config.AdditionalFrameworks); } PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction( ToolChain, Target, CPPFilesToBuild[0].PCHHeaderNameInCode, ModulePCHEnvironment.PrecompiledHeaderIncludeFilename, SharedPCHCompileEnvironment, DirectoryReference.Combine(CompileEnvironment.Config.OutputDirectory, "SharedPCHs"), (CompileEnvironment.Config.PCHOutputDirectory == null)? null : DirectoryReference.Combine(CompileEnvironment.Config.PCHOutputDirectory, "SharedPCHs"), "Shared", false); } ModulePCHEnvironment.PrecompiledHeaderFile = PCHOutput.PrecompiledHeaderFile; ModulePCHEnvironment.OutputObjectFiles.Clear(); ModulePCHEnvironment.OutputObjectFiles.AddRange(PCHOutput.ObjectFiles); } else if (CPPFilesToBuild.Count < MinFilesUsingPrecompiledHeader) { Log.TraceVerbose("Module " + Name + " doesn't use a Shared PCH, and only has " + CPPFilesToBuild.Count.ToString() + " unity source file(s). No Unique PCH will be generated."); } } if (ModulePCHEnvironment.PrecompiledHeaderFile != null) { // Link in the object files produced by creating the precompiled header. LinkInputFiles.AddRange(ModulePCHEnvironment.OutputObjectFiles); // if pch action was generated for the environment then use pch ModulePCHCompileEnvironment.PrecompiledHeaderFile = ModulePCHEnvironment.PrecompiledHeaderFile; // Use this compile environment from now on CPPCompileEnvironment = ModulePCHCompileEnvironment; } LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, CPPFilesToBuild, Name).ObjectFiles); bWasModuleCodeCompiled = true; } if (BuildConfiguration.bPrintPerformanceInfo) { double PCHGenTime = (DateTime.UtcNow - PCHGenTimerStart).TotalSeconds; TotalPCHGenTime += PCHGenTime; } } if (!bWasModuleCodeCompiled && SourceFilesToBuild.CPPFiles.Count > 0) { List<FileItem> CPPFilesToCompile = SourceFilesToBuild.CPPFiles; if (bModuleUsesUnityBuild) { CPPFilesToCompile = Unity.GenerateUnityCPPs(ToolChain, Target, CPPFilesToCompile, CPPCompileEnvironment, Name); } LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, CPPFilesToCompile, Name).ObjectFiles); } if (AutoGenerateCppInfo != null && AutoGenerateCppInfo.BuildInfo != null && !CPPCompileEnvironment.bHackHeaderGenerator) { string[] GeneratedFiles = Directory.GetFiles(Path.GetDirectoryName(AutoGenerateCppInfo.BuildInfo.FileWildcard), Path.GetFileName(AutoGenerateCppInfo.BuildInfo.FileWildcard)); foreach (string GeneratedFilename in GeneratedFiles) { FileItem GeneratedCppFileItem = FileItem.GetItemByPath(GeneratedFilename); CachePCHUsageForModuleSourceFile(Target, CPPCompileEnvironment, GeneratedCppFileItem); // @todo ubtmake: Check for ALL other places where we might be injecting .cpp or .rc files for compiling without caching CachedCPPIncludeInfo first (anything platform specific?) LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, new List<FileItem> { GeneratedCppFileItem }, Name).ObjectFiles); } } // Compile C files directly. LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, SourceFilesToBuild.CFiles, Name).ObjectFiles); // Compile CC files directly. LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, SourceFilesToBuild.CCFiles, Name).ObjectFiles); // Compile MM files directly. LinkInputFiles.AddRange(ToolChain.CompileCPPFiles(Target, CPPCompileEnvironment, SourceFilesToBuild.MMFiles, Name).ObjectFiles); // Compile RC files. LinkInputFiles.AddRange(ToolChain.CompileRCFiles(Target, CPPCompileEnvironment, SourceFilesToBuild.RCFiles).ObjectFiles); return LinkInputFiles; }
private LinkEnvironment SetupBinaryLinkEnvironment(ReadOnlyTargetRules Target, UEToolChain ToolChain, LinkEnvironment LinkEnvironment, CppCompileEnvironment CompileEnvironment, FileReference SingleFileToCompile, ISourceFileWorkingSet WorkingSet, DirectoryReference ExeDir, TargetMakefile Makefile) { LinkEnvironment BinaryLinkEnvironment = new LinkEnvironment(LinkEnvironment); HashSet <UEBuildModule> LinkEnvironmentVisitedModules = new HashSet <UEBuildModule>(); List <UEBuildBinary> BinaryDependencies = new List <UEBuildBinary>(); CppCompileEnvironment BinaryCompileEnvironment = CreateBinaryCompileEnvironment(CompileEnvironment); if (BinaryCompileEnvironment.bUseSharedBuildEnvironment && Target.ProjectFile != null && IntermediateDirectory.IsUnderDirectory(Target.ProjectFile.Directory)) { BinaryCompileEnvironment.bUseSharedBuildEnvironment = false; } foreach (UEBuildModule Module in Modules) { List <FileItem> LinkInputFiles; if (Module.Binary == null || Module.Binary == this) { // Compile each module. Log.TraceVerbose("Compile module: " + Module.Name); LinkInputFiles = Module.Compile(Target, ToolChain, BinaryCompileEnvironment, SingleFileToCompile, WorkingSet, Makefile); // Save the module outputs. In monolithic builds, this is just the object files. if (Target.LinkType == TargetLinkType.Monolithic) { Makefile.ModuleNameToOutputItems[Module.Name] = LinkInputFiles.ToArray(); } // NOTE: Because of 'Shared PCHs', in monolithic builds the same PCH file may appear as a link input // multiple times for a single binary. We'll check for that here, and only add it once. This avoids // a linker warning about redundant .obj files. foreach (FileItem LinkInputFile in LinkInputFiles) { if (!BinaryLinkEnvironment.InputFiles.Contains(LinkInputFile)) { BinaryLinkEnvironment.InputFiles.Add(LinkInputFile); } } // Force a reference to initialize module for this binary if (Module.Rules.bRequiresImplementModule.Value) { BinaryLinkEnvironment.IncludeFunctions.Add(String.Format("IMPLEMENT_MODULE_{0}", Module.Name)); } } else { BinaryDependencies.Add(Module.Binary); } // Allow the module to modify the link environment for the binary. Module.SetupPrivateLinkEnvironment(this, BinaryLinkEnvironment, BinaryDependencies, LinkEnvironmentVisitedModules, ExeDir); } // Allow the binary dependencies to modify the link environment. foreach (UEBuildBinary BinaryDependency in BinaryDependencies) { BinaryDependency.SetupDependentLinkEnvironment(BinaryLinkEnvironment); } // Set the link output file. BinaryLinkEnvironment.OutputFilePaths = OutputFilePaths.ToList(); // Set whether the link is allowed to have exports. BinaryLinkEnvironment.bHasExports = bAllowExports; // Set the output folder for intermediate files BinaryLinkEnvironment.IntermediateDirectory = IntermediateDirectory; // Put the non-executable output files (PDB, import library, etc) in the same directory as the production BinaryLinkEnvironment.OutputDirectory = OutputFilePaths[0].Directory; // Setup link output type BinaryLinkEnvironment.bIsBuildingDLL = IsBuildingDll(Type); BinaryLinkEnvironment.bIsBuildingLibrary = IsBuildingLibrary(Type); // If we don't have any resource file, use the default or compile a custom one for this module if (BinaryLinkEnvironment.Platform == UnrealTargetPlatform.Win32 || BinaryLinkEnvironment.Platform == UnrealTargetPlatform.Win64) { // Figure out if this binary has any custom resource files. Hacky check to ignore the resource file in the Launch module, since it contains dialogs that the engine needs and always needs to be included. FileItem[] CustomResourceFiles = BinaryLinkEnvironment.InputFiles.Where(x => x.Location.HasExtension(".res") && !x.Location.FullName.EndsWith("\\Launch\\PCLaunch.rc.res", StringComparison.OrdinalIgnoreCase)).ToArray(); if (CustomResourceFiles.Length == 0) { if (BinaryLinkEnvironment.DefaultResourceFiles.Count > 0) { // Use the default resource file if possible BinaryLinkEnvironment.InputFiles.AddRange(BinaryLinkEnvironment.DefaultResourceFiles); } else { // Get the intermediate directory DirectoryReference ResourceIntermediateDirectory = BinaryLinkEnvironment.IntermediateDirectory; // Create a compile environment for resource files CppCompileEnvironment ResourceCompileEnvironment = new CppCompileEnvironment(BinaryCompileEnvironment); // @todo: This should be in some Windows code somewhere... // Set the original file name macro; used in Default.rc2 to set the binary metadata fields. ResourceCompileEnvironment.Definitions.Add("ORIGINAL_FILE_NAME=\"" + OutputFilePaths[0].GetFileName() + "\""); // Set the other version fields ResourceCompileEnvironment.Definitions.Add(String.Format("BUILT_FROM_CHANGELIST={0}", Target.Version.Changelist)); ResourceCompileEnvironment.Definitions.Add(String.Format("BUILD_VERSION={0}", Target.BuildVersion)); // Otherwise compile the default resource file per-binary, so that it gets the correct ORIGINAL_FILE_NAME macro. FileItem DefaultResourceFile = FileItem.GetItemByFileReference(FileReference.Combine(UnrealBuildTool.EngineDirectory, "Build", "Windows", "Resources", "Default.rc2")); CPPOutput DefaultResourceOutput = ToolChain.CompileRCFiles(ResourceCompileEnvironment, new List <FileItem> { DefaultResourceFile }, ResourceIntermediateDirectory, Makefile.Actions); BinaryLinkEnvironment.InputFiles.AddRange(DefaultResourceOutput.ObjectFiles); } } } // Add all the common resource files BinaryLinkEnvironment.InputFiles.AddRange(BinaryLinkEnvironment.CommonResourceFiles); return(BinaryLinkEnvironment); }
/// <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.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. // 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(); } string PCHHeaderNameInCode = CPPFiles[0].PCHHeaderNameInCode; if (CompileEnvironment.Config.PrecompiledHeaderIncludeFilename != null) { PCHHeaderNameInCode = ToolChain.ConvertPath(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename.FullName); // 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; 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."); // 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 (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("Module.{0}.{1}_of_{2}.cpp", BaseName, CurrentUnityFileCount, AllUnityFiles.Count); } else { UnityCPPFileName = string.Format("Module.{0}.cpp", 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; UnityCPPFile.PCHHeaderNameInCode = PCHHeaderNameInCode; 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(Target, CompileEnvironment, UnityCPPFile); } return NewCPPFiles; }