2022-06-13 20:12:07 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
using System ;
2023-08-17 14:18:48 -04:00
using System.Collections.Concurrent ;
2022-06-13 20:12:07 -04:00
using System.Collections.Generic ;
using System.Diagnostics ;
2023-05-30 18:38:07 -04:00
using System.IO ;
2022-06-13 20:12:07 -04:00
using System.Linq ;
2023-05-30 18:38:07 -04:00
using System.Text ;
using EpicGames.Core ;
2022-06-13 20:12:07 -04:00
using Microsoft.Extensions.Logging ;
2023-05-30 18:38:07 -04:00
using UnrealBuildBase ;
2022-06-13 20:12:07 -04:00
namespace UnrealBuildTool
{
class VSCodeProject : ProjectFile
{
public VSCodeProject ( FileReference InitFilePath , DirectoryReference BaseDir )
: base ( InitFilePath , BaseDir )
{
}
public override bool WriteProjectFile ( List < UnrealTargetPlatform > InPlatforms , List < UnrealTargetConfiguration > InConfigurations , PlatformProjectGeneratorCollection PlatformProjectGenerators , ILogger Logger )
{
return true ;
}
}
class VSCodeProjectFileGenerator : ProjectFileGenerator
{
private UnrealTargetPlatform HostPlatform = BuildHostPlatform . Current . Platform ;
private bool bForeignProject ;
private DirectoryReference ProjectRoot ;
2023-09-15 14:42:22 -04:00
private string FrameworkExecutableExtension = OperatingSystem . IsWindows ( ) ? ".exe" : "" ;
2022-06-13 20:12:07 -04:00
private string FrameworkLibraryExtension = ".dll" ;
2023-08-17 14:18:48 -04:00
private readonly ConcurrentBag < BuildTarget > BuildTargets = new ( ) ;
2022-06-13 20:12:07 -04:00
/// <summary>
/// Includes all files in the generated workspace.
/// </summary>
[XmlConfigFile(Name = "IncludeAllFiles")]
private bool IncludeAllFiles = false ;
/// <summary>
/// Whether VS Code project generation should include debug configurations to allow attaching to already running processes
/// </summary>
[XmlConfigFile(Name = "AddDebugAttachConfig")]
private bool bAddDebugAttachConfig = false ;
/// <summary>
/// Whether VS Code project generation should include debug configurations to allow core dump debugging
/// </summary>
[XmlConfigFile(Name = "AddDebugCoreConfig")]
private bool bAddDebugCoreConfig = false ;
2022-10-28 12:59:39 -04:00
/// <summary>
/// Do not create compile commands json files with compiler arguments for each file; works better with VS Code extension using
/// UBT server mode.
/// </summary>
[XmlConfigFile(Name = "NoCompileCommands")]
[CommandLine("-NoCompileCommands")]
private bool bNoCompileCommands = false ;
2023-02-17 22:39:26 -05:00
/// <summary>
/// Create a workspace file for use with VS Code extension that communicates directly with UBT.
/// </summary>
[XmlConfigFile(Name = "UseVSCodeExtension")]
[CommandLine("-UseVSCodeExtension")]
private bool bUseVSCodeExtension = false ;
2022-06-13 20:12:07 -04:00
private enum EPathType
{
Absolute ,
Relative ,
}
private enum EQuoteType
{
2023-05-30 18:38:07 -04:00
Single , // can be ignored on platforms that don't need it (windows atm)
2022-06-13 20:12:07 -04:00
Double ,
}
private string CommonMakePathString ( FileSystemReference InRef , EPathType InPathType , DirectoryReference ? InRelativeRoot )
{
if ( InRelativeRoot = = null )
{
InRelativeRoot = ProjectRoot ;
}
string Processed = InRef . ToString ( ) ;
2023-05-30 18:38:07 -04:00
2022-06-13 20:12:07 -04:00
switch ( InPathType )
{
case EPathType . Relative :
{
2023-05-30 18:38:07 -04:00
if ( InRef . IsUnderDirectory ( InRelativeRoot ) )
{
Processed = InRef . MakeRelativeTo ( InRelativeRoot ) . ToString ( ) ;
}
break ;
2022-06-13 20:12:07 -04:00
}
default :
2023-05-30 18:38:07 -04:00
{
break ;
}
2022-06-13 20:12:07 -04:00
}
if ( HostPlatform = = UnrealTargetPlatform . Win64 )
{
Processed = Processed . Replace ( "/" , "\\" ) ;
}
else
{
Processed = Processed . Replace ( '\\' , '/' ) ;
}
return Processed ;
}
private string MakeQuotedPathString ( FileSystemReference InRef , EPathType InPathType , DirectoryReference ? InRelativeRoot = null , EQuoteType InQuoteType = EQuoteType . Double )
{
string Processed = CommonMakePathString ( InRef , InPathType , InRelativeRoot ) ;
2023-05-31 13:37:21 -04:00
if ( Processed . Contains ( ' ' ) )
2022-06-13 20:12:07 -04:00
{
if ( HostPlatform = = UnrealTargetPlatform . Win64 & & InQuoteType = = EQuoteType . Double )
{
Processed = "\"" + Processed + "\"" ;
}
else
{
Processed = "'" + Processed + "'" ;
}
2023-05-30 18:38:07 -04:00
}
2022-06-13 20:12:07 -04:00
return Processed ;
}
private string MakeUnquotedPathString ( FileSystemReference InRef , EPathType InPathType , DirectoryReference ? InRelativeRoot = null )
{
return CommonMakePathString ( InRef , InPathType , InRelativeRoot ) ;
}
private string MakePathString ( FileSystemReference InRef , bool bInAbsolute = false , bool bForceSkipQuotes = false )
{
if ( bForceSkipQuotes )
{
return MakeUnquotedPathString ( InRef , bInAbsolute ? EPathType . Absolute : EPathType . Relative , ProjectRoot ) ;
}
else
{
return MakeQuotedPathString ( InRef , bInAbsolute ? EPathType . Absolute : EPathType . Relative , ProjectRoot ) ;
}
}
public VSCodeProjectFileGenerator ( FileReference ? InOnlyGameProject )
: base ( InOnlyGameProject )
{
ProjectRoot = Unreal . RootDirectory ;
}
class JsonFile
{
public JsonFile ( )
{
}
public void BeginRootObject ( )
{
BeginObject ( ) ;
}
public void EndRootObject ( )
{
EndObject ( ) ;
if ( TabString . Length > 0 )
{
throw new Exception ( "Called EndRootObject before all objects and arrays have been closed" ) ;
}
}
public void BeginObject ( string? Name = null )
{
string Prefix = Name = = null ? "" : Quoted ( JsonWriter . EscapeString ( Name ) ) + ": " ;
Lines . Add ( TabString + Prefix + "{" ) ;
TabString + = "\t" ;
}
public void EndObject ( )
{
2024-04-03 17:18:04 -04:00
Lines [ ^ 1 ] = Lines [ ^ 1 ] . TrimEnd ( ',' ) ;
2022-06-13 20:12:07 -04:00
TabString = TabString . Remove ( TabString . Length - 1 ) ;
Lines . Add ( TabString + "}," ) ;
}
public void BeginArray ( string? Name = null )
{
string Prefix = Name = = null ? "" : Quoted ( JsonWriter . EscapeString ( Name ) ) + ": " ;
Lines . Add ( TabString + Prefix + "[" ) ;
TabString + = "\t" ;
}
public void EndArray ( )
{
2024-04-03 17:18:04 -04:00
Lines [ ^ 1 ] = Lines [ ^ 1 ] . TrimEnd ( ',' ) ;
2022-06-13 20:12:07 -04:00
TabString = TabString . Remove ( TabString . Length - 1 ) ;
Lines . Add ( TabString + "]," ) ;
}
public void AddField ( string Name , bool Value )
{
Lines . Add ( TabString + Quoted ( JsonWriter . EscapeString ( Name ) ) + ": " + Value . ToString ( ) . ToLower ( ) + "," ) ;
}
public void AddField ( string Name , string Value )
{
Lines . Add ( TabString + Quoted ( JsonWriter . EscapeString ( Name ) ) + ": " + Quoted ( JsonWriter . EscapeString ( Value ) ) + "," ) ;
}
public void AddUnnamedField ( string Value )
{
Lines . Add ( TabString + Quoted ( JsonWriter . EscapeString ( Value ) ) + "," ) ;
}
public void Write ( FileReference File )
{
2024-04-03 17:18:04 -04:00
Lines [ ^ 1 ] = Lines [ ^ 1 ] . TrimEnd ( ',' ) ;
2022-06-13 20:12:07 -04:00
FileReference . WriteAllLines ( File , Lines . ToArray ( ) ) ;
}
private string Quoted ( string Value )
{
return "\"" + Value + "\"" ;
}
private List < string > Lines = new List < string > ( ) ;
private string TabString = "" ;
}
2023-05-30 18:59:32 -04:00
public override string ProjectFileExtension = > ".vscode" ;
2022-06-13 20:12:07 -04:00
public override void CleanProjectFiles ( DirectoryReference InPrimaryProjectDirectory , string InPrimaryProjectName , DirectoryReference InIntermediateProjectFilesPath , ILogger Logger )
{
}
public override bool ShouldGenerateIntelliSenseData ( )
{
2023-02-17 22:39:26 -05:00
return ! bNoCompileCommands & & ! bUseVSCodeExtension ;
2022-06-13 20:12:07 -04:00
}
protected override ProjectFile AllocateProjectFile ( FileReference InitFilePath , DirectoryReference BaseDir )
{
return new VSCodeProject ( InitFilePath , BaseDir ) ;
}
protected override bool WritePrimaryProjectFile ( ProjectFile ? UBTProject , PlatformProjectGeneratorCollection PlatformProjectGenerators , ILogger Logger )
{
DirectoryReference VSCodeDir = DirectoryReference . Combine ( PrimaryProjectPath , ".vscode" ) ;
DirectoryReference . CreateDirectory ( VSCodeDir ) ;
bForeignProject = ! VSCodeDir . IsUnderDirectory ( ProjectRoot ) ;
List < ProjectFile > Projects ;
if ( bForeignProject )
{
Projects = new List < ProjectFile > ( ) ;
2023-05-31 13:37:21 -04:00
foreach ( ProjectFile Project in AllProjectFiles )
2022-06-13 20:12:07 -04:00
{
if ( GameProjectName = = Project . ProjectFilePath . GetFileNameWithoutAnyExtensions ( ) )
{
Projects . Add ( Project ) ;
break ;
}
}
}
else
{
Projects = new List < ProjectFile > ( AllProjectFiles ) ;
}
Projects . Sort ( ( A , B ) = > { return A . ProjectFilePath . GetFileName ( ) . CompareTo ( B . ProjectFilePath . GetFileName ( ) ) ; } ) ;
ProjectData ProjectData = GatherProjectData ( Projects , Logger ) ;
WriteWorkspaceIgnoreFile ( Projects ) ;
WriteCppPropertiesFile ( VSCodeDir , ProjectData ) ;
2023-05-12 17:40:11 -04:00
WriteWorkspaceFile ( ProjectData , Logger ) ;
2022-06-13 20:12:07 -04:00
if ( bForeignProject & & bIncludeEngineSource )
{
// for installed builds we need to write the cpp properties file under the installed engine as well for intellisense to work
DirectoryReference VsCodeDirectory = DirectoryReference . Combine ( Unreal . RootDirectory , ".vscode" ) ;
WriteCppPropertiesFile ( VsCodeDirectory , ProjectData ) ;
}
return true ;
}
private class BuildTarget
{
public readonly string Name ;
public readonly TargetType Type ;
public readonly UnrealTargetPlatform Platform ;
public readonly UnrealTargetConfiguration Configuration ;
public readonly CppStandardVersion CppStandard ;
public readonly FileReference ? CompilerPath ;
public readonly DirectoryReference ? SysRootPath ;
public readonly Dictionary < DirectoryReference , string > ModuleCommandLines ;
public BuildTarget ( string InName , TargetType InType , UnrealTargetPlatform InPlatform , UnrealTargetConfiguration InConfiguration , CppStandardVersion InCppStandard , FileReference ? InCompilerPath , DirectoryReference ? InSysRootPath , Dictionary < DirectoryReference , string > InModulesCommandLines )
{
Name = InName ;
Type = InType ;
Platform = InPlatform ;
Configuration = InConfiguration ;
CppStandard = InCppStandard ;
CompilerPath = InCompilerPath ;
SysRootPath = InSysRootPath ;
ModuleCommandLines = InModulesCommandLines ;
}
public override string ToString ( )
{
return Name . ToString ( ) + " " + Type . ToString ( ) ;
}
}
protected override void AddTargetForIntellisense ( UEBuildTarget Target , ILogger Logger )
{
base . AddTargetForIntellisense ( Target , Logger ) ;
bool UsingClang = true ;
FileReference ? CompilerPath = null ;
DirectoryReference ? SysRootPath = null ;
if ( OperatingSystem . IsWindows ( ) )
{
2024-01-26 17:21:00 -05:00
VCEnvironment Environment = VCEnvironment . Create ( WindowsPlatform . GetDefaultCompiler ( null , Target . Rules . WindowsPlatform . Architecture , Logger , true ) , WindowsCompiler . Default , Target . Platform , Target . Rules . WindowsPlatform . Architecture , null , null , Target . Rules . WindowsPlatform . WindowsSdkVersion , null , Target . Rules . WindowsPlatform . bUseCPPWinRT , Target . Rules . WindowsPlatform . bAllowClangLinker , Logger ) ;
2022-06-13 20:12:07 -04:00
CompilerPath = FileReference . FromString ( Environment . CompilerPath . FullName ) ;
UsingClang = false ;
}
else if ( OperatingSystem . IsLinux ( ) )
{
CompilerPath = FileReference . FromString ( LinuxCommon . WhichClang ( Logger ) ) ;
string? InternalSDKPath = UEBuildPlatform . GetSDK ( UnrealTargetPlatform . Linux ) ? . GetInternalSDKPath ( ) ;
2023-05-30 18:59:32 -04:00
if ( ! String . IsNullOrEmpty ( InternalSDKPath ) )
2022-06-13 20:12:07 -04:00
{
SysRootPath = DirectoryReference . FromString ( InternalSDKPath ) ;
}
}
else if ( OperatingSystem . IsMacOS ( ) )
{
MacToolChainSettings Settings = new MacToolChainSettings ( false , Logger ) ;
2023-06-30 12:08:50 -04:00
CompilerPath = FileReference . Combine ( Settings . ToolchainDir , "clang++" ) ;
SysRootPath = Settings . GetSDKPath ( ) ;
2022-06-13 20:12:07 -04:00
}
else
{
throw new Exception ( "Unknown platform " + HostPlatform . ToString ( ) ) ;
}
// we do not need to keep track of which binary the invocation belongs to, only which target, as such we join all binaries into a single set
Dictionary < DirectoryReference , string > ModuleDirectoryToCompileCommand = new Dictionary < DirectoryReference , string > ( ) ;
// Generate a compile environment for each module in the binary
CppCompileEnvironment GlobalCompileEnvironment = Target . CreateCompileEnvironmentForProjectFiles ( Logger ) ;
foreach ( UEBuildBinary Binary in Target . Binaries )
{
CppCompileEnvironment BinaryCompileEnvironment = Binary . CreateBinaryCompileEnvironment ( GlobalCompileEnvironment ) ;
foreach ( UEBuildModuleCPP Module in Binary . Modules . OfType < UEBuildModuleCPP > ( ) )
{
CppCompileEnvironment ModuleCompileEnvironment = Module . CreateCompileEnvironmentForIntellisense ( Target . Rules , BinaryCompileEnvironment , Logger ) ;
List < FileReference > ForceIncludePaths = new List < FileReference > ( ModuleCompileEnvironment . ForceIncludeFiles . Select ( x = > x . Location ) ) ;
if ( ModuleCompileEnvironment . PrecompiledHeaderIncludeFilename ! = null )
{
ForceIncludePaths . Add ( ModuleCompileEnvironment . PrecompiledHeaderIncludeFilename ) ;
}
StringBuilder CommandBuilder = new StringBuilder ( ) ;
foreach ( FileReference ForceIncludeFile in ForceIncludePaths )
{
CommandBuilder . AppendFormat ( "{0} \"{1}\" {2}" , UsingClang ? "-include" : "/FI" , ForceIncludeFile . FullName , Environment . NewLine ) ;
}
foreach ( string Definition in ModuleCompileEnvironment . Definitions )
{
CommandBuilder . AppendFormat ( "{0} \"{1}\" {2}" , UsingClang ? "-D" : "/D" , Definition , Environment . NewLine ) ;
}
foreach ( DirectoryReference IncludePath in ModuleCompileEnvironment . UserIncludePaths )
{
2023-05-30 18:38:07 -04:00
CommandBuilder . AppendFormat ( "{0} \"{1}\" {2}" , UsingClang ? "-I" : "/I" , IncludePath , Environment . NewLine ) ;
2022-06-13 20:12:07 -04:00
}
foreach ( DirectoryReference IncludePath in ModuleCompileEnvironment . SystemIncludePaths )
{
CommandBuilder . AppendFormat ( "{0} \"{1}\" {2}" , UsingClang ? "-I" : "/I" , IncludePath , Environment . NewLine ) ;
}
2022-06-24 16:02:55 -04:00
ModuleDirectoryToCompileCommand . TryAdd ( Module . ModuleDirectory , CommandBuilder . ToString ( ) ) ;
2022-06-13 20:12:07 -04:00
}
}
BuildTargets . Add ( new BuildTarget ( Target . TargetName , Target . TargetType , Target . Platform , Target . Configuration , GlobalCompileEnvironment . CppStandard , CompilerPath , SysRootPath , ModuleDirectoryToCompileCommand ) ) ;
}
private class ProjectData
{
public enum EOutputType
{
Library ,
Exe ,
WinExe , // some projects have this so we need to read it, but it will be converted across to Exe so no code should handle it!
}
public class BuildProduct
{
public FileReference OutputFile { get ; set ; }
public FileReference ? UProjectFile { get ; set ; }
public UnrealTargetConfiguration Config { get ; set ; }
public UnrealTargetPlatform Platform { get ; set ; }
public EOutputType OutputType { get ; set ; }
2023-05-30 18:38:07 -04:00
2022-06-13 20:12:07 -04:00
public CsProjectInfo ? CSharpInfo { get ; set ; }
public override string ToString ( )
{
2023-05-30 18:38:07 -04:00
return Platform . ToString ( ) + " " + Config . ToString ( ) ;
2022-06-13 20:12:07 -04:00
}
2023-05-30 18:38:07 -04:00
public BuildProduct ( FileReference OutputFile )
2022-06-13 20:12:07 -04:00
{
this . OutputFile = OutputFile ;
}
}
public class Target
{
public string Name ;
public TargetType Type ;
public List < BuildProduct > BuildProducts = new List < BuildProduct > ( ) ;
public Target ( Project InParentProject , string InName , TargetType InType )
{
Name = InName ;
Type = InType ;
InParentProject . Targets . Add ( this ) ;
}
public override string ToString ( )
{
2023-05-30 18:38:07 -04:00
return Name . ToString ( ) + " " + Type . ToString ( ) ;
2022-06-13 20:12:07 -04:00
}
}
public class Project
{
public string Name ;
public ProjectFile SourceProject ;
public List < Target > Targets = new List < Target > ( ) ;
public override string ToString ( )
{
return Name ;
}
public Project ( string Name , ProjectFile SourceProject )
{
this . Name = Name ;
this . SourceProject = SourceProject ;
}
}
public List < Project > NativeProjects = new List < Project > ( ) ;
public List < Project > CSharpProjects = new List < Project > ( ) ;
public List < Project > AllProjects = new List < Project > ( ) ;
}
private ProjectData GatherProjectData ( List < ProjectFile > InProjects , ILogger Logger )
{
ProjectData ProjectData = new ProjectData ( ) ;
foreach ( ProjectFile Project in InProjects )
{
// Create new project record
ProjectData . Project NewProject = new ProjectData . Project ( Project . ProjectFilePath . GetFileNameWithoutExtension ( ) , Project ) ;
ProjectData . AllProjects . Add ( NewProject ) ;
// Add into the correct easy-access list
if ( Project is VSCodeProject )
{
foreach ( ProjectTarget Target in Project . ProjectTargets . OfType < ProjectTarget > ( ) )
{
UnrealTargetConfiguration [ ] Configs = ( UnrealTargetConfiguration [ ] ) Enum . GetValues ( typeof ( UnrealTargetConfiguration ) ) ;
List < UnrealTargetPlatform > Platforms = new List < UnrealTargetPlatform > ( Target . TargetRules ! . GetSupportedPlatforms ( ) ) ;
ProjectData . Target NewTarget = new ProjectData . Target ( NewProject , Target . TargetRules . Name , Target . TargetRules . Type ) ;
if ( HostPlatform ! = UnrealTargetPlatform . Win64 )
{
Platforms . Remove ( UnrealTargetPlatform . Win64 ) ;
}
foreach ( UnrealTargetPlatform Platform in Platforms )
{
UEBuildPlatform . TryGetBuildPlatform ( Platform , out UEBuildPlatform ? BuildPlatform ) ;
if ( SupportedPlatforms . Contains ( Platform ) & & ( BuildPlatform ! = null ) & & ( BuildPlatform . HasRequiredSDKsInstalled ( ) = = SDKStatus . Valid ) )
{
foreach ( UnrealTargetConfiguration Config in Configs )
{
if ( MSBuildProjectFile . IsValidProjectPlatformAndConfiguration ( Target , Platform , Config , Logger ) )
{
NewTarget . BuildProducts . Add ( new ProjectData . BuildProduct ( GetExecutableFilename ( Project , Target , Platform , Config ) )
{
Platform = Platform ,
Config = Config ,
UProjectFile = Target . UnrealProjectFilePath ,
OutputType = ProjectData . EOutputType . Exe ,
CSharpInfo = null
} ) ;
}
}
}
}
}
ProjectData . NativeProjects . Add ( NewProject ) ;
}
else
{
VCSharpProjectFile VCSharpProject = ( VCSharpProjectFile ) Project ;
2023-05-30 18:38:07 -04:00
string ProjectName = Project . ProjectFilePath . GetFileNameWithoutExtension ( ) ;
2022-06-13 20:12:07 -04:00
2023-05-30 18:38:07 -04:00
ProjectData . Target Target = new ProjectData . Target ( NewProject , ProjectName , TargetType . Program ) ;
2022-06-13 20:12:07 -04:00
2023-05-30 18:38:07 -04:00
UnrealTargetConfiguration [ ] Configs = { UnrealTargetConfiguration . Debug , UnrealTargetConfiguration . Development } ;
foreach ( UnrealTargetConfiguration Config in Configs )
{
CsProjectInfo ? Info = VCSharpProject . GetProjectInfo ( Config ) ! ;
if ( Info . Properties . ContainsKey ( "OutputPath" ) )
2022-06-13 20:12:07 -04:00
{
2023-05-30 18:38:07 -04:00
ProjectData . EOutputType OutputType ;
string? OutputTypeName ;
if ( Info . Properties . TryGetValue ( "OutputType" , out OutputTypeName ) )
2022-06-13 20:12:07 -04:00
{
2023-05-30 18:38:07 -04:00
OutputType = ( ProjectData . EOutputType ) Enum . Parse ( typeof ( ProjectData . EOutputType ) , OutputTypeName ) ;
}
else
{
OutputType = ProjectData . EOutputType . Library ;
}
2022-06-13 20:12:07 -04:00
2023-05-30 18:38:07 -04:00
if ( OutputType = = ProjectData . EOutputType . WinExe )
{
OutputType = ProjectData . EOutputType . Exe ;
}
2022-06-13 20:12:07 -04:00
2023-05-30 18:38:07 -04:00
FileReference ? OutputFile = null ;
HashSet < FileReference > ProjectBuildProducts = new HashSet < FileReference > ( ) ;
Info . FindCompiledBuildProducts ( DirectoryReference . Combine ( VCSharpProject . ProjectFilePath . Directory , Info . Properties [ "OutputPath" ] ) , ProjectBuildProducts ) ;
foreach ( FileReference ProjectBuildProduct in ProjectBuildProducts )
{
if ( ( OutputType = = ProjectData . EOutputType . Exe & & ProjectBuildProduct . GetExtension ( ) = = FrameworkExecutableExtension ) | |
( OutputType = = ProjectData . EOutputType . Library & & ProjectBuildProduct . GetExtension ( ) = = FrameworkLibraryExtension ) )
2022-06-13 20:12:07 -04:00
{
2023-05-30 18:38:07 -04:00
OutputFile = ProjectBuildProduct ;
break ;
2022-06-13 20:12:07 -04:00
}
}
2023-05-30 18:38:07 -04:00
if ( OutputFile ! = null )
{
Target . BuildProducts . Add ( new ProjectData . BuildProduct ( OutputFile )
{
Platform = HostPlatform ,
Config = Config ,
OutputType = OutputType ,
CSharpInfo = Info
} ) ;
}
}
}
ProjectData . CSharpProjects . Add ( NewProject ) ;
2022-06-13 20:12:07 -04:00
}
}
return ProjectData ;
}
private void WriteCppPropertiesFile ( DirectoryReference OutputDirectory , ProjectData Projects )
{
DirectoryReference . CreateDirectory ( OutputDirectory ) ;
JsonFile OutFile = new JsonFile ( ) ;
OutFile . BeginRootObject ( ) ;
{
OutFile . BeginArray ( "configurations" ) ;
{
HashSet < FileReference > AllSourceFiles = new HashSet < FileReference > ( ) ;
Dictionary < DirectoryReference , string > AllModuleCommandLines = new Dictionary < DirectoryReference , string > ( ) ;
FileReference ? CompilerPath = null ;
DirectoryReference ? SysRootPath = null ;
CppStandardVersion CppStandard = CppStandardVersion . Default ;
2023-05-30 18:38:07 -04:00
2022-06-13 20:12:07 -04:00
foreach ( ProjectData . Project Project in Projects . AllProjects )
{
AllSourceFiles . UnionWith ( Project . SourceProject . SourceFiles . Select ( x = > x . Reference ) ) ;
foreach ( ProjectData . Target ProjectTarget in Project . Targets )
{
BuildTarget ? BuildTarget = BuildTargets . FirstOrDefault ( Target = > Target . Name = = ProjectTarget . Name ) ;
// we do not generate intellisense for every target, as that just causes a lot of redundancy, as such we will not find a mapping for a lot of the targets
if ( BuildTarget = = null )
2023-05-30 18:59:32 -04:00
{
2022-06-13 20:12:07 -04:00
continue ;
2023-05-30 18:59:32 -04:00
}
2022-06-13 20:12:07 -04:00
2023-05-30 18:59:32 -04:00
string Name = String . Format ( "{0} {1} {2} {3} ({4})" , ProjectTarget . Name , ProjectTarget . Type , BuildTarget . Platform , BuildTarget . Configuration , Project . Name ) ;
2022-06-13 20:12:07 -04:00
WriteConfiguration ( Name , Project . Name , Project . SourceProject . SourceFiles . Select ( x = > x . Reference ) , BuildTarget . CppStandard , BuildTarget . CompilerPath ! , BuildTarget . SysRootPath , BuildTarget . ModuleCommandLines , OutFile , OutputDirectory ) ;
CompilerPath = BuildTarget . CompilerPath ;
foreach ( KeyValuePair < DirectoryReference , string > Pair in BuildTarget . ModuleCommandLines )
{
2023-05-30 18:38:07 -04:00
if ( ! AllModuleCommandLines . ContainsKey ( Pair . Key ) )
2022-06-13 20:12:07 -04:00
{
AllModuleCommandLines [ Pair . Key ] = Pair . Value ;
}
}
if ( BuildTarget . CppStandard > CppStandard )
{
CppStandard = BuildTarget . CppStandard ;
}
if ( BuildTarget . SysRootPath ! = null )
{
SysRootPath = BuildTarget . SysRootPath ;
}
}
}
string DefaultConfigName ;
if ( HostPlatform = = UnrealTargetPlatform . Linux )
{
DefaultConfigName = "Linux" ;
}
else if ( HostPlatform = = UnrealTargetPlatform . Mac )
{
DefaultConfigName = "Mac" ;
}
else
{
DefaultConfigName = "Win32" ;
}
WriteConfiguration ( DefaultConfigName , "Default" , AllSourceFiles , CppStandard , CompilerPath ! , SysRootPath , AllModuleCommandLines , OutFile , OutputDirectory ) ;
}
OutFile . EndArray ( ) ;
}
OutFile . EndRootObject ( ) ;
OutFile . Write ( FileReference . Combine ( OutputDirectory , "c_cpp_properties.json" ) ) ;
}
private void WriteConfiguration ( string Name , string ProjectName , IEnumerable < FileReference > SourceFiles , CppStandardVersion CppStandard , FileReference CompilerPath , DirectoryReference ? SysRootPath , Dictionary < DirectoryReference , string > ModuleCommandLines , JsonFile OutFile , DirectoryReference OutputDirectory )
{
OutFile . BeginObject ( ) ;
OutFile . AddField ( "name" , Name ) ;
2022-10-28 12:59:39 -04:00
if ( CompilerPath ! = null )
{
OutFile . AddField ( "compilerPath" , CompilerPath . FullName ) ;
}
2022-06-13 20:12:07 -04:00
if ( HostPlatform = = UnrealTargetPlatform . Mac )
{
string SysRoot = SysRootPath ! = null ? SysRootPath . FullName : "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" ;
OutFile . BeginArray ( "compilerArgs" ) ;
{
OutFile . AddUnnamedField ( "-isysroot" ) ;
OutFile . AddUnnamedField ( SysRoot ) ;
}
OutFile . EndArray ( ) ;
OutFile . BeginArray ( "macFrameworkPath" ) ;
{
OutFile . AddUnnamedField ( SysRoot + "/System/Library/Frameworks" ) ;
}
OutFile . EndArray ( ) ;
}
else if ( SysRootPath ! = null )
{
OutFile . BeginArray ( "compilerArgs" ) ;
{
OutFile . AddUnnamedField ( "-isysroot" ) ;
OutFile . AddUnnamedField ( SysRootPath . FullName ) ;
}
OutFile . EndArray ( ) ;
}
switch ( CppStandard )
{
case CppStandardVersion . Cpp14 :
OutFile . AddField ( "cStandard" , "c11" ) ;
OutFile . AddField ( "cppStandard" , "c++14" ) ;
break ;
case CppStandardVersion . Cpp17 :
OutFile . AddField ( "cStandard" , "c17" ) ;
OutFile . AddField ( "cppStandard" , "c++17" ) ;
break ;
case CppStandardVersion . Cpp20 :
case CppStandardVersion . Latest :
OutFile . AddField ( "cStandard" , "c17" ) ;
OutFile . AddField ( "cppStandard" , "c++20" ) ;
break ;
default :
throw new BuildException ( $"Unsupported C++ standard type set: {CppStandard}" ) ;
}
if ( HostPlatform = = UnrealTargetPlatform . Win64 )
{
OutFile . AddField ( "intelliSenseMode" , "msvc-x64" ) ;
}
else
{
OutFile . AddField ( "intelliSenseMode" , "clang-x64" ) ;
}
2023-02-17 22:39:26 -05:00
if ( bUseVSCodeExtension )
{
OutFile . AddField ( "configurationProvider" , "epic.ue" ) ;
}
2022-10-28 12:59:39 -04:00
2023-02-17 22:39:26 -05:00
if ( ShouldGenerateIntelliSenseData ( ) )
{
2023-05-30 18:59:32 -04:00
FileReference CompileCommands = FileReference . Combine ( OutputDirectory , String . Format ( "compileCommands_{0}.json" , ProjectName ) ) ;
2023-02-17 22:39:26 -05:00
WriteCompileCommands ( CompileCommands , SourceFiles , CompilerPath ! , ModuleCommandLines ) ;
OutFile . AddField ( "compileCommands" , MakePathString ( CompileCommands , bInAbsolute : true , bForceSkipQuotes : true ) ) ;
}
2022-06-13 20:12:07 -04:00
OutFile . EndObject ( ) ;
}
private void WriteNativeTaskDeployAndroid ( ProjectData . Project InProject , JsonFile OutFile , ProjectData . Target Target , ProjectData . BuildProduct BuildProduct )
{
if ( BuildProduct . UProjectFile = = null )
{
return ;
}
string [ ] ConfigTypes = new string [ ] { "Cook+Deploy" , "Cook" , "Deploy" } ;
foreach ( string ConfigType in ConfigTypes )
{
OutFile . BeginObject ( ) ;
{
string TaskName = String . Format ( "{0} {1} {2} {3}" , Target . Name , BuildProduct . Platform . ToString ( ) , BuildProduct . Config , ConfigType ) ;
OutFile . AddField ( "label" , TaskName ) ;
OutFile . AddField ( "group" , "build" ) ;
if ( HostPlatform = = UnrealTargetPlatform . Win64 )
{
OutFile . AddField ( "command" , MakePathString ( FileReference . Combine ( ProjectRoot , "Engine" , "Build" , "BatchFiles" , "RunUAT.bat" ) ) ) ;
}
else
{
OutFile . AddField ( "command" , MakePathString ( FileReference . Combine ( ProjectRoot , "Engine" , "Build" , "BatchFiles" , "RunUAT.sh" ) ) ) ;
}
OutFile . BeginArray ( "args" ) ;
{
OutFile . AddUnnamedField ( "BuildCookRun" ) ;
OutFile . AddUnnamedField ( "-ScriptsForProject=" + BuildProduct . UProjectFile . ToNormalizedPath ( ) ) ;
OutFile . AddUnnamedField ( "-Project=" + BuildProduct . UProjectFile . ToNormalizedPath ( ) ) ;
OutFile . AddUnnamedField ( "-noP4" ) ;
OutFile . AddUnnamedField ( String . Format ( "-ClientConfig={0}" , BuildProduct . Config . ToString ( ) ) ) ;
OutFile . AddUnnamedField ( String . Format ( "-ServerConfig={0}" , BuildProduct . Config . ToString ( ) ) ) ;
OutFile . AddUnnamedField ( "-NoCompileEditor" ) ;
OutFile . AddUnnamedField ( "-utf8output" ) ;
OutFile . AddUnnamedField ( String . Format ( "-Platform={0}" , BuildProduct . Platform . ToString ( ) ) ) ;
OutFile . AddUnnamedField ( String . Format ( "-TargetPlatform={0}" , BuildProduct . Platform . ToString ( ) ) ) ;
OutFile . AddUnnamedField ( "-ini:Game:[/Script/UnrealEd.ProjectPackagingSettings]:BlueprintNativizationMethod=Disabled" ) ;
OutFile . AddUnnamedField ( "-Compressed" ) ;
OutFile . AddUnnamedField ( "-IterativeCooking" ) ;
OutFile . AddUnnamedField ( "-IterativeDeploy" ) ;
switch ( ConfigType )
{
case "Cook+Deploy" :
{
OutFile . AddUnnamedField ( "-Cook" ) ;
OutFile . AddUnnamedField ( "-Stage" ) ;
OutFile . AddUnnamedField ( "-Deploy" ) ;
break ;
}
case "Cook" :
{
OutFile . AddUnnamedField ( "-Cook" ) ;
break ;
}
case "Deploy" :
{
OutFile . AddUnnamedField ( "-DeploySoToDevice" ) ;
OutFile . AddUnnamedField ( "-SkipCook" ) ;
OutFile . AddUnnamedField ( "-Stage" ) ;
OutFile . AddUnnamedField ( "-Deploy" ) ;
break ;
}
}
}
OutFile . EndArray ( ) ;
OutFile . BeginArray ( "dependsOn" ) ;
{
switch ( ConfigType )
{
case "Cook+Deploy" :
case "Cook" :
{
OutFile . AddUnnamedField ( String . Format ( "{0}Editor {1} Development Build" , Target . Name , HostPlatform . ToString ( ) ) ) ;
OutFile . AddUnnamedField ( String . Format ( "{0} {1} {2} Build" , Target . Name , BuildProduct . Platform . ToString ( ) , BuildProduct . Config ) ) ;
break ;
}
default :
{
OutFile . AddUnnamedField ( String . Format ( "{0} {1} {2} Build" , Target . Name , BuildProduct . Platform . ToString ( ) , BuildProduct . Config ) ) ;
break ;
}
}
}
OutFile . EndArray ( ) ;
OutFile . AddField ( "type" , "shell" ) ;
OutFile . BeginObject ( "options" ) ;
{
OutFile . AddField ( "cwd" , MakeUnquotedPathString ( ProjectRoot , EPathType . Absolute ) ) ;
}
OutFile . EndObject ( ) ;
}
OutFile . EndObject ( ) ;
}
}
2023-05-30 18:38:07 -04:00
private void WriteCompileCommands ( FileReference CompileCommandsFile , IEnumerable < FileReference > SourceFiles ,
2022-06-13 20:12:07 -04:00
FileReference CompilerPath , Dictionary < DirectoryReference , string > ModuleCommandLines )
{
// this creates a compileCommands.json
// see VsCode Docs - https://code.visualstudio.com/docs/cpp/c-cpp-properties-schema-reference (compileCommands attribute)
// and the clang format description https://clang.llvm.org/docs/JSONCompilationDatabase.html
using ( JsonWriter Writer = new JsonWriter ( CompileCommandsFile ) )
{
Writer . WriteArrayStart ( ) ;
DirectoryReference ResponseFileDir = DirectoryReference . Combine ( CompileCommandsFile . Directory , CompileCommandsFile . GetFileNameWithoutExtension ( ) ) ;
DirectoryReference . CreateDirectory ( ResponseFileDir ) ;
Dictionary < DirectoryReference , FileReference ? > DirectoryToResponseFile = new Dictionary < DirectoryReference , FileReference ? > ( ) ;
2023-05-30 18:38:07 -04:00
foreach ( KeyValuePair < DirectoryReference , string > Pair in ModuleCommandLines )
2022-06-13 20:12:07 -04:00
{
FileReference ResponseFile = FileReference . Combine ( ResponseFileDir , String . Format ( "{0}.{1}.rsp" , Pair . Key . GetDirectoryName ( ) , DirectoryToResponseFile . Count ) ) ;
FileReference . WriteAllText ( ResponseFile , Pair . Value ) ;
DirectoryToResponseFile . Add ( Pair . Key , ResponseFile ) ;
}
foreach ( FileReference File in SourceFiles . OrderBy ( x = > x . FullName ) )
{
DirectoryReference Directory = File . Directory ;
FileReference ? ResponseFile = null ;
if ( ! DirectoryToResponseFile . TryGetValue ( Directory , out ResponseFile ) )
{
for ( DirectoryReference ? ParentDir = Directory ; ParentDir ! = null & & ParentDir ! = Unreal . RootDirectory ; ParentDir = ParentDir . ParentDirectory )
{
if ( DirectoryToResponseFile . TryGetValue ( ParentDir , out ResponseFile ) )
{
break ;
}
}
DirectoryToResponseFile [ Directory ] = ResponseFile ;
}
if ( ResponseFile = = null )
{
// no compiler command associated with the file, will happen for any file that is not a C++ file and is not an error
continue ;
}
Writer . WriteObjectStart ( ) ;
Writer . WriteValue ( "file" , MakePathString ( File , bInAbsolute : true , bForceSkipQuotes : true ) ) ;
Writer . WriteArrayStart ( "arguments" ) ;
Writer . WriteValue ( MakePathString ( CompilerPath , bInAbsolute : true , bForceSkipQuotes : true ) ) ;
Writer . WriteValue ( $"@{MakePathString(ResponseFile, bInAbsolute: true, bForceSkipQuotes: true)}" ) ;
Writer . WriteArrayEnd ( ) ;
Writer . WriteValue ( "directory" , Unreal . EngineSourceDirectory . ToString ( ) ) ;
Writer . WriteObjectEnd ( ) ;
}
Writer . WriteArrayEnd ( ) ;
}
}
private void WriteNativeTask ( ProjectData . Project InProject , JsonFile OutFile )
{
string [ ] Commands = { "Build" , "Rebuild" , "Clean" } ;
foreach ( ProjectData . Target Target in InProject . Targets )
{
foreach ( ProjectData . BuildProduct BuildProduct in Target . BuildProducts )
{
foreach ( string BaseCommand in Commands )
{
string Command = BaseCommand = = "Rebuild" ? "Build" : BaseCommand ;
string TaskName = String . Format ( "{0} {1} {2} {3}" , Target . Name , BuildProduct . Platform . ToString ( ) , BuildProduct . Config , BaseCommand ) ;
string CleanTaskName = String . Format ( "{0} {1} {2} {3}" , Target . Name , BuildProduct . Platform . ToString ( ) , BuildProduct . Config , "Clean" ) ;
OutFile . BeginObject ( ) ;
{
OutFile . AddField ( "label" , TaskName ) ;
OutFile . AddField ( "group" , "build" ) ;
string? CleanParam = Command = = "Clean" ? "-clean" : null ;
if ( HostPlatform = = UnrealTargetPlatform . Win64 )
{
OutFile . AddField ( "command" , MakePathString ( FileReference . Combine ( ProjectRoot , "Engine" , "Build" , "BatchFiles" , Command + ".bat" ) ) ) ;
CleanParam = null ;
}
else
{
OutFile . AddField ( "command" , MakePathString ( FileReference . Combine ( ProjectRoot , "Engine" , "Build" , "BatchFiles" , HostPlatform . ToString ( ) , "Build.sh" ) ) ) ;
if ( Command = = "Clean" )
{
CleanParam = "-clean" ;
}
}
2023-05-30 18:38:07 -04:00
2022-06-13 20:12:07 -04:00
OutFile . BeginArray ( "args" ) ;
{
OutFile . AddUnnamedField ( Target . Name ) ;
OutFile . AddUnnamedField ( BuildProduct . Platform . ToString ( ) ) ;
OutFile . AddUnnamedField ( BuildProduct . Config . ToString ( ) ) ;
if ( bForeignProject )
{
OutFile . AddUnnamedField ( MakeUnquotedPathString ( BuildProduct . UProjectFile ! , EPathType . Relative , null ) ) ;
}
OutFile . AddUnnamedField ( "-waitmutex" ) ;
2023-05-30 18:59:32 -04:00
if ( ! String . IsNullOrEmpty ( CleanParam ) )
2022-06-13 20:12:07 -04:00
{
OutFile . AddUnnamedField ( CleanParam ) ;
}
}
OutFile . EndArray ( ) ;
OutFile . AddField ( "problemMatcher" , "$msCompile" ) ;
if ( ! bForeignProject | | BaseCommand = = "Rebuild" )
{
OutFile . BeginArray ( "dependsOn" ) ;
{
if ( ! bForeignProject )
{
if ( Command = = "Build" & & Target . Type = = TargetType . Editor )
{
OutFile . AddUnnamedField ( "ShaderCompileWorker " + HostPlatform . ToString ( ) + " Development Build" ) ;
}
else
{
OutFile . AddUnnamedField ( "UnrealBuildTool " + HostPlatform . ToString ( ) + " Development Build" ) ;
}
}
if ( BaseCommand = = "Rebuild" )
{
OutFile . AddUnnamedField ( CleanTaskName ) ;
}
}
OutFile . EndArray ( ) ;
}
OutFile . AddField ( "type" , "shell" ) ;
OutFile . BeginObject ( "options" ) ;
{
OutFile . AddField ( "cwd" , MakeUnquotedPathString ( ProjectRoot , EPathType . Absolute ) ) ;
}
OutFile . EndObject ( ) ;
}
OutFile . EndObject ( ) ;
if ( BuildProduct . Platform = = UnrealTargetPlatform . Android & & BaseCommand . Equals ( "Build" ) )
{
WriteNativeTaskDeployAndroid ( InProject , OutFile , Target , BuildProduct ) ;
}
}
}
}
}
private void WriteCSharpTask ( ProjectData . Project InProject , JsonFile OutFile )
{
string [ ] Commands = { "Build" , "Clean" } ;
foreach ( ProjectData . Target Target in InProject . Targets )
{
foreach ( ProjectData . BuildProduct BuildProduct in Target . BuildProducts )
{
foreach ( string Command in Commands )
{
string TaskName = String . Format ( "{0} {1} {2} {3}" , Target . Name , BuildProduct . Platform , BuildProduct . Config , Command ) ;
OutFile . BeginObject ( ) ;
{
OutFile . AddField ( "label" , TaskName ) ;
OutFile . AddField ( "group" , "build" ) ;
2024-08-22 18:03:04 -04:00
OutFile . AddField ( "command" , MakePathString ( Unreal . DotnetPath ) ) ;
2022-06-13 20:12:07 -04:00
OutFile . BeginArray ( "args" ) ;
{
OutFile . AddUnnamedField ( Command . ToLower ( ) ) ;
OutFile . AddUnnamedField ( "--configuration" ) ;
OutFile . AddUnnamedField ( BuildProduct . Config . ToString ( ) ) ;
OutFile . AddUnnamedField ( MakeUnquotedPathString ( BuildProduct . CSharpInfo ! . ProjectPath , EPathType . Absolute ) ) ;
}
OutFile . EndArray ( ) ;
2024-08-22 18:03:04 -04:00
OutFile . AddField ( "problemMatcher" , "$msCompile" ) ;
OutFile . AddField ( "type" , "shell" ) ;
2022-06-13 20:12:07 -04:00
2024-08-22 18:03:04 -04:00
OutFile . BeginObject ( "options" ) ;
{
OutFile . AddField ( "cwd" , MakeUnquotedPathString ( ProjectRoot , EPathType . Absolute ) ) ;
OutFile . BeginObject ( "env" ) ;
{
OutFile . AddField ( "PATH" , $"{Unreal.DotnetDirectory}{Path.PathSeparator}${{env:PATH}}" ) ; ;
OutFile . AddField ( "DOTNET_MULTILEVEL_LOOKUP" , "0" ) ;
OutFile . AddField ( "DOTNET_ROLL_FORWARD" , "LatestMajor" ) ;
}
OutFile . EndObject ( ) ;
}
OutFile . EndObject ( ) ;
}
2022-06-13 20:12:07 -04:00
OutFile . EndObject ( ) ;
2024-08-22 18:03:04 -04:00
}
}
}
if ( InProject . Name . Equals ( "AutomationTool" , StringComparison . OrdinalIgnoreCase ) ) {
WriteAutomationScriptTask ( InProject , OutFile ) ;
}
}
private void WriteAutomationScriptTask ( ProjectData . Project InProject , JsonFile OutFile )
{
string [ ] Commands = { "Build" } ;
foreach ( ProjectData . Target Target in InProject . Targets )
{
foreach ( ProjectData . BuildProduct BuildProduct in Target . BuildProducts . Where ( x = > x . OutputType = = ProjectData . EOutputType . Exe ) )
{
foreach ( string Command in Commands )
{
string TaskName = String . Format ( "{0}+Scripts {1} {2} {3}" , Target . Name , BuildProduct . Platform , BuildProduct . Config , Command ) ;
string DependsOnTaskName = String . Format ( "{0} {1} {2} {3}" , Target . Name , BuildProduct . Platform , BuildProduct . Config , Command ) ;
OutFile . BeginObject ( ) ;
{
OutFile . AddField ( "label" , TaskName ) ;
OutFile . AddField ( "group" , "build" ) ;
OutFile . AddField ( "command" , MakePathString ( BuildProduct . OutputFile ) ) ;
OutFile . BeginArray ( "args" ) ;
{
OutFile . AddUnnamedField ( "-list" ) ;
}
OutFile . EndArray ( ) ;
OutFile . BeginArray ( "dependsOn" ) ;
{
OutFile . AddUnnamedField ( DependsOnTaskName ) ;
}
OutFile . EndArray ( ) ;
OutFile . AddField ( "problemMatcher" , "$msCompile" ) ;
OutFile . AddField ( "type" , "shell" ) ;
OutFile . BeginObject ( "options" ) ;
{
OutFile . AddField ( "cwd" , MakeUnquotedPathString ( ProjectRoot , EPathType . Absolute ) ) ;
OutFile . BeginObject ( "env" ) ;
{
OutFile . AddField ( "PATH" , $"{Unreal.DotnetDirectory}{Path.PathSeparator}${{env:PATH}}" ) ;
OutFile . AddField ( "DOTNET_MULTILEVEL_LOOKUP" , "0" ) ;
OutFile . AddField ( "DOTNET_ROLL_FORWARD" , "LatestMajor" ) ;
}
OutFile . EndObject ( ) ;
}
OutFile . EndObject ( ) ;
}
2022-06-13 20:12:07 -04:00
OutFile . EndObject ( ) ;
}
}
}
}
2023-05-12 17:40:11 -04:00
private void WriteTasks ( JsonFile OutFile , ProjectData ProjectData )
2022-06-13 20:12:07 -04:00
{
2023-05-12 17:40:11 -04:00
OutFile . AddField ( "version" , "2.0.0" ) ;
2022-06-13 20:12:07 -04:00
2023-05-12 17:40:11 -04:00
OutFile . BeginArray ( "tasks" ) ;
2022-06-13 20:12:07 -04:00
{
2023-05-12 17:40:11 -04:00
if ( ! bUseVSCodeExtension )
2022-06-13 20:12:07 -04:00
{
2023-05-12 17:40:11 -04:00
foreach ( ProjectData . Project NativeProject in ProjectData . NativeProjects )
2022-06-13 20:12:07 -04:00
{
2023-05-12 17:40:11 -04:00
WriteNativeTask ( NativeProject , OutFile ) ;
2022-06-13 20:12:07 -04:00
}
}
2023-05-12 17:40:11 -04:00
foreach ( ProjectData . Project CSharpProject in ProjectData . CSharpProjects )
{
WriteCSharpTask ( CSharpProject , OutFile ) ;
}
OutFile . EndArray ( ) ;
}
2022-06-13 20:12:07 -04:00
}
2023-05-30 18:38:07 -04:00
2022-06-13 20:12:07 -04:00
private FileReference GetExecutableFilename ( ProjectFile Project , ProjectTarget Target , UnrealTargetPlatform Platform , UnrealTargetConfiguration Configuration )
{
TargetRules ? TargetRulesObject = Target . TargetRules ;
FileReference TargetFilePath = Target . TargetFilePath ;
string TargetName = TargetFilePath = = null ? Project . ProjectFilePath . GetFileNameWithoutExtension ( ) : TargetFilePath . GetFileNameWithoutAnyExtensions ( ) ;
string UBTPlatformName = Platform . ToString ( ) ;
// Setup output path
UEBuildPlatform BuildPlatform = UEBuildPlatform . GetBuildPlatform ( Platform ) ;
// Figure out if this is a monolithic build
bool bShouldCompileMonolithic = BuildPlatform . ShouldCompileMonolithicBinary ( Platform ) ;
if ( TargetRulesObject ! = null )
{
2023-05-17 16:22:48 -04:00
try
{
bShouldCompileMonolithic | = ( Target . CreateRulesDelegate ( Platform , Configuration ) . LinkType = = TargetLinkType . Monolithic ) ;
}
catch ( BuildException )
{
}
2022-06-13 20:12:07 -04:00
}
TargetType TargetRulesType = Target . TargetRules = = null ? TargetType . Program : Target . TargetRules . Type ;
// Get the output directory
DirectoryReference RootDirectory = Unreal . EngineDirectory ;
if ( TargetRulesType ! = TargetType . Program & & ( bShouldCompileMonolithic | | TargetRulesObject ! . BuildEnvironment = = TargetBuildEnvironment . Unique ) )
{
2023-05-30 18:38:07 -04:00
if ( Target . UnrealProjectFilePath ! = null )
2022-06-13 20:12:07 -04:00
{
RootDirectory = Target . UnrealProjectFilePath . Directory ;
}
}
if ( TargetRulesType = = TargetType . Program )
{
2023-05-30 18:38:07 -04:00
if ( Target . UnrealProjectFilePath ! = null )
2022-06-13 20:12:07 -04:00
{
RootDirectory = Target . UnrealProjectFilePath . Directory ;
}
}
// Get the output directory
DirectoryReference OutputDirectory = DirectoryReference . Combine ( RootDirectory , "Binaries" , UBTPlatformName ) ;
// Get the executable name (minus any platform or config suffixes)
string BinaryName ;
2023-05-30 18:38:07 -04:00
if ( Target . TargetRules ! . BuildEnvironment = = TargetBuildEnvironment . Shared & & TargetRulesType ! = TargetType . Program )
2022-06-13 20:12:07 -04:00
{
BinaryName = UEBuildTarget . GetAppNameForTargetType ( TargetRulesType ) ;
}
else
{
BinaryName = TargetName ;
}
// Make the output file path
UnrealArch/UnrealArchitectures changes
- Creates the UnrealArchitectures class, which wraps a list of UnrealArch objects
- UnrealArch is a single architecture, expandable enum-like struct
- There is no more concept of "no/default architecture", there is always a valid active architecture when building
- Most uses of "string Architecture" are replaced with one of the two above, depending if multiple architectures are supported or not
- UnrealArch has some platform-extensions for platform-specific naming (like Linux adds in LinuxName that turns, for instance, Arm64 -> aarch64-unknown-linux-gnueabi, which is used in folder names, etc)
- UnrealArch has bIsX64 which can be used determine intel instruction set (as opposed to arm)
- TargetRules class has an "Architecture" accessor that will return a single architecture if the active architectures is a single architecture, or throw an exception if multiple. This is useful in a majority of the cases where a paltform can only have a single architecture active in TargetRules (microsoft platforms, for instance, will create separate targets when compiling multiple architectures at once)
- Added UnrealArchitectureConfig class, which contains all the architecture information for a platform (what architectures are supported, what ones are currently active for given project, etc)
#preflight 63c81fb5b065224750a1759e
#rb mike.fricker,roman.dzieciol,joe.kirchoff,dmytro.vovk,brandon.schaefer [various parts]
#p4v-preflight-copy 23562471
[CL 23829977 by josh adams in ue5-main branch]
2023-01-24 09:30:28 -05:00
string BinaryFileName = UEBuildTarget . MakeBinaryFileName ( BinaryName , Platform , Configuration , TargetRulesObject ! . Architectures , TargetRulesObject . UndecoratedConfiguration , UEBuildBinaryType . Executable ) ;
2022-06-13 20:12:07 -04:00
string ExecutableFilename = FileReference . Combine ( OutputDirectory , BinaryFileName ) . FullName ;
// Include the path to the actual executable for a Mac app bundle
if ( Platform = = UnrealTargetPlatform . Mac & & ! Target . TargetRules . bIsBuildingConsoleApplication )
{
ExecutableFilename + = ".app/Contents/MacOS/" + Path . GetFileName ( ExecutableFilename ) ;
}
return new FileReference ( ExecutableFilename ) ;
}
private void WriteNativeLaunchConfigAndroidOculus ( ProjectData . Project InProject , JsonFile OutFile , ProjectData . Target Target , ProjectData . BuildProduct BuildProduct , ILogger Logger )
{
ConfigHierarchy Ini = ConfigCache . ReadHierarchy ( ConfigHierarchyType . Engine , DirectoryReference . FromFile ( BuildProduct . UProjectFile ) , BuildProduct . Platform ) ;
2024-04-02 20:29:22 -04:00
bool ArrayResult = Ini . GetArray ( "/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" , "PackageForOculusMobile" , out List < string > ? OculusMobileDevices ) ; // Backcompat for deprecated oculus device target setting
bool BoolResult = Ini . GetBool ( "/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" , "bPackageForMetaQuest" , out bool bPackageForMetaQuest ) ;
2023-06-09 11:58:31 -04:00
// Check if packaging for Meta Quest
if ( ( ! ArrayResult | | OculusMobileDevices = = null | | OculusMobileDevices . Count = = 0 ) & & ( ! BoolResult | | ! bPackageForMetaQuest ) )
2022-06-13 20:12:07 -04:00
{
return ;
}
// Get package name
string PackageName ;
Ini . GetString ( "/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" , "PackageName" , out PackageName ) ;
if ( PackageName . Contains ( "[PROJECT]" ) )
{
// project name must start with a letter
2023-05-30 18:59:32 -04:00
if ( ! Char . IsLetter ( Target . Name [ 0 ] ) )
2022-06-13 20:12:07 -04:00
{
Trace . TraceWarning ( "Package name segments must all start with a letter. Please replace [PROJECT] with a valid name" ) ;
}
string ProjectName = Target . Name ;
// hyphens not allowed so change them to underscores in project name
2023-05-31 13:37:21 -04:00
if ( ProjectName . Contains ( '-' ) )
2022-06-13 20:12:07 -04:00
{
Trace . TraceWarning ( "Project name contained hyphens, converted to underscore" ) ;
ProjectName = ProjectName . Replace ( "-" , "_" ) ;
}
// check for special characters
for ( int Index = 0 ; Index < ProjectName . Length ; Index + + )
{
char c = ProjectName [ Index ] ;
2023-05-30 18:59:32 -04:00
if ( c ! = '.' & & c ! = '_' & & ! Char . IsLetterOrDigit ( c ) )
2022-06-13 20:12:07 -04:00
{
Trace . TraceWarning ( "Project name contains illegal characters (only letters, numbers, and underscore allowed); please replace [PROJECT] with a valid name" ) ;
ProjectName . Replace ( c , '_' ) ;
}
}
PackageName = PackageName . Replace ( "[PROJECT]" , ProjectName ) ;
}
// Get store version
int StoreVersion = 1 ;
int StoreVersionArm64 = 1 ;
int StoreVersionOffsetArm64 = 0 ;
Ini . GetInt32 ( "/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" , "StoreVersion" , out StoreVersion ) ;
Ini . GetInt32 ( "/Script/AndroidRuntimeSettings.AndroidRuntimeSettings" , "StoreVersionOffsetArm64" , out StoreVersionOffsetArm64 ) ;
StoreVersionArm64 = StoreVersion + StoreVersionOffsetArm64 ;
DirectoryReference SymbolPathArm64 = DirectoryReference . Combine (
BuildProduct . OutputFile . Directory ,
Target . Name + "_Symbols_v" + StoreVersionArm64 . ToString ( ) ,
Target . Name + "-arm64" ) ;
string LaunchTaskName = String . Format ( "{0} {1} {2} Deploy" , Target . Name , BuildProduct . Platform , BuildProduct . Config ) ;
List < string > ConfigTypes = new List < string > ( ) ;
ConfigTypes . Add ( "Launch" ) ;
if ( BuildProduct . Config = = UnrealTargetConfiguration . Development )
{
ConfigTypes . Add ( "Attach" ) ;
}
foreach ( string ConfigType in ConfigTypes )
{
OutFile . BeginObject ( ) ;
{
OutFile . AddField ( "name" , Target . Name + " Oculus (" + BuildProduct . Config . ToString ( ) + ") " + ConfigType ) ;
OutFile . AddField ( "request" , ConfigType . ToLowerInvariant ( ) ) ;
if ( ConfigType = = "Launch" )
{
OutFile . AddField ( "preLaunchTask" , LaunchTaskName ) ;
}
OutFile . AddField ( "type" , "fb-lldb" ) ;
OutFile . BeginObject ( "android" ) ;
{
OutFile . BeginObject ( "application" ) ;
{
OutFile . AddField ( "package" , PackageName ) ;
OutFile . AddField ( "activity" , "com.epicgames.unreal.GameActivity" ) ;
}
OutFile . EndObject ( ) ;
OutFile . BeginObject ( "lldbConfig" ) ;
{
OutFile . BeginArray ( "librarySearchPaths" ) ;
OutFile . AddUnnamedField ( "\\\"" + SymbolPathArm64 . ToNormalizedPath ( ) + "\\\"" ) ;
OutFile . EndArray ( ) ;
OutFile . BeginArray ( "lldbPreTargetCreateCommands" ) ;
FileReference DataFormatters = FileReference . Combine ( ProjectRoot , "Engine" , "Extras" , "LLDBDataFormatters" , "UEDataFormatters_2ByteChars.py" ) ;
OutFile . AddUnnamedField ( "command script import \\\"" + DataFormatters . FullName . Replace ( "\\" , "/" ) + "\\\"" ) ;
OutFile . EndArray ( ) ;
OutFile . BeginArray ( "lldbPostTargetCreateCommands" ) ;
//on Oculus devices, we use SIGILL for input redirection, so the debugger shouldn't catch it.
OutFile . AddUnnamedField ( "process handle --pass true --stop false --notify true SIGILL" ) ;
OutFile . EndArray ( ) ;
}
OutFile . EndObject ( ) ;
}
OutFile . EndObject ( ) ;
}
OutFile . EndObject ( ) ;
}
}
private void WriteNativeLaunchConfig ( ProjectData . Project InProject , JsonFile OutFile , ProjectData . Target Target , ProjectData . BuildProduct BuildProduct )
{
bool bIsLinux = BuildProduct . Platform = = UnrealTargetPlatform . Linux ;
List < string > Types = new List < string > ( ) ;
Types . Add ( "Launch" ) ;
2023-05-30 18:38:07 -04:00
if ( bAddDebugAttachConfig & & bIsLinux )
2022-06-13 20:12:07 -04:00
{
Types . Add ( "Attach" ) ;
}
if ( bAddDebugCoreConfig & & bIsLinux )
{
Types . Add ( "Debug Core" ) ;
}
string LaunchTaskName = String . Format ( "{0} {1} {2} Build" , Target . Name , BuildProduct . Platform , BuildProduct . Config ) ;
2023-05-30 18:38:07 -04:00
foreach ( string Type in Types )
2022-06-13 20:12:07 -04:00
{
OutFile . BeginObject ( ) ;
{
OutFile . AddField ( "name" , Type + " " + Target . Name + " (" + BuildProduct . Config . ToString ( ) + ")" ) ;
2023-05-30 18:38:07 -04:00
OutFile . AddField ( "request" , ( Type = = "Attach" ) ? "attach" : "launch" ) ;
2022-06-13 20:12:07 -04:00
OutFile . AddField ( "program" , MakeUnquotedPathString ( BuildProduct . OutputFile , EPathType . Absolute ) ) ;
switch ( Type )
{
case "Launch" :
OutFile . AddField ( "preLaunchTask" , LaunchTaskName ) ;
break ;
case "Debug Core" :
OutFile . AddField ( "coreDumpPath" , "${input:coreFileName}" ) ;
break ;
2023-05-30 18:38:07 -04:00
case "Attach" :
2022-06-13 20:12:07 -04:00
OutFile . AddField ( "processId" , "${command:pickProcess}" ) ;
break ;
}
2023-05-30 18:38:07 -04:00
if ( Type ! = "Attach" )
2022-06-13 20:12:07 -04:00
{
OutFile . BeginArray ( "args" ) ;
{
if ( Target . Type = = TargetRules . TargetType . Editor )
{
if ( InProject . Name ! = "UE5" )
{
if ( bForeignProject )
{
OutFile . AddUnnamedField ( MakePathString ( BuildProduct . UProjectFile ! , false , true ) ) ;
}
else
{
OutFile . AddUnnamedField ( InProject . Name ) ;
}
}
}
}
OutFile . EndArray ( ) ;
}
/ *
DirectoryReference CWD = BuildProduct . OutputFile . Directory ;
while ( HostPlatform = = UnrealTargetPlatform . Mac & & CWD ! = null & & CWD . ToString ( ) . Contains ( ".app" ) )
{
CWD = CWD . ParentDirectory ;
}
if ( CWD ! = null )
{
OutFile . AddField ( "cwd" , MakePathString ( CWD , true , true ) ) ;
}
* /
OutFile . AddField ( "cwd" , MakeUnquotedPathString ( ProjectRoot , EPathType . Absolute ) ) ;
if ( HostPlatform = = UnrealTargetPlatform . Win64 )
{
OutFile . AddField ( "stopAtEntry" , false ) ;
2024-02-13 13:25:37 -05:00
OutFile . AddField ( "console" , "integratedTerminal" ) ;
2022-06-13 20:12:07 -04:00
OutFile . AddField ( "type" , "cppvsdbg" ) ;
OutFile . AddField ( "visualizerFile" , MakeUnquotedPathString ( FileReference . Combine ( ProjectRoot , "Engine" , "Extras" , "VisualStudioDebugging" , "Unreal.natvis" ) , EPathType . Absolute ) ) ;
}
else if ( HostPlatform = = UnrealTargetPlatform . Linux )
{
OutFile . AddField ( "type" , "cppdbg" ) ;
OutFile . AddField ( "visualizerFile" , MakeUnquotedPathString ( FileReference . Combine ( ProjectRoot , "Engine" , "Extras" , "VisualStudioDebugging" , "Unreal.natvis" ) , EPathType . Absolute ) ) ;
OutFile . AddField ( "showDisplayString" , true ) ;
}
else
{
OutFile . AddField ( "type" , "lldb" ) ;
}
if ( UnrealBuildTool . OriginalCompilationRootDirectory ! = ProjectRoot )
{
OutFile . BeginObject ( "sourceFileMap" ) ;
{
OutFile . AddField (
MakeUnquotedPathString ( UnrealBuildTool . OriginalCompilationRootDirectory , EPathType . Absolute ) ,
MakeUnquotedPathString ( ProjectRoot , EPathType . Absolute ) ) ;
}
OutFile . EndObject ( ) ;
}
}
OutFile . EndObject ( ) ;
}
}
private void WriteNativeLaunchConfig ( ProjectData . Project InProject , JsonFile OutFile , ILogger Logger )
{
foreach ( ProjectData . Target Target in InProject . Targets )
{
foreach ( ProjectData . BuildProduct BuildProduct in Target . BuildProducts )
{
if ( BuildProduct . Platform = = HostPlatform )
{
WriteNativeLaunchConfig ( InProject , OutFile , Target , BuildProduct ) ;
}
else if ( BuildProduct . Platform = = UnrealTargetPlatform . Android )
{
WriteNativeLaunchConfigAndroidOculus ( InProject , OutFile , Target , BuildProduct , Logger ) ;
}
}
}
}
private void WriteSingleCSharpLaunchConfig ( JsonFile OutFile , string InTaskName , string InBuildTaskName , FileReference InExecutable , string [ ] ? InArgs )
{
OutFile . BeginObject ( ) ;
{
OutFile . AddField ( "name" , InTaskName ) ;
OutFile . AddField ( "type" , "coreclr" ) ;
OutFile . AddField ( "request" , "launch" ) ;
2023-05-30 18:59:32 -04:00
if ( ! String . IsNullOrEmpty ( InBuildTaskName ) )
2022-06-13 20:12:07 -04:00
{
OutFile . AddField ( "preLaunchTask" , InBuildTaskName ) ;
}
2023-05-30 18:38:07 -04:00
2022-06-13 20:12:07 -04:00
DirectoryReference CWD = ProjectRoot ;
OutFile . AddField ( "program" , MakeUnquotedPathString ( InExecutable , EPathType . Absolute ) ) ;
OutFile . BeginArray ( "args" ) ;
{
if ( InArgs ! = null )
{
foreach ( string Arg in InArgs )
{
OutFile . AddUnnamedField ( Arg ) ;
}
}
}
OutFile . EndArray ( ) ;
2024-08-22 18:03:04 -04:00
OutFile . BeginObject ( "env" ) ;
{
OutFile . AddField ( "PATH" , $"{Unreal.DotnetDirectory}{Path.PathSeparator}${{env:PATH}}" ) ;
OutFile . AddField ( "DOTNET_ROOT" , Unreal . DotnetDirectory . FullName ) ;
OutFile . AddField ( "DOTNET_HOST_PATH" , Unreal . DotnetPath . FullName ) ;
OutFile . AddField ( "DOTNET_MULTILEVEL_LOOKUP" , "0" ) ;
OutFile . AddField ( "DOTNET_ROLL_FORWARD" , "LatestMajor" ) ;
}
OutFile . EndObject ( ) ;
2022-06-13 20:12:07 -04:00
if ( HostPlatform = = UnrealTargetPlatform . Win64 )
{
2024-02-13 13:25:37 -05:00
OutFile . AddField ( "console" , "integratedTerminal" ) ;
2022-06-13 20:12:07 -04:00
}
else
{
OutFile . AddField ( "console" , "internalConsole" ) ;
OutFile . AddField ( "internalConsoleOptions" , "openOnSessionStart" ) ;
}
OutFile . AddField ( "stopAtEntry" , false ) ;
OutFile . AddField ( "cwd" , MakeUnquotedPathString ( CWD , EPathType . Absolute ) ) ;
}
OutFile . EndObject ( ) ;
}
private void WriteCSharpLaunchConfig ( ProjectData . Project InProject , JsonFile OutFile )
{
foreach ( ProjectData . Target Target in InProject . Targets )
{
foreach ( ProjectData . BuildProduct BuildProduct in Target . BuildProducts )
{
if ( BuildProduct . OutputType = = ProjectData . EOutputType . Exe )
{
string TaskName = String . Format ( "{0} ({1})" , Target . Name , BuildProduct . Config ) ;
string BuildTaskName = String . Format ( "{0} {1} {2} Build" , Target . Name , HostPlatform , BuildProduct . Config ) ;
2024-08-22 18:03:04 -04:00
string [ ] ? Args = null ;
if ( Target . Name . Equals ( "AutomationTool" , StringComparison . OrdinalIgnoreCase ) ) {
BuildTaskName = String . Format ( "{0}+Scripts {1} {2} Build" , Target . Name , HostPlatform , BuildProduct . Config ) ;
Args = new string [ ] { "-NoCompile" } ;
}
2022-06-13 20:12:07 -04:00
2024-08-22 18:03:04 -04:00
WriteSingleCSharpLaunchConfig ( OutFile , TaskName , BuildTaskName , BuildProduct . OutputFile , Args ) ;
2022-06-13 20:12:07 -04:00
}
}
}
}
2023-05-12 17:40:11 -04:00
private void WriteLaunch ( JsonFile OutFile , ProjectData ProjectData , ILogger Logger )
2022-06-13 20:12:07 -04:00
{
2023-05-12 17:40:11 -04:00
OutFile . AddField ( "version" , "0.2.0" ) ;
2023-05-30 18:38:07 -04:00
if ( bAddDebugCoreConfig )
2022-06-13 20:12:07 -04:00
{
2023-05-12 17:40:11 -04:00
OutFile . BeginArray ( "inputs" ) ;
OutFile . BeginObject ( ) ;
OutFile . AddField ( "id" , "coreFileName" ) ;
OutFile . AddField ( "type" , "command" ) ;
OutFile . AddField ( "command" , "filePicker.pick" ) ;
OutFile . BeginObject ( "args" ) ;
OutFile . AddField ( "masks" , "core*" ) ;
OutFile . BeginObject ( "display" ) ;
OutFile . AddField ( "type" , "fileRelativePath" ) ;
OutFile . AddField ( "detail" , "filePath" ) ;
OutFile . EndObject ( ) ;
OutFile . AddField ( "output" , "filePath" ) ;
OutFile . EndObject ( ) ;
OutFile . EndObject ( ) ;
2022-06-13 20:12:07 -04:00
OutFile . EndArray ( ) ;
}
2023-05-12 17:40:11 -04:00
OutFile . BeginArray ( "configurations" ) ;
{
if ( ! bUseVSCodeExtension )
{
foreach ( ProjectData . Project Project in ProjectData . NativeProjects )
{
WriteNativeLaunchConfig ( Project , OutFile , Logger ) ;
}
}
foreach ( ProjectData . Project Project in ProjectData . CSharpProjects )
{
WriteCSharpLaunchConfig ( Project , OutFile ) ;
}
}
// Add in a special task for regenerating project files
string PreLaunchTask = "" ;
List < string > Args = new List < string > ( ) ;
Args . Add ( "-projectfiles" ) ;
Args . Add ( "-vscode" ) ;
if ( bGeneratingGameProjectFiles )
{
Args . Add ( "-project=" + MakeUnquotedPathString ( OnlyGameProject ! , EPathType . Absolute ) ) ;
Args . Add ( "-game" ) ;
}
if ( bIncludeEngineSource )
{
Args . Add ( "-engine" ) ;
}
2023-05-30 18:38:07 -04:00
2023-05-12 17:40:11 -04:00
if ( bIncludeDotNetPrograms )
{
Args . Add ( "-dotnet" ) ;
PreLaunchTask = "UnrealBuildTool " + HostPlatform . ToString ( ) + " Development Build" ;
}
FileReference RunUbtPath = FileReference . Combine ( ProjectRoot , "Engine" , "Build" , "BatchFiles" , "RunUBT.bat" ) ;
WriteSingleCSharpLaunchConfig (
OutFile ,
"Generate Project Files" ,
PreLaunchTask ,
RunUbtPath ,
Args . ToArray ( )
) ;
OutFile . EndArray ( ) ;
2022-06-13 20:12:07 -04:00
}
private void WriteWorkspaceIgnoreFile ( List < ProjectFile > Projects )
{
List < string > PathsToExclude = new List < string > ( ) ;
foreach ( ProjectFile Project in Projects )
{
bool bFoundTarget = false ;
foreach ( ProjectTarget Target in Project . ProjectTargets . OfType < ProjectTarget > ( ) )
{
if ( Target . TargetFilePath ! = null )
{
DirectoryReference ProjDir = Target . TargetFilePath . Directory . GetDirectoryName ( ) = = "Source" ? Target . TargetFilePath . Directory . ParentDirectory ! : Target . TargetFilePath . Directory ;
GetExcludePathsCPP ( ProjDir , PathsToExclude ) ;
2023-05-30 18:38:07 -04:00
2022-06-13 20:12:07 -04:00
DirectoryReference PluginRootDir = DirectoryReference . Combine ( ProjDir , "Plugins" ) ;
WriteWorkspaceIgnoreFileForPlugins ( PluginRootDir , PathsToExclude ) ;
bFoundTarget = true ;
}
}
if ( ! bFoundTarget )
{
GetExcludePathsCSharp ( Project . ProjectFilePath . Directory . ToString ( ) , PathsToExclude ) ;
}
}
StringBuilder OutFile = new StringBuilder ( ) ;
if ( ! IncludeAllFiles )
{
// TODO: Adding ignore patterns to .ignore hides files from Open File Dialog but it does not hide them in the File Explorer
// but using files.exclude with our full set of excludes breaks vscode for larger code bases so a verbose file explorer
// seems like less of an issue and thus we are not adding these to files.exclude.
// see https://github.com/microsoft/vscode/issues/109380 for discussions with vscode team
DirectoryReference WorkspaceRoot = bForeignProject ? Projects [ 0 ] . BaseDir : Unreal . RootDirectory ;
string WorkspaceRootPath = WorkspaceRoot . ToString ( ) . Replace ( '\\' , '/' ) + "/" ;
if ( ! bForeignProject )
{
OutFile . AppendLine ( ".vscode" ) ;
}
foreach ( string PathToExclude in PathsToExclude )
{
2023-01-03 16:07:10 -05:00
OutFile . AppendLine ( PathToExclude . Replace ( '\\' , '/' ) . Replace ( WorkspaceRootPath , "/" ) ) ;
2022-06-13 20:12:07 -04:00
}
}
FileReference . WriteAllText ( FileReference . Combine ( PrimaryProjectPath , ".ignore" ) , OutFile . ToString ( ) ) ;
}
private void WriteWorkspaceIgnoreFileForPlugins ( DirectoryReference PluginBaseDir , List < string > PathsToExclude )
{
if ( DirectoryReference . Exists ( PluginBaseDir ) )
{
foreach ( DirectoryReference SubDir in DirectoryReference . EnumerateDirectories ( PluginBaseDir , "*" , SearchOption . TopDirectoryOnly ) )
{
string [ ] UPluginFiles = Directory . GetFiles ( SubDir . ToString ( ) , "*.uplugin" ) ;
if ( UPluginFiles . Length = = 1 )
{
DirectoryReference PluginDir = SubDir ;
GetExcludePathsCPP ( PluginDir , PathsToExclude ) ;
}
else
{
WriteWorkspaceIgnoreFileForPlugins ( SubDir , PathsToExclude ) ;
}
}
}
}
2023-05-30 18:38:07 -04:00
2023-05-12 17:40:11 -04:00
private void WriteWorkspaceFile ( ProjectData ProjectData , ILogger Logger )
2022-06-13 20:12:07 -04:00
{
JsonFile WorkspaceFile = new JsonFile ( ) ;
WorkspaceFile . BeginRootObject ( ) ;
{
WorkspaceFile . BeginArray ( "folders" ) ;
{
// Add the directory in which which the code-workspace file exists.
// This is also known as ${workspaceRoot}
WorkspaceFile . BeginObject ( ) ;
{
string ProjectName = bForeignProject ? GameProjectName ! : "UE5" ;
WorkspaceFile . AddField ( "name" , ProjectName ) ;
WorkspaceFile . AddField ( "path" , "." ) ;
}
WorkspaceFile . EndObject ( ) ;
// If this project is outside the engine folder, add the root engine directory
if ( bIncludeEngineSource & & bForeignProject )
{
WorkspaceFile . BeginObject ( ) ;
{
WorkspaceFile . AddField ( "name" , "UE5" ) ;
WorkspaceFile . AddField ( "path" , MakeUnquotedPathString ( Unreal . RootDirectory , EPathType . Absolute ) ) ;
}
WorkspaceFile . EndObject ( ) ;
}
}
WorkspaceFile . EndArray ( ) ;
}
WorkspaceFile . BeginObject ( "settings" ) ;
{
// disable autodetect for typescript files to workaround slowdown in vscode as a result of parsing all files
WorkspaceFile . AddField ( "typescript.tsc.autoDetect" , "off" ) ;
2023-02-17 22:39:26 -05:00
// disable npm script autodetect to avoid lag populating tasks list
WorkspaceFile . AddField ( "npm.autoDetect" , "off" ) ;
2024-08-22 18:03:04 -04:00
// set environment variables so correct dotnet is used
WorkspaceFile . BeginObject ( $"terminal.integrated.env.{(OperatingSystem.IsWindows() ? " windows " : OperatingSystem.IsMacOS() ? " mac " : " linux ")}" ) ;
{
WorkspaceFile . AddField ( "PATH" , $"{Unreal.DotnetDirectory}{Path.PathSeparator}${{env:PATH}}" ) ;
WorkspaceFile . AddField ( "DOTNET_ROOT" , Unreal . DotnetDirectory . FullName ) ;
WorkspaceFile . AddField ( "DOTNET_HOST_PATH" , Unreal . DotnetPath . FullName ) ;
WorkspaceFile . AddField ( "DOTNET_MULTILEVEL_LOOKUP" , "0" ) ;
WorkspaceFile . AddField ( "DOTNET_ROLL_FORWARD" , "LatestMajor" ) ;
}
WorkspaceFile . EndObject ( ) ;
2023-02-17 22:39:26 -05:00
if ( bUseVSCodeExtension )
{
if ( HostPlatform = = UnrealTargetPlatform . Win64 )
{
2023-03-07 00:29:05 -05:00
WorkspaceFile . AddField ( "UE.UBTScriptPath" , MakePathString ( FileReference . Combine ( ProjectRoot , "Engine" , "Build" , "BatchFiles" , "RunUBT.bat" ) , true ) ) ;
2023-02-17 22:39:26 -05:00
}
else
{
2023-03-07 00:29:05 -05:00
WorkspaceFile . AddField ( "UE.UBTScriptPath" , MakePathString ( FileReference . Combine ( ProjectRoot , "Engine" , "Build" , "BatchFiles" , HostPlatform . ToString ( ) , "RunUBT.sh" ) , true ) ) ;
2023-02-17 22:39:26 -05:00
}
2023-03-07 00:29:05 -05:00
// Exclude some large directories/filetypes by default
WorkspaceFile . BeginObject ( "files.exclude" ) ;
WorkspaceFile . AddField ( "**/Intermediate/" , true ) ;
WorkspaceFile . AddField ( "**/Binaries/" , true ) ;
WorkspaceFile . AddField ( "Engine/DerivedDataCache" , true ) ;
WorkspaceFile . AddField ( "**/*.uasset" , true ) ;
WorkspaceFile . AddField ( "**/*.umap" , true ) ;
WorkspaceFile . AddField ( "**/*.uexp" , true ) ;
WorkspaceFile . AddField ( "**/*.upayload" , true ) ;
WorkspaceFile . AddField ( "**/*.ubulk" , true ) ;
WorkspaceFile . AddField ( "**/*.m.ubulk" , true ) ;
WorkspaceFile . AddField ( "**/*.uptnl" , true ) ;
WorkspaceFile . EndObject ( ) ;
WorkspaceFile . BeginObject ( "search.exclude" ) ;
WorkspaceFile . EndObject ( ) ;
2023-02-17 22:39:26 -05:00
}
2022-06-13 20:12:07 -04:00
}
WorkspaceFile . EndObject ( ) ;
2023-05-30 18:38:07 -04:00
2022-06-13 20:12:07 -04:00
WorkspaceFile . BeginObject ( "extensions" ) ;
{
// extensions is a set of recommended extensions that a user should install.
// Adding this section aids discovery of extensions which are helpful to have installed for Unreal development.
WorkspaceFile . BeginArray ( "recommendations" ) ;
{
2022-10-28 12:59:39 -04:00
// Add when/if published to marketplace.
// WorkspaceFile.AddUnnamedField("epic.vscode-ue");
2022-06-13 20:12:07 -04:00
WorkspaceFile . AddUnnamedField ( "ms-vscode.cpptools" ) ;
WorkspaceFile . AddUnnamedField ( "ms-dotnettools.csharp" ) ;
2023-02-17 22:39:26 -05:00
if ( bUseVSCodeExtension )
{
WorkspaceFile . AddUnnamedField ( "epic.vscode-ue" ) ;
}
2022-06-13 20:12:07 -04:00
}
WorkspaceFile . EndArray ( ) ;
}
WorkspaceFile . EndObject ( ) ;
2023-05-12 17:40:11 -04:00
WorkspaceFile . BeginObject ( "tasks" ) ;
WriteTasks ( WorkspaceFile , ProjectData ) ;
WorkspaceFile . EndObject ( ) ;
WorkspaceFile . BeginObject ( "launch" ) ;
WriteLaunch ( WorkspaceFile , ProjectData , Logger ) ;
WorkspaceFile . EndObject ( ) ;
2022-06-13 20:12:07 -04:00
WorkspaceFile . EndRootObject ( ) ;
2023-03-10 22:22:51 -05:00
string? WorkspaceName = bForeignProject ? GameProjectName : PrimaryProjectName ;
2022-06-13 20:12:07 -04:00
WorkspaceFile . Write ( FileReference . Combine ( PrimaryProjectPath , WorkspaceName + ".code-workspace" ) ) ;
}
private void GetExcludePathsCPP ( DirectoryReference BaseDir , List < string > PathsToExclude )
{
string [ ] DirAllowList = { "Binaries" , "Build" , "Config" , "Plugins" , "Source" , "Private" , "Public" , "Internal" , "Classes" , "Resources" } ;
foreach ( DirectoryReference SubDir in DirectoryReference . EnumerateDirectories ( BaseDir , "*" , SearchOption . TopDirectoryOnly ) )
{
if ( Array . Find ( DirAllowList , Dir = > Dir = = SubDir . GetDirectoryName ( ) ) = = null )
{
string NewSubDir = SubDir . ToString ( ) ;
if ( ! PathsToExclude . Contains ( NewSubDir ) )
{
PathsToExclude . Add ( NewSubDir ) ;
}
}
}
}
private void GetExcludePathsCSharp ( string BaseDir , List < string > PathsToExclude )
{
string [ ] DenyList =
{
"obj" ,
"bin"
} ;
foreach ( string DenyListDir in DenyList )
{
string ExcludePath = Path . Combine ( BaseDir , DenyListDir ) ;
if ( ! PathsToExclude . Contains ( ExcludePath ) )
{
PathsToExclude . Add ( ExcludePath ) ;
}
}
}
}
}