2022-06-01 16:41:24 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
using System ;
using System.Collections.Generic ;
using System.Text.RegularExpressions ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
using Microsoft.Win32 ;
using EpicGames.Core ;
using UnrealBuildBase ;
using Microsoft.Extensions.Logging ;
namespace UnrealBuildTool
{
class LinuxToolChain : ClangToolChain
{
2022-07-11 16:55:46 -04:00
protected class LinuxToolChainInfo : ClangToolChainInfo
{
// cache the location of NDK tools
public bool bIsCrossCompiling { get ; init ; }
public DirectoryReference ? BaseLinuxPath { get ; init ; }
public DirectoryReference ? MultiArchRoot { get ; init ; }
public FileReference Objcopy { get ; init ; }
public FileReference DumpSyms { get ; init ; }
public FileReference BreakpadEncoder { get ; init ; }
public LinuxToolChainInfo ( DirectoryReference ? BaseLinuxPath , DirectoryReference ? MultiArchRoot , FileReference Clang , FileReference Archiver , FileReference Objcopy , ILogger Logger )
: base ( Clang , Archiver , Logger )
{
this . BaseLinuxPath = BaseLinuxPath ;
this . MultiArchRoot = MultiArchRoot ;
this . Objcopy = Objcopy ;
// these are supplied by the engine and do not change depending on the circumstances
DumpSyms = FileReference . Combine ( Unreal . EngineDirectory , "Binaries" , "Linux" , $"dump_syms{BuildHostPlatform.Current.BinarySuffix}" ) ;
BreakpadEncoder = FileReference . Combine ( Unreal . EngineDirectory , "Binaries" , "Linux" , $"BreakpadSymbolEncoder{BuildHostPlatform.Current.BinarySuffix}" ) ;
}
}
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
/** Flavor of the current build (will map to target triplet)*/
UnrealArch Architecture ;
2022-06-01 16:41:24 -04:00
/** Pass --gdb-index option to linker to generate .gdb_index section. */
protected bool bGdbIndexSection = true ;
/** Allows you to override the maximum binary size allowed to be passed to objcopy.exe when cross building on Windows. */
/** Max value is 2GB, due to bat file limitation */
protected UInt64 MaxBinarySizeOverrideForObjcopy = 0 ;
/** Platform SDK to use */
protected LinuxPlatformSDK PlatformSDK ;
2022-07-11 16:55:46 -04:00
protected LinuxToolChainInfo LinuxInfo = > ( Info as LinuxToolChainInfo ) ! ;
2022-06-01 16:41:24 -04:00
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
public LinuxToolChain ( UnrealArch InArchitecture , LinuxPlatformSDK InSDK , ClangToolChainOptions InOptions , ILogger InLogger )
2022-06-01 16:41:24 -04:00
: this ( UnrealTargetPlatform . Linux , InArchitecture , InSDK , InOptions , InLogger )
{
2022-07-11 16:55:46 -04:00
// prevent unknown clangs since the build is likely to fail on too old or too new compilers
2023-01-23 16:32:46 -05:00
if ( CompilerVersionLessThan ( 15 , 0 , 0 ) | | CompilerVersionGreaterOrEqual ( 16 , 0 , 0 ) )
2022-07-11 16:55:46 -04:00
{
throw new BuildException (
2023-01-23 16:32:46 -05:00
string . Format ( "This version of the Unreal Engine can only be compiled with clang 15.0. clang {0} may not build it - please use a different version." ,
2022-07-11 16:55:46 -04:00
Info . ClangVersion )
) ;
}
}
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
public LinuxToolChain ( UnrealTargetPlatform InPlatform , UnrealArch InArchitecture , LinuxPlatformSDK InSDK , ClangToolChainOptions InOptions , ILogger InLogger )
2022-07-11 16:55:46 -04:00
: base ( InOptions , InLogger )
{
Architecture = InArchitecture ;
PlatformSDK = InSDK ;
}
protected override ClangToolChainInfo GetToolChainInfo ( )
{
DirectoryReference ? MultiArchRoot = PlatformSDK . GetSDKLocation ( ) ;
DirectoryReference ? BaseLinuxPath = PlatformSDK . GetBaseLinuxPathForArchitecture ( Architecture ) ;
2022-06-01 16:41:24 -04:00
bool bForceUseSystemCompiler = PlatformSDK . ForceUseSystemCompiler ( ) ;
if ( bForceUseSystemCompiler )
{
// use native linux toolchain
2022-07-11 16:55:46 -04:00
FileReference ? ClangPath = FileReference . FromString ( LinuxCommon . WhichClang ( Logger ) ) ;
FileReference ? LlvmArPath = FileReference . FromString ( LinuxCommon . Which ( "llvm-ar" , Logger ) ) ;
FileReference ? ObjcopyPath = FileReference . FromString ( LinuxCommon . Which ( "llvm-objcopy" , Logger ) ) ;
if ( ClangPath = = null )
{
throw new BuildException ( "Unable to find system clang; cannot instantiate Linux toolchain" ) ;
}
else if ( LlvmArPath = = null )
{
throw new BuildException ( "Unable to find system llvm-ar; cannot instantiate Linux toolchain" ) ;
}
else if ( ObjcopyPath = = null )
{
throw new BuildException ( "Unable to find system llvm-objcopy; cannot instantiate Linux toolchain" ) ;
}
2022-06-01 16:41:24 -04:00
// When compiling on Linux, use a faster way to relink circularly dependent libraries.
// Race condition between actions linking to the .so and action overwriting it is avoided thanks to inodes
bUseFixdeps = false ;
bIsCrossCompiling = false ;
2022-07-11 16:55:46 -04:00
return new LinuxToolChainInfo ( null , null , ClangPath , LlvmArPath , ObjcopyPath , Logger ) ;
2022-06-01 16:41:24 -04:00
}
else
{
2022-06-27 20:18:23 -04:00
if ( BaseLinuxPath = = null )
2022-06-01 16:41:24 -04:00
{
throw new BuildException ( "LINUX_MULTIARCH_ROOT environment variable is not set; cannot instantiate Linux toolchain" ) ;
}
2022-06-27 20:18:23 -04:00
if ( MultiArchRoot = = null )
2022-06-01 16:41:24 -04:00
{
MultiArchRoot = BaseLinuxPath ;
Logger . LogInformation ( "Using LINUX_ROOT (deprecated, consider LINUX_MULTIARCH_ROOT)" ) ;
}
// set up the path to our toolchain
2022-07-11 16:55:46 -04:00
FileReference ClangPath = FileReference . Combine ( BaseLinuxPath , "bin" , $"clang++{BuildHostPlatform.Current.BinarySuffix}" ) ;
FileReference LlvmArPath = FileReference . Combine ( BaseLinuxPath , "bin" , $"llvm-ar{BuildHostPlatform.Current.BinarySuffix}" ) ;
FileReference ObjcopyPath = FileReference . Combine ( BaseLinuxPath , "bin" , $"llvm-objcopy{BuildHostPlatform.Current.BinarySuffix}" ) ;
2022-06-01 16:41:24 -04:00
// When cross-compiling on Windows, use old FixDeps. It is slow, but it does not have timing issues
bUseFixdeps = BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Win64 ;
if ( BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Linux )
{
Environment . SetEnvironmentVariable ( "LC_ALL" , "C" ) ;
}
bIsCrossCompiling = true ;
2022-07-11 16:55:46 -04:00
return new LinuxToolChainInfo ( BaseLinuxPath , MultiArchRoot , ClangPath , LlvmArPath , ObjcopyPath , Logger ) ;
2022-06-01 16:41:24 -04:00
}
}
protected virtual bool CrossCompiling ( )
{
return bIsCrossCompiling ;
}
protected internal virtual string GetDumpEncodeDebugCommand ( LinkEnvironment LinkEnvironment , FileItem OutputFile )
{
bool bUseCmdExe = BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Win64 ;
string DumpCommand = bUseCmdExe ? "\"{0}\" \"{1}\" \"{2}\" 2>NUL" : "\"{0}\" -c -o \"{2}\" \"{1}\"" ;
FileItem EncodedBinarySymbolsFile = FileItem . GetItemByPath ( Path . Combine ( LinkEnvironment . OutputDirectory ! . FullName , OutputFile . Location . GetFileNameWithoutExtension ( ) + ".sym" ) ) ;
FileItem SymbolsFile = FileItem . GetItemByPath ( Path . Combine ( LinkEnvironment . LocalShadowDirectory ! . FullName , OutputFile . Location . GetFileName ( ) + ".psym" ) ) ;
FileItem StrippedFile = FileItem . GetItemByPath ( Path . Combine ( LinkEnvironment . LocalShadowDirectory . FullName , OutputFile . Location . GetFileName ( ) + "_nodebug" ) ) ;
FileItem DebugFile = FileItem . GetItemByPath ( Path . Combine ( LinkEnvironment . OutputDirectory . FullName , OutputFile . Location . GetFileNameWithoutExtension ( ) + ".debug" ) ) ;
if ( Options . HasFlag ( ClangToolChainOptions . PreservePSYM ) )
{
SymbolsFile = FileItem . GetItemByPath ( Path . Combine ( LinkEnvironment . OutputDirectory . FullName , OutputFile . Location . GetFileNameWithoutExtension ( ) + ".psym" ) ) ;
}
StringWriter Out = new StringWriter ( ) ;
Out . NewLine = bUseCmdExe ? "\r\n" : "\n" ;
// dump_syms
Out . WriteLine ( DumpCommand ,
2022-07-11 16:55:46 -04:00
LinuxInfo . DumpSyms ,
2022-06-01 16:41:24 -04:00
OutputFile . AbsolutePath ,
SymbolsFile . AbsolutePath
) ;
// encode breakpad symbols
Out . WriteLine ( "\"{0}\" \"{1}\" \"{2}\"" ,
2022-07-11 16:55:46 -04:00
LinuxInfo . BreakpadEncoder ,
2022-06-01 16:41:24 -04:00
SymbolsFile . AbsolutePath ,
EncodedBinarySymbolsFile . AbsolutePath
) ;
if ( ! Options . HasFlag ( ClangToolChainOptions . DisableSplitDebugInfoWithObjCopy ) & & LinkEnvironment . bCreateDebugInfo )
{
if ( MaxBinarySizeOverrideForObjcopy > 0 & & bUseCmdExe )
{
Out . WriteLine ( "for /F \"tokens=*\" %%F in (\"{0}\") DO set size=%%~zF" ,
OutputFile . AbsolutePath
) ;
Out . WriteLine ( "if %size% LSS {0} (" , MaxBinarySizeOverrideForObjcopy ) ;
}
// objcopy stripped file
Out . WriteLine ( "\"{0}\" --strip-all \"{1}\" \"{2}\"" ,
2022-07-11 16:55:46 -04:00
LinuxInfo . Objcopy ,
2022-06-01 16:41:24 -04:00
OutputFile . AbsolutePath ,
StrippedFile . AbsolutePath
) ;
// objcopy debug file
Out . WriteLine ( "\"{0}\" --only-keep-debug \"{1}\" \"{2}\"" ,
2022-07-11 16:55:46 -04:00
LinuxInfo . Objcopy ,
2022-06-01 16:41:24 -04:00
OutputFile . AbsolutePath ,
DebugFile . AbsolutePath
) ;
// objcopy link debug file to final so
Out . WriteLine ( "\"{0}\" --add-gnu-debuglink=\"{1}\" \"{2}\" \"{3}.temp\"" ,
2022-07-11 16:55:46 -04:00
LinuxInfo . Objcopy ,
2022-06-01 16:41:24 -04:00
DebugFile . AbsolutePath ,
StrippedFile . AbsolutePath ,
OutputFile . AbsolutePath
) ;
if ( bUseCmdExe )
{
// Only move the temp final elf file once its done being linked by objcopy
Out . WriteLine ( "move /Y \"{0}.temp\" \"{1}\"" ,
OutputFile . AbsolutePath ,
OutputFile . AbsolutePath
) ;
if ( MaxBinarySizeOverrideForObjcopy > 0 )
{
// If we have an override size, then we need to create a dummy file if that size is exceeded
Out . WriteLine ( ") ELSE (" ) ;
Out . WriteLine ( "echo DummyDebug >> \"{0}\"" , DebugFile . AbsolutePath ) ;
Out . WriteLine ( ")" ) ;
}
}
else
{
// Only move the temp final elf file once its done being linked by objcopy
Out . WriteLine ( "mv \"{0}.temp\" \"{1}\"" ,
OutputFile . AbsolutePath ,
OutputFile . AbsolutePath
) ;
// Change the debug file to normal permissions. It was taking on the +x rights from the output file
Out . WriteLine ( "chmod 644 \"{0}\"" ,
DebugFile . AbsolutePath
) ;
}
}
else
{
// If we have disabled objcopy then we need to create a dummy debug file
Out . WriteLine ( "echo DummyDebug >> \"{0}\"" ,
DebugFile . AbsolutePath
) ;
}
return Out . ToString ( ) ;
}
/// <summary>
/// Architecture-specific compiler switches
/// </summary>
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
static string ArchitectureSpecificSwitches ( UnrealArch Architecture )
2022-06-01 16:41:24 -04:00
{
string Result = "" ;
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
if ( Architecture = = UnrealArch . Arm64 )
2022-06-01 16:41:24 -04:00
{
Result + = "-fsigned-char" ;
}
return Result ;
}
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
private static bool ShouldUseLibcxx ( )
2022-06-01 16:41:24 -04:00
{
// set UE_LINUX_USE_LIBCXX to either 0 or 1. If unset, defaults to 1.
string? UseLibcxxEnvVarOverride = Environment . GetEnvironmentVariable ( "UE_LINUX_USE_LIBCXX" ) ;
if ( string . IsNullOrEmpty ( UseLibcxxEnvVarOverride ) | | UseLibcxxEnvVarOverride = = "1" )
{
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
return true ;
2022-06-01 16:41:24 -04:00
}
return false ;
}
/// <inheritdoc/>
protected override void GetCompileArguments_WarningsAndErrors ( CppCompileEnvironment CompileEnvironment , List < string > Arguments )
{
base . GetCompileArguments_WarningsAndErrors ( CompileEnvironment , Arguments ) ;
//Arguments.Add("-Wunreachable-code"); // additional warning not normally included in Wall: warns if there is code that will never be executed - not helpful due to bIsGCC and similar
Arguments . Add ( "-Wno-undefined-bool-conversion" ) ; // hides checking if 'this' pointer is null
}
2022-06-02 13:13:59 -04:00
/// <inheritdoc/>
protected override void GetCompileArguments_Optimizations ( CppCompileEnvironment CompileEnvironment , List < string > Arguments )
{
base . GetCompileArguments_Optimizations ( CompileEnvironment , Arguments ) ;
// Unlike on other platforms, allow LTO be specified independently of PGO
if ( CompileEnvironment . bAllowLTCG )
{
if ( ( Options & ClangToolChainOptions . EnableThinLTO ) ! = 0 )
{
Arguments . Add ( "-flto=thin" ) ;
}
else
{
Arguments . Add ( "-flto" ) ;
}
}
// optimization level
if ( ! CompileEnvironment . bOptimizeCode )
{
Arguments . Add ( "-O0" ) ;
}
else
{
// Don't over optimise if using Address/MemorySanitizer or you'll get false positive errors due to erroneous optimisation of necessary Address/MemorySanitizer instrumentation.
if ( Options . HasFlag ( ClangToolChainOptions . EnableAddressSanitizer ) | | Options . HasFlag ( ClangToolChainOptions . EnableMemorySanitizer ) )
{
Arguments . Add ( "-O1 -g" ) ;
// This enables __asan_default_options() in UnixCommonStartup.h which disables the leak detector
Arguments . Add ( "-DDISABLE_ASAN_LEAK_DETECTOR=1" ) ;
}
else if ( Options . HasFlag ( ClangToolChainOptions . EnableThreadSanitizer ) )
{
Arguments . Add ( "-O1 -g" ) ;
}
else
{
2022-08-23 12:44:36 -04:00
if ( CompileEnvironment . OptimizationLevel = = OptimizationMode . Size )
{
Arguments . Add ( "-Oz" ) ;
}
else if ( CompileEnvironment . OptimizationLevel = = OptimizationMode . SizeAndSpeed )
{
Arguments . Add ( "-Os" ) ;
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
if ( Architecture = = UnrealArch . Arm64 )
2022-08-23 12:44:36 -04:00
{
Arguments . Add ( "-moutline" ) ;
}
}
else
{
Arguments . Add ( "-O3" ) ;
}
2022-06-02 13:13:59 -04:00
}
}
bool bRetainFramePointers = CompileEnvironment . bRetainFramePointers
2022-10-26 11:44:54 -04:00
| | Options . HasFlag ( ClangToolChainOptions . EnableAddressSanitizer ) | | Options . HasFlag ( ClangToolChainOptions . EnableMemorySanitizer ) | | Options . HasFlag ( ClangToolChainOptions . EnableThreadSanitizer )
2022-06-02 13:13:59 -04:00
| | CompileEnvironment . Configuration = = CppConfiguration . Debug ;
if ( CompileEnvironment . Configuration = = CppConfiguration . Shipping )
{
if ( ! bRetainFramePointers )
{
Arguments . Add ( "-fomit-frame-pointer" ) ;
}
}
// switches to help debugging
else if ( CompileEnvironment . Configuration = = CppConfiguration . Debug )
{
Arguments . Add ( "-fno-inline" ) ; // disable inlining for better debuggability (e.g. callstacks, "skip file" in gdb)
Arguments . Add ( "-fstack-protector" ) ; // detect stack smashing
}
if ( bRetainFramePointers )
{
Arguments . Add ( "-fno-optimize-sibling-calls" ) ;
Arguments . Add ( "-fno-omit-frame-pointer" ) ;
}
}
/// <inheritdoc/>
protected override void GetCompileArguments_Debugging ( CppCompileEnvironment CompileEnvironment , List < string > Arguments )
{
base . GetCompileArguments_Debugging ( CompileEnvironment , Arguments ) ;
// debug info
// bCreateDebugInfo is normally set for all configurations, including Shipping - this is needed to enable callstack in Shipping builds (proper resolution: UEPLAT-205, separate files with debug info)
if ( CompileEnvironment . bCreateDebugInfo )
{
Arguments . Add ( "-gdwarf-4" ) ;
if ( bGdbIndexSection )
{
// Generate .debug_pubnames and .debug_pubtypes sections in a format suitable for conversion into a
// GDB index. This option is only useful with a linker that can produce GDB index version 7.
Arguments . Add ( "-ggnu-pubnames" ) ;
}
if ( Options . HasFlag ( ClangToolChainOptions . TuneDebugInfoForLLDB ) )
{
Arguments . Add ( "-glldb" ) ;
}
}
if ( CompileEnvironment . bHideSymbolsByDefault )
{
Arguments . Add ( "-fvisibility-ms-compat" ) ;
Arguments . Add ( "-fvisibility-inlines-hidden" ) ;
}
}
2022-06-06 17:28:34 -04:00
/// <inheritdoc/>
protected override void GetCompilerArguments_Sanitizers ( CppCompileEnvironment CompileEnvironment , List < string > Arguments )
{
base . GetCompilerArguments_Sanitizers ( CompileEnvironment , Arguments ) ;
// ASan
if ( Options . HasFlag ( ClangToolChainOptions . EnableAddressSanitizer ) )
{
// Force using the ANSI allocator if ASan is enabled
Arguments . Add ( "-DFORCE_ANSI_ALLOCATOR=1" ) ;
}
// TSan
if ( Options . HasFlag ( ClangToolChainOptions . EnableThreadSanitizer ) )
{
// Force using the ANSI allocator if TSan is enabled
Arguments . Add ( "-DFORCE_ANSI_ALLOCATOR=1" ) ;
}
// UBSan
if ( Options . HasFlag ( ClangToolChainOptions . EnableUndefinedBehaviorSanitizer ) )
{
Arguments . Add ( "-fno-sanitize=vptr" ) ;
}
// MSan
if ( Options . HasFlag ( ClangToolChainOptions . EnableMemorySanitizer ) )
{
// Force using the ANSI allocator if MSan is enabled
// -fsanitize-memory-track-origins adds a 1.5x-2.5x slow down ontop of MSan normal amount of overhead
// -fsanitize-memory-track-origins=1 is faster but collects only allocation points but not intermediate stores
Arguments . Add ( "-fsanitize-memory-track-origins" ) ;
Arguments . Add ( "-DFORCE_ANSI_ALLOCATOR=1" ) ;
}
}
2022-06-01 16:41:24 -04:00
/// <inheritdoc/>
protected override void GetCompileArguments_Global ( CppCompileEnvironment CompileEnvironment , List < string > Arguments )
{
base . GetCompileArguments_Global ( CompileEnvironment , Arguments ) ;
// build up the commandline common to C and C++
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
if ( ShouldUseLibcxx ( ) )
2022-06-01 16:41:24 -04:00
{
Arguments . Add ( "-nostdinc++" ) ;
2022-06-27 19:21:20 -04:00
Arguments . Add ( GetSystemIncludePathArgument ( DirectoryReference . Combine ( Unreal . EngineSourceDirectory , "ThirdParty" , "Unix" , "LibCxx" , "include" ) ) ) ;
Arguments . Add ( GetSystemIncludePathArgument ( DirectoryReference . Combine ( Unreal . EngineSourceDirectory , "ThirdParty" , "Unix" , "LibCxx" , "include" , "c++" , "v1" ) ) ) ;
2022-06-01 16:41:24 -04:00
}
if ( CompilerVersionGreaterOrEqual ( 12 , 0 , 0 ) )
{
Arguments . Add ( "-fbinutils-version=2.36" ) ;
}
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
if ( CompileEnvironment . Architecture = = UnrealArch . Arm64 )
2022-06-01 16:41:24 -04:00
{
Arguments . Add ( "-funwind-tables" ) ; // generate unwind tables as they are needed for backtrace (on x86(64) they are generated implicitly)
}
Arguments . Add ( ArchitectureSpecificSwitches ( CompileEnvironment . Architecture ) ) ;
Arguments . Add ( "-fno-math-errno" ) ; // do not assume that math ops have side effects
Arguments . Add ( GetRTTIFlag ( CompileEnvironment ) ) ; // flag for run-time type info
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
if ( CompileEnvironment . Architecture = = UnrealArch . X64 )
2022-06-01 16:41:24 -04:00
{
Arguments . Add ( "-mssse3" ) ; // enable ssse3 by default for x86. This is default on for MSVC so lets reflect that here
}
//Arguments.Add("-DOPERATOR_NEW_INLINE=FORCENOINLINE");
if ( CompileEnvironment . bIsBuildingDLL )
{
Arguments . Add ( "-fPIC" ) ;
// Use local-dynamic TLS model. This generates less efficient runtime code for __thread variables, but avoids problems of running into
// glibc/ld.so limit (DTV_SURPLUS) for number of dlopen()'ed DSOs with static TLS (see e.g. https://www.cygwin.com/ml/libc-help/2013-11/msg00033.html)
Arguments . Add ( "-ftls-model=local-dynamic" ) ;
}
else
{
Arguments . Add ( "-ffunction-sections" ) ;
Arguments . Add ( "-fdata-sections" ) ;
}
2022-11-16 17:02:54 -05:00
// only suppress if happen to be using a system compiler and we have not explicitly requested pie
if ( ! CompileEnvironment . bIsBuildingDLL )
2022-06-01 16:41:24 -04:00
{
2022-11-16 17:02:54 -05:00
if ( CompileEnvironment . bUsePIE )
{
Arguments . Add ( "-fPIE" ) ;
}
2023-01-20 15:03:30 -05:00
else
2022-11-16 17:02:54 -05:00
{
Arguments . Add ( "-fno-PIE" ) ;
}
}
if ( CompileEnvironment . bUseStackProtection )
{
Arguments . Add ( "-fstack-protector" ) ;
2022-06-01 16:41:24 -04:00
}
if ( PlatformSDK . bVerboseCompiler )
{
Arguments . Add ( "-v" ) ; // for better error diagnosis
}
if ( CrossCompiling ( ) )
{
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
Arguments . Add ( $"-target {CompileEnvironment.Architecture.LinuxName}" ) ; // Set target triple
2022-07-11 16:55:46 -04:00
Arguments . Add ( $"--sysroot=\" { NormalizeCommandLinePath ( LinuxInfo . BaseLinuxPath ! ) } \ "" ) ;
2022-06-01 16:41:24 -04:00
}
}
/// <inheritdoc/>
protected override string EscapePreprocessorDefinition ( string Definition )
{
string [ ] SplitData = Definition . Split ( '=' ) ;
string? Key = SplitData . ElementAtOrDefault ( 0 ) ;
string? Value = SplitData . ElementAtOrDefault ( 1 ) ;
if ( string . IsNullOrEmpty ( Key ) ) { return "" ; }
if ( ! string . IsNullOrEmpty ( Value ) )
{
if ( ! Value . StartsWith ( "\"" ) & & ( Value . Contains ( " " ) | | Value . Contains ( "$" ) ) )
{
Value = Value . Trim ( '\"' ) ; // trim any leading or trailing quotes
Value = "\"" + Value + "\"" ; // ensure wrap string with double quotes
}
// replace double quotes to escaped double quotes if exists
Value = Value . Replace ( "\"" , "\\\"" ) ;
}
return Value = = null
? string . Format ( "{0}" , Key )
: string . Format ( "{0}={1}" , Key , Value ) ;
}
2022-06-27 19:21:20 -04:00
protected virtual void GetLinkArguments ( LinkEnvironment LinkEnvironment , List < string > Arguments )
2022-06-01 16:41:24 -04:00
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( ( BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Win64 ) ? "-fuse-ld=lld.exe" : "-fuse-ld=lld" ) ;
2022-06-01 16:41:24 -04:00
// debugging symbols
// Applying to all configurations @FIXME: temporary hack for FN to enable callstack in Shipping builds (proper resolution: UEPLAT-205)
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-rdynamic" ) ; // needed for backtrace_symbols()...
2022-06-01 16:41:24 -04:00
if ( LinkEnvironment . bIsBuildingDLL )
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-shared" ) ;
2022-06-01 16:41:24 -04:00
}
else
{
// ignore unresolved symbols in shared libs
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,--unresolved-symbols=ignore-in-shared-libs" ) ;
2022-06-01 16:41:24 -04:00
}
if ( Options . HasFlag ( ClangToolChainOptions . EnableAddressSanitizer ) | |
Options . HasFlag ( ClangToolChainOptions . EnableThreadSanitizer ) | |
Options . HasFlag ( ClangToolChainOptions . EnableUndefinedBehaviorSanitizer ) | |
Options . HasFlag ( ClangToolChainOptions . EnableMemorySanitizer ) )
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-g" ) ;
2022-06-01 16:41:24 -04:00
if ( Options . HasFlag ( ClangToolChainOptions . EnableSharedSanitizer ) )
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-shared-libsan" ) ;
2023-01-25 16:15:04 -05:00
// LLVM 15 compiler-rt introduced a new bug causing ASan to crash when going over net code
// https://github.com/llvm/llvm-project/issues/59007
// adding this fixes it, may be required now?
Arguments . Add ( "-lresolv" ) ;
2022-06-01 16:41:24 -04:00
}
if ( Options . HasFlag ( ClangToolChainOptions . EnableAddressSanitizer ) )
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-fsanitize=address" ) ;
2022-06-01 16:41:24 -04:00
}
else if ( Options . HasFlag ( ClangToolChainOptions . EnableThreadSanitizer ) )
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-fsanitize=thread" ) ;
2022-06-01 16:41:24 -04:00
}
else if ( Options . HasFlag ( ClangToolChainOptions . EnableUndefinedBehaviorSanitizer ) )
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-fsanitize=undefined" ) ;
2022-06-01 16:41:24 -04:00
}
else if ( Options . HasFlag ( ClangToolChainOptions . EnableMemorySanitizer ) )
{
// -fsanitize-memory-track-origins adds a 1.5x-2.5x slow ontop of MSan normal amount of overhead
// -fsanitize-memory-track-origins=1 is faster but collects only allocation points but not intermediate stores
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-fsanitize=memory -fsanitize-memory-track-origins" ) ;
2022-06-01 16:41:24 -04:00
}
if ( CrossCompiling ( ) )
{
2023-01-24 13:03:28 -05:00
// x64 only replaced the linux folder with arch, while on arm64 its still linux
2023-01-24 13:18:17 -05:00
if ( LinkEnvironment . Architecture = = UnrealArch . Arm64 )
2023-01-24 13:03:28 -05:00
{
Arguments . Add ( string . Format ( "-Wl,-rpath=\"{0}/lib/clang/{1}.{2}.{3}/lib/linux\"" ,
LinuxInfo . BaseLinuxPath , Info . ClangVersion . Major , Info . ClangVersion . Minor , Info . ClangVersion . Build ) ) ;
}
2023-01-24 13:18:17 -05:00
else
{
2023-01-24 13:26:46 -05:00
Arguments . Add ( string . Format ( "-Wl,-rpath=\"{0}/lib/clang/{1}.{2}.{3}/lib/x86_64-unknown-linux-gnu\"" ,
LinuxInfo . BaseLinuxPath , Info . ClangVersion . Major , Info . ClangVersion . Minor , Info . ClangVersion . Build ) ) ;
2023-01-24 13:18:17 -05:00
}
2022-06-01 16:41:24 -04:00
}
}
if ( LinkEnvironment . bCreateDebugInfo & & bGdbIndexSection )
{
// Generate .gdb_index section. On my machine, this cuts symbol loading time (breaking at main) from 45
// seconds to 17 seconds (with gdb v8.3.1).
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,--gdb-index" ) ;
2022-06-01 16:41:24 -04:00
}
// RPATH for third party libs
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,-rpath=${ORIGIN}" ) ;
Arguments . Add ( "-Wl,-rpath-link=${ORIGIN}" ) ;
Arguments . Add ( "-Wl,-rpath=${ORIGIN}/.." ) ; // for modules that are in sub-folders of the main Engine/Binary/Linux folder
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
if ( LinkEnvironment . Architecture = = UnrealArch . X64 )
2022-06-01 16:41:24 -04:00
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/Qualcomm/Linux" ) ;
2022-06-01 16:41:24 -04:00
}
else
{
// x86_64 is now using updated ICU that doesn't need extra .so
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
Arguments . Add ( "-Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/ICU/icu4c-53_1/Unix/" + LinkEnvironment . Architecture . LinuxName ) ;
2022-06-01 16:41:24 -04:00
}
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/OpenVR/OpenVRv1_5_17/linux64" ) ;
2022-06-01 16:41:24 -04:00
// @FIXME: Workaround for generating RPATHs for launching on devices UE-54136
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/PhysX3/Unix/x86_64-unknown-linux-gnu" ) ;
Arguments . Add ( "-Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/Intel/Embree/Embree2140/Linux/x86_64-unknown-linux-gnu/lib" ) ;
2022-06-01 16:41:24 -04:00
// Some OS ship ld with new ELF dynamic tags, which use DT_RUNPATH vs DT_RPATH. Since DT_RUNPATH do not propagate to dlopen()ed DSOs,
// this breaks the editor on such systems. See https://kenai.com/projects/maxine/lists/users/archive/2011-01/message/12 for details
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,--disable-new-dtags" ) ;
2022-06-01 16:41:24 -04:00
// This severely improves runtime linker performance. Without using FixDeps the impact on link time is not as big.
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,--as-needed" ) ;
2022-06-01 16:41:24 -04:00
// Additionally speeds up editor startup by 1-2s
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,--hash-style=gnu" ) ;
2022-06-01 16:41:24 -04:00
// This apparently can help LLDB speed up symbol lookups
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,--build-id" ) ;
2022-06-01 16:41:24 -04:00
if ( ! LinkEnvironment . bIsBuildingDLL )
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wl,--gc-sections" ) ;
2022-06-01 16:41:24 -04:00
2022-11-16 17:02:54 -05:00
if ( LinkEnvironment . bUsePIE )
2022-06-01 16:41:24 -04:00
{
2022-11-16 17:02:54 -05:00
Arguments . Add ( "-pie" ) ;
}
2023-01-20 15:03:30 -05:00
else
2023-01-17 16:35:14 -05:00
{
Arguments . Add ( "-Wl,-no-pie" ) ;
2022-06-01 16:41:24 -04:00
}
}
// Profile Guided Optimization (PGO) and Link Time Optimization (LTO)
// Whether we actually can enable that is checked in CanUseAdvancedLinkerFeatures() earlier
if ( LinkEnvironment . bPGOOptimize )
{
//
// Clang emits a warning for each compiled function that doesn't have a matching entry in the profile data.
// This can happen when the profile data is older than the binaries we're compiling.
//
// Disable this warning. It's far too verbose.
//
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-Wno-backend-plugin" ) ;
2022-06-01 16:41:24 -04:00
Log . TraceInformationOnce ( "Enabling Profile Guided Optimization (PGO). Linking will take a while." ) ;
2022-06-27 19:21:20 -04:00
Arguments . Add ( string . Format ( "-fprofile-instr-use=\"{0}\"" , Path . Combine ( LinkEnvironment . PGODirectory ! , LinkEnvironment . PGOFilenamePrefix ! ) ) ) ;
2022-06-01 16:41:24 -04:00
}
else if ( LinkEnvironment . bPGOProfile )
{
Log . TraceInformationOnce ( "Enabling Profile Guided Instrumentation (PGI). Linking will take a while." ) ;
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-fprofile-generate" ) ;
2022-06-01 16:41:24 -04:00
}
// whether we actually can do that is checked in CanUseAdvancedLinkerFeatures() earlier
if ( LinkEnvironment . bAllowLTCG )
{
if ( ( Options & ClangToolChainOptions . EnableThinLTO ) ! = 0 )
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( String . Format ( " -flto=thin -Wl,--thinlto-jobs={0}" , Utils . GetPhysicalProcessorCount ( ) ) ) ;
2022-06-01 16:41:24 -04:00
}
else
{
2022-06-27 19:21:20 -04:00
Arguments . Add ( "-flto" ) ;
2022-06-01 16:41:24 -04:00
}
}
if ( CrossCompiling ( ) )
{
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
Arguments . Add ( $"-target {LinkEnvironment.Architecture.LinuxName}" ) ; // Set target triple
2022-07-11 16:55:46 -04:00
DirectoryReference SysRootPath = LinuxInfo . BaseLinuxPath ! ;
2022-06-27 20:18:23 -04:00
Arguments . Add ( $"--sysroot=\" { NormalizeCommandLinePath ( SysRootPath ) } \ "" ) ;
2022-06-01 16:41:24 -04:00
// Linking with the toolchain on linux appears to not search usr/
if ( BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Linux )
{
2022-06-27 20:18:23 -04:00
Arguments . Add ( $"-B\" { NormalizeCommandLinePath ( DirectoryReference . Combine ( SysRootPath , "usr" , "lib" ) ) } \ "" ) ;
Arguments . Add ( $"-B\" { NormalizeCommandLinePath ( DirectoryReference . Combine ( SysRootPath , "usr" , "lib64" ) ) } \ "" ) ;
Arguments . Add ( $"-L\" { NormalizeCommandLinePath ( DirectoryReference . Combine ( SysRootPath , "usr" , "lib" ) ) } \ "" ) ;
Arguments . Add ( $"-L\" { NormalizeCommandLinePath ( DirectoryReference . Combine ( SysRootPath , "usr" , "lib64" ) ) } \ "" ) ;
2022-06-01 16:41:24 -04:00
}
}
}
string GetArchiveArguments ( LinkEnvironment LinkEnvironment )
{
return " rcs" ;
}
// cache the location of NDK tools
protected bool bIsCrossCompiling ;
/// <summary>
/// Whether to use old, slower way to relink circularly dependent libraries.
/// It makes sense to use it when cross-compiling on Windows due to race conditions between actions reading and modifying the libs.
/// </summary>
private bool bUseFixdeps = false ;
/// <summary>
/// Track which scripts need to be deleted before appending to
/// </summary>
private bool bHasWipedFixDepsScript = false ;
/// <summary>
/// Holds all the binaries for a particular target (except maybe the executable itself).
/// </summary>
2023-01-25 17:55:46 -05:00
private static Dictionary < ReadOnlyTargetRules , List < FileItem > > AllBinaries = new ( ) ;
2022-06-01 16:41:24 -04:00
/// <summary>
/// Tracks that information about used C++ library is only printed once
/// </summary>
private bool bHasPrintedBuildDetails = false ;
protected void PrintBuildDetails ( CppCompileEnvironment CompileEnvironment , ILogger Logger )
{
Logger . LogInformation ( "------- Build details --------" ) ;
2022-07-11 16:55:46 -04:00
Logger . LogInformation ( "Using {ToolchainInfo}." , LinuxInfo . BaseLinuxPath = = null ? "system toolchain" : $"toolchain located at '{LinuxInfo.BaseLinuxPath}'" ) ;
2022-06-01 16:41:24 -04:00
Logger . LogInformation ( "Using clang ({ClangPath}) version '{ClangVersionString}' (string), {ClangVersionMajor} (major), {ClangVersionMinor} (minor), {ClangVersionPatch} (patch)" ,
2022-07-11 16:55:46 -04:00
Info . Clang , Info . ClangVersionString , Info . ClangVersion . Major , Info . ClangVersion . Minor , Info . ClangVersion . Build ) ;
2022-06-01 16:41:24 -04:00
// inform the user which C++ library the engine is going to be compiled against - important for compatibility with third party code that uses STL
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
Logger . LogInformation ( "Using {Lib} standard C++ library." , ShouldUseLibcxx ( ) ? "bundled libc++" : "compiler default (most likely libstdc++)" ) ;
2022-06-01 16:41:24 -04:00
Logger . LogInformation ( "Using lld linker" ) ;
2022-07-11 16:55:46 -04:00
Logger . LogInformation ( "Using llvm-ar ({LlvmAr}) version '{LlvmArVersionString} (string)'" , Info . Archiver , Info . ArchiverVersionString ) ;
2022-06-01 16:41:24 -04:00
if ( Options . HasFlag ( ClangToolChainOptions . EnableAddressSanitizer ) | |
Options . HasFlag ( ClangToolChainOptions . EnableThreadSanitizer ) | |
Options . HasFlag ( ClangToolChainOptions . EnableUndefinedBehaviorSanitizer ) | |
Options . HasFlag ( ClangToolChainOptions . EnableMemorySanitizer ) )
{
string SanitizerInfo = "Building with:" ;
string StaticOrShared = Options . HasFlag ( ClangToolChainOptions . EnableSharedSanitizer ) ? " dynamically" : " statically" ;
SanitizerInfo + = Options . HasFlag ( ClangToolChainOptions . EnableAddressSanitizer ) ? StaticOrShared + " linked AddressSanitizer" : "" ;
SanitizerInfo + = Options . HasFlag ( ClangToolChainOptions . EnableThreadSanitizer ) ? StaticOrShared + " linked ThreadSanitizer" : "" ;
SanitizerInfo + = Options . HasFlag ( ClangToolChainOptions . EnableUndefinedBehaviorSanitizer ) ? StaticOrShared + " linked UndefinedBehaviorSanitizer" : "" ;
SanitizerInfo + = Options . HasFlag ( ClangToolChainOptions . EnableMemorySanitizer ) ? StaticOrShared + " linked MemorySanitizer" : "" ;
Logger . LogInformation ( "{SanitizerInfo}" , SanitizerInfo ) ;
}
// Also print other once-per-build information
if ( bUseFixdeps )
{
Logger . LogInformation ( "Using old way to relink circularly dependent libraries (with a FixDeps step)." ) ;
}
else
{
Logger . LogInformation ( "Using fast way to relink circularly dependent libraries (no FixDeps)." ) ;
}
if ( CompileEnvironment . bPGOOptimize )
{
Logger . LogInformation ( "Using PGO (profile guided optimization)." ) ;
Logger . LogInformation ( " Directory for PGO data files='{CompileEnvironmentPGODirectory}'" , CompileEnvironment . PGODirectory ) ;
Logger . LogInformation ( " Prefix for PGO data files='{CompileEnvironmentPGOFilenamePrefix}'" , CompileEnvironment . PGOFilenamePrefix ) ;
}
if ( CompileEnvironment . bPGOProfile )
{
Logger . LogInformation ( "Using PGI (profile guided instrumentation)." ) ;
}
if ( CompileEnvironment . bAllowLTCG )
{
Logger . LogInformation ( "Using LTO (link-time optimization)." ) ;
}
2022-11-16 17:02:54 -05:00
if ( CompileEnvironment . bUsePIE )
{
Logger . LogInformation ( "Using position independent executables (PIE)" ) ;
}
if ( CompileEnvironment . bUseStackProtection )
{
Logger . LogInformation ( "Using stack protection" ) ;
}
2022-06-01 16:41:24 -04:00
Logger . LogInformation ( "------------------------------" ) ;
}
2022-11-30 13:38:45 -05:00
protected override CPPOutput CompileCPPFiles ( CppCompileEnvironment CompileEnvironment , List < FileItem > InputFiles , DirectoryReference OutputDir , string ModuleName , IActionGraphBuilder Graph )
2022-06-01 16:41:24 -04:00
{
List < string > GlobalArguments = new ( ) ;
GetCompileArguments_Global ( CompileEnvironment , GlobalArguments ) ;
//var BuildPlatform = UEBuildPlatform.GetBuildPlatform(CompileEnvironment.Platform);
if ( ! bHasPrintedBuildDetails )
{
PrintBuildDetails ( CompileEnvironment , Logger ) ;
bHasPrintedBuildDetails = true ;
}
// Create a compile action for each source file.
CPPOutput Result = new CPPOutput ( ) ;
foreach ( FileItem SourceFile in InputFiles )
{
2022-07-11 16:55:46 -04:00
CompileCPPFile ( CompileEnvironment , SourceFile , OutputDir , ModuleName , Graph , GlobalArguments , Result ) ;
2022-06-01 16:41:24 -04:00
}
return Result ;
}
/// <summary>
/// Creates an action to archive all the .o files into single .a file
/// </summary>
public FileItem CreateArchiveAndIndex ( LinkEnvironment LinkEnvironment , IActionGraphBuilder Graph , ILogger Logger )
{
// Create an archive action
Action ArchiveAction = Graph . CreateAction ( ActionType . Link ) ;
ArchiveAction . WorkingDirectory = Unreal . EngineSourceDirectory ;
2022-07-11 16:55:46 -04:00
ArchiveAction . CommandPath = Info . Archiver ;
2022-06-01 16:41:24 -04:00
// this will produce a final library
ArchiveAction . bProducesImportLibrary = true ;
// Add the output file as a production of the link action.
FileItem OutputFile = FileItem . GetItemByFileReference ( LinkEnvironment . OutputFilePath ) ;
ArchiveAction . ProducedItems . Add ( OutputFile ) ;
ArchiveAction . CommandDescription = "Archive" ;
ArchiveAction . StatusDescription = Path . GetFileName ( OutputFile . AbsolutePath ) ;
ArchiveAction . CommandArguments + = string . Format ( "{1} \"{2}\"" , GetArchiveArguments ( LinkEnvironment ) , OutputFile . AbsolutePath ) ;
// Add the input files to a response file, and pass the response file on the command-line.
List < string > InputFileNames = new List < string > ( ) ;
foreach ( FileItem InputFile in LinkEnvironment . InputFiles )
{
string InputAbsolutePath = InputFile . AbsolutePath . Replace ( "\\" , "/" ) ;
InputFileNames . Add ( string . Format ( "\"{0}\"" , InputAbsolutePath ) ) ;
ArchiveAction . PrerequisiteItems . Add ( InputFile ) ;
}
// this won't stomp linker's response (which is not used when compiling static libraries)
FileReference ResponsePath = GetResponseFileName ( LinkEnvironment , OutputFile ) ;
if ( ! ProjectFileGenerator . bGenerateProjectFiles )
{
FileItem ResponseFileItem = Graph . CreateIntermediateTextFile ( ResponsePath , InputFileNames ) ;
ArchiveAction . PrerequisiteItems . Add ( ResponseFileItem ) ;
}
ArchiveAction . CommandArguments + = string . Format ( " @\"{0}\"" , ResponsePath . FullName ) ;
// Add the additional arguments specified by the environment.
ArchiveAction . CommandArguments + = LinkEnvironment . AdditionalArguments ;
ArchiveAction . CommandArguments = ArchiveAction . CommandArguments . Replace ( "\\" , "/" ) ;
if ( BuildHostPlatform . Current . ShellType = = ShellType . Sh )
{
ArchiveAction . CommandArguments + = "'" ;
}
else
{
ArchiveAction . CommandArguments + = "\"" ;
}
// Only execute linking on the local PC.
ArchiveAction . bCanExecuteRemotely = false ;
return OutputFile ;
}
2023-01-25 17:55:46 -05:00
public FileItem ? FixDependencies ( ReadOnlyTargetRules Target , LinkEnvironment LinkEnvironment , FileItem Executable , IActionGraphBuilder Graph , ILogger Logger )
2022-06-01 16:41:24 -04:00
{
if ( bUseFixdeps )
{
Logger . LogDebug ( "Adding postlink step" ) ;
bool bUseCmdExe = BuildHostPlatform . Current . ShellType = = ShellType . Cmd ;
FileReference ShellBinary = BuildHostPlatform . Current . Shell ;
string ExecuteSwitch = bUseCmdExe ? " /C" : "" ; // avoid -c so scripts don't need +x
string ScriptName = bUseCmdExe ? "FixDependencies.bat" : "FixDependencies.sh" ;
FileItem FixDepsScript = FileItem . GetItemByFileReference ( FileReference . Combine ( LinkEnvironment . LocalShadowDirectory ! , ScriptName ) ) ;
// if we never generated one we did not have a circular depends that needed fixing up
if ( ! FixDepsScript . Exists )
{
return null ;
}
Action PostLinkAction = Graph . CreateAction ( ActionType . Link ) ;
PostLinkAction . WorkingDirectory = Unreal . EngineSourceDirectory ;
PostLinkAction . CommandPath = ShellBinary ;
PostLinkAction . StatusDescription = string . Format ( "{0}" , Path . GetFileName ( Executable . AbsolutePath ) ) ;
PostLinkAction . CommandDescription = "FixDeps" ;
PostLinkAction . bCanExecuteRemotely = false ;
PostLinkAction . CommandArguments = ExecuteSwitch ;
PostLinkAction . CommandArguments + = bUseCmdExe ? " \"" : " -c '" ;
FileItem OutputFile = FileItem . GetItemByFileReference ( FileReference . Combine ( LinkEnvironment . LocalShadowDirectory ! , Path . GetFileNameWithoutExtension ( Executable . AbsolutePath ) + ".link" ) ) ;
// Make sure we don't run this script until the all executables and shared libraries
// have been built.
PostLinkAction . PrerequisiteItems . Add ( Executable ) ;
2023-01-25 17:55:46 -05:00
foreach ( FileItem Dependency in AllBinaries . GetValueOrDefault ( Target , new ( ) ) )
2022-06-01 16:41:24 -04:00
{
PostLinkAction . PrerequisiteItems . Add ( Dependency ) ;
}
PostLinkAction . CommandArguments + = ShellBinary + ExecuteSwitch + " \"" + FixDepsScript . AbsolutePath + "\" && " ;
// output file should not be empty or it will be rebuilt next time
string Touch = bUseCmdExe ? "echo \"Dummy\" >> \"{0}\" && copy /b \"{0}\" +,," : "echo \"Dummy\" >> \"{0}\"" ;
PostLinkAction . CommandArguments + = String . Format ( Touch , OutputFile . AbsolutePath ) ;
PostLinkAction . CommandArguments + = bUseCmdExe ? "\"" : "'" ;
System . Console . WriteLine ( "{0} {1}" , PostLinkAction . CommandPath , PostLinkAction . CommandArguments ) ;
PostLinkAction . ProducedItems . Add ( OutputFile ) ;
return OutputFile ;
}
else
{
return null ;
}
}
// allow sub-platforms to modify the name of the output file
protected virtual FileItem GetLinkOutputFile ( LinkEnvironment LinkEnvironment )
{
return FileItem . GetItemByFileReference ( LinkEnvironment . OutputFilePath ) ;
}
public override FileItem LinkFiles ( LinkEnvironment LinkEnvironment , bool bBuildImportLibraryOnly , IActionGraphBuilder Graph )
{
Debug . Assert ( ! bBuildImportLibraryOnly ) ;
List < string > RPaths = new List < string > ( ) ;
if ( LinkEnvironment . bIsBuildingLibrary | | bBuildImportLibraryOnly )
{
return CreateArchiveAndIndex ( LinkEnvironment , Graph , Logger ) ;
}
// Create an action that invokes the linker.
Action LinkAction = Graph . CreateAction ( ActionType . Link ) ;
LinkAction . WorkingDirectory = Unreal . EngineSourceDirectory ;
string LinkCommandString ;
2022-07-11 16:55:46 -04:00
LinkCommandString = "\"" + Info . Clang + "\"" ;
2022-06-01 16:41:24 -04:00
// Get link arguments.
2022-06-27 19:21:20 -04:00
List < string > LinkArguments = new List < string > ( ) ;
GetLinkArguments ( LinkEnvironment , LinkArguments ) ;
LinkCommandString + = " " + string . Join ( ' ' , LinkArguments ) ;
2022-06-01 16:41:24 -04:00
// Tell the action that we're building an import library here and it should conditionally be
// ignored as a prerequisite for other actions
LinkAction . bProducesImportLibrary = LinkEnvironment . bIsBuildingDLL ;
// Add the output file as a production of the link action.
FileItem OutputFile = GetLinkOutputFile ( LinkEnvironment ) ;
LinkAction . ProducedItems . Add ( OutputFile ) ;
// LTO/PGO can take a lot of time, make it clear for the user
if ( LinkEnvironment . bPGOProfile )
{
LinkAction . CommandDescription = "Link-PGI" ;
}
else if ( LinkEnvironment . bPGOOptimize )
{
LinkAction . CommandDescription = "Link-PGO" ;
}
else if ( LinkEnvironment . bAllowLTCG )
{
LinkAction . CommandDescription = "Link-LTO" ;
}
else
{
LinkAction . CommandDescription = "Link" ;
}
// because the logic choosing between lld and ld is somewhat messy atm (lld fails to link .DSO due to bugs), make the name of the linker clear
LinkAction . CommandDescription + = ( LinkCommandString . Contains ( "-fuse-ld=lld" ) ) ? " (lld)" : " (ld)" ;
2022-07-11 16:55:46 -04:00
LinkAction . CommandVersion = Info . ClangVersionString ;
2022-06-01 16:41:24 -04:00
LinkAction . StatusDescription = Path . GetFileName ( OutputFile . AbsolutePath ) ;
// Add the output file to the command-line.
LinkCommandString + = string . Format ( " -o \"{0}\"" , OutputFile . AbsolutePath ) ;
// Add the input files to a response file, and pass the response file on the command-line.
List < string > ResponseLines = new List < string > ( ) ;
foreach ( FileItem InputFile in LinkEnvironment . InputFiles )
{
ResponseLines . Add ( string . Format ( "\"{0}\"" , InputFile . AbsolutePath . Replace ( "\\" , "/" ) ) ) ;
LinkAction . PrerequisiteItems . Add ( InputFile ) ;
}
if ( LinkEnvironment . bIsBuildingDLL )
{
ResponseLines . Add ( string . Format ( " -soname=\"{0}\"" , OutputFile . Location . GetFileName ( ) ) ) ;
}
// Start with the configured LibraryPaths and also add paths to any libraries that
// we depend on (libraries that we've build ourselves).
List < DirectoryReference > AllLibraryPaths = LinkEnvironment . SystemLibraryPaths ;
IEnumerable < string > AdditionalLibraries = Enumerable . Concat ( LinkEnvironment . SystemLibraries , LinkEnvironment . Libraries . Select ( x = > x . FullName ) ) ;
foreach ( string AdditionalLibrary in AdditionalLibraries )
{
string PathToLib = Path . GetDirectoryName ( AdditionalLibrary ) ! ;
if ( ! String . IsNullOrEmpty ( PathToLib ) )
{
// make path absolute, because FixDependencies script may be executed in a different directory
DirectoryReference AbsolutePathToLib = new DirectoryReference ( PathToLib ) ;
if ( ! AllLibraryPaths . Contains ( AbsolutePathToLib ) )
{
AllLibraryPaths . Add ( AbsolutePathToLib ) ;
}
}
if ( ( AdditionalLibrary . Contains ( "Plugins" ) | | AdditionalLibrary . Contains ( "Binaries/ThirdParty" ) | | AdditionalLibrary . Contains ( "Binaries\\ThirdParty" ) ) & & Path . GetDirectoryName ( AdditionalLibrary ) ! = Path . GetDirectoryName ( OutputFile . AbsolutePath ) )
{
string RelativePath = new FileReference ( AdditionalLibrary ) . Directory . MakeRelativeTo ( OutputFile . Location . Directory ) ;
if ( LinkEnvironment . bIsBuildingDLL )
{
// Remove the root Unreal.RootDirectory from the RuntimeLibaryPath
string AdditionalLibraryRootPath = new FileReference ( AdditionalLibrary ) . Directory . MakeRelativeTo ( Unreal . RootDirectory ) ;
// Figure out how many dirs we need to go back
string RelativeRootPath = Unreal . RootDirectory . MakeRelativeTo ( OutputFile . Location . Directory ) ;
// Combine the two together ie. number of ../ + the path after the root
RelativePath = Path . Combine ( RelativeRootPath , AdditionalLibraryRootPath ) ;
}
// On Windows, MakeRelativeTo can silently fail if the engine and the project are located on different drives
if ( CrossCompiling ( ) & & RelativePath . StartsWith ( Unreal . RootDirectory . FullName ) )
{
// do not replace directly, but take care to avoid potential double slashes or missed slashes
string PathFromRootDir = RelativePath . Replace ( Unreal . RootDirectory . FullName , "" ) ;
// Path.Combine doesn't combine these properly
RelativePath = ( ( PathFromRootDir . StartsWith ( "\\" ) | | PathFromRootDir . StartsWith ( "/" ) ) ? "..\\..\\.." : "..\\..\\..\\" ) + PathFromRootDir ;
}
if ( ! RPaths . Contains ( RelativePath ) )
{
RPaths . Add ( RelativePath ) ;
ResponseLines . Add ( string . Format ( " -rpath=\"${{ORIGIN}}/{0}\"" , RelativePath . Replace ( '\\' , '/' ) ) ) ;
}
}
}
foreach ( string RuntimeLibaryPath in LinkEnvironment . RuntimeLibraryPaths )
{
string RelativePath = RuntimeLibaryPath ;
if ( ! RelativePath . StartsWith ( "$" ) )
{
if ( LinkEnvironment . bIsBuildingDLL )
{
// Remove the root Unreal.RootDirectory from the RuntimeLibaryPath
string RuntimeLibraryRootPath = new DirectoryReference ( RuntimeLibaryPath ) . MakeRelativeTo ( Unreal . RootDirectory ) ;
// Figure out how many dirs we need to go back
string RelativeRootPath = Unreal . RootDirectory . MakeRelativeTo ( OutputFile . Location . Directory ) ;
// Combine the two together ie. number of ../ + the path after the root
RelativePath = Path . Combine ( RelativeRootPath , RuntimeLibraryRootPath ) ;
}
else
{
string RelativeRootPath = new DirectoryReference ( RuntimeLibaryPath ) . MakeRelativeTo ( Unreal . RootDirectory ) ;
// We're assuming that the binary will be placed according to our ProjectName/Binaries/Platform scheme
RelativePath = Path . Combine ( ".." , ".." , ".." , RelativeRootPath ) ;
}
}
// On Windows, MakeRelativeTo can silently fail if the engine and the project are located on different drives
if ( CrossCompiling ( ) & & RelativePath . StartsWith ( Unreal . RootDirectory . FullName ) )
{
// do not replace directly, but take care to avoid potential double slashes or missed slashes
string PathFromRootDir = RelativePath . Replace ( Unreal . RootDirectory . FullName , "" ) ;
// Path.Combine doesn't combine these properly
RelativePath = ( ( PathFromRootDir . StartsWith ( "\\" ) | | PathFromRootDir . StartsWith ( "/" ) ) ? "..\\..\\.." : "..\\..\\..\\" ) + PathFromRootDir ;
}
if ( ! RPaths . Contains ( RelativePath ) )
{
RPaths . Add ( RelativePath ) ;
ResponseLines . Add ( string . Format ( " -rpath=\"${{ORIGIN}}/{0}\"" , RelativePath . Replace ( '\\' , '/' ) ) ) ;
}
}
ResponseLines . Add ( string . Format ( " -rpath-link=\"{0}\"" , Path . GetDirectoryName ( OutputFile . AbsolutePath ) ) ) ;
// Add the library paths to the argument list.
foreach ( DirectoryReference LibraryPath in AllLibraryPaths )
{
// use absolute paths because of FixDependencies script again
ResponseLines . Add ( string . Format ( " -L\"{0}\"" , LibraryPath . FullName . Replace ( '\\' , '/' ) ) ) ;
}
List < string > EngineAndGameLibrariesLinkFlags = new List < string > ( ) ;
List < FileItem > EngineAndGameLibrariesFiles = new List < FileItem > ( ) ;
// Pre-2.25 ld has symbol resolution problems when .so are mixed with .a in a single --start-group/--end-group
// when linking with --as-needed.
// Move external libraries to a separate --start-group/--end-group to fix it (and also make groups smaller and faster to link).
// See https://github.com/EpicGames/UnrealEngine/pull/2778 and https://github.com/EpicGames/UnrealEngine/pull/2793 for discussion
string ExternalLibraries = "" ;
// add libraries in a library group
ResponseLines . Add ( string . Format ( " --start-group" ) ) ;
foreach ( string AdditionalLibrary in AdditionalLibraries )
{
if ( String . IsNullOrEmpty ( Path . GetDirectoryName ( AdditionalLibrary ) ) )
{
// library was passed just like "jemalloc", turn it into -ljemalloc
ExternalLibraries + = string . Format ( " -l{0}" , AdditionalLibrary ) ;
}
else if ( Path . GetExtension ( AdditionalLibrary ) = = ".a" )
{
// static library passed in, pass it along but make path absolute, because FixDependencies script may be executed in a different directory
string AbsoluteAdditionalLibrary = Path . GetFullPath ( AdditionalLibrary ) ;
if ( AbsoluteAdditionalLibrary . Contains ( " " ) )
{
AbsoluteAdditionalLibrary = string . Format ( "\"{0}\"" , AbsoluteAdditionalLibrary ) ;
}
AbsoluteAdditionalLibrary = AbsoluteAdditionalLibrary . Replace ( '\\' , '/' ) ;
// libcrypto/libssl contain number of functions that are being used in different DSOs. FIXME: generalize?
if ( LinkEnvironment . bIsBuildingDLL & & ( AbsoluteAdditionalLibrary . Contains ( "libcrypto" ) | | AbsoluteAdditionalLibrary . Contains ( "libssl" ) ) )
{
ResponseLines . Add ( " --whole-archive " + AbsoluteAdditionalLibrary + " --no-whole-archive" ) ;
}
else
{
ResponseLines . Add ( " " + AbsoluteAdditionalLibrary ) ;
}
LinkAction . PrerequisiteItems . Add ( FileItem . GetItemByPath ( AdditionalLibrary ) ) ;
}
else
{
// Skip over full-pathed library dependencies when building DLLs to avoid circular
// dependencies.
FileItem LibraryDependency = FileItem . GetItemByPath ( AdditionalLibrary ) ;
string LibName = Path . GetFileNameWithoutExtension ( AdditionalLibrary ) ;
if ( LibName . StartsWith ( "lib" ) )
{
// Remove lib prefix
LibName = LibName . Remove ( 0 , 3 ) ;
}
else if ( LibraryDependency . Exists )
{
// The library exists, but it is not prefixed with "lib", so force the
// linker to find it without that prefix by prepending a colon to
// the file name.
LibName = string . Format ( ":{0}" , LibraryDependency . Name ) ;
}
string LibLinkFlag = string . Format ( " -l{0}" , LibName ) ;
if ( LinkEnvironment . bIsBuildingDLL & & LinkEnvironment . bIsCrossReferenced )
{
// We are building a cross referenced DLL so we can't actually include
// dependencies at this point. Instead we add it to the list of
// libraries to be used in the FixDependencies step.
EngineAndGameLibrariesLinkFlags . Add ( LibLinkFlag ) ;
EngineAndGameLibrariesFiles . Add ( LibraryDependency ) ;
// it is important to add this exactly to the same place where the missing libraries would have been, it will be replaced later
if ( ! ExternalLibraries . Contains ( "--allow-shlib-undefined" ) )
{
ExternalLibraries + = string . Format ( " -Wl,--allow-shlib-undefined" ) ;
}
}
else
{
LinkAction . PrerequisiteItems . Add ( LibraryDependency ) ;
ExternalLibraries + = LibLinkFlag ;
}
}
}
ResponseLines . Add ( " --end-group" ) ;
FileReference ResponseFileName = GetResponseFileName ( LinkEnvironment , OutputFile ) ;
FileItem ResponseFileItem = Graph . CreateIntermediateTextFile ( ResponseFileName , ResponseLines ) ;
LinkCommandString + = string . Format ( " -Wl,@\"{0}\"" , ResponseFileName ) ;
LinkAction . PrerequisiteItems . Add ( ResponseFileItem ) ;
LinkCommandString + = " -Wl,--start-group" ;
LinkCommandString + = ExternalLibraries ;
// make unresolved symbols an error, unless a) building a cross-referenced DSO b) we opted out
if ( ( ! LinkEnvironment . bIsBuildingDLL | | ! LinkEnvironment . bIsCrossReferenced ) & & ! LinkEnvironment . bIgnoreUnresolvedSymbols )
{
// This will make the linker report undefined symbols the current module, but ignore in the dependent DSOs.
// It is tempting, but may not be possible to change that report-all - due to circular dependencies between our libs.
LinkCommandString + = " -Wl,--unresolved-symbols=ignore-in-shared-libs" ;
}
LinkCommandString + = " -Wl,--end-group" ;
LinkCommandString + = " -lrt" ; // needed for clock_gettime()
LinkCommandString + = " -lm" ; // math
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
if ( ShouldUseLibcxx ( ) )
2022-06-01 16:41:24 -04:00
{
// libc++ and its abi lib
LinkCommandString + = " -nodefaultlibs" ;
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
LinkCommandString + = " -L" + "ThirdParty/Unix/LibCxx/lib/Unix/" + LinkEnvironment . Architecture . LinuxName + "/" ;
LinkCommandString + = " " + "ThirdParty/Unix/LibCxx/lib/Unix/" + LinkEnvironment . Architecture . LinuxName + "/libc++.a" ;
LinkCommandString + = " " + "ThirdParty/Unix/LibCxx/lib/Unix/" + LinkEnvironment . Architecture . LinuxName + "/libc++abi.a" ;
2022-06-01 16:41:24 -04:00
LinkCommandString + = " -lm" ;
LinkCommandString + = " -lc" ;
LinkCommandString + = " -lpthread" ; // pthread_mutex_trylock is missing from libc stubs
LinkCommandString + = " -lgcc_s" ;
LinkCommandString + = " -lgcc" ;
}
// these can be helpful for understanding the order of libraries or library search directories
if ( PlatformSDK . bVerboseLinker )
{
LinkCommandString + = " -Wl,--verbose" ;
LinkCommandString + = " -Wl,--trace" ;
LinkCommandString + = " -v" ;
}
// Add the additional arguments specified by the environment.
LinkCommandString + = LinkEnvironment . AdditionalArguments ;
LinkCommandString = LinkCommandString . Replace ( "\\\\" , "/" ) ;
LinkCommandString = LinkCommandString . Replace ( "\\" , "/" ) ;
bool bUseCmdExe = BuildHostPlatform . Current . ShellType = = ShellType . Cmd ;
FileReference ShellBinary = BuildHostPlatform . Current . Shell ;
string ExecuteSwitch = bUseCmdExe ? " /C" : "" ; // avoid -c so scripts don't need +x
// Linux has issues with scripts and parameter expansion from curely brakets
if ( ! bUseCmdExe )
{
LinkCommandString = LinkCommandString . Replace ( "{" , "'{" ) ;
LinkCommandString = LinkCommandString . Replace ( "}" , "}'" ) ;
LinkCommandString = LinkCommandString . Replace ( "$'{" , "'${" ) ; // fixing $'{ORIGIN}' to be '${ORIGIN}'
}
string LinkScriptName = string . Format ( ( bUseCmdExe ? "Link-{0}.link.bat" : "Link-{0}.link.sh" ) , OutputFile . Location . GetFileName ( ) ) ;
string LinkScriptFullPath = Path . Combine ( LinkEnvironment . LocalShadowDirectory ! . FullName , LinkScriptName ) ;
Logger . LogDebug ( "Creating link script: {LinkScriptFullPath}" , LinkScriptFullPath ) ;
Directory . CreateDirectory ( Path . GetDirectoryName ( LinkScriptFullPath ) ! ) ;
using ( StreamWriter LinkWriter = File . CreateText ( LinkScriptFullPath ) )
{
if ( bUseCmdExe )
{
LinkWriter . NewLine = "\r\n" ;
LinkWriter . WriteLine ( "@echo off" ) ;
LinkWriter . WriteLine ( "rem Automatically generated by UnrealBuildTool" ) ;
LinkWriter . WriteLine ( "rem *DO NOT EDIT*" ) ;
LinkWriter . WriteLine ( ) ;
LinkWriter . WriteLine ( "set Retries=0" ) ;
LinkWriter . WriteLine ( ":linkloop" ) ;
LinkWriter . WriteLine ( "if %Retries% GEQ 10 goto failedtorelink" ) ;
LinkWriter . WriteLine ( LinkCommandString ) ;
LinkWriter . WriteLine ( "if %errorlevel% neq 0 goto sleepandretry" ) ;
LinkWriter . WriteLine ( GetDumpEncodeDebugCommand ( LinkEnvironment , OutputFile ) ) ;
LinkWriter . WriteLine ( "exit 0" ) ;
LinkWriter . WriteLine ( ":sleepandretry" ) ;
LinkWriter . WriteLine ( "ping 127.0.0.1 -n 1 -w 5000 >NUL 2>NUL" ) ; // timeout complains about lack of redirection
LinkWriter . WriteLine ( "set /a Retries+=1" ) ;
LinkWriter . WriteLine ( "goto linkloop" ) ;
LinkWriter . WriteLine ( ":failedtorelink" ) ;
LinkWriter . WriteLine ( "echo Failed to link {0} after %Retries% retries" , OutputFile . AbsolutePath ) ;
LinkWriter . WriteLine ( "exit 1" ) ;
}
else
{
LinkWriter . NewLine = "\n" ;
LinkWriter . WriteLine ( "#!/bin/sh" ) ;
LinkWriter . WriteLine ( "# Automatically generated by UnrealBuildTool" ) ;
LinkWriter . WriteLine ( "# *DO NOT EDIT*" ) ;
LinkWriter . WriteLine ( ) ;
LinkWriter . WriteLine ( "set -o errexit" ) ;
LinkWriter . WriteLine ( LinkCommandString ) ;
LinkWriter . WriteLine ( GetDumpEncodeDebugCommand ( LinkEnvironment , OutputFile ) ) ;
}
} ;
LinkAction . CommandPath = ShellBinary ;
// This must maintain the quotes around the LinkScriptFullPath
LinkAction . CommandArguments = ExecuteSwitch + " \"" + LinkScriptFullPath + "\"" ;
// prepare a linker script
FileReference LinkerScriptPath = FileReference . Combine ( LinkEnvironment . LocalShadowDirectory , "remove-sym.ldscript" ) ;
if ( ! DirectoryReference . Exists ( LinkEnvironment . LocalShadowDirectory ) )
{
DirectoryReference . CreateDirectory ( LinkEnvironment . LocalShadowDirectory ) ;
}
if ( FileReference . Exists ( LinkerScriptPath ) )
{
FileReference . Delete ( LinkerScriptPath ) ;
}
// Only execute linking on the local PC.
LinkAction . bCanExecuteRemotely = false ;
// Prepare a script that will run later, once all shared libraries and the executable
// are created. This script will be called by action created in FixDependencies()
if ( LinkEnvironment . bIsCrossReferenced & & LinkEnvironment . bIsBuildingDLL )
{
if ( bUseFixdeps )
{
string ScriptName = bUseCmdExe ? "FixDependencies.bat" : "FixDependencies.sh" ;
string FixDepsScriptPath = Path . Combine ( LinkEnvironment . LocalShadowDirectory . FullName , ScriptName ) ;
if ( ! bHasWipedFixDepsScript )
{
bHasWipedFixDepsScript = true ;
Logger . LogDebug ( "Creating script: {FixDepsScriptPath}" , FixDepsScriptPath ) ;
Directory . CreateDirectory ( Path . GetDirectoryName ( FixDepsScriptPath ) ! ) ;
using ( StreamWriter Writer = File . CreateText ( FixDepsScriptPath ) )
{
if ( bUseCmdExe )
{
Writer . NewLine = "\r\n" ;
Writer . WriteLine ( "@echo off" ) ;
Writer . WriteLine ( "rem Automatically generated by UnrealBuildTool" ) ;
Writer . WriteLine ( "rem *DO NOT EDIT*" ) ;
Writer . WriteLine ( ) ;
}
else
{
Writer . NewLine = "\n" ;
Writer . WriteLine ( "#!/bin/sh" ) ;
Writer . WriteLine ( "# Automatically generated by UnrealBuildTool" ) ;
Writer . WriteLine ( "# *DO NOT EDIT*" ) ;
Writer . WriteLine ( ) ;
Writer . WriteLine ( "set -o errexit" ) ;
}
}
}
StreamWriter FixDepsScript = File . AppendText ( FixDepsScriptPath ) ;
FixDepsScript . NewLine = bUseCmdExe ? "\r\n" : "\n" ;
string EngineAndGameLibrariesString = "" ;
foreach ( string Library in EngineAndGameLibrariesLinkFlags )
{
EngineAndGameLibrariesString + = Library ;
}
FixDepsScript . WriteLine ( "echo Fixing {0}" , Path . GetFileName ( OutputFile . AbsolutePath ) ) ;
if ( ! bUseCmdExe )
{
FixDepsScript . WriteLine ( "TIMESTAMP=`stat --format %y \"{0}\"`" , OutputFile . AbsolutePath ) ;
}
string FixDepsLine = LinkCommandString ;
string Replace = "-Wl,--allow-shlib-undefined" ;
FixDepsLine = FixDepsLine . Replace ( Replace , EngineAndGameLibrariesString ) ;
string OutputFileForwardSlashes = OutputFile . AbsolutePath . Replace ( "\\" , "/" ) ;
FixDepsLine = FixDepsLine . Replace ( OutputFileForwardSlashes , OutputFileForwardSlashes + ".fixed" ) ;
FixDepsLine = FixDepsLine . Replace ( "$" , "\\$" ) ;
FixDepsScript . WriteLine ( FixDepsLine ) ;
if ( bUseCmdExe )
{
FixDepsScript . WriteLine ( "move /Y \"{0}.fixed\" \"{0}\"" , OutputFile . AbsolutePath ) ;
}
else
{
FixDepsScript . WriteLine ( "mv \"{0}.fixed\" \"{0}\"" , OutputFile . AbsolutePath ) ;
FixDepsScript . WriteLine ( "touch -d \"$TIMESTAMP\" \"{0}\"" , OutputFile . AbsolutePath ) ;
FixDepsScript . WriteLine ( ) ;
}
FixDepsScript . Close ( ) ;
}
else
{
// Create the action to relink the library. This actions does not overwrite the source file so it can be executed in parallel
Action RelinkAction = Graph . CreateAction ( ActionType . Link ) ;
RelinkAction . WorkingDirectory = LinkAction . WorkingDirectory ;
RelinkAction . StatusDescription = LinkAction . StatusDescription ;
RelinkAction . CommandDescription = "Relink" ;
RelinkAction . bCanExecuteRemotely = false ;
RelinkAction . ProducedItems . Clear ( ) ;
2023-01-17 16:35:14 -05:00
RelinkAction . PrerequisiteItems = new SortedSet < FileItem > ( LinkAction . PrerequisiteItems ) ;
2022-06-01 16:41:24 -04:00
foreach ( FileItem Dependency in EngineAndGameLibrariesFiles )
{
RelinkAction . PrerequisiteItems . Add ( Dependency ) ;
}
RelinkAction . PrerequisiteItems . Add ( OutputFile ) ; // also depend on the first link action's output
string LinkOutputFileForwardSlashes = OutputFile . AbsolutePath . Replace ( "\\" , "/" ) ;
string RelinkedFileForwardSlashes = Path . Combine ( LinkEnvironment . LocalShadowDirectory . FullName , OutputFile . Location . GetFileName ( ) ) + ".relinked" ;
// cannot use the real product because we need to maintain the timestamp on it
FileReference RelinkActionDummyProductRef = FileReference . Combine ( LinkEnvironment . LocalShadowDirectory , LinkEnvironment . OutputFilePath . GetFileNameWithoutExtension ( ) + ".relinked_action_ran" ) ;
RelinkAction . ProducedItems . Add ( FileItem . GetItemByFileReference ( RelinkActionDummyProductRef ) ) ;
string EngineAndGameLibrariesString = "" ;
foreach ( string Library in EngineAndGameLibrariesLinkFlags )
{
EngineAndGameLibrariesString + = Library ;
}
// create the relinking step
string RelinkScriptName = string . Format ( ( bUseCmdExe ? "Relink-{0}.bat" : "Relink-{0}.sh" ) , OutputFile . Location . GetFileName ( ) ) ;
string RelinkScriptFullPath = Path . Combine ( LinkEnvironment . LocalShadowDirectory . FullName , RelinkScriptName ) ;
Logger . LogDebug ( "Creating script: {RelinkScriptFullPath}" , RelinkScriptFullPath ) ;
Directory . CreateDirectory ( Path . GetDirectoryName ( RelinkScriptFullPath ) ! ) ;
using ( StreamWriter RelinkWriter = File . CreateText ( RelinkScriptFullPath ) )
{
string RelinkInvocation = LinkCommandString ;
string Replace = "-Wl,--allow-shlib-undefined" ;
RelinkInvocation = RelinkInvocation . Replace ( Replace , EngineAndGameLibrariesString ) ;
// should be the same as RelinkedFileRef
RelinkInvocation = RelinkInvocation . Replace ( LinkOutputFileForwardSlashes , RelinkedFileForwardSlashes ) ;
RelinkInvocation = RelinkInvocation . Replace ( "$" , "\\$" ) ;
if ( bUseCmdExe )
{
RelinkWriter . WriteLine ( "@echo off" ) ;
RelinkWriter . WriteLine ( "rem Automatically generated by UnrealBuildTool" ) ;
RelinkWriter . WriteLine ( "rem *DO NOT EDIT*" ) ;
RelinkWriter . WriteLine ( ) ;
RelinkWriter . WriteLine ( "set Retries=0" ) ;
RelinkWriter . WriteLine ( ":relinkloop" ) ;
RelinkWriter . WriteLine ( "if %Retries% GEQ 10 goto failedtorelink" ) ;
RelinkWriter . WriteLine ( RelinkInvocation ) ;
RelinkWriter . WriteLine ( "if %errorlevel% neq 0 goto sleepandretry" ) ;
RelinkWriter . WriteLine ( "copy /B \"{0}\" \"{1}.temp\" >NUL 2>NUL" , RelinkedFileForwardSlashes , OutputFile . AbsolutePath ) ;
RelinkWriter . WriteLine ( "if %errorlevel% neq 0 goto sleepandretry" ) ;
RelinkWriter . WriteLine ( "move /Y \"{0}.temp\" \"{1}\" >NUL 2>NUL" , OutputFile . AbsolutePath , OutputFile . AbsolutePath ) ;
RelinkWriter . WriteLine ( "if %errorlevel% neq 0 goto sleepandretry" ) ;
RelinkWriter . WriteLine ( GetDumpEncodeDebugCommand ( LinkEnvironment , OutputFile ) ) ;
RelinkWriter . WriteLine ( "echo \"Dummy\" >> \"{0}\" && copy /b \"{0}\" +,," , RelinkActionDummyProductRef . FullName ) ;
RelinkWriter . WriteLine ( "echo Relinked {0} successfully after %Retries% retries" , OutputFile . AbsolutePath ) ;
RelinkWriter . WriteLine ( "exit 0" ) ;
RelinkWriter . WriteLine ( ":sleepandretry" ) ;
RelinkWriter . WriteLine ( "ping 127.0.0.1 -n 1 -w 5000 >NUL 2>NUL" ) ; // timeout complains about lack of redirection
RelinkWriter . WriteLine ( "set /a Retries+=1" ) ;
RelinkWriter . WriteLine ( "goto relinkloop" ) ;
RelinkWriter . WriteLine ( ":failedtorelink" ) ;
RelinkWriter . WriteLine ( "echo Failed to relink {0} after %Retries% retries" , OutputFile . AbsolutePath ) ;
RelinkWriter . WriteLine ( "exit 1" ) ;
}
else
{
RelinkWriter . NewLine = "\n" ;
RelinkWriter . WriteLine ( "#!/bin/sh" ) ;
RelinkWriter . WriteLine ( "# Automatically generated by UnrealBuildTool" ) ;
RelinkWriter . WriteLine ( "# *DO NOT EDIT*" ) ;
RelinkWriter . WriteLine ( ) ;
RelinkWriter . WriteLine ( "set -o errexit" ) ;
RelinkWriter . WriteLine ( RelinkInvocation ) ;
RelinkWriter . WriteLine ( "TIMESTAMP=`stat --format %y \"{0}\"`" , OutputFile . AbsolutePath ) ;
RelinkWriter . WriteLine ( "cp \"{0}\" \"{1}.temp\"" , RelinkedFileForwardSlashes , OutputFile . AbsolutePath ) ;
RelinkWriter . WriteLine ( "mv \"{0}.temp\" \"{1}\"" , OutputFile . AbsolutePath , OutputFile . AbsolutePath ) ;
RelinkWriter . WriteLine ( GetDumpEncodeDebugCommand ( LinkEnvironment , OutputFile ) ) ;
RelinkWriter . WriteLine ( "touch -d \"$TIMESTAMP\" \"{0}\"" , OutputFile . AbsolutePath ) ;
RelinkWriter . WriteLine ( ) ;
RelinkWriter . WriteLine ( "echo \"Dummy\" >> \"{0}\"" , RelinkActionDummyProductRef . FullName ) ;
}
}
RelinkAction . CommandPath = ShellBinary ;
RelinkAction . CommandArguments = ExecuteSwitch + " \"" + RelinkScriptFullPath + "\"" ;
}
}
return OutputFile ;
}
2023-01-25 17:55:46 -05:00
public override void SetupBundleDependencies ( ReadOnlyTargetRules Target , List < UEBuildBinary > Binaries , string GameName )
2022-06-01 16:41:24 -04:00
{
if ( bUseFixdeps )
{
2023-01-25 17:55:46 -05:00
if ( ! AllBinaries . ContainsKey ( Target ) )
{
AllBinaries . Add ( Target , new ( ) ) ;
}
2022-06-01 16:41:24 -04:00
foreach ( UEBuildBinary Binary in Binaries )
{
2023-01-25 17:55:46 -05:00
AllBinaries [ Target ] . Add ( FileItem . GetItemByFileReference ( Binary . OutputFilePath ) ) ;
2022-06-01 16:41:24 -04:00
}
}
}
2023-01-25 17:55:46 -05:00
public override ICollection < FileItem > PostBuild ( ReadOnlyTargetRules Target , FileItem Executable , LinkEnvironment BinaryLinkEnvironment , IActionGraphBuilder Graph )
2022-06-01 16:41:24 -04:00
{
2023-01-25 17:55:46 -05:00
ICollection < FileItem > OutputFiles = base . PostBuild ( Target , Executable , BinaryLinkEnvironment , Graph ) ;
2022-06-01 16:41:24 -04:00
if ( bUseFixdeps )
{
if ( BinaryLinkEnvironment . bIsBuildingDLL | | BinaryLinkEnvironment . bIsBuildingLibrary )
{
return OutputFiles ;
}
2023-01-25 17:55:46 -05:00
FileItem ? FixDepsOutputFile = FixDependencies ( Target , BinaryLinkEnvironment , Executable , Graph , Logger ) ;
2022-06-01 16:41:24 -04:00
if ( FixDepsOutputFile ! = null )
{
OutputFiles . Add ( FixDepsOutputFile ) ;
}
}
else
{
// make build product of cross-referenced DSOs to be *.relinked_action_ran, so the relinking steps are executed
if ( BinaryLinkEnvironment . bIsBuildingDLL & & BinaryLinkEnvironment . bIsCrossReferenced )
{
FileReference RelinkedMapRef = FileReference . Combine ( BinaryLinkEnvironment . LocalShadowDirectory ! , BinaryLinkEnvironment . OutputFilePath . GetFileNameWithoutExtension ( ) + ".relinked_action_ran" ) ;
OutputFiles . Add ( FileItem . GetItemByFileReference ( RelinkedMapRef ) ) ;
}
}
return OutputFiles ;
}
public void StripSymbols ( FileReference SourceFile , FileReference TargetFile , ILogger Logger )
{
if ( SourceFile ! = TargetFile )
{
// Strip command only works in place so we need to copy original if target is different
File . Copy ( SourceFile . FullName , TargetFile . FullName , true ) ;
}
ProcessStartInfo StartInfo = new ProcessStartInfo ( ) ;
2022-07-11 16:55:46 -04:00
StartInfo . FileName = LinuxInfo . Objcopy . FullName ;
2022-06-01 16:41:24 -04:00
StartInfo . Arguments = "--strip-debug \"" + TargetFile . FullName + "\"" ;
StartInfo . UseShellExecute = false ;
StartInfo . CreateNoWindow = true ;
Utils . RunLocalProcessAndLogOutput ( StartInfo , Logger ) ;
}
2022-08-23 16:36:20 -04:00
public override void AddExtraToolArguments ( IList < string > ExtraArguments )
{
// We explicitly include the clang include directories so tools like IWYU can run outside of the clang directory.
// More info: https://github.com/include-what-you-use/include-what-you-use#how-to-install
string? InternalSdkPath = UEBuildPlatform . GetSDK ( UnrealTargetPlatform . Linux ) ! . GetInternalSDKPath ( ) ;
if ( InternalSdkPath ! = null )
{
ExtraArguments . Add ( String . Format ( "-isystem\"{0}\"" , System . IO . Path . Combine ( InternalSdkPath , "lib/clang/" + Info . ClangVersion + "/include/" ) . Replace ( "\\" , "/" ) ) ) ;
}
}
2022-06-01 16:41:24 -04:00
}
}