Exemple #1
0
        /// <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);
        }
Exemple #2
0
        private PrecompileHeaderEnvironment ApplySharedPCH(CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment, CPPEnvironment ModuleCompileEnvironment, List<FileItem> CPPFiles, ref FileItem SharedPCHHeaderFile)
        {
            // Check to see if we have a PCH header already setup that we can use
            var SharedPCHHeaderFileCopy = SharedPCHHeaderFile;
            var SharedPCHEnvironment = GlobalCompileEnvironment.SharedPCHEnvironments.Find(Env => Env.PrecompiledHeaderIncludeFilename == SharedPCHHeaderFileCopy);
            if (SharedPCHEnvironment == null)
            {
                return null;
            }

            // Don't mix CLR modes
            if (SharedPCHEnvironment.CLRMode != ModuleCompileEnvironment.Config.CLRMode)
            {
                Log.TraceVerbose("Module {0} cannot use existing Shared PCH '{1}' (from module '{2}') because CLR modes don't match", Name, SharedPCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath, SharedPCHEnvironment.ModuleName);
                SharedPCHHeaderFile = null;
                return null;
            }

            // Don't mix non-optimized code with optimized code (PCHs won't be compatible)
            var SharedPCHCodeOptimization = SharedPCHEnvironment.OptimizeCode;
            var ModuleCodeOptimization = ModuleCompileEnvironment.Config.OptimizeCode;

            if (CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug)
            {
                if (SharedPCHCodeOptimization == ModuleRules.CodeOptimization.InNonDebugBuilds)
                {
                    SharedPCHCodeOptimization = ModuleRules.CodeOptimization.Always;
                }

                if (ModuleCodeOptimization == ModuleRules.CodeOptimization.InNonDebugBuilds)
                {
                    ModuleCodeOptimization = ModuleRules.CodeOptimization.Always;
                }
            }

            if (SharedPCHCodeOptimization != ModuleCodeOptimization)
            {
                Log.TraceVerbose("Module {0} cannot use existing Shared PCH '{1}' (from module '{2}') because optimization levels don't match", Name, SharedPCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath, SharedPCHEnvironment.ModuleName);
                SharedPCHHeaderFile = null;
                return null;
            }

            return SharedPCHEnvironment;
        }
Exemple #3
0
        /// <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;
        }
Exemple #4
0
        /// <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(
            STBuildTarget Target,
            List<FileItem> CPPFiles,
            CPPEnvironment CompileEnvironment,
            string BaseName
            )
        {
            var ToolChain = STToolChain.GetPlatformToolChain(CompileEnvironment.Config.Target.Platform);

            var BuildPlatform = STBuildPlatform.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 fastubt urgent: Fails when building remotely for Mac because unity .cpp has an include for a PCH on the REMOTE machine
                STBuildModuleCPP.CachePCHUsageForModuleSourceFile(Target, CompileEnvironment, UnityCPPFile);
            }

            return UnityCPPFiles;
        }
Exemple #5
0
 /// <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);
Exemple #6
0
        protected void AddPrerequisiteSourceFile(STBuildTarget Target, ISTBuildPlatform BuildPlatform, CPPEnvironment CompileEnvironment, FileItem SourceFile, List<FileItem> PrerequisiteItems)
        {
            PrerequisiteItems.Add(SourceFile);

            var RemoteThis = this as RemoteToolChain;
            bool bAllowUploading = RemoteThis != null && BuildHostPlatform.Current.Platform != STTargetPlatform.Mac;	// Don't use remote features when compiling from a Mac
            if (bAllowUploading)
            {
                RemoteThis.QueueFileForBatchUpload(SourceFile);
            }

            if (!BuildConfiguration.bUseExperimentalFastBuildIteration)	// In fast build iteration mode, we'll gather includes later on
            {
                // @todo fastubt: What if one of the prerequisite files has become missing since it was updated in our cache? (usually, because a coder eliminated the source file)
                //		-> Two CASES:
                //				1) NOT WORKING: Non-unity file went away (SourceFile in this context).  That seems like an existing old use case.  Compile params or Response file should have changed?
                //				2) WORKING: Indirect file went away (unity'd original source file or include).  This would return a file that no longer exists and adds to the prerequiteitems list
                var IncludedFileList = CPPEnvironment.FindAndCacheAllIncludedFiles(Target, SourceFile, BuildPlatform, CompileEnvironment.Config.CPPIncludeInfo, bOnlyCachedDependencies: BuildConfiguration.bUseExperimentalFastDependencyScan);
                foreach (FileItem IncludedFile in IncludedFileList)
                {
                    PrerequisiteItems.Add(IncludedFile);

                    if (bAllowUploading &&
                        !BuildConfiguration.bUseExperimentalFastDependencyScan)	// With fast dependency scanning, we will not have an exhaustive list of dependencies here.  We rely on PostCodeGeneration() to upload these files.
                    {
                        RemoteThis.QueueFileForBatchUpload(IncludedFile);
                    }
                }
            }
        }
Exemple #7
0
 public abstract CPPOutput CompileCPPFiles(STBuildTarget Target, CPPEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName);
Exemple #8
0
 /** Compiles the module, and returns a list of files output by the compiler. */
 public abstract List<FileItem> Compile(CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment, bool bCompileMonolithic);
