/// <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(ISTToolChain ToolChain, CPPEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment);
/// <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(ISTToolChain 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... STToolChain.GetPlatformToolChain(CPPTargetPlatform.Win64).CompileCSharpProject( ProjectCSharpEnviroment, Config.ProjectFilePath, Config.OutputFilePath); return new FileItem[] { FileItem.GetItemByPath(Config.OutputFilePath) }; }
/// <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(ISTToolChain TargetToolChain, CPPEnvironment CompileEnvironment, LinkEnvironment LinkEnvironment) { // Determine the type of binary we're linking. switch (Config.Type) { case STBuildBinaryType.DynamicLinkLibrary: CompileEnvironment.Config.bIsBuildingDLL = true; CompileEnvironment.Config.bIsBuildingLibrary = false; break; case STBuildBinaryType.StaticLibrary: CompileEnvironment.Config.bIsBuildingDLL = false; CompileEnvironment.Config.bIsBuildingLibrary = true; break; default: CompileEnvironment.Config.bIsBuildingDLL = false; CompileEnvironment.Config.bIsBuildingLibrary = false; break; }; var OutputFiles = new List<FileItem>(); var BinaryCompileEnvironment = CompileEnvironment.DeepCopy(); var BinaryLinkEnvironment = LinkEnvironment.DeepCopy(); // Process each module that is linked into the binary. var BinaryDependencies = new List<STBuildBinary>(); var LinkEnvironmentVisitedModules = new Dictionary<STBuildModule, bool>(); // @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. var OriginalFilename = (Config.OriginalOutputFilePaths != null) ? Path.GetFileName(Config.OriginalOutputFilePaths[0]) : Path.GetFileName(Config.OutputFilePaths[0]); BinaryCompileEnvironment.Config.Definitions.Add("ORIGINAL_FILE_NAME=\"" + OriginalFilename + "\""); foreach (var ModuleName in ModuleNames) { var Module = Target.GetModuleByName(ModuleName); // Compile each module. Log.TraceVerbose("Compile module: " + ModuleName); var LinkInputFiles = Module.Compile(CompileEnvironment, BinaryCompileEnvironment, Config.bCompileMonolithic); // 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 (var LinkInputFile in LinkInputFiles) { if (!BinaryLinkEnvironment.InputFiles.Contains(LinkInputFile)) { BinaryLinkEnvironment.InputFiles.Add(LinkInputFile); } } // Allow the module to modify the link environment for the binary. Module.SetupPrivateLinkEnvironment(ref BinaryLinkEnvironment, ref BinaryDependencies, ref LinkEnvironmentVisitedModules); } // 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"); } // Allow the binary dependencies to modify the link environment. foreach (var BinaryDependency in BinaryDependencies) { BinaryDependency.SetupDependentLinkEnvironment(ref BinaryLinkEnvironment); } // Set the link output file. BinaryLinkEnvironment.Config.OutputFilePaths = Config.OutputFilePaths != null ? (string[])Config.OutputFilePaths.Clone() : null; // 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 = Path.GetDirectoryName(Config.OutputFilePaths[0]); // Determine the type of binary we're linking. switch (Config.Type) { case STBuildBinaryType.DynamicLinkLibrary: BinaryLinkEnvironment.Config.bIsBuildingDLL = true; BinaryLinkEnvironment.Config.bIsBuildingLibrary = false; break; case STBuildBinaryType.StaticLibrary: BinaryLinkEnvironment.Config.bIsBuildingDLL = false; BinaryLinkEnvironment.Config.bIsBuildingLibrary = true; break; default: BinaryLinkEnvironment.Config.bIsBuildingDLL = false; BinaryLinkEnvironment.Config.bIsBuildingLibrary = false; break; }; 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. OutputFiles = BinaryLinkEnvironment.InputFiles; } else 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. OutputFiles = BinaryLinkEnvironment.InputFiles; } else { 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(BinaryLinkEnvironment.LinkExecutable(true)); } } BinaryLinkEnvironment.Config.bIncludeDependentLibrariesInLibrary = bIncludeDependentLibrariesInLibrary; // Link the binary. FileItem[] Executables = BinaryLinkEnvironment.LinkExecutable(false); OutputFiles.AddRange(Executables); // Produce additional console app if requested if (BinaryLinkEnvironment.Config.CanProduceAdditionalConsoleApp && STBuildConfiguration.bBuildEditor) { // Produce additional binary but link it as a console app var ConsoleAppLinkEvironment = BinaryLinkEnvironment.DeepCopy(); ConsoleAppLinkEvironment.Config.bIsBuildingConsoleApplication = true; ConsoleAppLinkEvironment.Config.WindowsEntryPointOverride = "WinMainCRTStartup"; // For WinMain() instead of "main()" for Launch module for (int Index = 0; Index < Config.OutputFilePaths.Length; Index++) { ConsoleAppLinkEvironment.Config.OutputFilePaths[Index] = GetAdditionalConsoleAppPath(ConsoleAppLinkEvironment.Config.OutputFilePaths[Index]); } // Link the console app executable OutputFiles.AddRange(ConsoleAppLinkEvironment.LinkExecutable(false)); } foreach (var Executable in Executables) { OutputFiles.AddRange(TargetToolChain.PostBuild(Executable, BinaryLinkEnvironment)); } } return OutputFiles; }
public static void RegisterPlatformToolChain(CPPTargetPlatform InPlatform, ISTToolChain 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); } }
/// <summary> /// Setup target before build. This method finds dependencies, sets up global environment etc. /// </summary> /// <returns>Special Rocket lib files that are build products.</returns> public List<string> PreBuildSetup(ISTToolChain TargetToolChain) { // Set up the global compile and link environment in GlobalCompileEnvironment and GlobalLinkEnvironment. SetupGlobalEnvironment(); // Setup the target's modules. SetupModules(); // Setup the target's binaries. SetupBinaries(); // Setup the target's plugins SetupPlugins(); var SpecialRocketLibFilesThatAreBuildProducts = new List<string>(); // Add the enabled plugins to the build foreach (PluginInfo BuildPlugin in BuildPlugins) { SpecialRocketLibFilesThatAreBuildProducts.AddRange(AddPlugin(BuildPlugin)); } // Allow the platform to setup binaries/plugins/modules STBuildPlatform.GetBuildPlatform(Platform).SetupBinaries(this); // Describe what's being built. Log.TraceVerbose("Building {0} - {1} - {2} - {3}", AppName, GameName, Platform, Configuration); // Put the non-executable output files (PDB, import library, etc) in the intermediate directory. GlobalLinkEnvironment.Config.IntermediateDirectory = Path.GetFullPath(GlobalCompileEnvironment.Config.OutputDirectory); GlobalLinkEnvironment.Config.OutputDirectory = GlobalLinkEnvironment.Config.IntermediateDirectory; // By default, shadow source files for this target in the root OutputDirectory GlobalLinkEnvironment.Config.LocalShadowDirectory = GlobalLinkEnvironment.Config.OutputDirectory; // Add all of the extra modules, including game modules, that need to be compiled along // with this app. These modules aren't necessarily statically linked against, but may // still be required at runtime in order for the application to load and function properly! AddExtraModules(); // Bind modules for all app binaries. foreach (var Binary in AppBinaries) { Binary.BindModules(); } // Process all referenced modules and create new binaries for DLL dependencies if needed var NewBinaries = new List<STBuildBinary>(); foreach (var Binary in AppBinaries) { // Add binaries for all of our dependent modules var FoundBinaries = Binary.ProcessUnboundModules(AppBinaries[0]); if (FoundBinaries != null) { NewBinaries.AddRange(FoundBinaries); } } AppBinaries.AddRange(NewBinaries); // On Mac AppBinaries paths for non-console targets need to be adjusted to be inside the app bundle if (GlobalLinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Mac && !GlobalLinkEnvironment.Config.bIsBuildingConsoleApplication) { TargetToolChain.FixBundleBinariesPaths(this, AppBinaries); } // If we're building a single module, then find the binary for that module and add it to our target if (OnlyModules.Count > 0) { AppBinaries = FilterOnlyModules(); } else if (STBuildConfiguration.bHotReloadFromIDE) { AppBinaries = FilterGameModules(); } // Filter out binaries that were already built and are just used for linking. We will not build these binaries but we need them for link information { var FilteredBinaries = new List<STBuildBinary>(); foreach (var DLLBinary in AppBinaries) { if (DLLBinary.Config.bAllowCompilation) { FilteredBinaries.Add(DLLBinary); } } // Copy the result into the final list AppBinaries = FilteredBinaries; if (AppBinaries.Count == 0) { throw new BuildException("No modules found to build. All requested binaries were already built."); } } // If we are only interested in platform specific binaries, filter everything else out now if (STBuildTool.GetOnlyPlatformSpecificFor() != STTargetPlatform.Unknown) { var FilteredBinaries = new List<STBuildBinary>(); var OtherThingsWeNeedToBuild = new List<OnlyModule>(); foreach (var DLLBinary in AppBinaries) { if (DLLBinary.Config.bIsCrossTarget) { FilteredBinaries.Add(DLLBinary); bool bIncludeDynamicallyLoaded = false; var AllReferencedModules = DLLBinary.GetAllDependencyModules(bIncludeDynamicallyLoaded, bForceCircular: true); foreach (var Other in AllReferencedModules) { OtherThingsWeNeedToBuild.Add(new OnlyModule(Other.Name)); } } } foreach (var DLLBinary in AppBinaries) { if (!FilteredBinaries.Contains(DLLBinary) && DLLBinary.FindOnlyModule(OtherThingsWeNeedToBuild) != null) { if (!File.Exists(DLLBinary.Config.OutputFilePath)) { throw new BuildException("Module {0} is potentially needed for the {1} platform to work, but it isn't actually compiled. This either needs to be marked as platform specific or made a dynamic dependency of something compiled with the base editor.", DLLBinary.Config.OutputFilePath, STBuildTool.GetOnlyPlatformSpecificFor().ToString()); } } } // Copy the result into the final list AppBinaries = FilteredBinaries; } //@todo.Rocket: Will users be able to rebuild UnrealHeaderTool? NO if (STBuildTool.RunningRocket() && AppName != "UnrealHeaderTool") { var FilteredBinaries = new List<STBuildBinary>(); // Have to do absolute here as this could be a project that is under the root string FullUProjectPath = Path.GetFullPath(STBuildTool.GetUProjectPath()); // We only want to build rocket projects... foreach (var DLLBinary in AppBinaries) { if (Utils.IsFileUnderDirectory(DLLBinary.Config.OutputFilePath, FullUProjectPath)) { FilteredBinaries.Add(DLLBinary); } } // Copy the result into the final list AppBinaries = FilteredBinaries; if (AppBinaries.Count == 0) { throw new BuildException("Rocket: No modules found to build?"); } } return SpecialRocketLibFilesThatAreBuildProducts; }
/** Generates a public manifest file for writing out */ public void GenerateManifest(ISTToolChain ToolChain, List<STBuildBinary> Binaries, CPPTargetPlatform Platform, List<string> SpecialRocketLibFilesThatAreBuildProducts) { string ManifestPath; if (STBuildTool.RunningRocket()) { ManifestPath = Path.Combine(STBuildTool.GetUProjectPath(), BuildConfiguration.BaseIntermediateFolder, "Manifest.xml"); } else { ManifestPath = "../Intermediate/Build/Manifest.xml"; } BuildManifest Manifest = new BuildManifest(); if (STBuildConfiguration.bMergeManifests) { // Load in existing manifest (if any) Manifest = Utils.ReadClass<BuildManifest>(ManifestPath); } STTargetPlatform TargetPlatform = CPPTargetPlatformToSTTargetPlatform(Platform); var BuildPlatform = STBuildPlatform.GetBuildPlatform(TargetPlatform); // Iterate over all the binaries, and add the relevant info to the manifest foreach (STBuildBinary Binary in Binaries) { // Get the platform specific extension for debug info files // Don't add static library files to the manifest as we do not check them into perforce. // However, add them to the manifest when cleaning the project as we do want to delete // them in that case. if (STBuildConfiguration.bCleanProject == false) { if (Binary.Config.Type == STBuildBinaryType.StaticLibrary) { continue; } } string DebugInfoExtension = BuildPlatform.GetDebugInfoExtension(Binary.Config.Type); // Create and add the binary and associated debug info foreach (string OutputFilePath in Binary.Config.OutputFilePaths) { Manifest.AddBuildProduct(OutputFilePath, DebugInfoExtension); } // Add all the stripped debug symbols if (STBuildTool.BuildingRocket() && (Platform == CPPTargetPlatform.Win32 || Platform == CPPTargetPlatform.Win64)) { foreach (string OutputFilePath in Binary.Config.OutputFilePaths) { string StrippedPath = Path.Combine(Path.GetDirectoryName(OutputFilePath), Path.GetFileNameWithoutExtension(OutputFilePath) + "-Stripped" + DebugInfoExtension); Manifest.AddBuildProduct(StrippedPath); } } if (Binary.Config.Type == STBuildBinaryType.Executable && GlobalLinkEnvironment.Config.CanProduceAdditionalConsoleApp && STBuildConfiguration.bBuildEditor) { foreach (string OutputFilePath in Binary.Config.OutputFilePaths) { Manifest.AddBuildProduct(STBuildBinary.GetAdditionalConsoleAppPath(OutputFilePath), DebugInfoExtension); } } ToolChain.AddFilesToManifest(Manifest, Binary); } { string DebugInfoExtension = BuildPlatform.GetDebugInfoExtension(STBuildBinaryType.StaticLibrary); foreach (var RedistLib in SpecialRocketLibFilesThatAreBuildProducts) { Manifest.AddBuildProduct(RedistLib, DebugInfoExtension); } } if (STBuildConfiguration.bCleanProject) { CleanTarget(Binaries, Platform, Manifest); } if (STBuildConfiguration.bGenerateManifest) { Utils.WriteClass<BuildManifest>(Manifest, ManifestPath, ""); } }
/** Builds the target, appending list of output files and returns building result. */ public ECompilationResult Build(ISTToolChain TargetToolChain, out List<FileItem> OutputItems, out List<SHTModuleInfo> UObjectModules, out string EULAViolationWarning) { OutputItems = new List<FileItem>(); UObjectModules = new List<SHTModuleInfo>(); var SpecialRocketLibFilesThatAreBuildProducts = PreBuildSetup(TargetToolChain); EULAViolationWarning = !ProjectFileGenerator.bGenerateProjectFiles ? CheckForEULAViolation() : null; // If we're compiling monolithic, make sure the executable knows about all referenced modules if (ShouldCompileMonolithic()) { var ExecutableBinary = AppBinaries[0]; // Search through every binary for dependencies. When building plugin binaries that reference optional engine modules, // we still need to link them into the executable. foreach (STBuildBinary Binary in AppBinaries) { var AllReferencedModules = Binary.GetAllDependencyModules(bIncludeDynamicallyLoaded: true, bForceCircular: true); foreach (var CurModule in AllReferencedModules) { if (CurModule.Binary == null || CurModule.Binary == ExecutableBinary) { ExecutableBinary.AddModule(CurModule.Name); } } } bool IsCurrentPlatform; if (Utils.IsRunningOnMono) { IsCurrentPlatform = Platform == STTargetPlatform.Mac; } else { IsCurrentPlatform = Platform == STTargetPlatform.Win64 || Platform == STTargetPlatform.Win32; } if ((TargetRules.IsAGame(TargetType) || (TargetType == TargetRules.TargetType.Server)) && IsCurrentPlatform) { // The hardcoded engine directory needs to be a relative path to match the normal EngineDir format. Not doing so breaks the network file system (TTP#315861). string OutputFilePath = ExecutableBinary.Config.OutputFilePath; if (Platform == STTargetPlatform.Mac && OutputFilePath.Contains(".app/Contents/MacOS")) { OutputFilePath = OutputFilePath.Substring(0, OutputFilePath.LastIndexOf(".app/Contents/MacOS") + 4); } string EnginePath = Utils.CleanDirectorySeparators(Utils.MakePathRelativeTo(ProjectFileGenerator.EngineRelativePath, Path.GetDirectoryName(OutputFilePath)), '/'); if (EnginePath.EndsWith("/") == false) { EnginePath += "/"; } GlobalCompileEnvironment.Config.Definitions.Add("UE_ENGINE_DIRECTORY=" + EnginePath); } // Set the define for the project name. This allows the executable to locate the correct project file to use, which may not be the same as the game name or target. if (STBuildTool.HasUProjectFile()) { string ProjectName = Path.GetFileNameWithoutExtension(STBuildTool.GetUProjectFile()); GlobalCompileEnvironment.Config.Definitions.Add(String.Format("UE_PROJECT_NAME={0}", ProjectName)); } // Generate static libraries for monolithic games in Rocket if ((STBuildTool.BuildingRocket() || STBuildTool.RunningRocket()) && TargetRules.IsAGame(TargetType)) { List<STBuildModule> Modules = ExecutableBinary.GetAllDependencyModules(true, false); foreach (STBuildModuleCPP Module in Modules.OfType<STBuildModuleCPP>()) { if (Utils.IsFileUnderDirectory(Module.ModuleDirectory, BuildConfiguration.RelativeEnginePath) && Module.Binary == ExecutableBinary) { STTargetConfiguration LibraryConfiguration = (Configuration == STTargetConfiguration.DebugGame) ? STTargetConfiguration.Development : Configuration; Module.RedistStaticLibraryPaths = MakeBinaryPaths("", "UE4Game-Redist-" + Module.Name, Platform, LibraryConfiguration, STBuildBinaryType.StaticLibrary, TargetType, null, AppName); Module.bBuildingRedistStaticLibrary = STBuildTool.BuildingRocket(); if (Module.bBuildingRedistStaticLibrary) { SpecialRocketLibFilesThatAreBuildProducts.AddRange(Module.RedistStaticLibraryPaths); } } } } } // On Mac and Linux we have actions that should be executed after all the binaries are created if (GlobalLinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Mac || GlobalLinkEnvironment.Config.Target.Platform == CPPTargetPlatform.Linux) { TargetToolChain.SetupBundleDependencies(AppBinaries, GameName); } // Generate the external file list if (STBuildConfiguration.bGenerateExternalFileList) { GenerateExternalFileList(); return ECompilationResult.Succeeded; } // If we're only generating the manifest, return now if (STBuildConfiguration.bGenerateManifest || STBuildConfiguration.bCleanProject) { GenerateManifest(TargetToolChain, AppBinaries, GlobalLinkEnvironment.Config.Target.Platform, SpecialRocketLibFilesThatAreBuildProducts); if (!BuildConfiguration.bXGEExport) { return ECompilationResult.Succeeded; } } // We don't need to worry about shared PCH headers when only generating project files. This is // just an optimization if (!ProjectFileGenerator.bGenerateProjectFiles) { GlobalCompileEnvironment.SharedPCHHeaderFiles = FindSharedPCHHeaders(); } if ((BuildConfiguration.bXGEExport && STBuildConfiguration.bGenerateManifest) || (!STBuildConfiguration.bGenerateManifest && !STBuildConfiguration.bCleanProject && !ProjectFileGenerator.bGenerateProjectFiles)) { var UObjectDiscoveryStartTime = DateTime.UtcNow; // Figure out which modules have UObjects that we may need to generate headers for foreach (var Binary in AppBinaries) { var LocalUObjectModules = UObjectModules; // For lambda access var DependencyModules = Binary.GetAllDependencyModules(bIncludeDynamicallyLoaded: false, bForceCircular: false); foreach (var DependencyModuleCPP in DependencyModules.OfType<STBuildModuleCPP>().Where(CPPModule => !LocalUObjectModules.Any(Module => Module.ModuleName == CPPModule.Name))) { if (!DependencyModuleCPP.bIncludedInTarget) { throw new BuildException("Expecting module {0} to have bIncludeInTarget set", DependencyModuleCPP.Name); } var UHTModuleInfo = DependencyModuleCPP.GetUHTModuleInfo(); if (UHTModuleInfo.PublicUObjectClassesHeaders.Count > 0 || UHTModuleInfo.PrivateUObjectHeaders.Count > 0 || UHTModuleInfo.PublicUObjectHeaders.Count > 0) { // If we've got this far and there are no source files then it's likely we're running Rocket and ignoring // engine files, so we don't need a .generated.cpp either STBuildModuleCPP.AutoGenerateCppInfoClass.BuildInfoClass BuildInfo = null; UHTModuleInfo.GeneratedCPPFilenameBase = Path.Combine(STBuildModuleCPP.GetGeneratedCodeDirectoryForModule(this, UHTModuleInfo.ModuleDirectory, UHTModuleInfo.ModuleName), UHTModuleInfo.ModuleName) + ".generated"; if (DependencyModuleCPP.SourceFilesToBuild.Count != 0) { BuildInfo = new STBuildModuleCPP.AutoGenerateCppInfoClass.BuildInfoClass(UHTModuleInfo.GeneratedCPPFilenameBase + "*.cpp"); } DependencyModuleCPP.AutoGenerateCppInfo = new STBuildModuleCPP.AutoGenerateCppInfoClass(BuildInfo); // If we're running in "gather" mode only, we'll go ahead and cache PCH information for each module right now, so that we don't // have to do it in the assembling phase. It's OK for gathering to take a bit longer, even if UObject headers are not out of // date in order to save a lot of time in the assembling runs. UHTModuleInfo.PCH = ""; if (STBuildTool.IsGatheringBuild && !STBuildTool.IsAssemblingBuild) { // 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 ModuleCompileEnvironment = DependencyModuleCPP.CreateModuleCompileEnvironment(GlobalCompileEnvironment); DependencyModuleCPP.CachePCHUsageForModuleSourceFiles(ModuleCompileEnvironment); if (DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile != null) { UHTModuleInfo.PCH = DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile.AbsolutePath; } } UObjectModules.Add(UHTModuleInfo); Log.TraceVerbose("Detected UObject module: " + UHTModuleInfo.ModuleName); } } } if (BuildConfiguration.bPrintPerformanceInfo) { double UObjectDiscoveryTime = (DateTime.UtcNow - UObjectDiscoveryStartTime).TotalSeconds; Trace.TraceInformation("UObject discovery time: " + UObjectDiscoveryTime + "s"); } // @todo ubtmake: Even in Gather mode, we need to run UHT to make sure the files exist for the static action graph to be setup correctly. This is because UHT generates .cpp // files that are injected as top level prerequisites. If UHT only emitted included header files, we wouldn't need to run it during the Gather phase at all. if (UObjectModules.Count > 0) { // Execute the header tool string ModuleInfoFileName = Path.GetFullPath(Path.Combine(ProjectIntermediateDirectory, "UnrealHeaderTool.manifest")); ECompilationResult UHTResult = ECompilationResult.OtherCompilationError; if (!ExternalExecution.ExecuteHeaderToolIfNecessary(this, GlobalCompileEnvironment, UObjectModules, ModuleInfoFileName, ref UHTResult)) { Log.TraceInformation("UnrealHeaderTool failed for target '" + GetTargetName() + "' (platform: " + Platform.ToString() + ", module info: " + ModuleInfoFileName + ")."); return UHTResult; } } } if (ShouldCompileMonolithic() && !ProjectFileGenerator.bGenerateProjectFiles && Rules != null && TargetType != TargetRules.TargetType.Program) { // All non-program monolithic binaries implicitly depend on all static plugin libraries so they are always linked appropriately // In order to do this, we create a new module here with a cpp file we emit that invokes an empty function in each library. // If we do not do this, there will be no static initialization for libs if no symbols are referenced in them. CreateLinkerFixupsCPPFile(); } // Build the target's binaries. foreach (var Binary in AppBinaries) { OutputItems.AddRange(Binary.Build(TargetToolChain, GlobalCompileEnvironment, GlobalLinkEnvironment)); } return ECompilationResult.Succeeded; }