/// <summary>
        /// Gets the timestamp of CoreUObject.generated.cpp file.
        /// </summary>
        /// <returns>Last write time of CoreUObject.generated.cpp or DateTime.MaxValue if it doesn't exist.</returns>
        private static DateTime GetCoreGeneratedTimestamp(string ModuleName, string ModuleGeneratedCodeDirectory)
        {
            DateTime Timestamp;

            if (UnrealBuildTool.RunningRocket())
            {
                // In Rocket, we don't check the timestamps on engine headers.  Default to a very old date.
                Timestamp = DateTime.MinValue;
            }
            else
            {
                string CoreGeneratedFilename = Path.Combine(ModuleGeneratedCodeDirectory, ModuleName + ".generated.cpp");
                if (File.Exists(CoreGeneratedFilename))
                {
                    Timestamp = new FileInfo(CoreGeneratedFilename).LastWriteTime;
                }
                else
                {
                    // Doesn't exist, so use a 'newer that everything' date to force rebuild headers.
                    Timestamp = DateTime.MaxValue;
                }
            }

            return(Timestamp);
        }
Example #2
0
        /// <summary>
        /// Generates a full path to action history file for the specified target.
        /// </summary>
        public static FileReference GeneratePathForTarget(UEBuildTarget Target)
        {
            DirectoryReference Folder = null;

            if (Target.ShouldCompileMonolithic() || Target.TargetType == TargetRules.TargetType.Program)
            {
                // Monolithic configs and programs have their Action History stored in their respective project folders
                // or under engine intermediate folder + program name folder
                DirectoryReference RootDirectory;
                if (Target.ProjectFile != null)
                {
                    RootDirectory = Target.ProjectFile.Directory;
                }
                else
                {
                    RootDirectory = UnrealBuildTool.EngineDirectory;
                }
                Folder = DirectoryReference.Combine(RootDirectory, BuildConfiguration.PlatformIntermediateFolder, Target.GetTargetName());
            }
            else
            {
                // Shared action history (unless this is a rocket target)
                Folder = (UnrealBuildTool.RunningRocket() && Target.ProjectFile != null) ?
                         DirectoryReference.Combine(Target.ProjectFile.Directory, BuildConfiguration.BaseIntermediateFolder) :
                         DirectoryReference.Combine(UnrealBuildTool.EngineDirectory, BuildConfiguration.BaseIntermediateFolder);
            }
            return(FileReference.Combine(Folder, "ActionHistory.bin"));
        }
Example #3
0
        /** Updates the intermediate include directory timestamps of all the passed in UObject modules */
        private static void UpdateDirectoryTimestamps(List <UHTModuleInfo> UObjectModules)
        {
            foreach (var Module in UObjectModules)
            {
                string GeneratedCodeDirectory     = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase);
                var    GeneratedCodeDirectoryInfo = new DirectoryInfo(GeneratedCodeDirectory);

                try
                {
                    if (GeneratedCodeDirectoryInfo.Exists)
                    {
                        if (UnrealBuildTool.RunningRocket())
                        {
                            // If it is an Engine folder and we are building a rocket project do NOT update the timestamp!
                            // @todo Rocket: This contains check is hacky/fragile
                            string FullGeneratedCodeDirectory = GeneratedCodeDirectoryInfo.FullName;
                            FullGeneratedCodeDirectory = FullGeneratedCodeDirectory.Replace("\\", "/");
                            if (FullGeneratedCodeDirectory.Contains("Engine/Intermediate/Build"))
                            {
                                continue;
                            }

                            // Skip checking timestamps for engine plugin intermediate headers in Rocket
                            PluginInfo Info = Plugins.GetPluginInfoForModule(Module.ModuleName);
                            if (Info != null)
                            {
                                if (Info.LoadedFrom == PluginInfo.LoadedFromType.Engine)
                                {
                                    continue;
                                }
                            }
                        }

                        // Touch the include directory since we have technically 'generated' the headers
                        // However, the headers might not be touched at all since that would cause the compiler to recompile everything
                        // We can't alter the directory timestamp directly, because this may throw exceptions when the directory is
                        // open in visual studio or windows explorer, so instead we create a blank file that will change the timestamp for us
                        string TimestampFile = GeneratedCodeDirectoryInfo.FullName + Path.DirectorySeparatorChar + @"Timestamp";

                        if (!GeneratedCodeDirectoryInfo.Exists)
                        {
                            GeneratedCodeDirectoryInfo.Create();
                        }

                        if (File.Exists(TimestampFile))
                        {
                            File.Delete(TimestampFile);
                        }
                        using (File.Create(TimestampFile))
                        {
                        }
                    }
                }
                catch (Exception Exception)
                {
                    throw new BuildException(Exception, "Couldn't touch header directories: " + Exception.Message);
                }
            }
        }
        /// <summary>
        /// Validates the configuration. E.g. some options are mutually exclusive whereof some imply others. Also
        /// some functionality is not available on all platforms.
        /// @warning: the order of validation is important
        /// </summary>
        /// <param name="Configuration">Current configuration (e.g. development, debug, ...)</param>
        /// <param name="Platform">Current platform (e.g. Win32, PS3, ...)</param>
        /// <param name="bCreateDebugInfo">True if debug info should be created</param>
        public static void ValidateConfiguration(CPPTargetConfiguration Configuration, CPPTargetPlatform Platform, bool bCreateDebugInfo)
        {
            var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(Platform);

            // E&C support.
            if (bSupportEditAndContinue)
            {
                bUseIncrementalLinking = BuildPlatform.ShouldUseIncrementalLinking(Platform, Configuration);
            }

            // Incremental linking.
            if (bUseIncrementalLinking)
            {
                bUsePDBFiles = BuildPlatform.ShouldUsePDBFiles(Platform, Configuration, bCreateDebugInfo);
            }

            // Detailed stats
            if (bLogDetailedActionStats && bAllowXGE)
            {
                // Some build machines apparently have this turned on, so if you really want detailed stats, don't run with XGE
                bLogDetailedActionStats = false;
            }

            if (UnrealBuildTool.RunningRocket())
            {
                bAllowXGE = false;
            }

            // PDB
            if (bUsePDBFiles)
            {
                // NOTE: Currently we allow XGE to run, even with PDBs, until we notice an issue with this
                bool bDisallowXGEWithPDBFiles = false;
                if (bDisallowXGEWithPDBFiles)
                {
                    // Force local execution as we have one PDB for all files using the same PCH. This currently doesn't
                    // scale well with XGE due to required networking bandwidth. Xoreax mentioned that this was going to
                    // be fixed in a future version of the software.
                    bAllowXGE = false;
                }
            }

            // Allow for the build platform to perform custom validation here...
            // NOTE: This CAN modify the static BuildConfiguration settings!!!!
            BuildPlatform.ValidateBuildConfiguration(Configuration, Platform, bCreateDebugInfo);

            if (!BuildPlatform.CanUseXGE())
            {
                bAllowXGE = false;
            }

            if (!BuildPlatform.CanUseDistcc())
            {
                bAllowDistcc = false;
            }
        }
Example #5
0
        /// <summary>
        /// Loads JunkManifest.txt file and removes all junk files/folders defined in it.
        /// </summary>
        static public void DeleteJunk()
        {
            var JunkStartTime = DateTime.UtcNow;

            if (UnrealBuildTool.RunningRocket() == false)
            {
                List <string> JunkManifest = LoadJunkManifest();
                DeleteAllJunk(JunkManifest);
            }

            if (BuildConfiguration.bPrintPerformanceInfo)
            {
                var JunkTime = (DateTime.UtcNow - JunkStartTime).TotalSeconds;
                Log.TraceInformation("DeleteJunk took " + JunkTime + "s");
            }
        }
Example #6
0
        public UHTManifest(UEBuildTarget Target, string InRootLocalPath, string InRootBuildPath, IEnumerable <UHTModuleInfo> ModuleInfo)
        {
            IsGameTarget  = TargetRules.IsGameType(Target.TargetType);
            RootLocalPath = InRootLocalPath;
            RootBuildPath = InRootBuildPath;
            TargetName    = Target.GetTargetName();

            Modules = ModuleInfo.Select(Info => new Module {
                Name                     = Info.ModuleName,
                ModuleType               = Info.ModuleType,
                BaseDirectory            = Info.ModuleDirectory,
                IncludeBase              = Info.ModuleDirectory,
                OutputDirectory          = Path.GetDirectoryName(Info.GeneratedCPPFilenameBase),
                ClassesHeaders           = Info.PublicUObjectClassesHeaders.Select((Header) => Header.AbsolutePath).ToList(),
                PublicHeaders            = Info.PublicUObjectHeaders.Select((Header) => Header.AbsolutePath).ToList(),
                PrivateHeaders           = Info.PrivateUObjectHeaders.Select((Header) => Header.AbsolutePath).ToList(),
                PCH                      = Info.PCH,
                GeneratedCPPFilenameBase = Info.GeneratedCPPFilenameBase,
                //@todo.Rocket: This assumes Engine/Source is a 'safe' folder name to check for
                SaveExportedHeaders = !UnrealBuildTool.RunningRocket() || !Info.ModuleDirectory.Contains("Engine\\Source\\")
            }).ToList();
        }