Exemple #9
0
        public override CPPOutput CompileCPPFiles(STBuildTarget Target, CPPEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName)
        {
            var EnvVars = VCEnvironment.SetEnvironment(CompileEnvironment.Config.Target.Platform);

            StringBuilder Arguments = new StringBuilder();
            AppendCLArguments_Global(CompileEnvironment, EnvVars, Arguments);

            // Add include paths to the argument list.
            if (!BuildConfiguration.bRunUnrealCodeAnalyzer)
            {
                foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths)
                {
                    Arguments.AppendFormat(" /I \"{0}\"", IncludePath);
                }
            }
            else
            {
                foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths)
                {
                    Arguments.AppendFormat(" /I \"{0}\"", System.IO.Path.GetFullPath(IncludePath));
                }
            }
            foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths)
            {
                if (WindowsPlatform.bCompileWithClang)
                {
                    // @todo Clang: Clang uses a special command-line syntax for system headers.  This is used for two reasons.  The first is that Clang will automatically
                    // suppress compiler warnings in headers found in these directories, such as the DirectX SDK headers.  The other reason this is important is in the case
                    // where there the same header include path is passed as both a regular include path and a system include path (extracted from INCLUDE environment).  In
                    // this case Clang will ignore any earlier occurrence of the include path, preventing a system header include path from overriding a different system
                    // include path set later on by a module.  NOTE: When passing "-Xclang", these options will always appear at the end of the command-line string, meaning
                    // they will be forced to appear *after* all environment-variable-extracted includes.  This is technically okay though.
                    Arguments.AppendFormat(" -Xclang -internal-isystem -Xclang \"{0}\"", IncludePath);
                }
                else
                {
                    Arguments.AppendFormat(" /I \"{0}\"", IncludePath);
                }
            }

            if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled)
            {
                // Add .NET framework assembly paths.  This is needed so that C++/CLI projects
                // can reference assemblies with #using, without having to hard code a path in the
                // .cpp file to the assembly's location.
                foreach (string AssemblyPath in CompileEnvironment.Config.SystemDotNetAssemblyPaths)
                {
                    Arguments.AppendFormat(" /AI \"{0}\"", AssemblyPath);
                }

                // Add explicit .NET framework assembly references
                foreach (string AssemblyName in CompileEnvironment.Config.FrameworkAssemblyDependencies)
                {
                    Arguments.AppendFormat(" /FU \"{0}\"", AssemblyName);
                }

                // Add private assembly references
                foreach (PrivateAssemblyInfo CurAssemblyInfo in CompileEnvironment.PrivateAssemblyDependencies)
                {
                    Arguments.AppendFormat(" /FU \"{0}\"", CurAssemblyInfo.FileItem.AbsolutePath);
                }
            }

            // Add preprocessor definitions to the argument list.
            foreach (string Definition in CompileEnvironment.Config.Definitions)
            {
                // Escape all quotation marks so that they get properly passed with the command line.
                var DefinitionArgument = Definition.Contains("\"") ? Definition.Replace("\"", "\\\"") : Definition;
                Arguments.AppendFormat(" /D \"{0}\"", DefinitionArgument);
            }

            var BuildPlatform = STBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform);

            // Create a compile action for each source file.
            CPPOutput Result = new CPPOutput();
            foreach (FileItem SourceFile in SourceFiles)
            {
                Action CompileAction = new Action(ActionType.Compile);
                CompileAction.CommandDescription = "Compile";

                StringBuilder FileArguments = new StringBuilder();
                bool bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C";

                // Add the C++ source file and its included files to the prerequisite item list.
                AddPrerequisiteSourceFile(Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems);

                // If this is a CLR file then make sure our dependent assemblies are added as prerequisites
                if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled)
                {
                    foreach (PrivateAssemblyInfo CurPrivateAssemblyDependency in CompileEnvironment.PrivateAssemblyDependencies)
                    {
                        CompileAction.PrerequisiteItems.Add(CurPrivateAssemblyDependency.FileItem);
                    }
                }

                if (WindowsPlatform.bCompileWithClang)
                {
                    CompileAction.OutputEventHandler = new DataReceivedEventHandler(ClangCompilerOutputFormatter);
                }

                bool bEmitsObjectFile = true;
                if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
                {
                    // Generate a CPP File that just includes the precompiled header.
                    string PCHCPPFilename = "PCH." + ModuleName + "." + Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename) + ".cpp";
                    string PCHCPPPath = Path.Combine(CompileEnvironment.Config.OutputDirectory, PCHCPPFilename);
                    FileItem PCHCPPFile = FileItem.CreateIntermediateTextFile(
                        PCHCPPPath,
                        string.Format("#include \"{0}\"\r\n", CompileEnvironment.Config.PrecompiledHeaderIncludeFilename)
                        );

                    // Make sure the original source directory the PCH header file existed in is added as an include
                    // path -- it might be a private PCH header and we need to make sure that its found!
                    string OriginalPCHHeaderDirectory = Path.GetDirectoryName(SourceFile.AbsolutePath);
                    FileArguments.AppendFormat(" /I \"{0}\"", OriginalPCHHeaderDirectory);

                    var PrecompiledFileExtension = STBuildPlatform.BuildPlatformDictionary[STTargetPlatform.Win64].GetBinaryExtension(STBuildBinaryType.PrecompiledHeader);
                    // Add the precompiled header file to the produced items list.
                    FileItem PrecompiledHeaderFile = FileItem.GetItemByPath(
                        Path.Combine(
                            CompileEnvironment.Config.OutputDirectory,
                        Path.GetFileName(SourceFile.AbsolutePath) + PrecompiledFileExtension
                            )
                        );
                    CompileAction.ProducedItems.Add(PrecompiledHeaderFile);
                    Result.PrecompiledHeaderFile = PrecompiledHeaderFile;

                    // @todo clang: Ideally clang-cl would support the regular MSVC arguments for PCH files, and we wouldn't need to do this manually
                    if (WindowsPlatform.bCompileWithClang)
                    {
                        // Tell Clang to generate a PCH header
                        FileArguments.Append(" -Xclang -x -Xclang c++-header");	// @todo clang: Doesn't work due to Clang-cl overriding us at the end of the command-line (see -### option output)
                        FileArguments.AppendFormat(" /Fo\"{0}\"", PrecompiledHeaderFile.AbsolutePath);

                        // Clang PCH generation doesn't create an .obj file to link in, unlike MSVC
                        bEmitsObjectFile = false;
                    }
                    else
                    {
                        // Add the parameters needed to compile the precompiled header file to the command-line.
                        FileArguments.AppendFormat(" /Yc\"{0}\"", CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
                        FileArguments.AppendFormat(" /Fp\"{0}\"", PrecompiledHeaderFile.AbsolutePath);

                        // If we're creating a PCH that will be used to compile source files for a library, we need
                        // the compiled modules to retain a reference to PCH's module, so that debugging information
                        // will be included in the library.  This is also required to avoid linker warning "LNK4206"
                        // when linking an application that uses this library.
                        if (CompileEnvironment.Config.bIsBuildingLibrary)
                        {
                            // NOTE: The symbol name we use here is arbitrary, and all that matters is that it is
                            // unique per PCH module used in our library
                            string FakeUniquePCHSymbolName = Path.GetFileNameWithoutExtension(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
                            FileArguments.AppendFormat(" /Yl{0}", FakeUniquePCHSymbolName);
                        }
                    }

                    FileArguments.AppendFormat(" \"{0}\"", PCHCPPFile.AbsolutePath);

                    CompileAction.StatusDescription = PCHCPPFilename;
                }
                else
                {
                    if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
                    {
                        CompileAction.bIsUsingPCH = true;
                        CompileAction.PrerequisiteItems.Add(CompileEnvironment.PrecompiledHeaderFile);

                        if (WindowsPlatform.bCompileWithClang)
                        {
                            // NOTE: With Clang, PCH headers are ALWAYS forcibly included!
                            // NOTE: This needs to be before the other include paths to ensure Clang uses it instead of the source header file.
                            FileArguments.AppendFormat(" /FI\"{0}\"", Path.ChangeExtension(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath, null));
                        }
                        else
                        {
                            FileArguments.AppendFormat(" /Yu\"{0}\"", CompileEnvironment.Config.PCHHeaderNameInCode);
                            FileArguments.AppendFormat(" /Fp\"{0}\"", CompileEnvironment.PrecompiledHeaderFile.AbsolutePath);

                            // Is it unsafe to always force inclusion?  Clang is doing it, and .generated.cpp files
                            // won't work otherwise, because they're not located in the context of the module,
                            // so they can't access the module's PCH without an absolute path.
                            //if( CompileEnvironment.Config.bForceIncludePrecompiledHeader )
                            {
                                // Force include the precompiled header file.  This is needed because we may have selected a
                                // precompiled header that is different than the first direct include in the C++ source file, but
                                // we still need to make sure that our precompiled header is the first thing included!
                                FileArguments.AppendFormat(" /FI\"{0}\"", CompileEnvironment.Config.PCHHeaderNameInCode);
                            }
                        }
                    }

                    // UnrealCodeAnalyzer requires compiled file name to be first argument.
                    if (!BuildConfiguration.bRunUnrealCodeAnalyzer)
                    {
                        // Add the source file path to the command-line.
                        FileArguments.AppendFormat(" \"{0}\"", SourceFile.AbsolutePath);
                    }

                    CompileAction.StatusDescription = Path.GetFileName(SourceFile.AbsolutePath);
                }

                if (bEmitsObjectFile)
                {
                    var ObjectFileExtension = STBuildPlatform.BuildPlatformDictionary[STTargetPlatform.Win64].GetBinaryExtension(STBuildBinaryType.Object);
                    // Add the object file to the produced item list.
                    FileItem ObjectFile = FileItem.GetItemByPath(
                        Path.Combine(
                            CompileEnvironment.Config.OutputDirectory,
                            Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension
                            )
                        );
                    CompileAction.ProducedItems.Add(ObjectFile);
                    Result.ObjectFiles.Add(ObjectFile);
                    // UnrealCodeAnalyzer requires specific position of output file.
                    if (!BuildConfiguration.bRunUnrealCodeAnalyzer)
                    {
                        FileArguments.AppendFormat(" /Fo\"{0}\"", ObjectFile.AbsolutePath);
                    }
                }

                // Create PDB files if we were configured to do that.
                //
                // Also, when debug info is off and XGE is enabled, force PDBs, otherwise it will try to share
                // a PDB file, which causes PCH creation to be serial rather than parallel (when debug info is disabled)
                //		--> See https://udn.epicgames.com/lists/showpost.php?id=50619&list=unprog3
                if (BuildConfiguration.bUsePDBFiles ||
                    (BuildConfiguration.bAllowXGE && !CompileEnvironment.Config.bCreateDebugInfo))
                {
                    string PDBFileName;
                    bool bActionProducesPDB = false;

                    // All files using the same PCH are required to share the same PDB that was used when compiling the PCH
                    if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
                    {
                        PDBFileName = "PCH." + ModuleName + "." + Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
                    }
                    // Files creating a PCH use a PDB per file.
                    else if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
                    {
                        PDBFileName = "PCH." + ModuleName + "." + Path.GetFileName(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
                        bActionProducesPDB = true;
                    }
                    // Ungrouped C++ files use a PDB per file.
                    else if (!bIsPlainCFile)
                    {
                        PDBFileName = Path.GetFileName(SourceFile.AbsolutePath);
                        bActionProducesPDB = true;
                    }
                    // Group all plain C files that doesn't use PCH into the same PDB
                    else
                    {
                        PDBFileName = "MiscPlainC";
                    }

                    {
                        // Specify the PDB file that the compiler should write to.
                        FileItem PDBFile = FileItem.GetItemByPath(
                                Path.Combine(
                                    CompileEnvironment.Config.OutputDirectory,
                                    PDBFileName + ".pdb"
                                    )
                                );
                        FileArguments.AppendFormat(" /Fd\"{0}\"", PDBFile.AbsolutePath);

                        // Only use the PDB as an output file if we want PDBs and this particular action is
                        // the one that produces the PDB (as opposed to no debug info, where the above code
                        // is needed, but not the output PDB, or when multiple files share a single PDB, so
                        // only the action that generates it should count it as output directly)
                        if (BuildConfiguration.bUsePDBFiles && bActionProducesPDB)
                        {
                            CompileAction.ProducedItems.Add(PDBFile);
                            Result.DebugDataFiles.Add(PDBFile);
                        }
                    }
                }

                // Add C or C++ specific compiler arguments.
                if (bIsPlainCFile)
                {
                    AppendCLArguments_C(FileArguments);
                }
                else
                {
                    AppendCLArguments_CPP(CompileEnvironment, FileArguments);
                }

                CompileAction.WorkingDirectory = Path.GetFullPath(".");
                if (BuildConfiguration.bRunUnrealCodeAnalyzer)
                {
                    CompileAction.CommandPath = System.IO.Path.Combine(CompileAction.WorkingDirectory, @"..", @"Binaries", @"Win32", @"UnrealCodeAnalyzer.exe");
                }
                else
                {
                    CompileAction.CommandPath = EnvVars.CompilerPath;
                }

                if (!WindowsPlatform.bCompileWithClang || !BuildConfiguration.bRunUnrealCodeAnalyzer)
                {
                    CompileAction.bIsVCCompiler = true;
                }

                string UnrealCodeAnalyzerArguments = "";
                if (BuildConfiguration.bRunUnrealCodeAnalyzer)
                {
                    var ObjectFileExtension = @".includes";
                    FileItem ObjectFile = FileItem.GetItemByPath(Path.Combine(CompileEnvironment.Config.OutputDirectory, Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension));
                    var ClangPath = System.IO.Path.Combine(CompileAction.WorkingDirectory, @"ThirdParty", @"llvm", @"3.5.0", @"bin", @"vs2013", @"x86", @"release", @"clang++.exe");
                    UnrealCodeAnalyzerArguments = @"-CreateIncludeFiles " + SourceFile.AbsolutePath + @" -OutputFile=""" + ObjectFile.AbsolutePath + @""" -- " + ClangPath + @" --driver-mode=cl ";
                }

                CompileAction.CommandArguments = UnrealCodeAnalyzerArguments + Arguments.ToString() + FileArguments.ToString() + CompileEnvironment.Config.AdditionalArguments;

                if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
                {
                    Log.TraceVerbose("Creating PCH: " + CompileEnvironment.Config.PrecompiledHeaderIncludeFilename);
                    Log.TraceVerbose("     Command: " + CompileAction.CommandArguments);
                }
                else
                {
                    Log.TraceVerbose("   Compiling: " + CompileAction.StatusDescription);
                    Log.TraceVerbose("     Command: " + CompileAction.CommandArguments);
                }

                if (WindowsPlatform.bCompileWithClang || BuildConfiguration.bRunUnrealCodeAnalyzer)
                {
                    // Clang doesn't print the file names by default, so we'll do it ourselves
                    CompileAction.bShouldOutputStatusDescription = true;
                }
                else
                {
                    // VC++ always outputs the source file name being compiled, so we don't need to emit this ourselves
                    CompileAction.bShouldOutputStatusDescription = false;
                }

                // Don't farm out creation of precompiled headers as it is the critical path task.
                CompileAction.bCanExecuteRemotely =
                    CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create ||
                    BuildConfiguration.bAllowRemotelyCompiledPCHs
                    ;

                // @todo: XGE has problems remote compiling C++/CLI files that use .NET Framework 4.0
                if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled)
                {
                    CompileAction.bCanExecuteRemotely = false;
                }
            }
            return Result;
        }
Exemple #10
0
        public override CPPOutput CompileRCFiles(STBuildTarget Target, CPPEnvironment Environment, List<FileItem> RCFiles)
        {
            var EnvVars = VCEnvironment.SetEnvironment(Environment.Config.Target.Platform);

            CPPOutput Result = new CPPOutput();

            var BuildPlatform = STBuildPlatform.GetBuildPlatformForCPPTargetPlatform(Environment.Config.Target.Platform);

            foreach (FileItem RCFile in RCFiles)
            {
                Action CompileAction = new Action(ActionType.Compile);
                CompileAction.CommandDescription = "Resource";
                CompileAction.WorkingDirectory = Path.GetFullPath(".");
                CompileAction.CommandPath = EnvVars.ResourceCompilerPath;
                CompileAction.StatusDescription = Path.GetFileName(RCFile.AbsolutePath);

                // Resource tool can run remotely if possible
                CompileAction.bCanExecuteRemotely = true;

                if (WindowsPlatform.bCompileWithClang)
                {
                    CompileAction.OutputEventHandler = new DataReceivedEventHandler(ClangCompilerOutputFormatter);
                }

                // Suppress header spew
                CompileAction.CommandArguments += " /nologo";

                // If we're compiling for 64-bit Windows, also add the _WIN64 definition to the resource
                // compiler so that we can switch on that in the .rc file using #ifdef.
                if (Environment.Config.Target.Platform == CPPTargetPlatform.Win64)
                {
                    CompileAction.CommandArguments += " /D _WIN64";
                }

                // When targeting Windows XP with Visual Studio 2012+, we need to tell the compiler to use the older Windows SDK that works
                // with Windows XP (http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx)
                if (WindowsPlatform.IsWindowsXPSupported())
                {
                    CompileAction.CommandArguments += " /D_USING_V110_SDK71_";
                }

                // Language
                CompileAction.CommandArguments += " /l 0x409";

                // Include paths.
                foreach (string IncludePath in Environment.Config.CPPIncludeInfo.IncludePaths)
                {
                    CompileAction.CommandArguments += string.Format(" /i \"{0}\"", IncludePath);
                }

                // System include paths.
                foreach (var SystemIncludePath in Environment.Config.CPPIncludeInfo.SystemIncludePaths)
                {
                    CompileAction.CommandArguments += string.Format(" /i \"{0}\"", SystemIncludePath);
                }

                // Preprocessor definitions.
                foreach (string Definition in Environment.Config.Definitions)
                {
                    CompileAction.CommandArguments += string.Format(" /d \"{0}\"", Definition);
                }

                // Add the RES file to the produced item list.
                FileItem CompiledResourceFile = FileItem.GetItemByPath(
                    Path.Combine(
                        Environment.Config.OutputDirectory,
                        Path.GetFileName(RCFile.AbsolutePath) + ".res"
                        )
                    );
                CompileAction.ProducedItems.Add(CompiledResourceFile);
                CompileAction.CommandArguments += string.Format(" /fo \"{0}\"", CompiledResourceFile.AbsolutePath);
                Result.ObjectFiles.Add(CompiledResourceFile);

                // Add the RC file as a prerequisite of the action.
                CompileAction.CommandArguments += string.Format(" \"{0}\"", RCFile.AbsolutePath);

                // Add the C++ source file and its included files to the prerequisite item list.
                AddPrerequisiteSourceFile(Target, BuildPlatform, Environment, RCFile, CompileAction.PrerequisiteItems);
            }

            return Result;
        }
Exemple #11
0
        public override CPPOutput CompileCPPFiles(STBuildTarget Target, CPPEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName)
        {
            string Arguments = GetCompileArguments_Global(CompileEnvironment);
            string PCHArguments = "";

            if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
            {
                // Add the precompiled header file's path to the include path so GCC can find it.
                // This needs to be before the other include paths to ensure GCC uses it instead of the source header file.
                var PrecompiledFileExtension = STBuildPlatform.BuildPlatformDictionary[STTargetPlatform.IOS].GetBinaryExtension(STBuildBinaryType.PrecompiledHeader);
                PCHArguments += string.Format(" -include \"{0}\"", ConvertPath(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath.Replace(PrecompiledFileExtension, "")));
            }

            // Add include paths to the argument list.
            HashSet<string> AllIncludes = new HashSet<string>(CompileEnvironment.Config.CPPIncludeInfo.IncludePaths);
            AllIncludes.UnionWith(CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths);
            foreach (string IncludePath in AllIncludes)
            {
                Arguments += string.Format(" -I\"{0}\"", ConvertPath(Path.GetFullPath(IncludePath)));

                if (BuildHostPlatform.Current.Platform != STTargetPlatform.Mac)
                {
                    // sync any third party headers we may need
                    if (IncludePath.Contains("ThirdParty"))
                    {
                        string[] FileList = Directory.GetFiles(IncludePath, "*.h", SearchOption.AllDirectories);
                        foreach (string File in FileList)
                        {
                            FileItem ExternalDependency = FileItem.GetItemByPath(File);
                            LocalToRemoteFileItem(ExternalDependency, true);
                        }

                        FileList = Directory.GetFiles(IncludePath, "*.cpp", SearchOption.AllDirectories);
                        foreach (string File in FileList)
                        {
                            FileItem ExternalDependency = FileItem.GetItemByPath(File);
                            LocalToRemoteFileItem(ExternalDependency, true);
                        }
                    }
                }
            }

            foreach (string Definition in CompileEnvironment.Config.Definitions)
            {
                Arguments += string.Format(" -D\"{0}\"", Definition);
            }

            var BuildPlatform = STBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform);

            CPPOutput Result = new CPPOutput();
            // Create a compile action for each source file.
            foreach (FileItem SourceFile in SourceFiles)
            {
                Action CompileAction = new Action(ActionType.Compile);
                string FileArguments = "";
                string Extension = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant();

                if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
                {
                    // Compile the file as a C++ PCH.
                    FileArguments += GetCompileArguments_PCH();
                }
                else if (Extension == ".C")
                {
                    // Compile the file as C code.
                    FileArguments += GetCompileArguments_C();
                }
                else if (Extension == ".CC")
                {
                    // Compile the file as C++ code.
                    FileArguments += GetCompileArguments_CPP();
                }
                else if (Extension == ".MM")
                {
                    // Compile the file as Objective-C++ code.
                    FileArguments += GetCompileArguments_MM();
                }
                else if (Extension == ".M")
                {
                    // Compile the file as Objective-C++ code.
                    FileArguments += GetCompileArguments_M();
                }
                else
                {
                    // Compile the file as C++ code.
                    FileArguments += GetCompileArguments_CPP();

                    // only use PCH for .cpp files
                    FileArguments += PCHArguments;
                }

                // Add the C++ source file and its included files to the prerequisite item list.
                AddPrerequisiteSourceFile( Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems );

                if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
                {
                    var PrecompiledFileExtension = STBuildPlatform.BuildPlatformDictionary[STTargetPlatform.IOS].GetBinaryExtension(STBuildBinaryType.PrecompiledHeader);
                    // Add the precompiled header file to the produced item list.
                    FileItem PrecompiledHeaderFile = FileItem.GetItemByPath(
                        Path.Combine(
                            CompileEnvironment.Config.OutputDirectory,
                        Path.GetFileName(SourceFile.AbsolutePath) + PrecompiledFileExtension
                            )
                        );

                    FileItem RemotePrecompiledHeaderFile = LocalToRemoteFileItem(PrecompiledHeaderFile, false);
                    CompileAction.ProducedItems.Add(RemotePrecompiledHeaderFile);
                    Result.PrecompiledHeaderFile = RemotePrecompiledHeaderFile;

                    // Add the parameters needed to compile the precompiled header file to the command-line.
                    FileArguments += string.Format(" -o \"{0}\"", RemotePrecompiledHeaderFile.AbsolutePath, false);
                }
                else
                {
                    if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
                    {
                        CompileAction.bIsUsingPCH = true;
                        CompileAction.PrerequisiteItems.Add(CompileEnvironment.PrecompiledHeaderFile);
                    }
                    var ObjectFileExtension = STBuildPlatform.BuildPlatformDictionary[STTargetPlatform.IOS].GetBinaryExtension(STBuildBinaryType.Object);
                    // Add the object file to the produced item list.
                    FileItem ObjectFile = FileItem.GetItemByPath(
                        Path.Combine(
                            CompileEnvironment.Config.OutputDirectory,
                            Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension
                            )
                        );

                    FileItem RemoteObjectFile = LocalToRemoteFileItem(ObjectFile, false);
                    CompileAction.ProducedItems.Add(RemoteObjectFile);
                    Result.ObjectFiles.Add(RemoteObjectFile);
                    FileArguments += string.Format(" -o \"{0}\"", RemoteObjectFile.AbsolutePath, false);
                }

                // Add the source file path to the command-line.
                FileArguments += string.Format(" \"{0}\"", ConvertPath(SourceFile.AbsolutePath), false);

                string CompilerPath = XcodeDeveloperDir + "Toolchains/XcodeDefault.xctoolchain/usr/bin/" + IOSCompiler;
                if (!Utils.IsRunningOnMono && BuildHostPlatform.Current.Platform != STTargetPlatform.Mac)
                {
                    CompileAction.ActionHandler = new Action.BlockingActionHandler(RPCUtilHelper.RPCActionHandler);
                }

                // RPC utility parameters are in terms of the Mac side
                CompileAction.WorkingDirectory = GetMacDevSrcRoot();
                CompileAction.CommandPath = CompilerPath;
                CompileAction.CommandArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments;
                CompileAction.StatusDescription = string.Format("{0}", Path.GetFileName(SourceFile.AbsolutePath));
                CompileAction.bIsGCCCompiler = true;
                // We're already distributing the command by execution on Mac.
                CompileAction.bCanExecuteRemotely = false;
                CompileAction.bShouldOutputStatusDescription = true;
                CompileAction.OutputEventHandler = new DataReceivedEventHandler(RemoteOutputReceivedEventHandler);
            }
            return Result;
        }
Exemple #12
0
        string GetCompileArguments_Global(CPPEnvironment CompileEnvironment)
        {
            string Result = "";

            Result += " -fmessage-length=0";
            Result += " -pipe";
            Result += " -fpascal-strings";

            Result += " -fno-exceptions";
            Result += " -fno-rtti";
            Result += " -fvisibility=hidden"; // hides the linker warnings with PhysX

            // 			if (CompileEnvironment.Config.TargetConfiguration == CPPTargetConfiguration.Shipping)
            // 			{
            // 				Result += " -flto";
            // 			}

            Result += " -Wall -Werror";

            Result += " -Wno-unused-variable";
            Result += " -Wno-unused-value";
            // this will hide the warnings about static functions in headers that aren't used in every single .cpp file
            Result += " -Wno-unused-function";
            // this hides the "enumeration value 'XXXXX' not handled in switch [-Wswitch]" warnings - we should maybe remove this at some point and add UE_LOG(, Fatal, ) to default cases
            Result += " -Wno-switch";
            // this hides the "warning : comparison of unsigned expression < 0 is always false" type warnings due to constant comparisons, which are possible with template arguments
            Result += " -Wno-tautological-compare";
            //This will prevent the issue of warnings for unused private variables.
            Result += " -Wno-unused-private-field";
            Result += " -Wno-invalid-offsetof"; // needed to suppress warnings about using offsetof on non-POD types.

            Result += " -c";

            // What architecture(s) to build for
            Result += GetArchitectureArgument(CompileEnvironment.Config.Target.Configuration, CompileEnvironment.Config.Target.Architecture);

            if (CompileEnvironment.Config.Target.Architecture == "-simulator")
            {
                Result += " -isysroot " + BaseSDKDirSim + "/iPhoneSimulator" + IOSSDKVersion + ".sdk";
            }
            else
            {
                Result += " -isysroot " + BaseSDKDir + "/iPhoneOS" + IOSSDKVersion + ".sdk";
            }

            Result += " -miphoneos-version-min=" + BuildIOSVersion;

            // Optimize non- debug builds.
            if (CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug)
            {
                if (STBuildConfiguration.bCompileForSize)
                {
                    Result += " -Oz";
                }
                else
                {
                    Result += " -O3";
                }
            }
            else
            {
                Result += " -O0";
            }

            // Create DWARF format debug info if wanted,
            if (CompileEnvironment.Config.bCreateDebugInfo && !STBuildTool.BuildingRocket())
            {
                Result += " -gdwarf-2";
            }

            // Add additional frameworks so that their headers can be found
            foreach (STBuildFramework Framework in CompileEnvironment.Config.AdditionalFrameworks)
            {
                if (Framework.OwningModule != null && Framework.FrameworkZipPath != null && Framework.FrameworkZipPath != "")
                {
                    Result += " -F \"" + GetRemoteIntermediateFrameworkZipPath(Framework) + "\"";
                }
            }

            return Result;
        }
Exemple #13
0
        private void GenerateLinkerFixupsContents(STBuildBinary ExecutableBinary, List<string> LinkerFixupsFileContents, CPPEnvironment CompileEnvironment, string HeaderFilename, string LinkerFixupsName, List<string> PrivateDependencyModuleNames)
        {
            LinkerFixupsFileContents.Add("#include \"" + HeaderFilename + "\"");

            // To reduce the size of the command line for the compiler, we're going to put all definitions inside of the cpp file.
            foreach (var Definition in CompileEnvironment.Config.Definitions)
            {
                string MacroName;
                string MacroValue = String.Empty;
                int EqualsIndex = Definition.IndexOf('=');
                if (EqualsIndex >= 0)
                {
                    MacroName = Definition.Substring(0, EqualsIndex);
                    MacroValue = Definition.Substring(EqualsIndex + 1);
                }
                else
                {
                    MacroName = Definition;
                }
                LinkerFixupsFileContents.Add("#ifndef " + MacroName);
                LinkerFixupsFileContents.Add(String.Format("\t#define {0} {1}", MacroName, MacroValue));
                LinkerFixupsFileContents.Add("#endif");
            }

            // Add a function that is not referenced by anything that invokes all the empty functions in the different static libraries
            LinkerFixupsFileContents.Add("void " + LinkerFixupsName + "()");
            LinkerFixupsFileContents.Add("{");

            // Fill out the body of the function with the empty function calls. This is what causes the static libraries to be considered relevant
            var DependencyModules = ExecutableBinary.GetAllDependencyModules(bIncludeDynamicallyLoaded: false, bForceCircular: false);
            foreach (string ModuleName in DependencyModules.OfType<STBuildModuleCPP>().Where(CPPModule => CPPModule.AutoGenerateCppInfo != null).Select(CPPModule => CPPModule.Name).Distinct())
            {
                LinkerFixupsFileContents.Add("    extern void EmptyLinkFunctionForGeneratedCode" + ModuleName + "();");
                LinkerFixupsFileContents.Add("    EmptyLinkFunctionForGeneratedCode" + ModuleName + "();");
            }
            foreach (var DependencyModuleName in PrivateDependencyModuleNames)
            {
                LinkerFixupsFileContents.Add("    extern void EmptyLinkFunctionForStaticInitialization" + DependencyModuleName + "();");
                LinkerFixupsFileContents.Add("    EmptyLinkFunctionForStaticInitialization" + DependencyModuleName + "();");
            }

            // End the function body that was started above
            LinkerFixupsFileContents.Add("}");
        }
        /**
         * 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
         */
        public static bool ExecuteHeaderToolIfNecessary(STBuildTarget Target, CPPEnvironment GlobalCompileEnvironment, List<SHTModuleInfo> UObjectModules, string ModuleInfoFileName, ref ECompilationResult UHTResult)
        {
            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 BuildPlatform = STBuildPlatform.GetBuildPlatform(Target.Platform);
                var CppPlatform = BuildPlatform.GetCPPTargetPlatform(Target.Platform);
                var ToolChain = STToolChain.GetPlatformToolChain(CppPlatform);
                var RootLocalPath = Path.GetFullPath(ProjectFileGenerator.RootRelativePath);

                // ensure the headers are up to date
                bool bUHTNeedsToRun = (STBuildConfiguration.bForceHeaderGeneration == true || AreGeneratedCodeFilesOutOfDate(UObjectModules));
                if (bUHTNeedsToRun || STBuildTool.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 (STBuildTool.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 = (STBuildModuleCPP)Target.GetModuleByName(UHTModuleInfo.ModuleName);
                                var ModuleCompileEnvironment = DependencyModuleCPP.CreateModuleCompileEnvironment(GlobalCompileEnvironment);
                                DependencyModuleCPP.CachePCHUsageForModuleSourceFiles(ModuleCompileEnvironment);
                                if (DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile != null)
                                {
                                    UHTModuleInfo.PCH = DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile.AbsolutePath;
                                }
                            }
                        }
                    }
                }

                // @todo fastubt: @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)
                SHTManifest Manifest = new SHTManifest(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 (STBuildTool.RunningRocket() == false &&
                        STBuildConfiguration.bDoNotBuildUHT == false &&
                        STBuildConfiguration.bHotReloadFromIDE == false &&
                        !(!STBuildTool.IsGatheringBuild && STBuildTool.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(" " + STTargetConfiguration.Development.ToString());

                        // NOTE: We disable mutex when launching UBT from within UBT to compile UHT
                        UBTArguments.Append(" -NoMutex");

                        if (STBuildTool.CommandLineContains("-noxge"))
                        {
                            UBTArguments.Append(" -noxge");
                        }

                        if (RunExternalExecutable(STBuildTool.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(Path.GetDirectoryName(ModuleInfoFileName));
                    System.IO.File.WriteAllText(ModuleInfoFileName, fastJSON.JSON.Instance.ToJSON(Manifest, new fastJSON.JSONParameters { UseExtensions = false }));

                    string CmdLine = (STBuildTool.HasUProjectFile()) ? "\"" + STBuildTool.GetUProjectFile() + "\"" : Target.GetTargetName();
                    CmdLine += " \"" + ModuleInfoFileName + "\" -LogCmds=\"loginit warning, logexit warning, logdatabase error\"";
                    if (STBuildTool.RunningRocket())
                    {
                        CmdLine += " -rocket -installed";
                    }

                    if (STBuildConfiguration.bFailIfGeneratedCodeChanges)
                    {
                        CmdLine += " -FailIfGeneratedCodeChanges";
                    }

                    Stopwatch s = new Stopwatch();
                    s.Start();
                    UHTResult = (ECompilationResult)RunExternalExecutable(ExternalExecution.GetHeaderToolPath(), CmdLine);
                    s.Stop();

                    if (UHTResult != ECompilationResult.Succeeded)
                    {
                        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}", ActualTargetName);
                    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);
            }
            return true;
        }
Exemple #15
0
 // STBuildModule interface.
 public override List<FileItem> Compile(CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment, bool bCompileMonolithic)
 {
     return new List<FileItem>();
 }
Exemple #16
0
        // 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);
        }
Exemple #17
0
        static void AppendCLArguments_CPP(CPPEnvironment CompileEnvironment, StringBuilder Arguments)
        {
            // Explicitly compile the file as C++.
            Arguments.Append(" /TP");

            if (!CompileEnvironment.Config.bEnableBufferSecurityChecks)
            {
                // This will disable buffer security checks (which are enabled by default) that the MS compiler adds around arrays on the stack,
                // Which can add some performance overhead, especially in performance intensive code
                // Only disable this if you know what you are doing, because it will be disabled for the entire module!
                Arguments.Append(" /GS-");
            }

            // C++/CLI requires that RTTI is left enabled
            if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLRDisabled)
            {
                if (CompileEnvironment.Config.bUseRTTI)
                {
                    // Enable C++ RTTI.
                    Arguments.Append(" /GR");
                }
                else
                {
                    // Disable C++ RTTI.
                    Arguments.Append(" /GR-");
                }
            }

            // Set warning level.
            if (!BuildConfiguration.bRunUnrealCodeAnalyzer)
            {
                // Restrictive during regular compilation.
                Arguments.Append(" /W4");
            }
            else
            {
                // If we had /W4 with clang on windows we would be flooded with warnings. This will be fixed incrementally.
                Arguments.Append(" /W0");
            }

            if (WindowsPlatform.bCompileWithClang)
            {
                // Disable specific warnings that cause problems with Clang
                // NOTE: These must appear after we set the MSVC warning level

                // @todo clang: Ideally we want as few warnings disabled as possible
                //
                // Allow Microsoft-specific syntax to slide, even though it may be non-standard.  Needed for Windows headers.
                Arguments.Append(" -Wno-microsoft");

                // @todo clang: Kind of a shame to turn these off.  We'd like to catch unused variables, but it is tricky with how our assertion macros work.
                Arguments.Append(" -Wno-unused-variable");
                Arguments.Append(" -Wno-unused-function");
                Arguments.Append(" -Wno-unused-private-field");
                Arguments.Append(" -Wno-unused-value");

                Arguments.Append(" -Wno-inline-new-delete");	// @todo clang: We declare operator new as inline.  Clang doesn't seem to like that.

                // Sometimes we compare 'this' pointers against nullptr, which Clang warns about by default
                Arguments.Append(" -Wno-undefined-bool-conversion");

                // @todo clang: Disabled warnings were copied from MacToolChain for the most part
                Arguments.Append(" -Wno-deprecated-declarations");
                Arguments.Append(" -Wno-deprecated-writable-strings");
                Arguments.Append(" -Wno-deprecated-register");
                Arguments.Append(" -Wno-switch-enum");
                Arguments.Append(" -Wno-logical-op-parentheses");	// needed for external headers we shan't change
                Arguments.Append(" -Wno-null-arithmetic");			// needed for external headers we shan't change
                Arguments.Append(" -Wno-deprecated-declarations");	// needed for wxWidgets
                Arguments.Append(" -Wno-return-type-c-linkage");	// needed for PhysX
                Arguments.Append(" -Wno-ignored-attributes");		// needed for nvtesslib
                Arguments.Append(" -Wno-uninitialized");
                Arguments.Append(" -Wno-tautological-compare");
                Arguments.Append(" -Wno-switch");
                Arguments.Append(" -Wno-invalid-offsetof"); // needed to suppress warnings about using offsetof on non-POD types.
            }
        }
Exemple #18
0
        static void AppendCLArguments_Global(CPPEnvironment CompileEnvironment, VCEnvironment EnvVars, StringBuilder Arguments)
        {
            // @todo fastubt: Use should be using StringBuilder instead of concatenation in all of these toolchain files.  On Mono, string builder is dramatically faster.

            // @todo fastubt: We can use '/showIncludes' to accelerate outdatedness checking, as the compiler will discover all indirect includes itself.  But, the spew is pretty noisy!
            //		-> If no files in source file chain have changed, even if the build product is outdated, we can skip using /showIncludes (relies on cache surviving)
            // Arguments.Append( " /showIncludes" );

            if (WindowsPlatform.bCompileWithClang)
            {
                // Arguments.Append( " -###" );	// @todo clang: Print Clang command-lines (instead of outputting compile results!)

                // @todo clang: We're impersonating the Visual C++ compiler by setting MSC_VER and _MSC_FULL_VER to values that MSVC would set
                string VersionString;
                switch (WindowsPlatform.Compiler)
                {
                    case WindowsCompiler.VisualStudio2012:
                        VersionString = "1700";
                        break;

                    case WindowsCompiler.VisualStudio2013:
                        VersionString = "1800";
                        break;

                    default:
                        throw new BuildException("Unexpected value for WindowsPlatform.Compiler: " + WindowsPlatform.Compiler.ToString());
                }
                Arguments.Append(" -fmsc-version=" + VersionString);
                Arguments.Append(" /D_MSC_FULL_VER=" + VersionString + "00000");
            }

            // @todo clang: Clang on Windows doesn't respect "#pragma warning (error: ####)", and we're not passing "/WX", so warnings are not
            // treated as errors when compiling on Windows using Clang right now.

            if (BuildConfiguration.bEnableCodeAnalysis)
            {
                Arguments.Append(" /analyze");

                // Don't cause analyze warnings to be errors
                Arguments.Append(" /analyze:WX-");

                // Report functions that use a LOT of stack space.  You can lower this value if you
                // want more aggressive checking for functions that use a lot of stack memory.
                Arguments.Append(" /analyze:stacksize81940");

                // Don't bother generating code, only analyze code (may report fewer warnings though.)
                //Arguments.Append(" /analyze:only");
            }

            // Prevents the compiler from displaying its logo for each invocation.
            Arguments.Append(" /nologo");

            // Enable intrinsic functions.
            Arguments.Append(" /Oi");

            if (WindowsPlatform.bCompileWithClang)
            {
                // Tell the Clang compiler whether we want to generate 32-bit code or 64-bit code
                if (CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64)
                {
                    Arguments.Append(" --target=x86_64-pc-windows-msvc");
                }
                else
                {
                    Arguments.Append(" --target=x86-pc-windows-msvc");
                }
            }

            // Separate functions for linker.
            Arguments.Append(" /Gy");

            // Compile into an .obj file, and skip linking.
            Arguments.Append(" /c");

            // Allow 800% of the default memory allocation limit.
            Arguments.Append(" /Zm800");

            // Disable "The file contains a character that cannot be represented in the current code page" warning for non-US windows.
            Arguments.Append(" /wd4819");

            if (BuildConfiguration.bUseSharedPCHs)
            {
                // @todo SharedPCH: Disable warning about PCH defines not matching .cpp defines.  We "cheat" these defines a little
                // bit to make shared PCHs work.  But it's totally safe.  Trust us.
                Arguments.Append(" /wd4651");

                // @todo SharedPCH: Disable warning about redefining *API macros.  The PCH header is compiled with various DLLIMPORTs, but
                // when a module that uses that PCH header *IS* one of those imports, that module is compiled with EXPORTS, so the macro
                // is redefined on the command-line.  We need to clobber those defines to make shared PCHs work properly!
                Arguments.Append(" /wd4005");
            }

            // If compiling as a DLL, set the relevant defines
            if (CompileEnvironment.Config.bIsBuildingDLL)
            {
                Arguments.Append(" /D _WINDLL");
            }

            // When targeting Windows XP with Visual Studio 2012+, we need to tell the compiler to use the older Windows SDK that works
            // with Windows XP (http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx)
            if (WindowsPlatform.IsWindowsXPSupported())
            {
                Arguments.Append(" /D_USING_V110_SDK71_");
            }

            // Handle Common Language Runtime support (C++/CLI)
            if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLREnabled)
            {
                Arguments.Append(" /clr");

                // Don't use default lib path, we override it explicitly to use the 4.0 reference assemblies.
                Arguments.Append(" /clr:nostdlib");
            }

            //
            //	Debug
            //
            if (CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug)
            {
                // Disable compiler optimization.
                Arguments.Append(" /Od");

                // Favor code size (especially useful for embedded platforms).
                Arguments.Append(" /Os");

                // Allow inline method expansion unless E&C support is requested
                if (!BuildConfiguration.bSupportEditAndContinue)
                {
                    Arguments.Append(" /Ob2");
                }

                if ((CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win32) ||
                    (CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64))
                {
                    // Runtime stack checks are not allowed when compiling for CLR
                    if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLRDisabled)
                    {
                        Arguments.Append(" /RTCs");
                    }
                }
            }
            //
            //	Development and LTCG
            //
            else
            {
                // Maximum optimizations if desired.
                if (CompileEnvironment.Config.OptimizeCode >= ModuleRules.CodeOptimization.InNonDebugBuilds)
                {
                    Arguments.Append(" /Ox");
                }

                // Favor code speed.
                Arguments.Append(" /Ot");

                // Only omit frame pointers on the PC (which is implied by /Ox) if wanted.
                if (BuildConfiguration.bOmitFramePointers == false
                && ((CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win32) ||
                    (CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64)))
                {
                    Arguments.Append(" /Oy-");
                }

                // Allow inline method expansion
                Arguments.Append(" /Ob2");

                //
                // LTCG
                //
                if (CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Shipping)
                {
                    if (BuildConfiguration.bAllowLTCG)
                    {
                        // Enable link-time code generation.
                        Arguments.Append(" /GL");
                    }
                }
            }

            //
            //	PC
            //
            if ((CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win32) ||
                (CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64))
            {
                // SSE options are not allowed when using CLR compilation or the 64 bit toolchain
                // (both enable SSE2 automatically)
                if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLRDisabled &&
                    CompileEnvironment.Config.Target.Platform != CPPTargetPlatform.Win64)
                {
                    // Allow the compiler to generate SSE2 instructions.
                    Arguments.Append(" /arch:SSE2");
                }

                // Prompt the user before reporting internal errors to Microsoft.
                Arguments.Append(" /errorReport:prompt");

                if (CompileEnvironment.Config.CLRMode == CPPCLRMode.CLRDisabled)
                {
                    // Enable C++ exceptions when building with the editor or when building UHT.
                    if (!WindowsPlatform.bCompileWithClang &&	// @todo clang: C++ exceptions are not supported with Clang on Windows yet
                        (CompileEnvironment.Config.bEnableExceptions || STBuildConfiguration.bBuildEditor || STBuildConfiguration.bForceEnableExceptions))
                    {
                        // Enable C++ exception handling, but not C exceptions.
                        Arguments.Append(" /EHsc");
                    }
                    else
                    {
                        // This is required to disable exception handling in VC platform headers.
                        CompileEnvironment.Config.Definitions.Add("_HAS_EXCEPTIONS=0");
                    }
                }
                else
                {
                    // For C++/CLI all exceptions must be left enabled
                    Arguments.Append(" /EHa");
                }
            }

            // @todo - find a better place for this.
            if (CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.HTML5)
            {
                Arguments.Append(" /EHsc");
            }
            // If enabled, create debug information.
            if (CompileEnvironment.Config.bCreateDebugInfo)
            {
                // Store debug info in .pdb files.
                // @todo clang: PDB files are emited from Clang but do not fully work with Visual Studio yet (breakpoints won't hit due to "symbol read error")
                if (BuildConfiguration.bUsePDBFiles)
                {
                    // Create debug info suitable for E&C if wanted.
                    if (BuildConfiguration.bSupportEditAndContinue &&
                        // We only need to do this in debug as that's the only configuration that supports E&C.
                        CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug)
                    {
                        Arguments.Append(" /ZI");
                    }
                    // Regular PDB debug information.
                    else
                    {
                        Arguments.Append(" /Zi");
                    }
                    // We need to add this so VS won't lock the PDB file and prevent synchronous updates. This forces serialization through MSPDBSRV.exe.
                    // See http://msdn.microsoft.com/en-us/library/dn502518.aspx for deeper discussion of /FS switch.
                    if (BuildConfiguration.bUseIncrementalLinking && WindowsPlatform.Compiler == WindowsCompiler.VisualStudio2013)
                    {
                        Arguments.Append(" /FS");
                    }
                }
                // Store C7-format debug info in the .obj files, which is faster.
                else
                {
                    Arguments.Append(" /Z7");
                }
            }

            // Specify the appropriate runtime library based on the platform and config.
            if (CompileEnvironment.Config.bUseStaticCRT)
            {
                if (CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug && BuildConfiguration.bDebugBuildsActuallyUseDebugCRT)
                {
                    Arguments.Append(" /MTd");
                }
                else
                {
                    Arguments.Append(" /MT");
                }
            }
            else
            {
                if (CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug && BuildConfiguration.bDebugBuildsActuallyUseDebugCRT)
                {
                    Arguments.Append(" /MDd");
                }
                else
                {
                    Arguments.Append(" /MD");
                }
            }

            //
            // Options that aren't parsed by clang-cl yet
            //
            if (!WindowsPlatform.bCompileWithClang && !BuildConfiguration.bRunUnrealCodeAnalyzer)	// @todo clang: Not supported in clang-cl yet
            {
                // Allow large object files to avoid hitting the 2^16 section limit when running with -StressTestUnity.
                Arguments.Append(" /bigobj");

                // Relaxes floating point precision semantics to allow more optimization.
                Arguments.Append(" /fp:fast");

                if (CompileEnvironment.Config.OptimizeCode >= ModuleRules.CodeOptimization.InNonDebugBuilds)
                {
                    // Allow optimized code to be debugged more easily.  This makes PDBs a bit larger, but doesn't noticeably affect
                    // compile times.  The executable code is not affected at all by this switch, only the debugging information.
                    if (EnvVars.CLExeVersion >= new Version("18.0.30723"))
                    {
                        // VC2013 Update 3 has a new flag for doing this
                        Arguments.Append(" /Zo");
                    }
                    else
                    {
                        Arguments.Append(" /d2Zi+");
                    }
                }
            }

            //
            // Options skipped for UnrealCodeAnalyzer
            //
            if (!BuildConfiguration.bRunUnrealCodeAnalyzer)
            {
                if (CompileEnvironment.Config.Target.Platform == CPPTargetPlatform.Win64)
                {
                    // Pack struct members on 8-byte boundaries.
                    Arguments.Append(" /Zp8");
                }
                else if (!STBuildTool.CommandLineContains(@"UnrealCodeAnalyzer"))
                {
                    // Pack struct members on 4-byte boundaries.
                    // Disabled when compiling UnrealCodeAnalyzer as it breaks compilation (some structs in clang/llvm headers require 8-byte alignment in 32-bit compilation)
                    Arguments.Append(" /Zp4");
                }
            }

            //
            // Options required by UnrealCodeAnalyzer
            //
            if (BuildConfiguration.bRunUnrealCodeAnalyzer)
            {
                Arguments.Append(" /DUNREAL_CODE_ANALYZER");
            }
        }
Exemple #19
0
        public static FileItem CachePCHUsageForModuleSourceFile(STBuildTarget Target, CPPEnvironment ModuleCompileEnvironment, FileItem CPPFile)
        {
            if (!CPPFile.bExists)
            {
                throw new BuildException("Required source file not found: " + CPPFile.AbsolutePath);
            }

            var PCHCacheTimerStart = DateTime.UtcNow;

            var BuildPlatform = STBuildPlatform.GetBuildPlatformForCPPTargetPlatform(ModuleCompileEnvironment.Config.Target.Platform);
            var IncludePathsToSearch = ModuleCompileEnvironment.Config.CPPIncludeInfo.GetIncludesPathsToSearch(CPPFile);

            // Store the module compile environment along with the .cpp file.  This is so that we can use it later on when looking
            // for header dependencies
            CPPFile.CachedCPPIncludeInfo = ModuleCompileEnvironment.Config.CPPIncludeInfo;

            var PCHFile = CachePCHUsageForCPPFile(Target, CPPFile, BuildPlatform, IncludePathsToSearch, ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludeFileSearchDictionary);

            if (BuildConfiguration.bPrintPerformanceInfo)
            {
                var PCHCacheTime = (DateTime.UtcNow - PCHCacheTimerStart).TotalSeconds;
                TotalPCHCacheTime += PCHCacheTime;
            }

            return PCHFile;
        }
Exemple #20
0
 public virtual CPPOutput CompileRCFiles(STBuildTarget Target, CPPEnvironment Environment, List<FileItem> RCFiles)
 {
     CPPOutput Result = new CPPOutput();
     return Result;
 }
Exemple #21
0
        public void CachePCHUsageForModuleSourceFiles(CPPEnvironment ModuleCompileEnvironment)
        {
            if (ProcessedDependencies == null)
            {
                var PCHCacheTimerStart = DateTime.UtcNow;

                var BuildPlatform = STBuildPlatform.GetBuildPlatformForCPPTargetPlatform(ModuleCompileEnvironment.Config.Target.Platform);

                bool bFoundAProblemWithPCHs = false;

                FileItem UniquePCH = null;
                foreach (var CPPFile in SourceFilesFound.CPPFiles)	// @todo fastubt: We're not caching CPPEnvironments for .c/.mm files, etc.  Even though they don't use PCHs, they still have #includes!  This can break dependency checking!
                {
                    // Build a single list of include paths to search.
                    var IncludePathsToSearch = ModuleCompileEnvironment.Config.CPPIncludeInfo.GetIncludesPathsToSearch(CPPFile);   // @todo fastubt: This call could be made faster (list caching)

                    // Store the module compile environment along with the .cpp file.  This is so that we can use it later on when looking
                    // for header dependencies
                    CPPFile.CachedCPPIncludeInfo = ModuleCompileEnvironment.Config.CPPIncludeInfo;

                    // Find headers used by the source file.
                    var PCH = STBuildModuleCPP.CachePCHUsageForCPPFile(Target, CPPFile, BuildPlatform, IncludePathsToSearch, ModuleCompileEnvironment.Config.CPPIncludeInfo.IncludeFileSearchDictionary);
                    if (PCH == null)
                    {
                        throw new BuildException("Source file \"{0}\" is not including any headers.  We expect all modules to include a header file for precompiled header generation.  Please add an #include statement.", CPPFile.AbsolutePath);
                    }

                    if (UniquePCH == null)
                    {
                        UniquePCH = PCH;
                    }
                    else if (!UniquePCH.Info.Name.Equals(PCH.Info.Name, StringComparison.InvariantCultureIgnoreCase))		// @todo fastubt: We do a string compare on the file name (not path) here, because sometimes the include resolver will pick an Intermediate copy of a PCH header file and throw off our comparisons
                    {
                        // OK, looks like we have multiple source files including a different header file first.  We'll keep track of this and print out
                        // helpful information afterwards.
                        bFoundAProblemWithPCHs = true;
                    }
                }

                ProcessedDependencies = new ProcessedDependenciesClass { UniquePCHHeaderFile = UniquePCH };

                if (bFoundAProblemWithPCHs)
                {
                    // Map from pch header string to the source files that use that PCH
                    var UsageMapPCH = new Dictionary<string, List<FileItem>>(StringComparer.InvariantCultureIgnoreCase);
                    foreach (var CPPFile in SourceFilesToBuild.CPPFiles)	// @todo fastubt: Using SourceFilesToBuild here, but SourceFilesFound up above.  Why are there two arrays again?
                    {
                        // Create a new entry if not in the pch usage map
                        UsageMapPCH.GetOrAddNew(CPPFile.PrecompiledHeaderIncludeFilename).Add(CPPFile);
                    }

                    if (BuildConfiguration.bPrintDebugInfo)
                    {
                        Log.TraceVerbose("{0} PCH files for module {1}:", UsageMapPCH.Count, Name);
                        int MostFilesIncluded = 0;
                        foreach (var CurPCH in UsageMapPCH)
                        {
                            if (CurPCH.Value.Count > MostFilesIncluded)
                            {
                                MostFilesIncluded = CurPCH.Value.Count;
                            }

                            Log.TraceVerbose("   {0}  ({1} files including it: {2}, ...)", CurPCH.Key, CurPCH.Value.Count, CurPCH.Value[0].AbsolutePath);
                        }
                    }

                    if (UsageMapPCH.Count > 1)
                    {
                        // Keep track of the PCH file that is most used within this module
                        string MostFilesAreIncludingPCH = string.Empty;
                        int MostFilesIncluded = 0;
                        foreach (var CurPCH in UsageMapPCH.Where(PCH => PCH.Value.Count > MostFilesIncluded))
                        {
                            MostFilesAreIncludingPCH = CurPCH.Key;
                            MostFilesIncluded = CurPCH.Value.Count;
                        }

                        // Find all of the files that are not including our "best" PCH header
                        var FilesNotIncludingBestPCH = new StringBuilder();
                        foreach (var CurPCH in UsageMapPCH.Where(PCH => PCH.Key != MostFilesAreIncludingPCH))
                        {
                            foreach (var SourceFile in CurPCH.Value)
                            {
                                FilesNotIncludingBestPCH.AppendFormat("{0} (including {1})\n", SourceFile.AbsolutePath, CurPCH.Key);
                            }
                        }

                        // Bail out and let the user know which source files may need to be fixed up
                        throw new BuildException(
                            "All source files in module \"{0}\" must include the same precompiled header first.  Currently \"{1}\" is included by most of the source files.  The following source files are not including \"{1}\" as their first include:\n\n{2}",
                            Name,
                            MostFilesAreIncludingPCH,
                            FilesNotIncludingBestPCH);
                    }
                }

                if (BuildConfiguration.bPrintPerformanceInfo)
                {
                    var PCHCacheTime = (DateTime.UtcNow - PCHCacheTimerStart).TotalSeconds;
                    TotalPCHCacheTime += PCHCacheTime;
                }
            }
        }
Exemple #22
0
        /// <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;
        }
Exemple #23
0
        // STBuildModule interface.
        public override List<FileItem> Compile(CPPEnvironment GlobalCompileEnvironment, CPPEnvironment CompileEnvironment, bool bCompileMonolithic)
        {
            var BuildPlatform = STBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform);

            var LinkInputFiles = new List<FileItem>();
            if (ProjectFileGenerator.bGenerateProjectFiles && IntelliSenseGatherer == null)
            {
                // Nothing to do for IntelliSense, bail out early
                return LinkInputFiles;
            }

            if (RedistStaticLibraryPaths != null && !bBuildingRedistStaticLibrary)
            {
                // The redist static library will be added in SetupPrivateLinkEnvironment
                return LinkInputFiles;
            }

            var ModuleCompileEnvironment = CreateModuleCompileEnvironment(CompileEnvironment);

            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 == STBuildBinaryType.DynamicLinkLibrary)
            {
                // Add default PCLaunch.rc file if this module has no own resource file specified
                if (SourceFilesToBuild.RCFiles.Count <= 0)
                {
                    string DefRC = Utils.CleanDirectorySeparators(Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "Runtime/Launch/Resources/Windows/PCLaunch.rc")));
                    FileItem Item = FileItem.GetItemByFullPath(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.
                string ModuleVersionRC = Utils.CleanDirectorySeparators(Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "Runtime/Core/Resources/Windows/ModuleVersionResource.rc.inl")));
                FileItem ModuleVersionItem = FileItem.GetItemByFullPath(ModuleVersionRC);
                if (!SourceFilesToBuild.RCFiles.Contains(ModuleVersionItem))
                {
                    SourceFilesToBuild.RCFiles.Add(ModuleVersionItem);
                }
            }

            {
                // Process all of the header file dependencies for this module
                this.CachePCHUsageForModuleSourceFiles(ModuleCompileEnvironment);

                // Make sure our RC files have cached includes.
                foreach (var 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
            var IsGameModule = !Utils.IsFileUnderDirectory(this.ModuleDirectory, Path.Combine(ProjectFileGenerator.EngineRelativePath));

            // Should we force a precompiled header to be generated for this module?  Usually, we only bother with a
            // precompiled header if there are at least several source files in the module (after combining them for unity
            // builds.)  But for game modules, it can be convenient to always have a precompiled header to single-file
            // changes to code is really quick to compile.
            int MinFilesUsingPrecompiledHeader = BuildConfiguration.MinFilesUsingPrecompiledHeader;
            if (MinFilesUsingPrecompiledHeaderOverride != 0)
            {
                MinFilesUsingPrecompiledHeader = MinFilesUsingPrecompiledHeaderOverride;
            }
            else if (IsGameModule && BuildConfiguration.bForcePrecompiledHeaderForGameModules)
            {
                // This is a game module with only a small number of source files, so go ahead and force a precompiled header
                // to be generated to make incremental changes to source files as fast as possible for small projects.
                MinFilesUsingPrecompiledHeader = 1;
            }

            // Should we use unity build mode for this module?
            bool bModuleUsesUnityBuild = BuildConfiguration.bUseUnityBuild;
            if (bFasterWithoutUnity)
            {
                bModuleUsesUnityBuild = false;
            }
            else if (!BuildConfiguration.bForceUnityBuild && IsGameModule && SourceFilesToBuild.CPPFiles.Count < BuildConfiguration.MinGameModuleSourceFilesForUnityBuild)
            {
                // Game modules with only a small number of source files are usually better off having faster iteration times
                // on single source file changes, so we forcibly disable unity build for those modules
                bModuleUsesUnityBuild = false;
            }

            // The environment with which to compile the CPP files
            var CPPCompileEnvironment = ModuleCompileEnvironment;

            // Precompiled header support.
            bool bWasModuleCodeCompiled = false;
            if (BuildPlatform.ShouldUsePCHFiles(CompileEnvironment.Config.Target.Platform, CompileEnvironment.Config.Target.Configuration))
            {
                var 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;

                bool bDisableSharedPCHFiles = (Binary.Config.bCompileMonolithic && CompileEnvironment.Config.bIsBuildingLibrary);
                if (BuildConfiguration.bUseSharedPCHs && bDisableSharedPCHFiles)
                {
                    Log.TraceVerbose("Module '{0}' was not allowed to use Shared PCHs, because we're compiling to a library in monolithic mode", this.Name);
                }

                bool bUseSharedPCHFiles = BuildConfiguration.bUseSharedPCHs && (bDisableSharedPCHFiles == false) && !STBuildTool.BuildingRocket();

                if (bUseSharedPCHFiles)
                {
                    string SharingPCHHeaderFilePath = null;
                    bool bIsASharedPCHModule = bUseSharedPCHFiles && GlobalCompileEnvironment.SharedPCHHeaderFiles.Any(PCH => PCH.Module == this);
                    if (bIsASharedPCHModule)
                    {
                        SharingPCHHeaderFilePath = Path.GetFullPath(Path.Combine(ProjectFileGenerator.RootRelativePath, "Engine", "Source", this.SharedPCHHeaderFile));
                    }

                    // When compiling in modular mode, we can't use a shared PCH file when compiling a module
                    // with exports, because the shared PCH can only have imports in it to work correctly.
                    bool bCanModuleUseOwnSharedPCH = bAllowSharedPCH && bIsASharedPCHModule && bCompileMonolithic && ProcessedDependencies.UniquePCHHeaderFile.AbsolutePath.Equals(SharingPCHHeaderFilePath, StringComparison.InvariantCultureIgnoreCase);
                    if (bAllowSharedPCH && (!bIsASharedPCHModule || bCanModuleUseOwnSharedPCH))
                    {
                        // Figure out which shared PCH tier we're in
                        var DirectDependencyModules = new List<STBuildModule>();
                        {
                            var ReferencedModules = new Dictionary<string, STBuildModule>(StringComparer.InvariantCultureIgnoreCase);
                            this.GetAllDependencyModules(ref ReferencedModules, ref DirectDependencyModules, bIncludeDynamicallyLoaded: false, bForceCircular: false, bOnlyDirectDependencies: true);
                        }

                        int LargestSharedPCHHeaderFileIndex = -1;
                        foreach (var DependencyModule in DirectDependencyModules)
                        {
                            // These Shared PCHs are ordered from least complex to most complex.  We'll start at the last one and search backwards.
                            for (var SharedPCHHeaderFileIndex = GlobalCompileEnvironment.SharedPCHHeaderFiles.Count - 1; SharedPCHHeaderFileIndex > LargestSharedPCHHeaderFileIndex; --SharedPCHHeaderFileIndex)
                            {
                                var CurSharedPCHHeaderFile = GlobalCompileEnvironment.SharedPCHHeaderFiles[SharedPCHHeaderFileIndex];

                                if (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);
                        }
                    }
                    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 (var CPPFile in SourceFilesToBuild.CPPFiles)
                        {
                            CPPFile.PCHHeaderNameInCode = PCHToUse.AbsolutePath;
                            CPPFile.PrecompiledHeaderIncludeFilename = PCHToUse.AbsolutePath;
                        }
                    }

                    // A shared PCH was not already set up for us, so set one up.
                    if (ModulePCHEnvironment == null)
                    {
                        var PCHHeaderFile = ProcessedDependencies.UniquePCHHeaderFile;
                        var PCHModuleName = this.Name;
                        if (SharedPCHHeaderFile != null)
                        {
                            PCHHeaderFile = SharedPCHHeaderFile;
                            PCHModuleName = SharedPCHModuleName;
                        }
                        var 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.

                    var ModulePCHCompileEnvironment = ModuleCompileEnvironment.DeepCopy();
                    ModulePCHCompileEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Include;
                    ModulePCHCompileEnvironment.Config.PrecompiledHeaderIncludeFilename = ModulePCHEnvironment.PrecompiledHeaderIncludeFilename.AbsolutePath;
                    ModulePCHCompileEnvironment.Config.PCHHeaderNameInCode = ModulePCHEnvironment.PCHHeaderNameInCode;

                    if (SharedPCHHeaderFile != null)
                    {
                        // Shared PCH headers need to be force included, because we're basically forcing the module to use
                        // the precompiled header that we want, instead of the "first include" in each respective .cpp file
                        ModulePCHCompileEnvironment.Config.bForceIncludePrecompiledHeader = true;
                    }

                    var CPPFilesToBuild = SourceFilesToBuild.CPPFiles;
                    if (bModuleUsesUnityBuild)
                    {
                        // unity files generated for only the set of files which share the same PCH environment
                        CPPFilesToBuild = Unity.GenerateUnityCPPs(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)
                        {
                            bool bAllowDLLExports = true;
                            var PCHOutputDirectory = ModuleCompileEnvironment.Config.OutputDirectory;
                            var PCHModuleName = this.Name;

                            if (SharedPCHHeaderFile != null)
                            {
                                // Disallow DLLExports when generating shared PCHs.  These headers aren't able to export anything, because they're potentially shared between many modules.
                                bAllowDLLExports = false;

                                // Save shared PCHs to a specific folder
                                PCHOutputDirectory = Path.Combine(CompileEnvironment.Config.OutputDirectory, "SharedPCHs");

                                // Use a fake module name for "shared" PCHs.  It may be used by many modules, so we don't want to use this module's name.
                                PCHModuleName = "Shared";
                            }

                            var PCHOutput = PrecompileHeaderEnvironment.GeneratePCHCreationAction(
                                Target,
                                CPPFilesToBuild[0].PCHHeaderNameInCode,
                                ModulePCHEnvironment.PrecompiledHeaderIncludeFilename,
                                ModuleCompileEnvironment,
                                PCHOutputDirectory,
                                PCHModuleName,
                                bAllowDLLExports);
                            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(CPPCompileEnvironment.CompileFiles(Target, CPPFilesToBuild, Name).ObjectFiles);
                    bWasModuleCodeCompiled = true;
                }

                if (BuildConfiguration.bPrintPerformanceInfo)
                {
                    var PCHGenTime = (DateTime.UtcNow - PCHGenTimerStart).TotalSeconds;
                    TotalPCHGenTime += PCHGenTime;
                }
            }

            if (!bWasModuleCodeCompiled && SourceFilesToBuild.CPPFiles.Count > 0)
            {
                var CPPFilesToCompile = SourceFilesToBuild.CPPFiles;
                if (bModuleUsesUnityBuild)
                {
                    CPPFilesToCompile = Unity.GenerateUnityCPPs(Target, CPPFilesToCompile, CPPCompileEnvironment, Name);
                }
                LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles(Target, 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)
                {
                    var GeneratedCppFileItem = FileItem.GetItemByPath(GeneratedFilename);

                    CachePCHUsageForModuleSourceFile(this.Target, CPPCompileEnvironment, GeneratedCppFileItem);

                    // @todo fastubt: Check for ALL other places where we might be injecting .cpp or .rc files for compiling without caching CachedCPPIncludeInfo first (anything platform specfic?)
                    LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles(Target, new List<FileItem> { GeneratedCppFileItem }, Name).ObjectFiles);
                }
            }

            // Compile C files directly.
            LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles(Target, SourceFilesToBuild.CFiles, Name).ObjectFiles);

            // Compile CC files directly.
            LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles(Target, SourceFilesToBuild.CCFiles, Name).ObjectFiles);

            // Compile MM files directly.
            LinkInputFiles.AddRange(CPPCompileEnvironment.CompileFiles(Target, SourceFilesToBuild.MMFiles, Name).ObjectFiles);

            // If we're building Rocket, generate a static library for this module
            if (RedistStaticLibraryPaths != null)
            {
                // Create a link environment for it
                LinkEnvironment RedistLinkEnvironment = new LinkEnvironment();
                RedistLinkEnvironment.InputFiles.AddRange(LinkInputFiles);
                RedistLinkEnvironment.Config.Target = CompileEnvironment.Config.Target;
                RedistLinkEnvironment.Config.bIsBuildingDLL = false;
                RedistLinkEnvironment.Config.bIsBuildingLibrary = true;
                RedistLinkEnvironment.Config.IntermediateDirectory = Binary.Config.IntermediateDirectory;
                RedistLinkEnvironment.Config.OutputFilePaths = RedistStaticLibraryPaths != null ? (string[])RedistStaticLibraryPaths.Clone() : null;

                // Replace the items built so far with the library
                RedistLinkEnvironment.LinkExecutable(false);
                LinkInputFiles.Clear();
            }

            // Compile RC files.
            LinkInputFiles.AddRange(CPPCompileEnvironment.CompileRCFiles(Target, SourceFilesToBuild.RCFiles).ObjectFiles);

            return LinkInputFiles;
        }
Exemple #24
0
        /// <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) };
        }
Exemple #25
0
        /** Copy constructor. */
        protected CPPEnvironment(CPPEnvironment InCopyEnvironment)
        {
            PrecompiledHeaderFile = InCopyEnvironment.PrecompiledHeaderFile;
            PrivateAssemblyDependencies.AddRange(InCopyEnvironment.PrivateAssemblyDependencies);
            SharedPCHHeaderFiles.AddRange(InCopyEnvironment.SharedPCHHeaderFiles);
            SharedPCHEnvironments.AddRange(InCopyEnvironment.SharedPCHEnvironments);
            bHackHeaderGenerator = InCopyEnvironment.bHackHeaderGenerator;

            Config = new CPPEnvironmentConfiguration(InCopyEnvironment.Config);
        }