/// <summary>
        /// Builds and runs the header tool and touches the header directories.
        /// Performs any early outs if headers need no changes, given the UObject modules, tool path, game name, and configuration
        /// </summary>
        public static bool ExecuteHeaderToolIfNecessary(UEToolChain ToolChain, UEBuildTarget Target, CPPEnvironment GlobalCompileEnvironment, List <UHTModuleInfo> UObjectModules, FileReference ModuleInfoFileName, ref ECompilationResult UHTResult)
        {
            if (ProgressWriter.bWriteMarkup)
            {
                Log.WriteLine(LogEventType.Console, "@progress push 5%");
            }
            using (ProgressWriter Progress = new ProgressWriter("Generating code...", false))
            {
                // We never want to try to execute the header tool when we're already trying to build it!
                var bIsBuildingUHT = Target.GetTargetName().Equals("UnrealHeaderTool", StringComparison.InvariantCultureIgnoreCase);

                var RootLocalPath = Path.GetFullPath(ProjectFileGenerator.RootRelativePath);

                // check if UHT is out of date
                DateTime HeaderToolTimestamp = DateTime.MaxValue;
                bool     bHaveHeaderTool     = !bIsBuildingUHT && GetHeaderToolTimestamp(out HeaderToolTimestamp);

                // ensure the headers are up to date
                bool bUHTNeedsToRun = (UEBuildConfiguration.bForceHeaderGeneration == true || !bHaveHeaderTool || AreGeneratedCodeFilesOutOfDate(UObjectModules, HeaderToolTimestamp));
                if (bUHTNeedsToRun || UnrealBuildTool.IsGatheringBuild)
                {
                    // Since code files are definitely out of date, we'll now finish computing information about the UObject modules for UHT.  We
                    // want to save this work until we know that UHT actually needs to be run to speed up best-case iteration times.
                    if (UnrealBuildTool.IsGatheringBuild)                               // In assembler-only mode, PCH info is loaded from our UBTMakefile!
                    {
                        foreach (var UHTModuleInfo in UObjectModules)
                        {
                            // Only cache the PCH name if we don't already have one.  When running in 'gather only' mode, this will have already been cached
                            if (string.IsNullOrEmpty(UHTModuleInfo.PCH))
                            {
                                UHTModuleInfo.PCH = "";

                                // We need to figure out which PCH header this module is including, so that UHT can inject an include statement for it into any .cpp files it is synthesizing
                                var DependencyModuleCPP      = (UEBuildModuleCPP)Target.GetModuleByName(UHTModuleInfo.ModuleName);
                                var ModuleCompileEnvironment = DependencyModuleCPP.CreateModuleCompileEnvironment(GlobalCompileEnvironment);
                                DependencyModuleCPP.CachePCHUsageForModuleSourceFiles(ModuleCompileEnvironment);
                                if (DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile != null)
                                {
                                    UHTModuleInfo.PCH = DependencyModuleCPP.ProcessedDependencies.UniquePCHHeaderFile.AbsolutePath;
                                }
                            }
                        }
                    }
                }

                // @todo ubtmake: Optimization: Ideally we could avoid having to generate this data in the case where UHT doesn't even need to run!  Can't we use the existing copy?  (see below use of Manifest)
                UHTManifest Manifest = new UHTManifest(Target, RootLocalPath, ToolChain.ConvertPath(RootLocalPath + '\\'), UObjectModules);

                if (!bIsBuildingUHT && bUHTNeedsToRun)
                {
                    // Always build UnrealHeaderTool if header regeneration is required, unless we're running within a Rocket ecosystem or hot-reloading
                    if (UnrealBuildTool.RunningRocket() == false &&
                        UEBuildConfiguration.bDoNotBuildUHT == false &&
                        UEBuildConfiguration.bHotReloadFromIDE == false &&
                        !(bHaveHeaderTool && !UnrealBuildTool.IsGatheringBuild && UnrealBuildTool.IsAssemblingBuild))                           // If running in "assembler only" mode, we assume UHT is already up to date for much faster iteration!
                    {
                        // If it is out of date or not there it will be built.
                        // If it is there and up to date, it will add 0.8 seconds to the build time.
                        Log.TraceInformation("Building UnrealHeaderTool...");

                        var UBTArguments = new StringBuilder();

                        UBTArguments.Append("UnrealHeaderTool");

                        // Which desktop platform do we need to compile UHT for?
                        UBTArguments.Append(" " + BuildHostPlatform.Current.Platform.ToString());
                        // NOTE: We force Development configuration for UHT so that it runs quickly, even when compiling debug
                        UBTArguments.Append(" " + UnrealTargetConfiguration.Development.ToString());

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

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

                        // Propagate command-line option
                        if (UnrealBuildTool.CommandLineContains("-2015"))
                        {
                            UBTArguments.Append(" -2015");
                        }
                        if (UnrealBuildTool.CommandLineContains("-2013"))
                        {
                            UBTArguments.Append(" -2013");
                        }

                        // Add UHT plugins to UBT command line as external plugins
                        if (Target.UnrealHeaderToolPlugins != null && Target.UnrealHeaderToolPlugins.Count > 0)
                        {
                            foreach (PluginInfo Plugin in Target.UnrealHeaderToolPlugins)
                            {
                                UBTArguments.Append(" -PLUGIN \"" + Plugin.File + "\"");
                            }
                        }

                        if (RunExternalExecutable(UnrealBuildTool.GetUBTPath(), UBTArguments.ToString()) != 0)
                        {
                            return(false);
                        }
                    }

                    Progress.Write(1, 3);

                    var ActualTargetName = String.IsNullOrEmpty(Target.GetTargetName()) ? "UE4" : Target.GetTargetName();
                    Log.TraceInformation("Parsing headers for {0}", ActualTargetName);

                    string HeaderToolPath = GetHeaderToolPath();
                    if (!File.Exists(HeaderToolPath))
                    {
                        throw new BuildException("Unable to generate headers because UnrealHeaderTool binary was not found ({0}).", Path.GetFullPath(HeaderToolPath));
                    }

                    // Disable extensions when serializing to remove the $type fields
                    Directory.CreateDirectory(ModuleInfoFileName.Directory.FullName);
                    System.IO.File.WriteAllText(ModuleInfoFileName.FullName, fastJSON.JSON.Instance.ToJSON(Manifest, new fastJSON.JSONParameters {
                        UseExtensions = false
                    }));

                    string CmdLine = (Target.ProjectFile != null) ? "\"" + Target.ProjectFile.FullName + "\"" : Target.GetTargetName();
                    CmdLine += " \"" + ModuleInfoFileName + "\" -LogCmds=\"loginit warning, logexit warning, logdatabase error\" -Unattended -WarningsAsErrors";
                    if (UnrealBuildTool.IsEngineInstalled())
                    {
                        CmdLine += " -installed";
                    }

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

                    if (!bInvalidateUHTMakefile && BuildConfiguration.bUseUHTMakefiles)
                    {
                        CmdLine += " -UseMakefiles";
                    }

                    if (!UEBuildConfiguration.bCompileAgainstEngine)
                    {
                        CmdLine += " -NoEnginePlugins";
                    }

                    Log.TraceInformation("  Running UnrealHeaderTool {0}", CmdLine);

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

                    if (UHTResult != ECompilationResult.Succeeded)
                    {
                        // On Linux and Mac, the shell will return 128+signal number exit codes if UHT gets a signal (e.g. crashes or is interrupted)
                        if ((BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Linux ||
                             BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) &&
                            (int)(UHTResult) >= 128
                            )
                        {
                            // SIGINT is 2, so 128 + SIGINT is 130
                            UHTResult = ((int)(UHTResult) == 130) ? ECompilationResult.Canceled : ECompilationResult.CrashOrAssert;
                        }

                        Log.TraceInformation("Error: Failed to generate code for {0} - error code: {2} ({1})", ActualTargetName, (int)UHTResult, UHTResult.ToString());
                        return(false);
                    }

                    Log.TraceInformation("Reflection code generated for {0} in {1} seconds", ActualTargetName, s.Elapsed.TotalSeconds);
                    if (BuildConfiguration.bPrintPerformanceInfo)
                    {
                        Log.TraceInformation("UnrealHeaderTool took {1}", ActualTargetName, (double)s.ElapsedMilliseconds / 1000.0);
                    }

                    // Now that UHT has successfully finished generating code, we need to update all cached FileItems in case their last write time has changed.
                    // Otherwise UBT might not detect changes UHT made.
                    DateTime StartTime = DateTime.UtcNow;
                    FileItem.ResetInfos();
                    double ResetDuration = (DateTime.UtcNow - StartTime).TotalSeconds;
                    Log.TraceVerbose("FileItem.ResetInfos() duration: {0}s", ResetDuration);
                }
                else
                {
                    Log.TraceVerbose("Generated code is up to date.");
                }

                Progress.Write(2, 3);

                // There will never be generated code if we're building UHT, so this should never be called.
                if (!bIsBuildingUHT)
                {
                    // Allow generated code to be sync'd to remote machines if needed. This needs to be done even if UHT did not run because
                    // generated headers include other generated headers using absolute paths which in case of building remotely are already
                    // the remote machine absolute paths. Because of that parsing headers will not result in finding all includes properly.
                    // @todo ubtmake: Need to figure out what this does in the assembler case, and whether we need to run it
                    ToolChain.PostCodeGeneration(Manifest);
                }

                // touch the directories
                UpdateDirectoryTimestamps(UObjectModules);

                Progress.Write(3, 3);
            }
            if (ProgressWriter.bWriteMarkup)
            {
                Log.WriteLine(LogEventType.Console, "@progress pop");
            }
            return(true);
        }
		/// <summary>
		/// Creates a precompiled header action to generate a new pch file 
		/// </summary>
		/// <param name="PCHHeaderNameInCode">The precompiled header name as it appeared in an #include statement</param>
		/// <param name="PrecompiledHeaderIncludeFilename">Name of the header used for pch.</param>
		/// <param name="ProjectCPPEnvironment">The environment the C/C++ files in the project are compiled with.</param>
		/// <param name="OutputDirectory">The folder to save the generated PCH file to</param>
		/// <param name="ModuleName">Name of the module this PCH is being generated for</param>
		/// <param name="bAllowDLLExports">True if we should allow DLLEXPORT definitions for this PCH</param>
		/// <returns>the compilation output result of the created pch.</returns>
		public static CPPOutput GeneratePCHCreationAction(UEToolChain ToolChain, UEBuildTarget Target, string PCHHeaderNameInCode, FileItem PrecompiledHeaderIncludeFilename, CPPEnvironment ProjectCPPEnvironment, DirectoryReference OutputDirectory, DirectoryReference PCHOutputDirectory, string ModuleName, bool bAllowDLLExports)
		{
			// Find the header file to be precompiled. Don't skip external headers
			if (PrecompiledHeaderIncludeFilename.bExists)
			{
				// Create a Dummy wrapper around the PCH to avoid problems with #pragma once on clang
				string PCHGuardDefine = Path.GetFileNameWithoutExtension(PrecompiledHeaderIncludeFilename.AbsolutePath).ToUpperInvariant();
				string LocalPCHHeaderNameInCode = ToolChain.ConvertPath(PrecompiledHeaderIncludeFilename.AbsolutePath);
				string TmpPCHHeaderContents = String.Format("#ifndef __AUTO_{0}_H__\n#define __AUTO_{0}_H__\n//Last Write: {2}\n#include \"{1}\"\n#endif//__AUTO_{0}_H__", PCHGuardDefine, LocalPCHHeaderNameInCode, PrecompiledHeaderIncludeFilename.LastWriteTime);
				FileReference DummyPath = FileReference.Combine(
					ProjectCPPEnvironment.Config.OutputDirectory,
					Path.GetFileName(PrecompiledHeaderIncludeFilename.AbsolutePath));
				FileItem DummyPCH = FileItem.CreateIntermediateTextFile(DummyPath, TmpPCHHeaderContents);

				// Create a new C++ environment that is used to create the PCH.
				CPPEnvironment ProjectPCHEnvironment = ProjectCPPEnvironment.DeepCopy();
				ProjectPCHEnvironment.Config.PrecompiledHeaderAction = PrecompiledHeaderAction.Create;
				ProjectPCHEnvironment.Config.PrecompiledHeaderIncludeFilename = PrecompiledHeaderIncludeFilename.Reference;
				ProjectPCHEnvironment.Config.PCHHeaderNameInCode = PCHHeaderNameInCode;
				ProjectPCHEnvironment.Config.OutputDirectory = OutputDirectory;
				ProjectPCHEnvironment.Config.PCHOutputDirectory = PCHOutputDirectory;

				if (!bAllowDLLExports)
				{
					for (int CurDefinitionIndex = 0; CurDefinitionIndex < ProjectPCHEnvironment.Config.Definitions.Count; ++CurDefinitionIndex)
					{
						// We change DLLEXPORT to DLLIMPORT for "shared" PCH headers
						string OldDefinition = ProjectPCHEnvironment.Config.Definitions[CurDefinitionIndex];
						if (OldDefinition.EndsWith("=DLLEXPORT"))
						{
							ProjectPCHEnvironment.Config.Definitions[CurDefinitionIndex] = OldDefinition.Replace("DLLEXPORT", "DLLIMPORT");
						}
					}
				}

				// Cache our CPP environment so that we can check for outdatedness quickly.  Only files that have includes need this.
				DummyPCH.CachedCPPIncludeInfo = ProjectPCHEnvironment.Config.CPPIncludeInfo;

				Log.TraceVerbose("Found PCH file \"{0}\".", PrecompiledHeaderIncludeFilename);

				// Create the action to compile the PCH file.
				return ToolChain.CompileCPPFiles(Target, ProjectPCHEnvironment, new List<FileItem>() { DummyPCH }, ModuleName);
			}
			throw new BuildException("Couldn't find PCH file \"{0}\".", PrecompiledHeaderIncludeFilename);
		}
