// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using EpicGames.Core; using System.Text.RegularExpressions; using UnrealBuildBase; namespace UnrealBuildTool { class LinuxPlatformSDK : UEBuildPlatformSDK { public override string GetMainVersion() { return "v19_clang-11.0.1-centos7"; } public override void GetValidVersionRange(out string MinVersion, out string MaxVersion) { // all that matters is the number after the v, according to TryConvertVersionToInt() MinVersion = "v10_clang-5.0.0-centos7"; MaxVersion = "v19_clang-11.0.1-centos7"; } public override void GetValidSoftwareVersionRange(out string? MinVersion, out string? MaxVersion) { MinVersion = MaxVersion = null; } public override string GetAutoSDKPlatformName() { return TargetPlatformName; } public override string? GetInstalledSDKVersion() { // @todo turnkey: ForceUseSystemCompiler() returns true, we should probably run system clang -V or similar to get version string? SDKDir = GetSDKLocation(); if (string.IsNullOrEmpty(SDKDir)) { return null; } string VersionFile = Path.Combine(SDKDir, SDKVersionFileName()); if (!File.Exists(VersionFile)) { // ErrorMessage = "Cannot use an old toolchain (missing " + PlatformSDK.SDKVersionFileName() + " file, assuming version earlier than v11)"; return null; } StreamReader SDKVersionFile = new StreamReader(VersionFile); string? SDKVersionString = SDKVersionFile.ReadLine(); SDKVersionFile.Close(); return SDKVersionString; } public override bool TryConvertVersionToInt(string? StringValue, out UInt64 OutValue) { if (StringValue != null) { // 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; /// /// 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, TargetPlatformName, GetAutoSDKDirectoryForMainVersion(), LinuxPlatform.DefaultHostArchitecture); if (DirectoryReference.Exists(new DirectoryReference(AutoSDKPath))) { return AutoSDKPath; } } string InTreeSDKPath = Path.Combine(LinuxPlatformSDK.GetInTreeSDKRoot().FullName, GetMainVersion(), LinuxPlatform.DefaultHostArchitecture); 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; } /// /// 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 string? GetSDKLocation() { // if new multi-arch toolchain is used, prefer it string? MultiArchRoot = Environment.GetEnvironmentVariable("LINUX_MULTIARCH_ROOT"); if (String.IsNullOrEmpty(MultiArchRoot)) { // 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.FullName; } } } 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 string public virtual string? GetBaseLinuxPathForArchitecture(string Architecture) { // if new multi-arch toolchain is used, prefer it string? MultiArchRoot = GetSDKLocation(); string? BaseLinuxPath; if (!String.IsNullOrEmpty(MultiArchRoot)) { BaseLinuxPath = Path.Combine(MultiArchRoot, Architecture); } else { // use cross linux toolchain if LINUX_ROOT is specified BaseLinuxPath = 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 string? BaseLinuxPath = GetBaseLinuxPathForArchitecture(LinuxPlatform.DefaultHostArchitecture); if (ForceUseSystemCompiler()) { if (!String.IsNullOrEmpty(LinuxCommon.WhichClang()) || !String.IsNullOrEmpty(LinuxCommon.WhichGcc())) { return SDKStatus.Valid; } } else if (!String.IsNullOrEmpty(BaseLinuxPath)) { // paths to our toolchains if BaseLinuxPath is specified BaseLinuxPath = BaseLinuxPath.Replace("\"", ""); if (IsValidClangPath(new DirectoryReference(BaseLinuxPath))) { return SDKStatus.Valid; } } return SDKStatus.Invalid; } } }