You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown Nick.Penwarden
============================
MAJOR FEATURES & CHANGES
============================
Change 4073167 by Krzysztof.Narkowicz
Added subsurface profile for eye shading model.
#jira none
Change 4073422 by Krzysztof.Narkowicz
Added dual specular for subsurface profile shading model.
#jira none
Change 4075278 by Krzysztof.Narkowicz
Fixed forward reflection/refraction rendering issues, which caused ShaderModels.Material.Refraction to fail.
#jira none
Change 4084231 by Krzysztof.Narkowicz
Dual specular - replace lobe spread with two separate roughness multipliers. Default material roughness is now replaced by an average lobe roughness in order to support non dual specular features.
#jira none
Change 4092798 by Matt.Collins
Some HDR refactoring.
Previously the DisplayOutput and ColorGamut were only set in GameUserSettings.
I added a Sink that checks the HDR enable. If it's toggled we apply the correct DisplayOutput and ColorGamut for the current platform (this way we get good settings even if you toggle via the console). These settings are still exposed via the console and can be set independently if the user wants.
Change 4096954 by Chris.Bunner
Added ShaderModelID as scene texture option and renamed existing value to ShaderModelColor to better reflect the internal code.
Change 4111285 by Brian.Karis
Eye shading update.
Added Iris normal (disabled). Removed wrap. Fixed contact shadows.
Change 4155261 by Krzysztof.Narkowicz
Planar reflection prefilter - use scene viewport size instead of reflection target size in order to keep filter size constant in screen space. This makes planar reflection filter more stable in case of dynamic resolution.
#jira none
Change 4167644 by Krzysztof.Narkowicz
Global shader map is now stored in multiple DDC entries (one per shader filename) instead of keeping everything in a single one. This allows to skip recompilation of unchanged shader files.
Change 4183727 by Yuriy.ODonnell
Implemented auto-conversion from deferred to DBuffer decals in forward shading mode (when GBuffer is not available).
Added support for specular and metallic channels for DBuffer decals, based on work by Chris Bunner.
This requires DBufferC to be expanded from 2 to 4 channels, leading to slight increase in DBuffer bandwidth and memory requirements.
Appearance of DBuffer decals is affected by this change, as specular and metallic channel values previously hard-coded in DBufferDecalShared.ush.
Decals were forced to be non-metallic and have specular of 4% (0.5 numeric value). Now the authored decal material values will be used, which matches GBuffer decals.
Added support for DBuffer decals with emissive component.
Most decal types can now be automatically converted, with the exception of stain decals. Those are currently approximated as regular translucent decals.
Change 4197684 by laz.matech
Added a PostProcess Volume test to the map to test that Cinematic Depth of View can be achieved through PPVs as well. Changed the BP_DepthOfFeildPOV asset - I exposed Focus Method so that it can be disabled for the PPV test. Added a second Hair Model head to the InFocusHair test so that it tests in and out of focus hair models (changed the name of the test to FocusHair).
#jira none
Change 4225614 by Rolando.Caloca
DR - Enable depth collision particles on Vulkan mobile
Change 4235489 by Uriel.Doyon
Removed r.DefaultFeature.PointLightUnits and r.DefaultFeature.SpotLightUnits and replaced them by a single r.DefaultFeature.LightUnits which also controls the units of newly placed rect lights.
#jira UE-59525
Change 4260154 by Mark.Satterthwaite
Parallelize the creation of Metal archives and libraries when they are broken up into smaller sub-libraries, this should reduce apparent cook time by going wide across threads on the host of the cooker.
Change 4270594 by Brian.Karis
Fix for textured rect light L pointing away from plane due to approximate diffuse integration.
Change 4273361 by Daniel.Wright
Particle Cutouts with 8 verts now always use stochastic approach. Circle textures with > 234 edges in the convex hull were overflowing the uint64 calculation of the total number of combinations, causing an infinite loop.
Change 4309174 by Mark.Satterthwaite
Graph device utilization from the driver monitor stats - really helps see how well the GPU is being used.
Change 4310121 by Matt.Collins
Optmizing RemoveUniformBuffersFromSource. Brings it from ~20% to ~1.5% in my testing.
#jira none
Change 4312960 by Daniel.Wright
Fix from Stephen Hill for incorrect light grid culling near the near plane
Change 4314169 by Richard.Wallis
FShaderCache and associated public structures are now marked as deprecated. All FShaderCache code hooks removed from MetalRHI, OpenGLDrv and engine Launch/Shutdown logic.
#jira none
Change 4320760 by Arne.Schober
DR - Remove SV_Coverage from basepass interpolants when running with Masked in early Depth with ForwardShading as otherwise earlyZ will be disabled (as the PS has to run).
#jira UE-60992
Change 4334607 by Uriel.Doyon
Added custom overrides to reset ULightComponent::Intensity to default (in FLightComponentDetails).
Now settings a light Intesity to default resets the brightness to the archetype brightness.
This handles correctly cases where the intensity units differs between the two objects.
Also changed FLocalLightComponentDetails so that changing intensity units kepts the same brightness
(by recomputing the Intensity).
#jira UE-61401
Change 4336188 by Rolando.Caloca
DR - Added -ReduceThreadUsage so programs can use less threads (for SCW )
Change 4337967 by Rolando.Caloca
DR - Remove unused RHISupportsShaderCompression function
#rb none
[CL 4358751 by Rolando Caloca in Main branch]
1860 lines
69 KiB
C#
1860 lines
69 KiB
C#
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text.RegularExpressions;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Microsoft.Win32;
|
|
using Tools.DotNETCommon;
|
|
|
|
namespace UnrealBuildTool
|
|
{
|
|
class AndroidToolChain : UEToolChain, IAndroidToolChain
|
|
{
|
|
public static readonly string[] AllCpuSuffixes =
|
|
{
|
|
"-armv7",
|
|
"-arm64",
|
|
"-x86",
|
|
"-x64"
|
|
};
|
|
|
|
public static readonly string[] AllGpuSuffixes =
|
|
{
|
|
"-es2",
|
|
};
|
|
|
|
// sh0rt names for the above suffixes
|
|
public static readonly Dictionary<string, string> ShortArchNames = new Dictionary<string, string>()
|
|
{
|
|
{ "-armv7", "a7" },
|
|
{ "-arm64", "a8" },
|
|
{ "-x86", "x3" },
|
|
{ "-x64", "x6" },
|
|
{ "-es2", "" }, // since there's only one gpu arch now, we can strip it
|
|
//LUMIN_MERGE
|
|
{ "-gl4", "" }
|
|
};
|
|
|
|
|
|
protected FileReference ProjectFile;
|
|
private bool bUseLdGold;
|
|
private List<string> AdditionalArches;
|
|
private List<string> AdditionalGPUArches;
|
|
protected bool bExecuteCompilerThroughShell;
|
|
|
|
// the Clang version being used to compile
|
|
static int ClangVersionMajor = -1;
|
|
static int ClangVersionMinor = -1;
|
|
static int ClangVersionPatch = -1;
|
|
|
|
// the list of architectures we will compile for
|
|
protected List<string> Arches = null;
|
|
// the list of GPU architectures we will compile for
|
|
protected List<string> GPUArchitectures = null;
|
|
// a list of all architecture+GPUArchitecture names (-armv7-es2, etc)
|
|
protected List<string> AllComboNames = null;
|
|
// whether to enable NEON support for armv7 builds
|
|
private bool bUseNEONForArmV7 = false;
|
|
|
|
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", "ThirdParty/Oculus/LibOVRPlatform/LibOVRPlatform/lib/libovrplatformloader.so" } },
|
|
{ "-x86", new string[] { "nvToolsExt", "nvToolsExtStub", "oculus", "OVRPlugin", "vrapi", "vrintegrationloader", "ovrkernel", "systemutils", "openglloader", "ThirdParty/Oculus/LibOVRPlatform/LibOVRPlatform/lib/libovrplatformloader.so", "opus", "speex_resampler", } },
|
|
{ "-x64", new string[] { "nvToolsExt", "nvToolsExtStub", "oculus", "OVRPlugin", "vrapi", "vrintegrationloader", "ovrkernel", "systemutils", "openglloader", "ThirdParty/Oculus/LibOVRPlatform/LibOVRPlatform/lib/libovrplatformloader.so", "gpg", } },
|
|
};
|
|
|
|
static private Dictionary<string, string[]> ModulesToSkip = new Dictionary<string, string[]> {
|
|
{ "-armv7", new string[] { } },
|
|
{ "-arm64", new string[] { "OnlineSubsystemOculus", } },
|
|
{ "-x86", new string[] { "OnlineSubsystemOculus", } },
|
|
{ "-x64", new string[] { "OnlineSubsystemOculus", "OnlineSubsystemGooglePlay", } },
|
|
};
|
|
|
|
static private Dictionary<string, string[]> GeneratedModulesToSkip = new Dictionary<string, string[]> {
|
|
{ "-armv7", new string[] { } },
|
|
{ "-arm64", new string[] { "OculusEntitlementCallbackProxy", "OculusCreateSessionCallbackProxy", "OculusFindSessionsCallbackProxy", "OculusIdentityCallbackProxy", "OculusNetConnection", "OculusNetDriver", "OnlineSubsystemOculus_init" } },
|
|
{ "-x86", new string[] { "OculusEntitlementCallbackProxy", "OculusCreateSessionCallbackProxy", "OculusFindSessionsCallbackProxy", "OculusIdentityCallbackProxy", "OculusNetConnection", "OculusNetDriver", "OnlineSubsystemOculus_init" } },
|
|
{ "-x64", new string[] { "OculusEntitlementCallbackProxy", "OculusCreateSessionCallbackProxy", "OculusFindSessionsCallbackProxy", "OculusIdentityCallbackProxy", "OculusNetConnection", "OculusNetDriver", "OnlineSubsystemOculus_init" } },
|
|
};
|
|
|
|
public string NDKToolchainVersion;
|
|
public string NDKDefine;
|
|
public int NDKDefineInt;
|
|
|
|
protected void SetClangVersion(int Major, int Minor, int Patch)
|
|
{
|
|
ClangVersionMajor = Major;
|
|
ClangVersionMinor = Minor;
|
|
ClangVersionPatch = Patch;
|
|
}
|
|
|
|
public string GetClangVersionString()
|
|
{
|
|
return string.Format("{0}.{1}.{2}", ClangVersionMajor, ClangVersionMinor, ClangVersionPatch);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if compiler version matches the requirements
|
|
/// </summary>
|
|
private static bool CompilerVersionGreaterOrEqual(int Major, int Minor, int Patch)
|
|
{
|
|
return ClangVersionMajor > Major ||
|
|
(ClangVersionMajor == Major && ClangVersionMinor > Minor) ||
|
|
(ClangVersionMajor == Major && ClangVersionMinor == Minor && ClangVersionPatch >= Patch);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if compiler version matches the requirements
|
|
/// </summary>
|
|
private static bool CompilerVersionLessThan(int Major, int Minor, int Patch)
|
|
{
|
|
return ClangVersionMajor < Major ||
|
|
(ClangVersionMajor == Major && ClangVersionMinor < Minor) ||
|
|
(ClangVersionMajor == Major && ClangVersionMinor == Minor && ClangVersionPatch < Patch);
|
|
}
|
|
|
|
[CommandLine("-Architectures=", ListSeparator = '+')]
|
|
public List<string> ArchitectureArg = new List<string>();
|
|
|
|
protected bool bEnableGcSections = true;
|
|
|
|
public AndroidToolChain(FileReference InProjectFile, bool bInUseLdGold, IReadOnlyList<string> InAdditionalArches, IReadOnlyList<string> InAdditionalGPUArches)
|
|
: this(CppPlatform.Android, InProjectFile, bInUseLdGold, InAdditionalArches, InAdditionalGPUArches, false)
|
|
{
|
|
}
|
|
|
|
protected AndroidToolChain(CppPlatform InCppPlatform, FileReference InProjectFile, bool bInUseLdGold, IReadOnlyList<string> InAdditionalArches, IReadOnlyList<string> InAdditionalGPUArches, bool bAllowMissingNDK)
|
|
: base(InCppPlatform)
|
|
{
|
|
ProjectFile = InProjectFile;
|
|
bUseLdGold = bInUseLdGold;
|
|
AdditionalArches = new List<string>();
|
|
AdditionalGPUArches = new List<string>();
|
|
|
|
if (InAdditionalArches != null)
|
|
{
|
|
AdditionalArches.AddRange(InAdditionalArches);
|
|
}
|
|
|
|
if (InAdditionalGPUArches != null)
|
|
{
|
|
AdditionalGPUArches.AddRange(InAdditionalGPUArches);
|
|
}
|
|
|
|
// by default tools chains don't parse arguments, but we want to be able to check the -architectures flag defined above. This is
|
|
// only necessary when AndroidToolChain is used during UAT
|
|
CommandLine.ParseArguments(Environment.GetCommandLineArgs(), this);
|
|
|
|
if (AdditionalArches.Count == 0 && ArchitectureArg.Count > 0)
|
|
{
|
|
AdditionalArches.AddRange(ArchitectureArg);
|
|
}
|
|
|
|
string NDKPath = Environment.GetEnvironmentVariable("NDKROOT");
|
|
|
|
// don't register if we don't have an NDKROOT specified
|
|
if (String.IsNullOrEmpty(NDKPath))
|
|
{
|
|
if (bAllowMissingNDK)
|
|
{
|
|
return;
|
|
}
|
|
throw new BuildException("NDKROOT is not specified; cannot use Android toolchain.");
|
|
}
|
|
|
|
NDKPath = NDKPath.Replace("\"", "");
|
|
|
|
// figure out the NDK version
|
|
NDKToolchainVersion = "unknown";
|
|
NDKDefine = "100500"; // assume r10e
|
|
string SourcePropFilename = Path.Combine(NDKPath, "source.properties");
|
|
if (File.Exists(SourcePropFilename))
|
|
{
|
|
string RevisionString = "";
|
|
string[] PropertyContents = File.ReadAllLines(SourcePropFilename);
|
|
foreach (string PropertyLine in PropertyContents)
|
|
{
|
|
if (PropertyLine.StartsWith("Pkg.Revision"))
|
|
{
|
|
RevisionString = PropertyLine;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int EqualsIndex = RevisionString.IndexOf('=');
|
|
if (EqualsIndex > 0)
|
|
{
|
|
string[] RevisionParts = RevisionString.Substring(EqualsIndex + 1).Trim().Split('.');
|
|
int RevisionMinor = int.Parse(RevisionParts.Length > 1 ? RevisionParts[1] : "0");
|
|
char RevisionLetter = Convert.ToChar('a' + RevisionMinor);
|
|
int RevisionBeta = 0; // @TODO
|
|
NDKToolchainVersion = "r" + RevisionParts[0] + (RevisionMinor > 0 ? Char.ToString(RevisionLetter) : "");
|
|
NDKDefine = RevisionParts[0] + string.Format("{0:00}", RevisionMinor + 1) + string.Format("{0:00}", RevisionBeta);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
string ReleaseFilename = Path.Combine(NDKPath, "RELEASE.TXT");
|
|
if (File.Exists(ReleaseFilename))
|
|
{
|
|
string[] PropertyContents = File.ReadAllLines(SourcePropFilename);
|
|
NDKToolchainVersion = PropertyContents[0];
|
|
}
|
|
}
|
|
if (!int.TryParse(NDKDefine, out NDKDefineInt))
|
|
{
|
|
NDKDefineInt = 100500;
|
|
}
|
|
|
|
string ClangVersion = "";
|
|
string GccVersion = "";
|
|
|
|
string ArchitecturePath = "";
|
|
string ArchitecturePathWindows32 = @"prebuilt/windows";
|
|
string ArchitecturePathWindows64 = @"prebuilt/windows-x86_64";
|
|
string ArchitecturePathMac = @"prebuilt/darwin-x86_64";
|
|
string ArchitecturePathLinux = @"prebuilt/linux-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 if (Directory.Exists(Path.Combine(NDKPath, ArchitecturePathLinux)))
|
|
{
|
|
Log.TraceVerbose(" Found Linux versions of toolchain");
|
|
ArchitecturePath = ArchitecturePathLinux;
|
|
ExeExtension = "";
|
|
}
|
|
else
|
|
{
|
|
throw new BuildException("Couldn't find 32-bit or 64-bit versions of the Android toolchain with NDKROOT: " + NDKPath);
|
|
}
|
|
|
|
// prefer clang 3.6, but fall back if needed for now
|
|
if (Directory.Exists(Path.Combine(NDKPath, @"toolchains/llvm-3.6")))
|
|
{
|
|
SetClangVersion(3, 6, 0);
|
|
ClangVersion = "-3.6";
|
|
GccVersion = "4.9";
|
|
}
|
|
else if (Directory.Exists(Path.Combine(NDKPath, @"toolchains/llvm-3.5")))
|
|
{
|
|
SetClangVersion(3, 5, 0);
|
|
ClangVersion = "-3.5";
|
|
GccVersion = "4.9";
|
|
}
|
|
else if (Directory.Exists(Path.Combine(NDKPath, @"toolchains/llvm-3.3")))
|
|
{
|
|
SetClangVersion(3, 3, 0);
|
|
ClangVersion = "-3.3";
|
|
GccVersion = "4.8";
|
|
}
|
|
else if (Directory.Exists(Path.Combine(NDKPath, @"toolchains/llvm-3.1")))
|
|
{
|
|
SetClangVersion(3, 1, 0);
|
|
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('.');
|
|
SetClangVersion(int.Parse(VersionParts[0]), (VersionParts.Length > 1) ? int.Parse(VersionParts[1]) : 0, (VersionParts.Length > 2) ? int.Parse(VersionParts[2]) : 0);
|
|
ClangVersion = "";
|
|
GccVersion = "4.9";
|
|
}
|
|
else
|
|
{
|
|
throw new BuildException("Cannot find supported Android toolchain with NDKPath:" + NDKPath);
|
|
}
|
|
|
|
// set up the path to our toolchains
|
|
ClangPath = Utils.CollapseRelativeDirectories(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 NDKApiLevel32Int = GetNdkApiLevelInt();
|
|
int NDKApiLevel64Int = NDKApiLevel32Int;
|
|
string NDKApiLevel32Bit = GetNdkApiLevel();
|
|
string NDKApiLevel64Bit = NDKApiLevel32Bit;
|
|
if (NDKApiLevel64Int < 21)
|
|
{
|
|
NDKApiLevel64Int = 21;
|
|
NDKApiLevel64Bit = "android-21";
|
|
}
|
|
|
|
// toolchain params
|
|
ToolchainLinkParamsArm = " -target armv7-none-linux-androideabi" +
|
|
" --sysroot=\"" + Path.Combine(NDKPath, "platforms", NDKApiLevel32Bit, "arch-arm") + "\"" +
|
|
" -gcc-toolchain \"" + Path.Combine(NDKPath, @"toolchains/arm-linux-androideabi-" + GccVersion, ArchitecturePath) + "\"";
|
|
ToolchainLinkParamsArm64 = " -target aarch64-none-linux-android" +
|
|
" --sysroot=\"" + Path.Combine(NDKPath, "platforms", NDKApiLevel64Bit, "arch-arm64") + "\"" +
|
|
" -gcc-toolchain \"" + Path.Combine(NDKPath, @"toolchains/aarch64-linux-android-" + GccVersion, ArchitecturePath) + "\"";
|
|
ToolchainLinkParamsx86 = " -target i686-none-linux-android" +
|
|
" --sysroot=\"" + Path.Combine(NDKPath, "platforms", NDKApiLevel32Bit, "arch-x86") + "\"" +
|
|
" -gcc-toolchain \"" + Path.Combine(NDKPath, @"toolchains/x86-" + GccVersion, ArchitecturePath) + "\"";
|
|
ToolchainLinkParamsx64 = " -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) + "\"";
|
|
|
|
// use NDK version -D__ANDROID_API__ for r14b+
|
|
if (NDKDefineInt >= 140200)
|
|
{
|
|
ToolchainParamsArm = " -target armv7-none-linux-androideabi" +
|
|
" --sysroot=\"" + Path.Combine(NDKPath, "sysroot") + "\"" +
|
|
" -isystem " + Path.Combine(NDKPath, "sysroot/usr/include/arm-linux-androideabi/") +
|
|
" -D__ANDROID_API__=" + NDKApiLevel32Int;
|
|
ToolchainParamsArm64 = " -target aarch64-none-linux-android" +
|
|
" --sysroot=\"" + Path.Combine(NDKPath, "sysroot") + "\"" +
|
|
" -isystem " + Path.Combine(NDKPath, "sysroot/usr/include/aarch64-linux-android/") +
|
|
" -D__ANDROID_API__=" + NDKApiLevel64Int;
|
|
ToolchainParamsx86 = " -target i686-none-linux-android" +
|
|
" --sysroot=\"" + Path.Combine(NDKPath, "sysroot") + "\"" +
|
|
" -isystem " + Path.Combine(NDKPath, "sysroot/usr/include/i686-linux-android/") +
|
|
" -D__ANDROID_API__=" + NDKApiLevel32Int;
|
|
ToolchainParamsx86 = " -target x86_64-none-linux-android" +
|
|
" --sysroot=\"" + Path.Combine(NDKPath, "sysroot") + "\"" +
|
|
" -isystem " + Path.Combine(NDKPath, "sysroot/usr/include/x86_64-linux-android/") +
|
|
" -D__ANDROID_API__=" + NDKApiLevel64Int;
|
|
}
|
|
else
|
|
{
|
|
ToolchainParamsArm = ToolchainLinkParamsArm;
|
|
ToolchainParamsArm64 = ToolchainLinkParamsArm64;
|
|
ToolchainParamsx86 = ToolchainLinkParamsx86;
|
|
ToolchainParamsx64 = ToolchainLinkParamsx64;
|
|
}
|
|
}
|
|
|
|
public virtual void ParseArchitectures()
|
|
{
|
|
// look in ini settings for what platforms to compile for
|
|
ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.Android);
|
|
Arches = new List<string>();
|
|
bool bBuild = true;
|
|
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForArmV7", out bBuild) && bBuild
|
|
|| (AdditionalArches != null && AdditionalArches.Contains("armv7", StringComparer.OrdinalIgnoreCase)))
|
|
{
|
|
Arches.Add("-armv7");
|
|
}
|
|
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForArm64", out bBuild) && bBuild
|
|
|| (AdditionalArches != null && AdditionalArches.Contains("arm64", StringComparer.OrdinalIgnoreCase)))
|
|
{
|
|
Arches.Add("-arm64");
|
|
}
|
|
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForx86", out bBuild) && bBuild
|
|
|| (AdditionalArches != null && AdditionalArches.Contains("x86", StringComparer.OrdinalIgnoreCase)))
|
|
{
|
|
Arches.Add("-x86");
|
|
}
|
|
if (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildForx8664", out bBuild) && bBuild
|
|
|| (AdditionalArches != null && AdditionalArches.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
|
|
|| (AdditionalGPUArches != null && AdditionalGPUArches.Contains("es2", StringComparer.OrdinalIgnoreCase)))
|
|
{
|
|
GPUArchitectures.Add("-es2");
|
|
}
|
|
if (GPUArchitectures.Count == 0)
|
|
{
|
|
GPUArchitectures.Add("-es2");
|
|
}
|
|
|
|
Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bUseNEONForArmV7", out bUseNEONForArmV7);
|
|
|
|
AllComboNames = (from Arch in Arches
|
|
from GPUArch in GPUArchitectures
|
|
select Arch + GPUArch).ToList();
|
|
}
|
|
|
|
static public string GetGLESVersionFromGPUArch(string GPUArch, bool bES30Minimum, bool bBuildForES2, bool bBuildForES31)
|
|
{
|
|
GPUArch = GPUArch.Substring(1); // drop the '-' from the start
|
|
string GLESversion = "";
|
|
switch (GPUArch)
|
|
{
|
|
case "es2":
|
|
GLESversion = "0x00020000";
|
|
break;
|
|
default:
|
|
GLESversion = "0x00020000";
|
|
break;
|
|
}
|
|
if (bES30Minimum && (GLESversion[6] < '3'))
|
|
{
|
|
GLESversion = "0x00030000";
|
|
}
|
|
if (!bBuildForES2 && bBuildForES31)
|
|
{
|
|
GLESversion = "0x00030001";
|
|
}
|
|
|
|
return GLESversion;
|
|
}
|
|
|
|
private bool BuildWithHiddenSymbolVisibility(CppCompileEnvironment CompileEnvironment)
|
|
{
|
|
ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.Android);
|
|
bool bBuild = false;
|
|
return CompileEnvironment.Configuration == CppConfiguration.Shipping && (Ini.GetBool("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "bBuildWithHiddenSymbolVisibility", out bBuild) && bBuild);
|
|
}
|
|
|
|
public override void SetUpGlobalEnvironment(ReadOnlyTargetRules Target)
|
|
{
|
|
base.SetUpGlobalEnvironment(Target);
|
|
|
|
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;
|
|
}
|
|
|
|
protected virtual bool ValidateNDK(string PlatformsDir, string ApiString)
|
|
{
|
|
if (!Directory.Exists(PlatformsDir))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string NDKPlatformDir = Path.Combine(PlatformsDir, ApiString);
|
|
return Directory.Exists(NDKPlatformDir);
|
|
}
|
|
|
|
public string GetNdkApiLevel()
|
|
{
|
|
// ask the .ini system for what version to use
|
|
ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirectoryReference.FromFile(ProjectFile), UnrealTargetPlatform.Android);
|
|
string NDKLevel;
|
|
Ini.GetString("/Script/AndroidPlatformEditor.AndroidSDKSettings", "NDKAPILevel", out NDKLevel);
|
|
|
|
// check for project override of NDK API level
|
|
string ProjectNDKLevel;
|
|
Ini.GetString("/Script/AndroidRuntimeSettings.AndroidRuntimeSettings", "NDKAPILevelOverride", out ProjectNDKLevel);
|
|
ProjectNDKLevel = ProjectNDKLevel.Trim();
|
|
if (ProjectNDKLevel != "")
|
|
{
|
|
NDKLevel = ProjectNDKLevel;
|
|
}
|
|
|
|
string PlatformsDir = Environment.ExpandEnvironmentVariables("%NDKROOT%/platforms");
|
|
if (NDKLevel == "latest")
|
|
{
|
|
// get a list of NDK platforms
|
|
if (!Directory.Exists(PlatformsDir))
|
|
{
|
|
throw new BuildException("No NDK platforms found in {0}", PlatformsDir);
|
|
}
|
|
|
|
// return the largest of them
|
|
NDKLevel = GetLargestApiLevel(Directory.GetDirectories(PlatformsDir));
|
|
}
|
|
|
|
// validate the platform NDK is installed
|
|
if (!ValidateNDK(PlatformsDir, NDKLevel))
|
|
{
|
|
throw new BuildException("The NDK API requested '{0}' not installed in {1}", NDKLevel, 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;
|
|
}
|
|
|
|
protected virtual string GetCLArguments_Global(CppCompileEnvironment 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 += " -Wdelete-non-virtual-dtor";
|
|
|
|
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 (BuildWithHiddenSymbolVisibility(CompileEnvironment))
|
|
{
|
|
Result += " -fvisibility=hidden -fvisibility-inlines-hidden"; // Symbols default to hidden.
|
|
}
|
|
|
|
if (CompileEnvironment.bEnableShadowVariableWarnings)
|
|
{
|
|
Result += " -Wshadow -Wno-error=shadow";
|
|
}
|
|
|
|
if (CompileEnvironment.bEnableUndefinedIdentifierWarnings)
|
|
{
|
|
Result += " -Wundef" + (CompileEnvironment.bUndefinedIdentifierWarningsAsErrors ? "" : " -Wno-error=undef");
|
|
}
|
|
|
|
// new for clang4.5 warnings:
|
|
if (CompilerVersionGreaterOrEqual(3, 5, 0))
|
|
{
|
|
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 (CompilerVersionGreaterOrEqual(3, 6, 0))
|
|
{
|
|
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.
|
|
}
|
|
|
|
if (CompilerVersionGreaterOrEqual(3, 8, 275480))
|
|
{
|
|
Result += " -Wno-undefined-var-template"; // not really a good warning to disable
|
|
Result += " -Wno-nonportable-include-path"; // not all of these are real
|
|
}
|
|
|
|
if (CompilerVersionGreaterOrEqual(4, 0, 0))
|
|
{
|
|
Result += " -Wno-unused-lambda-capture"; // probably should fix the code
|
|
// Result += " -Wno-nonportable-include-path"; // not all of these are real
|
|
}
|
|
|
|
// shipping builds will cause this warning with "ensure", so disable only in those case
|
|
if (CompileEnvironment.Configuration == CppConfiguration.Shipping)
|
|
{
|
|
Result += " -Wno-unused-value";
|
|
}
|
|
|
|
// debug info
|
|
if (CompileEnvironment.bCreateDebugInfo)
|
|
{
|
|
Result += " -g2 -gdwarf-4";
|
|
}
|
|
|
|
// optimization level
|
|
if (!CompileEnvironment.bOptimizeCode)
|
|
{
|
|
Result += " -O0";
|
|
}
|
|
else
|
|
{
|
|
if (CompileEnvironment.bOptimizeForSize)
|
|
{
|
|
Result += " -Oz";
|
|
}
|
|
else
|
|
{
|
|
Result += " -O3";
|
|
}
|
|
}
|
|
|
|
|
|
// Optionally enable exception handling (off by default since it generates extra code needed to propagate exceptions)
|
|
if (CompileEnvironment.bEnableExceptions)
|
|
{
|
|
Result += " -fexceptions";
|
|
}
|
|
else
|
|
{
|
|
Result += " -fno-exceptions";
|
|
}
|
|
|
|
// Conditionally enable (default disabled) generation of information about every class with virtual functions for use by the C++ runtime type identification features
|
|
// (`dynamic_cast' and `typeid'). If you don't use those parts of the language, you can save some space by using -fno-rtti.
|
|
// Note that exception handling uses the same information, but it will generate it as needed.
|
|
if (CompileEnvironment.bUseRTTI)
|
|
{
|
|
Result += " -frtti";
|
|
}
|
|
else
|
|
{
|
|
Result += " -fno-rtti";
|
|
}
|
|
|
|
//@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-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";
|
|
|
|
if (bUseNEONForArmV7)
|
|
{
|
|
Result += " -mfpu=neon";
|
|
}
|
|
else
|
|
{
|
|
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.Configuration == CppConfiguration.Debug)
|
|
{
|
|
Result += " -fno-omit-frame-pointer"; // Disable removing the save/restore frame pointer for better debugging
|
|
if (CompilerVersionGreaterOrEqual(3, 6, 0))
|
|
{
|
|
Result += " -fno-function-sections"; // Improve breakpoint location
|
|
}
|
|
}
|
|
|
|
// Some switches interfere with on-device debugging
|
|
if (CompileEnvironment.Configuration != CppConfiguration.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 += " -fdata-sections"; // Places each data item 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-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.Configuration != CppConfiguration.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 += " -march=atom";
|
|
}
|
|
else if (Architecture == "-x64")
|
|
{
|
|
Result += " -fstrict-aliasing";
|
|
Result += " -fno-omit-frame-pointer";
|
|
Result += " -fno-strict-aliasing";
|
|
Result += " -fno-short-enums";
|
|
Result += " -march=atom";
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
static string GetCompileArguments_CPP(bool bDisableOptimizations)
|
|
{
|
|
string Result = "";
|
|
|
|
Result += " -x c++";
|
|
Result += " -std=c++14";
|
|
|
|
// 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++14";
|
|
|
|
// optimization level
|
|
if (bDisableOptimizations)
|
|
{
|
|
Result += " -O0";
|
|
}
|
|
else
|
|
{
|
|
Result += " -O3";
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
protected virtual string GetLinkArguments(LinkEnvironment LinkEnvironment, string Architecture)
|
|
{
|
|
string Result = "";
|
|
|
|
Result += " -nostdlib";
|
|
Result += " -Wl,-shared,-Bsymbolic";
|
|
Result += " -Wl,--no-undefined";
|
|
if(bEnableGcSections)
|
|
{
|
|
Result += " -Wl,-gc-sections"; // Enable garbage collection of unused input sections. works best with -ffunction-sections, -fdata-sections
|
|
}
|
|
|
|
if (!LinkEnvironment.bCreateDebugInfo)
|
|
{
|
|
Result += " -Wl,--strip-debug";
|
|
}
|
|
|
|
if (Architecture == "-arm64")
|
|
{
|
|
Result += ToolchainLinkParamsArm64;
|
|
Result += " -march=armv8-a";
|
|
}
|
|
else if (Architecture == "-x86")
|
|
{
|
|
Result += ToolchainLinkParamsx86;
|
|
Result += " -march=atom";
|
|
}
|
|
else if (Architecture == "-x64")
|
|
{
|
|
Result += ToolchainLinkParamsx64;
|
|
Result += " -march=atom";
|
|
}
|
|
else // if (Architecture == "-armv7")
|
|
{
|
|
Result += ToolchainLinkParamsArm;
|
|
Result += " -march=armv7-a";
|
|
Result += " -Wl,--fix-cortex-a8"; // required to route around a CPU bug in some Cortex-A8 implementations
|
|
|
|
if (LinkEnvironment.Configuration == CppConfiguration.Shipping)
|
|
{
|
|
Result += " -Wl,--icf=all"; // Enables ICF (Identical Code Folding). [all, safe] safe == fold functions that can be proven not to have their address taken.
|
|
Result += " -Wl,--icf-iterations=3";
|
|
}
|
|
}
|
|
|
|
if (bUseLdGold && CompilerVersionGreaterOrEqual(3, 6, 0) && CompilerVersionLessThan(3, 8, 0))
|
|
{
|
|
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";
|
|
|
|
Result += " -Wl,--build-id"; // add build-id to make debugging easier
|
|
|
|
// verbose output from the linker
|
|
// Result += " -v";
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
protected virtual void ModifyLibraries(LinkEnvironment LinkEnvironment)
|
|
{
|
|
// @todo Lumin: verify this works with base android
|
|
if (GetNdkApiLevelInt() >= 21)
|
|
{
|
|
// this file was added in NDK11 so use existence to detect (RELEASE.TXT no longer present)
|
|
string NDKRoot = Environment.GetEnvironmentVariable("NDKROOT").Replace("\\", "/");
|
|
if (File.Exists(Path.Combine(NDKRoot, "source.properties")))
|
|
{
|
|
LinkEnvironment.AdditionalLibraries.Add(Path.Combine(UnrealBuildTool.EngineDirectory.FullName, "Build/Android/Prebuilt/bsdsignal/lib/armeabi-v7a/libbsdsignal.a"));
|
|
LinkEnvironment.AdditionalLibraries.Add(Path.Combine(UnrealBuildTool.EngineDirectory.FullName, "Build/Android/Prebuilt/bsdsignal/lib/x86/libbsdsignal.a"));
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (KeyValuePair<string, string[]> Pair in AllArchNames)
|
|
{
|
|
if (Pair.Key != Arch)
|
|
{
|
|
foreach (string 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 (string 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 (string LibName in LibrariesToSkip[Arch])
|
|
{
|
|
if (LibName == Lib)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// deal with .so files with wrong architecture
|
|
if (Path.GetExtension(Lib) == ".so")
|
|
{
|
|
string ParentDirectory = Path.GetDirectoryName(Lib);
|
|
if (!IsDirectoryForArch(ParentDirectory, Arch))
|
|
{
|
|
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 private string GetNativeGluePath()
|
|
{
|
|
return Environment.GetEnvironmentVariable("NDKROOT") + "/sources/android/native_app_glue/android_native_app_glue.c";
|
|
}
|
|
|
|
protected virtual void ModifySourceFiles(CppCompileEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName)
|
|
{
|
|
// We need to add the extra glue and cpu code only to Launch module.
|
|
if (ModuleName.Equals("Launch") || ModuleName.Equals("AndroidLauncher"))
|
|
{
|
|
SourceFiles.Add(FileItem.GetItemByPath(GetNativeGluePath()));
|
|
|
|
// 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 Launch module
|
|
if (!ModuleName.Equals("Launch"))
|
|
{
|
|
return;
|
|
}
|
|
|
|
string LinkerExceptionsName = "../UELinkerExceptions";
|
|
FileReference LinkerExceptionsCPPFilename = FileReference.Combine(OutputDirectory, LinkerExceptionsName + ".cpp");
|
|
|
|
// Create the cpp filename
|
|
if (!FileReference.Exists(LinkerExceptionsCPPFilename))
|
|
{
|
|
// Create a dummy file in case it doesn't exist yet so that the module does not complain it's not there
|
|
FileItem.CreateIntermediateTextFile(LinkerExceptionsCPPFilename, new List<string>());
|
|
}
|
|
|
|
List<string> Result = new List<string>();
|
|
Result.Add("#include \"CoreTypes.h\"");
|
|
Result.Add("");
|
|
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 (string ModName in ModulesToSkip[Arch])
|
|
{
|
|
Result.Add(" void EmptyLinkFunctionForStaticInitialization" + ModName + "(){}");
|
|
}
|
|
foreach (string ModName in GeneratedModulesToSkip[Arch])
|
|
{
|
|
Result.Add(" void EmptyLinkFunctionForGeneratedCode" + 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 (FileReference.Exists(LinkerExceptionsCPPFilename))
|
|
{
|
|
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)
|
|
{
|
|
FileItem.CreateIntermediateTextFile(LinkerExceptionsCPPFilename, Result);
|
|
}
|
|
|
|
SourceFiles.Add(FileItem.GetItemByFileReference(LinkerExceptionsCPPFilename));
|
|
}
|
|
|
|
// cache the location of NDK tools
|
|
protected static string ClangPath;
|
|
protected static string ToolchainParamsArm;
|
|
protected static string ToolchainParamsArm64;
|
|
protected static string ToolchainParamsx86;
|
|
protected static string ToolchainParamsx64;
|
|
protected static string ToolchainLinkParamsArm;
|
|
protected static string ToolchainLinkParamsArm64;
|
|
protected static string ToolchainLinkParamsx86;
|
|
protected static string ToolchainLinkParamsx64;
|
|
protected static string ArPathArm;
|
|
protected static string ArPathArm64;
|
|
protected static string ArPathx86;
|
|
protected 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;
|
|
static private bool bHasHandledLaunchModule = false;
|
|
public override CPPOutput CompileCPPFiles(CppCompileEnvironment CompileEnvironment, List<FileItem> InputFiles, DirectoryReference OutputDir, string ModuleName, ActionGraph ActionGraph)
|
|
{
|
|
if (Arches.Count == 0)
|
|
{
|
|
throw new BuildException("At least one architecture (armv7, x86, etc) needs to be selected in the project settings to build");
|
|
}
|
|
|
|
CPPOutput Result = new CPPOutput();
|
|
|
|
// Skip if nothing to do
|
|
if (InputFiles.Count == 0)
|
|
{
|
|
return Result;
|
|
}
|
|
|
|
/*
|
|
Trace.TraceInformation("CompileCPPFiles: Module={0}, SourceFiles={1}", ModuleName, SourceFiles.Count);
|
|
foreach (string Arch in Arches)
|
|
{
|
|
Trace.TraceInformation(" Arch: {0}", Arch);
|
|
}
|
|
foreach (FileItem SourceFile in SourceFiles)
|
|
{
|
|
Trace.TraceInformation(" {0}", SourceFile.AbsolutePath);
|
|
}
|
|
*/
|
|
|
|
if (!bHasPrintedApiLevel)
|
|
{
|
|
Log.TraceInformation("Compiling Native code with NDK API '{0}'", GetNdkApiLevel());
|
|
bHasPrintedApiLevel = true;
|
|
}
|
|
|
|
string BaseArguments = "";
|
|
|
|
if (CompileEnvironment.PrecompiledHeaderAction != PrecompiledHeaderAction.Create)
|
|
{
|
|
BaseArguments += " -Werror";
|
|
|
|
}
|
|
|
|
string NativeGluePath = Path.GetFullPath(GetNativeGluePath());
|
|
|
|
// Deal with Launch module special if first time seen
|
|
if (!bHasHandledLaunchModule && (ModuleName.Equals("Launch") || ModuleName.Equals("AndroidLauncher")))
|
|
{
|
|
// Directly added NDK files for NDK extensions
|
|
ModifySourceFiles(CompileEnvironment, InputFiles, ModuleName);
|
|
// Deal with dynamic modules removed by architecture
|
|
GenerateEmptyLinkFunctionsForRemovedModules(InputFiles, ModuleName, OutputDir);
|
|
|
|
bHasHandledLaunchModule = true;
|
|
}
|
|
|
|
// Add preprocessor definitions to the argument list.
|
|
foreach (string Definition in CompileEnvironment.Definitions)
|
|
{
|
|
BaseArguments += string.Format(" -D \"{0}\"", Definition);
|
|
}
|
|
|
|
//LUMIN_MERGE
|
|
//string NDKRoot = Environment.GetEnvironmentVariable("NDKROOT").Replace("\\", "/");
|
|
|
|
string BasePCHName = "";
|
|
UEBuildPlatform BuildPlatform = UEBuildPlatform.GetBuildPlatform(UEBuildPlatform.CPPTargetPlatformToUnrealTargetPlatform(CompileEnvironment.Platform));
|
|
string PCHExtension = ".gch";
|
|
if (CompileEnvironment.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
|
|
{
|
|
BasePCHName = RemoveArchName(CompileEnvironment.PrecompiledHeaderFile.AbsolutePath).Replace(PCHExtension, "");
|
|
}
|
|
|
|
// Create a compile action for each source file.
|
|
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 (Arch == "-arm64" || (Arch == "-armv7" && bUseNEONForArmV7))
|
|
{
|
|
Arguments += " -DPLATFORM_ENABLE_VECTORINTRINSICS_NEON=1";
|
|
}
|
|
|
|
Arguments += " -DPLATFORM_ANDROIDGL4=" + ((GPUArchitecture == "-gl4") ? "1" : "0");
|
|
|
|
// which PCH file to include
|
|
string PCHArguments = "";
|
|
if (CompileEnvironment.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
|
|
{
|
|
// add the platform-specific PCH reference
|
|
PCHArguments += string.Format(" -include-pch \"{0}\"", InlineArchName(BasePCHName, Arch, GPUArchitecture) + PCHExtension);
|
|
|
|
// 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}\"", BasePCHName);
|
|
}
|
|
|
|
foreach (FileItem ForceIncludeFile in CompileEnvironment.ForceIncludeFiles)
|
|
{
|
|
PCHArguments += string.Format(" -include \"{0}\"", ForceIncludeFile.Location);
|
|
}
|
|
|
|
// Add include paths to the argument list (filtered by architecture)
|
|
foreach (DirectoryReference IncludePath in CompileEnvironment.IncludePaths.SystemIncludePaths)
|
|
{
|
|
if (IsDirectoryForArch(IncludePath.FullName, Arch))
|
|
{
|
|
Arguments += string.Format(" -I\"{0}\"", IncludePath);
|
|
}
|
|
}
|
|
foreach (DirectoryReference IncludePath in CompileEnvironment.IncludePaths.UserIncludePaths)
|
|
{
|
|
if (IsDirectoryForArch(IncludePath.FullName, Arch))
|
|
{
|
|
Arguments += string.Format(" -I\"{0}\"", IncludePath);
|
|
}
|
|
}
|
|
|
|
foreach (FileItem SourceFile in InputFiles)
|
|
{
|
|
Action CompileAction = ActionGraph.Add(ActionType.Compile);
|
|
CompileAction.PrerequisiteItems.AddRange(CompileEnvironment.ForceIncludeFiles);
|
|
|
|
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.bOptimizeCode)
|
|
{
|
|
Log.TraceWarning("Disabling optimizations on {0}", SourceFile.AbsolutePath);
|
|
}
|
|
|
|
bDisableOptimizations = bDisableOptimizations || !CompileEnvironment.bOptimizeCode;
|
|
|
|
// Add C or C++ specific compiler arguments.
|
|
if (bIsPlainCFile)
|
|
{
|
|
FileArguments += GetCompileArguments_C(bDisableOptimizations);
|
|
|
|
// remove shadow variable warnings for externally included files
|
|
if (!SourceFile.Location.IsUnderDirectory(UnrealBuildTool.RootDirectory))
|
|
{
|
|
bDisableShadowWarning = true;
|
|
}
|
|
}
|
|
else if (CompileEnvironment.PrecompiledHeaderAction == PrecompiledHeaderAction.Create)
|
|
{
|
|
FileArguments += GetCompileArguments_PCH(bDisableOptimizations);
|
|
}
|
|
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(CompileEnvironment, SourceFile, CompileAction.PrerequisiteItems);
|
|
|
|
if (CompileEnvironment.PrecompiledHeaderAction == PrecompiledHeaderAction.Create && !bIsPlainCFile)
|
|
{
|
|
// Add the precompiled header file to the produced item list.
|
|
FileItem PrecompiledHeaderFile = FileItem.GetItemByFileReference(
|
|
FileReference.Combine(
|
|
OutputDir,
|
|
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);
|
|
}
|
|
else
|
|
{
|
|
if (CompileEnvironment.PrecompiledHeaderAction == PrecompiledHeaderAction.Include)
|
|
{
|
|
CompileAction.bIsUsingPCH = true;
|
|
FileItem ArchPrecompiledHeaderFile = FileItem.GetItemByPath(InlineArchName(BasePCHName, Arch, GPUArchitecture) + PCHExtension);
|
|
CompileAction.PrerequisiteItems.Add(ArchPrecompiledHeaderFile);
|
|
}
|
|
|
|
string ObjectFileExtension;
|
|
if(CompileEnvironment.AdditionalArguments != null && CompileEnvironment.AdditionalArguments.Contains("-emit-llvm"))
|
|
{
|
|
ObjectFileExtension = ".bc";
|
|
}
|
|
else
|
|
{
|
|
ObjectFileExtension = ".o";
|
|
}
|
|
|
|
// Add the object file to the produced item list.
|
|
FileItem ObjectFile = FileItem.GetItemByFileReference(
|
|
FileReference.Combine(
|
|
OutputDir,
|
|
InlineArchName(Path.GetFileName(SourceFile.AbsolutePath) + ObjectFileExtension, Arch, GPUArchitecture, true)
|
|
)
|
|
);
|
|
CompileAction.ProducedItems.Add(ObjectFile);
|
|
Result.ObjectFiles.Add(ObjectFile);
|
|
|
|
FileArguments += string.Format(" -o \"{0}\"", ObjectFile.AbsolutePath);
|
|
}
|
|
|
|
// 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.AdditionalArguments;
|
|
|
|
if (SourceFile.AbsolutePath.Equals(NativeGluePath))
|
|
{
|
|
// Remove visibility settings for android native glue. Since it doesn't decorate with visibility attributes.
|
|
AllArguments = AllArguments.Replace("-fvisibility=hidden -fvisibility-inlines-hidden", "");
|
|
}
|
|
|
|
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].Location + ".rsp";
|
|
FileItem ResponseFileItem = FileItem.CreateIntermediateTextFile(ResponseFileName, new List<string> { AllArguments });
|
|
string ResponseArgument = string.Format("@\"{0}\"", ResponseFileName);
|
|
|
|
CompileAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory.FullName;
|
|
if(bExecuteCompilerThroughShell)
|
|
{
|
|
if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64)
|
|
{
|
|
CompileAction.CommandPath = "cmd.exe";
|
|
CompileAction.CommandArguments = String.Format("/c \"{0} {1}\"", ClangPath, ResponseArgument);
|
|
}
|
|
else
|
|
{
|
|
CompileAction.CommandPath = "/bin/sh";
|
|
CompileAction.CommandArguments = String.Format("-c \"{0} {1}\"", ClangPath, ResponseArgument);
|
|
CompileAction.CommandDescription = "Compile";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CompileAction.CommandPath = ClangPath;
|
|
CompileAction.CommandArguments = ResponseArgument;
|
|
}
|
|
CompileAction.PrerequisiteItems.Add(ResponseFileItem);
|
|
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.PrecompiledHeaderAction != PrecompiledHeaderAction.Create ||
|
|
CompileEnvironment.bAllowRemotelyCompiledPCHs;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
public override FileItem LinkFiles(LinkEnvironment LinkEnvironment, bool bBuildImportLibraryOnly, ActionGraph ActionGraph)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
static public string InlineArchName(string Pathname, string Arch, string GPUArchitecture, bool bUseShortNames = false)
|
|
{
|
|
string FinalArch = Arch;
|
|
string FinalGPUArch = GPUArchitecture;
|
|
if (bUseShortNames)
|
|
{
|
|
FinalArch = ShortArchNames[FinalArch];
|
|
FinalGPUArch = ShortArchNames[FinalGPUArch];
|
|
}
|
|
return Path.Combine(Path.GetDirectoryName(Pathname), Path.GetFileNameWithoutExtension(Pathname) + FinalArch + FinalGPUArch + 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, ActionGraph ActionGraph)
|
|
{
|
|
List<FileItem> Outputs = new List<FileItem>();
|
|
|
|
if (!LinkEnvironment.bIsBuildingLibrary)
|
|
{
|
|
// @todo Lumin: will this add them multiple times?
|
|
ModifyLibraries(LinkEnvironment);
|
|
}
|
|
|
|
for (int ArchIndex = 0; ArchIndex < Arches.Count; ArchIndex++)
|
|
{
|
|
string Arch = Arches[ArchIndex];
|
|
|
|
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.OutputFilePaths.Count < OutputPathIndex ||
|
|
!LinkEnvironment.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 = ActionGraph.Add(ActionType.Link);
|
|
LinkAction.WorkingDirectory = UnrealBuildTool.EngineSourceDirectory.FullName;
|
|
|
|
if (LinkEnvironment.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.IntermediateDirectory.FullName;
|
|
|
|
// Get link arguments.
|
|
LinkAction.CommandArguments = LinkEnvironment.bIsBuildingLibrary ? GetArArguments(LinkEnvironment) : GetLinkArguments(LinkEnvironment, Arch);
|
|
|
|
// Add the output file as a production of the link action.
|
|
FileItem OutputFile;
|
|
OutputFile = FileItem.GetItemByFileReference(LinkEnvironment.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.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(ShortArchNames[Arch] + ShortArchNames[GPUArchitecture]))
|
|
{
|
|
string InputPath;
|
|
if (InputFile.Location.IsUnderDirectory(LinkEnvironment.IntermediateDirectory))
|
|
{
|
|
InputPath = InputFile.Location.MakeRelativeTo(LinkEnvironment.IntermediateDirectory);
|
|
}
|
|
else
|
|
{
|
|
InputPath = InputFile.Location.FullName;
|
|
}
|
|
InputFileNames.Add(string.Format("\"{0}\"", InputPath.Replace('\\', '/')));
|
|
|
|
LinkAction.PrerequisiteItems.Add(InputFile);
|
|
}
|
|
}
|
|
|
|
string LinkResponseArguments = "";
|
|
|
|
// libs don't link in other libs
|
|
if (!LinkEnvironment.bIsBuildingLibrary)
|
|
{
|
|
// Add the library paths to the argument list.
|
|
foreach (DirectoryReference LibraryPath in LinkEnvironment.LibraryPaths)
|
|
{
|
|
// LinkerPaths could be relative or absolute
|
|
string AbsoluteLibraryPath = ActionThread.ExpandEnvironmentVariables(LibraryPath.FullName);
|
|
if (IsDirectoryForArch(AbsoluteLibraryPath, Arch))
|
|
{
|
|
// environment variables aren't expanded when using the $( style
|
|
if (Path.IsPathRooted(AbsoluteLibraryPath) == false)
|
|
{
|
|
AbsoluteLibraryPath = Path.Combine(LinkerPath, AbsoluteLibraryPath);
|
|
}
|
|
LinkResponseArguments += string.Format(" -L\"{0}\"", Utils.CollapseRelativeDirectories(AbsoluteLibraryPath));
|
|
}
|
|
}
|
|
|
|
// add libraries in a library group
|
|
LinkResponseArguments += string.Format(" -Wl,--start-group");
|
|
foreach (string AdditionalLibrary in LinkEnvironment.AdditionalLibraries)
|
|
{
|
|
if (!ShouldSkipLib(AdditionalLibrary, Arch, GPUArchitecture))
|
|
{
|
|
if (String.IsNullOrEmpty(Path.GetDirectoryName(AdditionalLibrary)))
|
|
{
|
|
LinkResponseArguments += string.Format(" \"-l{0}\"", AdditionalLibrary);
|
|
}
|
|
else
|
|
{
|
|
// full pathed libs are compiled by us, so we depend on linking them
|
|
LinkResponseArguments += string.Format(" \"{0}\"", Path.GetFullPath(AdditionalLibrary));
|
|
LinkAction.PrerequisiteItems.Add(FileItem.GetItemByPath(AdditionalLibrary));
|
|
}
|
|
}
|
|
}
|
|
LinkResponseArguments += string.Format(" -Wl,--end-group");
|
|
|
|
// Write the MAP file to the output directory.
|
|
if (LinkEnvironment.bCreateMapFile)
|
|
{
|
|
FileReference MAPFilePath = FileReference.Combine(LinkEnvironment.OutputDirectory, Path.GetFileNameWithoutExtension(OutputFile.AbsolutePath) + ".map");
|
|
FileItem MAPFile = FileItem.GetItemByFileReference(MAPFilePath);
|
|
LinkResponseArguments += String.Format(" -Wl,--cref -Wl,-Map,\"{0}\"", MAPFilePath);
|
|
LinkAction.ProducedItems.Add(MAPFile);
|
|
|
|
// Export a list of object file paths, so we can locate the object files referenced by the map file
|
|
ExportObjectFilePaths(LinkEnvironment, Path.ChangeExtension(MAPFilePath.FullName, ".objpaths"));
|
|
}
|
|
}
|
|
|
|
// Add the additional arguments specified by the environment.
|
|
LinkResponseArguments += LinkEnvironment.AdditionalArguments;
|
|
|
|
// Write out a response file
|
|
FileReference ResponseFileName = GetResponseFileName(LinkEnvironment, OutputFile);
|
|
InputFileNames.Add(LinkResponseArguments.Replace("\\", "/"));
|
|
|
|
FileItem ResponseFileItem = FileItem.CreateIntermediateTextFile(ResponseFileName, InputFileNames);
|
|
|
|
LinkAction.CommandArguments += string.Format(" @\"{0}\"", ResponseFileName);
|
|
LinkAction.PrerequisiteItems.Add(ResponseFileItem);
|
|
|
|
// Fix up the paths in commandline
|
|
LinkAction.CommandArguments = LinkAction.CommandArguments.Replace("\\", "/");
|
|
|
|
// Only execute linking on the local PC.
|
|
LinkAction.bCanExecuteRemotely = false;
|
|
|
|
if(bExecuteCompilerThroughShell)
|
|
{
|
|
if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64)
|
|
{
|
|
LinkAction.CommandArguments = String.Format("/c \"{0} {1}\"", LinkAction.CommandPath, LinkAction.CommandArguments);
|
|
LinkAction.CommandPath = "cmd.exe";
|
|
}
|
|
else
|
|
{
|
|
LinkAction.CommandArguments = String.Format("-c \"{0} {1}\"", LinkAction.CommandPath, LinkAction.CommandArguments);
|
|
LinkAction.CommandPath = "/bin/sh";
|
|
LinkAction.CommandDescription = "Link";
|
|
}
|
|
}
|
|
|
|
// Windows can run into an issue with too long of a commandline when clang tries to call ld to link.
|
|
// To work around this we call clang to just get the command it would execute and generate a
|
|
// second response file to directly call ld with the right arguments instead of calling through clang.
|
|
/* disable while tracking down some linker errors this introduces
|
|
if (!Utils.IsRunningOnMono)
|
|
{
|
|
// capture the actual link command without running it
|
|
ProcessStartInfo StartInfo = new ProcessStartInfo();
|
|
StartInfo.WorkingDirectory = LinkEnvironment.IntermediateDirectory.FullName;
|
|
StartInfo.FileName = LinkAction.CommandPath;
|
|
StartInfo.Arguments = "-### " + LinkAction.CommandArguments;
|
|
StartInfo.UseShellExecute = false;
|
|
StartInfo.CreateNoWindow = true;
|
|
StartInfo.RedirectStandardError = true;
|
|
|
|
LinkerCommandline = "";
|
|
|
|
Process Proc = new Process();
|
|
Proc.StartInfo = StartInfo;
|
|
Proc.ErrorDataReceived += new DataReceivedEventHandler(OutputReceivedForLinker);
|
|
Proc.Start();
|
|
Proc.BeginErrorReadLine();
|
|
Proc.WaitForExit(5000);
|
|
|
|
LinkerCommandline = LinkerCommandline.Trim();
|
|
|
|
// the command should be in quotes; if not we'll just use clang to link as usual
|
|
int FirstQuoteIndex = LinkerCommandline.IndexOf('"');
|
|
if (FirstQuoteIndex >= 0)
|
|
{
|
|
int SecondQuoteIndex = LinkerCommandline.Substring(FirstQuoteIndex + 1).IndexOf('"');
|
|
if (SecondQuoteIndex >= 0)
|
|
{
|
|
LinkAction.CommandPath = LinkerCommandline.Substring(FirstQuoteIndex + 1, SecondQuoteIndex - FirstQuoteIndex);
|
|
LinkAction.CommandArguments = LinkerCommandline.Substring(FirstQuoteIndex + SecondQuoteIndex + 3);
|
|
|
|
// replace double backslashes
|
|
LinkAction.CommandPath = LinkAction.CommandPath.Replace("\\\\", "/");
|
|
|
|
// now create a response file for the full command using ld directly
|
|
FileReference FinalResponseFileName = FileReference.Combine(LinkEnvironment.IntermediateDirectory, OutputFile.Location.GetFileName() + ".responseFinal");
|
|
FileItem FinalResponseFileItem = FileItem.CreateIntermediateTextFile(FinalResponseFileName, LinkAction.CommandArguments);
|
|
LinkAction.CommandArguments = string.Format("@\"{0}\"", FinalResponseFileName);
|
|
LinkAction.PrerequisiteItems.Add(FinalResponseFileItem);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
return Outputs.ToArray();
|
|
}
|
|
|
|
// captures stderr from clang
|
|
private static string LinkerCommandline = "";
|
|
static public void OutputReceivedForLinker(Object Sender, DataReceivedEventArgs Line)
|
|
{
|
|
if ((Line != null) && (Line.Data != null) && (Line.Data.Contains("--sysroot")))
|
|
{
|
|
LinkerCommandline += Line.Data;
|
|
}
|
|
}
|
|
|
|
private void ExportObjectFilePaths(LinkEnvironment LinkEnvironment, string FileName)
|
|
{
|
|
// Write the list of object file directories
|
|
HashSet<DirectoryReference> ObjectFileDirectories = new HashSet<DirectoryReference>();
|
|
foreach (FileItem InputFile in LinkEnvironment.InputFiles)
|
|
{
|
|
ObjectFileDirectories.Add(InputFile.Location.Directory);
|
|
}
|
|
foreach (FileItem InputLibrary in LinkEnvironment.InputLibraries)
|
|
{
|
|
ObjectFileDirectories.Add(InputLibrary.Location.Directory);
|
|
}
|
|
foreach (string AdditionalLibrary in LinkEnvironment.AdditionalLibraries.Where(x => Path.IsPathRooted(x)))
|
|
{
|
|
ObjectFileDirectories.Add(new FileReference(AdditionalLibrary).Directory);
|
|
}
|
|
foreach (DirectoryReference LibraryPath in LinkEnvironment.LibraryPaths)
|
|
{
|
|
ObjectFileDirectories.Add(LibraryPath);
|
|
}
|
|
foreach (string LibraryPath in (Environment.GetEnvironmentVariable("LIB") ?? "").Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
|
{
|
|
ObjectFileDirectories.Add(new DirectoryReference(LibraryPath));
|
|
}
|
|
Directory.CreateDirectory(Path.GetDirectoryName(FileName));
|
|
File.WriteAllLines(FileName, ObjectFileDirectories.Select(x => x.FullName).OrderBy(x => x).ToArray());
|
|
}
|
|
|
|
public override void ModifyBuildProducts(ReadOnlyTargetRules Target, UEBuildBinary Binary, List<string> Libraries, List<UEBuildBundleResource> BundleResources, Dictionary<FileReference, BuildProductType> BuildProducts)
|
|
{
|
|
// only the .so needs to be in the manifest; we always have to build the apk since its contents depend on the project
|
|
|
|
/*
|
|
// 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 (Target.bDeployAfterCompile && Binary.Config.Type != UEBuildBinaryType.StaticLibrary)
|
|
{
|
|
foreach (FileReference BinaryPath in Binary.Config.OutputFilePaths)
|
|
{
|
|
FileReference ApkFile = BinaryPath.ChangeExtension(".apk");
|
|
BuildProducts.Add(ApkFile, BuildProductType.Package);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
public static void OutputReceivedDataEventHandler(Object Sender, DataReceivedEventArgs Line)
|
|
{
|
|
if ((Line != null) && (Line.Data != null))
|
|
{
|
|
Log.TraceInformation(Line.Data);
|
|
}
|
|
}
|
|
|
|
public virtual string GetStripPath(FileReference SourceFile)
|
|
{
|
|
string StripExe;
|
|
if (SourceFile.FullName.Contains("-armv7"))
|
|
{
|
|
StripExe = ArPathArm;
|
|
}
|
|
else
|
|
if (SourceFile.FullName.Contains("-arm64"))
|
|
{
|
|
StripExe = ArPathArm64;
|
|
}
|
|
else
|
|
if (SourceFile.FullName.Contains("-x86"))
|
|
{
|
|
StripExe = ArPathx86;
|
|
}
|
|
else
|
|
if (SourceFile.FullName.Contains("-x64"))
|
|
{
|
|
StripExe = ArPathx64;
|
|
}
|
|
else
|
|
{
|
|
throw new BuildException("Couldn't determine Android architecture to strip symbols from {0}", SourceFile.FullName);
|
|
}
|
|
|
|
// fix the executable (replace the last -ar with -strip and keep any extension)
|
|
int ArIndex = StripExe.LastIndexOf("-ar");
|
|
StripExe = StripExe.Substring(0, ArIndex) + "-strip" + StripExe.Substring(ArIndex + 3);
|
|
return StripExe;
|
|
}
|
|
|
|
public void StripSymbols(FileReference SourceFile, FileReference TargetFile)
|
|
{
|
|
if (SourceFile != TargetFile)
|
|
{
|
|
// Strip command only works in place so we need to copy original if target is different
|
|
File.Copy(SourceFile.FullName, TargetFile.FullName, true);
|
|
}
|
|
|
|
ProcessStartInfo StartInfo = new ProcessStartInfo();
|
|
StartInfo.FileName = GetStripPath(SourceFile).Trim('"');
|
|
StartInfo.Arguments = " --strip-debug \"" + TargetFile.FullName + "\"";
|
|
StartInfo.UseShellExecute = false;
|
|
StartInfo.CreateNoWindow = true;
|
|
Utils.RunLocalProcessAndLogOutput(StartInfo);
|
|
}
|
|
};
|
|
}
|