Example #3
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(
            UEToolChain ToolChain,
            UEBuildTarget Target,
            List <FileItem> CPPFiles,
            CPPEnvironment CompileEnvironment,
            string BaseName
            )
        {
            List <FileItem> NewCPPFiles = new List <FileItem>();

            UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Platform);

            // Figure out size of all input files combined. We use this to determine whether to use larger unity threshold or not.
            long TotalBytesInCPPFiles = CPPFiles.Sum(F => F.Info.Length);

            // We have an increased threshold for unity file size if, and only if, all files fit into the same unity file. This
            // is beneficial when dealing with PCH files. The default PCH creation limit is X unity files so if we generate < X
            // this could be fairly slow and we'd rather bump the limit a bit to group them all into the same unity file.


            // When enabled, UnrealBuildTool will try to determine source files that you are actively iteratively changing, and break those files
            // out of their unity blobs so that you can compile them as individual translation units, much faster than recompiling the entire
            // unity blob each time.
            bool bUseAdaptiveUnityBuild = BuildConfiguration.bUseAdaptiveUnityBuild && !BuildConfiguration.bStressTestUnity;

            // Optimization only makes sense if PCH files are enabled.
            bool bForceIntoSingleUnityFile = BuildConfiguration.bStressTestUnity || (TotalBytesInCPPFiles < BuildConfiguration.NumIncludedBytesPerUnityCPP * 2 && CompileEnvironment.ShouldUsePCHs());

            // Build the list of unity files.
            List <FileCollection> AllUnityFiles;
            {
                // Sort the incoming file paths alphabetically, so there will be consistency in unity blobs across multiple machines.
                // Note that we're relying on this not only sorting files within each directory, but also the directories
                // themselves, so the whole list of file paths is the same across computers.
                List <FileItem> SortedCPPFiles = CPPFiles.GetRange(0, CPPFiles.Count);
                {
                    // Case-insensitive file path compare, because you never know what is going on with local file systems
                    Comparison <FileItem> FileItemComparer = (FileA, FileB) => { return(FileA.AbsolutePath.ToLowerInvariant().CompareTo(FileB.AbsolutePath.ToLowerInvariant())); };
                    SortedCPPFiles.Sort(FileItemComparer);
                }


                // Figure out whether we REALLY want to use adaptive unity for this module.  If nearly every file in the module appears in the working
                // set, we'll just go ahead and let unity build do its thing.
                if (bUseAdaptiveUnityBuild)
                {
                    int CandidateWorkingSetSourceFileCount = 0;
                    int WorkingSetSourceFileCount          = 0;
                    foreach (FileItem CPPFile in SortedCPPFiles)
                    {
                        // Don't include writable source files into unity blobs
                        if (!CPPFile.Reference.IsUnderDirectory(Target.EngineIntermediateDirectory) &&
                            !CPPFile.Reference.IsUnderDirectory(Target.ProjectIntermediateDirectory))
                        {
                            ++CandidateWorkingSetSourceFileCount;

                            if (UnrealBuildTool.ShouldSourceFileBePartOfWorkingSet(CPPFile.AbsolutePath))
                            {
                                ++WorkingSetSourceFileCount;

                                // Mark this file as part of the working set.  This will be saved into the UBT Makefile so that
                                // the assembler can automatically invalidate the Makefile when the working set changes (allowing this
                                // code to run again, to build up new unity blobs.)
                                SourceFileWorkingSet.Add(CPPFile);
                            }
                        }
                    }

                    if (WorkingSetSourceFileCount >= CandidateWorkingSetSourceFileCount)
                    {
                        // Every single file in the module appears in the working set, so don't bother using adaptive unity for this
                        // module.  Otherwise it would make full builds really slow.
                        bUseAdaptiveUnityBuild = false;
                    }
                }

                UnityFileBuilder CPPUnityFileBuilder          = new UnityFileBuilder(bForceIntoSingleUnityFile ? -1 : BuildConfiguration.NumIncludedBytesPerUnityCPP);
                StringBuilder    AdaptiveUnityBuildInfoString = new StringBuilder();
                foreach (FileItem CPPFile in SortedCPPFiles)
                {
                    if (!bForceIntoSingleUnityFile && CPPFile.AbsolutePath.IndexOf(".GeneratedWrapper.", StringComparison.InvariantCultureIgnoreCase) != -1)
                    {
                        NewCPPFiles.Add(CPPFile);
                    }

                    // When adaptive unity is enabled, go ahead and exclude any source files that we're actively working with
                    if (bUseAdaptiveUnityBuild && SourceFileWorkingSet.Contains(CPPFile))
                    {
                        // Just compile this file normally, not as part of the unity blob
                        NewCPPFiles.Add(CPPFile);

                        // Let the unity file builder know about the file, so that we can retain the existing size of the unity blobs.
                        // This won't actually make the source file part of the unity blob, but it will keep track of how big the
                        // file is so that other existing unity blobs from the same module won't be invalidated.  This prevents much
                        // longer compile times the first time you build after your working file set changes.
                        CPPUnityFileBuilder.AddVirtualFile(CPPFile);

                        string CPPFileName = Path.GetFileName(CPPFile.AbsolutePath);
                        if (AdaptiveUnityBuildInfoString.Length == 0)
                        {
                            AdaptiveUnityBuildInfoString.Append(String.Format("[Adaptive unity build] Excluded from {0} unity file: {1}", BaseName, CPPFileName));
                        }
                        else
                        {
                            AdaptiveUnityBuildInfoString.Append(", " + CPPFileName);
                        }
                    }

                    else
                    {
                        // If adaptive unity build is enabled for this module, add this source file to the set that will invalidate the makefile
                        if (bUseAdaptiveUnityBuild)
                        {
                            CandidateSourceFilesForWorkingSet.Add(CPPFile);
                        }

                        // Compile this file as part of the unity blob
                        CPPUnityFileBuilder.AddFile(CPPFile);

                        // Now that the CPPFile is part of this unity file, we will no longer need to treat it like a root level prerequisite for our
                        // dependency cache, as it is now an "indirect include" from the unity file.  We'll clear out the compile environment
                        // attached to this file.  This prevents us from having to cache all of the indirect includes from these files inside our
                        // dependency cache, which speeds up iterative builds a lot!
                        CPPFile.CachedCPPIncludeInfo = null;
                    }
                }

                if (AdaptiveUnityBuildInfoString.Length > 0)
                {
                    Log.TraceInformation(AdaptiveUnityBuildInfoString.ToString());
                }

                AllUnityFiles = CPPUnityFileBuilder.GetUnityFiles();
            }

            // Create a set of CPP files that combine smaller CPP files into larger compilation units, along with the corresponding
            // actions to compile them.
            int CurrentUnityFileCount = 0;

            foreach (FileCollection UnityFile in AllUnityFiles)
            {
                ++CurrentUnityFileCount;

                StringWriter OutputUnityCPPWriter      = new StringWriter();
                StringWriter OutputUnityCPPWriterExtra = null;

                // add an extra file for UBT to get the #include dependencies from
                if (BuildPlatform.RequiresExtraUnityCPPWriter() == true)
                {
                    OutputUnityCPPWriterExtra = new StringWriter();
                }

                OutputUnityCPPWriter.WriteLine("// This file is automatically generated at compile-time to include some subset of the user-created cpp files.");

                // Add source files to the unity file
                foreach (FileItem CPPFile in UnityFile.Files)
                {
                    OutputUnityCPPWriter.WriteLine("#include \"{0}\"", ToolChain.ConvertPath(CPPFile.AbsolutePath));
                    if (OutputUnityCPPWriterExtra != null)
                    {
                        OutputUnityCPPWriterExtra.WriteLine("#include \"{0}\"", CPPFile.AbsolutePath);
                    }
                }

                // Determine unity file path name
                string UnityCPPFileName;
                if (AllUnityFiles.Count > 1)
                {
                    UnityCPPFileName = string.Format("{0}{1}.{2}_of_{3}.cpp", ModulePrefix, BaseName, CurrentUnityFileCount, AllUnityFiles.Count);
                }
                else
                {
                    UnityCPPFileName = string.Format("{0}{1}.cpp", ModulePrefix, BaseName);
                }
                FileReference UnityCPPFilePath = FileReference.Combine(CompileEnvironment.Config.OutputDirectory, UnityCPPFileName);

                // Write the unity file to the intermediate folder.
                FileItem UnityCPPFile = FileItem.CreateIntermediateTextFile(UnityCPPFilePath, OutputUnityCPPWriter.ToString());
                if (OutputUnityCPPWriterExtra != null)
                {
                    FileItem.CreateIntermediateTextFile(UnityCPPFilePath + ".ex", OutputUnityCPPWriterExtra.ToString());
                }

                UnityCPPFile.RelativeCost = UnityFile.TotalLength;
                NewCPPFiles.Add(UnityCPPFile);

                // Cache information about the unity .cpp dependencies
                // @todo ubtmake urgent: Fails when building remotely for Mac because unity .cpp has an include for a PCH on the REMOTE machine
                UEBuildModuleCPP.CachePCHUsageForModuleSourceFile(CompileEnvironment, UnityCPPFile);
            }

            return(NewCPPFiles);
        }
