/// <summary>
        /// Recursively collapses all sub-folders that are redundant.  Should only be called after we're done adding
        /// files and projects to the master project.
        /// </summary>
        /// <param name="Folder">The folder whose sub-folders we should potentially collapse into</param>
        void EliminateRedundantMasterProjectSubFolders(MasterProjectFolder Folder, string ParentMasterProjectFolderPath)
        {
            // NOTE: This is for diagnostics output only
            var MasterProjectFolderPath = String.IsNullOrEmpty(ParentMasterProjectFolderPath) ? Folder.FolderName : (ParentMasterProjectFolderPath + "/" + Folder.FolderName);

            // We can eliminate folders that meet all of these requirements:
            //		1) Have only a single project file in them
            //		2) Have no files in the folder except project files, and no sub-folders
            //		3) The project file matches the folder name
            //
            // Additionally, if KeepSourceSubDirectories==false, we can eliminate directories called "Source".
            //
            // Also, we can kill folders that are completely empty.

            foreach (var SubFolder in Folder.SubFolders)
            {
                // Recurse
                EliminateRedundantMasterProjectSubFolders(SubFolder, MasterProjectFolderPath);
            }

            var SubFoldersToAdd = new List<MasterProjectFolder>();
            var SubFoldersToRemove = new List<MasterProjectFolder>();
            foreach (var SubFolder in Folder.SubFolders)
            {
                bool CanCollapseFolder = false;

                // 1)
                if (SubFolder.ChildProjects.Count == 1)
                {
                    // 2)
                    if (SubFolder.Files.Count == 0 &&
                        SubFolder.SubFolders.Count == 0)
                    {
                        // 3)
                        if (SubFolder.FolderName.Equals(Utils.GetFilenameWithoutAnyExtensions(SubFolder.ChildProjects[0].ProjectFilePath), StringComparison.InvariantCultureIgnoreCase))
                        {
                            CanCollapseFolder = true;
                        }
                    }
                }

                if (!KeepSourceSubDirectories)
                {
                    if (SubFolder.FolderName.Equals("Source", StringComparison.InvariantCultureIgnoreCase))
                    {
                        // Avoid collapsing the Engine's Source directory, since there are so many other solution folders in
                        // the parent directory.
                        if (!Folder.FolderName.Equals("Engine", StringComparison.InvariantCultureIgnoreCase))
                        {
                            CanCollapseFolder = true;
                        }
                    }
                }

                if (SubFolder.ChildProjects.Count == 0 && SubFolder.Files.Count == 0 & SubFolder.SubFolders.Count == 0)
                {
                    // Folder is totally empty
                    CanCollapseFolder = true;
                }

                if (CanCollapseFolder)
                {
                    // OK, this folder is redundant and can be collapsed away.

                    SubFoldersToAdd.AddRange(SubFolder.SubFolders);
                    SubFolder.SubFolders.Clear();

                    Folder.ChildProjects.AddRange(SubFolder.ChildProjects);
                    SubFolder.ChildProjects.Clear();

                    Folder.Files.AddRange(SubFolder.Files);
                    SubFolder.Files.Clear();

                    SubFoldersToRemove.Add(SubFolder);
                }
            }

            foreach (var SubFolderToRemove in SubFoldersToRemove)
            {
                Folder.SubFolders.Remove(SubFolderToRemove);
            }
            Folder.SubFolders.AddRange(SubFoldersToAdd);

            // After everything has been collapsed, do a bit of data validation
            Validate(Folder, ParentMasterProjectFolderPath);
        }
        /// <summary>
        /// Generates a Visual Studio solution file and Visual C++ project files for all known engine and game targets.
        /// Does not actually build anything.
        /// </summary>
        /// <param name="Arguments">Command-line arguments</param>
        /// <param name="bSuccess">True if everything went OK</param>
        public virtual void GenerateProjectFiles(String[] Arguments, out bool bSuccess)
        {
            bSuccess = true;

            // Parse project generator options
            bool IncludeAllPlatforms = true;
            ConfigureProjectFileGeneration(Arguments, ref IncludeAllPlatforms);

            if (bGeneratingGameProjectFiles)
            {
                Log.TraceInformation("Discovering modules, targets and source code for game...");

                MasterProjectRelativePath = STBuildTool.GetUProjectPath();

                // Set the project file name
                MasterProjectName = Path.GetFileNameWithoutExtension(STBuildTool.GetUProjectFile());

                if (!Directory.Exists(MasterProjectRelativePath + "/Source"))
                {
                    if (BuildHostPlatform.Current.Platform == STTargetPlatform.Mac)
                    {
                        MasterProjectRelativePath = Path.GetFullPath(Path.Combine(Utils.GetExecutingAssemblyDirectory(), "..", "..", "..", "Engine"));
                        GameProjectName = "UE4Game";
                    }
                    if (!Directory.Exists(MasterProjectRelativePath + "/Source"))
                    {
                        throw new BuildException("Directory '{0}' is missing 'Source' folder.", MasterProjectRelativePath);
                    }
                }
                IntermediateProjectFilesPath = Path.Combine(MasterProjectRelativePath, "Intermediate", "ProjectFiles");
            }
            else if (bGeneratingRocketProjectFiles)
            {
                Log.TraceInformation("Discovering modules, targets and source code for project...");

                // NOTE: Realistically, the distro that the Rocket user is generating projects FROM won't have NoRedist files in it.  But when
                //       testing from a developer branch, this is useful to get authentic projects. This only really matters when
                //       bIncludeEngineModulesInRocketProjects=true (defaults to false.)
                bExcludeNoRedistFiles = true;

                MasterProjectRelativePath = STBuildTool.GetUProjectPath();
                IntermediateProjectFilesPath = Path.Combine(MasterProjectRelativePath, "Intermediate", "ProjectFiles");

                // Set the project file name
                MasterProjectName = Path.GetFileNameWithoutExtension(STBuildTool.GetUProjectFile());

                if (!Directory.Exists(MasterProjectRelativePath + "/Source"))
                {
                    throw new BuildException("Directory '{0}' is missing 'Source' folder.", MasterProjectRelativePath);
                }
            }

            // Modify the name if specific platforms were given
            if (ProjectPlatforms.Count > 0)
            {
                // Sort the platforms names so we get consistent names
                List<string> SortedPlatformNames = new List<string>();
                foreach (STTargetPlatform SpecificPlatform in ProjectPlatforms)
                {
                    SortedPlatformNames.Add(SpecificPlatform.ToString());
                }
                SortedPlatformNames.Sort();

                MasterProjectName += "_";
                foreach (string SortedPlatform in SortedPlatformNames)
                {
                    MasterProjectName += SortedPlatform;
                    IntermediateProjectFilesPath += SortedPlatform;
                }
            }

            bool bCleanProjectFiles = STBuildTool.CommandLineContains("-CleanProjects");
            if (bCleanProjectFiles)
            {
                CleanProjectFiles(MasterProjectRelativePath, MasterProjectName, IntermediateProjectFilesPath);
            }

            // Figure out which platforms we should generate project files for.
            string SupportedPlatformNames;
            SetupSupportedPlatformsAndConfigurations(IncludeAllPlatforms: IncludeAllPlatforms, SupportedPlatformNames: out SupportedPlatformNames);

            Log.TraceVerbose("Detected supported platforms: " + SupportedPlatformNames);

            RootFolder = AllocateMasterProjectFolder(this, "<Root>");

            // Build the list of games to generate projects for
            var AllGameProjects = UProjectInfo.FilterGameProjects(true, bGeneratingGameProjectFiles ? GameProjectName : null);

            var AssemblyName = "ProjectFileGenerator";
            if (bGeneratingGameProjectFiles)
            {
                AssemblyName = GameProjectName + "ProjectFileGenerator";
            }
            else if (bGeneratingRocketProjectFiles)
            {
                AssemblyName = "RocketProjectFileGenerator";
            }

            List<string> AssemblyGameFolders = new List<string>();
            foreach (UProjectInfo Project in AllGameProjects)
            {
                AssemblyGameFolders.Add(Project.Folder);
            }
            RulesCompiler.SetAssemblyNameAndGameFolders(AssemblyName, AssemblyGameFolders);

            ProjectFile EngineProject = null;
            Dictionary<string, ProjectFile> GameProjects = null;
            Dictionary<string, ProjectFile> ProgramProjects = null;
            HashSet<ProjectFile> TemplateGameProjects = null;
            {
                // Setup buildable projects for all targets
                AddProjectsForAllTargets(AllGameProjects, out EngineProject, out GameProjects, out ProgramProjects, out TemplateGameProjects);

                // Add all game projects and game config files
                AddAllGameProjects(GameProjects, SupportedPlatformNames, RootFolder);

                // Set the game to be the default project
                if (bGeneratingGameProjectFiles && GameProjects.Count > 0)
                {
                    DefaultProject = GameProjects.Values.First();
                }

                // Place projects into root level solution folders
                if (IncludeEngineSource)
                {
                    // If we're still missing an engine project because we don't have any targets for it, make one up.
                    if (EngineProject == null)
                    {
                        string ProjectFilePath = Path.Combine(IntermediateProjectFilesPath, "UE4" + ProjectFileExtension);

                        bool bAlreadyExisted;
                        EngineProject = FindOrAddProject(Utils.MakePathRelativeTo(ProjectFilePath, MasterProjectRelativePath), true, out bAlreadyExisted);

                        EngineProject.IsForeignProject = false;
                        EngineProject.IsGeneratedProject = true;
                        EngineProject.IsStubProject = true;
                    }

                    if (EngineProject != null)
                    {
                        RootFolder.AddSubFolder("Engine").ChildProjects.Add(EngineProject);

                        // Engine config files
                        if (IncludeConfigFiles)
                        {
                            AddEngineConfigFiles(EngineProject);
                            if (IncludeEnginePrograms)
                            {
                                AddUnrealHeaderToolConfigFiles(EngineProject);
                                AddUBTConfigFilesToEngineProject(EngineProject);
                            }
                        }

                        // Engine localization files
                        if (IncludeLocalizationFiles)
                        {
                            AddEngineLocalizationFiles(EngineProject);
                        }

                        // Engine template files
                        if (IncludeTemplateFiles)
                        {
                            AddEngineTemplateFiles(EngineProject);
                        }

                        if (IncludeShaderSource)
                        {
                            Log.TraceVerbose("Adding shader source code...");

                            // Find shader source files and generate stub project
                            AddEngineShaderSource(EngineProject);
                        }

                        if (IncludeBuildSystemFiles)
                        {
                            Log.TraceVerbose("Adding build system files...");

                            AddEngineBuildFiles(EngineProject);
                        }

                        if (IncludeDocumentation)
                        {
                            AddEngineDocumentation(EngineProject);
                        }
                    }

                    foreach (var CurGameProject in GameProjects.Values)
                    {
                        // Templates go under a different solution folder than games
                        if (TemplateGameProjects.Contains(CurGameProject))
                        {
                            RootFolder.AddSubFolder("Templates").ChildProjects.Add(CurGameProject);
                        }
                        else
                        {
                            RootFolder.AddSubFolder("Games").ChildProjects.Add(CurGameProject);
                        }
                    }

                    foreach (var CurProgramProject in ProgramProjects.Values)
                    {
                        RootFolder.AddSubFolder("Programs").ChildProjects.Add(CurProgramProject);
                    }

                    // Add all of the config files for generated program targets
                    AddEngineProgramConfigFiles(ProgramProjects);
                }
            }

            // Find all of the module files.  This will filter out any modules or targets that don't belong to platforms
            // we're generating project files for.
            var AllModuleFiles = DiscoverModules();

            // Setup "stub" projects for all modules
            AddProjectsForAllModules(AllGameProjects, ProgramProjects, AllModuleFiles, bGatherThirdPartySource);

            {
                if (IncludeEnginePrograms)
                {
                    MasterProjectFolder ProgramsFolder = RootFolder.AddSubFolder("Programs");

                    // Add STBuildTool to the master project
                    AddSTBuildToolProject(ProgramsFolder);

                    // Add AutomationTool to the master project
                    ProgramsFolder.ChildProjects.Add(AddSimpleCSharpProject("AutomationTool", bShouldBuildForAllSolutionTargets: true, bForceDevelopmentConfiguration: true));

                    // Add UnrealAutomationTool (launcher) to the master project
                    ProgramsFolder.ChildProjects.Add(AddSimpleCSharpProject("AutomationToolLauncher", bShouldBuildForAllSolutionTargets: true, bForceDevelopmentConfiguration: true));

                    // Add automation.csproj files to the master project
                    AddAutomationModules(ProgramsFolder);

                    // Add Distill to the master project
                    ProgramsFolder.ChildProjects.Add(AddSimpleCSharpProject("Distill"));

                    // Add DotNETUtilities to the master project
                    ProgramsFolder.ChildProjects.Add(AddSimpleCSharpProject("DotNETCommon/DotNETUtilities", bShouldBuildForAllSolutionTargets: true, bForceDevelopmentConfiguration: true));

                    // Add the Git dependencies project
                    ProgramsFolder.ChildProjects.Add(AddSimpleCSharpProject("GitDependencies", bShouldBuildForAllSolutionTargets: true, bForceDevelopmentConfiguration: true));

                    // Add all of the IOS C# projects
                    AddIOSProjects(ProgramsFolder);

                    // Add all of the Android C# projects
                    AddAndroidProjects(ProgramsFolder);

                    // Add all of the PS4 C# projects
                    AddPS4Projects(ProgramsFolder);
                }

                // Eliminate all redundant master project folders.  E.g., folders which contain only one project and that project
                // has the same name as the folder itself.  To the user, projects "feel like" folders already in the IDE, so we
                // want to collapse them down where possible.
                EliminateRedundantMasterProjectSubFolders(RootFolder, "");

                bool bWriteFileManifest = STBuildTool.CommandLineContains("-filemanifest");

                if (bWriteFileManifest == false)
                {
                    // Figure out which targets we need about IntelliSense for.  We only need to worry about targets for projects
                    // that we're actually generating in this session.
                    var IntelliSenseTargetFiles = new List<Tuple<ProjectFile, string>>();
                    {
                        // Engine targets
                        if (EngineProject != null && !bGeneratingRocketProjectFiles)
                        {
                            foreach (var ProjectTarget in EngineProject.ProjectTargets)
                            {
                                if (!String.IsNullOrEmpty(ProjectTarget.TargetFilePath))
                                {
                                    // Only bother with the editor target.  We want to make sure that definitions are setup to be as inclusive as possible
                                    // for good quality IntelliSense.  For example, we want WITH_EDITORONLY_DATA=1, so using the editor targets works well.
                                    if (ProjectTarget.TargetRules.Type == TargetRules.TargetType.Editor)
                                    {
                                        IntelliSenseTargetFiles.Add(Tuple.Create(EngineProject, ProjectTarget.TargetFilePath));
                                    }
                                }
                            }
                        }

                        // Program targets
                        foreach (var ProgramProject in ProgramProjects.Values)
                        {
                            foreach (var ProjectTarget in ProgramProject.ProjectTargets)
                            {
                                if (!String.IsNullOrEmpty(ProjectTarget.TargetFilePath))
                                {
                                    IntelliSenseTargetFiles.Add(Tuple.Create(ProgramProject, ProjectTarget.TargetFilePath));
                                }
                            }
                        }

                        // Game/template targets
                        foreach (var GameProject in GameProjects.Values)
                        {
                            foreach (var ProjectTarget in GameProject.ProjectTargets)
                            {
                                if (!String.IsNullOrEmpty(ProjectTarget.TargetFilePath))
                                {
                                    // Only bother with the editor target.  We want to make sure that definitions are setup to be as inclusive as possible
                                    // for good quality IntelliSense.  For example, we want WITH_EDITORONLY_DATA=1, so using the editor targets works well.
                                    if (ProjectTarget.TargetRules.Type == TargetRules.TargetType.Editor)
                                    {
                                        IntelliSenseTargetFiles.Add(Tuple.Create(GameProject, ProjectTarget.TargetFilePath));
                                    }
                                }
                            }
                        }
                    }

                    // Generate IntelliSense data if we need to.  This involves having UBT simulate the action compilation of
                    // the targets so that we can extra the compiler defines, include paths, etc.
                    bSuccess = GenerateIntelliSenseData(Arguments, IntelliSenseTargetFiles);
                }

                // If everything went OK, we'll now save out all of the new project files
                if (bSuccess)
                {
                    if (bWriteFileManifest == false)
                    {
                        // Save new project files
                        WriteProjectFiles();

                        Log.TraceVerbose("Project generation complete ({0} generated, {1} imported)", GeneratedProjectFiles.Count, OtherProjectFiles.Count);
                    }
                    else
                    {
                        WriteProjectFileManifest();
                    }
                }
            }
        }
 /// <summary>
 /// Adds all of the PS4 C# projects to the master project
 /// </summary>
 private void AddPS4Projects(MasterProjectFolder Folder)
 {
     string ProjectFolderName = Path.Combine(EngineRelativePath, "Source", "Programs", "PS4");
     DirectoryInfo ProjectFolderInfo = new DirectoryInfo(ProjectFolderName);
     if (ProjectFolderInfo.Exists)
     {
         Folder.ChildProjects.Add(AddSimpleCSharpProject("PS4/PS4DevKitUtil"));
     }
 }
        /// <summary>
        /// Adds STBuildTool to the master project
        /// </summary>
        private void AddSTBuildToolProject(MasterProjectFolder ProgramsFolder)
        {
            var ProjectFileName = Utils.MakePathRelativeTo(Path.Combine(Path.Combine(EngineRelativePath, "Source"), "Programs", "STBuildTool", "STBuildTool.csproj"), MasterProjectRelativePath);
            var STBuildToolProject = new VCSharpProjectFile(ProjectFileName);
            STBuildToolProject.ShouldBuildForAllSolutionTargets = true;

            // Store it off as we need it when generating target projects.
            UBTProject = STBuildToolProject;

            // Add the project
            AddExistingProjectFile(STBuildToolProject, bNeedsAllPlatformAndConfigurations: true, bForceDevelopmentConfiguration: true);

            // Put this in a solution folder
            ProgramsFolder.ChildProjects.Add(STBuildToolProject);
        }
 /// <summary>
 /// Adds all of the IOS C# projects to the master project
 /// </summary>
 private void AddIOSProjects(MasterProjectFolder Folder)
 {
     string ProjectFolderName = Path.Combine(EngineRelativePath, "Source", "Programs", "IOS");
     DirectoryInfo ProjectFolderInfo = new DirectoryInfo(ProjectFolderName);
     if (ProjectFolderInfo.Exists)
     {
         Folder.ChildProjects.Add(AddSimpleCSharpProject("IOS/iPhonePackager"));
         Folder.ChildProjects.Add(AddSimpleCSharpProject("IOS/DeploymentInterface", true)); // Build by default; needed for UAT.
         Folder.ChildProjects.Add(AddSimpleCSharpProject("IOS/DeploymentServer"));
         Folder.ChildProjects.Add(AddSimpleCSharpProject("IOS/MobileDeviceInterface"));
     }
 }
        /// <summary>
        /// Adds all .automation.csproj files to the solution.
        /// </summary>
        void AddAutomationModules(MasterProjectFolder ProgramsFolder)
        {
            var Folder = ProgramsFolder.AddSubFolder("Automation");
            var AllGameFolders = STBuildTarget.DiscoverAllGameFolders();
            var BuildFolders = new List<string>(AllGameFolders.Count);
            foreach (var GameFolder in AllGameFolders)
            {
                var GameBuildFolder = Path.Combine(GameFolder, "Build");
                if (Directory.Exists(GameBuildFolder))
                {
                    BuildFolders.Add(GameBuildFolder);
                }
            }

            // Find all the automation modules .csproj files to add
            var ModuleFiles = RulesCompiler.FindAllRulesSourceFiles(RulesCompiler.RulesFileType.AutomationModule, BuildFolders);
            foreach (var ProjectFile in ModuleFiles)
            {
                FileInfo Info = new FileInfo(Path.Combine(ProjectFile));
                if (Info.Exists)
                {
                    var RelativeFileName = Utils.MakePathRelativeTo(ProjectFile, MasterProjectRelativePath);
                    var Project = new VCSharpProjectFile(RelativeFileName);
                    Project.ShouldBuildForAllSolutionTargets = true;
                    AddExistingProjectFile(Project, bForceDevelopmentConfiguration: true);

                    Folder.ChildProjects.Add(Project);
                }
            }
        }
 /// <summary>
 /// Adds all of the Android C# projects to the master project
 /// </summary>
 private void AddAndroidProjects(MasterProjectFolder Folder)
 {
 }
        /// <summary>
        /// Adds all game project files, including target projects and config files
        /// </summary>
        private void AddAllGameProjects(Dictionary<string, ProjectFile> GameProjects, string SupportedPlatformNames, MasterProjectFolder ProjectsFolder)
        {
            foreach (var GameFolderAndProjectFile in GameProjects)
            {
                var GameFolderName = GameFolderAndProjectFile.Key;

                // @todo projectfiles: We have engine localization files, but should we also add GAME localization files?

                string GameProjectDirectory = GameFolderName;
                GameProjectDirectory = GameProjectDirectory.Replace("/", "\\");

                // Game config files
                if (IncludeConfigFiles)
                {
                    var GameConfigDirectory = Path.Combine(GameProjectDirectory, "Config");
                    if (Directory.Exists(GameConfigDirectory))
                    {
                        var GameProjectFile = GameFolderAndProjectFile.Value;
                        var DirectoriesToSearch = new List<string>();
                        DirectoriesToSearch.Add(GameConfigDirectory);
                        GameProjectFile.AddFilesToProject(SourceFileSearch.FindFiles(DirectoriesToSearch, ExcludeNoRedistFiles: bExcludeNoRedistFiles), GameFolderName);
                    }
                }
            }
        }
        /// <summary>
        /// Validate the specified Folder. Default implementation requires
        /// for project file names to be unique!
        /// </summary>
        /// <param name="Folder">Folder.</param>
        /// <param name="MasterProjectFolderPath">Parent master project folder path.</param>
        protected virtual void Validate(MasterProjectFolder Folder, string MasterProjectFolderPath)
        {
            foreach (var CurChildProject in Folder.ChildProjects)
            {
                foreach (var OtherChildProject in Folder.ChildProjects)
                {
                    if (CurChildProject != OtherChildProject)
                    {
                        if (Utils.GetFilenameWithoutAnyExtensions(CurChildProject.ProjectFilePath).Equals(Utils.GetFilenameWithoutAnyExtensions(OtherChildProject.ProjectFilePath), StringComparison.InvariantCultureIgnoreCase))
                        {
                            throw new BuildException("Detected collision between two project files with the same path in the same master project folder, " + OtherChildProject.ProjectFilePath + " and " + CurChildProject.ProjectFilePath + " (master project folder: " + MasterProjectFolderPath + ")");
                        }
                    }
                }
            }

            foreach (var SubFolder in Folder.SubFolders)
            {
                // If the parent folder already has a child project or file item with the same name as this sub-folder, then
                // that's considered an error (it should never have been allowed to have a folder name that collided
                // with project file names or file items, as that's not supported in Visual Studio.)
                foreach (var CurChildProject in Folder.ChildProjects)
                {
                    if (Utils.GetFilenameWithoutAnyExtensions(CurChildProject.ProjectFilePath).Equals(SubFolder.FolderName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new BuildException("Detected collision between a master project sub-folder " + SubFolder.FolderName + " and a project within the outer folder " + CurChildProject.ProjectFilePath + " (master project folder: " + MasterProjectFolderPath + ")");
                    }
                }
                foreach (var CurFile in Folder.Files)
                {
                    if (Path.GetFileName(CurFile).Equals(SubFolder.FolderName, StringComparison.InvariantCultureIgnoreCase))
                    {
                        throw new BuildException("Detected collision between a master project sub-folder " + SubFolder.FolderName + " and a file within the outer folder " + CurFile + " (master project folder: " + MasterProjectFolderPath + ")");
                    }
                }
                foreach (var CurFolder in Folder.SubFolders)
                {
                    if (CurFolder != SubFolder)
                    {
                        if (CurFolder.FolderName.Equals(SubFolder.FolderName, StringComparison.InvariantCultureIgnoreCase))
                        {
                            throw new BuildException("Detected collision between a master project sub-folder " + SubFolder.FolderName + " and a sibling folder " + CurFolder.FolderName + " (master project folder: " + MasterProjectFolderPath + ")");
                        }
                    }
                }
            }
        }