Files
UnrealEngineUWP/Engine/Source/Programs/UnrealBuildTool/Android/AndroidToolChain.cs
Jack Porter 2082f7ec9b Copying //UE4/Dev-Mobile to //UE4/Dev-Main (Source: //UE4/Dev-Mobile @ 3056055)
#lockdown Nick.Penwarden
#rb None

==========================
MAJOR FEATURES + CHANGES
==========================

Change 3011102 on 2016/06/13 by Steve.Cano

	After taking a screenshot using glReadPixels, transfer the data to the target buffer from bottom row up to fix the "upside-down" render that OpenGL does. Confirmed with QA (owen.stupka_volt) that this does not appear to be happening on iOS (non-metal devices, inclusion of iOS in write-up was a mistake), verified on an ipod touch 5. Also confirmed that this does not happen on html5, and that Mobile HDR flag does not make a difference in function.

	#jira UE-26421
	#ue4
	#android

Change 3015801 on 2016/06/16 by Dmitriy.Dyomin

	Probbably fix for UE-30878, was not able to repro an actual crash(FFoliageInstanceBaseCache::AddInstanceBaseId). Added even more logging in case fix does not work.
	#jira  UE-30878

Change 3015903 on 2016/06/16 by Dmitriy.Dyomin

	Fixed: Levels window has Refresh/UI issues when World Composition is active
	#jira UE-26160

Change 3018352 on 2016/06/17 by Chris.Babcock

	Handle Android media prepare failure (URL without internet for example)
	#jira UE-32029
	#ue4
	#android

Change 3026387 on 2016/06/24 by Jack.Porter

	Remove FFuncTestManager warning about PIE when running on a standalone game binary

Change 3026398 on 2016/06/24 by Jack.Porter

	Prevent FSocketBSD::Recv returning false on SE_EWOULDBLOCK

Change 3027553 on 2016/06/25 by Niklas.Smedberg

	OpenGL: Made some block size calculation work for arbitrary block sizes (e.g. not pow-of-two).

Change 3027554 on 2016/06/25 by Niklas.Smedberg

	Metal: copyFromTexture now gets block-aligned size parameter (e.g. used for texture streaming)

Change 3028061 on 2016/06/26 by Jack.Porter

	Fixed a problem where newly discovered instances were not added to an existing session in the Session Browser.
	Fixed a problem where selecting an instance in a session with multiple instances didn't deselect the previously selected instance correctly.

Change 3029220 on 2016/06/27 by Steve.Cano

	Change Android Tilt values to use GetRotationMatrix/GetOrientation logic, same as java-side android would use, and adjust slightly to match as closely as possible to iOS values for tilt. There is drift and some differences in the "Y" value but the same sort of inconsistencies are also seen on iOS.

	#jira UE-6135
	#ue4
	#android

Change 3030420 on 2016/06/28 by Jack.Porter

	Fix crash with RenderOutputValidation when running with cooked content

Change 3030426 on 2016/06/28 by Jack.Porter

	Fix to CL 3026398 - make FSocketBSD(IPv6)::Recv(From) return false when recv returns 0.
	A return value of 0 indicates the connection was shutdown in an orderly manner.

Change 3030973 on 2016/06/28 by Steve.Cano

	Added a landscape downloader background along with the options to change it from within Android settings

	#ue4
	#android
	#jira UE-32318

Change 3031757 on 2016/06/28 by Chris.Babcock

	Remove unused methods from AndroidJNI header
	#ue4
	#android

Change 3032387 on 2016/06/29 by Allan.Bentham

	Rename android es31+aep -> glesdeferred.

Change 3032711 on 2016/06/29 by Allan.Bentham

	Rename GLSL_310_ES_EXT shader define:
	ES31_AEP_PROFILE -> ESDEFERRED_PROFILE
	bumped UE_SHADER_GLSL_310_ES_EXT_VER version number.

Change 3033698 on 2016/06/29 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3034210 on 2016/06/30 by Steve.Cano

	Added a new AndroidRuntimeSettings variable that allows creation of installers for both Windows and Mac/Linux if set to true.

	#jira UE-32302

	#ue4
	#android

Change 3034530 on 2016/06/30 by Chris.Babcock

	Rename FManifestReader to FAndroidFileManifestReader in AndroidFile
	#jira UE-32679
	#ue4
	#android

Change 3034612 on 2016/06/30 by Steve.Cano

	Change Alpha from being set to a range of 0-255 to being in a range of 0-1 (which is the correct range of values)

	#jira UE-25325
	#ue4
	#android

Change 3034679 on 2016/06/30 by Chris.Babcock

	Fix tooltip (.command for mac, not .sh)
	#jira UE-32302
	#ue4
	#android

Change 3038881 on 2016/07/05 by Jack.Porter

	Package and launch on multiple Android devices simultaneously using the -Device=xxxxxxx+yyyyyyyy+zzzzzzzz format generated by a Project Launcher profile when you select multiple devices

	#jira UEMOB-115

Change 3039240 on 2016/07/06 by Jack.Porter

	TcpMessageTransport - connection-based message bus transport.

	#jira UEMOB-112
	#jira UEMOB-113

Change 3039252 on 2016/07/06 by Jack.Porter

	Enable messaging and session services and functional testing on Android when launched with -messaging
	Android device detection module support for adding port forwarding and connection announcement for TcpMessageTransport

	#jira UEMOB-112
	#jira UEMOB-113

Change 3039264 on 2016/07/06 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3040041 on 2016/07/06 by Chris.Babcock

	Pass proper value to script generator functions
	#jira UE-32861
	#ue4
	#android

Change 3040890 on 2016/07/07 by Allan.Bentham

	Fix shadow crash
	#jira UE-32884

Change 3041458 on 2016/07/07 by Peter.Sauerbrei

	fix for IOS launch on failures

Change 3041542 on 2016/07/07 by Peter.Sauerbrei

	better fix for the multi-device deployment issue

Change 3041774 on 2016/07/07 by Steve.Cano

	Fixing crash that occurs when a games app id for Google Play is set before configuring the apk packaging. Also validating the value that is inserted and using it to override any values that have been hand-inserted into the GooglePlayAppID.xml

	#jira UE-16992
	#android
	#ue4

Change 3042222 on 2016/07/08 by Dmitriy.Dyomin

	Mobile packaging scenarious
	Added a wizard for creating launcher profiles (Android & IOS) for scenario: Minimal App + Downloadable content
	Added Archive step to launcher profiles to be able to store build product into specified directory
	Changes to a cooker to be able to pack DLC based with a different flavor to a release App
	Changes to DLC packaging to be able to build streaming data without chunking pak files
	#jira UEMOB-119

Change 3042244 on 2016/07/08 by Dmitriy.Dyomin

	Fixed crash in FTcpMessageTransportConnection::Stop

Change 3042270 on 2016/07/08 by Dmitriy.Dyomin

	GitHub #2320 : [ULevelStreamingKismet] Load Level Instance, Enables UE4 Users to create multiple transformed instances of a .umap without having to include in persistent level's list ? Rama
	contributed by: EverNewJoy
	#jira UE-29867

Change 3042449 on 2016/07/08 by Dmitriy.Dyomin

	Fixing Mac Editor build erros from CL# 3042222

Change 3042480 on 2016/07/08 by Allan.Bentham

	Add ES3.1 profile & compiler_glsl_es3_1 to shaders.

Change 3042481 on 2016/07/08 by Allan.Bentham

	hlslcc - ES3.1 changes.
	set ES3.1 version number to 310
	Do not use ES2 keywords for ES3.1.
	Generate Layout Locations for ES3.1
	bump version.

Change 3042483 on 2016/07/08 by Allan.Bentham

	Add mobile ES3.1 support.
	Recreates EGL and ES3.1 context during PlatformInitOpenGL if ES3.1 is required.

