2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Xml ;
using System.Xml.XPath ;
using System.Xml.Linq ;
using System.Linq ;
using System.Text ;
namespace UnrealBuildTool
{
public interface IntelliSenseGatherer
{
/// <summary>
/// Adds all of the specified preprocessor definitions to this VCProject's list of preprocessor definitions for all modules in the project
/// </summary>
/// <param name="NewPreprocessorDefinitions">List of preprocessor definitons to add</param>
2015-09-24 12:37:21 -04:00
void AddIntelliSensePreprocessorDefinitions ( List < string > NewPreprocessorDefinitions ) ;
2014-03-14 14:13:41 -04:00
/// <summary>
/// Adds all of the specified include paths to this VCProject's list of include paths for all modules in the project
/// </summary>
/// <param name="NewIncludePaths">List of include paths to add</param>
2014-04-02 18:09:23 -04:00
/// <param name="bAddingSystemIncludes">Are the include paths to add system include paths</param>
2014-09-11 03:21:51 -04:00
void AddInteliiSenseIncludePaths ( HashSet < string > NewIncludePaths , bool bAddingSystemIncludes ) ;
2014-03-14 14:13:41 -04:00
}
/// <summary>
/// A single target within a project. A project may have any number of targets within it, which are basically compilable projects
/// in themselves that the project wraps up.
/// </summary>
public class ProjectTarget
{
/// The target rules file path on disk, if we have one
2015-09-03 08:47:24 -04:00
public FileReference TargetFilePath ;
2014-03-14 14:13:41 -04:00
2015-09-30 16:40:04 -04:00
/// The project file path on disk
public FileReference ProjectFilePath ;
2014-03-14 14:13:41 -04:00
/// Optional target rules for this target. If the target came from a *.Target.cs file on disk, then it will have one of these.
/// For targets that are synthetic (like UnrealBuildTool or other manually added project files) we won't have a rules object for those.
public TargetRules TargetRules ;
/// Extra supported build platforms. Normally the target rules determines these, but for synthetic targets we'll add them here.
public List < UnrealTargetPlatform > ExtraSupportedPlatforms = new List < UnrealTargetPlatform > ( ) ;
/// Extra supported build configurations. Normally the target rules determines these, but for synthetic targets we'll add them here.
public List < UnrealTargetConfiguration > ExtraSupportedConfigurations = new List < UnrealTargetConfiguration > ( ) ;
/// If true, forces Development configuration regardless of which configuration is set as the Solution Configuration
public bool ForceDevelopmentConfiguration = false ;
/// Whether the project requires 'Deploy' option set (VC projects)
public bool ProjectDeploys = false ;
public override string ToString ( )
{
2015-09-03 08:47:24 -04:00
return TargetFilePath . GetFileNameWithoutExtension ( ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-04-24 04:43:16 -04:00
/// <summary>
/// Class that stores info about aliased file.
/// </summary>
2015-09-03 11:41:30 -04:00
public struct AliasedFile
2014-04-24 04:43:16 -04:00
{
public AliasedFile ( string FileSystemPath , string ProjectPath )
{
this . FileSystemPath = FileSystemPath ;
this . ProjectPath = ProjectPath ;
}
// File system path.
2015-09-03 11:41:30 -04:00
public readonly string FileSystemPath ;
2014-04-24 04:43:16 -04:00
// Project path.
2015-09-03 11:41:30 -04:00
public readonly string ProjectPath ;
2014-04-24 04:43:16 -04:00
}
2014-03-14 14:13:41 -04:00
public abstract class ProjectFile : IntelliSenseGatherer
{
/// <summary>
/// Represents a single source file (or other type of file) in a project
/// </summary>
public class SourceFile
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="InitFilePath">Path to the source file on disk</param>
/// <param name="InitRelativeBaseFolder">The directory on this the path within the project will be relative to</param>
2015-09-24 12:37:21 -04:00
public SourceFile ( FileReference InReference , DirectoryReference InBaseFolder )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
Reference = InReference ;
BaseFolder = InBaseFolder ;
2014-03-14 14:13:41 -04:00
}
public SourceFile ( )
{
}
/// <summary>
/// File path to file on disk
/// </summary>
2015-09-03 08:47:24 -04:00
public FileReference Reference
2014-03-14 14:13:41 -04:00
{
get ;
private set ;
}
/// <summary>
2015-09-03 08:47:24 -04:00
/// Optional directory that overrides where files in this project are relative to when displayed in the IDE. If null, will default to the project's BaseFolder.
2014-03-14 14:13:41 -04:00
/// </summary>
2015-09-03 08:47:24 -04:00
public DirectoryReference BaseFolder
2014-03-14 14:13:41 -04:00
{
get ;
private set ;
}
}
/// <summary>
/// Constructs a new project file object
/// </summary>
/// <param name="InitFilePath">The path to the project file, relative to the master project file</param>
2015-09-24 12:37:21 -04:00
protected ProjectFile ( FileReference InProjectFilePath )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
ProjectFilePath = InProjectFilePath ;
2015-03-02 10:06:55 -05:00
ShouldBuildByDefaultForSolutionTargets = true ;
2014-03-14 14:13:41 -04:00
}
2015-09-03 08:47:24 -04:00
/// Project file path
public FileReference ProjectFilePath
2014-03-14 14:13:41 -04:00
{
get ;
private set ;
}
2015-09-24 12:37:21 -04:00
2014-03-14 14:13:41 -04:00
/// Returns true if this is a generated project (as opposed to an imported project)
public bool IsGeneratedProject
{
get ;
set ;
}
/// Returns true if this is a "stub" project. Stub projects function as dumb containers for source files
/// and are never actually "built" by the master project. Stub projects are always "generated" projects.
public bool IsStubProject
{
get ;
set ;
}
2014-06-24 17:33:20 -04:00
/// Returns true if this is a foreign project, and requires UBT to be passed the path to the .uproject file
/// on the command line.
public bool IsForeignProject
{
get ;
set ;
}
2014-08-29 12:25:29 -04:00
/// Whether this project should be built for all solution targets
public bool ShouldBuildForAllSolutionTargets
{
get ;
set ;
}
2015-03-02 10:06:55 -05:00
/// Whether this project should be built by default. Can still be built from the IDE through the context menu.
public bool ShouldBuildByDefaultForSolutionTargets
{
get ;
set ;
}
2014-08-29 12:25:29 -04:00
2014-06-24 17:33:20 -04:00
2014-03-14 14:13:41 -04:00
/// All of the targets in this project. All non-stub projects must have at least one target.
public readonly List < ProjectTarget > ProjectTargets = new List < ProjectTarget > ( ) ;
/// <summary>
/// Adds a list of files to this project, ignoring dupes
/// </summary>
/// <param name="FilesToAdd">Files to add</param>
/// <param name="RelativeBaseFolder">The directory the path within the project will be relative to</param>
2015-09-24 12:37:21 -04:00
public void AddFilesToProject ( List < FileReference > FilesToAdd , DirectoryReference BaseFolder )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
foreach ( var CurFile in FilesToAdd )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
AddFileToProject ( CurFile , BaseFolder ) ;
2014-03-14 14:13:41 -04:00
}
}
2014-04-24 04:43:16 -04:00
/// Aliased (i.e. files is custom filter tree) in this project
public readonly List < AliasedFile > AliasedFiles = new List < AliasedFile > ( ) ;
/// <summary>
/// Adds aliased file to the project.
/// </summary>
/// <param name="File">Aliased file.</param>
public void AddAliasedFileToProject ( AliasedFile File )
{
AliasedFiles . Add ( File ) ;
}
2015-09-24 12:37:21 -04:00
2014-03-14 14:13:41 -04:00
/// <summary>
/// Adds a file to this project, ignoring dupes
/// </summary>
/// <param name="FilePath">Path to the file on disk</param>
2015-09-03 08:47:24 -04:00
/// <param name="BaseFolder">The directory the path within the project will be relative to</param>
2015-09-24 12:37:21 -04:00
public void AddFileToProject ( FileReference FilePath , DirectoryReference BaseFolder )
2014-03-14 14:13:41 -04:00
{
// Don't add duplicates
SourceFile ExistingFile = null ;
2015-09-24 12:37:21 -04:00
if ( SourceFileMap . TryGetValue ( FilePath , out ExistingFile ) )
{
if ( ExistingFile . BaseFolder ! = BaseFolder )
{
throw new BuildException ( "Trying to add file '" + FilePath + "' to project '" + ProjectFilePath + "' when the file already exists, but with a different relative base folder '" + BaseFolder + "' is different than the current file's '" + ExistingFile . BaseFolder + "'!" ) ;
}
2014-03-14 14:13:41 -04:00
}
else
{
2015-09-24 12:37:21 -04:00
SourceFile File = AllocSourceFile ( FilePath , BaseFolder ) ;
if ( File ! = null )
2014-03-14 14:13:41 -04:00
{
SourceFileMap [ FilePath ] = File ;
2015-09-24 12:37:21 -04:00
SourceFiles . Add ( File ) ;
2014-03-14 14:13:41 -04:00
}
}
}
/// <summary>
/// Splits the definition text into macro name and value (if any).
/// </summary>
/// <param name="Definition">Definition text</param>
2015-01-06 09:10:20 -05:00
/// <param name="Key">Out: The definition name</param>
/// <param name="Value">Out: The definition value or null if it has none</param>
2014-03-14 14:13:41 -04:00
/// <returns>Pair representing macro name and value.</returns>
2015-01-06 09:10:20 -05:00
private void SplitDefinitionAndValue ( string Definition , out String Key , out String Value )
2014-03-14 14:13:41 -04:00
{
int EqualsIndex = Definition . IndexOf ( '=' ) ;
if ( EqualsIndex > = 0 )
{
2015-01-06 09:10:20 -05:00
Key = Definition . Substring ( 0 , EqualsIndex ) ;
Value = Definition . Substring ( EqualsIndex + 1 ) ;
2014-03-14 14:13:41 -04:00
}
else
{
2015-01-06 09:10:20 -05:00
Key = Definition ;
Value = "" ;
2014-03-14 14:13:41 -04:00
}
}
/// <summary>
/// Adds all of the specified preprocessor definitions to this VCProject's list of preprocessor definitions for all modules in the project
/// </summary>
/// <param name="NewPreprocessorDefinitions">List of preprocessor definitons to add</param>
2015-09-24 12:37:21 -04:00
public void AddIntelliSensePreprocessorDefinitions ( List < string > NewPreprocessorDefinitions )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( ProjectFileGenerator . OnlyGenerateIntelliSenseDataForProject = = null | |
ProjectFileGenerator . OnlyGenerateIntelliSenseDataForProject = = this )
{
foreach ( var CurDef in NewPreprocessorDefinitions )
2014-03-14 14:13:41 -04:00
{
2015-01-06 09:10:20 -05:00
// Don't add definitions and value combinations that have already been added for this project
2015-09-24 12:37:21 -04:00
if ( KnownIntelliSensePreprocessorDefinitions . Add ( CurDef ) )
2014-03-14 14:13:41 -04:00
{
2015-01-06 09:10:20 -05:00
// Go ahead and check to see if the definition already exists, but the value is different
var AlreadyExists = false ;
2014-03-14 14:13:41 -04:00
2015-01-06 09:10:20 -05:00
string Def , Value ;
SplitDefinitionAndValue ( CurDef , out Def , out Value ) ;
for ( int DefineIndex = 0 ; DefineIndex < IntelliSensePreprocessorDefinitions . Count ; + + DefineIndex )
{
string ExistingDef , ExistingValue ;
SplitDefinitionAndValue ( IntelliSensePreprocessorDefinitions [ DefineIndex ] , out ExistingDef , out ExistingValue ) ;
if ( ExistingDef = = Def )
{
// Already exists, but the value is changing. We don't bother clobbering values for existing defines for this project.
AlreadyExists = true ;
break ;
}
}
2015-09-24 12:37:21 -04:00
if ( ! AlreadyExists )
2015-01-06 09:10:20 -05:00
{
2015-09-24 12:37:21 -04:00
IntelliSensePreprocessorDefinitions . Add ( CurDef ) ;
2015-01-06 09:10:20 -05:00
}
2014-03-14 14:13:41 -04:00
}
}
}
}
/// <summary>
/// Adds all of the specified include paths to this VCProject's list of include paths for all modules in the project
/// </summary>
/// <param name="NewIncludePaths">List of include paths to add</param>
2015-09-24 12:37:21 -04:00
public void AddInteliiSenseIncludePaths ( HashSet < string > NewIncludePaths , bool bAddingSystemIncludes )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( ProjectFileGenerator . OnlyGenerateIntelliSenseDataForProject = = null | |
ProjectFileGenerator . OnlyGenerateIntelliSenseDataForProject = = this )
{
foreach ( var CurPath in NewIncludePaths )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
if ( bAddingSystemIncludes ? KnownIntelliSenseSystemIncludeSearchPaths . Add ( CurPath ) : KnownIntelliSenseIncludeSearchPaths . Add ( CurPath ) )
2014-03-14 14:13:41 -04:00
{
2015-01-06 09:10:20 -05:00
string PathRelativeToProjectFile ;
2014-03-14 14:13:41 -04:00
2015-01-06 09:10:20 -05:00
// If the include string is an environment variable (e.g. $(DXSDK_DIR)), then we never want to
// give it a relative path
2015-09-24 12:37:21 -04:00
if ( CurPath . StartsWith ( "$(" ) )
2014-03-14 14:13:41 -04:00
{
2015-01-06 09:10:20 -05:00
PathRelativeToProjectFile = CurPath ;
}
else
{
// Incoming include paths are relative to the solution directory, but we need these paths to be
// relative to the project file's directory
2015-09-24 12:37:21 -04:00
PathRelativeToProjectFile = NormalizeProjectPath ( CurPath ) ;
2014-03-14 14:13:41 -04:00
}
2015-01-06 09:10:20 -05:00
// Trim any trailing slash
PathRelativeToProjectFile = PathRelativeToProjectFile . TrimEnd ( '/' , '\\' ) ;
// Make sure that it doesn't exist already
var AlreadyExists = false ;
List < string > SearchPaths = bAddingSystemIncludes ? IntelliSenseSystemIncludeSearchPaths : IntelliSenseIncludeSearchPaths ;
2015-09-24 12:37:21 -04:00
foreach ( var ExistingPath in SearchPaths )
2015-01-06 09:10:20 -05:00
{
2015-09-24 12:37:21 -04:00
if ( PathRelativeToProjectFile = = ExistingPath )
2015-01-06 09:10:20 -05:00
{
AlreadyExists = true ;
break ;
}
}
2015-09-24 12:37:21 -04:00
if ( ! AlreadyExists )
2015-01-06 09:10:20 -05:00
{
2015-09-24 12:37:21 -04:00
SearchPaths . Add ( PathRelativeToProjectFile ) ;
2015-01-06 09:10:20 -05:00
}
2014-03-14 14:13:41 -04:00
}
}
}
}
/// <summary>
/// Add the given project to the DepondsOn project list.
/// </summary>
/// <param name="InProjectFile">The project this project is dependent on</param>
public void AddDependsOnProject ( ProjectFile InProjectFile )
{
// Make sure that it doesn't exist already
var AlreadyExists = false ;
foreach ( var ExistingDependentOn in DependsOnProjects )
{
if ( ExistingDependentOn = = InProjectFile )
{
AlreadyExists = true ;
break ;
}
}
if ( AlreadyExists = = false )
{
DependsOnProjects . Add ( InProjectFile ) ;
}
}
/// <summary>
/// Writes a project file to disk
/// </summary>
/// <param name="InPlatforms">The platforms to write the project files for</param>
/// <param name="InConfigurations">The configurations to add to the project files</param>
/// <returns>True on success</returns>
public virtual bool WriteProjectFile ( List < UnrealTargetPlatform > InPlatforms , List < UnrealTargetConfiguration > InConfigurations )
{
2015-09-24 12:37:21 -04:00
throw new BuildException ( "UnrealBuildTool cannot automatically generate this project type because WriteProjectFile() was not overridden." ) ;
2014-03-14 14:13:41 -04:00
}
public virtual void LoadGUIDFromExistingProject ( )
{
}
/// <summary>
/// Allocates a generator-specific source file object
/// </summary>
/// <param name="InitFilePath">Path to the source file on disk</param>
/// <param name="InitProjectSubFolder">Optional sub-folder to put the file in. If empty, this will be determined automatically from the file's path relative to the project file</param>
/// <returns>The newly allocated source file object</returns>
2015-09-24 12:37:21 -04:00
public virtual SourceFile AllocSourceFile ( FileReference InitFilePath , DirectoryReference InitProjectSubFolder = null )
2014-03-14 14:13:41 -04:00
{
2015-09-24 12:37:21 -04:00
return new SourceFile ( InitFilePath , InitProjectSubFolder ) ;
2014-03-14 14:13:41 -04:00
}
2015-09-24 13:47:13 -04:00
/// <summary>
/// Takes the given path and tries to rebase it relative to the project or solution directory variables.
/// </summary>
2014-03-14 14:13:41 -04:00
public string NormalizeProjectPath ( string InputPath )
{
// If the path is rooted in an environment variable, leave it be.
2015-09-24 12:37:21 -04:00
if ( InputPath . StartsWith ( "$(" ) )
2014-03-14 14:13:41 -04:00
{
return InputPath ;
}
2015-09-03 08:47:24 -04:00
else
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
return NormalizeProjectPath ( new FileReference ( InputPath ) ) ;
2014-03-14 14:13:41 -04:00
}
2015-09-03 08:47:24 -04:00
}
2015-09-24 13:47:13 -04:00
/// <summary>
/// Takes the given path and tries to rebase it relative to the project.
/// </summary>
2015-09-03 08:47:24 -04:00
public string NormalizeProjectPath ( FileReference InputPath )
{
// Try to make it relative to the solution directory.
2015-09-24 12:37:21 -04:00
if ( InputPath . IsUnderDirectory ( ProjectFileGenerator . MasterProjectPath ) )
2014-03-14 14:13:41 -04:00
{
2015-09-03 08:47:24 -04:00
return InputPath . MakeRelativeTo ( ProjectFileGenerator . IntermediateProjectFilesPath ) ;
2014-04-02 18:09:23 -04:00
}
else
{
2015-09-03 08:47:24 -04:00
return InputPath . FullName ;
2014-03-14 14:13:41 -04:00
}
}
2015-09-24 13:47:13 -04:00
/// <summary>
/// Takes the given path, normalizes it, and quotes it if necessary.
/// </summary>
2014-03-14 14:13:41 -04:00
public string EscapePath ( string InputPath )
{
string Result = InputPath ;
if ( Result . Contains ( ' ' ) )
{
Result = "\"" + Result + "\"" ;
}
return Result ;
}
2015-09-24 13:47:13 -04:00
/// <summary>
/// Visualizer for the debugger
/// </summary>
2014-08-29 12:25:29 -04:00
public override string ToString ( )
{
2015-09-03 08:47:24 -04:00
return ProjectFilePath . ToString ( ) ;
2014-08-29 12:25:29 -04:00
}
2014-03-14 14:13:41 -04:00
/// Map of file paths to files in the project.
2015-09-24 12:37:21 -04:00
private readonly Dictionary < FileReference , SourceFile > SourceFileMap = new Dictionary < FileReference , SourceFile > ( ) ;
2014-03-14 14:13:41 -04:00
/// Files in this project
public readonly List < SourceFile > SourceFiles = new List < SourceFile > ( ) ;
/// Include paths for every single module in the project file, merged together
public readonly List < string > IntelliSenseIncludeSearchPaths = new List < string > ( ) ;
2014-04-02 18:09:23 -04:00
public readonly List < string > IntelliSenseSystemIncludeSearchPaths = new List < string > ( ) ;
2015-09-24 12:37:21 -04:00
public readonly HashSet < string > KnownIntelliSenseIncludeSearchPaths = new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase ) ;
public readonly HashSet < string > KnownIntelliSenseSystemIncludeSearchPaths = new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase ) ;
2014-03-14 14:13:41 -04:00
/// Preprocessor definitions for every single module in the project file, merged together
public readonly List < string > IntelliSensePreprocessorDefinitions = new List < string > ( ) ;
2015-09-24 12:37:21 -04:00
public readonly HashSet < string > KnownIntelliSensePreprocessorDefinitions = new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase ) ;
2014-03-14 14:13:41 -04:00
/// Projects that this project is dependent on
public readonly List < ProjectFile > DependsOnProjects = new List < ProjectFile > ( ) ;
}
}