// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.IO; using System.Text.RegularExpressions; using EpicGames.Core; using Microsoft.Extensions.Logging; using UnrealBuildBase; /////////////////////////////////////////////////////////////////// // If you are looking for supported version numbers, look in the // LinuxPlatformSDK.Versions.cs file next to this file /////////////////////////////////////////////////////////////////// namespace UnrealBuildTool { partial class LinuxPlatformSDK : UEBuildPlatformSDK { public LinuxPlatformSDK(ILogger Logger) : base(Logger) { } protected override string? GetInstalledSDKVersion() { // @todo turnkey: ForceUseSystemCompiler() returns true, we should probably run system clang -V or similar to get version DirectoryReference? SDKDir = GetSDKLocation(); if (SDKDir == null) { return null; } FileReference VersionFile = FileReference.Combine(SDKDir, SDKVersionFileName()); if (!FileReference.Exists(VersionFile)) { // ErrorMessage = "Cannot use an old toolchain (missing " + PlatformSDK.SDKVersionFileName() + " file, assuming version earlier than v11)"; return null; } StreamReader SDKVersionFile = new StreamReader(VersionFile.FullName); string? SDKVersionString = SDKVersionFile.ReadLine(); SDKVersionFile.Close(); return SDKVersionString; } public override bool TryConvertVersionToInt(string? StringValue, out ulong OutValue, string? Hint) { if (StringValue != null) { // if it doesnt start with a v (for an SDK version), assume its a valid version // which will be used for devices. If a messed up toolchain ends up not having a v // 1 will be an invalid range number to pass which will fail the SDK range check if (StringValue[0] != 'v') { OutValue = 1; return true; } // Example: v11_clang-5.0.0-centos7 string FullVersionPattern = @"^v([0-9]+)_.*$"; Match Result = Regex.Match(StringValue, FullVersionPattern); if (Result.Success) { return UInt64.TryParse(Result.Groups[1].Value, out OutValue); } } OutValue = 0; return false; } protected override bool PlatformSupportsAutoSDKs() { return true; } protected override bool DoesHookRequireAdmin(SDKHookType Hook) { return false; } /// /// Platform name (embeds architecture for now) /// private const string TargetPlatformName = "Linux_x64"; /// /// Force using system compiler and error out if not possible /// private int bForceUseSystemCompiler = -1; /// /// Force using legacy LibCxx in Engine/Source/ThirdParty/Unix /// private int bForceUseLegacyLibCxx = -1; /// /// Whether to compile with the verbose flag /// public bool bVerboseCompiler = false; /// /// Whether to link with the verbose flag /// public bool bVerboseLinker = false; /// /// Whether platform supports switching SDKs during runtime /// /// true if supports /// /// Returns a path to the internal SDK /// /// Valid path to the internal SDK, null otherwise public override string? GetInternalSDKPath() { string? SDKRoot = Environment.GetEnvironmentVariable(SDKRootEnvVar); if (!String.IsNullOrEmpty(SDKRoot)) { string AutoSDKPath = Path.Combine(SDKRoot, "Host" + BuildHostPlatform.Current.Platform, GetAutoSDKPlatformName(), GetAutoSDKDirectoryForMainVersion(), LinuxPlatform.DefaultHostArchitecture.LinuxName); if (DirectoryReference.Exists(new DirectoryReference(AutoSDKPath))) { return AutoSDKPath; } } string InTreeSDKPath = Path.Combine(LinuxPlatformSDK.GetInTreeSDKRoot().FullName, GetMainVersion(), LinuxPlatform.DefaultHostArchitecture.LinuxName); if (DirectoryReference.Exists(new DirectoryReference(InTreeSDKPath))) { return InTreeSDKPath; } return null; } protected override bool PreferAutoSDK() { // having LINUX_ROOT set (for legacy reasons or for convenience of cross-compiling certain third party libs) should not make UBT skip AutoSDKs return true; } public string HaveLinuxDependenciesFile() { // This file must have no extension so that GitDeps considers it a binary dependency - it will only be pulled by the Setup script if Linux is enabled. return "HaveLinuxDependencies"; } public string SDKVersionFileName() { return "ToolchainVersion.txt"; } /// /// Returns the in-tree root for the Linux Toolchain for this host platform. /// private static DirectoryReference GetInTreeSDKRoot() { return DirectoryReference.Combine(Unreal.RootDirectory, "Engine/Extras/ThirdPartyNotUE/SDKs", "Host" + BuildHostPlatform.Current.Platform, TargetPlatformName); } /// /// Whether a host can use its system sdk for this platform /// public virtual bool ForceUseSystemCompiler() { // by default tools chains don't parse arguments, but we want to be able to check the -bForceUseSystemCompiler flag. if (bForceUseSystemCompiler == -1) { bForceUseSystemCompiler = 0; string[] CmdLine = Environment.GetCommandLineArgs(); foreach (string CmdLineArg in CmdLine) { if (CmdLineArg.Equals("-ForceUseSystemCompiler", StringComparison.OrdinalIgnoreCase)) { bForceUseSystemCompiler = 1; break; } } } return bForceUseSystemCompiler == 1; } /// /// Force use of Legacy LibCxx in Engine/Source/ThirdParty/Unix, which will be deprecated in the future /// public virtual bool ForceUseLegacyLibCxx() { // by default tools chains don't parse arguments, but we want to be able to check the -bForceUseSystemCompiler flag. if (bForceUseLegacyLibCxx == -1) { bForceUseLegacyLibCxx = 0; string[] CmdLine = Environment.GetCommandLineArgs(); foreach (string CmdLineArg in CmdLine) { if (CmdLineArg.Equals("-ForceUseLegacyLibCxx", StringComparison.OrdinalIgnoreCase)) { bForceUseLegacyLibCxx = 1; break; } } } return bForceUseLegacyLibCxx == 1; } /// /// Returns the root SDK path for all architectures /// WARNING: Do not cache this value - it may be changed after sourcing OutputEnvVars.txt /// /// Valid SDK string public virtual DirectoryReference? GetSDKLocation() { // if new multi-arch toolchain is used, prefer it DirectoryReference? MultiArchRoot = DirectoryReference.FromString(Environment.GetEnvironmentVariable("LINUX_MULTIARCH_ROOT")); if (MultiArchRoot == null) { // check if in-tree SDK is available DirectoryReference InTreeSDKVersionRoot = GetInTreeSDKRoot(); if (InTreeSDKVersionRoot != null) { DirectoryReference InTreeSDKVersionPath = DirectoryReference.Combine(InTreeSDKVersionRoot, GetMainVersion()); if (DirectoryReference.Exists(InTreeSDKVersionPath)) { MultiArchRoot = InTreeSDKVersionPath; } } } return MultiArchRoot; } /// /// Returns the SDK path for a specific architecture /// WARNING: Do not cache this value - it may be changed after sourcing OutputEnvVars.txt /// /// Valid SDK DirectoryReference public virtual DirectoryReference? GetBaseLinuxPathForArchitecture(UnrealArch Architecture) { // if new multi-arch toolchain is used, prefer it DirectoryReference? MultiArchRoot = GetSDKLocation(); DirectoryReference? BaseLinuxPath; if (MultiArchRoot != null) { BaseLinuxPath = DirectoryReference.Combine(MultiArchRoot, Architecture.LinuxName); } else { // use cross linux toolchain if LINUX_ROOT is specified BaseLinuxPath = DirectoryReference.FromString(Environment.GetEnvironmentVariable("LINUX_ROOT")); } return BaseLinuxPath; } /// /// Whether the path contains a valid clang version /// private static bool IsValidClangPath(DirectoryReference BaseLinuxPath) { FileReference ClangPath = FileReference.Combine(BaseLinuxPath, @"bin", (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64) ? "clang++.exe" : "clang++"); return FileReference.Exists(ClangPath); } /// /// Whether the required external SDKs are installed for this platform /// protected override SDKStatus HasRequiredManualSDKInternal() { // FIXME: UBT should loop across all the architectures and compile for all the selected ones. // do not cache this value - it may be changed after sourcing OutputEnvVars.txt DirectoryReference? BaseLinuxPath = GetBaseLinuxPathForArchitecture(LinuxPlatform.DefaultHostArchitecture); if (ForceUseSystemCompiler()) { if (!String.IsNullOrEmpty(LinuxCommon.WhichClang(Logger))) { return SDKStatus.Valid; } } else if (BaseLinuxPath != null) { // paths to our toolchains if BaseLinuxPath is specified if (IsValidClangPath(BaseLinuxPath)) { return SDKStatus.Valid; } } return SDKStatus.Invalid; } } }