2023-02-17 22:39:26 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
using EpicGames.Core ;
using Microsoft.Extensions.Logging ;
using System ;
using System.Linq ;
using System.Text.Json ;
using System.Text.Json.Serialization ;
using System.Collections.Generic ;
using OpenTracing.Util ;
using UnrealBuildTool ;
using UnrealBuildBase ;
namespace UnrealBuildTool
{
// TODO: Namespacing?
enum QueryType
{
Capabilities ,
AvailableTargets ,
TargetDetails ,
}
internal class TargetIntellisenseInfo
{
internal class CompileSettings
{
public List < string > IncludePaths { get ; set ; } = new ( ) ;
public List < string > Defines { get ; set ; } = new ( ) ;
public string? Standard { get ; set ; }
public List < string > ForcedIncludes { get ; set ; } = new ( ) ;
public string? CompilerPath { get ; set ; }
public List < string > CompilerArgs { get ; set ; } = new ( ) ;
public string? WindowsSdkVersion { get ; set ; }
}
public Dictionary < UEBuildModule , CompileSettings > ModuleToCompileSettings = new ( ) ;
public Dictionary < DirectoryReference , UEBuildModule > DirToModule = new ( ) ;
public UEBuildModule ? FindModuleForFile ( FileReference File )
{
DirectoryReference ? Dir = File . Directory ;
while ( Dir ! = null )
{
if ( DirToModule . TryGetValue ( Dir , out UEBuildModule ? Module ) )
{
return Module ;
}
Dir = Dir . ParentDirectory ;
}
return null ;
}
public UEBuildModule ? FindModuleForDirectory ( DirectoryReference Directory )
{
DirectoryReference ? Dir = Directory ;
while ( Dir ! = null )
{
if ( DirToModule . TryGetValue ( Dir , out UEBuildModule ? Module ) )
{
return Module ;
}
Dir = Dir . ParentDirectory ;
}
return null ;
}
}
internal class TargetConfigs
{
public string ProjectPath { get ; set ; } = "" ;
public List < string > Platforms { get ; set ; } = new ( ) ;
public List < string > Configurations { get ; set ; } = new ( ) ;
}
[ToolMode("Query", ToolModeOptions.BuildPlatforms | ToolModeOptions.XmlConfig | ToolModeOptions.UseStartupTraceListener)]
class QueryMode : ToolMode
{
[CommandLine("-LogDirectory=")]
public DirectoryReference ? LogDirectory = null ;
[CommandLine("-Query=")]
public QueryType ? Query = null ;
[CommandLine("-IncludeEngineSource=")]
public bool bIncludeEngineSource = true ;
[CommandLine("-Target=")]
public string? TargetName ;
[CommandLine("-Configuration=")]
public string? TargetConfiguration ;
[CommandLine("-Platform=")]
public string? TargetPlatform ;
[CommandLine("-Indented")]
public bool bIndented ;
private BuildConfiguration BuildConfiguration = new ( ) ;
public override int Execute ( CommandLineArguments Arguments , ILogger Logger )
{
Arguments . ApplyTo ( this ) ;
if ( LogDirectory = = null )
{
LogDirectory = DirectoryReference . Combine ( Unreal . EngineProgramSavedDirectory , "UnrealBuildTool" ) ;
}
DirectoryReference . CreateDirectory ( LogDirectory ) ;
FileReference LogFile = FileReference . Combine ( LogDirectory , "Log_Query.txt" ) ; // TODO: More history? Pass a log file path on the cmd line from extension and prune from there?
Log . AddFileWriter ( "DefaultLogTraceListener" , LogFile ) ;
XmlConfig . ApplyTo ( BuildConfiguration ) ;
Arguments . ApplyTo ( BuildConfiguration ) ;
// TODO: Document this hack
ProjectFileGenerator . bGenerateProjectFiles = true ;
if ( Query = = null )
{
return 0 ;
}
var ResponseOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy . CamelCase ,
WriteIndented = bIndented ,
} ;
switch ( Query )
{
case QueryType . Capabilities :
Logger . LogInformation ( "QueryCapabilities" ) ;
return QueryCapabilities ( Arguments , Logger , ResponseOptions ) ;
case QueryType . AvailableTargets :
Logger . LogInformation ( "QueryAvailableTargets" ) ;
return QueryAvailableTargets ( Arguments , Logger , ResponseOptions ) ;
case QueryType . TargetDetails :
Logger . LogInformation ( "QueryTargetDetails" ) ;
return QueryTargetDetails ( Arguments , Logger , ResponseOptions ) ;
}
return 0 ;
}
private int QueryCapabilities ( CommandLineArguments Arguments , ILogger Logger , JsonSerializerOptions JsonOptions )
{
var Reply = new
{
Queries = new List < string > { QueryType . Capabilities . ToString ( ) , QueryType . AvailableTargets . ToString ( ) , QueryType . TargetDetails . ToString ( ) }
} ;
Console . WriteLine ( JsonSerializer . Serialize ( Reply , JsonOptions ) ) ;
return 0 ;
}
private int QueryAvailableTargets ( CommandLineArguments Arguments , ILogger Logger , JsonSerializerOptions JsonOptions )
{
try
{
GenerateProjectFilesMode . TryParseProjectFileArgument ( Arguments , Logger , out FileReference ? ProjectFileArg ) ;
List < UnrealTargetPlatform > Platforms = new ( ) ;
foreach ( UnrealTargetPlatform Platform in UnrealTargetPlatform . GetValidPlatforms ( ) )
{
2023-03-06 20:34:17 -05:00
// If there is a build platform present, add it to the SupportedPlatforms list
UEBuildPlatform ? BuildPlatform ;
if ( UEBuildPlatform . TryGetBuildPlatform ( Platform , out BuildPlatform ) )
2023-02-17 22:39:26 -05:00
{
2023-03-06 20:34:17 -05:00
if ( InstalledPlatformInfo . IsValidPlatform ( Platform , EProjectType . Code ) )
2023-02-17 22:39:26 -05:00
{
2023-03-06 20:34:17 -05:00
Platforms . Add ( Platform ) ;
2023-02-17 22:39:26 -05:00
}
}
}
List < UnrealTargetConfiguration > AllowedTargetConfigurations = new List < UnrealTargetConfiguration > ( ) ;
AllowedTargetConfigurations = Enum . GetValues ( typeof ( UnrealTargetConfiguration ) ) . Cast < UnrealTargetConfiguration > ( ) . ToList ( ) ;
List < string > Configurations = new ( ) ;
foreach ( UnrealTargetConfiguration CurConfiguration in AllowedTargetConfigurations )
{
if ( CurConfiguration ! = UnrealTargetConfiguration . Unknown )
{
if ( InstalledPlatformInfo . IsValidConfiguration ( CurConfiguration , EProjectType . Code ) )
{
Configurations . Add ( CurConfiguration . ToString ( ) ) ;
}
}
}
List < FileReference > Projects = ProjectFileArg ! = null ? new List < FileReference > ( new [ ] { ProjectFileArg } ) : NativeProjects . EnumerateProjectFiles ( Logger ) . ToList ( ) ;
List < FileReference > AllTargetFiles = ProjectFileGenerator . DiscoverTargets ( Projects , Logger , null , Platforms , bIncludeEngineSource : bIncludeEngineSource , bIncludeTempTargets : false ) ;
// TODO: Check valid configurations/platforms per target
Dictionary < string , TargetConfigs > Targets = new ( ) ;
string? DefaultTarget = null ;
foreach ( FileReference TargetFilePath in AllTargetFiles )
{
string TargetName = TargetFilePath . GetFileNameWithoutAnyExtensions ( ) ;
FileReference ? ProjectPath = Projects . FirstOrDefault ( p = > TargetFilePath . IsUnderDirectory ( p . Directory ) ) ;
Targets . Add ( TargetName , new TargetConfigs ( ) { ProjectPath = ProjectPath ? . ToString ( ) ? ? "" , Configurations = Configurations , Platforms = Platforms . Select ( x = > x . ToString ( ) ) . ToList ( ) } ) ;
if ( DefaultTarget = = null | | TargetName = = "UnrealEditor" )
{
DefaultTarget = TargetName ;
}
}
var Reply = new
{
Targets = Targets ,
DefaultTarget = DefaultTarget ,
DefaultPlatform = Platforms [ 0 ] . ToString ( ) ,
DefaultConfiguration = UnrealTargetConfiguration . Development . ToString ( ) ,
} ;
Console . WriteLine ( JsonSerializer . Serialize ( Reply , JsonOptions ) ) ;
return 0 ;
}
catch ( Exception e )
{
Logger . LogError ( "Failed to query available targets: {0}" , e . Message ) ;
return 1 ;
}
}
private int QueryTargetDetails ( CommandLineArguments Arguments , ILogger Logger , JsonSerializerOptions JsonOptions )
{
if ( TargetName = = null | | TargetConfiguration = = null | | TargetPlatform = = null )
{
return 1 ;
}
GenerateProjectFilesMode . TryParseProjectFileArgument ( Arguments , Logger , out FileReference ? ProjectFileArg ) ;
var RawArgs = new List < string > { TargetName , TargetConfiguration , TargetPlatform } ;
if ( ProjectFileArg ! = null )
{
RawArgs . Add ( ProjectFileArg . ToString ( ) ) ;
}
CommandLineArguments Args = new CommandLineArguments ( RawArgs . ToArray ( ) ) ;
List < TargetDescriptor > TargetDescriptors = new ( ) ;
TargetDescriptor . ParseSingleCommandLine ( Args , false , false , false , TargetDescriptors , Logger ) ;
if ( TargetDescriptors . Count = = 0 )
{
// TOOD: Error
return 1 ;
}
HashSet < string > BrowseConfigurationFolders = new HashSet < string > ( ) ;
try
{
UEBuildTarget CurrentTarget ;
using ( GlobalTracer . Instance . BuildSpan ( "UEBuildTarget.Create()" ) . StartActive ( ) )
{
bool bUsePrecompiled = false ;
CurrentTarget = UEBuildTarget . Create ( TargetDescriptors [ 0 ] , false , false , bUsePrecompiled , Logger ) ;
}
var CurrentTargetIntellisenseInfo = new TargetIntellisenseInfo ( ) ;
var CurrentBrowseConfiguration = new GetBrowseConfigurationResponse { Success = true } ;
// Partially duplicated from UEBuildTarget.Build because we just want to get C++ compile actions without running UHT
// or generating link actions / full dependency graph
CppConfiguration CppConfiguration = UEBuildTarget . GetCppConfiguration ( CurrentTarget . Configuration ) ;
SourceFileMetadataCache MetadataCache = SourceFileMetadataCache . CreateHierarchy ( null , Logger ) ;
2023-02-17 22:39:31 -05:00
CppCompileEnvironment GlobalCompileEnvironment = new CppCompileEnvironment ( CurrentTarget . Platform , CppConfiguration , CurrentTarget . Architectures , MetadataCache ) ;
LinkEnvironment GlobalLinkEnvironment = new LinkEnvironment ( GlobalCompileEnvironment . Platform , GlobalCompileEnvironment . Configuration , GlobalCompileEnvironment . Architectures ) ;
2023-02-17 22:39:26 -05:00
UEToolChain TargetToolChain = CurrentTarget . CreateToolchain ( CurrentTarget . Platform ) ;
TargetToolChain . SetEnvironmentVariables ( ) ;
CurrentTarget . SetupGlobalEnvironment ( TargetToolChain , GlobalCompileEnvironment , GlobalLinkEnvironment ) ;
// TODO: For installed builds, filter out all the binaries that aren't in mods
foreach ( UEBuildBinary Binary in CurrentTarget . Binaries )
{
HashSet < UEBuildModule > LinkEnvironmentVisitedModules = new HashSet < UEBuildModule > ( ) ;
CppCompileEnvironment BinaryCompileEnvironment = Binary . CreateBinaryCompileEnvironment ( GlobalCompileEnvironment ) ;
CurrentBrowseConfiguration . Standard = BinaryCompileEnvironment . CppStandard . ToString ( ) ;
CurrentBrowseConfiguration . WindowsSdkVersion = CurrentTarget . Rules . WindowsPlatform . WindowsSdkVersion ;
foreach ( UEBuildModuleCPP Module in Binary . Modules . OfType < UEBuildModuleCPP > ( ) )
{
if ( Module . Binary ! = null & & Module . Binary ! = Binary )
{
continue ;
}
CppCompileEnvironment ModuleCompileEnvironment = Module . CreateModuleCompileEnvironment ( CurrentTarget . Rules , BinaryCompileEnvironment , Logger ) ;
foreach ( DirectoryReference Dir in Module . ModuleDirectories )
{
BrowseConfigurationFolders . Add ( Dir . ToString ( ) ) ;
CurrentTargetIntellisenseInfo . DirToModule . TryAdd ( Dir , Module ) ;
}
if ( Module . GeneratedCodeDirectory ! = null )
{
CurrentTargetIntellisenseInfo . DirToModule . TryAdd ( Module . GeneratedCodeDirectory , Module ) ;
}
foreach ( DirectoryReference Dir in ModuleCompileEnvironment . SystemIncludePaths )
{
BrowseConfigurationFolders . Add ( Dir . ToString ( ) ) ;
}
foreach ( DirectoryReference Dir in ModuleCompileEnvironment . UserIncludePaths )
{
BrowseConfigurationFolders . Add ( Dir . ToString ( ) ) ;
}
var Settings = new TargetIntellisenseInfo . CompileSettings ( ) ;
if ( OperatingSystem . IsWindows ( ) )
{
if ( CurrentTarget . Platform = = UnrealTargetPlatform . Win64 )
{
// TODO: Correct compiler
Settings . IncludePaths . AddRange ( VCToolChain . GetVCIncludePaths ( UnrealTargetPlatform . Win64 , WindowsCompiler . VisualStudio2022 , null , Logger ) . Split ( ";" ) ) ;
}
}
Settings . IncludePaths . AddRange ( ModuleCompileEnvironment . SystemIncludePaths . Select ( x = > x . ToString ( ) ) ) ;
Settings . IncludePaths . AddRange ( ModuleCompileEnvironment . UserIncludePaths . Select ( x = > x . ToString ( ) ) ) ;
Settings . Defines = ModuleCompileEnvironment . Definitions ;
Settings . Standard = ModuleCompileEnvironment . CppStandard . ToString ( ) ;
Settings . ForcedIncludes = ModuleCompileEnvironment . ForceIncludeFiles . Select ( x = > x . ToString ( ) ) . ToList ( ) ;
Settings . CompilerPath = TargetToolChain . GetCppCompilerPath ( ) ? . ToString ( ) ;
Settings . WindowsSdkVersion = CurrentTarget . Rules . WindowsPlatform . WindowsSdkVersion ;
CurrentTargetIntellisenseInfo . ModuleToCompileSettings . Add ( Module , Settings ) ;
}
}
CurrentBrowseConfiguration . Paths = BrowseConfigurationFolders . ToList ( ) ;
var Result = new
{
DirToModule = CurrentTargetIntellisenseInfo . DirToModule . ToDictionary ( x = > x . Key . ToString ( ) , x = > x . Value . Name ) ,
ModuleToCompileSettings = CurrentTargetIntellisenseInfo . ModuleToCompileSettings . ToDictionary ( x = > x . Key . Name , x = > x . Value ) ,
} ;
Console . WriteLine ( JsonSerializer . Serialize ( Result , JsonOptions ) ) ;
return 0 ;
}
catch ( Exception e )
{
Logger . LogError ( "Caught exception setting up target: {0}" , e ) ;
return 1 ;
}
}
}
}