/// <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; }
// STBuildModule interface. public override List<FileItem> Compile(CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment, bool bCompileMonolithic) { var 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 (var PrivateAssemblyReference in PrivateAssemblyReferences) { ModuleCLREnvironment.AddPrivateAssembly(PrivateAssemblyReference); } // Pass the CLR compilation environment to the standard C++ module compilation code. return base.Compile(GlobalCompileEnvironment, ModuleCLREnvironment, bCompileMonolithic); }
/// <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(STBuildTarget Target, string PCHHeaderNameInCode, FileItem PrecompiledHeaderIncludeFilename, CPPEnvironment ProjectCPPEnvironment, string OutputDirectory, 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 var ToolChain = STToolChain.GetPlatformToolChain(ProjectCPPEnvironment.Config.Target.Platform); string PCHGuardDefine = Path.GetFileNameWithoutExtension(PrecompiledHeaderIncludeFilename.AbsolutePath).ToUpper(); 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); string DummyPath = Path.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. var ProjectPCHEnvironment = ProjectCPPEnvironment.DeepCopy(); ProjectPCHEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Create; ProjectPCHEnvironment.Config.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFilename.AbsolutePath; ProjectPCHEnvironment.Config.PCHHeaderNameInCode = PCHHeaderNameInCode; ProjectPCHEnvironment.Config.OutputDirectory = OutputDirectory; if (!bAllowDLLExports) { for (var CurDefinitionIndex = 0; CurDefinitionIndex < ProjectPCHEnvironment.Config.Definitions.Count; ++CurDefinitionIndex) { // We change DLLEXPORT to DLLIMPORT for "shared" PCH headers var 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 ProjectPCHEnvironment.CompileFiles(Target, new List<FileItem>() { DummyPCH }, ModuleName); } throw new BuildException("Couldn't find PCH file \"{0}\".", PrecompiledHeaderIncludeFilename); }
/// <summary> /// Creates a compile environment from a base environment based on the module settings. /// </summary> /// <param name="BaseCompileEnvironment">An existing environment to base the module compile environment on.</param> /// <returns>The new module compile environment.</returns> public CPPEnvironment CreateModuleCompileEnvironment(CPPEnvironment BaseCompileEnvironment) { var Result = BaseCompileEnvironment.DeepCopy(); // Override compile environment Result.Config.bFasterWithoutUnity = bFasterWithoutUnity; Result.Config.OptimizeCode = OptimizeCode; Result.Config.bUseRTTI = bUseRTTI; Result.Config.bEnableBufferSecurityChecks = bEnableBufferSecurityChecks; Result.Config.bFasterWithoutUnity = bFasterWithoutUnity; Result.Config.MinFilesUsingPrecompiledHeaderOverride = MinFilesUsingPrecompiledHeaderOverride; Result.Config.bEnableExceptions = bEnableExceptions; Result.Config.bUseStaticCRT = (Target.Rules != null && Target.Rules.bUseStaticCRT); Result.Config.OutputDirectory = Path.Combine(Binary.Config.IntermediateDirectory, Name); // Switch the optimization flag if we're building a game module. Also pass the definition for building in DebugGame along (see ModuleManager.h for notes). if (Target.Configuration == STTargetConfiguration.DebugGame) { PluginInfo Plugin = Plugins.GetPluginInfoForModule(Name); if ((Plugin != null && Plugin.LoadedFrom == PluginInfo.LoadedFromType.GameProject) || Type == STBuildModuleType.Game) { Result.Config.Target.Configuration = CPPTargetConfiguration.Debug; Result.Config.Definitions.Add("UE_BUILD_DEVELOPMENT_WITH_DEBUGGAME=1"); } } // Add the module's private definitions. Result.Config.Definitions.AddRange(Definitions); // Setup the compile environment for the module. SetupPrivateCompileEnvironment(ref Result.Config.CPPIncludeInfo.IncludePaths, ref Result.Config.CPPIncludeInfo.SystemIncludePaths, ref Result.Config.Definitions, ref Result.Config.AdditionalFrameworks); return Result; }