// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using EpicGames.Core;
using Microsoft.Extensions.Logging;
using UnrealBuildBase;
namespace UnrealBuildTool
{
///
/// Base class for platform-specific project generators
///
class LinuxProjectGenerator : PlatformProjectGenerator
{
///
/// Constructor
///
/// Command line arguments passed to the project generator
/// Logger for output
public LinuxProjectGenerator(CommandLineArguments Arguments, ILogger Logger)
: base(Arguments, Logger)
{
}
///
/// Enumerate all the platforms that this generator supports
///
public override IEnumerable GetPlatforms()
{
yield return UnrealTargetPlatform.Linux;
yield return UnrealTargetPlatform.LinuxArm64;
}
///
public override bool HasVisualStudioSupport(VSSettings InVSSettings)
{
return false;
}
///
public override IList GetSystemIncludePaths(UEBuildTarget InTarget)
{
List Result = new List();
string EngineDirectory = Unreal.EngineDirectory.ToString();
string? UseLibcxxEnvVarOverride = Environment.GetEnvironmentVariable("UE_LINUX_USE_LIBCXX");
UnrealArch TargetArchitecture = InTarget.Architectures.SingleArchitecture;
if (String.IsNullOrEmpty(UseLibcxxEnvVarOverride) || UseLibcxxEnvVarOverride == "1")
{
if (TargetArchitecture == UnrealArch.X64 || TargetArchitecture == UnrealArch.Arm64)
{
// libc++ include directories
Result.Add(Path.Combine(EngineDirectory, "Source/ThirdParty/Unix/LibCxx/include/"));
Result.Add(Path.Combine(EngineDirectory, "Source/ThirdParty/Unix/LibCxx/include/c++/v1"));
}
}
UEBuildPlatformSDK BuildPlatformSdk = UEBuildPlatform.GetSDK(InTarget.Platform)!;
string? InternalSdkPath = BuildPlatformSdk.GetInternalSDKPath();
if (InternalSdkPath != null)
{
string PlatformSdkVersionString = BuildPlatformSdk.GetInstalledVersion()!;
string Version = GetLinuxToolchainVersionFromFullString(PlatformSdkVersionString);
string ClangIncludeDirectory = Path.Combine(InternalSdkPath, "lib/clang/" + Version + "/include/");
Result.Add(Path.Combine(InternalSdkPath, "include"));
Result.Add(Path.Combine(InternalSdkPath, "usr/include"));
Result.Add(ClangIncludeDirectory);
if (!Directory.Exists(ClangIncludeDirectory))
{
Logger.LogWarning("Clang include directory doesn't exist on disk. VersionString={VersionString}, ClangDir={ClangDir}",
PlatformSdkVersionString, ClangIncludeDirectory);
}
}
return Result;
}
///
/// Get clang toolchain version from full version string
/// v17_clang-10.0.1-centos7 -> 10.0.1
/// v17_clang-16.0.1-centos7 -> 16
///
/// Full clang toolchain version string. Example: "v17_clang-10.0.1-centos7"
/// Clang toolchain version. Example: 10.0.1 or 16
/// Starting with clang 16.x the directory naming changed to include major version only
private static string GetLinuxToolchainVersionFromFullString(string FullVersion)
{
string FullVersionPattern = @"^v[0-9]+_.*-(([0-9]+)\.[0-9]+\.[0-9]+)-.*$";
Regex Regex = new Regex(FullVersionPattern);
Match Match = Regex.Match(FullVersion);
if (!Match.Success)
{
throw new ArgumentException("Wrong full version string", FullVersion);
}
Group MajorVersionGroup = Match.Groups[2];
CaptureCollection MajorVersionCaptures = MajorVersionGroup.Captures;
if (MajorVersionCaptures.Count != 1)
{
throw new ArgumentException("Multiple regex captures in major version string", FullVersion);
}
if (Int32.TryParse(MajorVersionCaptures[0].Value, out int MajorVersion))
{
if (MajorVersion >= 16)
{
return MajorVersionCaptures[0].Value;
}
}
Group FullNumberVersionGroup = Match.Groups[1];
CaptureCollection FullNumberVersionCaptures = FullNumberVersionGroup.Captures;
return FullNumberVersionCaptures[0].Value;
}
}
}