Example #7
0
        /**
         *	Register the platform with the UEBuildPlatform class
         */
        protected override void RegisterBuildPlatformInternal()
        {
            //@todo.Rocket: Add platform support
            if (UnrealBuildTool.RunningRocket() || Utils.IsRunningOnMono)
            {
                return;
            }

            if ((ProjectFileGenerator.bGenerateProjectFiles == true) || (IsVisualStudioInstalled() == true))
            {
                bool bRegisterBuildPlatform = true;

                // We also need to check for the generated projects... to handle the case where someone generates projects w/out WinRT.
                // Hardcoding this for now - but ideally it would be dynamically discovered.
                string EngineSourcePath = Path.Combine(ProjectFileGenerator.EngineRelativePath, "Source");
                string WinRTRHIFile     = Path.Combine(EngineSourcePath, "Runtime", "Windows", "D3D11RHI", "D3D11RHI.build.cs");
                if (File.Exists(WinRTRHIFile) == false)
                {
                    bRegisterBuildPlatform = false;
                }

                if (bRegisterBuildPlatform == true)
                {
                    // Register this build platform for WinRT
                    Log.TraceVerbose("        Registering for {0}", UnrealTargetPlatform.WinRT.ToString());
                    UEBuildPlatform.RegisterBuildPlatform(UnrealTargetPlatform.WinRT, this);
                    UEBuildPlatform.RegisterPlatformWithGroup(UnrealTargetPlatform.WinRT, UnrealPlatformGroup.Microsoft);

                    // For now only register WinRT_ARM is truly a Windows 8 machine.
                    // This will prevent people who do all platform builds from running into the compiler issue.
                    if (WinRTPlatform.IsWindows8() == true)
                    {
                        Log.TraceVerbose("        Registering for {0}", UnrealTargetPlatform.WinRT_ARM.ToString());
                        UEBuildPlatform.RegisterBuildPlatform(UnrealTargetPlatform.WinRT_ARM, this);
                        UEBuildPlatform.RegisterPlatformWithGroup(UnrealTargetPlatform.WinRT_ARM, UnrealPlatformGroup.Microsoft);
                    }
                }
            }
        }
Example #8
0
        /**
         *	Register the platform with the UEBuildPlatform class
         */
        protected override void RegisterBuildPlatformInternal()
        {
            //@todo.Rocket: Add platform support
            if (UnrealBuildTool.RunningRocket() || UnrealBuildTool.BuildingRocket())
            {
                return;
            }

            // Make sure the SDK is installed
            if ((ProjectFileGenerator.bGenerateProjectFiles == true) || (HasRequiredSDKsInstalled() == SDKStatus.Valid))
            {
                bool bRegisterBuildPlatform = true;

                // make sure we have the HTML5 files; if not, then this user doesn't really have HTML5 access/files, no need to compile HTML5!
                string EngineSourcePath        = Path.Combine(ProjectFileGenerator.EngineRelativePath, "Source");
                string HTML5TargetPlatformFile = Path.Combine(EngineSourcePath, "Developer", "HTML5", "HTML5TargetPlatform", "HTML5TargetPlatform.Build.cs");
                if ((File.Exists(HTML5TargetPlatformFile) == false))
                {
                    bRegisterBuildPlatform = false;
                    Log.TraceWarning("Missing required components (.... HTML5TargetPlatformFile, others here...). Check source control filtering, or try resyncing.");
                }

                if (bRegisterBuildPlatform == true)
                {
                    // Register this build platform for HTML5
                    Log.TraceVerbose("        Registering for {0}", UnrealTargetPlatform.HTML5.ToString());
                    UEBuildPlatform.RegisterBuildPlatform(UnrealTargetPlatform.HTML5, this);
                    if (GetActiveArchitecture() == "-win32")
                    {
                        UEBuildPlatform.RegisterPlatformWithGroup(UnrealTargetPlatform.HTML5, UnrealPlatformGroup.Simulator);
                    }
                    else
                    {
                        UEBuildPlatform.RegisterPlatformWithGroup(UnrealTargetPlatform.HTML5, UnrealPlatformGroup.Device);
                    }
                }
            }
        }
