2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
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>
void AddIntelliSensePreprocessorDefinitions ( List < string > NewPreprocessorDefinitions ) ;
/// <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
public string TargetFilePath ;
/// 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 ( )
{
return Path . GetFileNameWithoutExtension ( TargetFilePath ) ;
}
}
2014-04-24 04:43:16 -04:00
/// <summary>
/// Class that stores info about aliased file.
/// </summary>
public class AliasedFile
{
public AliasedFile ( string FileSystemPath , string ProjectPath )
{
this . FileSystemPath = FileSystemPath ;
this . ProjectPath = ProjectPath ;
}
// File system path.
public string FileSystemPath { get ; private set ; }
// Project path.
public string ProjectPath { get ; private set ; }
}
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>
public SourceFile ( string InitFilePath , string InitRelativeBaseFolder )
{
FilePath = InitFilePath ;
RelativeBaseFolder = InitRelativeBaseFolder ;
}
public SourceFile ( )
{
}
/// <summary>
/// File path to file on disk
/// </summary>
public string FilePath
{
get ;
private set ;
}
/// <summary>
/// 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 RelativeBaseFolder.
/// </summary>
public string RelativeBaseFolder
{
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>
protected ProjectFile ( string InitRelativeFilePath )
{
RelativeProjectFilePath = InitRelativeFilePath ;
}
/// Full path to the project file on disk
public string ProjectFilePath
{
get
{
return Path . Combine ( ProjectFileGenerator . MasterProjectRelativePath , RelativeProjectFilePath ) ;
}
}
/// Project file path, relative to the master project
public string RelativeProjectFilePath
{
get ;
private set ;
}
/// 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 ;
}
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>
public void AddFilesToProject ( List < string > FilesToAdd , string RelativeBaseFolder )
{
foreach ( var CurFile in FilesToAdd )
{
AddFileToProject ( CurFile , RelativeBaseFolder ) ;
}
}
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 ) ;
}
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>
/// <param name="RelativeBaseFolder">The directory the path within the project will be relative to</param>
public void AddFileToProject ( string FilePath , string RelativeBaseFolder )
{
// Don't add duplicates
SourceFile ExistingFile = null ;
if ( SourceFileMap . TryGetValue ( FilePath , out ExistingFile ) )
{
if ( ExistingFile . RelativeBaseFolder ! = RelativeBaseFolder )
{
if ( ( ExistingFile . RelativeBaseFolder ! = null ) ! = ( RelativeBaseFolder ! = null ) | |
! ExistingFile . RelativeBaseFolder . Equals ( RelativeBaseFolder , StringComparison . InvariantCultureIgnoreCase ) )
{
throw new BuildException ( "Trying to add file '" + FilePath + "' to project '" + ProjectFilePath + "' when the file already exists, but with a different relative base folder '" + RelativeBaseFolder + "' is different than the current file's '" + ExistingFile . RelativeBaseFolder + "'!" ) ;
}
else
{
throw new BuildException ( "Trying to add file '" + FilePath + "' to project '" + ProjectFilePath + "' when the file already exists, but the specified project relative base folder is different than the current file's!" ) ;
}
}
}
else
{
SourceFile File = AllocSourceFile ( FilePath , RelativeBaseFolder ) ;
if ( File ! = null )
{
SourceFileMap [ FilePath ] = File ;
SourceFiles . Add ( File ) ;
}
}
}
/// <summary>
/// Splits the definition text into macro name and value (if any).
/// </summary>
/// <param name="Definition">Definition text</param>
/// <returns>Pair representing macro name and value.</returns>
private KeyValuePair < string , string > SplitDefinitionAndValue ( string Definition )
{
int EqualsIndex = Definition . IndexOf ( '=' ) ;
KeyValuePair < string , string > DefineAndValue ;
if ( EqualsIndex > = 0 )
{
DefineAndValue = new KeyValuePair < string , string > ( Definition . Substring ( 0 , EqualsIndex ) , Definition . Substring ( EqualsIndex + 1 ) ) ;
}
else
{
DefineAndValue = new KeyValuePair < string , string > ( Definition , "" ) ;
}
return DefineAndValue ;
}
/// <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>
public void AddIntelliSensePreprocessorDefinitions ( List < string > NewPreprocessorDefinitions )
{
foreach ( var CurDef in NewPreprocessorDefinitions )
{
if ( KnownIntelliSensePreprocessorDefinitions . Add ( CurDef ) )
{
// Make sure that it doesn't exist already
var AlreadyExists = false ;
var CurDefAndValue = SplitDefinitionAndValue ( CurDef ) ;
for ( int DefineIndex = 0 ; DefineIndex < IntelliSensePreprocessorDefinitions . Count ; + + DefineIndex )
{
var ExistingDefAndValue = SplitDefinitionAndValue ( IntelliSensePreprocessorDefinitions [ DefineIndex ] ) ;
if ( ExistingDefAndValue . Key = = CurDefAndValue . Key )
{
// Favor defines that set their value to 1 and replace the old one if it was set to "0"
if ( CurDefAndValue . Value = = "1" & & ExistingDefAndValue . Value = = "0" )
{
IntelliSensePreprocessorDefinitions [ DefineIndex ] = CurDef ;
}
AlreadyExists = true ;
break ;
}
}
if ( ! AlreadyExists )
{
IntelliSensePreprocessorDefinitions . Add ( CurDef ) ;
}
}
}
}
/// <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-09-11 03:21:51 -04:00
public void AddInteliiSenseIncludePaths ( HashSet < string > NewIncludePaths , bool bAddingSystemIncludes )
2014-03-14 14:13:41 -04:00
{
foreach ( var CurPath in NewIncludePaths )
{
2014-04-02 18:09:23 -04:00
if ( bAddingSystemIncludes ? KnownIntelliSenseSystemIncludeSearchPaths . Add ( CurPath ) : KnownIntelliSenseIncludeSearchPaths . Add ( CurPath ) )
2014-03-14 14:13:41 -04:00
{
string PathRelativeToProjectFile ;
// If the include string is an environment variable (e.g. $(DXSDK_DIR)), then we never want to
// give it a relative path
if ( CurPath . StartsWith ( "$(" ) )
{
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
PathRelativeToProjectFile = NormalizeProjectPath ( CurPath ) ;
}
// Trim any trailing slash
PathRelativeToProjectFile = PathRelativeToProjectFile . TrimEnd ( '/' , '\\' ) ;
// Make sure that it doesn't exist already
var AlreadyExists = false ;
2014-04-02 18:09:23 -04:00
List < string > SearchPaths = bAddingSystemIncludes ? IntelliSenseSystemIncludeSearchPaths : IntelliSenseIncludeSearchPaths ;
foreach ( var ExistingPath in SearchPaths )
2014-03-14 14:13:41 -04:00
{
if ( PathRelativeToProjectFile = = ExistingPath )
{
AlreadyExists = true ;
break ;
}
}
if ( ! AlreadyExists )
{
2014-04-02 18:09:23 -04:00
SearchPaths . Add ( PathRelativeToProjectFile ) ;
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 )
{
throw new BuildException ( "UnrealBuildTool cannot automatically generate this project type because WriteProjectFile() was not overridden." ) ;
}
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>
public virtual SourceFile AllocSourceFile ( string InitFilePath , string InitProjectSubFolder = null )
{
return new SourceFile ( InitFilePath , InitProjectSubFolder ) ;
}
/** Takes the given path and tries to rebase it relative to the project or solution directory variables. */
public string NormalizeProjectPath ( string InputPath )
{
// If the path is rooted in an environment variable, leave it be.
if ( InputPath . StartsWith ( "$(" ) )
{
return InputPath ;
}
// Otherwise make sure it's absolute
2014-07-17 13:49:42 -04:00
string FullInputPath = Utils . CleanDirectorySeparators ( Path . GetFullPath ( InputPath ) ) ;
2014-03-14 14:13:41 -04:00
// Try to make it relative to the solution directory.
2014-07-17 13:49:42 -04:00
string FullSolutionPath = Utils . CleanDirectorySeparators ( Path . GetFullPath ( ProjectFileGenerator . MasterProjectRelativePath ) ) ;
if ( FullSolutionPath . Last ( ) ! = Path . DirectorySeparatorChar )
2014-03-14 14:13:41 -04:00
{
2014-07-17 13:49:42 -04:00
FullSolutionPath + = Path . DirectorySeparatorChar ;
2014-03-14 14:13:41 -04:00
}
if ( FullInputPath . StartsWith ( FullSolutionPath ) )
{
2014-04-02 18:09:23 -04:00
FullInputPath = Utils . MakePathRelativeTo ( Utils . CleanDirectorySeparators ( FullInputPath ) , Path . GetDirectoryName ( Path . GetFullPath ( ProjectFilePath ) ) ) ;
}
else
{
FullInputPath = Utils . CleanDirectorySeparators ( FullInputPath ) ;
2014-03-14 14:13:41 -04:00
}
// Otherwise return the input
return FullInputPath ;
}
/** Takes the given path, normalizes it, and quotes it if necessary. */
public string EscapePath ( string InputPath )
{
string Result = InputPath ;
if ( Result . Contains ( ' ' ) )
{
Result = "\"" + Result + "\"" ;
}
return Result ;
}
2014-08-29 12:25:29 -04:00
/** Visualizer for the debugger */
public override string ToString ( )
{
return RelativeProjectFilePath ;
}
2014-03-14 14:13:41 -04:00
/// Map of file paths to files in the project.
private readonly Dictionary < string , SourceFile > SourceFileMap = new Dictionary < string , SourceFile > ( StringComparer . InvariantCultureIgnoreCase ) ;
/// 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 > ( ) ;
2014-03-14 14:13:41 -04:00
public readonly HashSet < string > KnownIntelliSenseIncludeSearchPaths = new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase ) ;
2014-04-02 18:09:23 -04:00
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 > ( ) ;
public readonly HashSet < string > KnownIntelliSensePreprocessorDefinitions = new HashSet < string > ( StringComparer . InvariantCultureIgnoreCase ) ;
/// Projects that this project is dependent on
public readonly List < ProjectFile > DependsOnProjects = new List < ProjectFile > ( ) ;
}
}