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 ;
2020-12-21 23:07:37 -04:00
using EpicGames.Core ;
2020-10-12 18:14:03 -04:00
using System.Diagnostics ;
2021-06-09 13:40:10 -04:00
using UnrealBuildBase ;
2021-11-18 14:37:34 -05:00
using System.Diagnostics.CodeAnalysis ;
2020-12-20 18:47:42 -04:00
2018-10-01 08:42:30 -04:00
namespace UnrealBuildTool
{
/// <summary>
/// Generates project files for one or more projects
/// </summary>
2020-10-12 18:14:03 -04:00
[ToolMode("GenerateProjectFiles", ToolModeOptions.XmlConfig | ToolModeOptions.BuildPlatforms | ToolModeOptions.SingleInstance | ToolModeOptions.UseStartupTraceListener)]
2018-10-01 08:42:30 -04:00
class GenerateProjectFilesMode : ToolMode
{
/// <summary>
/// Types of project files to generate
/// </summary>
[CommandLine("-ProjectFileFormat")]
2018-12-05 01:04:21 -05:00
[CommandLine("-2019", Value = nameof(ProjectFileFormat.VisualStudio2019))] // + override compiler
2021-05-26 19:58:10 -04:00
[CommandLine("-2022", Value = nameof(ProjectFileFormat.VisualStudio2022))] // + 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))]
2021-04-29 19:32:06 -04:00
#if __VPROJECT_AVAILABLE__
[CommandLine("-VProject", Value = nameof(ProjectFileFormat.VProject))]
#endif
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")]
2021-11-18 14:37:34 -05:00
string [ ] ? DisablePlatformProjectGenerators = null ;
2019-01-03 16:31:41 -05:00
2020-11-24 18:42:39 -04:00
/// <summary>
/// Whether this command is being run in an automated mode
/// </summary>
[CommandLine("-Automated")]
bool bAutomated = false ;
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 ) ;
2020-10-12 18:14:03 -04:00
// set up logging (taken from BuildMode)
FileReference LogFile = FileReference . Combine ( UnrealBuildTool . EngineProgramSavedDirectory , "UnrealBuildTool" , "Log_GPF.txt" ) ;
2021-11-07 23:43:01 -05:00
Log . AddFileWriter ( "DefaultLogTraceListener" , LogFile ) ;
2020-10-12 18:14:03 -04:00
2018-12-08 11:31:03 -05:00
// Parse rocket-specific arguments.
2021-11-18 14:37:34 -05:00
FileReference ? ProjectFile ;
2018-12-09 10:58:16 -05:00
TryParseProjectFileArgument ( Arguments , out ProjectFile ) ;
2018-12-08 11:31:03 -05:00
2020-03-20 20:55:23 -04:00
// Warn if there are explicit project file formats specified
2020-11-24 18:42:39 -04:00
if ( ProjectFileFormats . Count > 0 & & ! bAutomated )
2020-03-20 20:55:23 -04:00
{
StringBuilder Configuration = new StringBuilder ( ) ;
Configuration . Append ( "Project file formats specified via the command line will be ignored when generating\n" ) ;
Configuration . Append ( "project files from the editor and other engine tools.\n" ) ;
Configuration . Append ( "\n" ) ;
Configuration . Append ( "Consider setting your desired IDE from the editor preferences window, or modify your\n" ) ;
Configuration . Append ( "BuildConfiguration.xml file with:\n" ) ;
Configuration . Append ( "\n" ) ;
Configuration . Append ( "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" ) ;
Configuration . Append ( "<Configuration xmlns=\"https://www.unrealengine.com/BuildConfiguration\">\n" ) ;
Configuration . Append ( " <ProjectFileGenerator>\n" ) ;
foreach ( ProjectFileFormat ProjectFileFormat in ProjectFileFormats )
{
Configuration . AppendFormat ( " <Format>{0}</Format>\n" , ProjectFileFormat ) ;
}
Configuration . Append ( " </ProjectFileGenerator>\n" ) ;
Configuration . Append ( "</Configuration>\n" ) ;
Log . TraceWarning ( "{0}" , Configuration . ToString ( ) ) ;
}
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 ) ) )
{
2021-11-18 14:37:34 -05:00
PlatformProjectGenerator Generator = ( PlatformProjectGenerator ) Activator . CreateInstance ( CheckType , Arguments ) ! ;
2018-12-08 11:31:03 -05:00
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
2020-10-09 13:20:15 -04:00
// print out any errors to the log
2020-10-12 18:14:03 -04:00
List < string > BadPlatformNames = new List < string > ( ) ;
Log . TraceLog ( "\n--- SDK INFO START ---" ) ;
2020-10-09 13:20:15 -04:00
foreach ( string PlatformName in UnrealTargetPlatform . GetValidPlatformNames ( ) )
{
2021-11-18 14:37:34 -05:00
UEBuildPlatformSDK ? SDK = UEBuildPlatformSDK . GetSDKForPlatform ( PlatformName ) ;
2020-10-09 13:20:15 -04:00
if ( SDK ! = null & & SDK . bIsSdkAllowedOnHost )
{
2020-10-12 18:14:03 -04:00
// print out the info to the log, and if it's invalid, remember it
SDKStatus Validity = SDK . PrintSDKInfoAndReturnValidity ( LogEventType . Verbose , LogFormatOptions . NoConsoleOutput , LogEventType . Warning , LogFormatOptions . NoConsoleOutput ) ;
if ( Validity = = SDKStatus . Invalid )
{
BadPlatformNames . Add ( PlatformName ) ;
}
2020-10-09 13:20:15 -04:00
}
}
2020-10-12 18:14:03 -04:00
if ( BadPlatformNames . Count > 0 )
{
2021-08-30 08:44:36 -04:00
Log . TraceInformationOnce ( "\nSome Platforms were skipped due to invalid SDK setup: {0}.\nSee the log file for detailed information\n\n" , string . Join ( ", " , BadPlatformNames ) ) ;
2020-10-12 18:14:03 -04:00
Log . TraceInformation ( "" ) ;
}
Log . TraceLog ( "--- SDK INFO END ---" ) ;
Log . TraceLog ( "" ) ;
2020-10-09 13:20:15 -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 . VisualStudio2019 :
Generator = new VCProjectFileGenerator ( ProjectFile , VCProjectFileFormat . VisualStudio2019 , Arguments ) ;
break ;
2021-05-26 19:58:10 -04:00
case ProjectFileFormat . VisualStudio2022 :
Generator = new VCProjectFileGenerator ( ProjectFile , VCProjectFileFormat . VisualStudio2022 , Arguments ) ;
break ;
2018-12-08 11:31:03 -05:00
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 ;
2021-04-29 19:32:06 -04:00
#if __VPROJECT_AVAILABLE__
case ProjectFileFormat . VProject :
Generator = new VProjectFileGenerator ( ProjectFile ) ;
break ;
#endif
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 )
{
2021-02-03 14:57:28 -04:00
ProjectFileGenerator . Current = Generator ;
bool bGenerateSuccess = Generator . GenerateProjectFiles ( PlatformProjectGenerators , Arguments . GetRawArray ( ) ) ;
ProjectFileGenerator . Current = null ;
if ( ! bGenerateSuccess )
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>
2021-11-18 14:37:34 -05:00
private static bool TryParseProjectFileArgument ( CommandLineArguments Arguments , [ NotNullWhen ( true ) ] out FileReference ? ProjectFile )
2018-12-09 10:58:16 -05:00
{
2021-11-18 14:37:34 -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 ;
}
2021-11-18 14:37:34 -05:00
FileReference ? InstalledProjectFile = UnrealBuildTool . GetInstalledProjectFile ( ) ;
2018-12-09 10:58:16 -05:00
if ( InstalledProjectFile ! = null )
{
ProjectFile = InstalledProjectFile ;
return true ;
}
ProjectFile = null ;
return false ;
}
2018-10-01 08:42:30 -04:00
}
}