2019-12-26 23:01:54 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2018-11-26 16:46:35 -05:00
2018-10-01 08:42:30 -04:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
2018-10-01 09:45:58 -04:00
using System.Reflection ;
2018-10-01 08:42:30 -04:00
using System.Text ;
using System.Threading.Tasks ;
using Tools.DotNETCommon ;
namespace UnrealBuildTool
{
/// <summary>
/// Generates project files for one or more projects
/// </summary>
2018-12-08 11:31:03 -05:00
[ToolMode("GenerateProjectFiles", ToolModeOptions.XmlConfig | ToolModeOptions.BuildPlatforms | ToolModeOptions.SingleInstance)]
2018-10-01 08:42:30 -04:00
class GenerateProjectFilesMode : ToolMode
{
/// <summary>
/// Types of project files to generate
/// </summary>
[CommandLine("-ProjectFileFormat")]
[CommandLine("-2012unsupported", Value = nameof(ProjectFileFormat.VisualStudio2012))]
[CommandLine("-2013unsupported", Value = nameof(ProjectFileFormat.VisualStudio2013))]
[CommandLine("-2015", Value = nameof(ProjectFileFormat.VisualStudio2015))] // + override compiler
[CommandLine("-2017", Value = nameof(ProjectFileFormat.VisualStudio2017))] // + override compiler
2018-12-05 01:04:21 -05:00
[CommandLine("-2019", Value = nameof(ProjectFileFormat.VisualStudio2019))] // + override compiler
2018-10-01 08:42:30 -04:00
[CommandLine("-Makefile", Value = nameof(ProjectFileFormat.Make))]
[CommandLine("-CMakefile", Value = nameof(ProjectFileFormat.CMake))]
[CommandLine("-QMakefile", Value = nameof(ProjectFileFormat.QMake))]
[CommandLine("-KDevelopfile", Value = nameof(ProjectFileFormat.KDevelop))]
[CommandLine("-CodeLiteFiles", Value = nameof(ProjectFileFormat.CodeLite))]
[CommandLine("-XCodeProjectFiles", Value = nameof(ProjectFileFormat.XCode))]
[CommandLine("-EddieProjectFiles", Value = nameof(ProjectFileFormat.Eddie))]
[CommandLine("-VSCode", Value = nameof(ProjectFileFormat.VisualStudioCode))]
[CommandLine("-VSMac", Value = nameof(ProjectFileFormat.VisualStudioMac))]
[CommandLine("-CLion", Value = nameof(ProjectFileFormat.CLion))]
2019-08-16 13:41:27 -04:00
[CommandLine("-Rider", Value = nameof(ProjectFileFormat.Rider))]
2018-10-01 08:42:30 -04:00
HashSet < ProjectFileFormat > ProjectFileFormats = new HashSet < ProjectFileFormat > ( ) ;
2019-01-03 16:31:41 -05:00
/// <summary>
2019-10-11 16:59:16 -04:00
/// Disable native project file generators for platforms. Platforms with native project file generators typically require IDE extensions to be installed.
2019-01-03 16:31:41 -05:00
/// </summary>
[XmlConfigFile(Category = "ProjectFileGenerator")]
string [ ] DisablePlatformProjectGenerators = null ;
2018-10-01 08:42:30 -04:00
/// <summary>
/// Execute the tool mode
/// </summary>
/// <param name="Arguments">Command line arguments</param>
/// <returns>Exit code</returns>
public override int Execute ( CommandLineArguments Arguments )
{
2018-12-08 11:31:03 -05:00
// Apply any command line arguments to this class
Arguments . ApplyTo ( this ) ;
2019-01-03 16:31:41 -05:00
// Apply the XML config to this class
XmlConfig . ApplyTo ( this ) ;
2018-12-08 11:31:03 -05:00
// Parse rocket-specific arguments.
FileReference ProjectFile ;
2018-12-09 10:58:16 -05:00
TryParseProjectFileArgument ( Arguments , out ProjectFile ) ;
2018-12-08 11:31:03 -05:00
// If there aren't any formats set, read the default project file format from the config file
if ( ProjectFileFormats . Count = = 0 )
2018-10-01 08:42:30 -04:00
{
2018-12-08 11:31:03 -05:00
// Read from the XML config
if ( ! String . IsNullOrEmpty ( ProjectFileGeneratorSettings . Format ) )
{
ProjectFileFormats . UnionWith ( ProjectFileGeneratorSettings . ParseFormatList ( ProjectFileGeneratorSettings . Format ) ) ;
}
2018-10-01 08:42:30 -04:00
2018-12-08 11:31:03 -05:00
// Read from the editor config
ProjectFileFormat PreferredSourceCodeAccessor ;
if ( ProjectFileGenerator . GetPreferredSourceCodeAccessor ( ProjectFile , out PreferredSourceCodeAccessor ) )
{
ProjectFileFormats . Add ( PreferredSourceCodeAccessor ) ;
}
2018-10-01 08:42:30 -04:00
2018-12-08 11:31:03 -05:00
// If there's still nothing set, get the default project file format for this platform
2018-10-01 08:42:30 -04:00
if ( ProjectFileFormats . Count = = 0 )
{
2018-12-08 11:31:03 -05:00
ProjectFileFormats . UnionWith ( BuildHostPlatform . Current . GetDefaultProjectFileFormats ( ) ) ;
}
}
2018-10-01 08:42:30 -04:00
2018-12-08 11:31:03 -05:00
// Register all the platform project generators
PlatformProjectGeneratorCollection PlatformProjectGenerators = new PlatformProjectGeneratorCollection ( ) ;
foreach ( Type CheckType in Assembly . GetExecutingAssembly ( ) . GetTypes ( ) )
{
if ( CheckType . IsClass & & ! CheckType . IsAbstract & & CheckType . IsSubclassOf ( typeof ( PlatformProjectGenerator ) ) )
{
PlatformProjectGenerator Generator = ( PlatformProjectGenerator ) Activator . CreateInstance ( CheckType , Arguments ) ;
foreach ( UnrealTargetPlatform Platform in Generator . GetPlatforms ( ) )
2018-10-01 08:42:30 -04:00
{
2019-01-10 16:14:38 -05:00
if ( DisablePlatformProjectGenerators = = null | | ! DisablePlatformProjectGenerators . Any ( x = > x . Equals ( Platform . ToString ( ) , StringComparison . OrdinalIgnoreCase ) ) )
2019-01-03 16:31:41 -05:00
{
Log . TraceVerbose ( "Registering project generator {0} for {1}" , CheckType , Platform ) ;
PlatformProjectGenerators . RegisterPlatformProjectGenerator ( Platform , Generator ) ;
}
2018-10-01 08:42:30 -04:00
}
}
2018-12-08 11:31:03 -05:00
}
2018-10-01 08:42:30 -04:00
2018-12-08 11:31:03 -05:00
// Create each project generator and run it
List < ProjectFileGenerator > Generators = new List < ProjectFileGenerator > ( ) ;
foreach ( ProjectFileFormat ProjectFileFormat in ProjectFileFormats . Distinct ( ) )
{
ProjectFileGenerator Generator ;
switch ( ProjectFileFormat )
2018-10-01 09:45:58 -04:00
{
2018-12-08 11:31:03 -05:00
case ProjectFileFormat . Make :
Generator = new MakefileGenerator ( ProjectFile ) ;
break ;
case ProjectFileFormat . CMake :
Generator = new CMakefileGenerator ( ProjectFile ) ;
break ;
case ProjectFileFormat . QMake :
Generator = new QMakefileGenerator ( ProjectFile ) ;
break ;
case ProjectFileFormat . KDevelop :
Generator = new KDevelopGenerator ( ProjectFile ) ;
break ;
case ProjectFileFormat . CodeLite :
Generator = new CodeLiteGenerator ( ProjectFile , Arguments ) ;
break ;
case ProjectFileFormat . VisualStudio :
Generator = new VCProjectFileGenerator ( ProjectFile , VCProjectFileFormat . Default , Arguments ) ;
break ;
case ProjectFileFormat . VisualStudio2012 :
Generator = new VCProjectFileGenerator ( ProjectFile , VCProjectFileFormat . VisualStudio2012 , Arguments ) ;
break ;
case ProjectFileFormat . VisualStudio2013 :
Generator = new VCProjectFileGenerator ( ProjectFile , VCProjectFileFormat . VisualStudio2013 , Arguments ) ;
break ;
case ProjectFileFormat . VisualStudio2015 :
Generator = new VCProjectFileGenerator ( ProjectFile , VCProjectFileFormat . VisualStudio2015 , Arguments ) ;
break ;
case ProjectFileFormat . VisualStudio2017 :
Generator = new VCProjectFileGenerator ( ProjectFile , VCProjectFileFormat . VisualStudio2017 , Arguments ) ;
break ;
case ProjectFileFormat . VisualStudio2019 :
Generator = new VCProjectFileGenerator ( ProjectFile , VCProjectFileFormat . VisualStudio2019 , Arguments ) ;
break ;
case ProjectFileFormat . XCode :
2019-03-18 16:38:36 -04:00
Generator = new XcodeProjectFileGenerator ( ProjectFile , Arguments ) ;
2018-12-08 11:31:03 -05:00
break ;
case ProjectFileFormat . Eddie :
Generator = new EddieProjectFileGenerator ( ProjectFile ) ;
break ;
case ProjectFileFormat . VisualStudioCode :
Generator = new VSCodeProjectFileGenerator ( ProjectFile ) ;
break ;
case ProjectFileFormat . CLion :
Generator = new CLionGenerator ( ProjectFile ) ;
break ;
case ProjectFileFormat . VisualStudioMac :
Generator = new VCMacProjectFileGenerator ( ProjectFile , Arguments ) ;
break ;
2019-08-16 13:41:27 -04:00
case ProjectFileFormat . Rider :
Generator = new RiderProjectFileGenerator ( ProjectFile , Arguments ) ;
break ;
2018-12-08 11:31:03 -05:00
default :
throw new BuildException ( "Unhandled project file type '{0}" , ProjectFileFormat ) ;
2018-10-01 09:45:58 -04:00
}
2018-12-08 11:31:03 -05:00
Generators . Add ( Generator ) ;
}
2018-10-01 09:45:58 -04:00
2018-12-08 11:31:03 -05:00
// Check there are no superfluous command line arguments
2019-01-16 19:30:43 -05:00
// TODO (still pass raw arguments below)
// Arguments.CheckAllArgumentsUsed();
2018-12-08 11:31:03 -05:00
// Now generate project files
ProjectFileGenerator . bGenerateProjectFiles = true ;
foreach ( ProjectFileGenerator Generator in Generators )
{
if ( ! Generator . GenerateProjectFiles ( PlatformProjectGenerators , Arguments . GetRawArray ( ) ) )
2018-10-01 08:42:30 -04:00
{
2018-12-20 14:53:34 -05:00
return ( int ) CompilationResult . OtherCompilationError ;
2018-10-01 08:42:30 -04:00
}
}
2018-12-20 14:53:34 -05:00
return ( int ) CompilationResult . Succeeded ;
2018-10-01 08:42:30 -04:00
}
2018-12-09 10:58:16 -05:00
/// <summary>
/// Try to parse the project file from the command line
/// </summary>
/// <param name="Arguments">The command line arguments</param>
/// <param name="ProjectFile">The project file that was parsed</param>
/// <returns>True if the project file was parsed, false otherwise</returns>
private static bool TryParseProjectFileArgument ( CommandLineArguments Arguments , out FileReference ProjectFile )
{
2020-01-31 10:24:39 -05:00
string CandidateProjectPath = null ;
2018-12-09 10:58:16 -05:00
2020-01-31 10:24:39 -05:00
// look for -project=<path>, if it does not exist check arguments for anything that has .uproject in it
if ( ! Arguments . TryGetValue ( "-Project=" , out CandidateProjectPath ) )
2018-12-09 10:58:16 -05:00
{
2020-01-31 10:24:39 -05:00
// Go through the argument list and try to match poorly (or well..) formed arguments like
// EngineTest, EngineTest.uproject
// Collaboration/FooProject
// by checking for those in the native project list
for ( int Idx = 0 ; Idx < Arguments . Count ; Idx + + )
2018-12-09 10:58:16 -05:00
{
2020-01-31 10:24:39 -05:00
if ( Arguments [ Idx ] [ 0 ] ! = '-' & & Arguments [ Idx ] . EndsWith ( ".uproject" , StringComparison . OrdinalIgnoreCase ) )
{
CandidateProjectPath = Arguments [ Idx ] ;
Arguments . MarkAsUsed ( Idx ) ;
break ;
}
2018-12-09 10:58:16 -05:00
}
}
2020-01-31 10:24:39 -05:00
// We have a project file either via -project= or because there was something called .uproject in the arg list
// so now validate it
if ( ! string . IsNullOrEmpty ( CandidateProjectPath ) )
{
FileReference CandidateProjectFile = new FileReference ( CandidateProjectPath ) ;
// if the path doesn't exist then check native paths (ueprojectdirs)
if ( ! FileReference . Exists ( CandidateProjectFile ) )
{
// clean everything the user provided to just the name and make sure it has the expected extension
string ProjectName = CandidateProjectFile . ChangeExtension ( "uproject" ) . GetFileName ( ) ;
// check native project paths (uprojectdirs)
IEnumerable < FileReference > NativeProjectFiles = NativeProjects . EnumerateProjectFiles ( ) ;
CandidateProjectFile = NativeProjectFiles . Where ( F = > F . GetFileName ( ) . Equals ( ProjectName , StringComparison . InvariantCultureIgnoreCase ) ) . FirstOrDefault ( ) ;
}
if ( CandidateProjectFile = = null | | ! FileReference . Exists ( CandidateProjectFile ) )
{
// if we didn't find anything then throw an error as the user explicitly provided a uproject
throw new Exception ( string . Format ( "Unable to find project file based on argument {0}" , CandidateProjectPath ) ) ;
}
Log . TraceVerbose ( "Resolved project argument {0} to {1}" , CandidateProjectPath , CandidateProjectFile ) ;
ProjectFile = CandidateProjectFile ;
return true ;
}
2018-12-09 10:58:16 -05:00
FileReference InstalledProjectFile = UnrealBuildTool . GetInstalledProjectFile ( ) ;
if ( InstalledProjectFile ! = null )
{
ProjectFile = InstalledProjectFile ;
return true ;
}
ProjectFile = null ;
return false ;
}
2018-10-01 08:42:30 -04:00
}
}