Change 3042485 on 2016/07/08 by Allan.Bentham

	Undo android XGE change.

Change 3042506 on 2016/07/08 by Dmitriy.Dyomin

	One more compile fix from CL# 3042222

Change 3044173 on 2016/07/10 by Dmitriy.Dyomin

	UAT: Added support for building target platforms with multiple cook flavors
	ex: -targetplatform=Android -cookflavor=ETC1+ETC2

Change 3044213 on 2016/07/11 by Dmitriy.Dyomin

	Fixed: Can't stream in a level whose name is a substring of another streaming level
	#jira UE-32999

Change 3044221 on 2016/07/11 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3044815 on 2016/07/11 by Allan.Bentham

	Corrected NAME_GLSL_ES3_1_ANDROID format string.

Change 3046911 on 2016/07/12 by Chris.Babcock

	Add handling of OnTextChanged for virtual keyboard input on Android
	#jira UE-32348
	#ue4
	#android

Change 3046958 on 2016/07/12 by Chris.Babcock

	Rename some functions with Error in the name to prevent false coloring in the logs
	#jira UE-30541
	#ue4
	#android

Change 3047169 on 2016/07/12 by Chris.Babcock

	Return player ID and handle auth token for Google Play Games on Android (contributed by gameDNAstudio)
	#jira UE-30610
	#pr #2372
	#ue4
	#android

Change 3047406 on 2016/07/12 by Jack.Porter

	Add missing import to GameActivity.java

Change 3047442 on 2016/07/13 by Dmitriy.Dyomin

	Added: Mobile custom post-process
	Limitations: can fetch only from PostProcessInput0 (SceneColor) other scene textures are not supported. Does not support "Replacing the Tonemapper" blendable location.
	#jira UEMOB-147

Change 3047466 on 2016/07/13 by Dmitriy.Dyomin

	Disabled engine crash handler on Android, system crash handler works more reliably across different os versions/devices

Change 3047746 on 2016/07/13 by Jack.Porter

	Rename FBasePassFowardDynamicPointLightInfo

Change 3047778 on 2016/07/13 by Jack.Porter

	Missing file for rename FBasePassFowardDynamicPointLightInfo

Change 3047788 on 2016/07/13 by Allan.Bentham

	Fix incorrect TargetPlatformDescriptor string generation.

Change 3047790 on 2016/07/13 by Allan.Bentham

	Fixed half3x3 matrix use with ES3.1 glsl
	Fixed couple of interpolator precision mismatch.
	Fixed ES3.1 support detection issues

Change 3047816 on 2016/07/13 by Allan.Bentham

	Remove AndroidGL4 remnants.

Change 3048926 on 2016/07/13 by Chris.Babcock

	Added detection of Amazon Fire TV to disable requiring virtual joysticks
	#ue4
	#android

Change 3049335 on 2016/07/14 by Dmitriy.Dyomin

	Fixing UAT crash when packaging project for iOS

Change 3049390 on 2016/07/14 by Jack.Porter

	Disabled error for warning 4819 "The file contains a character that cannot be represented in the current code page (xxx). Save the file in Unicode format to prevent data loss"
	This is triggered by European characters and copyright symbols in source saved as latin-1 when compiling on non-US windows. Seen often in 3rd party headers, eg nvapi.

	#code_review: Ben.Marsh

Change 3049391 on 2016/07/14 by Jack.Porter

	Fixed incorrect comment order in CL 3049390

Change 3049545 on 2016/07/14 by Dmitriy.Dyomin

	Reworking some code from CL#3047442 to make static analizer happy

Change 3049626 on 2016/07/14 by Allan.Bentham

	Automatic CSM shader toggling
	#jira UE-27429

Change 3051574 on 2016/07/15 by Jack.Porter

	Support for lighting channels on Mobile
	- Multiple directional lights are supported in different channels but primitives are only affected by the directional light in the first channel they have set
	- CSM shadows from stationary or movable directional lights correctly follow their lighting channels
	- No channel limitations for dynamic point lights

	Notes:
	Removed mobile-specific directional light shadowing fields from View uniform buffer and mobile no longers uses SimpleDirectionalLight.
	Separate uniform buffers for mobile directional light are generated for each lighting channel.
	CSM culling information is now stored in FViewInfo and not per FVisibleLightViewInfo as the visibility bits are per view.

	#code_review Daniel.Wright
	#jira UEMOB-110

Change 3051699 on 2016/07/15 by Steve.Cano

	Preserve the original, pre-transformed input vertices for Slate shaders, which is required to properly do anti-aliasing (the ViewProjection-transformed values were causing the lines to not be drawn).

	#jira UE-20320
	#ue4
	#android

Change 3051744 on 2016/07/15 by Chris.Babcock

	Fix Android Vulkan include path checks (contributed by kodomastro)
	#jira UE-33311
	#PR #2602
	#ue4
	#android

Change 3052023 on 2016/07/15 by Chris.Babcock

	Fix shadowed variables

Change 3052110 on 2016/07/15 by Chris.Babcock

	Compile fixes for light channel support on mobile
	- missing template
	- accessor function for MobileDirectionalLights from scene

Change 3052242 on 2016/07/15 by Chris.Babcock

	Compile fixes for light channel support on mobile
	- removed dependency on C++14 feature

Change 3052730 on 2016/07/16 by Dmitriy.Dyomin

	Win32 build fix

Change 3053041 on 2016/07/17 by Jack.Porter

	Merging //UE4/Dev-Main to Dev-Mobile (//UE4/Dev-Mobile)

Change 3053054 on 2016/07/17 by Jack.Porter

	Changed use of old function ShouldUseDeferredRenderer() to new GetShadingPath()

Change 3053055 on 2016/07/17 by Jack.Porter

	Fixed local variable aliasing in unity build

Change 3053206 on 2016/07/18 by Jack.Porter

	Support ExecuteJavascript on iOS and Android
	Expose ExecuteJavascript to widget blueprint
	Fix ExecuteJavascript unicode string support on desktop platforms

	#jira UEMOB-152

Change 3053323 on 2016/07/18 by Dmitriy.Dyomin

	Added: Ability to set thread affinity for a device in Device Profiles (ex: +CVars=android.SetThreadAffinity=RT 0x02 GT 0x01)
	#jira UEMOB-107

Change 3053723 on 2016/07/18 by Jack.Porter

	Fix for UnrealTournamentProto.Automation.cs build errors

Change 3055090 on 2016/07/19 by Dmitriy.Dyomin

	Junk OnlineBlueprintSupport module binaries

[CL 3056789 by Jack Porter in Main branch]
2016-07-19 19:13:01 -04:00

