You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
2227 lines
85 KiB
C#
2227 lines
85 KiB
C#
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using Microsoft.Win32;
|
|
using System.Linq;
|
|
using System.Diagnostics;
|
|
|
|
namespace UnrealBuildTool
|
|
{
|
|
/// <summary>
|
|
/// Represents a folder within the master project (e.g. Visual Studio solution)
|
|
/// </summary>
|
|
public abstract class MasterProjectFolder
|
|
{
|
|
/// <summary>
|
|
/// Constructor
|
|
/// </summary>
|
|
/// <param name="InitOwnerProjectFileGenerator">Project file generator that owns this object</param>
|
|
/// <param name="InitFolderName">Name for this folder</param>
|
|
public MasterProjectFolder( ProjectFileGenerator InitOwnerProjectFileGenerator, string InitFolderName )
|
|
{
|
|
OwnerProjectFileGenerator = InitOwnerProjectFileGenerator;
|
|
FolderName = InitFolderName;
|
|
}
|
|
|
|
/// Name of this folder
|
|
public string FolderName
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Adds a new sub-folder to this folder
|
|
/// </summary>
|
|
/// <param name="SubFolderName">Name of the new folder</param>
|
|
/// <returns>The newly-added folder</returns>
|
|
public MasterProjectFolder AddSubFolder( string SubFolderName )
|
|
{
|
|
foreach( var CurFolder in SubFolders )
|
|
{
|
|
if( CurFolder.FolderName.Equals( SubFolderName, StringComparison.InvariantCultureIgnoreCase ) )
|
|
{
|
|
// Already exists!
|
|
return CurFolder;
|
|
}
|
|
}
|
|
var NewFolder = OwnerProjectFileGenerator.AllocateMasterProjectFolder( OwnerProjectFileGenerator, SubFolderName );
|
|
SubFolders.Add( NewFolder );
|
|
return NewFolder;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Recursively searches for the specified project and returns the folder that it lives in, or null if not found
|
|
/// </summary>
|
|
/// <param name="Project">The project file to look for</param>
|
|
/// <returns>The found folder that the project is in, or null</returns>
|
|
public MasterProjectFolder FindFolderForProject( ProjectFile Project )
|
|
{
|
|
foreach( var CurFolder in SubFolders )
|
|
{
|
|
MasterProjectFolder FoundFolder = CurFolder.FindFolderForProject( Project );
|
|
if( FoundFolder != null )
|
|
{
|
|
return FoundFolder;
|
|
}
|
|
}
|
|
|
|
foreach( var ChildProject in ChildProjects )
|
|
{
|
|
if( ChildProject == Project )
|
|
{
|
|
return this;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// Owner project generator
|
|
readonly ProjectFileGenerator OwnerProjectFileGenerator;
|
|
|
|
/// Sub-folders
|
|
public readonly List<MasterProjectFolder> SubFolders = new List<MasterProjectFolder>();
|
|
|
|
/// Child projects
|
|
public readonly List<ProjectFile> ChildProjects = new List<ProjectFile>();
|
|
|
|
/// Files in this folder. These are files that aren't part of any project, but display in the IDE under the project folder
|
|
/// and can be browsed/opened by the user easily in the user interface
|
|
public readonly List<string> Files = new List<string>();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Base class for all project file generators
|
|
/// </summary>
|
|
public abstract class ProjectFileGenerator
|
|
{
|
|
/// Global static that enables generation of project files. Doesn't actually compile anything.
|
|
/// This is enabled only via UnrealBuildTool command-line.
|
|
public static bool bGenerateProjectFiles = false;
|
|
|
|
/// True if we're generating lightweight project files for a single game only, excluding most engine code, documentation, etc.
|
|
public bool bGeneratingGameProjectFiles = false;
|
|
|
|
/// True if we're generating project files to be used with Rocket
|
|
public static bool bGeneratingRocketProjectFiles = false;
|
|
|
|
/// Optional list of platforms to generate projects for
|
|
readonly List<UnrealTargetPlatform> ProjectPlatforms = new List<UnrealTargetPlatform>();
|
|
|
|
/// Whether engine module projects should be included in single-game projects, or Rocket projects. This is useful if you want to be able to
|
|
/// quickly locate engine header files using VAX, for example. But it clutters up the solution file.
|
|
protected bool bAlwaysIncludeEngineModules = false;
|
|
|
|
/// When bGeneratingGameProjectFiles=true, this is the game name we're generating projects for
|
|
protected string GameProjectName = null;
|
|
|
|
/// Global static that only adds platforms that are supported when generating a given target.
|
|
/// This was the old behavior, and it resulted in scenarios where having an unsupported platform selected
|
|
/// in the platform drop-down would silently 'switch' to building Win32.
|
|
/// The new behavior is to add all platforms when generating a target, and then check if it is supported
|
|
/// at build time. If it is not, then a BuildException is thrown informing the user of an unsupported platform.
|
|
/// NOTE: This only matters when using "-AllProjects". It can increase the project file load times though, because of all
|
|
/// of the extra project configuration combinations we need to store
|
|
public static bool bCreateDummyConfigsForUnsupportedPlatforms = true;
|
|
|
|
/// Whether we should include configurations for "Test" and "Shipping" in generated projects (pass "-NoShippingConfigs" to disable this)
|
|
/// NOTE: This is currently ignored when generating Rocket projects
|
|
public static bool bIncludeTestAndShippingConfigs = true;
|
|
|
|
/// True if intellisense data should be generated (takes a while longer)
|
|
bool bGenerateIntelliSenseData = true;
|
|
|
|
/// True if we should include documentation in the generated projects
|
|
bool IncludeDocumentation = true;
|
|
|
|
/// True if all documentation languages should be included in generated projects, otherwise only "INT" will be included
|
|
bool bAllDocumentationLanguages = false;
|
|
|
|
/// True if shader source files should be included in the generated projects
|
|
bool IncludeShaderSource = true;
|
|
|
|
/// True if build system files should be included
|
|
bool IncludeBuildSystemFiles = true;
|
|
|
|
/// True if we should include config files (.ini files) in the generated project
|
|
bool IncludeConfigFiles = true;
|
|
|
|
/// True if we should include localization files (.int/.kor/etc files) in the generated project
|
|
bool IncludeLocalizationFiles = false;
|
|
|
|
/// True if we should include template files (.template files) in the generated project
|
|
bool IncludeTemplateFiles = true;
|
|
|
|
/// True if we should reflect "Source" sub-directories on disk in the master project as master project directories.
|
|
/// This arguably adds some visual clutter to the master project, but is truer to the on-disk file organization.
|
|
bool KeepSourceSubDirectories = true;
|
|
|
|
/// Relative path to the root of the engine/games (e.g. the directory above "Engine" and any sibling game directories)
|
|
public static readonly string RootRelativePath = ".." + Path.DirectorySeparatorChar + ".."; // Assume CWD is "<root>/Engine/Source"
|
|
|
|
/// Relative path from the CWD to the engine directory
|
|
public static readonly string EngineRelativePath = Path.Combine( RootRelativePath, "Engine" );
|
|
|
|
/// Relative path to the directory where the master project file will be saved to
|
|
public static string MasterProjectRelativePath = RootRelativePath; // We'll save the master project to our "root" folder
|
|
|
|
/// Name of the UE4 engine project name that contains all of the engine code, config files and other files
|
|
static readonly string EngineProjectFileNameBase = "UE4";
|
|
|
|
/// When ProjectsAreIntermediate is true, this is the directory to store generated project files
|
|
// @todo projectfiles: Ideally, projects for game modules/targets would be created in the game's Intermediate folder!
|
|
public static string IntermediateProjectFilesPath = Path.Combine( EngineRelativePath, "Intermediate", "ProjectFiles" );
|
|
|
|
/// Global static new line string used by ProjectFileGenerator to generate project files.
|
|
public static readonly string NewLine = Environment.NewLine;
|
|
|
|
/// If true, any module or source file under a NoRedist folder will not be included in the generated projects
|
|
protected bool bExcludeNoRedistFiles = false;
|
|
|
|
/// If true, we'll parse subdirectories of third-party projects to locate source and header files to include in the
|
|
/// generated projects. This can make the generated projects quite a bit bigger, but makes it easier to open files
|
|
/// directly from the IDE.
|
|
bool bGatherThirdPartySource = false;
|
|
|
|
/// Name of the master project file (e.g. base file name for the solution file for Visual Studio, or the Xcode project file on Mac)
|
|
protected string MasterProjectName = "UE4";
|
|
|
|
/// Maps all module names that were included in generated project files, to actual project file objects.
|
|
/// @todo projectfiles: Nasty global static list. This is only really used for IntelliSense, and to avoid extra folder searches for projects we've already cached source files for.
|
|
public static readonly Dictionary<string, ProjectFile> ModuleToProjectFileMap = new Dictionary<string, ProjectFile>( StringComparer.InvariantCultureIgnoreCase );
|
|
|
|
|
|
/// File extension for project files we'll be generating (e.g. ".vcxproj")
|
|
abstract public string ProjectFileExtension
|
|
{
|
|
get;
|
|
}
|
|
|
|
/// True if we should include IntelliSense data in the generated project files when possible
|
|
virtual public bool ShouldGenerateIntelliSenseData()
|
|
{
|
|
return bGenerateIntelliSenseData;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds all .automation.csproj files to the solution.
|
|
/// </summary>
|
|
void AddAutomationModules(MasterProjectFolder ProgramsFolder)
|
|
{
|
|
var Folder = ProgramsFolder.AddSubFolder("Automation");
|
|
var AllGameFolders = UEBuildTarget.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);
|
|
AddExistingProjectFile(Project);
|
|
|
|
Folder.ChildProjects.Add( Project );
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <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 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 = UnrealBuildTool.GetUProjectPath();
|
|
|
|
// Set the project file name
|
|
MasterProjectName = Path.GetFileNameWithoutExtension(UnrealBuildTool.GetUProjectFile());
|
|
|
|
if (!Directory.Exists(MasterProjectRelativePath + "/Source"))
|
|
{
|
|
if (ExternalExecution.GetRuntimePlatform () == UnrealTargetPlatform.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");
|
|
|
|
IncludeDocumentation = false;
|
|
IncludeBuildSystemFiles = false;
|
|
IncludeShaderSource = false;
|
|
IncludeTemplateFiles = false;
|
|
IncludeConfigFiles = true;
|
|
}
|
|
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 = UnrealBuildTool.GetUProjectPath();
|
|
IntermediateProjectFilesPath = Path.Combine( MasterProjectRelativePath, "Intermediate", "ProjectFiles" );
|
|
|
|
// Set the project file name
|
|
MasterProjectName = Path.GetFileNameWithoutExtension(UnrealBuildTool.GetUProjectFile());
|
|
|
|
if (!Directory.Exists(MasterProjectRelativePath + "/Source"))
|
|
{
|
|
throw new BuildException("Directory '{0}' is missing 'Source' folder.", MasterProjectRelativePath);
|
|
}
|
|
|
|
IncludeDocumentation = false;
|
|
IncludeBuildSystemFiles = false;
|
|
IncludeShaderSource = false;
|
|
IncludeTemplateFiles = false;
|
|
IncludeConfigFiles = true;
|
|
}
|
|
|
|
// 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 (UnrealTargetPlatform SpecificPlatform in ProjectPlatforms)
|
|
{
|
|
SortedPlatformNames.Add(SpecificPlatform.ToString());
|
|
}
|
|
SortedPlatformNames.Sort();
|
|
|
|
MasterProjectName += "_";
|
|
foreach (string SortedPlatform in SortedPlatformNames)
|
|
{
|
|
MasterProjectName += SortedPlatform;
|
|
IntermediateProjectFilesPath += SortedPlatform;
|
|
}
|
|
}
|
|
|
|
bool bCleanProjectFiles = UnrealBuildTool.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 );
|
|
|
|
MasterProjectFolder ProgramsFolder = RootFolder;
|
|
if( ( !bGeneratingGameProjectFiles && !bGeneratingRocketProjectFiles ) || bAlwaysIncludeEngineModules )
|
|
{
|
|
ProgramsFolder = RootFolder.AddSubFolder( "Programs" );
|
|
}
|
|
|
|
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);
|
|
|
|
// Place projects into root level solution folders
|
|
if( ( !bGeneratingGameProjectFiles && !bGeneratingRocketProjectFiles ) || bAlwaysIncludeEngineModules )
|
|
{
|
|
if( EngineProject != null )
|
|
{
|
|
RootFolder.AddSubFolder( "Engine" ).ChildProjects.Add( EngineProject );
|
|
|
|
// Engine config files
|
|
if( IncludeConfigFiles )
|
|
{
|
|
AddEngineConfigFiles( EngineProject );
|
|
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 )
|
|
{
|
|
ProgramsFolder.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( !bGeneratingRocketProjectFiles )
|
|
{
|
|
// Add UnrealBuildTool to the master project
|
|
AddUnrealBuildToolProject( ProgramsFolder );
|
|
}
|
|
|
|
if( ( !bGeneratingGameProjectFiles && !bGeneratingRocketProjectFiles ) || bAlwaysIncludeEngineModules )
|
|
{
|
|
// Add AutomationTool to the master project
|
|
ProgramsFolder.ChildProjects.Add(AddSimpleCSharpProject("AutomationTool"));
|
|
|
|
// Add UnrealAutomationTool (launcher) to the master project
|
|
ProgramsFolder.ChildProjects.Add(AddSimpleCSharpProject("AutomationToolLauncher"));
|
|
|
|
// 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" ) );
|
|
|
|
// Add all of the IOS C# projects
|
|
AddIOSProjects( ProgramsFolder );
|
|
|
|
// Add all of the Android C# projects
|
|
AddAndroidProjects( 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 = UnrealBuildTool.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.
|
|
List<string> IntelliSenseTargetFiles = new List<string>();
|
|
{
|
|
// Engine targets
|
|
if( EngineProject != null )
|
|
{
|
|
foreach( var ProjectTarget in EngineProject.ProjectTargets )
|
|
{
|
|
if( !String.IsNullOrEmpty( ProjectTarget.TargetFilePath ) )
|
|
{
|
|
IntelliSenseTargetFiles.Add( ProjectTarget.TargetFilePath );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Program targets
|
|
foreach( var ProgramProject in ProgramProjects.Values )
|
|
{
|
|
foreach( var ProjectTarget in ProgramProject.ProjectTargets )
|
|
{
|
|
if( !String.IsNullOrEmpty( ProjectTarget.TargetFilePath ) )
|
|
{
|
|
IntelliSenseTargetFiles.Add( ProjectTarget.TargetFilePath );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Game/template targets
|
|
foreach( var GameProject in GameProjects.Values )
|
|
{
|
|
foreach( var ProjectTarget in GameProject.ProjectTargets )
|
|
{
|
|
if( !String.IsNullOrEmpty( ProjectTarget.TargetFilePath ) )
|
|
{
|
|
IntelliSenseTargetFiles.Add( 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 detected UBT configuration files (BuildConfiguration.xml) to engine project.
|
|
/// </summary>
|
|
/// <param name="EngineProject">Engine project to add files to.</param>
|
|
private void AddUBTConfigFilesToEngineProject(ProjectFile EngineProject)
|
|
{
|
|
foreach(var BuildConfigurationPath in XmlConfigLoader.ConfigLocationHierarchy)
|
|
{
|
|
if(!BuildConfigurationPath.bExists)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
EngineProject.AddAliasedFileToProject(
|
|
new AliasedFile(
|
|
BuildConfigurationPath.FSLocation,
|
|
Path.Combine("Config", "UnrealBuildTool", BuildConfigurationPath.IDEFolderName)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clean project files
|
|
/// </summary>
|
|
/// <param name="InMasterProjectRelativePath">The MasterProjectRelativePath</param>
|
|
/// <param name="InMasterProjectName">The name of the master project</param>
|
|
/// <param name="InIntermediateProjectFilesPath">The intermediate path of project files</param>
|
|
public abstract void CleanProjectFiles(string InMasterProjectRelativePath, string InMasterProjectName, string InIntermediateProjectFilesPath);
|
|
|
|
/// <summary>
|
|
/// Configures project generator based on command-line options
|
|
/// </summary>
|
|
/// <param name="Arguments">Arguments passed into the program</param>
|
|
/// <param name="IncludeAllPlatforms">True if all platforms should be included</param>
|
|
protected virtual void ConfigureProjectFileGeneration( String[] Arguments, ref bool IncludeAllPlatforms )
|
|
{
|
|
foreach( var CurArgument in Arguments )
|
|
{
|
|
if( CurArgument.StartsWith( "-" ) )
|
|
{
|
|
if (CurArgument.StartsWith( "-Platforms=", StringComparison.InvariantCultureIgnoreCase ))
|
|
{
|
|
// Parse the list... will be in Foo+Bar+New format
|
|
string PlatformList = CurArgument.Substring(11);
|
|
while (PlatformList.Length > 0)
|
|
{
|
|
string PlatformString = PlatformList;
|
|
Int32 PlusIdx = PlatformList.IndexOf("+");
|
|
if (PlusIdx != -1)
|
|
{
|
|
PlatformString = PlatformList.Substring(0, PlusIdx);
|
|
PlatformList = PlatformList.Substring(PlusIdx + 1);
|
|
}
|
|
else
|
|
{
|
|
// We are on the last platform... clear the list to exit the loop
|
|
PlatformList = "";
|
|
}
|
|
|
|
// Is the string a valid platform? If so, add it to the list
|
|
UnrealTargetPlatform SpecifiedPlatform = UnrealTargetPlatform.Unknown;
|
|
foreach (UnrealTargetPlatform PlatformParam in Enum.GetValues(typeof(UnrealTargetPlatform)))
|
|
{
|
|
if (PlatformString.Equals(PlatformParam.ToString(), StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
SpecifiedPlatform = PlatformParam;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (SpecifiedPlatform != UnrealTargetPlatform.Unknown)
|
|
{
|
|
if (ProjectPlatforms.Contains(SpecifiedPlatform) == false)
|
|
{
|
|
ProjectPlatforms.Add(SpecifiedPlatform);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log.TraceWarning("ProjectFiles invalid platform specified: {0}", PlatformString);
|
|
}
|
|
}
|
|
}
|
|
else switch( CurArgument.ToUpperInvariant() )
|
|
{
|
|
case "-ALLPLATFORMS":
|
|
IncludeAllPlatforms = true;
|
|
break;
|
|
|
|
case "-CURRENTPLATFORM":
|
|
IncludeAllPlatforms = false;
|
|
break;
|
|
|
|
case "-THIRDPARTY":
|
|
bGatherThirdPartySource = true;
|
|
break;
|
|
|
|
case "-GAME":
|
|
// Generates project files for a single game
|
|
// @todo rocketprojects: Needs documentation
|
|
bGeneratingGameProjectFiles = true;
|
|
break;
|
|
|
|
case "-ENGINE":
|
|
// Forces engine modules and targets to be included in game-specific project files
|
|
// @todo rocketprojects: Needs documentation
|
|
bAlwaysIncludeEngineModules = true;
|
|
break;
|
|
|
|
case "-NOINTELLISENSE":
|
|
bGenerateIntelliSenseData = false;
|
|
break;
|
|
|
|
case "-INTELLISENSE":
|
|
bGenerateIntelliSenseData = true;
|
|
break;
|
|
|
|
case "-SHIPPINGCONFIGS":
|
|
bIncludeTestAndShippingConfigs = true;
|
|
break;
|
|
|
|
case "-NOSHIPPINGCONFIGS":
|
|
bIncludeTestAndShippingConfigs = false;
|
|
break;
|
|
|
|
case "-DUMMYCONFIGS":
|
|
bCreateDummyConfigsForUnsupportedPlatforms = true;
|
|
break;
|
|
|
|
case "-NODUMMYCONFIGS":
|
|
bCreateDummyConfigsForUnsupportedPlatforms = false;
|
|
break;
|
|
|
|
case "-ALLLANGUAGES":
|
|
bAllDocumentationLanguages = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (UnrealBuildTool.HasUProjectFile())
|
|
{
|
|
bGeneratingRocketProjectFiles = UnrealBuildTool.RunningRocket();
|
|
}
|
|
|
|
|
|
if( bGeneratingGameProjectFiles )
|
|
{
|
|
GameProjectName = Path.GetFileNameWithoutExtension(UnrealBuildTool.GetUProjectFile());
|
|
if (String.IsNullOrEmpty(GameProjectName))
|
|
{
|
|
throw new BuildException("A valid game project was not found in the specified location (" + UnrealBuildTool.GetUProjectPath() + ")");
|
|
}
|
|
}
|
|
else if( bGeneratingRocketProjectFiles )
|
|
{
|
|
// We expected a project path to be passed in
|
|
if (!UnrealBuildTool.HasUProjectFile())
|
|
{
|
|
throw new BuildException( "When -Rocket is used, you must also specify a path to the Rocket project file on the command-line" );
|
|
}
|
|
|
|
// Make sure we can get a valid game name out of this project
|
|
var GameName = Path.GetFileNameWithoutExtension(UnrealBuildTool.GetUProjectFile());
|
|
if( String.IsNullOrEmpty( GameName ) )
|
|
{
|
|
throw new BuildException("A valid Rocket game project was not found in the specified location (" + UnrealBuildTool.GetUProjectPath() + ")");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// At least one extra argument was specified, but we weren't expected it. Ignored.
|
|
}
|
|
}
|
|
|
|
|
|
/// <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 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// Adds all engine localization text files to the specified project
|
|
private void AddEngineLocalizationFiles( ProjectFile EngineProject )
|
|
{
|
|
var EngineLocalizationDirectory = Path.Combine( EngineRelativePath, "Content", "Localization" );
|
|
if( Directory.Exists( EngineLocalizationDirectory ) )
|
|
{
|
|
var DirectoriesToSearch = new List<string>();
|
|
DirectoriesToSearch.Add( EngineLocalizationDirectory );
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles( DirectoriesToSearch, ExcludeNoRedistFiles:bExcludeNoRedistFiles), EngineRelativePath );
|
|
}
|
|
}
|
|
|
|
|
|
/// Adds all engine template text files to the specified project
|
|
private void AddEngineTemplateFiles( ProjectFile EngineProject )
|
|
{
|
|
var EngineTemplateDirectory = Path.Combine(EngineRelativePath, "Content", "Editor", "Templates");
|
|
if (Directory.Exists(EngineTemplateDirectory))
|
|
{
|
|
var DirectoriesToSearch = new List<string>();
|
|
DirectoriesToSearch.Add( EngineTemplateDirectory );
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles( DirectoriesToSearch, ExcludeNoRedistFiles:bExcludeNoRedistFiles), EngineRelativePath );
|
|
}
|
|
}
|
|
|
|
|
|
/// Adds all engine config files to the specified project
|
|
private void AddEngineConfigFiles( ProjectFile EngineProject )
|
|
{
|
|
var EngineConfigDirectory = Path.Combine(EngineRelativePath, "Config" );
|
|
if( Directory.Exists( EngineConfigDirectory ) )
|
|
{
|
|
var DirectoriesToSearch = new List<string>();
|
|
DirectoriesToSearch.Add( EngineConfigDirectory );
|
|
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles( DirectoriesToSearch, ExcludeNoRedistFiles:bExcludeNoRedistFiles), EngineRelativePath );
|
|
}
|
|
}
|
|
|
|
/// Adds UnrealHeaderTool config files to the specified project
|
|
private void AddUnrealHeaderToolConfigFiles(ProjectFile EngineProject)
|
|
{
|
|
var UHTConfigDirectory = Path.Combine(EngineRelativePath, "Programs", "UnrealHeaderTool", "Config");
|
|
if (Directory.Exists(UHTConfigDirectory))
|
|
{
|
|
var DirectoriesToSearch = new List<string>();
|
|
DirectoriesToSearch.Add(UHTConfigDirectory);
|
|
|
|
EngineProject.AddFilesToProject(SourceFileSearch.FindFiles(DirectoriesToSearch, ExcludeNoRedistFiles: bExcludeNoRedistFiles), EngineRelativePath);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all module files (filtering by platform)
|
|
/// </summary>
|
|
/// <returns>Filtered list of module files</returns>
|
|
protected List<string> DiscoverModules()
|
|
{
|
|
var AllModuleFiles = new List<string>();
|
|
|
|
// Locate all modules (*.Build.cs files)
|
|
var FoundModuleFiles = RulesCompiler.FindAllRulesSourceFiles( RulesCompiler.RulesFileType.Module, AdditionalSearchPaths:null );
|
|
foreach( var BuildFileName in FoundModuleFiles )
|
|
{
|
|
var CleanBuildFileName = Utils.CleanDirectorySeparators( BuildFileName );
|
|
|
|
bool IncludeThisModule = true;
|
|
|
|
// Skip NoRedist files if necessary
|
|
if( bExcludeNoRedistFiles )
|
|
{
|
|
if( CleanBuildFileName.IndexOf( Path.DirectorySeparatorChar + "NoRedist" + Path.DirectorySeparatorChar, StringComparison.InvariantCultureIgnoreCase ) != -1 )
|
|
{
|
|
IncludeThisModule = false;
|
|
}
|
|
}
|
|
|
|
if( IncludeThisModule )
|
|
{
|
|
AllModuleFiles.Add( CleanBuildFileName );
|
|
}
|
|
}
|
|
return AllModuleFiles;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Finds all target files (filtering by platform)
|
|
/// </summary>
|
|
/// <returns>Filtered list of target files</returns>
|
|
protected List<string> DiscoverTargets()
|
|
{
|
|
var AllTargetFiles = new List<string>();
|
|
|
|
// Make a list of all platform name strings that we're *not* including in the project files
|
|
var UnsupportedPlatformNameStrings = Utils.MakeListOfUnsupportedPlatforms( SupportedPlatforms );
|
|
|
|
// Locate all targets (*.Target.cs files)
|
|
var FoundTargetFiles = RulesCompiler.FindAllRulesSourceFiles( RulesCompiler.RulesFileType.Target, AdditionalSearchPaths:null );
|
|
foreach( var CurTargetFile in FoundTargetFiles )
|
|
{
|
|
var CleanTargetFileName = Utils.CleanDirectorySeparators( CurTargetFile );
|
|
|
|
// Skip targets in unsupported platform directories
|
|
bool IncludeThisTarget = true;
|
|
foreach( var CurPlatformName in UnsupportedPlatformNameStrings )
|
|
{
|
|
if( CleanTargetFileName.IndexOf( Path.DirectorySeparatorChar + CurPlatformName + Path.DirectorySeparatorChar, StringComparison.InvariantCultureIgnoreCase ) != -1 )
|
|
{
|
|
IncludeThisTarget = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Skip NoRedist files if necessary
|
|
if( bExcludeNoRedistFiles )
|
|
{
|
|
if( CleanTargetFileName.IndexOf( Path.DirectorySeparatorChar + "NoRedist" + Path.DirectorySeparatorChar, StringComparison.InvariantCultureIgnoreCase ) != -1 )
|
|
{
|
|
IncludeThisTarget = false;
|
|
}
|
|
}
|
|
|
|
if( IncludeThisTarget )
|
|
{
|
|
AllTargetFiles.Add( CleanTargetFileName );
|
|
}
|
|
}
|
|
|
|
return AllTargetFiles;
|
|
}
|
|
|
|
|
|
|
|
/// <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>
|
|
/// 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 + ")");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds UnrealBuildTool to the master project
|
|
/// </summary>
|
|
private void AddUnrealBuildToolProject( MasterProjectFolder ProgramsFolder )
|
|
{
|
|
var ProjectFileName = Utils.MakePathRelativeTo( Path.Combine( Path.Combine( EngineRelativePath, "Source" ), "Programs", "UnrealBuildTool", "UnrealBuildTool.csproj" ), MasterProjectRelativePath );
|
|
var UnrealBuildToolProject = new VCSharpProjectFile( ProjectFileName );
|
|
|
|
// Store it off as we need it when generating target projects.
|
|
UBTProject = UnrealBuildToolProject;
|
|
|
|
// Add the project
|
|
AddExistingProjectFile(UnrealBuildToolProject, bNeedsAllPlatformAndConfigurations:true, bForceDevelopmentConfiguration:true);
|
|
|
|
// Put this in a solution folder
|
|
ProgramsFolder.ChildProjects.Add( UnrealBuildToolProject );
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a C# project to the master project
|
|
/// </summary>
|
|
/// <param name="ProjectName">Name of project file to add</param>
|
|
/// <returns>ProjectFile if the operation was successful, otherwise null.</returns>
|
|
private ProjectFile AddSimpleCSharpProject(string ProjectName)
|
|
{
|
|
ProjectFile Project = null;
|
|
|
|
var ProjectFileName = Path.Combine( EngineRelativePath, "Source", "Programs", ProjectName, Path.GetFileName( ProjectName ) + ".csproj" );
|
|
FileInfo Info = new FileInfo( ProjectFileName );
|
|
if( Info.Exists )
|
|
{
|
|
var FileNameRelativeToMasterProject = Utils.MakePathRelativeTo( ProjectFileName, MasterProjectRelativePath );
|
|
Project = new VCSharpProjectFile(FileNameRelativeToMasterProject);
|
|
AddExistingProjectFile(Project);
|
|
}
|
|
else
|
|
{
|
|
throw new BuildException( ProjectFileName + " doesn't exist!" );
|
|
}
|
|
|
|
return Project;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a Sandcastle Help File Builder project to the master project
|
|
/// </summary>
|
|
/// <param name="ProjectName">Name of project file to add</param>
|
|
private ProjectFile AddSimpleSHFBProject( string ProjectName )
|
|
{
|
|
// We only need this for non-native projects
|
|
ProjectFile Project = null;
|
|
|
|
string ProjectFileName = Path.Combine( EngineRelativePath, "Source", "Programs", ProjectName, Path.GetFileName( ProjectName ) + ".shfbproj" );
|
|
FileInfo Info = new FileInfo( ProjectFileName );
|
|
if( Info.Exists )
|
|
{
|
|
string FileNameRelativeToMasterProject = Utils.MakePathRelativeTo( ProjectFileName, MasterProjectRelativePath );
|
|
Project = new VSHFBProjectFile( FileNameRelativeToMasterProject );
|
|
AddExistingProjectFile(Project);
|
|
}
|
|
else
|
|
{
|
|
throw new BuildException( ProjectFileName + " doesn't exist!" );
|
|
}
|
|
|
|
return Project;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check the registry for MVC3 project support
|
|
/// </summary>
|
|
/// <param name="RootKey"></param>
|
|
/// <param name="VisualStudioVersion"></param>
|
|
/// <returns></returns>
|
|
private bool CheckRegistryKey( RegistryKey RootKey, string VisualStudioVersion )
|
|
{
|
|
bool bInstalled = false;
|
|
RegistryKey VSSubKey = RootKey.OpenSubKey( "SOFTWARE\\Microsoft\\VisualStudio\\" + VisualStudioVersion + "\\Projects\\{E53F8FEA-EAE0-44A6-8774-FFD645390401}" );
|
|
if( VSSubKey != null )
|
|
{
|
|
bInstalled = true;
|
|
VSSubKey.Close();
|
|
}
|
|
|
|
return bInstalled;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check to see if a Visual Studio Extension is installed
|
|
/// </summary>
|
|
/// <param name="VisualStudioFolder"></param>
|
|
/// <param name="VisualStudioVersion"></param>
|
|
/// <param name="Extension"></param>
|
|
/// <returns></returns>
|
|
private bool CheckVisualStudioExtensionPackage( string VisualStudioFolder, string VisualStudioVersion, string Extension )
|
|
{
|
|
DirectoryInfo DirInfo = new DirectoryInfo( Path.Combine( VisualStudioFolder, VisualStudioVersion, "Extensions" ) );
|
|
if( DirInfo.Exists )
|
|
{
|
|
List<FileInfo> PackageDefs = DirInfo.GetFiles( "*.pkgdef", SearchOption.AllDirectories ).ToList();
|
|
List<string> PackageDefNames = PackageDefs.Select( x => x.Name ).ToList();
|
|
if( PackageDefNames.Contains( Extension ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <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" ) );
|
|
Folder.ChildProjects.Add( AddSimpleCSharpProject( "IOS/DeploymentServer" ) );
|
|
Folder.ChildProjects.Add( AddSimpleCSharpProject( "IOS/MobileDeviceInterface" ) );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds all of the Android C# projects to the master project
|
|
/// </summary>
|
|
private void AddAndroidProjects(MasterProjectFolder Folder)
|
|
{
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Adds all of the config files for program targets to their project files
|
|
/// </summary>
|
|
private void AddEngineProgramConfigFiles( Dictionary<string, ProjectFile> ProgramProjects )
|
|
{
|
|
if( IncludeConfigFiles )
|
|
{
|
|
foreach( var ProjectFolderAndFile in ProgramProjects )
|
|
{
|
|
var ProgramFolder = ProjectFolderAndFile.Key;
|
|
var ProgramProjectFile = ProjectFolderAndFile.Value;
|
|
|
|
var ProgramName = Path.GetFileName( ProgramFolder );
|
|
|
|
// @todo projectfiles: The config folder for programs is kind of weird -- you end up going UP a few directories to get to it. This stuff is not great.
|
|
// @todo projectfiles: Fragile assumption here about Programs always being under /Engine/Programs
|
|
var ProgramDirectory = Path.Combine( EngineRelativePath, "Programs", ProgramName );
|
|
var ProgramConfigDirectory = Path.Combine( ProgramDirectory, "Config" );
|
|
if( Directory.Exists( ProgramConfigDirectory ) )
|
|
{
|
|
var DirectoriesToSearch = new List<string>();
|
|
DirectoriesToSearch.Add( ProgramConfigDirectory );
|
|
ProgramProjectFile.AddFilesToProject( SourceFileSearch.FindFiles( DirectoriesToSearch, ExcludeNoRedistFiles:bExcludeNoRedistFiles), ProgramDirectory );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Generates data for IntelliSense (compile definitions, include paths)
|
|
/// </summary>
|
|
/// <param name="Arguments">Incoming command-line arguments to UBT</param>
|
|
/// <param name="TargetFiles">Target files</param>
|
|
/// <return>Whether the process was successful or not</return>
|
|
private bool GenerateIntelliSenseData( String[] Arguments, List<string> TargetFiles )
|
|
{
|
|
var bSuccess = true;
|
|
if( ShouldGenerateIntelliSenseData() && TargetFiles.Count > 0 )
|
|
{
|
|
using(ProgressWriter Progress = new ProgressWriter("Binding IntelliSense data...", true))
|
|
{
|
|
for(int TargetIndex = 0; TargetIndex < TargetFiles.Count; ++TargetIndex)
|
|
{
|
|
var CurTarget = TargetFiles[ TargetIndex ];
|
|
var TargetName = Utils.GetFilenameWithoutAnyExtensions( CurTarget ); // Twice, to remove both extensions from *.Target.cs file
|
|
|
|
Log.TraceVerbose( "Found target: " + TargetName );
|
|
|
|
var ArgumentsCopy = new string[ Arguments.Length + 1 ];
|
|
ArgumentsCopy[ 0 ] = TargetName;
|
|
Array.Copy(Arguments, 0, ArgumentsCopy, 1, Arguments.Length);
|
|
|
|
bSuccess = UnrealBuildTool.RunUBT( ArgumentsCopy ) == ECompilationResult.Succeeded;
|
|
if( !bSuccess )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Display progress
|
|
Progress.Write(TargetIndex + 1, TargetFiles.Count);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Selects which platforms and build configurations we want in the project file
|
|
/// </summary>
|
|
/// <param name="IncludeAllPlatforms">True if we should include ALL platforms that are supported on this machine. Otherwise, only desktop platforms will be included.</param>
|
|
/// <param name="SupportedPlatformNames">Output string for supported platforms, returned as comma-separated values.</param>
|
|
protected virtual void SetupSupportedPlatformsAndConfigurations(bool IncludeAllPlatforms, out string SupportedPlatformNames)
|
|
{
|
|
var SupportedPlatformsString = new StringBuilder();
|
|
|
|
System.Array PlatformEnums = Enum.GetValues(typeof(UnrealTargetPlatform));
|
|
foreach (UnrealTargetPlatform Platform in PlatformEnums)
|
|
{
|
|
// project is in the explicit platform list or we include them all, we add the valid desktop platforms as they are required
|
|
bool bInProjectPlatformsList = (ProjectPlatforms.Count > 0) ? (UnrealBuildTool.IsValidDesktopPlatform(Platform) || ProjectPlatforms.Contains(Platform)) : true;
|
|
|
|
// project is a desktop platform or we have specified some platforms explicitly
|
|
bool IsRequiredPlatform = (UnrealBuildTool.IsValidDesktopPlatform(Platform) || ProjectPlatforms.Count > 0);
|
|
|
|
// Only include desktop platforms unless we were explicitly asked to include all platforms or restricted to a single platform.
|
|
if (bInProjectPlatformsList && (IncludeAllPlatforms || IsRequiredPlatform))
|
|
{
|
|
// If there is a build platform present, add it to the SupportedPlatforms list
|
|
var BuildPlatform = UEBuildPlatform.GetBuildPlatform( Platform, true );
|
|
if( BuildPlatform != null )
|
|
{
|
|
if (UnrealBuildTool.IsValidPlatform(Platform))
|
|
{
|
|
SupportedPlatforms.Add(Platform);
|
|
|
|
if (SupportedPlatformsString.Length > 0)
|
|
{
|
|
SupportedPlatformsString.Append(", ");
|
|
}
|
|
SupportedPlatformsString.Append(Platform.ToString());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We have to unregister any build platforms we aren't supporting.
|
|
// Otherwise, they can add modules that can cause issues when doing tasks
|
|
// like generating IntelliSense.
|
|
UEBuildPlatform.UnregisterBuildPlatform(Platform);
|
|
}
|
|
}
|
|
|
|
// Add all configurations
|
|
foreach( UnrealTargetConfiguration CurConfiguration in Enum.GetValues( typeof(UnrealTargetConfiguration) ) )
|
|
{
|
|
if( CurConfiguration != UnrealTargetConfiguration.Unknown )
|
|
{
|
|
if (UnrealBuildTool.IsValidConfiguration(CurConfiguration))
|
|
{
|
|
SupportedConfigurations.Add(CurConfiguration);
|
|
}
|
|
}
|
|
}
|
|
|
|
SupportedPlatformNames = SupportedPlatformsString.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find the game which contains a given input file.
|
|
/// </summary>
|
|
/// <param name="AllGameFolders">All game folders</param>
|
|
/// <param name="File">Full path of the file to search for</param>
|
|
protected UProjectInfo FindGameContainingFile(List<UProjectInfo> AllGames, string File)
|
|
{
|
|
foreach (var Game in AllGames)
|
|
{
|
|
string GameFolderPath = Path.GetFullPath(Game.Folder);
|
|
if (Utils.IsFileUnderDirectory(File, GameFolderPath + Path.DirectorySeparatorChar))
|
|
{
|
|
return Game;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds all modules and code files, given a list of games to process
|
|
/// </summary>
|
|
/// <param name="AllGameFolders">All game folders</param>
|
|
/// <param name="ProgramProjects">All program projects</param>
|
|
/// <param name="AllModuleFiles">List of *.Build.cs files for all engine programs and games</param>
|
|
/// <param name="bGatherThirdPartySource">True to gather source code from third party projects too</param>
|
|
protected void AddProjectsForAllModules( List<UProjectInfo> AllGames, Dictionary<string, ProjectFile> ProgramProjects, List<string> AllModuleFiles, bool bGatherThirdPartySource )
|
|
{
|
|
foreach( var CurModuleFile in AllModuleFiles )
|
|
{
|
|
Log.TraceVerbose("AddProjectsForAllModules " + CurModuleFile);
|
|
|
|
// The module's "base directory" is simply the directory where its xxx.Build.cs file is stored. We'll always
|
|
// harvest source files for this module in this base directory directory and all of its sub-directories.
|
|
var ModuleName = Utils.GetFilenameWithoutAnyExtensions(CurModuleFile); // Remove both ".cs" and ".Build"
|
|
|
|
bool WantProjectFileForModule = true;
|
|
|
|
// We'll keep track of whether this is an "engine" or "external" module. This is determined below while loading module rules.
|
|
bool IsEngineModule = false;
|
|
bool IsThirdPartyModule = false;
|
|
|
|
// Check to see if this is an Engine module. That is, the module is located under the "Engine" folder
|
|
string ModuleFileRelativeToEngineDirectory = Utils.MakePathRelativeTo(CurModuleFile, Path.Combine(EngineRelativePath));
|
|
if (!ModuleFileRelativeToEngineDirectory.StartsWith( ".." ) && !Path.IsPathRooted( ModuleFileRelativeToEngineDirectory ))
|
|
{
|
|
// This is an engine module
|
|
IsEngineModule = true;
|
|
}
|
|
|
|
string ModuleFileRelativeToThirdPartyDirectory = Utils.MakePathRelativeTo(CurModuleFile, Path.Combine(EngineRelativePath, "Source", "ThirdParty" ));
|
|
if (!ModuleFileRelativeToThirdPartyDirectory.StartsWith( ".." ) && !Path.IsPathRooted( ModuleFileRelativeToThirdPartyDirectory ))
|
|
{
|
|
// This is a third partymodule
|
|
IsThirdPartyModule = true;
|
|
}
|
|
|
|
bool IncludePrivateSourceCode = true;
|
|
if( bGeneratingGameProjectFiles || bGeneratingRocketProjectFiles )
|
|
{
|
|
if( IsEngineModule )
|
|
{
|
|
if( bAlwaysIncludeEngineModules )
|
|
{
|
|
if( bGeneratingRocketProjectFiles )
|
|
{
|
|
// This is an engine module, so strip out private code from the Rocket projects. Rocket users only need public headers to compile
|
|
IncludePrivateSourceCode = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We were asked to exclude engine modules from the generated projects
|
|
WantProjectFileForModule = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( WantProjectFileForModule )
|
|
{
|
|
string ProjectFileNameBase = null;
|
|
string BaseFolder = null;
|
|
|
|
string PossibleProgramTargetName = Utils.GetFilenameWithoutAnyExtensions( CurModuleFile );
|
|
|
|
// @todo projectfiles: This works fine for now, but is pretty busted. It assumes only one module per program and that it matches the program target file name. (see TTP 307091)
|
|
if( ProgramProjects != null && ProgramProjects.ContainsKey( PossibleProgramTargetName ) ) // @todo projectfiles: When building (in mem projects), ProgramProjects will be null so we are using the UE4 project instead
|
|
{
|
|
ProjectFileNameBase = PossibleProgramTargetName;
|
|
BaseFolder = Path.GetDirectoryName( CurModuleFile );
|
|
}
|
|
else if( IsEngineModule )
|
|
{
|
|
ProjectFileNameBase = EngineProjectFileNameBase;
|
|
BaseFolder = EngineRelativePath;
|
|
}
|
|
else
|
|
{
|
|
// Figure out which game project this target belongs to
|
|
UProjectInfo ProjectInfo = FindGameContainingFile(AllGames, CurModuleFile);
|
|
if(ProjectInfo == null)
|
|
{
|
|
throw new BuildException( "Found a non-engine module file (" + CurModuleFile + ") that did not exist within any of the known game folders" );
|
|
}
|
|
BaseFolder = ProjectInfo.Folder;
|
|
ProjectFileNameBase = ProjectInfo.GameName;
|
|
}
|
|
|
|
// Setup a project file entry for this module's project. Remember, some projects may host multiple modules!
|
|
string ProjectFileName = Utils.MakePathRelativeTo( Path.Combine( IntermediateProjectFilesPath, ProjectFileNameBase + ProjectFileExtension ), MasterProjectRelativePath );
|
|
bool bProjectAlreadyExisted;
|
|
var ProjectFile = FindOrAddProject( ProjectFileName, IncludeInGeneratedProjects:true, bAlreadyExisted:out bProjectAlreadyExisted );
|
|
|
|
// Update our module map
|
|
ModuleToProjectFileMap[ ModuleName ] = ProjectFile;
|
|
ProjectFile.IsGeneratedProject = true;
|
|
|
|
// Only search subdirectories for non-external modules. We don't want to add all of the source and header files
|
|
// for every third-party module, unless we were configured to do so.
|
|
var SearchSubdirectories = !IsThirdPartyModule || bGatherThirdPartySource;
|
|
|
|
if( bGatherThirdPartySource )
|
|
{
|
|
Log.TraceInformation( "Searching for third-party source files..." );
|
|
}
|
|
|
|
|
|
// Find all of the source files (and other files) and add them to the project
|
|
var FoundFiles = SourceFileSearch.FindModuleSourceFiles( CurModuleFile, ExcludeNoRedistFiles: bExcludeNoRedistFiles, SearchSubdirectories:SearchSubdirectories, IncludePrivateSourceCode:IncludePrivateSourceCode );
|
|
ProjectFile.AddFilesToProject( FoundFiles, BaseFolder );
|
|
|
|
|
|
// Is this module part of a plugin? If so then we'll make sure to add other plugin-related fiels
|
|
var PluginInfo = Plugins.GetPluginInfoForModule( ModuleName );
|
|
if( PluginInfo != null )
|
|
{
|
|
// NOTE: For plugins with multiple modules, we may attempt to re-add the same plugin files here. That's OK,
|
|
// this is handled safely in AddFileToProject()!
|
|
var UPluginFilePath = Path.Combine( PluginInfo.Directory, Path.GetFileName( PluginInfo.Directory ) + ".uplugin" );
|
|
if( File.Exists( UPluginFilePath ) )
|
|
{
|
|
ProjectFile.AddFileToProject( UPluginFilePath, BaseFolder );
|
|
}
|
|
else
|
|
{
|
|
throw new BuildException( "Not expecting to find a plugin module with no corresponding .uplugin file. File '{0}' doesn't exist", UPluginFilePath );
|
|
}
|
|
|
|
// Add plugin "resource" files if we have any
|
|
var PluginResourcesFolder = Path.Combine( PluginInfo.Directory, "Resources" );
|
|
if( Directory.Exists( PluginResourcesFolder ) )
|
|
{
|
|
var DirectoriesToSearch = new List<string>();
|
|
DirectoriesToSearch.Add( PluginResourcesFolder );
|
|
ProjectFile.AddFilesToProject(
|
|
SourceFileSearch.FindFiles(
|
|
DirectoriesToSearch: DirectoriesToSearch,
|
|
ExcludeNoRedistFiles: bExcludeNoRedistFiles ),
|
|
BaseFolder );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Creates project entries for all known targets (*.Target.cs files)
|
|
/// </summary>
|
|
/// <param name="AllGameFolders">All game folders</param>
|
|
/// <param name="EngineProject">The engine project we created</param>
|
|
/// <param name="GameProjects">Map of game folder name to all of the game projects we created</param>
|
|
/// <param name="ProgramProjects">Map of program names to all of the program projects we created</param>
|
|
/// <param name="TemplateGameProjects">Set of template game projects we found. These will also be in the GameProjects map</param>
|
|
private void AddProjectsForAllTargets( List<UProjectInfo> AllGames, out ProjectFile EngineProject, out Dictionary<string, ProjectFile> GameProjects, out Dictionary<string, ProjectFile> ProgramProjects, out HashSet<ProjectFile> TemplateGameProjects )
|
|
{
|
|
// As we're creating project files, we'll also keep track of whether we created an "engine" project and return that if we have one
|
|
EngineProject = null;
|
|
GameProjects = new Dictionary<string,ProjectFile>( StringComparer.InvariantCultureIgnoreCase );
|
|
ProgramProjects = new Dictionary<string,ProjectFile>( StringComparer.InvariantCultureIgnoreCase );
|
|
TemplateGameProjects = new HashSet<ProjectFile>();
|
|
|
|
|
|
// Find all of the target files. This will filter out any modules or targets that don't
|
|
// belong to platforms we're generating project files for.
|
|
var AllTargetFiles = DiscoverTargets();
|
|
|
|
foreach( var TargetFilePath in AllTargetFiles )
|
|
{
|
|
var TargetName = Utils.GetFilenameWithoutAnyExtensions(TargetFilePath); // Remove both ".cs" and ".Target"
|
|
|
|
// Check to see if this is an Engine target. That is, the target is located under the "Engine" folder
|
|
bool IsEngineTarget = false;
|
|
string TargetFileRelativeToEngineDirectory = Utils.MakePathRelativeTo(TargetFilePath, Path.Combine(EngineRelativePath), AlwaysTreatSourceAsDirectory: false);
|
|
if (!TargetFileRelativeToEngineDirectory.StartsWith( ".." ) && !Path.IsPathRooted( TargetFileRelativeToEngineDirectory ))
|
|
{
|
|
// This is an engine target
|
|
IsEngineTarget = true;
|
|
}
|
|
|
|
bool WantProjectFileForTarget = true;
|
|
if( bGeneratingGameProjectFiles || bGeneratingRocketProjectFiles )
|
|
{
|
|
if( IsEngineTarget )
|
|
{
|
|
if( !bAlwaysIncludeEngineModules )
|
|
{
|
|
// We were asked to exclude engine modules from the generated projects
|
|
WantProjectFileForTarget = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (WantProjectFileForTarget)
|
|
{
|
|
// Create target rules for all of the platforms and configuration combinations that we want to enable support for.
|
|
// Just use Win64 or Mac as we only need to recover the target type and both should be supported for all targets...
|
|
string UnusedTargetFilePath;
|
|
var TargetRulesObject = RulesCompiler.CreateTargetRules(TargetName, new TargetInfo(Utils.IsRunningOnMono ? UnrealTargetPlatform.Mac : UnrealTargetPlatform.Win64, UnrealTargetConfiguration.Development), false, out UnusedTargetFilePath);
|
|
|
|
// Exclude client and server targets under binary Rocket; it's impossible to build without precompiled engine binaries
|
|
if (!UnrealBuildTool.RunningRocket() || (TargetRulesObject.Type != TargetRules.TargetType.Client && TargetRulesObject.Type != TargetRules.TargetType.Server))
|
|
{
|
|
bool IsProgramTarget = false;
|
|
|
|
string GameFolder = null;
|
|
string ProjectFileNameBase = null;
|
|
if (TargetRulesObject.Type == TargetRules.TargetType.Program)
|
|
{
|
|
IsProgramTarget = true;
|
|
ProjectFileNameBase = TargetName;
|
|
}
|
|
else if (IsEngineTarget)
|
|
{
|
|
ProjectFileNameBase = EngineProjectFileNameBase;
|
|
}
|
|
else
|
|
{
|
|
// Figure out which game project this target belongs to
|
|
UProjectInfo ProjectInfo = FindGameContainingFile(AllGames, TargetFilePath);
|
|
if (ProjectInfo == null)
|
|
{
|
|
throw new BuildException("Found a non-engine target file (" + TargetFilePath + ") that did not exist within any of the known game folders");
|
|
}
|
|
GameFolder = ProjectInfo.Folder;
|
|
ProjectFileNameBase = ProjectInfo.GameName;
|
|
}
|
|
|
|
// @todo projectfiles: We should move all of the Target.cs files out of sub-folders to clean up the project directories a bit (e.g. GameUncooked folder)
|
|
|
|
var ProjectFilePath = Path.Combine(IntermediateProjectFilesPath, ProjectFileNameBase + ProjectFileExtension);
|
|
|
|
if (TargetRules.IsGameType(TargetRulesObject.Type) &&
|
|
(TargetRules.IsEditorType(TargetRulesObject.Type) == false))
|
|
{
|
|
// Allow platforms to generate stub projects here...
|
|
UEPlatformProjectGenerator.GenerateGameProjectStubs(
|
|
InGenerator: this,
|
|
InTargetName: TargetName,
|
|
InTargetFilepath: TargetFilePath,
|
|
InTargetRules: TargetRulesObject,
|
|
InPlatforms: SupportedPlatforms,
|
|
InConfigurations: SupportedConfigurations);
|
|
}
|
|
|
|
ProjectFilePath = Utils.MakePathRelativeTo(ProjectFilePath, MasterProjectRelativePath);
|
|
|
|
bool bProjectAlreadyExisted;
|
|
var ProjectFile = FindOrAddProject(ProjectFilePath, IncludeInGeneratedProjects: true, bAlreadyExisted: out bProjectAlreadyExisted);
|
|
ProjectFile.IsGeneratedProject = true;
|
|
ProjectFile.IsStubProject = false;
|
|
|
|
bool IsTemplateTarget = false;
|
|
{
|
|
// Check to see if this is a template target. That is, the target is located under the "Templates" folder
|
|
string TargetFileRelativeToTemplatesDirectory = Utils.MakePathRelativeTo(TargetFilePath, Path.Combine(RootRelativePath, "Templates"));
|
|
if (!TargetFileRelativeToTemplatesDirectory.StartsWith("..") && !Path.IsPathRooted(TargetFileRelativeToTemplatesDirectory))
|
|
{
|
|
IsTemplateTarget = true;
|
|
}
|
|
}
|
|
string BaseFolder = null;
|
|
if (IsProgramTarget)
|
|
{
|
|
ProgramProjects[TargetName] = ProjectFile;
|
|
BaseFolder = Path.GetDirectoryName(TargetFilePath);
|
|
}
|
|
else if (IsEngineTarget)
|
|
{
|
|
EngineProject = ProjectFile;
|
|
BaseFolder = EngineRelativePath;
|
|
}
|
|
else
|
|
{
|
|
GameProjects[GameFolder] = ProjectFile;
|
|
if (IsTemplateTarget)
|
|
{
|
|
TemplateGameProjects.Add(ProjectFile);
|
|
}
|
|
BaseFolder = GameFolder;
|
|
|
|
if (!bProjectAlreadyExisted)
|
|
{
|
|
// Add the .uproject file for this game/template
|
|
var UProjectFilePath = Path.Combine(BaseFolder, ProjectFileNameBase + ".uproject");
|
|
if (File.Exists(UProjectFilePath))
|
|
{
|
|
ProjectFile.AddFileToProject(UProjectFilePath, BaseFolder);
|
|
}
|
|
else
|
|
{
|
|
throw new BuildException("Not expecting to find a game with no .uproject file. File '{0}' doesn't exist", UProjectFilePath);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
foreach (var ExistingProjectTarget in ProjectFile.ProjectTargets)
|
|
{
|
|
if (ExistingProjectTarget.TargetRules.ConfigurationName.Equals(TargetRulesObject.ConfigurationName, StringComparison.InvariantCultureIgnoreCase))
|
|
{
|
|
throw new BuildException("Not expecting project {0} to already have a target rules of with configuration name {1} ({2}) while trying to add: {3}", ProjectFilePath, TargetRulesObject.ConfigurationName, ExistingProjectTarget.TargetRules.ToString(), TargetRulesObject.ToString());
|
|
}
|
|
|
|
// Not expecting to have both a game and a program in the same project. These would alias because we share the project and solution configuration names (just because it makes sense to)
|
|
if (ExistingProjectTarget.TargetRules.Type == TargetRules.TargetType.Game && ExistingProjectTarget.TargetRules.Type == TargetRules.TargetType.Program ||
|
|
ExistingProjectTarget.TargetRules.Type == TargetRules.TargetType.Program && ExistingProjectTarget.TargetRules.Type == TargetRules.TargetType.Game)
|
|
{
|
|
throw new BuildException("Not expecting project {0} to already have a Game/Program target ({1}) associated with it while trying to add: {2}", ProjectFilePath, ExistingProjectTarget.TargetRules.ToString(), TargetRulesObject.ToString());
|
|
}
|
|
}
|
|
|
|
var ProjectTarget = new ProjectTarget()
|
|
{
|
|
TargetRules = TargetRulesObject,
|
|
TargetFilePath = TargetFilePath
|
|
};
|
|
|
|
if (TargetName == "ShaderCompileWorker") // @todo projectfiles: Ideally, the target rules file should set this
|
|
{
|
|
ProjectTarget.ForceDevelopmentConfiguration = true;
|
|
}
|
|
|
|
ProjectFile.ProjectTargets.Add(ProjectTarget);
|
|
|
|
// Make sure the *.Target.cs file is in the project.
|
|
ProjectFile.AddFileToProject(TargetFilePath, BaseFolder);
|
|
|
|
|
|
// We special case ShaderCompileWorker. It needs to always be compiled in Development mode.
|
|
Log.TraceVerbose("Generating target {0} for {1}", TargetRulesObject.Type.ToString(), ProjectFilePath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// Adds shader source code to the specified project
|
|
protected void AddEngineShaderSource( ProjectFile EngineProject )
|
|
{
|
|
// Setup a project file entry for this module's project. Remember, some projects may host multiple modules!
|
|
var ShadersDirectory = Path.Combine( EngineRelativePath, "Shaders" );
|
|
|
|
var DirectoriesToSearch = new List<string>();
|
|
DirectoriesToSearch.Add( ShadersDirectory );
|
|
var SubdirectoryNamesToExclude = new List<string>();
|
|
{
|
|
// Don't include binary shaders in the project file.
|
|
SubdirectoryNamesToExclude.Add( "Binaries" );
|
|
// We never want shader intermediate files in our project file
|
|
SubdirectoryNamesToExclude.Add( "PDBDump" );
|
|
SubdirectoryNamesToExclude.Add( "WorkingDirectory" );
|
|
}
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles(
|
|
DirectoriesToSearch: DirectoriesToSearch,
|
|
ExcludeNoRedistFiles: bExcludeNoRedistFiles,
|
|
SubdirectoryNamesToExclude: SubdirectoryNamesToExclude ), EngineRelativePath );
|
|
}
|
|
|
|
|
|
/// Adds engine build infrastructure files to the specified project
|
|
protected void AddEngineBuildFiles( ProjectFile EngineProject )
|
|
{
|
|
var BuildDirectory = Path.Combine( EngineRelativePath, "Build" );
|
|
|
|
var DirectoriesToSearch = new List<string>();
|
|
DirectoriesToSearch.Add( BuildDirectory );
|
|
var SubdirectoryNamesToExclude = new List<string>();
|
|
{
|
|
// Nothing to exclude, yet!
|
|
// SubdirectoryNamesToExclude.Add( "DirectoryName" );
|
|
}
|
|
EngineProject.AddFilesToProject( SourceFileSearch.FindFiles(
|
|
DirectoriesToSearch: DirectoriesToSearch,
|
|
ExcludeNoRedistFiles: bExcludeNoRedistFiles,
|
|
SubdirectoryNamesToExclude: SubdirectoryNamesToExclude ), EngineRelativePath );
|
|
}
|
|
|
|
|
|
/// Adds engine documentation to the specified project
|
|
protected void AddEngineDocumentation( ProjectFile EngineProject )
|
|
{
|
|
// NOTE: The project folder added here will actually be collapsed away later if not needed
|
|
var DocumentationProjectDirectory = Path.Combine( EngineRelativePath, "Documentation" );
|
|
var DocumentationSourceDirectory = Path.Combine( EngineRelativePath, "Documentation", "Source" );
|
|
DirectoryInfo DirInfo = new DirectoryInfo( DocumentationProjectDirectory );
|
|
if( DirInfo.Exists && Directory.Exists( DocumentationSourceDirectory ) )
|
|
{
|
|
Log.TraceVerbose( "Adding documentation files..." );
|
|
|
|
var DirectoriesToSearch = new List<string>();
|
|
DirectoriesToSearch.Add( DocumentationSourceDirectory );
|
|
|
|
var SubdirectoryNamesToExclude = new List<string>();
|
|
{
|
|
// We never want any of the images or attachment files included in our generated project
|
|
SubdirectoryNamesToExclude.Add( "Images" );
|
|
SubdirectoryNamesToExclude.Add( "Attachments" );
|
|
|
|
// The API directory is huge, so don't include any of it
|
|
SubdirectoryNamesToExclude.Add("API");
|
|
|
|
// Omit Javascript source because it just confuses the Visual Studio IDE
|
|
SubdirectoryNamesToExclude.Add( "Javascript" );
|
|
}
|
|
|
|
var DocumentationFiles = SourceFileSearch.FindFiles(
|
|
DirectoriesToSearch: DirectoriesToSearch,
|
|
ExcludeNoRedistFiles: bExcludeNoRedistFiles,
|
|
SubdirectoryNamesToExclude: SubdirectoryNamesToExclude );
|
|
|
|
// Filter out non-English documentation files if we were configured to do so
|
|
if( !bAllDocumentationLanguages )
|
|
{
|
|
var FilteredDocumentationFiles = new List<string>();
|
|
foreach( var DocumentationFile in DocumentationFiles )
|
|
{
|
|
bool bPassesFilter = true;
|
|
if( DocumentationFile.EndsWith( ".udn", StringComparison.InvariantCultureIgnoreCase ) )
|
|
{
|
|
var LanguageSuffix = Path.GetExtension( Path.GetFileNameWithoutExtension( DocumentationFile ) );
|
|
if( !String.IsNullOrEmpty( LanguageSuffix ) &&
|
|
!LanguageSuffix.Equals( ".int", StringComparison.InvariantCultureIgnoreCase ) )
|
|
{
|
|
bPassesFilter = false;
|
|
}
|
|
}
|
|
|
|
if( bPassesFilter )
|
|
{
|
|
FilteredDocumentationFiles.Add( DocumentationFile );
|
|
}
|
|
}
|
|
DocumentationFiles = FilteredDocumentationFiles;
|
|
}
|
|
|
|
EngineProject.AddFilesToProject( DocumentationFiles, EngineRelativePath );
|
|
}
|
|
else
|
|
{
|
|
Log.TraceVerbose("Skipping documentation project... directory not found");
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Adds a new project file and returns an object that represents that project file (or if the project file is already known, returns that instead.)
|
|
/// </summary>
|
|
/// <param name="FilePath">Full path to the project file</param>
|
|
/// <param name="IncludeInGeneratedProjects">True if this project should be included in the set of generated projects. Only matters when actually generating project files.</param>
|
|
/// <param name="bAlreadyExisted">True if we already had this project file</param>
|
|
/// <returns>Object that represents this project file in Unreal Build Tool</returns>
|
|
public ProjectFile FindOrAddProject( string FilePath, bool IncludeInGeneratedProjects, out bool bAlreadyExisted )
|
|
{
|
|
if( String.IsNullOrEmpty( FilePath ) )
|
|
{
|
|
throw new BuildException( "Not valid to call FindOrAddProject() with an empty file path!" );
|
|
}
|
|
|
|
// Do we already have this project?
|
|
ProjectFile ExistingProjectFile;
|
|
if( ProjectFileMap.TryGetValue( FilePath, out ExistingProjectFile ) )
|
|
{
|
|
bAlreadyExisted = true;
|
|
return ExistingProjectFile;
|
|
}
|
|
|
|
// Add a new project file for the specified path
|
|
var NewProjectFile = AllocateProjectFile( FilePath );
|
|
ProjectFileMap[ FilePath ] = NewProjectFile;
|
|
|
|
if( IncludeInGeneratedProjects )
|
|
{
|
|
GeneratedProjectFiles.Add( NewProjectFile );
|
|
}
|
|
|
|
bAlreadyExisted = false;
|
|
return NewProjectFile;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Allocates a generator-specific project file object
|
|
/// </summary>
|
|
/// <param name="InitFilePath">Path to the project file</param>
|
|
/// <returns>The newly allocated project file object</returns>
|
|
protected abstract ProjectFile AllocateProjectFile( string InitFilePath );
|
|
|
|
|
|
/// <summary>
|
|
/// Allocates a generator-specific master project folder object
|
|
/// </summary>
|
|
/// <param name="InitOwnerProjectFileGenerator">Project file generator that owns this object</param>
|
|
/// <param name="InitFolderName">Name for this folder</param>
|
|
/// <returns>The newly allocated project folder object</returns>
|
|
public abstract MasterProjectFolder AllocateMasterProjectFolder(ProjectFileGenerator OwnerProjectFileGenerator, string FolderName );
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Returns a list of all the known project files
|
|
/// </summary>
|
|
/// <returns>Project file list</returns>
|
|
public List<ProjectFile> AllProjectFiles
|
|
{
|
|
get
|
|
{
|
|
var CombinedList = new List<ProjectFile>();
|
|
CombinedList.AddRange( GeneratedProjectFiles );
|
|
CombinedList.AddRange( OtherProjectFiles );
|
|
return CombinedList;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Writes the project files to disk
|
|
/// </summary>
|
|
/// <returns>True if successful</returns>
|
|
protected virtual bool WriteProjectFiles()
|
|
{
|
|
using(ProgressWriter Progress = new ProgressWriter("Writing project files...", true))
|
|
{
|
|
var TotalProjectFileCount = GeneratedProjectFiles.Count + 1; // +1 for the master project file, which we'll save next
|
|
|
|
for(int ProjectFileIndex = 0 ; ProjectFileIndex < GeneratedProjectFiles.Count; ++ProjectFileIndex )
|
|
{
|
|
var CurProject = GeneratedProjectFiles[ ProjectFileIndex ];
|
|
if( !CurProject.WriteProjectFile(
|
|
InPlatforms: SupportedPlatforms,
|
|
InConfigurations: SupportedConfigurations ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Progress.Write(ProjectFileIndex + 1, TotalProjectFileCount);
|
|
}
|
|
|
|
WriteMasterProjectFile( UBTProject: UBTProject );
|
|
Progress.Write(TotalProjectFileCount, TotalProjectFileCount);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Write the project files manifest
|
|
/// This is used by CIS to verify all files referenced are checked into perforce.
|
|
/// </summary>
|
|
protected virtual bool WriteProjectFileManifest()
|
|
{
|
|
FileManifest Manifest = new FileManifest();
|
|
foreach( var CurProject in GeneratedProjectFiles )
|
|
{
|
|
foreach( var SourceFile in CurProject.SourceFiles )
|
|
{
|
|
Manifest.AddFileName( SourceFile.FilePath );
|
|
}
|
|
}
|
|
|
|
string ManifestName = Path.Combine( ProjectFileGenerator.IntermediateProjectFilesPath, "UE4SourceFiles.xml" );
|
|
Utils.WriteClass<FileManifest>( Manifest, ManifestName, "" );
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Writes the master project file (e.g. Visual Studio Solution file)
|
|
/// </summary>
|
|
/// <param name="UBTProject">The UnrealBuildTool project</param>
|
|
/// <returns>True if successful</returns>
|
|
protected abstract bool WriteMasterProjectFile( ProjectFile UBTProject );
|
|
|
|
|
|
/// <summary>
|
|
/// Writes the specified string content to a file. Before writing to the file, it loads the existing file (if present) to see if the contents have changed
|
|
/// </summary>
|
|
/// <param name="FileName">File to write</param>
|
|
/// <param name="NewFileContents">File content</param>
|
|
/// <returns>True if the file was saved, or if it didn't need to be overwritten because the content was unchanged</returns>
|
|
public static bool WriteFileIfChanged( string FileName, string NewFileContents )
|
|
{
|
|
// Check to see if the file already exists, and if so, load it up
|
|
string LoadedFileContent = null;
|
|
var FileAlreadyExists = File.Exists( FileName );
|
|
if( FileAlreadyExists )
|
|
{
|
|
try
|
|
{
|
|
LoadedFileContent = File.ReadAllText( FileName );
|
|
}
|
|
catch( Exception )
|
|
{
|
|
Log.TraceInformation( "Error while trying to load existing file {0}. Ignored.", FileName );
|
|
}
|
|
}
|
|
|
|
|
|
// Don't bother saving anything out if the new file content is the same as the old file's content
|
|
bool FileNeedsSave = true;
|
|
if( LoadedFileContent != null )
|
|
{
|
|
var bIgnoreProjectFileWhitespaces = true;
|
|
if (ProjectFileComparer.CompareOrdinalIgnoreCase(LoadedFileContent, NewFileContents, bIgnoreProjectFileWhitespaces) == 0)
|
|
{
|
|
// Exact match!
|
|
FileNeedsSave = false;
|
|
}
|
|
|
|
if( !FileNeedsSave )
|
|
{
|
|
Log.TraceVerbose( "Skipped saving {0} because contents haven't changed.", Path.GetFileName( FileName ) );
|
|
}
|
|
}
|
|
|
|
if( FileNeedsSave )
|
|
{
|
|
// Save the file
|
|
try
|
|
{
|
|
Directory.CreateDirectory( Path.GetDirectoryName( FileName ) );
|
|
// When WriteAllText is passed Encoding.UTF8 it likes to write a BOM marker
|
|
// at the start of the file (adding two bytes to the file length). For most
|
|
// files this is only mildly annoying but for Makefiles it can actually make
|
|
// them un-useable.
|
|
// TODO(sbc): See if we can just drop the Encoding.UTF8 argument on all
|
|
// platforms. In this case UTF8 encoding will still be used but without the
|
|
// BOM, which is, AFAICT, desirable in almost all cases.
|
|
if (ExternalExecution.GetRuntimePlatform() == UnrealTargetPlatform.Linux || ExternalExecution.GetRuntimePlatform() == UnrealTargetPlatform.Mac)
|
|
File.WriteAllText(FileName, NewFileContents);
|
|
else
|
|
File.WriteAllText(FileName, NewFileContents, Encoding.UTF8);
|
|
Log.TraceVerbose("Saved {0}", Path.GetFileName(FileName));
|
|
}
|
|
catch( Exception ex )
|
|
{
|
|
// Unable to write to the project file.
|
|
string Message = string.Format("Error while trying to write file {0}. The file is probably read-only.", FileName);
|
|
Console.WriteLine();
|
|
Log.TraceError(Message);
|
|
throw new BuildException(ex, Message);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the given project to the OtherProjects list
|
|
/// </summary>
|
|
/// <param name="InProject">The project to add</param>
|
|
/// <returns>True if successful</returns>
|
|
public void AddExistingProjectFile(ProjectFile InProject, bool bNeedsAllPlatformAndConfigurations = false, bool bForceDevelopmentConfiguration = false, bool bProjectDeploys = false, List<UnrealTargetPlatform> InSupportedPlatforms = null, List<UnrealTargetConfiguration> InSupportedConfigurations = null)
|
|
{
|
|
if( InProject.ProjectTargets.Count != 0 )
|
|
{
|
|
throw new BuildException( "Expecting existing project to not have any ProjectTargets defined yet." );
|
|
}
|
|
|
|
var ProjectTarget = new ProjectTarget();
|
|
if( bForceDevelopmentConfiguration )
|
|
{
|
|
ProjectTarget.ForceDevelopmentConfiguration = true;
|
|
}
|
|
ProjectTarget.ProjectDeploys = bProjectDeploys;
|
|
|
|
if (bNeedsAllPlatformAndConfigurations)
|
|
{
|
|
// Add all platforms
|
|
var AllPlatforms = Enum.GetValues(typeof(UnrealTargetPlatform));
|
|
foreach (UnrealTargetPlatform CurPlatfrom in AllPlatforms)
|
|
{
|
|
ProjectTarget.ExtraSupportedPlatforms.Add(CurPlatfrom);
|
|
}
|
|
|
|
// Add all configurations
|
|
var AllConfigurations = Enum.GetValues(typeof(UnrealTargetConfiguration));
|
|
foreach (UnrealTargetConfiguration CurConfiguration in AllConfigurations)
|
|
{
|
|
ProjectTarget.ExtraSupportedConfigurations.Add( CurConfiguration );
|
|
}
|
|
}
|
|
else if (InSupportedPlatforms != null || InSupportedConfigurations != null)
|
|
{
|
|
if (InSupportedPlatforms != null)
|
|
{
|
|
// Add all explicitly specified platforms
|
|
foreach (UnrealTargetPlatform CurPlatfrom in InSupportedPlatforms)
|
|
{
|
|
ProjectTarget.ExtraSupportedPlatforms.Add(CurPlatfrom);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, add all platforms
|
|
var AllPlatforms = Enum.GetValues(typeof(UnrealTargetPlatform));
|
|
foreach (UnrealTargetPlatform CurPlatfrom in AllPlatforms)
|
|
{
|
|
ProjectTarget.ExtraSupportedPlatforms.Add(CurPlatfrom);
|
|
}
|
|
}
|
|
|
|
if (InSupportedConfigurations != null)
|
|
{
|
|
// Add all explicitly specified configurations
|
|
foreach (UnrealTargetConfiguration CurConfiguration in InSupportedConfigurations)
|
|
{
|
|
ProjectTarget.ExtraSupportedConfigurations.Add(CurConfiguration);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, add all configurations
|
|
var AllConfigurations = Enum.GetValues(typeof(UnrealTargetConfiguration));
|
|
foreach (UnrealTargetConfiguration CurConfiguration in AllConfigurations)
|
|
{
|
|
ProjectTarget.ExtraSupportedConfigurations.Add(CurConfiguration);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// For existing project files, just support the default desktop platforms and configurations
|
|
UnrealBuildTool.GetAllDesktopPlatforms(ref ProjectTarget.ExtraSupportedPlatforms, false);
|
|
// Debug and Development only
|
|
ProjectTarget.ExtraSupportedConfigurations.Add(UnrealTargetConfiguration.Debug);
|
|
ProjectTarget.ExtraSupportedConfigurations.Add(UnrealTargetConfiguration.Development);
|
|
}
|
|
|
|
InProject.ProjectTargets.Add( ProjectTarget );
|
|
|
|
// Existing projects must always have a GUID. This will throw an exception if one isn't found.
|
|
InProject.LoadGUIDFromExistingProject();
|
|
|
|
OtherProjectFiles.Add( InProject );
|
|
}
|
|
|
|
|
|
/// The project for UnrealBuildTool. Note that when generating Rocket project files, we won't have
|
|
/// an UnrealBuildTool project at all.
|
|
protected ProjectFile UBTProject;
|
|
|
|
/// List of platforms that we'll support in the project files
|
|
protected List<UnrealTargetPlatform> SupportedPlatforms = new List<UnrealTargetPlatform>();
|
|
|
|
/// List of build configurations that we'll support in the project files
|
|
protected List<UnrealTargetConfiguration> SupportedConfigurations = new List<UnrealTargetConfiguration>();
|
|
|
|
/// Map of project file names to their project files. This includes every single project file in memory or otherwise that
|
|
/// we know about so far. Note that when generating project files, this map may even include project files that we won't
|
|
/// be including in the generated projects.
|
|
protected readonly Dictionary<string, ProjectFile> ProjectFileMap = new Dictionary<string, ProjectFile>( StringComparer.InvariantCultureIgnoreCase );
|
|
|
|
/// List of project files that we'll be generating
|
|
protected readonly List<ProjectFile> GeneratedProjectFiles = new List<ProjectFile>();
|
|
|
|
/// List of other project files that we want to include in a generated solution file, even though we
|
|
/// aren't generating them ourselves. Note that these may *not* always be C++ project files (e.g. C#)
|
|
protected readonly List<ProjectFile> OtherProjectFiles = new List<ProjectFile>();
|
|
|
|
/// List of top-level folders in the master project file
|
|
protected MasterProjectFolder RootFolder;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper class used for comparing the existing and generated project files.
|
|
/// </summary>
|
|
class ProjectFileComparer
|
|
{
|
|
//static readonly string GUIDRegexPattern = "(\\{){0,1}[0-9a-fA-F]{8}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{4}\\-[0-9a-fA-F]{12}(\\}){0,1}";
|
|
//static readonly string GUIDReplaceString = "GUID";
|
|
|
|
/// <summary>
|
|
/// Used by CompareOrdinalIgnoreWhitespaceAndCase to determine if a whitespace can be ignored.
|
|
/// </summary>
|
|
/// <param name="Whitespace">Whitespace character.</param>
|
|
/// <returns>true if the character can be ignored, false otherwise.</returns>
|
|
static bool CanIgnoreWhitespace(char Whitespace)
|
|
{
|
|
// Only ignore spaces and tabs.
|
|
return Whitespace == ' ' || Whitespace == '\t';
|
|
}
|
|
|
|
/*
|
|
/// <summary>
|
|
/// Replaces all GUIDs in the project file with "GUID" text.
|
|
/// </summary>
|
|
/// <param name="ProjectFileContents">Contents of the project file to remove GUIDs from.</param>
|
|
/// <returns>String with all GUIDs replaced with "GUID" text.</returns>
|
|
static string StripGUIDs(string ProjectFileContents)
|
|
{
|
|
// Replace all GUIDs with "GUID" text.
|
|
return System.Text.RegularExpressions.Regex.Replace(ProjectFileContents, GUIDRegexPattern, GUIDReplaceString);
|
|
}
|
|
*/
|
|
|
|
/// <summary>
|
|
/// Compares two project files ignoring whitespaces, case and GUIDs.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Compares two specified String objects by evaluating the numeric values of the corresponding Char objects in each string.
|
|
/// Only space and tabulation characters are ignored. Ignores leading whitespaces at the beginning of each line and
|
|
/// differences in whitespace sequences between matching non-whitespace sub-strings.
|
|
/// </remarks>
|
|
/// <param name="StrA">The first string to compare.</param>
|
|
/// <param name="StrB">The second string to compare. </param>
|
|
/// <returns>An integer that indicates the lexical relationship between the two comparands.</returns>
|
|
public static int CompareOrdinalIgnoreWhitespaceAndCase(string StrA, string StrB)
|
|
{
|
|
// Remove GUIDs before processing the strings.
|
|
//StrA = StripGUIDs(StrA);
|
|
//StrB = StripGUIDs(StrB);
|
|
|
|
int IndexA = 0;
|
|
int IndexB = 0;
|
|
while (IndexA < StrA.Length && IndexB < StrB.Length)
|
|
{
|
|
char A = Char.ToLowerInvariant(StrA[IndexA]);
|
|
char B = Char.ToLowerInvariant(StrB[IndexB]);
|
|
if (Char.IsWhiteSpace(A) && Char.IsWhiteSpace(B) && CanIgnoreWhitespace(A) && CanIgnoreWhitespace(B))
|
|
{
|
|
// Skip whitespaces in both strings
|
|
for (IndexA++; IndexA < StrA.Length && Char.IsWhiteSpace(StrA[IndexA]) == true; IndexA++) ;
|
|
for (IndexB++; IndexB < StrB.Length && Char.IsWhiteSpace(StrB[IndexB]) == true; IndexB++) ;
|
|
}
|
|
else if (Char.IsWhiteSpace(A) && IndexA > 0 && StrA[IndexA - 1] == '\n')
|
|
{
|
|
// Skip whitespaces in StrA at the beginning of each line
|
|
for (IndexA++; IndexA < StrA.Length && Char.IsWhiteSpace(StrA[IndexA]) == true; IndexA++) ;
|
|
}
|
|
else if (Char.IsWhiteSpace(B) && IndexB > 0 && StrB[IndexB - 1] == '\n')
|
|
{
|
|
// Skip whitespaces in StrA at the beginning of each line
|
|
for (IndexB++; IndexB < StrB.Length && Char.IsWhiteSpace(StrB[IndexB]) == true; IndexB++) ;
|
|
}
|
|
else if (A != B)
|
|
{
|
|
return A - B;
|
|
}
|
|
else
|
|
{
|
|
IndexA++;
|
|
IndexB++;
|
|
}
|
|
}
|
|
// Check if we reached the end in both strings
|
|
return (StrA.Length - IndexA) - (StrB.Length - IndexB);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares two project files ignoring case and GUIDs.
|
|
/// </summary>
|
|
/// <param name="StrA">The first string to compare.</param>
|
|
/// <param name="StrB">The second string to compare. </param>
|
|
/// <returns>An integer that indicates the lexical relationship between the two comparands.</returns>
|
|
public static int CompareOrdinalIgnoreCase(string StrA, string StrB)
|
|
{
|
|
// Remove GUIDs before processing the strings.
|
|
//StrA = StripGUIDs(StrA);
|
|
//StrB = StripGUIDs(StrB);
|
|
|
|
// Use simple ordinal comparison.
|
|
return String.Compare(StrA, StrB, StringComparison.InvariantCultureIgnoreCase);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compares two project files ignoring case and GUIDs.
|
|
/// </summary>
|
|
/// <see cref="CompareOrdinalIgnoreWhitespaceAndCase"/>
|
|
/// <param name="StrA">The first string to compare.</param>
|
|
/// <param name="StrB">The second string to compare. </param>
|
|
/// <param name="bIgnoreWhitespace">True if whitsapces should be ignored.</param>
|
|
/// <returns>An integer that indicates the lexical relationship between the two comparands.</returns>
|
|
public static int CompareOrdinalIgnoreCase(string StrA, string StrB, bool bIgnoreWhitespace)
|
|
{
|
|
if (bIgnoreWhitespace)
|
|
{
|
|
return CompareOrdinalIgnoreWhitespaceAndCase(StrA, StrB);
|
|
}
|
|
else
|
|
{
|
|
return CompareOrdinalIgnoreCase(StrA, StrB);
|
|
}
|
|
}
|
|
}
|
|
}
|