Files
UnrealEngine/Engine/Source/Programs/UnrealBuildTool/System/InstalledPlatformInfo.cs
2025-05-18 13:04:45 +08:00

435 lines
14 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using EpicGames.Core;
using UnrealBuildBase;
namespace UnrealBuildTool
{
/// <summary>
/// The project type that support is required for
/// </summary>
public enum EProjectType
{
/// <summary>
///
/// </summary>
Unknown,
/// <summary>
///
/// </summary>
Any,
/// <summary>
/// Support for code projects
/// </summary>
Code,
/// <summary>
/// Support for deploying content projects
/// </summary>
Content,
};
/// <summary>
/// The state of a downloaded platform
/// </summary>
[Flags]
public enum InstalledPlatformState
{
/// <summary>
/// Query whether the platform is supported
/// </summary>
Supported,
/// <summary>
/// Query whether the platform has been downloaded
/// </summary>
Downloaded,
}
/// <summary>
/// Contains methods to allow querying the available installed platforms
/// </summary>
public class InstalledPlatformInfo
{
/// <summary>
/// Information about a single installed platform configuration
/// </summary>
public struct InstalledPlatformConfiguration
{
/// <summary>
/// Build Configuration of this combination
/// </summary>
public UnrealTargetConfiguration Configuration;
/// <summary>
/// Platform for this combination
/// </summary>
public UnrealTargetPlatform Platform;
/// <summary>
/// Type of Platform for this combination
/// </summary>
public TargetType PlatformType;
/// <summary>
/// Architecture for this combination
/// </summary>
public UnrealArch Architecture;
/// <summary>
/// Location of a file that must exist for this combination to be valid (optional)
/// </summary>
public string RequiredFile;
/// <summary>
/// Type of project this configuration can be used for
/// </summary>
public EProjectType ProjectType;
/// <summary>
/// Whether to display this platform as an option even if it is not valid
/// </summary>
public bool bCanBeDisplayed;
/// <summary>
/// Constructor
/// </summary>
/// <param name="InConfiguration"></param>
/// <param name="InPlatform"></param>
/// <param name="InPlatformType"></param>
/// <param name="InArchitecture"></param>
/// <param name="InRequiredFile"></param>
/// <param name="InProjectType"></param>
/// <param name="bInCanBeDisplayed"></param>
public InstalledPlatformConfiguration(UnrealTargetConfiguration InConfiguration, UnrealTargetPlatform InPlatform, TargetType InPlatformType, UnrealArch InArchitecture, string InRequiredFile, EProjectType InProjectType, bool bInCanBeDisplayed)
{
Configuration = InConfiguration;
Platform = InPlatform;
PlatformType = InPlatformType;
Architecture = InArchitecture;
RequiredFile = InRequiredFile;
ProjectType = InProjectType;
bCanBeDisplayed = bInCanBeDisplayed;
}
}
private static List<InstalledPlatformConfiguration>? InstalledPlatformConfigurations;
static InstalledPlatformInfo()
{
List<string>? InstalledPlatforms;
ConfigHierarchy Ini = ConfigCache.ReadHierarchy(ConfigHierarchyType.Engine, (DirectoryReference?)null, BuildHostPlatform.Current.Platform);
bool bHasInstalledPlatformInfo;
if (Ini.TryGetValue("InstalledPlatforms", "HasInstalledPlatformInfo", out bHasInstalledPlatformInfo) && bHasInstalledPlatformInfo)
{
InstalledPlatformConfigurations = new List<InstalledPlatformConfiguration>();
if (Ini.GetArray("InstalledPlatforms", "InstalledPlatformConfigurations", out InstalledPlatforms))
{
foreach (string InstalledPlatform in InstalledPlatforms)
{
ParsePlatformConfiguration(InstalledPlatform);
}
}
}
}
/// <summary>
/// Initializes the InstalledPlatformInfo. While this is not necessary to be called, it allows timing the static constructor call.
/// </summary>
internal static void Initialize()
{
// Unused, but allows timing call of the static constructor
}
private static void ParsePlatformConfiguration(string PlatformConfiguration)//, ILogger Logger)
{
// Trim whitespace at the beginning.
PlatformConfiguration = PlatformConfiguration.Trim();
// Remove brackets.
PlatformConfiguration = PlatformConfiguration.TrimStart('(');
PlatformConfiguration = PlatformConfiguration.TrimEnd(')');
bool bCanCreateEntry = true;
string ConfigurationName;
UnrealTargetConfiguration Configuration = UnrealTargetConfiguration.Unknown;
if (ParseSubValue(PlatformConfiguration, "Configuration=", out ConfigurationName))
{
Enum.TryParse(ConfigurationName, out Configuration);
}
if (Configuration == UnrealTargetConfiguration.Unknown)
{
// Logger.LogWarning("Unable to read configuration from {PlatformConfiguration}", PlatformConfiguration);
bCanCreateEntry = false;
}
string PlatformName;
if (ParseSubValue(PlatformConfiguration, "PlatformName=", out PlatformName))
{
if (!UnrealTargetPlatform.IsValidName(PlatformName))
{
// Logger.LogWarning("Unable to read platform from {PlatformConfiguration}", PlatformConfiguration);
bCanCreateEntry = false;
}
}
string PlatformTypeName;
TargetType PlatformType = TargetType.Game;
if (ParseSubValue(PlatformConfiguration, "PlatformType=", out PlatformTypeName))
{
if (!Enum.TryParse(PlatformTypeName, out PlatformType))
{
// Logger.LogWarning("Unable to read Platform Type from {PlatformConfiguration}, defaulting to Game", PlatformConfiguration);
PlatformType = TargetType.Game;
}
}
if (PlatformType == TargetType.Program)
{
// Logger.LogWarning("Program is not a valid PlatformType for an Installed Platform, defaulting to Game");
PlatformType = TargetType.Game;
}
string ArchitectureString;
ParseSubValue(PlatformConfiguration, "Architecture=", out ArchitectureString);
UnrealArch Architecture = UnrealArch.Parse(ArchitectureString);
string RequiredFile;
if (ParseSubValue(PlatformConfiguration, "RequiredFile=", out RequiredFile))
{
RequiredFile = FileReference.Combine(Unreal.RootDirectory, RequiredFile).ToString();
}
string ProjectTypeName;
EProjectType ProjectType = EProjectType.Any;
if (ParseSubValue(PlatformConfiguration, "ProjectType=", out ProjectTypeName))
{
Enum.TryParse(ProjectTypeName, out ProjectType);
}
if (ProjectType == EProjectType.Unknown)
{
// Logger.LogWarning("Unable to read project type from {PlatformConfiguration}", PlatformConfiguration);
bCanCreateEntry = false;
}
string CanBeDisplayedString;
bool bCanBeDisplayed = false;
if (ParseSubValue(PlatformConfiguration, "bCanBeDisplayed=", out CanBeDisplayedString))
{
bCanBeDisplayed = Convert.ToBoolean(CanBeDisplayedString);
}
if (bCanCreateEntry)
{
InstalledPlatformConfigurations!.Add(new InstalledPlatformConfiguration(Configuration, UnrealTargetPlatform.Parse(PlatformName), PlatformType, Architecture, RequiredFile, ProjectType, bCanBeDisplayed));
}
}
private static bool ParseSubValue(string TrimmedLine, string Match, out string Result)
{
Result = String.Empty;
int MatchIndex = TrimmedLine.IndexOf(Match);
if (MatchIndex < 0)
{
return false;
}
// Get the remainder of the string after the match
MatchIndex += Match.Length;
TrimmedLine = TrimmedLine.Substring(MatchIndex);
if (String.IsNullOrEmpty(TrimmedLine))
{
return false;
}
// get everything up to the first comma and trim any new whitespace
Result = TrimmedLine.Split(',')[0];
Result = Result.Trim();
if (Result.StartsWith("\""))
{
// Remove quotes
int QuoteEnd = Result.LastIndexOf('\"');
if (QuoteEnd > 0)
{
Result = Result.Substring(1, QuoteEnd - 1);
}
}
return true;
}
/// <summary>
/// Determine if the given configuration is available for any platform
/// </summary>
/// <param name="Configuration">Configuration type to check</param>
/// <param name="ProjectType">The type of project</param>
/// <returns>True if supported</returns>
public static bool IsValidConfiguration(UnrealTargetConfiguration Configuration, EProjectType ProjectType = EProjectType.Any)
{
return ContainsValidConfiguration(
(InstalledPlatformConfiguration CurConfig) =>
{
return CurConfig.Configuration == Configuration
&& (ProjectType == EProjectType.Any || CurConfig.ProjectType == EProjectType.Any
|| CurConfig.ProjectType == ProjectType);
}
);
}
/// <summary>
/// Determine if the given platform is available
/// </summary>
/// <param name="Platform">Platform to check</param>
/// <param name="ProjectType">The type of project</param>
/// <returns>True if supported</returns>
public static bool IsValidPlatform(UnrealTargetPlatform Platform, EProjectType ProjectType = EProjectType.Any)
{
// HACK: For installed builds, we always need to treat Mac as a valid platform for generating project files. When remote building from PC, we won't have all the libraries to do this, so we need to fake it.
if (Platform == UnrealTargetPlatform.Mac && ProjectType == EProjectType.Any && BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac && Unreal.IsEngineInstalled())
{
return true;
}
return ContainsValidConfiguration(
(InstalledPlatformConfiguration CurConfig) =>
{
return CurConfig.Platform == Platform
&& (ProjectType == EProjectType.Any || CurConfig.ProjectType == EProjectType.Any
|| CurConfig.ProjectType == ProjectType);
}
);
}
/// <summary>
/// Determine whether the given platform/configuration/project type combination is supported
/// </summary>
/// <param name="Configuration">Configuration for the project</param>
/// <param name="Platform">Platform for the project</param>
/// <param name="ProjectType">Type of the project</param>
/// <returns>True if the combination is supported</returns>
public static bool IsValidPlatformAndConfiguration(UnrealTargetConfiguration Configuration, UnrealTargetPlatform Platform, EProjectType ProjectType = EProjectType.Any)
{
return ContainsValidConfiguration(
(InstalledPlatformConfiguration CurConfig) =>
{
return CurConfig.Configuration == Configuration && CurConfig.Platform == Platform
&& (ProjectType == EProjectType.Any || CurConfig.ProjectType == EProjectType.Any
|| CurConfig.ProjectType == ProjectType);
}
);
}
/// <summary>
/// Determines whether the given target type is supported
/// </summary>
/// <param name="TargetType">The target type being built</param>
/// <param name="Platform">The platform being built</param>
/// <param name="Configuration">The configuration being built</param>
/// <param name="ProjectType">The project type required</param>
/// <param name="State">State of the given platform support</param>
/// <returns>True if the target can be built</returns>
public static bool IsValid(TargetType? TargetType, UnrealTargetPlatform? Platform, UnrealTargetConfiguration? Configuration, EProjectType ProjectType, InstalledPlatformState State)
{
if (!Unreal.IsEngineInstalled() || InstalledPlatformConfigurations == null)
{
return true;
}
foreach (InstalledPlatformConfiguration Config in InstalledPlatformConfigurations)
{
// Check whether this configuration matches all the criteria
if (TargetType.HasValue && Config.PlatformType != TargetType.Value)
{
continue;
}
if (Platform.HasValue && Config.Platform != Platform.Value)
{
continue;
}
if (Configuration.HasValue && Config.Configuration != Configuration.Value)
{
continue;
}
if (ProjectType != EProjectType.Any && Config.ProjectType != EProjectType.Any && Config.ProjectType != ProjectType)
{
continue;
}
if (State == InstalledPlatformState.Downloaded && !String.IsNullOrEmpty(Config.RequiredFile) && !File.Exists(Config.RequiredFile))
{
continue;
}
// Success!
return true;
}
return false;
}
private static bool ContainsValidConfiguration(Predicate<InstalledPlatformConfiguration> ConfigFilter)
{
if (Unreal.IsEngineInstalled() && InstalledPlatformConfigurations != null)
{
foreach (InstalledPlatformConfiguration PlatformConfiguration in InstalledPlatformConfigurations)
{
// Check whether filter accepts this configuration and it has required file
if (ConfigFilter(PlatformConfiguration)
&& (String.IsNullOrEmpty(PlatformConfiguration.RequiredFile)
|| File.Exists(PlatformConfiguration.RequiredFile)))
{
return true;
}
}
return false;
}
return true;
}
/// <summary>
///
/// </summary>
/// <param name="Configs"></param>
/// <param name="OutEntries"></param>
public static void WriteConfigFileEntries(List<InstalledPlatformConfiguration> Configs, ref List<string> OutEntries)
{
// Write config section header
OutEntries.Add("[InstalledPlatforms]");
OutEntries.Add("HasInstalledPlatformInfo=true");
foreach (InstalledPlatformConfiguration Config in Configs)
{
WriteConfigFileEntry(Config, ref OutEntries);
}
}
private static void WriteConfigFileEntry(InstalledPlatformConfiguration Config, ref List<string> OutEntries)
{
string ConfigDescription = "+InstalledPlatformConfigurations=(";
ConfigDescription += String.Format("PlatformName=\"{0}\", ", Config.Platform.ToString());
if (Config.Configuration != UnrealTargetConfiguration.Unknown)
{
ConfigDescription += String.Format("Configuration=\"{0}\", ", Config.Configuration.ToString());
}
if (Config.PlatformType != TargetType.Program)
{
ConfigDescription += String.Format("PlatformType=\"{0}\", ", Config.PlatformType.ToString());
}
ConfigDescription += String.Format("Architecture=\"{0}\", ", Config.Architecture);
if (!String.IsNullOrEmpty(Config.RequiredFile))
{
ConfigDescription += String.Format("RequiredFile=\"{0}\", ", Config.RequiredFile);
}
if (Config.ProjectType != EProjectType.Unknown)
{
ConfigDescription += String.Format("ProjectType=\"{0}\", ", Config.ProjectType.ToString());
}
ConfigDescription += String.Format("bCanBeDisplayed={0})", Config.bCanBeDisplayed.ToString());
OutEntries.Add(ConfigDescription);
}
}
}