2014-12-07 19:09:38 -05:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
2014-03-14 14:13:41 -04:00
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-09-08 16:47:10 -04:00
/** Splits compiler version string into numerical components, leaving unchanged if not known */
private void DetermineCompilerMajMinPatchFromVersionString ( )
{
string [ ] Parts = CompilerVersionString . Split ( '.' ) ;
if ( Parts . Length > = 1 )
{
CompilerVersionMajor = Convert . ToInt32 ( Parts [ 0 ] ) ;
}
if ( Parts . Length > = 2 )
{
CompilerVersionMinor = Convert . ToInt32 ( Parts [ 1 ] ) ;
}
if ( Parts . Length > = 3 )
{
CompilerVersionPatch = Convert . ToInt32 ( Parts [ 2 ] ) ;
}
}
/** Queries compiler for the version */
private bool DetermineCompilerVersion ( )
{
CompilerVersionString = null ;
CompilerVersionMajor = - 1 ;
CompilerVersionMinor = - 1 ;
CompilerVersionPatch = - 1 ;
Process Proc = new Process ( ) ;
Proc . StartInfo . UseShellExecute = false ;
Proc . StartInfo . CreateNoWindow = true ;
Proc . StartInfo . RedirectStandardOutput = true ;
Proc . StartInfo . RedirectStandardError = true ;
2014-09-12 17:06:44 -04:00
if ( ! String . IsNullOrEmpty ( GCCPath ) & & File . Exists ( GCCPath ) )
2014-09-08 16:47:10 -04:00
{
Proc . StartInfo . FileName = GCCPath ;
Proc . StartInfo . Arguments = " -dumpversion" ;
Proc . Start ( ) ;
Proc . WaitForExit ( ) ;
if ( Proc . ExitCode = = 0 )
{
// read just the first string
CompilerVersionString = Proc . StandardOutput . ReadLine ( ) ;
DetermineCompilerMajMinPatchFromVersionString ( ) ;
}
}
2014-09-12 17:06:44 -04:00
else if ( ! String . IsNullOrEmpty ( ClangPath ) & & File . Exists ( ClangPath ) )
2014-09-08 16:47:10 -04:00
{
Proc . StartInfo . FileName = ClangPath ;
Proc . StartInfo . Arguments = " --version" ;
Proc . Start ( ) ;
Proc . WaitForExit ( ) ;
if ( Proc . ExitCode = = 0 )
{
// read just the first string
string VersionString = Proc . StandardOutput . ReadLine ( ) ;
Regex VersionPattern = new Regex ( "version \\d+(\\.\\d+)+" ) ;
Match VersionMatch = VersionPattern . Match ( VersionString ) ;
// version match will be like "version 3.3", so remove the "version"
if ( VersionMatch . Value . StartsWith ( "version " ) )
{
CompilerVersionString = VersionMatch . Value . Replace ( "version " , "" ) ;
DetermineCompilerMajMinPatchFromVersionString ( ) ;
}
}
}
else
{
// icl?
}
2014-09-09 12:19:16 -04:00
if ( ! CrossCompiling ( ) )
{
Console . WriteLine ( "Using {0} version '{1}' (string), {2} (major), {3} (minor), {4} (patch)" ,
String . IsNullOrEmpty ( ClangPath ) ? "gcc" : "clang" ,
CompilerVersionString , CompilerVersionMajor , CompilerVersionMinor , CompilerVersionPatch ) ;
}
2014-09-08 16:47:10 -04:00
return ! String . IsNullOrEmpty ( CompilerVersionString ) ;
}
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-11-15 19:55:14 -05:00
string [ ] ClangNames = { "clang++" , "clang++-3.5" , "clang++-3.3" } ;
foreach ( var ClangName in ClangNames )
{
ClangPath = Which ( ClangName ) ;
if ( ! String . IsNullOrEmpty ( ClangPath ) )
{
break ;
}
}
2014-05-08 13:01:46 -04:00
GCCPath = Which ( "g++" ) ;
ArPath = Which ( "ar" ) ;
RanlibPath = Which ( "ranlib" ) ;
2015-04-27 12:53:24 -04:00
StripPath = Which ( "strip" ) ;
2014-09-08 16:47:10 -04:00
// if clang is available, zero out gcc (@todo: support runtime switching?)
if ( ! String . IsNullOrEmpty ( ClangPath ) )
{
GCCPath = null ;
}
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-10-30 19:44:12 -04:00
// set up the path to our toolchains
2014-05-30 10:58:16 -04:00
GCCPath = "" ;
2014-10-30 19:44:12 -04:00
ClangPath = Path . Combine ( BaseLinuxPath , @"bin\clang++.exe" ) ;
// ar and ranlib will be switched later to match the architecture
ArPath = "ar.exe" ;
RanlibPath = "ranlib.exe" ;
2015-04-27 12:53:24 -04:00
StripPath = "strip.exe" ;
2014-10-30 19:44:12 -04:00
}
2014-03-14 14:13:41 -04:00
2014-09-08 16:47:10 -04:00
if ( ! DetermineCompilerVersion ( ) )
{
2014-10-23 16:21:44 -04:00
Console . WriteLine ( "\n*** Could not determine version of the compiler, not registering Linux toolchain.\n" ) ;
return ;
}
// refuse to use compilers that we know won't work
// disable that only if you are a dev and you know what you are doing
if ( ! UsingClang ( ) )
{
Console . WriteLine ( "\n*** This version of the engine can only be compiled by clang - refusing to register the Linux toolchain.\n" ) ;
return ;
}
else if ( CompilerVersionMajor = = 3 & & CompilerVersionMinor = = 4 )
{
Console . WriteLine ( "\n*** clang 3.4.x is known to miscompile the engine - refusing to register the Linux toolchain.\n" ) ;
2014-09-08 16:47:10 -04:00
return ;
}
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 ) ;
}
2014-09-08 16:47:10 -04:00
/** Checks if compiler version matches the requirements */
private static bool CompilerVersionGreaterOrEqual ( int Major , int Minor , int Patch )
{
return CompilerVersionMajor > Major | |
( CompilerVersionMajor = = Major & & CompilerVersionMinor > Minor ) | |
( CompilerVersionMajor = = Major & & CompilerVersionMinor = = Minor & & CompilerVersionPatch > = Patch ) ;
}
2014-10-30 19:44:12 -04:00
/** Architecture-specific compiler switches */
static string ArchitectureSpecificSwitches ( string Architecture )
{
string Result = "" ;
if ( Architecture . StartsWith ( "x86_64" ) )
{
Result + = " -mmmx -msse -msse2" ;
}
else if ( Architecture . StartsWith ( "arm" ) )
{
Result + = " -fsigned-char" ;
// FreeType2's ftconfig.h will trigger this
Result + = " -Wno-deprecated-register" ;
}
return Result ;
}
static string ArchitectureSpecificDefines ( string Architecture )
{
string Result = "" ;
if ( Architecture . StartsWith ( "x86_64" ) )
{
Result + = " -D_LINUX64" ;
}
return Result ;
}
/** Gets architecture-specific ar paths */
private static string GetArPath ( string Architecture )
{
if ( CrossCompiling ( ) )
{
return Path . Combine ( Path . Combine ( BaseLinuxPath , String . Format ( "bin/{0}-{1}" , Architecture , ArPath ) ) ) ;
}
return ArPath ;
}
/** Gets architecture-specific ranlib paths */
private static string GetRanlibPath ( string Architecture )
{
if ( CrossCompiling ( ) )
{
return Path . Combine ( Path . Combine ( BaseLinuxPath , String . Format ( "bin/{0}-{1}" , Architecture , RanlibPath ) ) ) ;
}
return RanlibPath ;
}
2015-04-27 12:53:24 -04:00
/** Gets architecture-specific strip path */
private static string GetStripPath ( string Architecture )
{
if ( CrossCompiling ( ) )
{
return Path . Combine ( Path . Combine ( BaseLinuxPath , String . Format ( "bin/{0}-{1}" , Architecture , StripPath ) ) ) ;
}
return StripPath ;
}
2014-10-30 19:44:12 -04:00
static string GetCLArguments_Global ( CPPEnvironment CompileEnvironment )
2014-03-14 14:13:41 -04:00
{
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
2014-10-30 19:44:12 -04:00
Result + = ArchitectureSpecificSwitches ( CompileEnvironment . Config . Target . Architecture ) ;
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
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-strict-overflow" ; // Array.h:518
}
else
{
// Clang only options
2015-04-08 14:51:57 -04:00
if ( CrossCompiling ( ) )
{
Result + = " -fdiagnostics-format=msvc" ; // make diagnostics compatible with MSVC when cross-compiling
}
2014-05-08 13:01:46 -04:00
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" ;
2014-09-08 16:47:10 -04:00
// this switch is understood by clang 3.5.0, but not clang-3.5 as packaged by Ubuntu 14.04 atm
if ( CompilerVersionGreaterOrEqual ( 3 , 5 , 0 ) )
{
Result + = " -Wno-undefined-bool-conversion" ; // hides checking if 'this' pointer is null
}
2015-03-23 13:07:46 -04:00
if ( CompilerVersionGreaterOrEqual ( 3 , 6 , 0 ) )
{
Result + = " -Wno-unused-local-typedef" ; // clang is being overly strict here? PhysX headers trigger this.
2015-04-22 15:40:07 -04:00
Result + = " -Wno-inconsistent-missing-override" ; // these have to be suppressed for UE 4.8, should be fixed later.
2015-03-23 13:07:46 -04:00
}
2014-05-08 13:01:46 -04:00
}
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.
2015-04-22 09:58:49 -04:00
if ( BuildConfiguration . bEnableShadowVariableWarning )
{
Result + = " -Wshadow" ;
}
2014-03-14 14:13:41 -04:00
//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
2014-12-15 15:29:48 -05:00
// Not stripping debug info in Shipping @FIXME: temporary hack for FN to enable callstack in Shipping builds (proper resolution: UEPLAT-205)
Result + = " -fomit-frame-pointer" ;
2014-05-09 22:24:58 -04:00
}
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
}
2015-03-10 13:00:25 -04:00
// debug info (bCreateDebugInfo is normally set for all configurations, and we don't want it to affect Development/Shipping performance)
if ( CompileEnvironment . Config . bCreateDebugInfo & & CompileEnvironment . Config . Target . Configuration = = CPPTargetConfiguration . Debug )
2014-03-14 14:13:41 -04:00
{
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-12-15 15:29:48 -05:00
// Applying to all configurations, including Shipping @FIXME: temporary hack for FN to enable callstack in Shipping builds (proper resolution: UEPLAT-205)
else
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-09-28 23:27:35 -04:00
Result + = " -O2" ; // warning: as of now (2014-09-28), clang 3.5.0 miscompiles PlatformerGame with -O3 (bitfields?)
2014-03-14 14:13:41 -04:00
}
2014-04-02 18:09:23 -04:00
if ( CompileEnvironment . Config . bIsBuildingDLL )
{
Result + = " -fPIC" ;
2015-03-09 23:52:38 -04:00
// 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)
Result + = " -ftls-model=local-dynamic" ;
2014-04-02 18:09:23 -04:00
}
2014-03-14 14:13:41 -04:00
//Result += " -v"; // for better error diagnosis
2014-10-30 19:44:12 -04:00
Result + = ArchitectureSpecificDefines ( CompileEnvironment . Config . Target . Architecture ) ;
if ( CrossCompiling ( ) )
2014-04-02 18:09:23 -04:00
{
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
}
2015-02-27 14:38:49 -05:00
Result + = String . Format ( " --sysroot=\"{0}\"" , BaseLinuxPath ) ;
2014-04-02 18:09:23 -04:00
}
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-12-15 15:29:48 -05:00
// Applying to all configurations @FIXME: temporary hack for FN to enable callstack in Shipping builds (proper resolution: UEPLAT-205)
Result + = " -rdynamic" ; // needed for backtrace_symbols()...
if ( LinkEnvironment . Config . bIsBuildingDLL )
2014-03-14 14:13:41 -04:00
{
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-09-30 17:04:15 -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-09-30 17:04:15 -04:00
Result + = " -Wl,-rpath=${ORIGIN}/.." ; // for modules that are in sub-folders of the main Engine/Binary/Linux folder
// FIXME: really ugly temp solution. Modules need to be able to specify this
2014-07-03 23:30:39 -04:00
Result + = " -Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/ICU/icu4c-53_1/Linux/x86_64-unknown-linux-gnu" ;
2015-04-21 11:51:45 -04:00
Result + = " -Wl,-rpath=${ORIGIN}/../../../Engine/Binaries/ThirdParty/LinuxNativeDialogs/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 ;
}
2015-04-08 14:51:57 -04:00
if ( CrossCompiling ( ) )
{
// format the string so the output errors are clickable in Visual Studio
2014-03-14 14:13:41 -04:00
2015-04-08 14:51:57 -04:00
// 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).*" ;
2014-03-14 14:13:41 -04:00
2015-04-08 14:51:57 -04:00
// 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 ;
2014-03-14 14:13:41 -04:00
2015-04-08 14:51:57 -04:00
// If any of the above matches failed, do nothing
if ( MatchFilePath . Length = = 0 | |
MatchLineNumber . Length = = 0 | |
MatchDescription . Length = = 0 )
{
Console . WriteLine ( Output ) ;
return ;
}
2014-03-14 14:13:41 -04:00
2015-04-08 14:51:57 -04:00
// Convert Path
string RegexStrippedPath = @"\\Engine\\.*" ; //@"(Engine\/|[A-Za-z0-9_\-\.]*\/).*";
string ConvertedFilePath = Regex . Match ( MatchFilePath , RegexStrippedPath ) . Value ;
ConvertedFilePath = Path . GetFullPath ( "..\\.." + ConvertedFilePath ) ;
2014-03-14 14:13:41 -04:00
2015-04-08 14:51:57 -04:00
// 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
}
else
{
// native platform tools expect this in stderror
Console . Error . WriteLine ( Output ) ;
}
2014-03-14 14:13:41 -04:00
}
// 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 ;
2015-04-27 12:53:24 -04:00
static string StripPath ;
2014-03-14 14:13:41 -04:00
2014-09-08 16:47:10 -04:00
/** Version string of the current compiler, whether clang or gcc or whatever */
static string CompilerVersionString ;
/** Major version of the current compiler, whether clang or gcc or whatever */
static int CompilerVersionMajor = - 1 ;
/** Minor version of the current compiler, whether clang or gcc or whatever */
static int CompilerVersionMinor = - 1 ;
/** Patch version of the current compiler, whether clang or gcc or whatever */
static int CompilerVersionPatch = - 1 ;
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 = "" ;
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.
Experimental UnrealBuildTool makefile support
UnrealBuildTool 'Makefiles' allow for very fast iterative builds.
- New BuildConfiguration.xml setting added: "bUseExperimentalFastBuildIteration" (disabled by default)
- Turning this on causes Unreal Build Tool to emit 'UBT Makefiles' for targets when they're built the first time.
- Subsequent builds will load these Makefiles and begin outdatedness checking and build invocation very quickly.
- The caveat is that if source files are added or removed to the project, UBT will need to gather information about those in order for your build to complete successfully.
- Currently, you must run the project file generator after adding/removing source files to tell UBT to re-gather this information.
- Events that can invalidate the 'UBT Makefile':
- Adding/removing .cpp files
- Adding/removing .h files with UObjects
- Adding new UObject types to a file that didn't previously have any
- Changing global build settings (most settings in this file qualify.)
- Changed code that affects how Unreal Header Tool works
- You can force regeneration of the 'UBT Makefile' by passing the '-Gather' argument, or simply regenerating project files
- New command-line parameters added:
- "-Gather": Tells UBT to always perform the gather step (slower but will catch project structural changes)
- "-NoGather": Disables the gather step, unless UBT detects that it must be done. This is the default when bUseExperimentalFastBuildIteration is enabled
- "-GatherOnly": Runs the gather step and saves a UBTMakefile, but doesn't build anything
- "-Assemble": Tells UBT to also assemble build products. This always defaults to enabled
- "-NoAssemble": Tells UBT to skip the assemble step, whether we gathered build products or not
- "-AssembleOnly": Tells UBT to only assemble build products and not to gather, unless UBT determines it must
Other changes:
- UBT now keeps track of which targets it was building in an intermediate file, to help it invalidate cached includes in subsequent runs when the targets are different
- C++ includes are now stored in a class separate from the C++ compile enviroment (for easier serialization)
- The method that UBT uses to find the CoreUObject module timestamp was rewritten
- Various '@todo ubtmake' comments added to tag possible remaining Makefile tasks
- The 'FileItem' class had some member variable comments and code cleaned up, while making it serializable
- Cleaned up the comments and member variables in the "Action" class, while making it serializable
- Some UBT classes are now "serializable". This is because we need to store the data in UBTMakefiles.
- Removed support for Actions to tinker with Stdout and Stderror (was not used for anything)
- Moved PrecompileHeaderEnvironment class to the UEBuildModule.cs source file
- Plugin intermediate include directories are now selected on demand rather than cached early
- Toolchain code for gathering prerequisite headers is now shared in a single function (AddPrerequisiteSourceFile)
- Removed Action.StatusDetailedDescription, was not used for anything
- Removed UEBuildConfiguration.bExcludePlugins, was not used for anything
- Removed ECompilationResult.FailedDueToHeaderChange, was not used for anything
[CL 2254472 by Mike Fricker in Main branch]
2014-08-13 08:17:43 -04:00
foreach ( string IncludePath in CompileEnvironment . Config . CPPIncludeInfo . IncludePaths )
2014-03-14 14:13:41 -04:00
{
Arguments + = string . Format ( " -I\"{0}\"" , IncludePath ) ;
}
Experimental UnrealBuildTool makefile support
UnrealBuildTool 'Makefiles' allow for very fast iterative builds.
- New BuildConfiguration.xml setting added: "bUseExperimentalFastBuildIteration" (disabled by default)
- Turning this on causes Unreal Build Tool to emit 'UBT Makefiles' for targets when they're built the first time.
- Subsequent builds will load these Makefiles and begin outdatedness checking and build invocation very quickly.
- The caveat is that if source files are added or removed to the project, UBT will need to gather information about those in order for your build to complete successfully.
- Currently, you must run the project file generator after adding/removing source files to tell UBT to re-gather this information.
- Events that can invalidate the 'UBT Makefile':
- Adding/removing .cpp files
- Adding/removing .h files with UObjects
- Adding new UObject types to a file that didn't previously have any
- Changing global build settings (most settings in this file qualify.)
- Changed code that affects how Unreal Header Tool works
- You can force regeneration of the 'UBT Makefile' by passing the '-Gather' argument, or simply regenerating project files
- New command-line parameters added:
- "-Gather": Tells UBT to always perform the gather step (slower but will catch project structural changes)
- "-NoGather": Disables the gather step, unless UBT detects that it must be done. This is the default when bUseExperimentalFastBuildIteration is enabled
- "-GatherOnly": Runs the gather step and saves a UBTMakefile, but doesn't build anything
- "-Assemble": Tells UBT to also assemble build products. This always defaults to enabled
- "-NoAssemble": Tells UBT to skip the assemble step, whether we gathered build products or not
- "-AssembleOnly": Tells UBT to only assemble build products and not to gather, unless UBT determines it must
Other changes:
- UBT now keeps track of which targets it was building in an intermediate file, to help it invalidate cached includes in subsequent runs when the targets are different
- C++ includes are now stored in a class separate from the C++ compile enviroment (for easier serialization)
- The method that UBT uses to find the CoreUObject module timestamp was rewritten
- Various '@todo ubtmake' comments added to tag possible remaining Makefile tasks
- The 'FileItem' class had some member variable comments and code cleaned up, while making it serializable
- Cleaned up the comments and member variables in the "Action" class, while making it serializable
- Some UBT classes are now "serializable". This is because we need to store the data in UBTMakefiles.
- Removed support for Actions to tinker with Stdout and Stderror (was not used for anything)
- Moved PrecompileHeaderEnvironment class to the UEBuildModule.cs source file
- Plugin intermediate include directories are now selected on demand rather than cached early
- Toolchain code for gathering prerequisite headers is now shared in a single function (AddPrerequisiteSourceFile)
- Removed Action.StatusDetailedDescription, was not used for anything
- Removed UEBuildConfiguration.bExcludePlugins, was not used for anything
- Removed ECompilationResult.FailedDueToHeaderChange, was not used for anything
[CL 2254472 by Mike Fricker in Main branch]
2014-08-13 08:17:43 -04:00
foreach ( string IncludePath in CompileEnvironment . Config . CPPIncludeInfo . SystemIncludePaths )
2014-03-14 14:13:41 -04:00
{
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 ;
}
Experimental UnrealBuildTool makefile support
UnrealBuildTool 'Makefiles' allow for very fast iterative builds.
- New BuildConfiguration.xml setting added: "bUseExperimentalFastBuildIteration" (disabled by default)
- Turning this on causes Unreal Build Tool to emit 'UBT Makefiles' for targets when they're built the first time.
- Subsequent builds will load these Makefiles and begin outdatedness checking and build invocation very quickly.
- The caveat is that if source files are added or removed to the project, UBT will need to gather information about those in order for your build to complete successfully.
- Currently, you must run the project file generator after adding/removing source files to tell UBT to re-gather this information.
- Events that can invalidate the 'UBT Makefile':
- Adding/removing .cpp files
- Adding/removing .h files with UObjects
- Adding new UObject types to a file that didn't previously have any
- Changing global build settings (most settings in this file qualify.)
- Changed code that affects how Unreal Header Tool works
- You can force regeneration of the 'UBT Makefile' by passing the '-Gather' argument, or simply regenerating project files
- New command-line parameters added:
- "-Gather": Tells UBT to always perform the gather step (slower but will catch project structural changes)
- "-NoGather": Disables the gather step, unless UBT detects that it must be done. This is the default when bUseExperimentalFastBuildIteration is enabled
- "-GatherOnly": Runs the gather step and saves a UBTMakefile, but doesn't build anything
- "-Assemble": Tells UBT to also assemble build products. This always defaults to enabled
- "-NoAssemble": Tells UBT to skip the assemble step, whether we gathered build products or not
- "-AssembleOnly": Tells UBT to only assemble build products and not to gather, unless UBT determines it must
Other changes:
- UBT now keeps track of which targets it was building in an intermediate file, to help it invalidate cached includes in subsequent runs when the targets are different
- C++ includes are now stored in a class separate from the C++ compile enviroment (for easier serialization)
- The method that UBT uses to find the CoreUObject module timestamp was rewritten
- Various '@todo ubtmake' comments added to tag possible remaining Makefile tasks
- The 'FileItem' class had some member variable comments and code cleaned up, while making it serializable
- Cleaned up the comments and member variables in the "Action" class, while making it serializable
- Some UBT classes are now "serializable". This is because we need to store the data in UBTMakefiles.
- Removed support for Actions to tinker with Stdout and Stderror (was not used for anything)
- Moved PrecompileHeaderEnvironment class to the UEBuildModule.cs source file
- Plugin intermediate include directories are now selected on demand rather than cached early
- Toolchain code for gathering prerequisite headers is now shared in a single function (AddPrerequisiteSourceFile)
- Removed Action.StatusDetailedDescription, was not used for anything
- Removed UEBuildConfiguration.bExcludePlugins, was not used for anything
- Removed ECompilationResult.FailedDueToHeaderChange, was not used for anything
[CL 2254472 by Mike Fricker in Main branch]
2014-08-13 08:17:43 -04:00
// Add the C++ source file and its included files to the prerequisite item list.
AddPrerequisiteSourceFile ( Target , BuildPlatform , CompileEnvironment , SourceFile , CompileAction . PrerequisiteItems ) ;
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 . 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" ;
2015-02-27 14:38:49 -05:00
ArchiveAction . CommandArguments = "/c \"" ;
2014-04-02 18:09:23 -04:00
}
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 ) ;
2015-02-27 14:38:49 -05:00
ArchiveAction . CommandArguments + = string . Format ( "\"{0}\" {1} \"{2}\"" , GetArPath ( LinkEnvironment . Config . Target . Architecture ) , GetArchiveArguments ( LinkEnvironment ) , OutputFile . AbsolutePath ) ;
2014-03-14 14:13:41 -04:00
// 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
2015-02-27 14:38:49 -05:00
ArchiveAction . CommandArguments + = string . Format ( " && \"{0}\" \"{1}\"" , GetRanlibPath ( LinkEnvironment . Config . Target . Architecture ) , OutputFile . AbsolutePath ) ;
2014-03-14 14:13:41 -04:00
// 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 + = "'" ;
}
2015-02-27 14:38:49 -05:00
else
{
ArchiveAction . CommandArguments + = "\"" ;
}
2014-04-02 18:09:23 -04:00
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 ) ;
2014-10-28 20:44:29 -04:00
if ( AbsoluteAdditionalLibrary . Contains ( " " ) )
{
AbsoluteAdditionalLibrary = string . Format ( "\"{0}\"" , AbsoluteAdditionalLibrary ) ;
}
2014-07-17 13:49:42 -04:00
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 )
{
2014-09-16 16:32:04 -04:00
Writer . Write ( "@echo off\n" ) ;
Writer . Write ( "rem Automatically generated by UnrealBuildTool\n" ) ;
2014-07-17 13:49:42 -04:00
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 ) ;
2014-09-16 16:32:04 -04:00
string OutputFileForwardSlashes = OutputFile . AbsolutePath . Replace ( "\\" , "/" ) ;
FixDepsLine = FixDepsLine . Replace ( OutputFileForwardSlashes , OutputFileForwardSlashes + ".fixed" ) ;
2014-07-17 13:49:42 -04:00
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-09-03 21:56:29 -04:00
public override void SetupBundleDependencies ( List < UEBuildBinary > Binaries , string GameName )
2014-07-17 13:49:42 -04:00
{
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-12-02 14:58:59 -05:00
public override UnrealTargetPlatform GetPlatform ( )
{
return UnrealTargetPlatform . Linux ;
}
2015-04-27 12:53:24 -04:00
public override void StripSymbols ( string SourceFileName , string TargetFileName )
{
File . Copy ( SourceFileName , TargetFileName , true ) ;
ProcessStartInfo StartInfo = new ProcessStartInfo ( ) ;
StartInfo . FileName = GetStripPath ( UEBuildPlatform . GetBuildPlatform ( UnrealTargetPlatform . Linux ) . GetActiveArchitecture ( ) ) ;
StartInfo . Arguments = TargetFileName ;
StartInfo . UseShellExecute = false ;
StartInfo . CreateNoWindow = true ;
Utils . RunLocalProcessAndLogOutput ( StartInfo ) ;
}
2014-12-02 14:58:59 -05:00
}
2014-03-14 14:13:41 -04:00
}