2019-12-26 23:01:54 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-08-20 09:26:18 -04:00
2019-10-25 11:09:19 -04:00
using System ;
2019-08-16 13:41:27 -04:00
using System.Collections.Generic ;
2020-10-22 19:19:16 -04:00
using System.Diagnostics ;
2020-04-14 09:30:15 -04:00
using System.IO ;
2019-08-16 13:41:27 -04:00
using System.Linq ;
2020-12-21 23:07:37 -04:00
using EpicGames.Core ;
2019-08-16 13:41:27 -04:00
2020-12-20 17:32:59 -04:00
#nullable disable
2019-08-16 13:41:27 -04:00
namespace UnrealBuildTool
{
internal class RiderProjectFile : ProjectFile
{
2020-10-22 19:19:16 -04:00
private static readonly XcrunRunner AppleHelper = new XcrunRunner ( ) ;
2019-08-16 13:41:27 -04:00
public DirectoryReference RootPath ;
public HashSet < TargetType > TargetTypes ;
public CommandLineArguments Arguments ;
2020-09-24 00:43:27 -04:00
private ToolchainInfo RootToolchainInfo = new ToolchainInfo ( ) ;
private UEBuildTarget CurrentTarget ;
2019-08-16 13:41:27 -04:00
public RiderProjectFile ( FileReference InProjectFilePath ) : base ( InProjectFilePath )
{
}
/// <summary>
/// Write project file info in JSON file.
/// For every combination of <c>UnrealTargetPlatform</c>, <c>UnrealTargetConfiguration</c> and <c>TargetType</c>
/// will be generated separate JSON file.
/// Project file will be stored:
/// For UE4: {UE4Root}/Engine/Intermediate/ProjectFiles/.Rider/{Platform}/{Configuration}/{TargetType}/{ProjectName}.json
/// For game: {GameRoot}/Intermediate/ProjectFiles/.Rider/{Platform}/{Configuration}/{TargetType}/{ProjectName}.json
/// </summary>
/// <remarks>
/// * <c>TargetType.Editor</c> will be generated for current platform only and will ignore <c>UnrealTargetConfiguration.Test</c> and <c>UnrealTargetConfiguration.Shipping</c> configurations
/// * <c>TargetType.Program</c> will be generated for current platform only and <c>UnrealTargetConfiguration.Development</c> configuration only
/// </remarks>
/// <param name="InPlatforms"></param>
/// <param name="InConfigurations"></param>
/// <param name="PlatformProjectGenerators"></param>
2020-09-24 00:43:27 -04:00
/// <param name="Minimize"></param>
2019-08-16 13:41:27 -04:00
/// <returns></returns>
2020-09-24 00:43:27 -04:00
public bool WriteProjectFile ( List < UnrealTargetPlatform > InPlatforms ,
2019-08-16 13:41:27 -04:00
List < UnrealTargetConfiguration > InConfigurations ,
2020-09-24 00:43:27 -04:00
PlatformProjectGeneratorCollection PlatformProjectGenerators , JsonWriterStyle Minimize )
2019-08-16 13:41:27 -04:00
{
string ProjectName = ProjectFilePath . GetFileNameWithoutAnyExtensions ( ) ;
2020-09-24 00:43:27 -04:00
DirectoryReference ProjectRootFolder = RootPath ;
2020-04-14 09:30:15 -04:00
List < Tuple < FileReference , UEBuildTarget > > FileToTarget = new List < Tuple < FileReference , UEBuildTarget > > ( ) ;
foreach ( UnrealTargetPlatform Platform in InPlatforms )
2019-08-16 13:41:27 -04:00
{
foreach ( UnrealTargetConfiguration Configuration in InConfigurations )
{
foreach ( ProjectTarget ProjectTarget in ProjectTargets )
{
if ( TargetTypes . Any ( ) & & ! TargetTypes . Contains ( ProjectTarget . TargetRules . Type ) ) continue ;
2020-09-24 00:43:27 -04:00
// Skip Programs for all configs except for current platform + Development & Debug configurations
if ( ProjectTarget . TargetRules . Type = = TargetType . Program & &
( BuildHostPlatform . Current . Platform ! = Platform | |
! ( Configuration = = UnrealTargetConfiguration . Development | | Configuration = = UnrealTargetConfiguration . Debug ) ) )
2019-08-16 13:41:27 -04:00
{
2019-08-16 13:49:52 -04:00
continue ;
}
// Skip Editor for all platforms except for current platform
if ( ProjectTarget . TargetRules . Type = = TargetType . Editor & & ( BuildHostPlatform . Current . Platform ! = Platform | | ( Configuration = = UnrealTargetConfiguration . Test | | Configuration = = UnrealTargetConfiguration . Shipping ) ) )
{
continue ;
2019-08-16 13:41:27 -04:00
}
2019-10-25 11:09:19 -04:00
2020-04-14 09:30:15 -04:00
DirectoryReference ConfigurationFolder = DirectoryReference . Combine ( ProjectRootFolder , Platform . ToString ( ) , Configuration . ToString ( ) ) ;
2019-08-16 13:41:27 -04:00
DirectoryReference TargetFolder =
2019-10-25 11:09:19 -04:00
DirectoryReference . Combine ( ConfigurationFolder , ProjectTarget . TargetRules . Type . ToString ( ) ) ;
2019-08-16 13:41:27 -04:00
string DefaultArchitecture = UEBuildPlatform
2020-04-14 09:30:15 -04:00
. GetBuildPlatform ( Platform )
2019-08-16 13:41:27 -04:00
. GetDefaultArchitecture ( ProjectTarget . UnrealProjectFilePath ) ;
TargetDescriptor TargetDesc = new TargetDescriptor ( ProjectTarget . UnrealProjectFilePath , ProjectTarget . Name ,
2020-04-14 09:30:15 -04:00
Platform , Configuration , DefaultArchitecture , Arguments ) ;
2020-02-22 17:30:20 -05:00
try
{
UEBuildTarget BuildTarget = UEBuildTarget . Create ( TargetDesc , false , false ) ;
FileReference OutputFile = FileReference . Combine ( TargetFolder , $"{ProjectName}.json" ) ;
2020-04-14 09:30:15 -04:00
FileToTarget . Add ( Tuple . Create ( OutputFile , BuildTarget ) ) ;
2020-02-22 17:30:20 -05:00
}
catch ( Exception Ex )
{
2020-04-14 09:30:15 -04:00
Log . TraceWarning ( "Exception while generating include data for Target:{0}, Platform: {1}, Configuration: {2}" , TargetDesc . Name , Platform . ToString ( ) , Configuration . ToString ( ) ) ;
Log . TraceWarning ( Ex . ToString ( ) ) ;
2020-02-22 17:30:20 -05:00
}
2019-08-16 13:41:27 -04:00
}
}
}
2020-04-14 09:30:15 -04:00
foreach ( Tuple < FileReference , UEBuildTarget > tuple in FileToTarget )
2019-10-25 11:09:19 -04:00
{
2020-10-22 19:19:16 -04:00
try
{
CurrentTarget = tuple . Item2 ;
CurrentTarget . PreBuildSetup ( ) ;
SerializeTarget ( tuple . Item1 , CurrentTarget , Minimize ) ;
}
catch ( Exception Ex )
{
Log . TraceWarning ( "Exception while generating include data for Target:{0}, Platform: {1}, Configuration: {2}" , tuple . Item2 . AppName , tuple . Item2 . Platform . ToString ( ) , tuple . Item2 . Configuration . ToString ( ) ) ;
Log . TraceWarning ( Ex . ToString ( ) ) ;
}
2019-10-25 11:09:19 -04:00
}
2019-08-16 13:41:27 -04:00
return true ;
}
2020-09-24 00:43:27 -04:00
private void SerializeTarget ( FileReference OutputFile , UEBuildTarget BuildTarget , JsonWriterStyle Minimize )
2019-10-25 11:09:19 -04:00
{
DirectoryReference . CreateDirectory ( OutputFile . Directory ) ;
2020-09-24 00:43:27 -04:00
using ( JsonWriter Writer = new JsonWriter ( OutputFile , Minimize ) )
2019-10-25 11:09:19 -04:00
{
ExportTarget ( BuildTarget , Writer ) ;
}
}
2019-08-16 13:41:27 -04:00
/// <summary>
/// Write a Target to a JSON writer. Is array is empty, don't write anything
/// </summary>
/// <param name="Target"></param>
/// <param name="Writer">Writer for the array data</param>
2020-09-24 00:43:27 -04:00
private void ExportTarget ( UEBuildTarget Target , JsonWriter Writer )
2019-08-16 13:41:27 -04:00
{
Writer . WriteObjectStart ( ) ;
Writer . WriteValue ( "Name" , Target . TargetName ) ;
Writer . WriteValue ( "Configuration" , Target . Configuration . ToString ( ) ) ;
Writer . WriteValue ( "Platform" , Target . Platform . ToString ( ) ) ;
2020-10-22 19:19:16 -04:00
Writer . WriteValue ( "TargetFile" , Target . TargetRulesFile . FullName ) ;
2019-08-16 13:41:27 -04:00
if ( Target . ProjectFile ! = null )
{
2020-10-22 19:19:16 -04:00
Writer . WriteValue ( "ProjectFile" , Target . ProjectFile . FullName ) ;
2019-08-16 13:41:27 -04:00
}
ExportEnvironmentToJson ( Target , Writer ) ;
if ( Target . Binaries . Any ( ) )
{
Writer . WriteArrayStart ( "Binaries" ) ;
foreach ( UEBuildBinary Binary in Target . Binaries )
{
Writer . WriteObjectStart ( ) ;
ExportBinary ( Binary , Writer ) ;
Writer . WriteObjectEnd ( ) ;
}
Writer . WriteArrayEnd ( ) ;
}
2020-04-14 09:30:15 -04:00
CppCompileEnvironment GlobalCompileEnvironment = Target . CreateCompileEnvironmentForProjectFiles ( ) ;
HashSet < string > ModuleNames = new HashSet < string > ( ) ;
2019-10-25 11:09:19 -04:00
Writer . WriteObjectStart ( "Modules" ) ;
foreach ( UEBuildBinary Binary in Target . Binaries )
2019-08-16 13:41:27 -04:00
{
2020-04-14 09:30:15 -04:00
CppCompileEnvironment BinaryCompileEnvironment = Binary . CreateBinaryCompileEnvironment ( GlobalCompileEnvironment ) ;
2019-10-25 11:09:19 -04:00
foreach ( UEBuildModule Module in Binary . Modules )
2019-08-16 13:41:27 -04:00
{
2020-04-14 09:30:15 -04:00
if ( ModuleNames . Add ( Module . Name ) )
2019-10-25 11:09:19 -04:00
{
Writer . WriteObjectStart ( Module . Name ) ;
ExportModule ( Module , Binary . OutputDir , Target . GetExecutableDir ( ) , Writer ) ;
2020-04-14 09:30:15 -04:00
UEBuildModuleCPP ModuleCpp = Module as UEBuildModuleCPP ;
if ( ModuleCpp ! = null )
{
CppCompileEnvironment ModuleCompileEnvironment = ModuleCpp . CreateCompileEnvironmentForIntellisense ( Target . Rules , BinaryCompileEnvironment ) ;
ExportModuleCpp ( ModuleCpp , ModuleCompileEnvironment , Writer ) ;
}
2019-10-25 11:09:19 -04:00
Writer . WriteObjectEnd ( ) ;
}
2019-08-16 13:41:27 -04:00
}
}
2019-10-25 11:09:19 -04:00
Writer . WriteObjectEnd ( ) ;
2019-08-16 13:41:27 -04:00
ExportPluginsFromTarget ( Target , Writer ) ;
Writer . WriteObjectEnd ( ) ;
}
2020-09-24 00:43:27 -04:00
private void ExportModuleCpp ( UEBuildModuleCPP ModuleCPP , CppCompileEnvironment ModuleCompileEnvironment , JsonWriter Writer )
2020-04-14 09:30:15 -04:00
{
2020-09-24 00:43:27 -04:00
Writer . WriteValue ( "GeneratedCodeDirectory" , ModuleCPP . GeneratedCodeDirectory ! = null ? ModuleCPP . GeneratedCodeDirectory . FullName : string . Empty ) ;
ToolchainInfo ModuleToolchainInfo = GenerateToolchainInfo ( ModuleCompileEnvironment ) ;
if ( ! ModuleToolchainInfo . Equals ( RootToolchainInfo ) )
{
Writer . WriteObjectStart ( "ToolchainInfo" ) ;
foreach ( Tuple < string , object > Field in ModuleToolchainInfo . GetDiff ( RootToolchainInfo ) )
{
WriteField ( ModuleCPP . Name , Writer , Field ) ;
}
Writer . WriteObjectEnd ( ) ;
}
2020-04-14 09:30:15 -04:00
if ( ModuleCompileEnvironment . PrecompiledHeaderIncludeFilename ! = null )
{
string CorrectFilePathPch ;
if ( ExtractWrappedIncludeFile ( ModuleCompileEnvironment . PrecompiledHeaderIncludeFilename , out CorrectFilePathPch ) )
Writer . WriteValue ( "SharedPCHFilePath" , CorrectFilePathPch ) ;
}
}
private static bool ExtractWrappedIncludeFile ( FileSystemReference FileRef , out string CorrectFilePathPch )
{
CorrectFilePathPch = "" ;
try
{
using ( StreamReader Reader = new StreamReader ( FileRef . FullName ) )
{
string Line = Reader . ReadLine ( ) ;
if ( Line ! = null )
{
CorrectFilePathPch = Line . Substring ( "// PCH for " . Length ) . Trim ( ) ;
return true ;
}
}
}
finally
{
Log . TraceVerbose ( "Couldn't extract path to PCH from {0}" , FileRef ) ;
}
return false ;
}
2019-08-16 13:41:27 -04:00
/// <summary>
/// Write a Module to a JSON writer. If array is empty, don't write anything
/// </summary>
2019-10-25 11:09:19 -04:00
/// <param name="BinaryOutputDir"></param>
2019-08-16 13:41:27 -04:00
/// <param name="TargetOutputDir"></param>
/// <param name="Writer">Writer for the array data</param>
/// <param name="Module"></param>
2019-10-25 11:09:19 -04:00
private static void ExportModule ( UEBuildModule Module , DirectoryReference BinaryOutputDir , DirectoryReference TargetOutputDir , JsonWriter Writer )
2019-08-16 13:41:27 -04:00
{
Writer . WriteValue ( "Name" , Module . Name ) ;
2020-09-24 00:43:27 -04:00
Writer . WriteValue ( "Directory" , Module . ModuleDirectory . FullName ) ;
Writer . WriteValue ( "Rules" , Module . RulesFile . FullName ) ;
2019-08-16 13:41:27 -04:00
Writer . WriteValue ( "PCHUsage" , Module . Rules . PCHUsage . ToString ( ) ) ;
2019-08-16 13:49:52 -04:00
2019-08-16 13:41:27 -04:00
if ( Module . Rules . PrivatePCHHeaderFile ! = null )
{
2020-10-22 19:19:16 -04:00
Writer . WriteValue ( "PrivatePCH" , FileReference . Combine ( Module . ModuleDirectory , Module . Rules . PrivatePCHHeaderFile ) . FullName ) ;
2019-08-16 13:41:27 -04:00
}
if ( Module . Rules . SharedPCHHeaderFile ! = null )
{
2020-10-22 19:19:16 -04:00
Writer . WriteValue ( "SharedPCH" , FileReference . Combine ( Module . ModuleDirectory , Module . Rules . SharedPCHHeaderFile ) . FullName ) ;
2019-08-16 13:41:27 -04:00
}
ExportJsonModuleArray ( Writer , "PublicDependencyModules" , Module . PublicDependencyModules ) ;
ExportJsonModuleArray ( Writer , "PublicIncludePathModules" , Module . PublicIncludePathModules ) ;
ExportJsonModuleArray ( Writer , "PrivateDependencyModules" , Module . PrivateDependencyModules ) ;
ExportJsonModuleArray ( Writer , "PrivateIncludePathModules" , Module . PrivateIncludePathModules ) ;
ExportJsonModuleArray ( Writer , "DynamicallyLoadedModules" , Module . DynamicallyLoadedModules ) ;
2020-09-24 00:43:27 -04:00
ExportJsonStringArray ( Writer , "PublicSystemIncludePaths" , Module . PublicSystemIncludePaths . Select ( x = > x . FullName ) ) ;
ExportJsonStringArray ( Writer , "PublicIncludePaths" , Module . PublicIncludePaths . Select ( x = > x . FullName ) ) ;
2019-08-16 13:41:27 -04:00
2020-09-24 00:43:27 -04:00
ExportJsonStringArray ( Writer , "LegacyPublicIncludePaths" , Module . LegacyPublicIncludePaths . Select ( x = > x . FullName ) ) ;
2019-08-16 13:41:27 -04:00
ExportJsonStringArray ( Writer , "PrivateIncludePaths" , Module . PrivateIncludePaths . Select ( x = > x . FullName ) ) ;
ExportJsonStringArray ( Writer , "PublicLibraryPaths" , Module . PublicSystemLibraryPaths . Select ( x = > x . FullName ) ) ;
2020-04-14 09:30:15 -04:00
ExportJsonStringArray ( Writer , "PublicAdditionalLibraries" , Module . PublicSystemLibraries . Concat ( Module . PublicLibraries . Select ( x = > x . FullName ) ) ) ;
2019-08-16 13:41:27 -04:00
ExportJsonStringArray ( Writer , "PublicFrameworks" , Module . PublicFrameworks ) ;
ExportJsonStringArray ( Writer , "PublicWeakFrameworks" , Module . PublicWeakFrameworks ) ;
ExportJsonStringArray ( Writer , "PublicDelayLoadDLLs" , Module . PublicDelayLoadDLLs ) ;
ExportJsonStringArray ( Writer , "PublicDefinitions" , Module . PublicDefinitions ) ;
ExportJsonStringArray ( Writer , "PrivateDefinitions" , Module . Rules . PrivateDefinitions ) ;
ExportJsonStringArray ( Writer , "ProjectDefinitions" , /* TODO: Add method ShouldAddProjectDefinitions */ ! Module . Rules . bTreatAsEngineModule ? Module . Rules . Target . ProjectDefinitions : new string [ 0 ] ) ;
ExportJsonStringArray ( Writer , "ApiDefinitions" , Module . GetEmptyApiMacros ( ) ) ;
Writer . WriteValue ( "ShouldAddLegacyPublicIncludePaths" , Module . Rules . bLegacyPublicIncludePaths ) ;
if ( Module . Rules . CircularlyReferencedDependentModules . Any ( ) )
{
Writer . WriteArrayStart ( "CircularlyReferencedModules" ) ;
foreach ( string ModuleName in Module . Rules . CircularlyReferencedDependentModules )
{
Writer . WriteValue ( ModuleName ) ;
}
Writer . WriteArrayEnd ( ) ;
}
if ( Module . Rules . RuntimeDependencies . Inner . Any ( ) )
{
2019-10-25 11:09:19 -04:00
// We don't use info from RuntimeDependencies for code analyzes (at the moment)
// So we're OK with skipping some values if they are not presented
2019-08-16 13:41:27 -04:00
Writer . WriteArrayStart ( "RuntimeDependencies" ) ;
foreach ( ModuleRules . RuntimeDependency RuntimeDependency in Module . Rules . RuntimeDependencies . Inner )
{
Writer . WriteObjectStart ( ) ;
2019-10-25 11:09:19 -04:00
try
{
Writer . WriteValue ( "Path" ,
Module . ExpandPathVariables ( RuntimeDependency . Path , BinaryOutputDir , TargetOutputDir ) ) ;
}
catch ( BuildException buildException )
{
Log . TraceVerbose ( "Value {0} for module {1} will not be stored. Reason: {2}" , "Path" , Module . Name , buildException ) ;
}
2019-08-16 13:41:27 -04:00
if ( RuntimeDependency . SourcePath ! = null )
{
2019-10-25 11:09:19 -04:00
try
{
Writer . WriteValue ( "SourcePath" ,
Module . ExpandPathVariables ( RuntimeDependency . SourcePath , BinaryOutputDir ,
TargetOutputDir ) ) ;
}
catch ( BuildException buildException )
{
Log . TraceVerbose ( "Value {0} for module {1} will not be stored. Reason: {2}" , "SourcePath" , Module . Name , buildException ) ;
}
2019-08-16 13:41:27 -04:00
}
Writer . WriteValue ( "Type" , RuntimeDependency . Type . ToString ( ) ) ;
Writer . WriteObjectEnd ( ) ;
}
Writer . WriteArrayEnd ( ) ;
}
}
/// <summary>
/// Write an array of Modules to a JSON writer. If array is empty, don't write anything
/// </summary>
/// <param name="Writer">Writer for the array data</param>
/// <param name="ArrayName">Name of the array property</param>
/// <param name="Modules">Sequence of Modules to write. May be null.</param>
private static void ExportJsonModuleArray ( JsonWriter Writer , string ArrayName , IEnumerable < UEBuildModule > Modules )
{
if ( Modules = = null | | ! Modules . Any ( ) ) return ;
Writer . WriteArrayStart ( ArrayName ) ;
foreach ( UEBuildModule Module in Modules )
{
Writer . WriteValue ( Module . Name ) ;
}
Writer . WriteArrayEnd ( ) ;
}
/// <summary>
/// Write an array of strings to a JSON writer. Ifl array is empty, don't write anything
/// </summary>
/// <param name="Writer">Writer for the array data</param>
/// <param name="ArrayName">Name of the array property</param>
/// <param name="Strings">Sequence of strings to write. May be null.</param>
static void ExportJsonStringArray ( JsonWriter Writer , string ArrayName , IEnumerable < string > Strings )
{
if ( Strings = = null | | ! Strings . Any ( ) ) return ;
Writer . WriteArrayStart ( ArrayName ) ;
foreach ( string String in Strings )
{
Writer . WriteValue ( String ) ;
}
Writer . WriteArrayEnd ( ) ;
}
/// <summary>
/// Write uplugin content to a JSON writer
/// </summary>
/// <param name="Plugin">Uplugin description</param>
/// <param name="Writer">JSON writer</param>
private static void ExportPlugin ( UEBuildPlugin Plugin , JsonWriter Writer )
{
Writer . WriteObjectStart ( Plugin . Name ) ;
2020-09-24 00:43:27 -04:00
Writer . WriteValue ( "File" , Plugin . File . FullName ) ;
2019-08-16 13:41:27 -04:00
Writer . WriteValue ( "Type" , Plugin . Type . ToString ( ) ) ;
if ( Plugin . Dependencies . Any ( ) )
{
Writer . WriteStringArrayField ( "Dependencies" , Plugin . Dependencies . Select ( it = > it . Name ) ) ;
}
if ( Plugin . Modules . Any ( ) )
{
Writer . WriteStringArrayField ( "Modules" , Plugin . Modules . Select ( it = > it . Name ) ) ;
}
Writer . WriteObjectEnd ( ) ;
}
/// <summary>
/// Setup plugins for Target and write plugins to JSON writer. Don't write anything if there are no plugins
/// </summary>
/// <param name="Target"></param>
/// <param name="Writer"></param>
private static void ExportPluginsFromTarget ( UEBuildTarget Target , JsonWriter Writer )
{
Target . SetupPlugins ( ) ;
if ( ! Target . BuildPlugins . Any ( ) ) return ;
Writer . WriteObjectStart ( "Plugins" ) ;
foreach ( UEBuildPlugin plugin in Target . BuildPlugins )
{
ExportPlugin ( plugin , Writer ) ;
}
Writer . WriteObjectEnd ( ) ;
}
/// <summary>
/// Write information about this binary to a JSON file
/// </summary>
/// <param name="Binary"></param>
/// <param name="Writer">Writer for this binary's data</param>
private static void ExportBinary ( UEBuildBinary Binary , JsonWriter Writer )
{
2020-09-24 00:43:27 -04:00
Writer . WriteValue ( "File" , Binary . OutputFilePath . FullName ) ;
2019-08-16 13:41:27 -04:00
Writer . WriteValue ( "Type" , Binary . Type . ToString ( ) ) ;
Writer . WriteArrayStart ( "Modules" ) ;
foreach ( UEBuildModule Module in Binary . Modules )
{
Writer . WriteValue ( Module . Name ) ;
}
Writer . WriteArrayEnd ( ) ;
}
/// <summary>
/// Write C++ toolchain information to JSON writer
/// </summary>
/// <param name="Target"></param>
/// <param name="Writer"></param>
2020-09-24 00:43:27 -04:00
private void ExportEnvironmentToJson ( UEBuildTarget Target , JsonWriter Writer )
2019-08-16 13:41:27 -04:00
{
CppCompileEnvironment GlobalCompileEnvironment = Target . CreateCompileEnvironmentForProjectFiles ( ) ;
2020-09-24 00:43:27 -04:00
RootToolchainInfo = GenerateToolchainInfo ( GlobalCompileEnvironment ) ;
Writer . WriteObjectStart ( "ToolchainInfo" ) ;
foreach ( Tuple < string , object > Field in RootToolchainInfo . GetFields ( ) )
{
WriteField ( Target . TargetName , Writer , Field ) ;
}
Writer . WriteObjectEnd ( ) ;
2019-08-16 13:41:27 -04:00
Writer . WriteArrayStart ( "EnvironmentIncludePaths" ) ;
foreach ( DirectoryReference Path in GlobalCompileEnvironment . UserIncludePaths )
{
2020-09-24 00:43:27 -04:00
Writer . WriteValue ( Path . FullName ) ;
2019-08-16 13:41:27 -04:00
}
foreach ( DirectoryReference Path in GlobalCompileEnvironment . SystemIncludePaths )
{
2020-09-24 00:43:27 -04:00
Writer . WriteValue ( Path . FullName ) ;
2019-08-16 13:41:27 -04:00
}
if ( UEBuildPlatform . IsPlatformInGroup ( Target . Platform , UnrealPlatformGroup . Windows ) )
{
foreach ( DirectoryReference Path in Target . Rules . WindowsPlatform . Environment . IncludePaths )
{
2020-10-22 19:19:16 -04:00
Writer . WriteValue ( Path . FullName ) ;
2019-08-16 13:41:27 -04:00
}
}
2020-10-22 19:19:16 -04:00
else if ( UEBuildPlatform . IsPlatformInGroup ( Target . Platform , UnrealPlatformGroup . Apple ) & &
UEBuildPlatform . IsPlatformInGroup ( BuildHostPlatform . Current . Platform , UnrealPlatformGroup . Apple ) )
{
// Only generate Apple system include paths when host platform is Apple OS
// TODO: Fix case when working with MacOS on Windows host platform
foreach ( string Path in AppleHelper . GetAppleSystemIncludePaths ( GlobalCompileEnvironment . Architecture , Target . Platform ) )
{
Writer . WriteValue ( Path ) ;
}
}
// TODO: get corresponding includes for Linux
2019-08-16 13:41:27 -04:00
Writer . WriteArrayEnd ( ) ;
Writer . WriteArrayStart ( "EnvironmentDefinitions" ) ;
foreach ( string Definition in GlobalCompileEnvironment . Definitions )
{
Writer . WriteValue ( Definition ) ;
}
Writer . WriteArrayEnd ( ) ;
}
2020-09-24 00:43:27 -04:00
private static void WriteField ( string ModuleOrTargetName , JsonWriter Writer , Tuple < string , object > Field )
{
if ( Field . Item2 = = null ) return ;
string Name = Field . Item1 ;
if ( Field . Item2 is bool )
{
Writer . WriteValue ( Name , ( bool ) Field . Item2 ) ;
}
else if ( Field . Item2 is string )
{
string FieldValue = ( string ) Field . Item2 ;
if ( FieldValue ! = "" )
Writer . WriteValue ( Name , ( string ) Field . Item2 ) ;
}
else if ( Field . Item2 is int )
{
Writer . WriteValue ( Name , ( int ) Field . Item2 ) ;
}
else if ( Field . Item2 is double )
{
Writer . WriteValue ( Name , ( double ) Field . Item2 ) ;
}
else if ( Field . Item2 is Enum )
{
Writer . WriteValue ( Name , Field . Item2 . ToString ( ) ) ;
}
else if ( Field . Item2 is IEnumerable < string > )
{
IEnumerable < string > FieldValue = ( IEnumerable < string > ) Field . Item2 ;
if ( FieldValue . Any ( ) )
Writer . WriteStringArrayField ( Name , FieldValue ) ;
}
else
{
Log . TraceWarning ( "Dumping incompatible ToolchainInfo field: {0} with type: {1} for: {2}" ,
Name , Field . Item2 , ModuleOrTargetName ) ;
}
}
private ToolchainInfo GenerateToolchainInfo ( CppCompileEnvironment CompileEnvironment )
{
ToolchainInfo ToolchainInfo = new ToolchainInfo
{
CppStandard = CompileEnvironment . CppStandard ,
Configuration = CompileEnvironment . Configuration . ToString ( ) ,
bEnableExceptions = CompileEnvironment . bEnableExceptions ,
bOptimizeCode = CompileEnvironment . bOptimizeCode ,
bUseInlining = CompileEnvironment . bUseInlining ,
bUseUnity = CompileEnvironment . bUseUnity ,
bCreateDebugInfo = CompileEnvironment . bCreateDebugInfo ,
bIsBuildingLibrary = CompileEnvironment . bIsBuildingLibrary ,
bUseAVX = CompileEnvironment . bUseAVX ,
bIsBuildingDLL = CompileEnvironment . bIsBuildingDLL ,
bUseDebugCRT = CompileEnvironment . bUseDebugCRT ,
bUseRTTI = CompileEnvironment . bUseRTTI ,
bUseStaticCRT = CompileEnvironment . bUseStaticCRT ,
PrecompiledHeaderAction = CompileEnvironment . PrecompiledHeaderAction . ToString ( ) ,
PrecompiledHeaderFile = CompileEnvironment . PrecompiledHeaderFile ? . ToString ( ) ,
ForceIncludeFiles = CompileEnvironment . ForceIncludeFiles . Select ( Item = > Item . ToString ( ) ) . ToList ( )
} ;
if ( CurrentTarget . Platform . IsInGroup ( UnrealPlatformGroup . Windows ) )
{
ToolchainInfo . Architecture = WindowsExports . GetArchitectureSubpath ( CurrentTarget . Rules . WindowsPlatform . Architecture ) ;
WindowsCompiler WindowsPlatformCompiler = CurrentTarget . Rules . WindowsPlatform . Compiler ;
ToolchainInfo . bStrictConformanceMode = WindowsPlatformCompiler > = WindowsCompiler . VisualStudio2017 & & CurrentTarget . Rules . WindowsPlatform . bStrictConformanceMode ;
ToolchainInfo . Compiler = WindowsPlatformCompiler . ToString ( ) ;
}
else
{
string PlatformName = $"{CurrentTarget.Platform}Platform" ;
object Value = typeof ( ReadOnlyTargetRules ) . GetProperty ( PlatformName ) ? . GetValue ( CurrentTarget . Rules ) ;
object CompilerField = Value ? . GetType ( ) . GetProperty ( "Compiler" ) ? . GetValue ( Value ) ;
if ( CompilerField ! = null )
ToolchainInfo . Compiler = CompilerField . ToString ( ) ;
}
return ToolchainInfo ;
}
2020-10-22 19:19:16 -04:00
private class XcrunRunner
{
private readonly Dictionary < string , IList < string > > CachedIncludePaths =
new Dictionary < string , IList < string > > ( ) ;
private string CurrentlyProcessedSDK = string . Empty ;
private Process XcrunProcess ;
private bool IsReadingIncludesSection ;
public IList < string > GetAppleSystemIncludePaths ( string Architecture , UnrealTargetPlatform Platform )
{
if ( ! UEBuildPlatform . IsPlatformInGroup ( Platform , UnrealPlatformGroup . Apple ) )
{
throw new InvalidOperationException ( "xcrun can be run only for Apple's platforms" ) ;
}
string SDKPath = GetSDKPath ( Architecture , Platform ) ;
if ( ! CachedIncludePaths . ContainsKey ( SDKPath ) )
{
CalculateSystemIncludePaths ( SDKPath ) ;
}
return CachedIncludePaths [ SDKPath ] ;
}
private void CalculateSystemIncludePaths ( string SDKPath )
{
if ( CurrentlyProcessedSDK ! = string . Empty )
{
throw new InvalidOperationException ( "Cannot calculate include paths for several platforms at once" ) ;
}
CurrentlyProcessedSDK = SDKPath ;
CachedIncludePaths [ SDKPath ] = new List < string > ( ) ;
using ( XcrunProcess = new Process ( ) )
{
string AppName = "xcrun" ;
string Arguments = "clang++ -Wp,-v -x c++ - -fsyntax-only" +
( string . IsNullOrEmpty ( SDKPath ) ? string . Empty : ( " -isysroot " + SDKPath ) ) ;
XcrunProcess . StartInfo . FileName = AppName ;
XcrunProcess . StartInfo . Arguments = Arguments ;
XcrunProcess . StartInfo . UseShellExecute = false ;
XcrunProcess . StartInfo . CreateNoWindow = true ;
// For some weird reason output of this command is written to error channel so we're redirecting both channels
XcrunProcess . StartInfo . RedirectStandardOutput = true ;
XcrunProcess . StartInfo . RedirectStandardError = true ;
XcrunProcess . OutputDataReceived + = OnOutputDataReceived ;
XcrunProcess . ErrorDataReceived + = OnOutputDataReceived ;
XcrunProcess . Start ( ) ;
XcrunProcess . BeginOutputReadLine ( ) ;
XcrunProcess . BeginErrorReadLine ( ) ;
// xcrun is not finished on it's own. It should be killed by OnOutputDataReceived when reading is finished. But we'll add timeout as a safeguard
XcrunProcess . WaitForExit ( 3000 ) ;
}
XcrunProcess = null ;
IsReadingIncludesSection = false ;
CurrentlyProcessedSDK = string . Empty ;
}
private void OnOutputDataReceived ( object Sender , DataReceivedEventArgs Args )
{
if ( Args . Data ! = null )
{
if ( IsReadingIncludesSection )
{
if ( Args . Data . StartsWith ( "End of search" ) )
{
IsReadingIncludesSection = false ;
XcrunProcess . Kill ( ) ;
}
else
{
if ( ! Args . Data . EndsWith ( "(framework directory)" ) )
{
CachedIncludePaths [ CurrentlyProcessedSDK ] . Add ( Args . Data . Trim ( ' ' , '"' ) ) ;
}
}
}
if ( Args . Data . StartsWith ( "#include <...>" ) )
{
IsReadingIncludesSection = true ;
}
}
}
private string GetSDKPath ( string Architecture , UnrealTargetPlatform Platform )
{
if ( Platform = = UnrealTargetPlatform . Mac )
{
return MacToolChain . SDKPath ;
}
if ( Platform = = UnrealTargetPlatform . IOS )
{
return new IOSToolChainSettings ( ) . GetSDKPath ( Architecture ) ;
}
if ( Platform = = UnrealTargetPlatform . TVOS )
{
return new TVOSToolChainSettings ( ) . GetSDKPath ( Architecture ) ;
}
throw new NotImplementedException ( "Path to SDK has to be specified for each Apple's platform" ) ;
}
}
2019-08-16 13:41:27 -04:00
}
}