Files
UnrealEngineUWP/Engine/Source/Programs/UnrealBuildTool/Platform/Lumin/LuminToolChain.cs
jonathan adamczewski 44a6d5481e Move FileItem and DirectoryItem into BuildUtilities
#jira none

[CL 16596289 by jonathan adamczewski in ue5-main branch]
2021-06-08 19:05:33 -04:00

422 lines
14 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Microsoft.Win32;
using EpicGames.Core;
using UnrealBuildBase;
#nullable disable
namespace UnrealBuildTool
{
class LuminToolChain : AndroidToolChain
{
//static private Dictionary<string, string[]> AllArchNames = new Dictionary<string, string[]> {
// { "-arm64", new string[] { "arm64", "arm64-v8a", "android-L-egl-t132-a64" } },
//};
//static private string[] LibrariesToSkip = new string[] { "nvToolsExt", "nvToolsExtStub", "oculus", "vrapi", "ovrkernel", "systemutils", "openglloader", "gpg", };
private bool bUseLLD = false;
private List<string> AdditionalGPUArches;
protected string StripPath;
protected string ObjCopyPath;
private string MabuPath_ = null;
private string MabuPath
{
get
{
if (MabuPath_ == null)
{
MabuPath_ = Environment.ExpandEnvironmentVariables("%MLSDK%/mabu" + (RuntimePlatform.IsWindows ? ".cmd" : ""));
MabuPath_ = MabuPath_.Replace("\"", "");
if (!File.Exists(MabuPath_))
{
throw new BuildException("Could not find mabu command at '{0}'", MabuPath_);
}
}
return MabuPath_;
}
}
[CommandLine("-GpuArchitectures=", ListSeparator = '+')]
public List<string> GPUArchitectureArg = new List<string>();
public LuminToolChain(FileReference InProjectFile, bool bInUseLdGold = true, IReadOnlyList<string> InAdditionalArches = null, IReadOnlyList<string> InAdditionalGPUArches = null, bool bAllowMissingNDK = true)
: base(InProjectFile,
// @todo Lumin: ld gold?
true, null, null, true, AndroidToolChainOptions.None)
{
AdditionalGPUArches = new List<string>();
if (InAdditionalGPUArches != null)
{
AdditionalGPUArches.AddRange(InAdditionalGPUArches);
}
// by default tools chains don't parse arguments, but we want to be able to check the -gpuarchitectures flag defined above. This is
// only necessary when LuminToolChain is used during UAT
CommandLine.ParseArguments(Environment.GetCommandLineArgs(), this);
if (AdditionalGPUArches.Count == 0 && GPUArchitectureArg.Count > 0)
{
AdditionalGPUArches.AddRange(GPUArchitectureArg);
}
string MLSDKPath = Environment.GetEnvironmentVariable("MLSDK");
// don't register if we don't have an MLSDK specified
if (String.IsNullOrEmpty(MLSDKPath))
{
throw new BuildException("MLSDK is not specified; cannot use Lumin toolchain.");
}
MLSDKPath = MLSDKPath.Replace("\"", "");
string MabuSpec = RunMabuAndReadOutput("-t lumin_clang-8.0 --print-spec -q");
// parse clange version
Regex SpecRegex = new Regex("\\s*(?:[a-z]+_lumin_clang-)(\\d)[.](\\d)\\s*");
Match SpecMatch = SpecRegex.Match(MabuSpec);
if (SpecMatch.Groups.Count != 3)
{
throw new BuildException("Could not parse clang version from mabu spec '{0}'", MabuSpec);
}
//string ClangVersion = string.Format("{0}.{1}", SpecMatch.Groups[1].Value, SpecMatch.Groups[2].Value);
SetClangVersion(int.Parse(SpecMatch.Groups[1].Value), int.Parse(SpecMatch.Groups[2].Value), 0);
string MabuTools = RunMabuAndReadOutput("-t lumin_clang-8.0 --print-tools -q");
Dictionary<string, string> ToolsDict = new Dictionary<string, string>();
using (StringReader Reader = new StringReader(MabuTools))
{
string Line = null;
while (null != (Line = Reader.ReadLine()))
{
string[] Split = Line.Split('=');
if (Split.Length != 2)
{
throw new BuildException("Unexpected output from mabu in --print-tools: '{0}'", Line);
}
ToolsDict.Add(Split[0].Trim(), Split[1].Trim());
}
}
// set up the path to our toolchains
// ClangPath and ArPathArm64 are quoted in SetupActionToExecuteCompilerThroughShell() just before use if path has spaces in it.
ClangPath = ToolsDict["CXX"];
ArPathArm64 = ToolsDict["AR"];
// The strip command does not execute through the shell. Hence we don't quote it.
StripPath = ToolsDict["STRIP"];
// The objcopy command does not execute through the shell. Hence we don't quote it.
ObjCopyPath = ToolsDict["OBJCOPY"];
// force the compiler to be executed through a command prompt instance
bExecuteCompilerThroughShell = true;
// toolchain params
ToolchainParamsArm64 = " --sysroot=\"" + Path.Combine(MLSDKPath, "lumin") + "\"";
ToolchainLinkParamsArm64 = ToolchainParamsArm64;
ToolchainLinkParamsx64 = ToolchainParamsx64;
string LLDPath = Path.Combine(MLSDKPath, "tools", "toolchains", "llvm-8", "bin", "ld.lld" + GetHostPlatformBinarySuffix());
string LLDBuild = Utils.RunLocalProcessAndReturnStdOut(LLDPath, "--version");
if (!string.IsNullOrEmpty(LLDBuild))
{
string[] Split = LLDBuild.Split(' ');
if (Split.Length < 2)
{
throw new BuildException("Unexpected output from lld in --version: '{0}'", LLDBuild);
}
bUseLLD = VersionNumber.Parse(Split[1]) >= VersionNumber.Parse("11.0.0");
}
}
public bool UseVulkan()
{
DirectoryReference DirRef = (!string.IsNullOrEmpty(UnrealBuildTool.GetRemoteIniPath()) ? new DirectoryReference(UnrealBuildTool.GetRemoteIniPath()) : (ProjectFile != null ? ProjectFile.Directory : null));
ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirRef, UnrealTargetPlatform.Lumin);
// go by string
bool bUseVulkan = true;
Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bUseVulkan", out bUseVulkan);
return bUseVulkan;
}
public bool UseMobileRendering()
{
DirectoryReference DirRef = (!string.IsNullOrEmpty(UnrealBuildTool.GetRemoteIniPath()) ? new DirectoryReference(UnrealBuildTool.GetRemoteIniPath()) : (ProjectFile != null ? ProjectFile.Directory : null));
ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, DirRef, UnrealTargetPlatform.Lumin);
// go by string
bool bUseMobileRendering = true;
Ini.GetBool("/Script/LuminRuntimeSettings.LuminRuntimeSettings", "bUseMobileRendering", out bUseMobileRendering);
return bUseMobileRendering;
}
public override void ParseArchitectures()
{
Arches = new List<string>() { "-arm64" };
GPUArchitectures = new List<string>();
if (AdditionalGPUArches != null)
{
if (!UseMobileRendering() && AdditionalGPUArches.Contains("lumingl4"))
{
GPUArchitectures.Add("-lumingl4");
}
else
{
GPUArchitectures.Add("-lumin");
}
}
if (GPUArchitectures.Count == 0)
{
if (UseMobileRendering() || UseVulkan())
{
GPUArchitectures.Add("-lumin");
}
else
{
GPUArchitectures.Add("-lumingl4");
}
}
AllComboNames = (from Arch in Arches
from GPUArch in GPUArchitectures
select Arch + GPUArch).ToList();
}
public override void ModifyBuildProducts(ReadOnlyTargetRules Target, UEBuildBinary Binary, List<string> Libraries, List<UEBuildBundleResource> BundleResources, Dictionary<FileReference, BuildProductType> BuildProducts)
{
}
protected override void ModifySourceFiles(CppCompileEnvironment CompileEnvironment, List<FileItem> SourceFiles, string ModuleName)
{
}
protected override void ModifyLibraries(LinkEnvironment LinkEnvironment)
{
}
protected override string GetCLArguments_Global(CppCompileEnvironment CompileEnvironment, string Architecture)
{
string Params = base.GetCLArguments_Global(CompileEnvironment, Architecture);
Params += " -Wno-undefined-var-template";
if (GPUArchitectures.Contains("-lumingl4"))
{
Params += " -DPLATFORM_LUMINGL4=1";
}
else
{
Params += " -DPLATFORM_LUMINGL4=0";
}
// jf: added for seal, as XGE seems to not preserve case in includes properly
Params += " -Wno-nonportable-include-path"; // not all of these are real
return Params;
}
protected override string GetLinkArguments(LinkEnvironment LinkEnvironment, string Architecture)
{
string Result = "";
if (bUseLLD)
{
Result += " -fuse-ld=lld";
}
else
{
Log.TraceWarning("The linker in the Magic Leap LLVM toolchain is known to fail when linking large projects." +
" Run the UpdateLinkerLumin script in Engine/Extras/Android to update the linker if the build command fails.");
}
if (LinkEnvironment.bIsBuildingDLL)
{
Result += " -Wl,-shared,-Bsymbolic";
}
else
{
// ignore unresolved symbols in shared libs
Result += string.Format(" -Wl,--unresolved-symbols=ignore-in-shared-libs");
}
Result += " -Wl,-z,nocopyreloc";
Result += " -Wl,--warn-shared-textrel";
Result += " -Wl,--fatal-warnings";
Result += " -Wl,--no-undefined";
Result += " -no-canonical-prefixes";
Result += " -Wl,-z,relro";
Result += " -Wl,-z,now";
Result += " -Wl,--enable-new-dtags";
Result += " -Wl,--export-dynamic";
Result += " -Wl,-rpath=$ORIGIN";
Result += " -fdiagnostics-format=msvc";
if (!LinkEnvironment.bCreateDebugInfo)
{
Result += " -Wl,--strip-debug";
}
if (!LinkEnvironment.bIsBuildingDLL)
{
// Position independent code executable *only*.
Result += " -pie";
}
Result += ToolchainParamsArm64;
return Result;
}
private static void RunCommandLineProgramWithException(string WorkingDirectory, string Command, string Params, string OverrideDesc = null, bool bUseShellExecute = false)
{
if (OverrideDesc == null)
{
Log.TraceInformation("\nRunning: " + Command + " " + Params);
}
else if (OverrideDesc != "")
{
Log.TraceInformation(OverrideDesc);
Log.TraceVerbose("\nRunning: " + Command + " " + Params);
}
ProcessStartInfo StartInfo = new ProcessStartInfo();
StartInfo.RedirectStandardInput = true;
StartInfo.RedirectStandardError = true;
StartInfo.RedirectStandardOutput = true;
StartInfo.WorkingDirectory = WorkingDirectory;
StartInfo.FileName = Command;
StartInfo.Arguments = Params;
StartInfo.UseShellExecute = bUseShellExecute;
StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
Process Proc = new Process();
Proc.StartInfo = StartInfo;
Proc.Start();
Proc.WaitForExit();
// bat failure
if (Proc.ExitCode != 0)
{
throw new BuildException("{0} failed with args {1}", Command, Params);
}
}
public void RunMabuWithException(string WorkingDirectory, string Params, string OverrideDesc = null)
{
RunCommandLineProgramWithException(WorkingDirectory, MabuPath, Params, OverrideDesc, false);
}
public string RunMabuAndReadOutput(string Params)
{
try
{
return Utils.RunLocalProcessAndReturnStdOut(MabuPath, Params);
}
catch (Exception e)
{
throw new BuildException("Running mabu failed: '{0}'", e.Message);
}
}
protected override bool ValidateNDK(string PlatformsDir, string ApiString)
{
return true;
}
public override string GetNdkApiLevel()
{
return "android-21";
}
public override string GetStripPath(FileReference SourceFile)
{
return StripPath;
}
/// <summary>
/// Creates an object file with only the symbolic debug information from an executable.
/// </summary>
/// <param name="SourceExeFile">The executable with debug symbol information.</param>
/// <param name="TargetSymFile">The generated object file with debug symbols.</param>
public void ExtractSymbols(FileReference SourceExeFile, FileReference TargetSymFile)
{
ProcessStartInfo StartInfo = new ProcessStartInfo();
StartInfo.FileName =ObjCopyPath;
StartInfo.Arguments = " --only-keep-debug \"" + SourceExeFile.FullName + "\" \"" + TargetSymFile.FullName + "\"";
StartInfo.UseShellExecute = false;
StartInfo.CreateNoWindow = true;
Utils.RunLocalProcessAndLogOutput(StartInfo);
}
/// <summary>
/// Creates a debugger link in an executable referencing where the debug symbols for it are located.
/// </summary>
/// <param name="SourceDebugFile">An object file with the debug symbols, can be an executable or the file generated with ExtractSymbols.</param>
/// <param name="TargetExeFile">The executable to reference the split debug info into.</param>
public void LinkSymbols(FileReference SourceDebugFile, FileReference TargetExeFile)
{
ProcessStartInfo StartInfo = new ProcessStartInfo();
StartInfo.FileName = ObjCopyPath;
StartInfo.Arguments = " --add-gnu-debuglink=\"" + SourceDebugFile.FullName + "\" \"" + TargetExeFile.FullName + "\"";
StartInfo.UseShellExecute = false;
StartInfo.CreateNoWindow = true;
Utils.RunLocalProcessAndLogOutput(StartInfo);
}
protected override void SetupActionToExecuteCompilerThroughShell(ref Action CompileOrLinkAction, string CommandPath, string CommandArguments, string CommandDescription)
{
base.SetupActionToExecuteCompilerThroughShell(ref CompileOrLinkAction, CommandPath, CommandArguments, CommandDescription);
string QuotedCommandPath = CommandPath;
if (CommandPath.Contains(' '))
{
QuotedCommandPath = "'" + CommandPath + "'";
}
if (BuildHostPlatform.Current.ShellType == ShellType.Cmd)
{
// Workaround for UE-91990: The first argument is sensitive to whitespaces even when
// properly quoted, so make it something trivial as a workaround.
CompileOrLinkAction.CommandArguments = String.Format("/c \"{0} -DLUMIN_CLANG_CMD_WORKAROUND=1 {1}\"", QuotedCommandPath, CommandArguments);
}
else
{
// When quoting the command for the shell, we also need to escape any quotes. Otherwise
// they inadvertently unquote the interior arguments. For example it would unquote the
// command itself, if it was quoted to allow for spaces in the path, causing errors.
CompileOrLinkAction.CommandArguments = String.Format("-c \'{0} {1}\'", QuotedCommandPath.Replace("\'", "\\\'"), CommandArguments.Replace("\'", "\\\'"));
}
}
private string GetHostPlatformBinarySuffix()
{
if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64)
{
return ".exe";
}
return "";
}
};
}