Example #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(
			UEToolChain ToolChain,
			UEBuildTarget Target,
			List<FileItem> CPPFiles,
			CPPEnvironment CompileEnvironment,
			string BaseName
			)
		{
			List<FileItem> NewCPPFiles = new List<FileItem>();

			UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform);

			// Figure out size of all input files combined. We use this to determine whether to use larger unity threshold or not.
			long TotalBytesInCPPFiles = CPPFiles.Sum(F => F.Info.Length);

			// We have an increased threshold for unity file size if, and only if, all files fit into the same unity file. This
			// is beneficial when dealing with PCH files. The default PCH creation limit is X unity files so if we generate < X 
			// this could be fairly slow and we'd rather bump the limit a bit to group them all into the same unity file.


			// When enabled, UnrealBuildTool will try to determine source files that you are actively iteratively changing, and break those files
			// out of their unity blobs so that you can compile them as individual translation units, much faster than recompiling the entire
			// unity blob each time.
			bool bUseAdaptiveUnityBuild = BuildConfiguration.bUseAdaptiveUnityBuild && !BuildConfiguration.bStressTestUnity;

			// Optimization only makes sense if PCH files are enabled.
			bool bForceIntoSingleUnityFile = BuildConfiguration.bStressTestUnity || (TotalBytesInCPPFiles < BuildConfiguration.NumIncludedBytesPerUnityCPP * 2 && CompileEnvironment.ShouldUsePCHs());

			// Build the list of unity files.
			List<FileCollection> AllUnityFiles;
			{
				// Sort the incoming file paths alphabetically, so there will be consistency in unity blobs across multiple machines.
				// Note that we're relying on this not only sorting files within each directory, but also the directories
				// themselves, so the whole list of file paths is the same across computers.
				List<FileItem> SortedCPPFiles = CPPFiles.GetRange(0, CPPFiles.Count);
				{
					// Case-insensitive file path compare, because you never know what is going on with local file systems
					Comparison<FileItem> FileItemComparer = (FileA, FileB) => { return FileA.AbsolutePath.ToLowerInvariant().CompareTo(FileB.AbsolutePath.ToLowerInvariant()); };
					SortedCPPFiles.Sort(FileItemComparer);
				}


				// Figure out whether we REALLY want to use adaptive unity for this module.  If nearly every file in the module appears in the working
				// set, we'll just go ahead and let unity build do its thing.
				if (bUseAdaptiveUnityBuild)
				{
					int CandidateWorkingSetSourceFileCount = 0;
					int WorkingSetSourceFileCount = 0;
					foreach (FileItem CPPFile in SortedCPPFiles)
					{
						// Don't include writable source files into unity blobs
						if (!CPPFile.Reference.IsUnderDirectory(Target.EngineIntermediateDirectory) &&
							!CPPFile.Reference.IsUnderDirectory(Target.ProjectIntermediateDirectory))
						{
							++CandidateWorkingSetSourceFileCount;

							if (UnrealBuildTool.ShouldSourceFileBePartOfWorkingSet(CPPFile.AbsolutePath))
							{
								++WorkingSetSourceFileCount;

								// Mark this file as part of the working set.  This will be saved into the UBT Makefile so that
								// the assembler can automatically invalidate the Makefile when the working set changes (allowing this
								// code to run again, to build up new unity blobs.)
								SourceFileWorkingSet.Add(CPPFile);
							}
						}
					}

					if (WorkingSetSourceFileCount >= CandidateWorkingSetSourceFileCount)
					{
						// Every single file in the module appears in the working set, so don't bother using adaptive unity for this
						// module.  Otherwise it would make full builds really slow.
						bUseAdaptiveUnityBuild = false;
					}
				}

				UnityFileBuilder CPPUnityFileBuilder = new UnityFileBuilder(bForceIntoSingleUnityFile ? -1 : BuildConfiguration.NumIncludedBytesPerUnityCPP);
				StringBuilder AdaptiveUnityBuildInfoString = new StringBuilder();
				foreach (FileItem CPPFile in SortedCPPFiles)
				{
					if (!bForceIntoSingleUnityFile && CPPFile.AbsolutePath.IndexOf(".GeneratedWrapper.", StringComparison.InvariantCultureIgnoreCase) != -1)
					{
						NewCPPFiles.Add(CPPFile);
					}

					// When adaptive unity is enabled, go ahead and exclude any source files that we're actively working with
					if (bUseAdaptiveUnityBuild && SourceFileWorkingSet.Contains(CPPFile))
					{
						// Just compile this file normally, not as part of the unity blob
						NewCPPFiles.Add(CPPFile);

						// Let the unity file builder know about the file, so that we can retain the existing size of the unity blobs.
						// This won't actually make the source file part of the unity blob, but it will keep track of how big the
						// file is so that other existing unity blobs from the same module won't be invalidated.  This prevents much
						// longer compile times the first time you build after your working file set changes.
						CPPUnityFileBuilder.AddVirtualFile(CPPFile);

						string CPPFileName = Path.GetFileName(CPPFile.AbsolutePath);
						if (AdaptiveUnityBuildInfoString.Length == 0)
						{
							AdaptiveUnityBuildInfoString.Append(String.Format("[Adaptive unity build] Excluded from {0} unity file: {1}", BaseName, CPPFileName));
						}
						else
						{
							AdaptiveUnityBuildInfoString.Append(", " + CPPFileName);
						}
					}

					else
					{
						// If adaptive unity build is enabled for this module, add this source file to the set that will invalidate the makefile
						if(bUseAdaptiveUnityBuild)
						{
							CandidateSourceFilesForWorkingSet.Add(CPPFile);
						}

						// Compile this file as part of the unity blob
						CPPUnityFileBuilder.AddFile(CPPFile);

						// Now that the CPPFile is part of this unity file, we will no longer need to treat it like a root level prerequisite for our
						// dependency cache, as it is now an "indirect include" from the unity file.  We'll clear out the compile environment
						// attached to this file.  This prevents us from having to cache all of the indirect includes from these files inside our
						// dependency cache, which speeds up iterative builds a lot!
						CPPFile.CachedCPPIncludeInfo = null;
					}
				}

				if (AdaptiveUnityBuildInfoString.Length > 0)
				{
					Log.TraceInformation(AdaptiveUnityBuildInfoString.ToString());
				}

				AllUnityFiles = CPPUnityFileBuilder.GetUnityFiles();
			}

			string PCHHeaderNameInCode = CPPFiles[0].PCHHeaderNameInCode;
			if (CompileEnvironment.Config.PrecompiledHeaderIncludeFilename != null)
			{
				PCHHeaderNameInCode = ToolChain.ConvertPath(CompileEnvironment.Config.PrecompiledHeaderIncludeFilename.FullName);

				// Generated unity .cpp files always include the PCH using an absolute path, so we need to update
				// our compile environment's PCH header name to use this instead of the text it pulled from the original
				// C++ source files
				CompileEnvironment.Config.PCHHeaderNameInCode = PCHHeaderNameInCode;
			}

			// Create a set of CPP files that combine smaller CPP files into larger compilation units, along with the corresponding 
			// actions to compile them.
			int CurrentUnityFileCount = 0;
			foreach (FileCollection UnityFile in AllUnityFiles)
			{
				++CurrentUnityFileCount;

				StringWriter OutputUnityCPPWriter = new StringWriter();
				StringWriter OutputUnityCPPWriterExtra = null;

				// add an extra file for UBT to get the #include dependencies from
				if (BuildPlatform.RequiresExtraUnityCPPWriter() == true)
				{
					OutputUnityCPPWriterExtra = new StringWriter();
				}

				OutputUnityCPPWriter.WriteLine("// This file is automatically generated at compile-time to include some subset of the user-created cpp files.");

				// Explicitly include the precompiled header first, since Visual C++ expects the first top-level #include to be the header file
				// that was used to create the PCH.
				if (CompileEnvironment.Config.PrecompiledHeaderIncludeFilename != null)
				{
					OutputUnityCPPWriter.WriteLine("#include \"{0}\"", PCHHeaderNameInCode);
					if (OutputUnityCPPWriterExtra != null)
					{
						OutputUnityCPPWriterExtra.WriteLine("#include \"{0}\"", PCHHeaderNameInCode);
					}
				}

				// Add source files to the unity file
				foreach (FileItem CPPFile in UnityFile.Files)
				{
					OutputUnityCPPWriter.WriteLine("#include \"{0}\"", ToolChain.ConvertPath(CPPFile.AbsolutePath));
					if (OutputUnityCPPWriterExtra != null)
					{
						OutputUnityCPPWriterExtra.WriteLine("#include \"{0}\"", CPPFile.AbsolutePath);
					}
				}

				// Determine unity file path name
				string UnityCPPFileName;
				if (AllUnityFiles.Count > 1)
				{
					UnityCPPFileName = string.Format("Module.{0}.{1}_of_{2}.cpp", BaseName, CurrentUnityFileCount, AllUnityFiles.Count);
				}
				else
				{
					UnityCPPFileName = string.Format("Module.{0}.cpp", BaseName);
				}
				FileReference UnityCPPFilePath = FileReference.Combine(CompileEnvironment.Config.OutputDirectory, UnityCPPFileName);

				// Write the unity file to the intermediate folder.
				FileItem UnityCPPFile = FileItem.CreateIntermediateTextFile(UnityCPPFilePath, OutputUnityCPPWriter.ToString());
				if (OutputUnityCPPWriterExtra != null)
				{
					FileItem.CreateIntermediateTextFile(UnityCPPFilePath + ".ex", OutputUnityCPPWriterExtra.ToString());
				}

				UnityCPPFile.RelativeCost = UnityFile.TotalLength;
				UnityCPPFile.PCHHeaderNameInCode = PCHHeaderNameInCode;
				NewCPPFiles.Add(UnityCPPFile);

				// Cache information about the unity .cpp dependencies
				// @todo ubtmake urgent: Fails when building remotely for Mac because unity .cpp has an include for a PCH on the REMOTE machine
				UEBuildModuleCPP.CachePCHUsageForModuleSourceFile(Target, CompileEnvironment, UnityCPPFile);
			}

			return NewCPPFiles;
		}