Example #9
0
        /// <summary>
        /// Generates a full path to action history file for the specified target.
        /// </summary>
        public static string GeneratePathForTarget(UEBuildTarget Target)
        {
            string Folder = null;

            if (Target.ShouldCompileMonolithic() || Target.TargetType == TargetRules.TargetType.Program)
            {
                // Monolithic configs and programs have their Action History stored in their respective project folders
                // or under engine intermediate folder + program name folder
                string RootDirectory = UnrealBuildTool.GetUProjectPath();
                if (String.IsNullOrEmpty(RootDirectory))
                {
                    RootDirectory = Path.GetFullPath(BuildConfiguration.RelativeEnginePath);
                }
                Folder = Path.Combine(RootDirectory, BuildConfiguration.PlatformIntermediateFolder, Target.GetTargetName());
            }
            else
            {
                // Shared action history (unless this is a rocket target)
                Folder = UnrealBuildTool.RunningRocket() ?
                         Path.Combine(UnrealBuildTool.GetUProjectPath(), BuildConfiguration.BaseIntermediateFolder) :
                         BuildConfiguration.BaseIntermediatePath;
            }
            return(Path.Combine(Folder, "ActionHistory.bin").Replace("\\", "/"));
        }
        /// <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);
        }
        protected override bool WriteMasterProjectFile(ProjectFile UBTProject)
        {
            bool bSuccess = true;

            var SolutionFileName = MasterProjectName + ".sln";

            // Setup solution file content
            var VCSolutionFileContent = new StringBuilder();


            // Solution file header
            if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2013)
            {
                VCSolutionFileContent.Append(
                    ProjectFileGenerator.NewLine +
                    "Microsoft Visual Studio Solution File, Format Version 12.00" + ProjectFileGenerator.NewLine +
                    "# Visual Studio 2013" + ProjectFileGenerator.NewLine);

                /* This is not required by VS 2013 to load the projects
                 * VCSolutionFileContent.Append(
                 *      "VisualStudioVersion = 12.0.20617.1 PREVIEW" + ProjectFileGenerator.NewLine +
                 *      "MinimumVisualStudioVersion = 10.0.40219.1" + ProjectFileGenerator.NewLine );*/
            }
            else if (ProjectFileFormat == VCProjectFileFormat.VisualStudio2012)
            {
                VCSolutionFileContent.Append(
                    ProjectFileGenerator.NewLine +
                    "Microsoft Visual Studio Solution File, Format Version 12.00" + ProjectFileGenerator.NewLine +
                    "# Visual Studio 2012" + ProjectFileGenerator.NewLine);
            }
            else
            {
                throw new BuildException("Unexpected ProjectFileFormat");
            }


            // Solution folders, files and project entries
            {
                // This the GUID that Visual Studio uses to identify a solution folder
                var SolutionFolderEntryGUID = "{2150E333-8FDC-42A3-9474-1A3956D46DE8}";

                // Solution folders
                {
                    var AllSolutionFolders = new List <MasterProjectFolder>();
                    System.Action <List <MasterProjectFolder> /* Folders */> GatherFoldersFunction = null;
                    GatherFoldersFunction = FolderList =>
                    {
                        AllSolutionFolders.AddRange(FolderList);
                        foreach (var CurSubFolder in FolderList)
                        {
                            GatherFoldersFunction(CurSubFolder.SubFolders);
                        }
                    };
                    GatherFoldersFunction(RootFolder.SubFolders);

                    foreach (VisualStudioSolutionFolder CurFolder in AllSolutionFolders)
                    {
                        var FolderGUIDString = CurFolder.FolderGUID.ToString("B").ToUpperInvariant();
                        VCSolutionFileContent.Append(
                            "Project(\"" + SolutionFolderEntryGUID + "\") = \"" + CurFolder.FolderName + "\", \"" + CurFolder.FolderName + "\", \"" + FolderGUIDString + "\"" + ProjectFileGenerator.NewLine);

                        // Add any files that are inlined right inside the solution folder
                        if (CurFolder.Files.Count > 0)
                        {
                            VCSolutionFileContent.Append(
                                "	ProjectSection(SolutionItems) = preProject"+ ProjectFileGenerator.NewLine);
                            foreach (var CurFile in CurFolder.Files)
                            {
                                // Syntax is:  <relative file path> = <relative file path>
                                VCSolutionFileContent.Append(
                                    "		"+ CurFile + " = " + CurFile + ProjectFileGenerator.NewLine);
                            }
                            VCSolutionFileContent.Append(
                                "	EndProjectSection"+ ProjectFileGenerator.NewLine);
                        }

                        VCSolutionFileContent.Append(
                            "EndProject" + ProjectFileGenerator.NewLine
                            );
                    }
                }


                // Project files
                foreach (MSBuildProjectFile CurProject in AllProjectFiles)
                {
                    // Visual Studio uses different GUID types depending on the project type
                    string ProjectTypeGUID = CurProject.ProjectTypeGUID;

                    // NOTE: The project name in the solution doesn't actually *have* to match the project file name on disk.  However,
                    //       we prefer it when it does match so we use the actual file name here.
                    var ProjectNameInSolution = Path.GetFileNameWithoutExtension(CurProject.ProjectFilePath);

                    // Use the existing project's GUID that's already known to us
                    var ProjectGUID = CurProject.ProjectGUID.ToString("B").ToUpperInvariant();

                    VCSolutionFileContent.Append(
                        "Project(\"" + ProjectTypeGUID + "\") = \"" + ProjectNameInSolution + "\", \"" + CurProject.RelativeProjectFilePath + "\", \"" + ProjectGUID + "\"" + ProjectFileGenerator.NewLine);

                    // Setup dependency on UnrealBuildTool, if we need that.  This makes sure that UnrealBuildTool is
                    // freshly compiled before kicking off any build operations on this target project
                    if (!CurProject.IsStubProject)
                    {
                        // Don't add self as a project dependency!
                        if ((CurProject != UBTProject) && (CurProject.IsGeneratedProject || (CurProject.DependsOnProjects.Count > 0)))
                        {
                            VCSolutionFileContent.Append(
                                "	ProjectSection(ProjectDependencies) = postProject"+ ProjectFileGenerator.NewLine);

                            if (CurProject.IsGeneratedProject && UBTProject != null)
                            {
                                var UBTProjectGUID = ((MSBuildProjectFile)UBTProject).ProjectGUID.ToString("B").ToUpperInvariant();
                                VCSolutionFileContent.Append(
                                    "		"+ UBTProjectGUID + " = " + UBTProjectGUID + ProjectFileGenerator.NewLine);
                            }

                            // Setup any addition dependencies this project has...
                            foreach (var DependsOnProject in CurProject.DependsOnProjects)
                            {
                                var DependsOnProjectGUID = ((MSBuildProjectFile)DependsOnProject).ProjectGUID.ToString("B").ToUpperInvariant();
                                VCSolutionFileContent.Append(
                                    "		"+ DependsOnProjectGUID + " = " + DependsOnProjectGUID + ProjectFileGenerator.NewLine);
                            }

                            VCSolutionFileContent.Append(
                                "	EndProjectSection"+ ProjectFileGenerator.NewLine);
                        }
                    }

                    VCSolutionFileContent.Append(
                        "EndProject" + ProjectFileGenerator.NewLine
                        );
                }
            }

            // Solution configuration platforms.  This is just a list of all of the platforms and configurations that
            // appear in Visual Studio's build configuration selector.
            var SolutionConfigCombinations = new List <VCSolutionConfigCombination>();

            // The "Global" section has source control, solution configurations, project configurations,
            // preferences, and project hierarchy data
            {
                VCSolutionFileContent.Append(
                    "Global" + ProjectFileGenerator.NewLine);
                {
                    {
                        VCSolutionFileContent.Append(
                            "	GlobalSection(SolutionConfigurationPlatforms) = preSolution"+ ProjectFileGenerator.NewLine);

                        var SolutionConfigurationsValidForProjects = new Dictionary <string, Tuple <UnrealTargetConfiguration, string> >();
                        var PlatformsValidForProjects = new HashSet <UnrealTargetPlatform>();

                        foreach (var CurConfiguration in SupportedConfigurations)
                        {
                            if (UnrealBuildTool.IsValidConfiguration(CurConfiguration))
                            {
                                foreach (var CurPlatform in SupportedPlatforms)
                                {
                                    if (UnrealBuildTool.IsValidPlatform(CurPlatform))
                                    {
                                        foreach (var CurProject in AllProjectFiles)
                                        {
                                            if (!CurProject.IsStubProject)
                                            {
                                                if (CurProject.ProjectTargets.Count == 0)
                                                {
                                                    throw new BuildException("Expecting project '" + CurProject.ProjectFilePath + "' to have at least one ProjectTarget associated with it!");
                                                }

                                                // Figure out the set of valid target configuration names
                                                foreach (var ProjectTarget in CurProject.ProjectTargets)
                                                {
                                                    if (VCProjectFile.IsValidProjectPlatformAndConfiguration(ProjectTarget, CurPlatform, CurConfiguration))
                                                    {
                                                        PlatformsValidForProjects.Add(CurPlatform);

                                                        // Default to a target configuration name of "Game", since that will collapse down to an empty string
                                                        var TargetConfigurationName = TargetRules.TargetType.Game.ToString();
                                                        if (ProjectTarget.TargetRules != null)
                                                        {
                                                            TargetConfigurationName = ProjectTarget.TargetRules.ConfigurationName;
                                                        }

                                                        var SolutionConfigName = MakeSolutionConfigurationName(CurConfiguration, TargetConfigurationName);
                                                        SolutionConfigurationsValidForProjects[SolutionConfigName] = new Tuple <UnrealTargetConfiguration, string>(CurConfiguration, TargetConfigurationName);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }

                        foreach (var CurPlatform in PlatformsValidForProjects)
                        {
                            foreach (var SolutionConfigKeyValue in SolutionConfigurationsValidForProjects)
                            {
                                // e.g.  "Development|Win64 = Development|Win64"
                                var SolutionConfigName      = SolutionConfigKeyValue.Key;
                                var Configuration           = SolutionConfigKeyValue.Value.Item1;
                                var TargetConfigurationName = SolutionConfigKeyValue.Value.Item2;

                                var SolutionPlatformName = CurPlatform.ToString();

                                // For Rocket, there are currently no targets that are valid to build both for Win32 and Win64.  So we simply things by
                                // only displaying a "Windows" platform and building for the appropriate Windows platform automatically based on whichever
                                // configuration they have selected.
                                if (UnrealBuildTool.RunningRocket() && (CurPlatform == UnrealTargetPlatform.Win32 || CurPlatform == UnrealTargetPlatform.Win64))
                                {
                                    SolutionPlatformName = "Windows";
                                    if (Configuration == UnrealTargetConfiguration.Shipping)
                                    {
                                        if (CurPlatform != UnrealTargetPlatform.Win32)
                                        {
                                            continue;
                                        }
                                    }
                                    else
                                    {
                                        if (CurPlatform != UnrealTargetPlatform.Win64)
                                        {
                                            continue;
                                        }
                                    }
                                }

                                var SolutionConfigAndPlatformPair = SolutionConfigName + "|" + SolutionPlatformName;
                                SolutionConfigCombinations.Add(
                                    new VCSolutionConfigCombination
                                {
                                    VCSolutionConfigAndPlatformName = SolutionConfigAndPlatformPair,
                                    Configuration           = Configuration,
                                    Platform                = CurPlatform,
                                    TargetConfigurationName = TargetConfigurationName
                                }
                                    );
                            }
                        }

                        // Sort the list of solution platform strings alphabetically (Visual Studio prefers it)
                        SolutionConfigCombinations.Sort(
                            new Comparison <VCSolutionConfigCombination>(
                                (x, y) => { return(String.Compare(x.VCSolutionConfigAndPlatformName, y.VCSolutionConfigAndPlatformName, StringComparison.InvariantCultureIgnoreCase)); }
                                )
                            );

                        var AppendedSolutionConfigAndPlatformNames = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase);
                        foreach (var SolutionConfigCombination in SolutionConfigCombinations)
                        {
                            // We alias "Game" and "Program" to both have the same solution configuration, so we're careful not to add the same combination twice.
                            if (!AppendedSolutionConfigAndPlatformNames.Contains(SolutionConfigCombination.VCSolutionConfigAndPlatformName))
                            {
                                VCSolutionFileContent.Append(
                                    "		"+ SolutionConfigCombination.VCSolutionConfigAndPlatformName + " = " + SolutionConfigCombination.VCSolutionConfigAndPlatformName + ProjectFileGenerator.NewLine);
                                AppendedSolutionConfigAndPlatformNames.Add(SolutionConfigCombination.VCSolutionConfigAndPlatformName);
                            }
                        }

                        VCSolutionFileContent.Append(
                            "	EndGlobalSection"+ ProjectFileGenerator.NewLine);
                    }


                    // Assign each project's "project configuration" to our "solution platform + configuration" pairs.  This
                    // also sets up which projects are actually built when building the solution.
                    {
                        VCSolutionFileContent.Append(
                            "	GlobalSection(ProjectConfigurationPlatforms) = postSolution"+ ProjectFileGenerator.NewLine);

                        var CombinationsThatWereMatchedToProjects = new List <VCSolutionConfigCombination>();

                        foreach (MSBuildProjectFile CurProject in AllProjectFiles)
                        {
                            // NOTE: We don't emit solution configuration entries for "stub" projects.  Those projects are only
                            // built using UnrealBuildTool and don't require a presence in the solution project list

                            // NOTE: We also process projects that were "imported" here, hoping to match those to our solution
                            //       configurations.  In some cases this may not be successful though.  Imported projects
                            //       should always be carefully setup to match our project generator's solution configs.
                            if (!CurProject.IsStubProject)
                            {
                                if (CurProject.ProjectTargets.Count == 0)
                                {
                                    throw new BuildException("Expecting project '" + CurProject.ProjectFilePath + "' to have at least one ProjectTarget associated with it!");
                                }
                                var IsProgramProject = CurProject.ProjectTargets[0].TargetRules != null && CurProject.ProjectTargets[0].TargetRules.Type == TargetRules.TargetType.Program;

                                var GameOrProgramConfigsAlreadyMapped = new HashSet <string>();
                                foreach (var SolutionConfigCombination in SolutionConfigCombinations)
                                {
                                    // Handle aliasing of Program and Game target configuration names
                                    if ((IsProgramProject && GameOrProgramConfigsAlreadyMapped.Add(SolutionConfigCombination.VCSolutionConfigAndPlatformName)) ||
                                        IsProgramProject && SolutionConfigCombination.TargetConfigurationName != TargetRules.TargetType.Game.ToString() ||
                                        !IsProgramProject && SolutionConfigCombination.TargetConfigurationName != TargetRules.TargetType.Program.ToString())
                                    {
                                        var TargetConfigurationName = SolutionConfigCombination.TargetConfigurationName;
                                        if (IsProgramProject && TargetConfigurationName == TargetRules.TargetType.Game.ToString())
                                        {
                                            TargetConfigurationName = TargetRules.TargetType.Program.ToString();
                                        }

                                        // Now, we want to find a target in this project that maps to the current solution config combination.  Only up to one target should
                                        // and every solution config combination should map to at least one target in one project (otherwise we shouldn't have added it!).
                                        ProjectTarget MatchingProjectTarget = null;
                                        foreach (var ProjectTarget in CurProject.ProjectTargets)
                                        {
                                            bool IsMatchingCombination = VCProjectFile.IsValidProjectPlatformAndConfiguration(ProjectTarget, SolutionConfigCombination.Platform, SolutionConfigCombination.Configuration);
                                            if (ProjectTarget.TargetRules != null)
                                            {
                                                if (TargetConfigurationName != ProjectTarget.TargetRules.ConfigurationName)
                                                {
                                                    // Solution configuration name for this combination doesn't match this target's configuration name.  It's not buildable.
                                                    IsMatchingCombination = false;
                                                }
                                            }
                                            else
                                            {
                                                // UBT gets a pass because it is a dependency of every single configuration combination
                                                if (CurProject != UBTProject &&
                                                    !CurProject.ShouldBuildForAllSolutionTargets &&
                                                    TargetConfigurationName != TargetRules.TargetType.Game.ToString())
                                                {
                                                    // Can't build non-generated project in configurations except for the default (Game)
                                                    IsMatchingCombination = false;
                                                }
                                            }

                                            if (IsMatchingCombination)
                                            {
                                                if (MatchingProjectTarget != null)
                                                {
                                                    // Not expecting more than one target to match a single solution configuration per project!
                                                    throw new BuildException("Not expecting more than one target for project " + CurProject.ProjectFilePath + " to match solution configuration " + SolutionConfigCombination.VCSolutionConfigAndPlatformName);
                                                }

                                                MatchingProjectTarget = ProjectTarget;

                                                // NOTE: For faster perf, we could "break" here and bail out early, but that would circumvent the error checking
                                                //		 for multiple targets within a project that may map to a single solution configuration.
                                            }
                                        }

                                        var SolutionConfiguration = SolutionConfigCombination.Configuration;
                                        var SolutionPlatform      = SolutionConfigCombination.Platform;


                                        if (MatchingProjectTarget == null)
                                        {
                                            // The current configuration/platform and target configuration name doesn't map to anything our project actually supports.
                                            // We'll map it to a default config.
                                            SolutionConfiguration = UnrealTargetConfiguration.Development;

                                            // Prefer using Win64 as the default, but fall back to a platform the project file actually supports if needed.  This is for
                                            // projects that can never be compiled in Windows, such as UnrealLaunchDaemon which is an iOS-only program
                                            SolutionPlatform = UnrealTargetPlatform.Win64;
                                            if (CurProject.ProjectTargets[0].TargetRules != null)
                                            {
                                                var ProjectSupportedPlatforms = new List <UnrealTargetPlatform>();
                                                CurProject.ProjectTargets[0].TargetRules.GetSupportedPlatforms(ref ProjectSupportedPlatforms);
                                                if (!ProjectSupportedPlatforms.Contains(SolutionPlatform))
                                                {
                                                    SolutionPlatform = ProjectSupportedPlatforms[0];
                                                }
                                            }


                                            if (IsProgramProject)
                                            {
                                                TargetConfigurationName = TargetRules.TargetType.Program.ToString();
                                            }
                                            else
                                            {
                                                TargetConfigurationName = TargetRules.TargetType.Game.ToString();
                                            }
                                        }


                                        // If the project wants to always build in "Development", regardless of what the solution
                                        // configuration is set to, then we'll do that here.  This is used for projects like
                                        // UnrealBuildTool and ShaderCompileWorker
                                        if (MatchingProjectTarget != null)
                                        {
                                            if (MatchingProjectTarget.ForceDevelopmentConfiguration)
                                            {
                                                SolutionConfiguration = UnrealTargetConfiguration.Development;
                                            }
                                        }


                                        string ProjectConfigName;
                                        string ProjectPlatformName;
                                        CurProject.MakeProjectPlatformAndConfigurationNames(SolutionPlatform, SolutionConfiguration, TargetConfigurationName, out ProjectPlatformName, out ProjectConfigName);

                                        var ProjectConfigAndPlatformPair = ProjectConfigName.ToString() + "|" + ProjectPlatformName.ToString();

                                        // e.g.  "{4232C52C-680F-4850-8855-DC39419B5E9B}.Debug|iOS.ActiveCfg = iOS_Debug|Win32"
                                        var CurProjectGUID = CurProject.ProjectGUID.ToString("B").ToUpperInvariant();
                                        VCSolutionFileContent.Append(
                                            "		"+ CurProjectGUID + "." + SolutionConfigCombination.VCSolutionConfigAndPlatformName + ".ActiveCfg = " + ProjectConfigAndPlatformPair + ProjectFileGenerator.NewLine);


                                        // Set whether this project configuration should be built when the user initiates "build solution"
                                        if (MatchingProjectTarget != null)
                                        {
                                            VCSolutionFileContent.Append(
                                                "		"+ CurProjectGUID + "." + SolutionConfigCombination.VCSolutionConfigAndPlatformName + ".Build.0 = " + ProjectConfigAndPlatformPair + ProjectFileGenerator.NewLine);

                                            var ProjGen = UEPlatformProjectGenerator.GetPlatformProjectGenerator(SolutionConfigCombination.Platform, true);
                                            if (MatchingProjectTarget.ProjectDeploys ||
                                                ((ProjGen != null) && (ProjGen.GetVisualStudioDeploymentEnabled(SolutionPlatform, SolutionConfiguration) == true)))
                                            {
                                                VCSolutionFileContent.Append(
                                                    "		"+ CurProjectGUID + "." + SolutionConfigCombination.VCSolutionConfigAndPlatformName + ".Deploy.0 = " + ProjectConfigAndPlatformPair + ProjectFileGenerator.NewLine);
                                            }
                                        }

                                        CombinationsThatWereMatchedToProjects.Add(SolutionConfigCombination);
                                    }
                                }
                            }
                        }

                        // Check for problems
                        foreach (var SolutionConfigCombination in SolutionConfigCombinations)
                        {
                            if (!CombinationsThatWereMatchedToProjects.Contains(SolutionConfigCombination))
                            {
                                throw new BuildException("Unable to find a ProjectTarget that matches the solution configuration/platform mapping: " + SolutionConfigCombination.Configuration.ToString() + ", " + SolutionConfigCombination.Platform.ToString() + ", " + SolutionConfigCombination.TargetConfigurationName);
                            }
                        }
                        VCSolutionFileContent.Append(
                            "	EndGlobalSection"+ ProjectFileGenerator.NewLine);
                    }


                    // Setup other solution properties
                    {
                        VCSolutionFileContent.Append(
                            "	GlobalSection(SolutionProperties) = preSolution"+ ProjectFileGenerator.NewLine);

                        // HideSolutionNode sets whether or not the top-level solution entry is completely hidden in the UI.
                        // We don't want that, as we need users to be able to right click on the solution tree item.
                        VCSolutionFileContent.Append(
                            "		HideSolutionNode = FALSE"+ ProjectFileGenerator.NewLine);

                        VCSolutionFileContent.Append(
                            "	EndGlobalSection"+ ProjectFileGenerator.NewLine);
                    }



                    // Solution directory hierarchy
                    {
                        VCSolutionFileContent.Append(
                            "	GlobalSection(NestedProjects) = preSolution"+ ProjectFileGenerator.NewLine);

                        // Every entry in this section is in the format "Guid1 = Guid2".  Guid1 is the child project (or solution
                        // filter)'s GUID, and Guid2 is the solution filter directory to parent the child project (or solution
                        // filter) to.  This sets up the hierarchical solution explorer tree for all solution folders and projects.

                        System.Action <StringBuilder /* VCSolutionFileContent */, List <MasterProjectFolder> /* Folders */> FolderProcessorFunction = null;
                        FolderProcessorFunction = (LocalVCSolutionFileContent, LocalMasterProjectFolders) =>
                        {
                            foreach (VisualStudioSolutionFolder CurFolder in LocalMasterProjectFolders)
                            {
                                var CurFolderGUIDString = CurFolder.FolderGUID.ToString("B").ToUpperInvariant();

                                foreach (MSBuildProjectFile ChildProject in CurFolder.ChildProjects)
                                {
                                    //	e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}"
                                    LocalVCSolutionFileContent.Append(
                                        "		"+ ChildProject.ProjectGUID.ToString("B").ToUpperInvariant() + " = " + CurFolderGUIDString + ProjectFileGenerator.NewLine);
                                }

                                foreach (VisualStudioSolutionFolder SubFolder in CurFolder.SubFolders)
                                {
                                    //	e.g. "{BF6FB09F-A2A6-468F-BE6F-DEBE07EAD3EA} = {C43B6BB5-3EF0-4784-B896-4099753BCDA9}"
                                    LocalVCSolutionFileContent.Append(
                                        "		"+ SubFolder.FolderGUID.ToString("B").ToUpperInvariant() + " = " + CurFolderGUIDString + ProjectFileGenerator.NewLine);
                                }

                                // Recurse into subfolders
                                FolderProcessorFunction(LocalVCSolutionFileContent, CurFolder.SubFolders);
                            }
                        };
                        FolderProcessorFunction(VCSolutionFileContent, RootFolder.SubFolders);

                        VCSolutionFileContent.Append(
                            "	EndGlobalSection"+ ProjectFileGenerator.NewLine);
                    }
                }

                VCSolutionFileContent.Append(
                    "EndGlobal" + ProjectFileGenerator.NewLine);
            }


            // Save the solution file
            if (bSuccess)
            {
                var SolutionFilePath = Path.Combine(MasterProjectRelativePath, SolutionFileName);
                bSuccess = WriteFileIfChanged(SolutionFilePath, VCSolutionFileContent.ToString());
            }


            // Save a solution config file which selects the development editor configuration by default.
            if (bSuccess)
            {
                // Figure out the filename for the SUO file. VS2013 will automatically import the VS2012 options if necessary.
                string SolutionOptionsExtension = (ProjectFileFormat == VCProjectFileFormat.VisualStudio2012)? "v11.suo" : "v12.suo";

                // Check it doesn't exist before overwriting it. Since these files store the user's preferences, it'd be bad form to overwrite them.
                string SolutionOptionsFileName = Path.Combine(MasterProjectRelativePath, Path.ChangeExtension(SolutionFileName, SolutionOptionsExtension));
                if (!File.Exists(SolutionOptionsFileName))
                {
                    VCSolutionOptions Options = new VCSolutionOptions();

                    // Set the default configuration and startup project
                    VCSolutionConfigCombination DefaultConfig = SolutionConfigCombinations.Find(x => x.Configuration == UnrealTargetConfiguration.Development && x.Platform == UnrealTargetPlatform.Win64 && x.TargetConfigurationName == "Editor");
                    if (DefaultConfig != null)
                    {
                        List <VCBinarySetting> Settings = new List <VCBinarySetting>();
                        Settings.Add(new VCBinarySetting("ActiveCfg", DefaultConfig.VCSolutionConfigAndPlatformName));
                        if (DefaultProject != null)
                        {
                            Settings.Add(new VCBinarySetting("StartupProject", ((MSBuildProjectFile)DefaultProject).ProjectGUID.ToString("B")));
                        }
                        Options.SetConfiguration(Settings);
                    }

                    // Mark all the projects as closed by default, apart from the startup project
                    VCSolutionExplorerState ExplorerState = new VCSolutionExplorerState();
                    foreach (ProjectFile ProjectFile in AllProjectFiles)
                    {
                        string ProjectName = Path.GetFileNameWithoutExtension(ProjectFile.ProjectFilePath);
                        if (ProjectFile == DefaultProject)
                        {
                            ExplorerState.OpenProjects.Add(new Tuple <string, string[]>(ProjectName, new string[] { ProjectName }));
                        }
                        else
                        {
                            ExplorerState.OpenProjects.Add(new Tuple <string, string[]>(ProjectName, new string[] { }));
                        }
                    }
                    if (IncludeEnginePrograms)
                    {
                        ExplorerState.OpenProjects.Add(new Tuple <string, string[]>("Automation", new string[0]));
                    }
                    Options.SetExplorerState(ExplorerState);

                    // Write the file
                    if (Options.Sections.Count > 0)
                    {
                        Options.Write(SolutionOptionsFileName);
                    }
                }
            }

            return(bSuccess);
        }
        /*
         * Checks to see if the assembly needs compilation
         */
        private static bool RequiresCompilation(List <string> SourceFileNames, string AssemblySourceListFilePath, string OutputAssemblyPath)
        {
            if (UnrealBuildTool.RunningRocket() && ProjectFileGenerator.bGenerateProjectFiles)
            {
                // @todo rocket Do we need a better way to determine if project generation rules modules need to be compiled?
                return(true);
            }

            // Check to see if we already have a compiled assembly file on disk
            FileInfo OutputAssemblyInfo = new FileInfo(OutputAssemblyPath);

            if (OutputAssemblyInfo.Exists)
            {
                // Check the time stamp of the UnrealBuildTool.exe file.  If Unreal Build Tool was compiled more
                // recently than the dynamically-compiled assembly, then we'll always recompile it.  This is
                // because Unreal Build Tool's code may have changed in such a way that invalidate these
                // previously-compiled assembly files.
                if (UBTExecutableFileInfo.LastWriteTimeUtc > OutputAssemblyInfo.LastWriteTimeUtc)
                {
                    // UnrealBuildTool.exe has been recompiled more recently than our cached assemblies
                    Log.TraceVerbose("UnrealBuildTool.exe has been recompiled more recently than " + OutputAssemblyInfo.Name);

                    return(true);
                }
                else
                {
                    // Make sure we have a manifest of source files used to compile the output assembly.  If it doesn't exist
                    // for some reason (not an expected case) then we'll need to recompile.
                    var AssemblySourceListFile = new FileInfo(AssemblySourceListFilePath);
                    if (!AssemblySourceListFile.Exists)
                    {
                        return(true);
                    }
                    else
                    {
                        // Make sure the source files we're compiling are the same as the source files that were compiled
                        // for the assembly that we want to load
                        var ExistingAssemblySourceFileNames = new List <string>();
                        {
                            using (var Reader = AssemblySourceListFile.OpenRead())
                            {
                                using (var TextReader = new StreamReader(Reader))
                                {
                                    for (var ExistingSourceFileName = TextReader.ReadLine(); ExistingSourceFileName != null; ExistingSourceFileName = TextReader.ReadLine())
                                    {
                                        ExistingAssemblySourceFileNames.Add(ExistingSourceFileName);

                                        // Was the existing assembly compiled with a source file that we aren't interested in?  If so, then it needs to be recompiled.
                                        if (!SourceFileNames.Contains(ExistingSourceFileName))
                                        {
                                            return(true);
                                        }
                                    }
                                }
                            }
                        }

                        // Test against source file time stamps
                        foreach (var SourceFileName in SourceFileNames)
                        {
                            // Was the existing assembly compiled without this source file?  If so, then we definitely need to recompile it!
                            if (!ExistingAssemblySourceFileNames.Contains(SourceFileName))
                            {
                                return(true);
                            }

                            var SourceFileInfo = new FileInfo(Path.GetFullPath(SourceFileName));

                            // Check to see if the source file exists
                            if (!SourceFileInfo.Exists)
                            {
                                throw new BuildException("Could not locate source file for dynamic compilation: {0}", SourceFileName);
                            }

                            // Ignore temp files
                            if (!SourceFileInfo.Extension.Equals(".tmp", StringComparison.CurrentCultureIgnoreCase))
                            {
                                // Check to see if the source file is newer than the compiled assembly file.  We don't want to
                                // bother recompiling it if it hasn't changed.
                                if (SourceFileInfo.LastWriteTimeUtc > OutputAssemblyInfo.LastWriteTimeUtc)
                                {
                                    // Source file has changed since we last compiled the assembly, so we'll need to recompile it now!
                                    Log.TraceVerbose(SourceFileInfo.Name + " has been modified more recently than " + OutputAssemblyInfo.Name);

                                    return(true);
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                // File doesn't exist, so we'll definitely have to compile it!
                Log.TraceVerbose(OutputAssemblyInfo.Name + " doesn't exist yet");
                return(true);
            }

            return(false);
        }
Example #13
0
        /**
         * Checks the class header files and determines if generated UObject code files are out of date in comparison.
         * @param UObjectModules	Modules that we generate headers for
         *
         * @return					True if the code files are out of date
         * */
        private static bool AreGeneratedCodeFilesOutOfDate(List <UHTModuleInfo> UObjectModules)
        {
            bool bIsOutOfDate = false;

            // Get UnrealHeaderTool timestamp. If it's newer than generated headers, they need to be rebuilt too.
            var HeaderToolTimestamp = CheckIfUnrealHeaderToolIsUpToDate();

            // Get CoreUObject.generated.cpp timestamp.  If the source files are older than the CoreUObject generated code, we'll
            // need to regenerate code for the module
            DateTime?CoreGeneratedTimestamp = null;

            {
                // Find the CoreUObject module
                foreach (var Module in UObjectModules)
                {
                    if (Module.ModuleName.Equals("CoreUObject", StringComparison.InvariantCultureIgnoreCase))
                    {
                        CoreGeneratedTimestamp = GetCoreGeneratedTimestamp(Module.ModuleName, Path.GetDirectoryName(Module.GeneratedCPPFilenameBase));
                        break;
                    }
                }
                if (CoreGeneratedTimestamp == null)
                {
                    throw new BuildException("Could not find CoreUObject in list of all UObjectModules");
                }
            }


            foreach (var Module in UObjectModules)
            {
                // In Rocket, we skip checking timestamps for modules that don't exist within the project's directory
                if (UnrealBuildTool.RunningRocket())
                {
                    // @todo Rocket: This could be done in a better way I'm sure
                    if (!Utils.IsFileUnderDirectory(Module.ModuleDirectory, UnrealBuildTool.GetUProjectPath()))
                    {
                        // Engine or engine plugin module - Rocket does not regenerate them so don't compare their timestamps
                        continue;
                    }
                }

                // Make sure we have an existing folder for generated code.  If not, then we definitely need to generate code!
                var GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase);
                var TestDirectory          = (FileSystemInfo) new DirectoryInfo(GeneratedCodeDirectory);
                if (TestDirectory.Exists)
                {
                    // Grab our special "Timestamp" file that we saved after the last set of headers were generated.  This file
                    // actually contains the list of source files which contained UObjects, so that we can compare to see if any
                    // UObject source files were deleted (or no longer contain UObjects), which means we need to run UHT even
                    // if no other source files were outdated
                    string TimestampFile          = Path.Combine(GeneratedCodeDirectory, @"Timestamp");
                    var    SavedTimestampFileInfo = (FileSystemInfo) new FileInfo(TimestampFile);
                    if (SavedTimestampFileInfo.Exists)
                    {
                        // Make sure the last UHT run completed after UnrealHeaderTool.exe was compiled last, and after the CoreUObject headers were touched last.
                        var SavedTimestamp = SavedTimestampFileInfo.LastWriteTime;
                        if (SavedTimestamp.CompareTo(HeaderToolTimestamp) > 0 &&
                            SavedTimestamp.CompareTo(CoreGeneratedTimestamp) > 0)
                        {
                            // Iterate over our UObjects headers and figure out if any of them have changed
                            var AllUObjectHeaders = new List <FileItem>();
                            AllUObjectHeaders.AddRange(Module.PublicUObjectClassesHeaders);
                            AllUObjectHeaders.AddRange(Module.PublicUObjectHeaders);
                            AllUObjectHeaders.AddRange(Module.PrivateUObjectHeaders);

                            // Load up the old timestamp file and check to see if anything has changed
                            {
                                var UObjectFilesFromPreviousRun = File.ReadAllLines(TimestampFile);
                                if (AllUObjectHeaders.Count == UObjectFilesFromPreviousRun.Length)
                                {
                                    for (int FileIndex = 0; FileIndex < AllUObjectHeaders.Count; ++FileIndex)
                                    {
                                        if (!UObjectFilesFromPreviousRun[FileIndex].Equals(AllUObjectHeaders[FileIndex].AbsolutePath, StringComparison.InvariantCultureIgnoreCase))
                                        {
                                            bIsOutOfDate = true;
                                            Log.TraceVerbose("UnrealHeaderTool needs to run because the set of UObject source files in module {0} has changed", Module.ModuleName);
                                            break;
                                        }
                                    }
                                }
                                else
                                {
                                    bIsOutOfDate = true;
                                    Log.TraceVerbose("UnrealHeaderTool needs to run because there are a different number of UObject source files in module {0}", Module.ModuleName);
                                }
                            }

                            foreach (var HeaderFile in AllUObjectHeaders)
                            {
                                var HeaderFileTimestamp = HeaderFile.Info.LastWriteTime;

                                // Has the source header changed since we last generated headers successfully?
                                if (SavedTimestamp.CompareTo(HeaderFileTimestamp) < 0)
                                {
                                    Log.TraceVerbose("UnrealHeaderTool needs to run because SavedTimestamp is older than HeaderFileTimestamp (" + HeaderFile.AbsolutePath + ") for module {0}", Module.ModuleName);
                                    bIsOutOfDate = true;
                                    break;
                                }
                            }
                        }
                        else
                        {
                            // Generated code is older UnrealHeaderTool.exe or CoreUObject headers.  Out of date!
                            Log.TraceVerbose("UnrealHeaderTool needs to run because UnrealHeaderTool.exe or CoreUObject headers are newer than SavedTimestamp for module {0}", Module.ModuleName);
                            bIsOutOfDate = true;
                        }
                    }
                    else
                    {
                        // Timestamp file was missing (possibly deleted/cleaned), so headers are out of date
                        Log.TraceVerbose("UnrealHeaderTool needs to run because UHT Timestamp file did not exist for module {0}", Module.ModuleName);
                        bIsOutOfDate = true;
                    }
                }
                else
                {
                    // Generated code directory is missing entirely!
                    Log.TraceVerbose("UnrealHeaderTool needs to run because no generated code directory was found for module {0}", Module.ModuleName);
                    bIsOutOfDate = true;
                }

                // If even one module is out of date, we're done!  UHT does them all in one fell swoop.;
                if (bIsOutOfDate)
                {
                    break;
                }
            }

            return(bIsOutOfDate);
        }
        public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List <FileItem> SourceFiles, string ModuleName)
        {
            if (Arches.Length == 0)
            {
                throw new BuildException("At least one architecture (armv7, x86, etc) needs to be selected in the project settings to build");
            }

            if (!bHasPrintedApiLevel)
            {
                Console.WriteLine("Compiling with NDK API '{0}'", GetNdkApiLevel());
                bHasPrintedApiLevel = true;
            }

            string BaseArguments = "";

            if (CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create)
            {
                BaseArguments += " -Werror";
            }

            // Directly added NDK files for NDK extensions
            if (!UnrealBuildTool.RunningRocket())
            {
                ConditionallyAddNDKSourceFiles(SourceFiles);
            }

            // Add preprocessor definitions to the argument list.
            foreach (string Definition in CompileEnvironment.Config.Definitions)
            {
                BaseArguments += string.Format(" -D \"{0}\"", Definition);
            }

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



            string BasePCHName  = "";
            var    PCHExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Android].GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader);

            if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
            {
                BasePCHName = RemoveArchName(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath).Replace(PCHExtension, "");
            }

            // Create a compile action for each source file.
            CPPOutput Result = new CPPOutput();

            foreach (string Arch in Arches)
            {
                foreach (string GPUArchitecture in GPUArchitectures)
                {
                    // which toolchain to use
                    string Arguments = GetCLArguments_Global(CompileEnvironment, Arch) + BaseArguments;

                    if (GPUArchitecture == "-gl4")
                    {
                        Arguments += " -DPLATFORM_ANDROIDGL4=1";
                    }
                    else if (GPUArchitecture == "-es31")
                    {
                        Arguments += " -DPLATFORM_ANDROIDES31=1";
                    }

                    // which PCH file to include
                    string PCHArguments = "";
                    if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
                    {
                        // Add the precompiled header file's path to the include path so Clang can find it.
                        // This needs to be before the other include paths to ensure Clang uses it instead of the source header file.
                        PCHArguments += string.Format(" -include \"{0}\"", InlineArchName(BasePCHName, Arch, GPUArchitecture));
                    }

                    // Add include paths to the argument list (filtered by architecture)
                    foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths)
                    {
                        if (IsDirectoryForArch(IncludePath, Arch))
                        {
                            Arguments += string.Format(" -I\"{0}\"", IncludePath);
                        }
                    }
                    foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths)
                    {
                        if (IsDirectoryForArch(IncludePath, Arch))
                        {
                            Arguments += string.Format(" -I\"{0}\"", IncludePath);
                        }
                    }

                    foreach (FileItem SourceFile in SourceFiles)
                    {
                        Action CompileAction = new Action(ActionType.Compile);
                        string FileArguments = "";
                        bool   bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C";

                        // should we disable optimizations on this file?
                        // @todo android - We wouldn't need this if we could disable optimizations per function (via pragma)
                        bool bDisableOptimizations = false;                        // SourceFile.AbsolutePath.ToUpperInvariant().IndexOf("\\SLATE\\") != -1;
                        if (bDisableOptimizations && CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug)
                        {
                            Log.TraceWarning("Disabling optimizations on {0}", SourceFile.AbsolutePath);
                        }

                        bDisableOptimizations = bDisableOptimizations || CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug;

                        // Add C or C++ specific compiler arguments.
                        if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
                        {
                            FileArguments += GetCompileArguments_PCH(bDisableOptimizations);
                        }
                        else if (bIsPlainCFile)
                        {
                            FileArguments += GetCompileArguments_C(bDisableOptimizations);
                        }
                        else
                        {
                            FileArguments += GetCompileArguments_CPP(bDisableOptimizations);

                            // 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)
                        {
                            // Add the precompiled header file to the produced item list.
                            FileItem PrecompiledHeaderFile = FileItem.GetItemByPath(
                                Path.Combine(
                                    CompileEnvironment.Config.OutputDirectory,
                                    Path.GetFileName(InlineArchName(SourceFile.AbsolutePath, Arch, GPUArchitecture) + PCHExtension)
                                    )
                                );

                            CompileAction.ProducedItems.Add(PrecompiledHeaderFile);
                            Result.PrecompiledHeaderFile = PrecompiledHeaderFile;

                            // Add the parameters needed to compile the precompiled header file to the command-line.
                            FileArguments += string.Format(" -o \"{0}\"", PrecompiledHeaderFile.AbsolutePath, false);
                        }
                        else
                        {
                            if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
                            {
                                CompileAction.bIsUsingPCH = true;
                                FileItem ArchPrecompiledHeaderFile = FileItem.GetItemByPath(InlineArchName(BasePCHName, Arch, GPUArchitecture) + PCHExtension);
                                CompileAction.PrerequisiteItems.Add(ArchPrecompiledHeaderFile);
                            }

                            var ObjectFileExtension = UEBuildPlatform.BuildPlatformDictionary[UnrealTargetPlatform.Android].GetBinaryExtension(UEBuildBinaryType.Object);

                            // Add the object file to the produced item list.
                            FileItem ObjectFile = FileItem.GetItemByPath(
                                InlineArchName(Path.Combine(
                                                   CompileEnvironment.Config.OutputDirectory,
                                                   Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension), Arch, GPUArchitecture
                                               )
                                );
                            CompileAction.ProducedItems.Add(ObjectFile);
                            Result.ObjectFiles.Add(ObjectFile);

                            FileArguments += string.Format(" -o \"{0}\"", ObjectFile.AbsolutePath, false);
                        }

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

                        // Build a full argument list
                        string AllArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments;
                        AllArguments = ActionThread.ExpandEnvironmentVariables(AllArguments);
                        AllArguments = AllArguments.Replace("\\", "/");

                        // Create the response file
                        string ResponseFileName = CompileAction.ProducedItems[0].AbsolutePath + ".response";
                        string ResponseArgument = string.Format("@\"{0}\"", ResponseFile.Create(ResponseFileName, new List <string> {
                            AllArguments
                        }));

                        CompileAction.WorkingDirectory  = Path.GetFullPath(".");
                        CompileAction.CommandPath       = ClangPath;
                        CompileAction.CommandArguments  = ResponseArgument;
                        CompileAction.StatusDescription = string.Format("{0} [{1}-{2}]", Path.GetFileName(SourceFile.AbsolutePath), Arch.Replace("-", ""), GPUArchitecture.Replace("-", ""));

                        CompileAction.OutputEventHandler = new DataReceivedEventHandler(CompileOutputReceivedDataEventHandler);

                        // VC++ always outputs the source file name being compiled, so we don't need to emit this ourselves
                        CompileAction.bShouldOutputStatusDescription = true;

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

            return(Result);
        }
Example #15
0
        /**
         *	Modify the newly created module passed in for this platform.
         *	This is not required - but allows for hiding details of a
         *	particular platform.
         *
         *	@param	InModule		The newly loaded module
         *	@param	Target			The target being build
         */
        public override void ModifyNewlyLoadedModule(UEBuildModule InModule, TargetInfo Target)
        {
            if (Target.Platform == UnrealTargetPlatform.HTML5)
            {
                if (InModule.ToString() == "Core")
                {
                    InModule.AddPublicIncludePath("Runtime/Core/Public/HTML5");
                    InModule.AddPublicDependencyModule("zlib");
                }
                else if (InModule.ToString() == "Engine")
                {
                    InModule.AddPrivateDependencyModule("zlib");
                    InModule.AddPrivateDependencyModule("UElibPNG");
                    InModule.AddPublicDependencyModule("UEOgg");
                    InModule.AddPublicDependencyModule("Vorbis");
                }

                if (InModule.ToString() == "NetworkFile")
                {
                    if (EnableHTTPForNFS)
                    {
                        InModule.AddPublicDefinition("ENABLE_HTTP_FOR_NFS=1");
                        if (Target.Architecture == "-win32")
                        {
                            InModule.AddPrivateDependencyModule("HTML5Win32");
                        }
                        else
                        {
                            InModule.AddPrivateDependencyModule("HTML5JS");
                        }
                    }
                }
            }
            else if (Target.Platform == UnrealTargetPlatform.Win32 || Target.Platform == UnrealTargetPlatform.Win64 || Target.Platform == UnrealTargetPlatform.Mac)
            {
                // allow standalone tools to use targetplatform modules, without needing Engine
                if ((!UEBuildConfiguration.bBuildRequiresCookedData &&
                     InModule.ToString() == "Engine" &&
                     UEBuildConfiguration.bBuildDeveloperTools) ||
                    UEBuildConfiguration.bForceBuildTargetPlatforms)
                {
                    InModule.AddPlatformSpecificDynamicallyLoadedModule("HTML5TargetPlatform");
                }

                if (EnableHTTPForNFS && (Target.Platform == UnrealTargetPlatform.Win64 ||
                                         Target.Platform == UnrealTargetPlatform.Mac)
                    )
                {
                    if (InModule.ToString() == "NetworkFile") // client
                    {
                        InModule.AddPrivateDependencyModule("HTTP");
                        InModule.AddPublicDefinition("ENABLE_HTTP_FOR_NFS=1");
                    }
                    else if (InModule.ToString() == "NetworkFileSystem") // server
                    {
                        if (UnrealBuildTool.RunningRocket() == false ||
                            Target.Type == TargetRules.TargetType.Game)
                        {
                            InModule.AddPrivateDependencyModule("WebSockets");
                            InModule.AddPublicDefinition("ENABLE_HTTP_FOR_NFS=1");
                        }
                    }
                }
            }
        }
Example #16
0
        /** Updates the intermediate include directory timestamps of all the passed in UObject modules */
        private static void UpdateDirectoryTimestamps(List <UHTModuleInfo> UObjectModules)
        {
            foreach (var Module in UObjectModules)
            {
                string GeneratedCodeDirectory     = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase);
                var    GeneratedCodeDirectoryInfo = new DirectoryInfo(GeneratedCodeDirectory);

                try
                {
                    if (GeneratedCodeDirectoryInfo.Exists)
                    {
                        if (UnrealBuildTool.RunningRocket())
                        {
                            // If it is an Engine folder and we are building a rocket project do NOT update the timestamp!
                            // @todo Rocket: This contains check is hacky/fragile
                            string FullGeneratedCodeDirectory = GeneratedCodeDirectoryInfo.FullName;
                            FullGeneratedCodeDirectory = FullGeneratedCodeDirectory.Replace("\\", "/");
                            if (FullGeneratedCodeDirectory.Contains("Engine/Intermediate/Build"))
                            {
                                continue;
                            }

                            // Skip checking timestamps for engine plugin intermediate headers in Rocket
                            PluginInfo Info = Plugins.GetPluginInfoForModule(Module.ModuleName);
                            if (Info != null)
                            {
                                if (Info.LoadedFrom == PluginInfo.LoadedFromType.Engine)
                                {
                                    continue;
                                }
                            }
                        }

                        // Touch the include directory since we have technically 'generated' the headers
                        // However, the headers might not be touched at all since that would cause the compiler to recompile everything
                        // We can't alter the directory timestamp directly, because this may throw exceptions when the directory is
                        // open in visual studio or windows explorer, so instead we create a blank file that will change the timestamp for us
                        string TimestampFile = GeneratedCodeDirectoryInfo.FullName + Path.DirectorySeparatorChar + @"Timestamp";

                        if (!GeneratedCodeDirectoryInfo.Exists)
                        {
                            GeneratedCodeDirectoryInfo.Create();
                        }

                        // Save all of the UObject files to a timestamp file.  We'll load these on the next run to see if any new
                        // files with UObject classes were deleted, so that we'll know to run UHT even if the timestamps of all
                        // of the other source files were unchanged
                        {
                            var AllUObjectFiles = new List <string>();
                            AllUObjectFiles.AddRange(Module.PublicUObjectClassesHeaders.ConvertAll(Item => Item.AbsolutePath));
                            AllUObjectFiles.AddRange(Module.PublicUObjectHeaders.ConvertAll(Item => Item.AbsolutePath));
                            AllUObjectFiles.AddRange(Module.PrivateUObjectHeaders.ConvertAll(Item => Item.AbsolutePath));
                            ResponseFile.Create(TimestampFile, AllUObjectFiles);
                        }
                    }
                }
                catch (Exception Exception)
                {
                    throw new BuildException(Exception, "Couldn't touch header directories: " + Exception.Message);
                }
            }
        }
Example #17
0
        /**
         * Checks the class header files and determines if generated UObject code files are out of date in comparison.
         * @param UObjectModules	Modules that we generate headers for
         *
         * @return					True if the code files are out of date
         * */
        private static bool AreGeneratedCodeFilesOutOfDate(List <UHTModuleInfo> UObjectModules)
        {
            bool bIsOutOfDate = false;

            // Get UnrealHeaderTool timestamp. If it's newer than generated headers, they need to be rebuilt too.
            var HeaderToolTimestamp = CheckIfUnrealHeaderToolIsUpToDate();

            // Get CoreUObject.generated.cpp timestamp.  If the source files are older than the CoreUObject generated code, we'll
            // need to regenerate code for the module
            DateTime?CoreGeneratedTimestamp = null;

            {
                // Find the CoreUObject module
                foreach (var Module in UObjectModules)
                {
                    if (Module.ModuleName.Equals("CoreUObject", StringComparison.InvariantCultureIgnoreCase))
                    {
                        CoreGeneratedTimestamp = GetCoreGeneratedTimestamp(Module.ModuleName, Path.GetDirectoryName(Module.GeneratedCPPFilenameBase));
                        break;
                    }
                }
                if (CoreGeneratedTimestamp == null)
                {
                    throw new BuildException("Could not find CoreUObject in list of all UObjectModules");
                }
            }


            foreach (var Module in UObjectModules)
            {
                // In Rocket, we skip checking timestamps for modules that don't exist within the project's directory
                if (UnrealBuildTool.RunningRocket())
                {
                    // @todo Rocket: This could be done in a better way I'm sure
                    if (!Utils.IsFileUnderDirectory(Module.ModuleDirectory, UnrealBuildTool.GetUProjectPath()))
                    {
                        // Engine or engine plugin module - Rocket does not regenerate them so don't compare their timestamps
                        continue;
                    }
                }

                // Make sure we have an existing folder for generated code.  If not, then we definitely need to generate code!
                var GeneratedCodeDirectory = Path.GetDirectoryName(Module.GeneratedCPPFilenameBase);
                var TestDirectory          = (FileSystemInfo) new DirectoryInfo(GeneratedCodeDirectory);
                if (TestDirectory.Exists)
                {
                    // Grab our special "Timestamp" file that we saved after the last set of headers were generated
                    string TimestampFile          = Path.Combine(GeneratedCodeDirectory, @"Timestamp");
                    var    SavedTimestampFileInfo = (FileSystemInfo) new FileInfo(TimestampFile);
                    if (SavedTimestampFileInfo.Exists)
                    {
                        // Make sure the last UHT run completed after UnrealHeaderTool.exe was compiled last, and after the CoreUObject headers were touched last.
                        var SavedTimestamp = SavedTimestampFileInfo.LastWriteTime;
                        if (SavedTimestamp.CompareTo(HeaderToolTimestamp) > 0 &&
                            SavedTimestamp.CompareTo(CoreGeneratedTimestamp) > 0)
                        {
                            // Iterate over our UObjects headers and figure out if any of them have changed
                            var AllUObjectHeaders = new List <FileItem>();
                            AllUObjectHeaders.AddRange(Module.PublicUObjectClassesHeaders);
                            AllUObjectHeaders.AddRange(Module.PublicUObjectHeaders);
                            AllUObjectHeaders.AddRange(Module.PrivateUObjectHeaders);
                            foreach (var HeaderFile in AllUObjectHeaders)
                            {
                                var HeaderFileTimestamp = HeaderFile.Info.LastWriteTime;

                                // Has the source header changed since we last generated headers successfully?
                                if (SavedTimestamp.CompareTo(HeaderFileTimestamp) < 0)
                                {
                                    bIsOutOfDate = true;
                                    break;
                                }

                                // When we're running in assembler mode, outdatedness cannot be inferred by checking the directory timestamp
                                // of the source headers.  We don't care if source files were added or removed in this mode, because we're only
                                // able to process the known UObject headers that are in the Makefile.  If UObject header files are added/removed,
                                // we expect the user to re-run GenerateProjectFiles which will force UBTMakefile outdatedness.
                                // @todo ubtmake: Possibly, we should never be doing this check these days.
                                if (UnrealBuildTool.IsGatheringBuild || !UnrealBuildTool.IsAssemblingBuild)
                                {
                                    // Also check the timestamp on the directory the source file is in.  If the directory timestamp has
                                    // changed, new source files may have been added or deleted.  We don't know whether the new/deleted
                                    // files were actually UObject headers, but because we don't know all of the files we processed
                                    // in the previous run, we need to assume our generated code is out of date if the directory timestamp
                                    // is newer.
                                    var HeaderDirectoryTimestamp = new DirectoryInfo(Path.GetDirectoryName(HeaderFile.AbsolutePath)).LastWriteTime;
                                    if (SavedTimestamp.CompareTo(HeaderDirectoryTimestamp) < 0)
                                    {
                                        bIsOutOfDate = true;
                                        break;
                                    }
                                }
                            }
                        }
                        else
                        {
                            // Generated code is older UnrealHeaderTool.exe or CoreUObject headers.  Out of date!
                            bIsOutOfDate = true;
                        }
                    }
                    else
                    {
                        // Timestamp file was missing (possibly deleted/cleaned), so headers are out of date
                        bIsOutOfDate = true;
                    }
                }
                else
                {
                    // Generated code directory is missing entirely!
                    bIsOutOfDate = true;
                }

                // If even one module is out of date, we're done!  UHT does them all in one fell swoop.;
                if (bIsOutOfDate)
                {
                    break;
                }
            }

            return(bIsOutOfDate);
        }