2014-03-14 14:13:41 -04:00
// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved.
using System ;
using System.Collections.Generic ;
using System.Text.RegularExpressions ;
using System.Diagnostics ;
using System.IO ;
using Microsoft.Win32 ;
2014-07-17 13:49:42 -04:00
namespace UnrealBuildTool
2014-03-14 14:13:41 -04:00
{
class LinuxToolChain : UEToolChain
{
2014-04-02 18:09:23 -04:00
protected static bool CrossCompiling ( )
{
2014-08-06 07:05:15 -04:00
return BuildHostPlatform . Current . Platform ! = UnrealTargetPlatform . Linux ;
2014-04-02 18:09:23 -04:00
}
2014-05-30 10:58:16 -04:00
protected static bool UsingClang ( )
{
return ! String . IsNullOrEmpty ( ClangPath ) ;
}
2014-05-08 13:01:46 -04:00
private string Which ( string name )
{
Process proc = new Process ( ) ;
proc . StartInfo . FileName = "/bin/sh" ;
proc . StartInfo . Arguments = String . Format ( "-c 'which {0}'" , name ) ;
proc . StartInfo . UseShellExecute = false ;
proc . StartInfo . CreateNoWindow = true ;
proc . StartInfo . RedirectStandardOutput = true ;
proc . StartInfo . RedirectStandardError = true ;
proc . Start ( ) ;
proc . WaitForExit ( ) ;
string path = proc . StandardOutput . ReadLine ( ) ;
2014-07-17 13:49:42 -04:00
Log . TraceVerbose ( String . Format ( "which {0} result: ({1}) {2}" , name , proc . ExitCode , path ) ) ;
2014-05-08 13:01:46 -04:00
if ( proc . ExitCode = = 0 & & String . IsNullOrEmpty ( proc . StandardError . ReadToEnd ( ) ) )
{
return path ;
}
return null ;
}
2014-03-14 14:13:41 -04:00
public override void RegisterToolChain ( )
{
2014-04-02 18:09:23 -04:00
if ( ! CrossCompiling ( ) )
{
// use native linux toolchain
2014-05-08 13:01:46 -04:00
ClangPath = Which ( "clang++" ) ;
GCCPath = Which ( "g++" ) ;
ArPath = Which ( "ar" ) ;
RanlibPath = Which ( "ranlib" ) ;
2014-04-02 18:09:23 -04:00
}
else
{
// use cross linux toolchain if LINUX_ROOT is specified
BaseLinuxPath = Environment . GetEnvironmentVariable ( "LINUX_ROOT" ) ;
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
// don't register if we don't have an LINUX_ROOT specified
if ( String . IsNullOrEmpty ( BaseLinuxPath ) )
return ;
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
BaseLinuxPath = BaseLinuxPath . Replace ( "\"" , "" ) ;
2014-03-14 14:13:41 -04:00
2014-05-30 10:58:16 -04:00
// set up the path to our toolchains (FIXME: support switching per architecture)
GCCPath = "" ;
2014-04-02 18:09:23 -04:00
ClangPath = Path . Combine ( BaseLinuxPath , @"bin\Clang++.exe" ) ;
ArPath = Path . Combine ( BaseLinuxPath , @"bin\x86_64-unknown-linux-gnu-ar.exe" ) ;
RanlibPath = Path . Combine ( BaseLinuxPath , @"bin\x86_64-unknown-linux-gnu-ranlib.exe" ) ;
}
2014-03-14 14:13:41 -04:00
// Register this tool chain for both Linux
if ( BuildConfiguration . bPrintDebugInfo )
{
Console . WriteLine ( " Registered for {0}" , CPPTargetPlatform . Linux . ToString ( ) ) ;
}
UEToolChain . RegisterPlatformToolChain ( CPPTargetPlatform . Linux , this ) ;
}
static string GetCLArguments_Global ( CPPEnvironment CompileEnvironment )
{
string Result = "" ;
// build up the commandline common to C and C++
Result + = " -c" ;
Result + = " -pipe" ;
2014-04-02 18:09:23 -04:00
if ( CrossCompiling ( ) )
{
2014-08-08 21:42:14 -04:00
// There are exceptions used in the code base (e.g. UnrealHeadTool). @todo: weed out exceptions
// So this flag cannot be used, at least not for native Linux builds.
2014-04-02 18:09:23 -04:00
Result + = " -fno-exceptions" ; // no exceptions
2014-08-08 21:42:14 -04:00
Result + = " -DPLATFORM_EXCEPTIONS_DISABLED=1" ;
}
else
{
Result + = " -DPLATFORM_EXCEPTIONS_DISABLED=0" ;
2014-04-02 18:09:23 -04:00
}
2014-03-14 14:13:41 -04:00
Result + = " -Wall -Werror" ;
2014-07-17 13:49:42 -04:00
// test without this next line?
Result + = " -funwind-tables" ; // generate unwind tables as they seem to be needed for stack tracing (why??)
2014-03-14 14:13:41 -04:00
Result + = " -Wsequence-point" ; // additional warning not normally included in Wall: warns if order of operations is ambigious
//Result += " -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
//Result += " -Wshadow"; // additional warning not normally included in Wall: warns if there variable/typedef shadows some other variable - not helpful because we have gobs of code that shadows variables
Result + = " -mmmx -msse -msse2" ; // allows use of SIMD intrinsics
Result + = " -fno-math-errno" ; // do not assume that math ops have side effects
2014-05-29 17:47:36 -04:00
Result + = " -fno-rtti" ; // no run-time type info
2014-05-08 13:01:46 -04:00
2014-07-17 13:49:42 -04:00
// these needs to be removed ASAP
Result + = " -Wno-delete-non-virtual-dtor" ;
Result + = " -Wno-reorder" ;
Result + = " -Wno-logical-op-parentheses" ;
Result + = " -Wno-ignored-attributes" ;
Result + = " -Wno-overloaded-virtual" ;
Result + = " -Wno-unused-value" ;
2014-05-08 13:01:46 -04:00
if ( String . IsNullOrEmpty ( ClangPath ) )
{
// GCC only option
Result + = " -fno-strict-aliasing" ;
Result + = " -Wno-sign-compare" ; // needed to suppress: comparison between signed and unsigned integer expressions
Result + = " -Wno-enum-compare" ; // Stats2.h triggers this (ALIGNOF(int64) <= DATA_ALIGN)
Result + = " -Wno-return-type" ; // Variant.h triggers this
Result + = " -Wno-unused-local-typedefs" ;
Result + = " -Wno-multichar" ;
Result + = " -Wno-unused-but-set-variable" ;
Result + = " -Wno-sequence-point" ; // TaskGraph.cpp:755 FTaskThread* Target = Target = &Thread(ThreadToExecuteOn);
Result + = " -Wno-strict-overflow" ; // Array.h:518
}
else
{
// Clang only options
Result + = " -fdiagnostics-format=msvc" ; // make diagnostics compatible with MSVC
Result + = " -Wno-unused-private-field" ; // MultichannelTcpSocket.h triggers this, possibly more
// this hides the "warning : comparison of unsigned expression < 0 is always false" type warnings due to constant comparisons, which are possible with template arguments
Result + = " -Wno-tautological-compare" ;
if ( ! CrossCompiling ( ) )
{
Result + = " -Wno-logical-op-parentheses" ; // needed for external headers we can't change
}
}
2014-03-14 14:13:41 -04:00
Result + = " -Wno-unused-variable" ;
// this will hide the warnings about static functions in headers that aren't used in every single .cpp file
Result + = " -Wno-unused-function" ;
// this hides the "enumeration value 'XXXXX' not handled in switch [-Wswitch]" warnings - we should maybe remove this at some point and add UE_LOG(, Fatal, ) to default cases
Result + = " -Wno-switch" ;
Result + = " -Wno-unknown-pragmas" ; // Slate triggers this (with its optimize on/off pragmas)
Result + = " -Wno-invalid-offsetof" ; // needed to suppress warnings about using offsetof on non-POD types.
//Result += " -DOPERATOR_NEW_INLINE=FORCENOINLINE";
// shipping builds will cause this warning with "ensure", so disable only in those case
2014-06-09 11:12:01 -04:00
if ( CompileEnvironment . Config . Target . Configuration = = CPPTargetConfiguration . Shipping )
2014-03-14 14:13:41 -04:00
{
Result + = " -Wno-unused-value" ;
2014-05-09 22:24:58 -04:00
// in shipping, strip as much info as possible
Result + = " -g0" ;
Result + = " -fomit-frame-pointer" ;
Result + = " -fvisibility=hidden" ; // prevents from exporting all symbols (reduces the size of the binary)
}
2014-06-09 11:12:01 -04:00
else if ( CompileEnvironment . Config . Target . Configuration = = CPPTargetConfiguration . Debug )
2014-05-09 22:24:58 -04:00
{
2014-05-29 17:47:36 -04:00
Result + = " -fno-inline" ; // disable inlining for better debuggability (e.g. callstacks, "skip file" in gdb)
2014-03-14 14:13:41 -04:00
}
// debug info
if ( CompileEnvironment . Config . bCreateDebugInfo )
{
Result + = " -g3" ;
2014-04-02 18:09:23 -04:00
Result + = " -fno-omit-frame-pointer" ;
2014-07-17 13:49:42 -04:00
Result + = " -funwind-tables" ; // generate unwind tables as they seem to be needed for stack tracing
2014-04-02 18:09:23 -04:00
Result + = " -fstack-protector" ;
//Result += " -fsanitize=address"; // Preferred clang tool for detecting address based errors but unusable for some reason with Module.Engine.7_of_42.cpp
2014-03-14 14:13:41 -04:00
}
2014-06-09 11:12:01 -04:00
else if ( CompileEnvironment . Config . Target . Configuration < CPPTargetConfiguration . Shipping )
2014-03-14 14:13:41 -04:00
{
Result + = " -gline-tables-only" ; // include debug info for meaningful callstacks
}
// optimization level
2014-06-09 11:12:01 -04:00
if ( CompileEnvironment . Config . Target . Configuration = = CPPTargetConfiguration . Debug )
2014-03-14 14:13:41 -04:00
{
Result + = " -O0" ;
}
else
{
2014-03-15 01:14:25 -04:00
Result + = " -O2" ;
2014-03-14 14:13:41 -04:00
}
2014-04-02 18:09:23 -04:00
if ( CompileEnvironment . Config . bIsBuildingDLL )
{
Result + = " -fPIC" ;
}
2014-03-14 14:13:41 -04:00
//Result += " -v"; // for better error diagnosis
2014-05-09 22:24:58 -04:00
// assume we will not perform 32 bit builds on Linux, so define
// _LINUX64 in any case
2014-05-30 10:58:16 -04:00
Result + = " -D_LINUX64" ;
2014-04-02 18:09:23 -04:00
if ( CrossCompiling ( ) )
{
2014-05-30 10:58:16 -04:00
if ( UsingClang ( ) )
{
2014-06-10 17:14:40 -04:00
Result + = String . Format ( " -target {0}" , CompileEnvironment . Config . Target . Architecture ) ; // Set target triple
2014-05-30 10:58:16 -04:00
}
2014-04-02 18:09:23 -04:00
Result + = String . Format ( " --sysroot={0}" , BaseLinuxPath ) ;
}
2014-03-14 14:13:41 -04:00
return Result ;
}
static string GetCompileArguments_CPP ( )
{
string Result = "" ;
Result + = " -x c++" ;
Result + = " -std=c++11" ;
return Result ;
}
static string GetCompileArguments_C ( )
{
string Result = "" ;
Result + = " -x c" ;
return Result ;
}
static string GetCompileArguments_MM ( )
{
string Result = "" ;
Result + = " -x objective-c++" ;
Result + = " -fobjc-abi-version=2" ;
Result + = " -fobjc-legacy-dispatch" ;
Result + = " -fno-rtti" ;
Result + = " -std=c++11" ;
return Result ;
}
static string GetCompileArguments_M ( )
{
string Result = "" ;
Result + = " -x objective-c" ;
Result + = " -fobjc-abi-version=2" ;
Result + = " -fobjc-legacy-dispatch" ;
Result + = " -std=c++11" ;
return Result ;
}
static string GetCompileArguments_PCH ( )
{
string Result = "" ;
Result + = " -x c++-header" ;
Result + = " -std=c++11" ;
return Result ;
}
static string GetLinkArguments ( LinkEnvironment LinkEnvironment )
{
string Result = "" ;
// debugging symbols
2014-06-09 11:12:01 -04:00
if ( LinkEnvironment . Config . Target . Configuration < CPPTargetConfiguration . Shipping )
2014-03-14 14:13:41 -04:00
{
2014-04-23 18:38:28 -04:00
Result + = " -rdynamic" ; // needed for backtrace_symbols()...
2014-03-14 14:13:41 -04:00
}
else
{
2014-04-02 18:09:23 -04:00
Result + = " -s" ; // Strip binaries in Shipping
2014-03-14 14:13:41 -04:00
}
if ( LinkEnvironment . Config . bIsBuildingDLL )
{
Result + = " -shared" ;
}
2014-04-02 18:09:23 -04:00
else
{
2014-07-17 13:49:42 -04:00
// ignore unresolved symbols in shared libs
Result + = string . Format ( " -Wl,--unresolved-symbols=ignore-in-shared-libs" ) ;
2014-04-02 18:09:23 -04:00
}
2014-03-14 14:13:41 -04:00
2014-04-02 18:09:23 -04:00
// RPATH for third party libs
2014-07-17 13:49:42 -04:00
Result + = " -Wl,-rpath=${{ORIGIN}}" ;
Result + = " -Wl,-rpath-link=${{ORIGIN}}" ;
2014-05-08 13:01:46 -04:00
Result + = " -Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/Linux" ;
2014-07-03 23:30:39 -04:00
// FIXME: really ugly temp solution. Modules need to be able to specify this
Result + = " -Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/jemalloc/Linux/x86_64-unknown-linux-gnu" ;
Result + = " -Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/ICU/icu4c-53_1/Linux/x86_64-unknown-linux-gnu" ;
2014-07-03 23:40:45 -04:00
Result + = " -Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/SDL2/Linux/x86_64-unknown-linux-gnu" ;
Result + = " -Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/zlib/Linux/x86_64-unknown-linux-gnu" ;
2014-07-17 23:57:18 -04:00
Result + = " -Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/libPNG/Linux/x86_64-unknown-linux-gnu" ;
2014-04-02 18:09:23 -04:00
if ( CrossCompiling ( ) )
{
2014-05-30 10:58:16 -04:00
if ( UsingClang ( ) )
{
2014-06-10 17:14:40 -04:00
Result + = String . Format ( " -target {0}" , LinkEnvironment . Config . Target . Architecture ) ; // Set target triple
2014-05-30 10:58:16 -04:00
}
2014-04-02 18:09:23 -04:00
string SysRootPath = BaseLinuxPath . TrimEnd ( new char [ ] { '\\' , '/' } ) ;
Result + = String . Format ( " \"--sysroot={0}\"" , SysRootPath ) ;
}
2014-03-14 14:13:41 -04:00
return Result ;
}
static string GetArchiveArguments ( LinkEnvironment LinkEnvironment )
{
return " rc" ;
}
public static void CompileOutputReceivedDataEventHandler ( Object Sender , DataReceivedEventArgs e )
{
string Output = e . Data ;
if ( String . IsNullOrEmpty ( Output ) )
{
return ;
}
// Need to match following for clickable links
string RegexFilePath = @"^[A-Z]\:([\\\/][A-Za-z0-9_\-\.]*)+\.(cpp|c|mm|m|hpp|h)" ;
string RegexLineNumber = @"\:\d+\:\d+\:" ;
string RegexDescription = @"(\serror:\s|\swarning:\s|\snote:\s).*" ;
// Get Matches
string MatchFilePath = Regex . Match ( Output , RegexFilePath ) . Value . Replace ( "Engine\\Source\\..\\..\\" , "" ) ;
string MatchLineNumber = Regex . Match ( Output , RegexLineNumber ) . Value ;
string MatchDescription = Regex . Match ( Output , RegexDescription ) . Value ;
// If any of the above matches failed, do nothing
if ( MatchFilePath . Length = = 0 | |
MatchLineNumber . Length = = 0 | |
MatchDescription . Length = = 0 )
{
Console . WriteLine ( Output ) ;
return ;
}
// Convert Path
string RegexStrippedPath = @"\\Engine\\.*" ; //@"(Engine\/|[A-Za-z0-9_\-\.]*\/).*";
string ConvertedFilePath = Regex . Match ( MatchFilePath , RegexStrippedPath ) . Value ;
ConvertedFilePath = Path . GetFullPath ( "..\\.." + ConvertedFilePath ) ;
// Extract Line + Column Number
string ConvertedLineNumber = Regex . Match ( MatchLineNumber , @"\d+" ) . Value ;
string ConvertedColumnNumber = Regex . Match ( MatchLineNumber , @"(?<=:\d+:)\d+" ) . Value ;
// Write output
string ConvertedExpression = " " + ConvertedFilePath + "(" + ConvertedLineNumber + "," + ConvertedColumnNumber + "):" + MatchDescription ;
Console . WriteLine ( ConvertedExpression ) ; // To create clickable vs link
}
// cache the location of NDK tools
static string BaseLinuxPath ;
static string ClangPath ;
2014-05-08 13:01:46 -04:00
static string GCCPath ;
2014-03-14 14:13:41 -04:00
static string ArPath ;
static string RanlibPath ;
2014-07-17 13:49:42 -04:00
/** Track which scripts need to be deleted before appending to */
private bool bHasWipedFixDepsScript = false ;
private static List < FileItem > BundleDependencies = new List < FileItem > ( ) ;
2014-07-31 09:34:11 -04:00
public override CPPOutput CompileCPPFiles ( UEBuildTarget Target , CPPEnvironment CompileEnvironment , List < FileItem > SourceFiles , string ModuleName )
2014-03-14 14:13:41 -04:00
{
string Arguments = GetCLArguments_Global ( CompileEnvironment ) ;
string PCHArguments = "" ;
2014-07-17 13:49:42 -04:00
if ( CompileEnvironment . Config . bIsBuildingDLL )
{
Arguments + = " -fPIC" ;
}
2014-03-14 14:13:41 -04:00
if ( CompileEnvironment . Config . PrecompiledHeaderAction = = PrecompiledHeaderAction . Include )
{
// Add the precompiled header file's path to the include path so Clang can find it.
// This needs to be before the other include paths to ensure Clang uses it instead of the source header file.
2014-06-05 12:11:58 -04:00
var PrecompiledFileExtension = UEBuildPlatform . BuildPlatformDictionary [ UnrealTargetPlatform . Linux ] . GetBinaryExtension ( UEBuildBinaryType . PrecompiledHeader ) ;
PCHArguments + = string . Format ( " -include \"{0}\"" , CompileEnvironment . PrecompiledHeaderFile . AbsolutePath . Replace ( PrecompiledFileExtension , "" ) ) ;
2014-03-14 14:13:41 -04:00
}
// Add include paths to the argument list.
foreach ( string IncludePath in CompileEnvironment . Config . IncludePaths )
{
Arguments + = string . Format ( " -I\"{0}\"" , IncludePath ) ;
}
foreach ( string IncludePath in CompileEnvironment . Config . SystemIncludePaths )
{
Arguments + = string . Format ( " -I\"{0}\"" , IncludePath ) ;
}
// Add preprocessor definitions to the argument list.
foreach ( string Definition in CompileEnvironment . Config . Definitions )
{
Arguments + = string . Format ( " -D \"{0}\"" , Definition ) ;
}
2014-07-31 09:34:11 -04:00
var BuildPlatform = UEBuildPlatform . GetBuildPlatformForCPPTargetPlatform ( CompileEnvironment . Config . Target . Platform ) ;
2014-03-14 14:13:41 -04:00
// Create a compile action for each source file.
CPPOutput Result = new CPPOutput ( ) ;
foreach ( FileItem SourceFile in SourceFiles )
{
Action CompileAction = new Action ( ActionType . Compile ) ;
string FileArguments = "" ;
string Extension = Path . GetExtension ( SourceFile . AbsolutePath ) . ToUpperInvariant ( ) ;
// Add C or C++ specific compiler arguments.
if ( CompileEnvironment . Config . PrecompiledHeaderAction = = PrecompiledHeaderAction . Create )
{
FileArguments + = GetCompileArguments_PCH ( ) ;
}
else if ( Extension = = ".C" )
{
// Compile the file as C code.
FileArguments + = GetCompileArguments_C ( ) ;
}
else if ( Extension = = ".CC" )
{
// Compile the file as C++ code.
FileArguments + = GetCompileArguments_CPP ( ) ;
}
else if ( Extension = = ".MM" )
{
// Compile the file as Objective-C++ code.
FileArguments + = GetCompileArguments_MM ( ) ;
}
else if ( Extension = = ".M" )
{
// Compile the file as Objective-C code.
FileArguments + = GetCompileArguments_M ( ) ;
}
else
{
FileArguments + = GetCompileArguments_CPP ( ) ;
// only use PCH for .cpp files
FileArguments + = PCHArguments ;
}
// Add the C++ source file and its included files to the prerequisite item list.
CompileAction . PrerequisiteItems . Add ( SourceFile ) ;
2014-07-31 09:34:11 -04:00
{
var IncludedFileList = CPPEnvironment . FindAndCacheAllIncludedFiles ( Target , SourceFile , BuildPlatform , CompileEnvironment . GetIncludesPathsToSearch ( SourceFile ) , CompileEnvironment . IncludeFileSearchDictionary , bOnlyCachedDependencies : BuildConfiguration . bUseExperimentalFastDependencyScan ) ;
CompileAction . PrerequisiteItems . AddRange ( IncludedFileList ) ;
}
2014-03-14 14:13:41 -04:00
if ( CompileEnvironment . Config . PrecompiledHeaderAction = = PrecompiledHeaderAction . Create )
{
2014-06-05 12:11:58 -04:00
var PrecompiledFileExtension = UEBuildPlatform . BuildPlatformDictionary [ UnrealTargetPlatform . Linux ] . GetBinaryExtension ( UEBuildBinaryType . PrecompiledHeader ) ;
2014-03-14 14:13:41 -04:00
// Add the precompiled header file to the produced item list.
FileItem PrecompiledHeaderFile = FileItem . GetItemByPath (
Path . Combine (
CompileEnvironment . Config . OutputDirectory ,
2014-06-05 12:11:58 -04:00
Path . GetFileName ( SourceFile . AbsolutePath ) + PrecompiledFileExtension
2014-03-14 14:13:41 -04:00
)
) ;
CompileAction . ProducedItems . Add ( PrecompiledHeaderFile ) ;
Result . PrecompiledHeaderFile = PrecompiledHeaderFile ;
// Add the parameters needed to compile the precompiled header file to the command-line.
FileArguments + = string . Format ( " -o \"{0}\"" , PrecompiledHeaderFile . AbsolutePath , false ) ;
}
else
{
if ( CompileEnvironment . Config . PrecompiledHeaderAction = = PrecompiledHeaderAction . Include )
{
CompileAction . bIsUsingPCH = true ;
CompileAction . PrerequisiteItems . Add ( CompileEnvironment . PrecompiledHeaderFile ) ;
}
2014-06-05 12:11:58 -04:00
var ObjectFileExtension = UEBuildPlatform . BuildPlatformDictionary [ UnrealTargetPlatform . Linux ] . GetBinaryExtension ( UEBuildBinaryType . Object ) ;
2014-03-14 14:13:41 -04:00
// Add the object file to the produced item list.
FileItem ObjectFile = FileItem . GetItemByPath (
Path . Combine (
CompileEnvironment . Config . OutputDirectory ,
2014-06-05 12:11:58 -04:00
Path . GetFileName ( SourceFile . AbsolutePath ) + ObjectFileExtension
2014-03-14 14:13:41 -04:00
)
) ;
CompileAction . ProducedItems . Add ( ObjectFile ) ;
Result . ObjectFiles . Add ( ObjectFile ) ;
FileArguments + = string . Format ( " -o \"{0}\"" , ObjectFile . AbsolutePath , false ) ;
}
// Add the source file path to the command-line.
FileArguments + = string . Format ( " \"{0}\"" , SourceFile . AbsolutePath ) ;
CompileAction . WorkingDirectory = Path . GetFullPath ( "." ) ;
2014-05-30 10:58:16 -04:00
if ( ! UsingClang ( ) )
2014-05-08 13:01:46 -04:00
{
CompileAction . CommandPath = GCCPath ;
}
else
{
CompileAction . CommandPath = ClangPath ;
}
2014-03-14 14:13:41 -04:00
CompileAction . CommandArguments = Arguments + FileArguments + CompileEnvironment . Config . AdditionalArguments ;
2014-04-23 18:38:28 -04:00
CompileAction . CommandDescription = "Compile" ;
CompileAction . StatusDescription = Path . GetFileName ( SourceFile . AbsolutePath ) ;
2014-03-14 14:13:41 -04:00
CompileAction . StatusDetailedDescription = SourceFile . Description ;
CompileAction . bIsGCCCompiler = true ;
// Don't farm out creation of pre-compiled headers as it is the critical path task.
CompileAction . bCanExecuteRemotely =
CompileEnvironment . Config . PrecompiledHeaderAction ! = PrecompiledHeaderAction . Create | |
BuildConfiguration . bAllowRemotelyCompiledPCHs ;
CompileAction . OutputEventHandler = new DataReceivedEventHandler ( CompileOutputReceivedDataEventHandler ) ;
}
return Result ;
}
/** Creates an action to archive all the .o files into single .a file */
public FileItem CreateArchiveAndIndex ( LinkEnvironment LinkEnvironment )
{
// Create an archive action
Action ArchiveAction = new Action ( ActionType . Link ) ;
ArchiveAction . WorkingDirectory = Path . GetFullPath ( "." ) ;
2014-08-06 07:05:15 -04:00
bool bUsingSh = BuildHostPlatform . Current . Platform ! = UnrealTargetPlatform . Win64 & & BuildHostPlatform . Current . Platform ! = UnrealTargetPlatform . Win32 ;
2014-04-02 18:09:23 -04:00
if ( bUsingSh )
{
ArchiveAction . CommandPath = "/bin/sh" ;
ArchiveAction . CommandArguments = "-c '" ;
}
else
{
ArchiveAction . CommandPath = "cmd.exe" ;
ArchiveAction . CommandArguments = "/c " ;
}
2014-03-14 14:13:41 -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 . GetItemByPath ( LinkEnvironment . Config . OutputFilePath ) ;
ArchiveAction . ProducedItems . Add ( OutputFile ) ;
2014-04-23 18:38:28 -04:00
ArchiveAction . CommandDescription = "Archive" ;
ArchiveAction . StatusDescription = Path . GetFileName ( OutputFile . AbsolutePath ) ;
2014-03-14 14:13:41 -04:00
ArchiveAction . CommandArguments + = string . Format ( "{0} {1} \"{2}\"" , ArPath , 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 ) ;
ArchiveAction . CommandArguments + = string . Format ( " \"{0}\"" , InputAbsolutePath ) ;
}
// add ranlib
ArchiveAction . CommandArguments + = string . Format ( " && {0} \"{1}\"" , RanlibPath , OutputFile . AbsolutePath ) ;
// Add the additional arguments specified by the environment.
ArchiveAction . CommandArguments + = LinkEnvironment . Config . AdditionalArguments ;
ArchiveAction . CommandArguments . Replace ( "\\" , "/" ) ;
2014-04-02 18:09:23 -04:00
if ( bUsingSh )
{
ArchiveAction . CommandArguments + = "'" ;
}
2014-03-14 14:13:41 -04:00
// Only execute linking on the local PC.
ArchiveAction . bCanExecuteRemotely = false ;
return OutputFile ;
}
2014-07-17 13:49:42 -04:00
public FileItem FixDependencies ( LinkEnvironment LinkEnvironment , FileItem Executable )
{
if ( ! LinkEnvironment . Config . bIsCrossReferenced )
{
return null ;
}
Log . TraceVerbose ( "Adding postlink step" ) ;
2014-08-06 07:05:15 -04:00
bool bUseCmdExe = BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Win64 | | BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Win32 ;
2014-07-17 13:49:42 -04:00
string ShellBinary = bUseCmdExe ? "cmd.exe" : "/bin/sh" ;
2014-07-23 00:30:22 -04:00
string ExecuteSwitch = bUseCmdExe ? " /C" : "" ; // avoid -c so scripts don't need +x
2014-07-17 13:49:42 -04:00
string ScriptName = bUseCmdExe ? "FixDependencies.bat" : "FixDependencies.sh" ;
FileItem FixDepsScript = FileItem . GetItemByFullPath ( Path . Combine ( LinkEnvironment . Config . LocalShadowDirectory , ScriptName ) ) ;
Action PostLinkAction = new Action ( ActionType . Link ) ;
PostLinkAction . WorkingDirectory = Path . GetFullPath ( "." ) ;
PostLinkAction . CommandPath = ShellBinary ;
PostLinkAction . StatusDescription = string . Format ( "{0}" , Path . GetFileName ( Executable . AbsolutePath ) ) ;
PostLinkAction . CommandDescription = "FixDeps" ;
PostLinkAction . bCanExecuteRemotely = false ;
PostLinkAction . CommandArguments = ExecuteSwitch ;
2014-07-23 00:30:22 -04:00
PostLinkAction . CommandArguments + = bUseCmdExe ? " \"" : " -c '" ;
2014-07-17 13:49:42 -04:00
FileItem OutputFile = FileItem . GetItemByPath ( Path . Combine ( LinkEnvironment . Config . 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 ) ;
foreach ( FileItem Dependency in BundleDependencies )
{
PostLinkAction . PrerequisiteItems . Add ( Dependency ) ;
}
PostLinkAction . CommandArguments + = ShellBinary + ExecuteSwitch + " \"" + FixDepsScript . AbsolutePath + "\" && " ;
2014-07-23 00:30:22 -04:00
string Touch = bUseCmdExe ? "echo \"\" >> \"{0}\" && copy /b \"{0}\" +,," : "touch \"{0}\"" ;
2014-07-17 13:49:42 -04:00
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 ;
}
2014-03-14 14:13:41 -04:00
public override FileItem LinkFiles ( LinkEnvironment LinkEnvironment , bool bBuildImportLibraryOnly )
{
2014-07-17 13:49:42 -04:00
Debug . Assert ( ! bBuildImportLibraryOnly ) ;
List < string > RPaths = new List < string > ( ) ;
2014-03-14 14:13:41 -04:00
if ( LinkEnvironment . Config . bIsBuildingLibrary | | bBuildImportLibraryOnly )
{
return CreateArchiveAndIndex ( LinkEnvironment ) ;
}
// Create an action that invokes the linker.
Action LinkAction = new Action ( ActionType . Link ) ;
LinkAction . WorkingDirectory = Path . GetFullPath ( "." ) ;
2014-05-08 13:01:46 -04:00
if ( String . IsNullOrEmpty ( ClangPath ) )
{
LinkAction . CommandPath = GCCPath ;
}
else
{
LinkAction . CommandPath = ClangPath ;
}
2014-03-14 14:13:41 -04:00
// Get link arguments.
LinkAction . CommandArguments = GetLinkArguments ( LinkEnvironment ) ;
// 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 . Config . bIsBuildingDLL ;
// Add the output file as a production of the link action.
FileItem OutputFile = FileItem . GetItemByPath ( LinkEnvironment . Config . OutputFilePath ) ;
LinkAction . ProducedItems . Add ( OutputFile ) ;
2014-04-23 18:38:28 -04:00
LinkAction . CommandDescription = "Link" ;
LinkAction . StatusDescription = Path . GetFileName ( OutputFile . AbsolutePath ) ;
2014-03-14 14:13:41 -04:00
// Add the output file to the command-line.
LinkAction . CommandArguments + = 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 > InputFileNames = new List < string > ( ) ;
foreach ( FileItem InputFile in LinkEnvironment . InputFiles )
{
InputFileNames . Add ( string . Format ( "\"{0}\"" , InputFile . AbsolutePath . Replace ( "\\" , "/" ) ) ) ;
LinkAction . PrerequisiteItems . Add ( InputFile ) ;
}
string ResponseFileName = GetResponseFileName ( LinkEnvironment , OutputFile ) ;
2014-05-07 20:37:19 -04:00
LinkAction . CommandArguments + = string . Format ( " -Wl,@\"{0}\"" , ResponseFile . Create ( ResponseFileName , InputFileNames ) ) ;
2014-03-14 14:13:41 -04:00
2014-07-17 13:49:42 -04:00
if ( LinkEnvironment . Config . bIsBuildingDLL )
{
LinkAction . CommandArguments + = string . Format ( " -Wl,-soname={0}" , OutputFile ) ;
}
// Start with the configured LibraryPaths and also add paths to any libraries that
// we depend on (libraries that we've build ourselves).
List < string > AllLibraryPaths = LinkEnvironment . Config . LibraryPaths ;
foreach ( string AdditionalLibrary in LinkEnvironment . Config . AdditionalLibraries )
{
string PathToLib = Path . GetDirectoryName ( AdditionalLibrary ) ;
if ( ! String . IsNullOrEmpty ( PathToLib ) )
{
// make path absolute, because FixDependencies script may be executed in a different directory
string AbsolutePathToLib = Path . GetFullPath ( 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 = Utils . MakePathRelativeTo ( Path . GetDirectoryName ( AdditionalLibrary ) , Path . GetDirectoryName ( OutputFile . AbsolutePath ) ) ;
if ( ! RPaths . Contains ( RelativePath ) )
{
RPaths . Add ( RelativePath ) ;
LinkAction . CommandArguments + = string . Format ( " -Wl,-rpath=\"${{ORIGIN}}/{0}\"" , RelativePath ) ;
}
}
}
LinkAction . CommandArguments + = string . Format ( " -Wl,-rpath-link=\"{0}\"" , Path . GetDirectoryName ( OutputFile . AbsolutePath ) ) ;
2014-03-14 14:13:41 -04:00
// Add the library paths to the argument list.
2014-07-17 13:49:42 -04:00
foreach ( string LibraryPath in AllLibraryPaths )
2014-03-14 14:13:41 -04:00
{
2014-07-17 13:49:42 -04:00
// use absolute paths because of FixDependencies script again
LinkAction . CommandArguments + = string . Format ( " -L\"{0}\"" , Path . GetFullPath ( LibraryPath ) ) ;
2014-03-14 14:13:41 -04:00
}
// add libraries in a library group
2014-04-23 18:38:28 -04:00
LinkAction . CommandArguments + = string . Format ( " -Wl,--start-group" ) ;
2014-07-17 13:49:42 -04:00
List < string > EngineAndGameLibraries = new List < string > ( ) ;
2014-04-02 18:09:23 -04:00
foreach ( string AdditionalLibrary in LinkEnvironment . Config . AdditionalLibraries )
2014-07-17 13:49:42 -04:00
{
if ( String . IsNullOrEmpty ( Path . GetDirectoryName ( AdditionalLibrary ) ) )
{
// library was passed just like "jemalloc", turn it into -ljemalloc
LinkAction . CommandArguments + = 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 ) ;
LinkAction . CommandArguments + = ( " " + 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 ) ;
var LibName = Path . GetFileNameWithoutExtension ( AdditionalLibrary ) ;
if ( LibName . StartsWith ( "lib" ) )
{
// Remove lib prefix
LibName = LibName . Remove ( 0 , 3 ) ;
}
string LibLinkFlag = string . Format ( " -l{0}" , LibName ) ;
if ( LinkEnvironment . Config . bIsBuildingDLL & & LinkEnvironment . Config . 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.
EngineAndGameLibraries . Add ( LibLinkFlag ) ;
if ( ! LinkAction . CommandArguments . Contains ( "--allow-shlib-undefined" ) )
{
LinkAction . CommandArguments + = string . Format ( " -Wl,--allow-shlib-undefined" ) ;
}
}
else
{
LinkAction . PrerequisiteItems . Add ( LibraryDependency ) ;
LinkAction . CommandArguments + = LibLinkFlag ;
}
}
}
2014-03-14 14:13:41 -04:00
LinkAction . CommandArguments + = " -lrt" ; // needed for clock_gettime()
2014-04-02 18:09:23 -04:00
LinkAction . CommandArguments + = " -lm" ; // math
2014-04-23 18:38:28 -04:00
LinkAction . CommandArguments + = string . Format ( " -Wl,--end-group" ) ;
2014-03-14 14:13:41 -04:00
// Add the additional arguments specified by the environment.
LinkAction . CommandArguments + = LinkEnvironment . Config . AdditionalArguments ;
2014-07-17 15:50:19 -04:00
LinkAction . CommandArguments = LinkAction . CommandArguments . Replace ( "\\\\" , "/" ) ;
LinkAction . CommandArguments = LinkAction . CommandArguments . Replace ( "\\" , "/" ) ;
2014-03-14 14:13:41 -04:00
2014-05-29 16:54:33 -04:00
// prepare a linker script
string LinkerScriptPath = Path . Combine ( LinkEnvironment . Config . LocalShadowDirectory , "remove-sym.ldscript" ) ;
if ( ! Directory . Exists ( LinkEnvironment . Config . LocalShadowDirectory ) )
{
Directory . CreateDirectory ( LinkEnvironment . Config . LocalShadowDirectory ) ;
}
if ( File . Exists ( LinkerScriptPath ) )
{
File . Delete ( LinkerScriptPath ) ;
}
using ( StreamWriter Writer = File . CreateText ( LinkerScriptPath ) )
{
Writer . WriteLine ( "UE4 {" ) ;
Writer . WriteLine ( " global: *;" ) ;
Writer . WriteLine ( " local: _Znwm;" ) ;
Writer . WriteLine ( " _Znam;" ) ;
Writer . WriteLine ( " _ZdaPv;" ) ;
Writer . WriteLine ( " _ZdlPv;" ) ;
Writer . WriteLine ( "};" ) ;
} ;
2014-07-03 16:39:13 -04:00
LinkAction . CommandArguments + = string . Format ( " -Wl,--version-script=\"{0}\"" , LinkerScriptPath ) ;
2014-05-29 16:54:33 -04:00
2014-03-14 14:13:41 -04:00
// Only execute linking on the local PC.
LinkAction . bCanExecuteRemotely = false ;
2014-07-17 13:49:42 -04:00
// 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 . Config . bIsCrossReferenced & & LinkEnvironment . Config . bIsBuildingDLL )
{
2014-08-06 07:05:15 -04:00
bool bUseCmdExe = BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Win64 | | BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Win32 ;
2014-07-17 13:49:42 -04:00
string ScriptName = bUseCmdExe ? "FixDependencies.bat" : "FixDependencies.sh" ;
string FixDepsScriptPath = Path . Combine ( LinkEnvironment . Config . LocalShadowDirectory , ScriptName ) ;
if ( ! bHasWipedFixDepsScript )
{
bHasWipedFixDepsScript = true ;
Log . TraceVerbose ( "Creating script: {0}" , FixDepsScriptPath ) ;
StreamWriter Writer = File . CreateText ( FixDepsScriptPath ) ;
if ( bUseCmdExe )
{
Writer . Write ( "rem Automatically generated by UnrealBuildTool\n" ) ;
Writer . Write ( "rem *DO NOT EDIT*\n\n" ) ;
}
else
{
Writer . Write ( "#!/bin/sh\n" ) ;
Writer . Write ( "# Automatically generated by UnrealBuildTool\n" ) ;
Writer . Write ( "# *DO NOT EDIT*\n\n" ) ;
Writer . Write ( "set -o errexit\n" ) ;
}
Writer . Close ( ) ;
}
StreamWriter FixDepsScript = File . AppendText ( FixDepsScriptPath ) ;
string EngineAndGameLibrariesString = "" ;
foreach ( string Library in EngineAndGameLibraries )
{
EngineAndGameLibrariesString + = Library ;
}
FixDepsScript . Write ( string . Format ( "echo Fixing {0}\n" , Path . GetFileName ( OutputFile . AbsolutePath ) ) ) ;
if ( ! bUseCmdExe )
{
FixDepsScript . Write ( string . Format ( "TIMESTAMP=`stat --format %y \"{0}\"`\n" , OutputFile . AbsolutePath ) ) ;
}
string FixDepsLine = LinkAction . CommandPath + " " + LinkAction . CommandArguments ;
string Replace = "-Wl,--allow-shlib-undefined" ;
FixDepsLine = FixDepsLine . Replace ( Replace , EngineAndGameLibrariesString ) ;
FixDepsLine = FixDepsLine . Replace ( OutputFile . AbsolutePath , OutputFile . AbsolutePath + ".fixed" ) ;
FixDepsLine = FixDepsLine . Replace ( "$" , "\\$" ) ;
FixDepsScript . Write ( FixDepsLine + "\n" ) ;
if ( bUseCmdExe )
{
FixDepsScript . Write ( string . Format ( "move /Y \"{0}.fixed\" \"{0}\"\n" , OutputFile . AbsolutePath ) ) ;
}
else
{
FixDepsScript . Write ( string . Format ( "mv \"{0}.fixed\" \"{0}\"\n" , OutputFile . AbsolutePath ) ) ;
FixDepsScript . Write ( string . Format ( "touch -d \"$TIMESTAMP\" \"{0}\"\n\n" , OutputFile . AbsolutePath ) ) ;
}
FixDepsScript . Close ( ) ;
}
//LinkAction.CommandArguments += " -v";
2014-03-14 14:13:41 -04:00
return OutputFile ;
}
public override void CompileCSharpProject ( CSharpEnvironment CompileEnvironment , string ProjectFileName , string DestinationFile )
{
throw new BuildException ( "Linux cannot compile C# files" ) ;
}
2014-05-08 13:01:46 -04:00
2014-07-17 13:49:42 -04:00
static public void SetupBundleDependencies ( List < UEBuildBinary > Binaries , string GameName )
{
foreach ( UEBuildBinary Binary in Binaries )
{
BundleDependencies . Add ( FileItem . GetItemByPath ( Binary . ToString ( ) ) ) ;
}
}
2014-05-08 13:01:46 -04:00
/** Converts the passed in path from UBT host to compiler native format. */
public override String ConvertPath ( String OriginalPath )
{
2014-08-06 07:05:15 -04:00
if ( BuildHostPlatform . Current . Platform = = UnrealTargetPlatform . Linux )
2014-05-08 13:01:46 -04:00
{
return OriginalPath . Replace ( "\\" , "/" ) ;
}
else
{
return OriginalPath ;
}
}
2014-07-17 13:49:42 -04:00
public override ICollection < FileItem > PostBuild ( FileItem Executable , LinkEnvironment BinaryLinkEnvironment )
{
var OutputFiles = base . PostBuild ( Executable , BinaryLinkEnvironment ) ;
if ( BinaryLinkEnvironment . Config . bIsBuildingDLL | | BinaryLinkEnvironment . Config . bIsBuildingLibrary )
{
return OutputFiles ;
}
FileItem FixDepsOutputFile = FixDependencies ( BinaryLinkEnvironment , Executable ) ;
if ( FixDepsOutputFile ! = null )
{
OutputFiles . Add ( FixDepsOutputFile ) ;
}
return OutputFiles ;
}
2014-03-14 14:13:41 -04:00
}
}