1324 lines
48 KiB
C#

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.Win32;
namespace UnrealBuildTool
{
public class AndroidToolChain : UEToolChain
{
private FileReference ProjectFile;
// the number of the clang version being used to compile
private float ClangVersionFloat = 0;
// the list of architectures we will compile for
private List<string> Arches = null;
// the list of GPU architectures we will compile for
private List<string> GPUArchitectures = null;
// a list of all architecture+GPUArchitecture names (-armv7-es2, etc)
private List<string> AllComboNames = null;
static private Dictionary<string, string[]> AllArchNames = new Dictionary<string, string[]> {
{ "-armv7", new string[] { "armv7", "armeabi-v7a", } },
{ "-arm64", new string[] { "arm64", "arm64-v8a", } },
{ "-x86", new string[] { "x86", } },
{ "-x64", new string[] { "x64", "x86_64", } },
};
static private Dictionary<string, string[]> LibrariesToSkip = new Dictionary<string, string[]> {
{ "-armv7", new string[] { } },
{ "-arm64", new string[] { "nvToolsExt", "nvToolsExtStub", "oculus", "vrapi", "ovrkernel", "systemutils", "openglloader", "gpg", } },
{ "-x86", new string[] { "nvToolsExt", "nvToolsExtStub", "oculus", "vrapi", "ovrkernel", "systemutils", "openglloader", } },
{ "-x64", new string[] { "nvToolsExt", "nvToolsExtStub", "oculus", "vrapi", "ovrkernel", "systemutils", "openglloader", "gpg", } },
};
static private Dictionary<string, string[]> ModulesToSkip = new Dictionary<string, string[]> {
{ "-armv7", new string[] { } },
{ "-arm64", new string[] { "OnlineSubsystemGooglePlay", } },
{ "-x86", new string[] { } },
{ "-x64", new string[] { "OnlineSubsystemGooglePlay", } },
};
public AndroidToolChain(FileReference InProjectFile)
: base(CPPTargetPlatform.Android)
{
ProjectFile = InProjectFile;
string NDKPath = Environment.GetEnvironmentVariable("NDKROOT");
// don't register if we don't have an NDKROOT specified
if (String.IsNullOrEmpty(NDKPath))
{
throw new BuildException("NDKROOT is not specified; cannot use Android toolchain.");
}
NDKPath = NDKPath.Replace("\"", "");
string ClangVersion = "";
string GccVersion = "";
string ArchitecturePath = "";
string ArchitecturePathWindows32 = @"prebuilt/windows";
string ArchitecturePathWindows64 = @"prebuilt/windows-x86_64";
string ArchitecturePathMac = @"prebuilt/darwin-x86_64";
string ExeExtension = ".exe";
if (Directory.Exists(Path.Combine(NDKPath, ArchitecturePathWindows64)))
{
Log.TraceVerbose(" Found Windows 64 bit versions of toolchain");
ArchitecturePath = ArchitecturePathWindows64;
}
else if (Directory.Exists(Path.Combine(NDKPath, ArchitecturePathWindows32)))
{
Log.TraceVerbose(" Found Windows 32 bit versions of toolchain");
ArchitecturePath = ArchitecturePathWindows32;
}
else if (Directory.Exists(Path.Combine(NDKPath, ArchitecturePathMac)))
{
Log.TraceVerbose(" Found Mac versions of toolchain");
ArchitecturePath = ArchitecturePathMac;
ExeExtension = "";
}
else
{
throw new BuildException("Couldn't find 32-bit or 64-bit versions of the Android toolchain");
}
// prefer clang 3.6, but fall back if needed for now
if (Directory.Exists(Path.Combine(NDKPath, @"toolchains/llvm-3.6")))
{
ClangVersionFloat = 3.6f;
ClangVersion = "-3.6";
GccVersion = "4.9";
}
else if (Directory.Exists(Path.Combine(NDKPath, @"toolchains/llvm-3.5")))
{
ClangVersionFloat = 3.5f;
ClangVersion = "-3.5";
GccVersion = "4.9";
}
else if (Directory.Exists(Path.Combine(NDKPath, @"toolchains/llvm-3.3")))
{
ClangVersionFloat = 3.3f;
ClangVersion = "-3.3";
GccVersion = "4.8";
}
else if (Directory.Exists(Path.Combine(NDKPath, @"toolchains/llvm-3.1")))
{
ClangVersionFloat = 3.1f;
ClangVersion = "-3.1";
GccVersion = "4.6";
}
else if (Directory.Exists(Path.Combine(NDKPath, @"toolchains/llvm")))
{
// look for version in AndroidVersion.txt (fail if not found)
string VersionFilename = Path.Combine(NDKPath, @"toolchains/llvm/", ArchitecturePath, @"AndroidVersion.txt");
if (!File.Exists(VersionFilename))
{
throw new BuildException("Cannot find supported Android toolchain");
}
string[] VersionFile = File.ReadAllLines(VersionFilename);
string[] VersionParts = VersionFile[0].Split('.');
ClangVersionFloat = float.Parse(VersionParts[0] + "." + VersionParts[1], System.Globalization.CultureInfo.InvariantCulture);
ClangVersion = "";
GccVersion = "4.9";
}
else
{
throw new BuildException("Cannot find supported Android toolchain");
}
// set up the path to our toolchains
ClangPath = Path.Combine(NDKPath, @"toolchains/llvm" + ClangVersion, ArchitecturePath, @"bin/clang++" + ExeExtension);
ArPathArm = Path.Combine(NDKPath, @"toolchains/arm-linux-androideabi-" + GccVersion, ArchitecturePath, @"bin/arm-linux-androideabi-ar" + ExeExtension); //@todo android: use llvm-ar.exe instead?
ArPathArm64 = Path.Combine(NDKPath, @"toolchains/aarch64-linux-android-" + GccVersion, ArchitecturePath, @"bin/aarch64-linux-android-ar" + ExeExtension); //@todo android: use llvm-ar.exe instead?
ArPathx86 = Path.Combine(NDKPath, @"toolchains/x86-" + GccVersion, ArchitecturePath, @"bin/i686-linux-android-ar" + ExeExtension); //@todo android: verify x86 toolchain
ArPathx64 = Path.Combine(NDKPath, @"toolchains/x86_64-" + GccVersion, ArchitecturePath, @"bin/x86_64-linux-android-ar" + ExeExtension); //@todo android: verify x64 toolchain
// NDK setup (use no less than 21 for 64-bit targets)
int NDKApiLevelInt = GetNdkApiLevelInt();
string NDKApiLevel32Bit = GetNdkApiLevel();
string NDKApiLevel64Bit = NDKApiLevel32Bit;
if (NDKApiLevelInt < 21)
{
NDKApiLevel64Bit = "android-21";
}
// toolchain params
ToolchainParamsArm = " -target armv7-none-linux-androideabi" +
" --sysroot=\"" + Path.Combine(NDKPath, "platforms", NDKApiLevel32Bit, "arch-arm") + "\"" +
" -gcc-toolchain \"" + Path.Combine(NDKPath, @"toolchains/arm-linux-androideabi-" + GccVersion, ArchitecturePath) + "\"";
ToolchainParamsArm64 = " -target aarch64-none-linux-android" +
" --sysroot=\"" + Path.Combine(NDKPath, "platforms", NDKApiLevel64Bit, "arch-arm64") + "\"" +
" -gcc-toolchain \"" + Path.Combine(NDKPath, @"toolchains/aarch64-linux-android-" + GccVersion, ArchitecturePath) + "\"";
ToolchainParamsx86 = " -target i686-none-linux-android" +
" --sysroot=\"" + Path.Combine(NDKPath, "platforms", NDKApiLevel32Bit, "arch-x86") + "\"" +
" -gcc-toolchain \"" + Path.Combine(NDKPath, @"toolchains/x86-" + GccVersion, ArchitecturePath) + "\"";
ToolchainParamsx64 = " -target x86_64-none-linux-android" +
" --sysroot=\"" + Path.Combine(NDKPath, "platforms", NDKApiLevel64Bit, "arch-x86_64") + "\"" +
" -gcc-toolchain \"" + Path.Combine(NDKPath, @"toolchains\x86_64-" + GccVersion, ArchitecturePath) + "\"";
}
public void ParseArchitectures()
{
// look in ini settings for what platforms to compile for
ConfigCacheIni Ini = ConfigCacheIni.CreateConfigCacheIni(UnrealTargetPlatform.Android, "Engine", DirectoryReference.FromFile(ProjectFile));
Arches = new List<string>();
bool bBuild = true;
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForArmV7", out bBuild) && bBuild
|| UEBuildConfiguration.Architectures.Contains("armv7", StringComparer.OrdinalIgnoreCase))
{
Arches.Add("-armv7");
}
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForArm64", out bBuild) && bBuild
|| UEBuildConfiguration.Architectures.Contains("arm64", StringComparer.OrdinalIgnoreCase))
{
Arches.Add("-arm64");
}
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForx86", out bBuild) && bBuild
|| UEBuildConfiguration.Architectures.Contains("x86", StringComparer.OrdinalIgnoreCase))
{
Arches.Add("-x86");
}
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForx8664", out bBuild) && bBuild
|| UEBuildConfiguration.Architectures.Contains("x64", StringComparer.OrdinalIgnoreCase))
{
Arches.Add("-x64");
}
// force armv7 if something went wrong
if (Arches.Count == 0)
{
Arches.Add("-armv7");
}
// Parse selected GPU architectures
GPUArchitectures = new List<string>();
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForES2", out bBuild) && bBuild
|| UEBuildConfiguration.GPUArchitectures.Contains("es2", StringComparer.OrdinalIgnoreCase))
{
GPUArchitectures.Add("-es2");
}
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForESDeferred", out bBuild) && bBuild
|| UEBuildConfiguration.GPUArchitectures.Contains("esdeferred", StringComparer.OrdinalIgnoreCase))
{
GPUArchitectures.Add("-esdeferred");
}
if (GPUArchitectures.Count == 0)
{
GPUArchitectures.Add("-es2");
}
AllComboNames = (from Arch in Arches
from GPUArch in GPUArchitectures
select Arch + GPUArch).ToList();
}
static public string GetGLESVersionFromGPUArch(string GPUArch)
{
GPUArch = GPUArch.Substring(1); // drop the '-' from the start
string GLESversion = "";
switch (GPUArch)
{
case "es2":
GLESversion = "0x00020000";
break;
case "esdeferred":
GLESversion = "0x00030001";
break;
default:
GLESversion = "0x00020000";
break;
}
return GLESversion;
}
public override void SetUpGlobalEnvironment()
{
base.SetUpGlobalEnvironment();
ParseArchitectures();
}
public List<string> GetAllArchitectures()
{
if (Arches == null)
{
ParseArchitectures();
}
return Arches;
}
public List<string> GetAllGPUArchitectures()
{
if (GPUArchitectures == null)
{
ParseArchitectures();
}
return GPUArchitectures;
}
public int GetNdkApiLevelInt(int MinNdk = 19)
{
string NDKVersion = GetNdkApiLevel();
int NDKVersionInt = MinNdk;
if (NDKVersion.Contains("-"))
{
int Version;
if (int.TryParse(NDKVersion.Substring(NDKVersion.LastIndexOf('-') + 1), out Version))
{
if (Version > NDKVersionInt)
NDKVersionInt = Version;
}
}
return NDKVersionInt;
}
public string GetNdkApiLevel()
{
// ask the .ini system for what version to use
ConfigCacheIni Ini = ConfigCacheIni.CreateConfigCacheIni(UnrealTargetPlatform.Android, "Engine", DirectoryReference.FromFile(ProjectFile));
string NDKLevel;
Ini.GetString("/Script/AndroidPlatformEditor.AndroidSDKSettings", "NDKAPILevel", out NDKLevel);
if (NDKLevel == "latest")
{
// get a list of NDK platforms
string PlatformsDir = Environment.ExpandEnvironmentVariables("%NDKROOT%/platforms");
if (!Directory.Exists(PlatformsDir))
{
throw new BuildException("No platforms found in {0}", PlatformsDir);
}
// return the largest of them
NDKLevel = GetLargestApiLevel(Directory.GetDirectories(PlatformsDir));
}
return NDKLevel;
}
public string GetLargestApiLevel(string[] ApiLevels)
{
int LargestLevel = 0;
string LargestString = null;
// look for largest integer
foreach (string Level in ApiLevels)
{
string LocalLevel = Path.GetFileName(Level);
string[] Tokens = LocalLevel.Split("-".ToCharArray());
if (Tokens.Length >= 2)
{
try
{
int ParsedLevel = int.Parse(Tokens[1]);
// bigger? remember it
if (ParsedLevel > LargestLevel)
{
LargestLevel = ParsedLevel;
LargestString = LocalLevel;
}
}
catch (Exception)
{
// ignore poorly formed string
}
}
}
return LargestString;
}
string GetCLArguments_Global(CPPEnvironment CompileEnvironment, string Architecture)
{
string Result = "";
switch (Architecture)
{
case "-armv7": Result += ToolchainParamsArm; break;
case "-arm64": Result += ToolchainParamsArm64; break;
case "-x86": Result += ToolchainParamsx86; break;
case "-x64": Result += ToolchainParamsx64; break;
default: Result += ToolchainParamsArm; break;
}
// build up the commandline common to C and C++
Result += " -c";
Result += " -fdiagnostics-format=msvc";
Result += " -Wall";
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";
// 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";
//This will prevent the issue of warnings for unused private variables.
Result += " -Wno-unused-private-field";
Result += " -Wno-local-type-template-args"; // engine triggers this
Result += " -Wno-return-type-c-linkage"; // needed for PhysX
Result += " -Wno-reorder"; // member initialization order
Result += " -Wno-unknown-pragmas"; // probably should kill this one, sign of another issue in PhysX?
Result += " -Wno-invalid-offsetof"; // needed to suppress warnings about using offsetof on non-POD types.
Result += " -Wno-logical-op-parentheses"; // needed for external headers we can't change
if (CompileEnvironment.Config.bEnableShadowVariableWarning)
{
Result += " -Wshadow -Wno-error=shadow";
}
// new for clang4.5 warnings:
if (ClangVersionFloat >= 3.5f)
{
Result += " -Wno-undefined-bool-conversion"; // 'this' pointer cannot be null in well-defined C++ code; pointer may be assumed to always convert to true (if (this))
// we use this feature to allow static FNames.
Result += " -Wno-gnu-string-literal-operator-template";
}
if (ClangVersionFloat >= 3.6f)
{
Result += " -Wno-unused-local-typedef"; // clang is being overly strict here? PhysX headers trigger this.
Result += " -Wno-inconsistent-missing-override"; // these have to be suppressed for UE 4.8, should be fixed later.
}
// shipping builds will cause this warning with "ensure", so disable only in those case
if (CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Shipping)
{
Result += " -Wno-unused-value";
}
// debug info
if (CompileEnvironment.Config.bCreateDebugInfo)
{
Result += " -g2 -gdwarf-4";
}
// optimization level
if (CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug)
{
Result += " -O0";
}
else
{
if (UEBuildConfiguration.bCompileForSize)
{
Result += " -Oz";
}
else
{
Result += " -O3";
}
}
//@todo android: these are copied verbatim from UE3 and probably need adjustment
if (Architecture == "-armv7")
{
// Result += " -mthumb-interwork"; // Generates code which supports calling between ARM and Thumb instructions, w/o it you can't reliability use both together
Result += " -funwind-tables"; // Just generates any needed static data, affects no code
Result += " -fstack-protector"; // Emits extra code to check for buffer overflows
// Result += " -mlong-calls"; // Perform function calls by first loading the address of the function into a reg and then performing the subroutine call
Result += " -fno-strict-aliasing"; // Prevents unwanted or invalid optimizations that could produce incorrect code
Result += " -fpic"; // Generates position-independent code (PIC) suitable for use in a shared library
Result += " -fno-exceptions"; // Do not enable exception handling, generates extra code needed to propagate exceptions
Result += " -fno-rtti"; //
Result += " -fno-short-enums"; // Do not allocate to an enum type only as many bytes as it needs for the declared range of possible values
// Result += " -finline-limit=64"; // GCC limits the size of functions that can be inlined, this flag allows coarse control of this limit
// Result += " -Wno-psabi"; // Warn when G++ generates code that is probably not compatible with the vendor-neutral C++ ABI
Result += " -march=armv7-a";
Result += " -mfloat-abi=softfp";
Result += " -mfpu=vfpv3-d16"; //@todo android: UE3 was just vfp. arm7a should all support v3 with 16 registers
// Add flags for on-device debugging
if (CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug)
{
Result += " -fno-omit-frame-pointer"; // Disable removing the save/restore frame pointer for better debugging
if (ClangVersionFloat >= 3.6f)
{
Result += " -fno-function-sections"; // Improve breakpoint location
}
}
// Some switches interfere with on-device debugging
if (CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug)
{
Result += " -ffunction-sections"; // Places each function in its own section of the output file, linker may be able to perform opts to improve locality of reference
}
Result += " -fsigned-char"; // Treat chars as signed //@todo android: any concerns about ABI compatibility with libs here?
}
else if (Architecture == "-arm64")
{
Result += " -funwind-tables"; // Just generates any needed static data, affects no code
Result += " -fstack-protector"; // Emits extra code to check for buffer overflows
Result += " -fno-strict-aliasing"; // Prevents unwanted or invalid optimizations that could produce incorrect code
Result += " -fpic"; // Generates position-independent code (PIC) suitable for use in a shared library
Result += " -fno-exceptions"; // Do not enable exception handling, generates extra code needed to propagate exceptions
Result += " -fno-rtti"; //
Result += " -fno-short-enums"; // Do not allocate to an enum type only as many bytes as it needs for the declared range of possible values
Result += " -D__arm64__"; // for some reason this isn't defined and needed for PhysX
Result += " -march=armv8-a";
//Result += " -mfloat-abi=softfp";
//Result += " -mfpu=vfpv3-d16"; //@todo android: UE3 was just vfp. arm7a should all support v3 with 16 registers
// Some switches interfere with on-device debugging
if (CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug)
{
Result += " -ffunction-sections"; // Places each function in its own section of the output file, linker may be able to perform opts to improve locality of reference
}
Result += " -fsigned-char"; // Treat chars as signed //@todo android: any concerns about ABI compatibility with libs here?
}
else if (Architecture == "-x86")
{
Result += " -fstrict-aliasing";
Result += " -fno-omit-frame-pointer";
Result += " -fno-strict-aliasing";
Result += " -fno-short-enums";
Result += " -fno-exceptions";
Result += " -fno-rtti";
Result += " -march=atom";
}
else if (Architecture == "-x64")
{
Result += " -fstrict-aliasing";
Result += " -fno-omit-frame-pointer";
Result += " -fno-strict-aliasing";
Result += " -fno-short-enums";
Result += " -fno-exceptions";
Result += " -fno-rtti";
Result += " -march=atom";
}
return Result;
}
static string GetCompileArguments_CPP(bool bDisableOptimizations)
{
string Result = "";
Result += " -x c++";
Result += " -std=c++11";
// optimization level
if (bDisableOptimizations)
{
Result += " -O0";
}
else
{
Result += " -O3";
}
return Result;
}
static string GetCompileArguments_C(bool bDisableOptimizations)
{
string Result = "";
Result += " -x c";
// optimization level
if (bDisableOptimizations)
{
Result += " -O0";
}
else
{
Result += " -O3";
}
return Result;
}
static string GetCompileArguments_PCH(bool bDisableOptimizations)
{
string Result = "";
Result += " -x c++-header";
Result += " -std=c++11";
// optimization level
if (bDisableOptimizations)
{
Result += " -O0";
}
else
{
Result += " -O3";
}
return Result;
}
string GetLinkArguments(LinkEnvironment LinkEnvironment, string Architecture)
{
string Result = "";
Result += " -nostdlib";
Result += " -Wl,-shared,-Bsymbolic";
Result += " -Wl,--no-undefined";
if (Architecture == "-arm64")
{
Result += ToolchainParamsArm64;
Result += " -march=armv8-a";
}
else if (Architecture == "-x86")
{
Result += ToolchainParamsx86;
Result += " -march=atom";
}
else if (Architecture == "-x64")
{
Result += ToolchainParamsx64;
Result += " -march=atom";
}
else // if (Architecture == "-armv7")
{
Result += ToolchainParamsArm;
Result += " -march=armv7-a";
Result += " -Wl,--fix-cortex-a8"; // required to route around a CPU bug in some Cortex-A8 implementations
}
if (BuildConfiguration.bUseUnityBuild && ClangVersionFloat >= 3.6f && ClangVersionFloat < 3.8f)
{
Result += " -fuse-ld=gold"; // ld.gold is available in r10e (clang 3.6)
}
// make sure the DT_SONAME field is set properly (or we can a warning toast at startup on new Android)
Result += " -Wl,-soname,libUE4.so";
// verbose output from the linker
// Result += " -v";
return Result;
}
static string GetArArguments(LinkEnvironment LinkEnvironment)
{
string Result = "";
Result += " -r";
return Result;
}
static bool IsDirectoryForArch(string Dir, string Arch)
{
// make sure paths use one particular slash
Dir = Dir.Replace("\\", "/").ToLowerInvariant();
// look for other architectures in the Dir path, and fail if it finds it
foreach (var Pair in AllArchNames)
{
if (Pair.Key != Arch)
{
foreach (var ArchName in Pair.Value)
{
// if there's a directory in the path with a bad architecture name, reject it
if (Regex.IsMatch(Dir, "/" + ArchName + "$") || Regex.IsMatch(Dir, "/" + ArchName + "/"))
{
return false;
}
}
}
}
// if nothing was found, we are okay
return true;
}
static bool ShouldSkipModule(string ModuleName, string Arch)
{
foreach (var ModName in ModulesToSkip[Arch])
{
if (ModName == ModuleName)
{
return true;
}
}
// if nothing was found, we are okay
return false;
}
bool ShouldSkipLib(string Lib, string Arch, string GPUArchitecture)
{
// reject any libs we outright don't want to link with
foreach (var LibName in LibrariesToSkip[Arch])
{
if (LibName == Lib)
{
return true;
}
}
// if another architecture is in the filename, reject it
foreach (string ComboName in AllComboNames)
{
if (ComboName != Arch + GPUArchitecture)
{
if (Path.GetFileNameWithoutExtension(Lib).EndsWith(ComboName))
{
return true;
}
}
}
// if nothing was found, we are okay
return false;
}
static void ConditionallyAddNDKSourceFiles(List<FileItem> SourceFiles, string ModuleName)
{
// We need to add the extra glue and cpu code only to Launch module.
if (ModuleName.Equals("Launch"))
{
SourceFiles.Add(FileItem.GetItemByPath(Environment.GetEnvironmentVariable("NDKROOT") + "/sources/android/native_app_glue/android_native_app_glue.c"));
// Newer NDK cpu_features.c uses getauxval() which causes a SIGSEGV in libhoudini.so (ARM on Intel translator) in older versions of Houdini
// so we patch the file to use alternative methods of detecting CPU features if libhoudini.so is detected
// The basis for this patch is from here: https://android-review.googlesource.com/#/c/110650/
string CpuFeaturesPath = Environment.GetEnvironmentVariable("NDKROOT") + "/sources/android/cpufeatures/";
string CpuFeaturesPatchedFile = CpuFeaturesPath + "cpu-features-patched.c";
if (!File.Exists(CpuFeaturesPatchedFile))
{
// Either make a copy or patch it
string[] CpuFeaturesLines = File.ReadAllLines(CpuFeaturesPath + "cpu-features.c");
// Look for get_elf_hwcap_from_getauxval in the file
bool NeedsPatch = false;
int LineIndex;
for (LineIndex = 0; LineIndex < CpuFeaturesLines.Length; ++LineIndex)
{
if (CpuFeaturesLines[LineIndex].Contains("get_elf_hwcap_from_getauxval"))
{
NeedsPatch = true;
// Make sure it doesn't already have the patch (r10c and 10d have it already, but removed in 10e)
for (int LineIndex2 = LineIndex; LineIndex2 < CpuFeaturesLines.Length; ++LineIndex2)
{
if (CpuFeaturesLines[LineIndex2].Contains("has_houdini_binary_translator(void)"))
{
NeedsPatch = false;
break;
}
}
break;
}
}
// Apply patch or write unchanged
if (NeedsPatch)
{
List<string> CpuFeaturesList = new List<string>(CpuFeaturesLines);
// Skip down to section to add Houdini check function for arm
while (!CpuFeaturesList[++LineIndex].StartsWith("#if defined(__arm__)")) ;
CpuFeaturesList.Insert(++LineIndex, "/* Check Houdini Binary Translator is installed on the system.");
CpuFeaturesList.Insert(++LineIndex, " *");
CpuFeaturesList.Insert(++LineIndex, " * If this function returns 1, get_elf_hwcap_from_getauxval() function");
CpuFeaturesList.Insert(++LineIndex, " * will causes SIGSEGV while calling getauxval() function.");
CpuFeaturesList.Insert(++LineIndex, " */");
CpuFeaturesList.Insert(++LineIndex, "static int");
CpuFeaturesList.Insert(++LineIndex, "has_houdini_binary_translator(void) {");
CpuFeaturesList.Insert(++LineIndex, " int found = 0;");
CpuFeaturesList.Insert(++LineIndex, " if (access(\"/system/lib/libhoudini.so\", F_OK) != -1) {");
CpuFeaturesList.Insert(++LineIndex, " D(\"Found Houdini binary translator\\n\");");
CpuFeaturesList.Insert(++LineIndex, " found = 1;");
CpuFeaturesList.Insert(++LineIndex, " }");
CpuFeaturesList.Insert(++LineIndex, " return found;");
CpuFeaturesList.Insert(++LineIndex, "}");
CpuFeaturesList.Insert(++LineIndex, "");
// Add the Houdini check call
while (!CpuFeaturesList[++LineIndex].Contains("/* Extract the list of CPU features from ELF hwcaps */")) ;
CpuFeaturesList.Insert(LineIndex++, " /* Check Houdini binary translator is installed */");
CpuFeaturesList.Insert(LineIndex++, " int has_houdini = has_houdini_binary_translator();");
CpuFeaturesList.Insert(LineIndex++, "");
// Make the get_elf_hwcap_from_getauxval() calls conditional
while (!CpuFeaturesList[++LineIndex].Contains("hwcaps = get_elf_hwcap_from_getauxval(AT_HWCAP);")) ;
CpuFeaturesList.Insert(LineIndex++, " if (!has_houdini) {");
CpuFeaturesList.Insert(++LineIndex, " }");
while (!CpuFeaturesList[++LineIndex].Contains("hwcaps2 = get_elf_hwcap_from_getauxval(AT_HWCAP2);")) ;
CpuFeaturesList.Insert(LineIndex++, " if (!has_houdini) {");
CpuFeaturesList.Insert(++LineIndex, " }");
File.WriteAllLines(CpuFeaturesPatchedFile, CpuFeaturesList.ToArray());
}
else
{
File.WriteAllLines(CpuFeaturesPatchedFile, CpuFeaturesLines);
}
}
SourceFiles.Add(FileItem.GetItemByPath(CpuFeaturesPatchedFile));
}
}
void GenerateEmptyLinkFunctionsForRemovedModules(List<FileItem> SourceFiles, string ModuleName, DirectoryReference OutputDirectory)
{
// Only add to UELinkerFixups module
if (!ModuleName.Equals("Launch"))
{
return;
}
string LinkerExceptionsName = "../UELinkerExceptions";
FileReference LinkerExceptionsCPPFilename = FileReference.Combine(OutputDirectory, LinkerExceptionsName + ".cpp");
// Create the cpp filename
if (!LinkerExceptionsCPPFilename.Exists())
{
// Create a dummy file in case it doesn't exist yet so that the module does not complain it's not there
ResponseFile.Create(LinkerExceptionsCPPFilename, new List<string>());
}
var Result = new List<string>();
foreach (string Arch in Arches)
{
switch (Arch)
{
case "-armv7": Result.Add("#if PLATFORM_ANDROID_ARM"); break;
case "-arm64": Result.Add("#if PLATFORM_ANDROID_ARM64"); break;
case "-x86": Result.Add("#if PLATFORM_ANDROID_X86"); break;
case "-x64": Result.Add("#if PLATFORM_ANDROID_X64"); break;
default: Result.Add("#if PLATFORM_ANDROID_ARM"); break;
}
foreach (var ModName in ModulesToSkip[Arch])
{
Result.Add(" void EmptyLinkFunctionForStaticInitialization" + ModName + "(){}");
}
Result.Add("#endif");
}
// Determine if the file changed. Write it if it either doesn't exist or the contents are different.
bool bShouldWriteFile = true;
if (LinkerExceptionsCPPFilename.Exists())
{
string[] ExistingExceptionText = File.ReadAllLines(LinkerExceptionsCPPFilename.FullName);
string JoinedNewContents = string.Join("", Result.ToArray());
string JoinedOldContents = string.Join("", ExistingExceptionText);
bShouldWriteFile = (JoinedNewContents != JoinedOldContents);
}
// If we determined that we should write the file, write it now.
if (bShouldWriteFile)
{
ResponseFile.Create(LinkerExceptionsCPPFilename, Result);
}
SourceFiles.Add(FileItem.GetItemByFileReference(LinkerExceptionsCPPFilename));
}
// cache the location of NDK tools
static string ClangPath;
static string ToolchainParamsArm;
static string ToolchainParamsArm64;
static string ToolchainParamsx86;
static string ToolchainParamsx64;
static string ArPathArm;
static string ArPathArm64;
static string ArPathx86;
static string ArPathx64;
static public string GetStripExecutablePath(string UE4Arch)
{
string StripPath;
switch (UE4Arch)
{
case "-armv7": StripPath = ArPathArm; break;
case "-arm64": StripPath = ArPathArm64; break;
case "-x86": StripPath = ArPathx86; break;
case "-x64": StripPath = ArPathx64; break;
default: StripPath = ArPathArm; break;
}
return StripPath.Replace("-ar", "-strip");
}
static private bool bHasPrintedApiLevel = false;
public override CPPOutput CompileCPPFiles(UEBuildTarget Target, CPPEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName)
{
if (Arches.Count == 0)
{
throw new BuildException("At least one architecture (armv7, x86, etc) needs to be selected in the project settings to build");
}
if (!bHasPrintedApiLevel)
{
Console.WriteLine("Compiling Native code with NDK API '{0}'", GetNdkApiLevel());
bHasPrintedApiLevel = true;
}
string BaseArguments = "";
if (CompileEnvironment.Config.PrecompiledHeaderAction != PrecompiledHeaderAction.Create)
{
BaseArguments += " -Werror";
}
// Directly added NDK files for NDK extensions
ConditionallyAddNDKSourceFiles(SourceFiles, ModuleName);
// Deal with dynamic modules removed by architecture
GenerateEmptyLinkFunctionsForRemovedModules(SourceFiles, ModuleName, CompileEnvironment.Config.OutputDirectory);
// Add preprocessor definitions to the argument list.
foreach (string Definition in CompileEnvironment.Config.Definitions)
{
BaseArguments += string.Format(" -D \"{0}\"", Definition);
}
var BuildPlatform = UEBuildPlatform.GetBuildPlatformForCPPTargetPlatform(CompileEnvironment.Config.Target.Platform);
var NDKRoot = Environment.GetEnvironmentVariable("NDKROOT").Replace("\\", "/");
string BasePCHName = "";
var PCHExtension = UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.Android).GetBinaryExtension(UEBuildBinaryType.PrecompiledHeader);
if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
{
BasePCHName = RemoveArchName(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath).Replace(PCHExtension, "");
}
// Create a compile action for each source file.
CPPOutput Result = new CPPOutput();
foreach (string Arch in Arches)
{
if (ShouldSkipModule(ModuleName, Arch))
{
continue;
}
foreach (string GPUArchitecture in GPUArchitectures)
{
// which toolchain to use
string Arguments = GetCLArguments_Global(CompileEnvironment, Arch) + BaseArguments;
switch (Arch)
{
case "-armv7": Arguments += " -DPLATFORM_64BITS=0 -DPLATFORM_ANDROID_ARM=1"; break;
case "-arm64": Arguments += " -DPLATFORM_64BITS=1 -DPLATFORM_ANDROID_ARM64=1"; break;
case "-x86": Arguments += " -DPLATFORM_64BITS=0 -DPLATFORM_ANDROID_X86=1"; break;
case "-x64": Arguments += " -DPLATFORM_64BITS=1 -DPLATFORM_ANDROID_X64=1"; break;
default: Arguments += " -DPLATFORM_64BITS=0 -DPLATFORM_ANDROID_ARM=1"; break;
}
if (GPUArchitecture == "-esdeferred")
{
Arguments += " -DPLATFORM_ANDROIDESDEFERRED=1";
}
// which PCH file to include
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.
PCHArguments += string.Format(" -include \"{0}\"", InlineArchName(BasePCHName, Arch, GPUArchitecture));
}
// Add include paths to the argument list (filtered by architecture)
foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.SystemIncludePaths)
{
if (IsDirectoryForArch(IncludePath, Arch))
{
Arguments += string.Format(" -I\"{0}\"", IncludePath);
}
}
foreach (string IncludePath in CompileEnvironment.Config.CPPIncludeInfo.IncludePaths)
{
if (IsDirectoryForArch(IncludePath, Arch))
{
Arguments += string.Format(" -I\"{0}\"", IncludePath);
}
}
foreach (FileItem SourceFile in SourceFiles)
{
Action CompileAction = new Action(ActionType.Compile);
string FileArguments = "";
bool bIsPlainCFile = Path.GetExtension(SourceFile.AbsolutePath).ToUpperInvariant() == ".C";
bool bDisableShadowWarning = false;
// should we disable optimizations on this file?
// @todo android - We wouldn't need this if we could disable optimizations per function (via pragma)
bool bDisableOptimizations = false;// SourceFile.AbsolutePath.ToUpperInvariant().IndexOf("\\SLATE\\") != -1;
if (bDisableOptimizations && CompileEnvironment.Config.Target.Configuration != CPPTargetConfiguration.Debug)
{
Log.TraceWarning("Disabling optimizations on {0}", SourceFile.AbsolutePath);
}
bDisableOptimizations = bDisableOptimizations || CompileEnvironment.Config.Target.Configuration == CPPTargetConfiguration.Debug;
// Add C or C++ specific compiler arguments.
if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
{
FileArguments += GetCompileArguments_PCH(bDisableOptimizations);
}
else if (bIsPlainCFile)
{
FileArguments += GetCompileArguments_C(bDisableOptimizations);
// remove shadow variable warnings for NDK files
if (SourceFile.AbsolutePath.Replace("\\", "/").StartsWith(NDKRoot))
{
bDisableShadowWarning = true;
}
}
else
{
FileArguments += GetCompileArguments_CPP(bDisableOptimizations);
// only use PCH for .cpp files
FileArguments += PCHArguments;
}
// Add the C++ source file and its included files to the prerequisite item list.
AddPrerequisiteSourceFile(Target, BuildPlatform, CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems);
if (CompileEnvironment.Config.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
{
// Add the precompiled header file to the produced item list.
FileItem PrecompiledHeaderFile = FileItem.GetItemByFileReference(
FileReference.Combine(
CompileEnvironment.Config.OutputDirectory,
Path.GetFileName(InlineArchName(SourceFile.AbsolutePath, Arch, GPUArchitecture) + PCHExtension)
)
);
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;
FileItem ArchPrecompiledHeaderFile = FileItem.GetItemByPath(InlineArchName(BasePCHName, Arch, GPUArchitecture) + PCHExtension);
CompileAction.PrerequisiteItems.Add(ArchPrecompiledHeaderFile);
}
var ObjectFileExtension = UEBuildPlatform.GetBuildPlatform(UnrealTargetPlatform.Android).GetBinaryExtension(UEBuildBinaryType.Object);
// Add the object file to the produced item list.
FileItem ObjectFile = FileItem.GetItemByFileReference(
FileReference.Combine(
CompileEnvironment.Config.OutputDirectory,
InlineArchName(Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension, Arch, GPUArchitecture)
)
);
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);
// Build a full argument list
string AllArguments = Arguments + FileArguments + CompileEnvironment.Config.AdditionalArguments;
AllArguments = ActionThread.ExpandEnvironmentVariables(AllArguments);
AllArguments = AllArguments.Replace("\\", "/");
// Remove shadow warning for this file if requested
if (bDisableShadowWarning)
{
int WarningIndex = AllArguments.IndexOf(" -Wshadow");
if (WarningIndex > 0)
{
AllArguments = AllArguments.Remove(WarningIndex, 9);
}
}
// Create the response file
FileReference ResponseFileName = CompileAction.ProducedItems[0].Reference + ".response";
string ResponseArgument = string.Format("@\"{0}\"", ResponseFile.Create(ResponseFileName, new List<string> { AllArguments }).FullName);
CompileAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory.FullName;
CompileAction.CommandPath = ClangPath;
CompileAction.CommandArguments = ResponseArgument;
CompileAction.StatusDescription = string.Format("{0} [{1}-{2}]", Path.GetFileName(SourceFile.AbsolutePath), Arch.Replace("-", ""), GPUArchitecture.Replace("-", ""));
// VC++ always outputs the source file name being compiled, so we don't need to emit this ourselves
CompileAction.bShouldOutputStatusDescription = 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;
}
}
}
return Result;
}
public override FileItem LinkFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly)
{
return null;
}
static public string InlineArchName(string Pathname, string Arch, string GPUArchitecture)
{
return Path.Combine(Path.GetDirectoryName(Pathname), Path.GetFileNameWithoutExtension(Pathname) + Arch + GPUArchitecture + Path.GetExtension(Pathname));
}
public string RemoveArchName(string Pathname)
{
// remove all architecture names
foreach (string Arch in GetAllArchitectures())
{
foreach (string GPUArchitecture in GetAllGPUArchitectures())
{
Pathname = Path.Combine(Path.GetDirectoryName(Pathname), Path.GetFileName(Pathname).Replace(Arch + GPUArchitecture, ""));
}
}
return Pathname;
}
public override FileItem[] LinkAllFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly)
{
List<FileItem> Outputs = new List<FileItem>();
var NDKRoot = Environment.GetEnvironmentVariable("NDKROOT").Replace("\\", "/");
int NDKApiLevelInt = GetNdkApiLevelInt();
string OptionalLinkArguments;
for (int ArchIndex = 0; ArchIndex < Arches.Count; ArchIndex++)
{
string Arch = Arches[ArchIndex];
// 32-bit ABI may need fixup for removed bsd_signal in NDK11 for android-21+
OptionalLinkArguments = "";
if (NDKApiLevelInt >= 21)
{
// this file was added in NDK11 so use existence to detect (RELEASE.TXT no longer present)
if (File.Exists(Path.Combine(NDKRoot, "source.properties")))
{
switch (Arch)
{
case "-armv7":
OptionalLinkArguments = string.Format(" \"{0}\"", Path.Combine(UnrealBuildTool.EngineDirectory.FullName, "Build/Android/Prebuilt/bsdsignal/lib/armeabi-v7a/libbsdsignal.a"));
break;
case "-x86":
OptionalLinkArguments = string.Format(" \"{0}\"", Path.Combine(UnrealBuildTool.EngineDirectory.FullName, "Build/Android/Prebuilt/bsdsignal/lib/x86/libbsdsignal.a"));
break;
}
}
}
for (int GPUArchIndex = 0; GPUArchIndex < GPUArchitectures.Count; GPUArchIndex++)
{
string GPUArchitecture = GPUArchitectures[GPUArchIndex];
int OutputPathIndex = ArchIndex * GPUArchitectures.Count + GPUArchIndex;
// Android will have an array of outputs
if (LinkEnvironment.Config.OutputFilePaths.Count < OutputPathIndex ||
!LinkEnvironment.Config.OutputFilePaths[OutputPathIndex].GetFileNameWithoutExtension().EndsWith(Arch + GPUArchitecture))
{
throw new BuildException("The OutputFilePaths array didn't match the Arches array in AndroidToolChain.LinkAllFiles");
}
// Create an action that invokes the linker.
Action LinkAction = new Action(ActionType.Link);
LinkAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory.FullName;
if (LinkEnvironment.Config.bIsBuildingLibrary)
{
switch (Arch)
{
case "-armv7": LinkAction.CommandPath = ArPathArm; break;
case "-arm64": LinkAction.CommandPath = ArPathArm64; break;
case "-x86": LinkAction.CommandPath = ArPathx86; ; break;
case "-x64": LinkAction.CommandPath = ArPathx64; ; break;
default: LinkAction.CommandPath = ArPathArm; ; break;
}
}
else
{
LinkAction.CommandPath = ClangPath;
}
string LinkerPath = LinkAction.WorkingDirectory;
LinkAction.WorkingDirectory = LinkEnvironment.Config.IntermediateDirectory.FullName;
// Get link arguments.
LinkAction.CommandArguments = LinkEnvironment.Config.bIsBuildingLibrary ? GetArArguments(LinkEnvironment) : GetLinkArguments(LinkEnvironment, Arch);
// Add the output file as a production of the link action.
FileItem OutputFile;
OutputFile = FileItem.GetItemByFileReference(LinkEnvironment.Config.OutputFilePaths[OutputPathIndex]);
Outputs.Add(OutputFile);
LinkAction.ProducedItems.Add(OutputFile);
LinkAction.StatusDescription = string.Format("{0}", Path.GetFileName(OutputFile.AbsolutePath));
// LinkAction.bPrintDebugInfo = true;
// Add the output file to the command-line.
if (LinkEnvironment.Config.bIsBuildingLibrary)
{
LinkAction.CommandArguments += string.Format(" \"{0}\"", OutputFile.AbsolutePath);
}
else
{
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)
{
// make sure it's for current Arch
if (Path.GetFileNameWithoutExtension(InputFile.AbsolutePath).EndsWith(Arch + GPUArchitecture))
{
string AbsolutePath = InputFile.AbsolutePath.Replace("\\", "/");
AbsolutePath = AbsolutePath.Replace(LinkEnvironment.Config.IntermediateDirectory.FullName.Replace("\\", "/"), "");
AbsolutePath = AbsolutePath.TrimStart(new char[] { '/' });
InputFileNames.Add(string.Format("\"{0}\"", AbsolutePath));
LinkAction.PrerequisiteItems.Add(InputFile);
}
}
FileReference ResponseFileName = GetResponseFileName(LinkEnvironment, OutputFile);
LinkAction.CommandArguments += string.Format(" @\"{0}\"", ResponseFile.Create(ResponseFileName, InputFileNames));
// libs don't link in other libs
if (!LinkEnvironment.Config.bIsBuildingLibrary)
{
// Add the library paths to the argument list.
foreach (string LibraryPath in LinkEnvironment.Config.LibraryPaths)
{
// LinkerPaths could be relative or absolute
string AbsoluteLibraryPath = ActionThread.ExpandEnvironmentVariables(LibraryPath);
if (IsDirectoryForArch(AbsoluteLibraryPath, Arch))
{
// environment variables aren't expanded when using the $( style
if (Path.IsPathRooted(AbsoluteLibraryPath) == false)
{
AbsoluteLibraryPath = Path.Combine(LinkerPath, AbsoluteLibraryPath);
}
LinkAction.CommandArguments += string.Format(" -L\"{0}\"", AbsoluteLibraryPath);
}
}
// add libraries in a library group
LinkAction.CommandArguments += string.Format(" -Wl,--start-group");
foreach (string AdditionalLibrary in LinkEnvironment.Config.AdditionalLibraries)
{
if (!ShouldSkipLib(AdditionalLibrary, Arch, GPUArchitecture))
{
if (String.IsNullOrEmpty(Path.GetDirectoryName(AdditionalLibrary)))
{
LinkAction.CommandArguments += string.Format(" \"-l{0}\"", AdditionalLibrary);
}
else
{
// full pathed libs are compiled by us, so we depend on linking them
LinkAction.CommandArguments += string.Format(" \"{0}\"", Path.GetFullPath(AdditionalLibrary));
LinkAction.PrerequisiteItems.Add(FileItem.GetItemByPath(AdditionalLibrary));
}
}
}
LinkAction.CommandArguments += OptionalLinkArguments;
LinkAction.CommandArguments += string.Format(" -Wl,--end-group");
}
// Add the additional arguments specified by the environment.
LinkAction.CommandArguments += LinkEnvironment.Config.AdditionalArguments;
LinkAction.CommandArguments = LinkAction.CommandArguments.Replace("\\", "/");
// Only execute linking on the local PC.
LinkAction.bCanExecuteRemotely = false;
}
}
return Outputs.ToArray();
}
public override void ModifyBuildProducts(UEBuildBinary Binary, Dictionary<FileReference, BuildProductType> BuildProducts)
{
// the binary will have all of the .so's in the output files, we need to trim down to the shared apk (which is what needs to go into the manifest)
if (Binary.Config.Type != UEBuildBinaryType.StaticLibrary)
{
foreach (FileReference BinaryPath in Binary.Config.OutputFilePaths)
{
FileReference ApkFile = BinaryPath.ChangeExtension(".apk");
BuildProducts.Add(ApkFile, BuildProductType.Executable);
}
}
}
public override void CompileCSharpProject(CSharpEnvironment CompileEnvironment, FileReference ProjectFileName, FileReference DestinationFile)
{
throw new BuildException("Android cannot compile C# files");
}
public static void OutputReceivedDataEventHandler(Object Sender, DataReceivedEventArgs Line)
{
if ((Line != null) && (Line.Data != null))
{
Log.TraceInformation(Line.Data);
}
}
public override void StripSymbols(string SourceFileName, string TargetFileName)
{
File.Copy(SourceFileName, TargetFileName, true);
ProcessStartInfo StartInfo = new ProcessStartInfo();
if (SourceFileName.Contains("-armv7"))
{
StartInfo.FileName = ArPathArm.Replace("-ar.exe", "-strip.exe");
}
else
if (SourceFileName.Contains("-arm64"))
{
StartInfo.FileName = ArPathArm64.Replace("-ar.exe", "-strip.exe");
}
else
if (SourceFileName.Contains("-x86"))
{
StartInfo.FileName = ArPathx86.Replace("-ar.exe", "-strip.exe");
}
else
if (SourceFileName.Contains("-x64"))
{
StartInfo.FileName = ArPathx64.Replace("-ar.exe", "-strip.exe");
}
else
{
throw new BuildException("Couldn't determine Android architecture to strip symbols from {0}", SourceFileName);
}
StartInfo.Arguments = "--strip-debug " + TargetFileName;
StartInfo.UseShellExecute = false;
StartInfo.CreateNoWindow = true;
Utils.RunLocalProcessAndLogOutput(StartInfo);
}
};
}