Files
UnrealEngine/Engine/Source/Programs/AutomationTool/Turnkey/TurnkeyUtils.cs
2025-05-18 13:04:45 +08:00

874 lines
28 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Globalization;
using EpicGames.Core;
using AutomationTool;
using UnrealBuildTool;
using UnrealBuildBase;
using System.Diagnostics;
namespace Turnkey
{
static class TurnkeyUtils
{
// object that commands, etc can use to access UAT functionality
static public BuildCommand CommandUtilHelper;
// replacement for Environment.ExitCode
static public ExitCode ExitCode = ExitCode.Success;
static public void Initialize(IOProvider InIOProvider, BuildCommand InCommandUtilHelper)
{
IOProvider = InIOProvider;
CommandUtilHelper = InCommandUtilHelper;
// set up some lists
SetVariable("AllPlatforms", string.Join(",", UnrealTargetPlatform.GetValidPlatformNames()));
// walk over all the SDKs and get their AutoSDK string
IEnumerable<string> AutoSDKPlatforms = UEBuildPlatformSDK.AllPlatformSDKObjects.Select(x => x.GetAutoSDKPlatformName()).Distinct();
SetVariable("AutoSDKPlatforms", string.Join(",", AutoSDKPlatforms));
// TurnkeyUtils.Log("AllPlatforms = {0}", GetVariableValue("AllPlatforms"));
// TurnkeyUtils.Log("AutoSDKPlatforms = {0}", GetVariableValue("AutoSDKPlatforms"));
SetVariable("HOST_PLATFORM_NAME", HostPlatform.Current.HostEditorPlatform.ToString());
}
#region Turnkey Variables
static Dictionary<string, string> TurnkeyVariables = new Dictionary<string, string>();
public static string SetVariable(string Key, string Value)
{
string Previous;
TurnkeyVariables.TryGetValue(Key, out Previous);
TurnkeyVariables[Key] = Value;
return Previous;
}
public static string GetVariableValue(string Key)
{
string Value;
if (TurnkeyVariables.TryGetValue(Key, out Value) || TurnkeySettings.GetSetUserSettings().TryGetValue(Key, out Value))
{
return Value;
}
return null;
}
public static void ClearVariable(string Key)
{
TurnkeyVariables.Remove(Key);
}
public static bool HasVariable(string Key)
{
return TurnkeyVariables.ContainsKey(Key);
}
public static string ExpandVariables(string Str, bool bUseOnlyTurnkeyVariables = false)
{
// don't crash on null
if (Str == null)
{
return null;
}
string ExpandedUserVariables = UnrealBuildTool.Utils.ExpandVariables(Str, TurnkeySettings.GetSetUserSettings(), true);
return UnrealBuildTool.Utils.ExpandVariables(ExpandedUserVariables, TurnkeyVariables, bUseOnlyTurnkeyVariables);
}
#endregion
#region Commandline Handling
public static bool ParseParam(string Param, string[] ExtraOptions)
{
// our internal extraoptions still have - in front, but CommandUtilHelper won't have the dashes
return CommandUtils.ParseParam(ExtraOptions, "-" + Param) || CommandUtilHelper.ParseParam(Param);
}
public static string ParseParamValue(string Param, string Default, string[] ExtraOptions)
{
// our internal extraoptions still have - in front, but CommandUtilHelper won't have the dashes
string Value = CommandUtils.ParseParamValue(ExtraOptions, "-" + Param, null);
if (Value == null)
{
Value = CommandUtilHelper.ParseParamValue(Param, Default);
}
return Value == null ? Default : Value;
}
private static List<UnrealTargetPlatform> GetAllValidPlatforms(List<UnrealTargetPlatform> SourcePlatforms=null)
{
if (SourcePlatforms == null)
{
SourcePlatforms = UnrealTargetPlatform.GetValidPlatforms().ToList();
}
return SourcePlatforms.Where(x => UEBuildPlatformSDK.GetSDKForPlatform(x.ToString()) != null).ToList();
}
public static List<UnrealTargetPlatform> GetPlatformsFromCommandLineOrUser(string[] CommandOptions, List<UnrealTargetPlatform> PossiblePlatforms)
{
string PlatformString = TurnkeyUtils.ParseParamValue("Platform", null, CommandOptions);
bool bUnattended = TurnkeyUtils.ParseParam("Unattended", CommandOptions);
// Remove known bad platforms
PossiblePlatforms = GetAllValidPlatforms(PossiblePlatforms);
// sort by name
PossiblePlatforms.Sort((x, y) => string.Compare(x.ToString(), y.ToString()));
List<UnrealTargetPlatform> Platforms = new List<UnrealTargetPlatform>();
// prompt user for a platform
if (PlatformString == null)
{
if (bUnattended)
{
// can't ask
return null;
}
// default to last platform chosen
string LastPlatform = TurnkeySettings.GetUserSettingIfSet("User_LastSelectedPlatform", "");
List<string> PlatformOptions = PossiblePlatforms.ConvertAll(x => x.ToString());
PlatformOptions.Add("All of the Above");
int Default = PlatformOptions.FindIndex(x => x.Equals(LastPlatform, StringComparison.OrdinalIgnoreCase));
int PlatformChoice = TurnkeyUtils.ReadInputInt("Choose a platform:", PlatformOptions, true, Default == -1 ? -1 : Default + 1);
if (PlatformChoice == 0)
{
return null;
}
// All platforms means to install every platform with an installer
if (PlatformChoice == PlatformOptions.Count)
{
Platforms = PossiblePlatforms;
}
else
{
Platforms.Add(PossiblePlatforms[PlatformChoice - 1]);
// remember for next time
TurnkeySettings.SetUserSetting("User_LastSelectedPlatform", PossiblePlatforms[PlatformChoice - 1].ToString());
}
}
else if (PlatformString.ToLower() == "all")
{
Platforms = PossiblePlatforms;
}
else
{
string[] Tokens = PlatformString.Split("+".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
foreach (string Token in Tokens)
{
UnrealTargetPlatform Platform;
if (!UnrealTargetPlatform.TryParse(Token, out Platform))
{
TurnkeyUtils.Log("Platform {0} is unknown", Token);
continue;
}
// if the platform isn't in the possible list, then don't add it
if (PossiblePlatforms.Contains(Platform))
{
Platforms.Add(Platform);
}
}
}
return Platforms.OrderBy(x => x.ToString()).ToList();
}
private enum ProjectMode
{
Normal,
Templates,
Sandbox,
Misc,
Samples,
Collaboration,
Personal,
Recent,
}
public static FileReference GetProjectFromCommandLineOrUser(string[] CommandOptions)
{
ProjectMode CurrentMode = ProjectMode.Normal;
string Project = TurnkeyUtils.GetVariableValue("Project");
string LastProject = TurnkeySettings.GetUserSettingIfSet("User_LastSelectedProject", "");
// create and initialize a dictionary of type to project list
Dictionary<ProjectMode, List<FileReference>> ProjectsByType = new Dictionary<ProjectMode, List<FileReference>>();
foreach (ProjectMode Mode in Enum.GetValues(typeof(ProjectMode)))
{
ProjectsByType.Add(Mode, new List<FileReference>());
}
// look up old manual projects
string ManualProjectString = TurnkeySettings.GetUserSettingIfSet("User_ManualProjects", "");
List<string> ManualProjects = ManualProjectString.Split(";", StringSplitOptions.RemoveEmptyEntries).ToList();
ProjectsByType[ProjectMode.Recent].AddRange(ManualProjects.Select(x => new FileReference(x)).ToList());
// start on Recent page if last one was a manual project
if (ProjectsByType[ProjectMode.Recent].Any(x => x.FullName.Equals(LastProject, StringComparison.OrdinalIgnoreCase)))
{
CurrentMode = ProjectMode.Recent;
}
// sort the discovered projects
foreach (FileReference NativeProject in NativeProjects.EnumerateProjectFiles(EpicGames.Core.Log.Logger))
{
DirectoryReference ProjectDir = NativeProject.Directory;
ProjectMode Mode = ProjectMode.Normal;
if (ProjectDir.ContainsName("StarterContent", 0) || ProjectDir.ContainsName("ContentExamples", 0))
{
Mode = ProjectMode.Misc;
}
else if (ProjectDir.ContainsName("Sandbox", 0))
{
Mode = ProjectMode.Sandbox;
}
else if (ProjectDir.ContainsName("Templates", 0))
{
Mode = ProjectMode.Templates;
}
else if (ProjectDir.ContainsName("Samples", 0))
{
Mode = ProjectMode.Samples;
}
else if (ProjectDir.ContainsName("Collaboration", 0))
{
Mode = ProjectMode.Collaboration;
}
// if the last project was this project, then use this as the starting type
if (LastProject != null && NativeProject.GetFileNameWithoutAnyExtensions().Equals(LastProject, StringComparison.OrdinalIgnoreCase))
{
CurrentMode = Mode;
}
ProjectsByType[Mode].Add(NativeProject);
}
// now look for the My Documents location
DirectoryReference PersonalProjects = new DirectoryReference(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Unreal Projects"));
if (DirectoryReference.Exists(PersonalProjects))
{
foreach (DirectoryReference PersonalProjectDir in DirectoryReference.EnumerateDirectories(PersonalProjects))
{
FileReference PersonalProjectFile = DirectoryReference.EnumerateFiles(PersonalProjectDir, "*.uproject").FirstOrDefault();
if (PersonalProjectFile != null)
{
ProjectsByType[ProjectMode.Personal].Add(PersonalProjectFile);
}
}
}
while (string.IsNullOrEmpty(Project))
{
// get list of project names from the dictionary
List<string> ProjectNames = new List<string>();
// add options to switch mode
Dictionary<int, ProjectMode> IndexToMode = new Dictionary<int, ProjectMode>();
foreach (ProjectMode Mode in Enum.GetValues(typeof(ProjectMode)))
{
if (Mode != CurrentMode)
{
IndexToMode.Add(ProjectNames.Count, Mode);
ProjectNames.Add(string.Format("[Show {0} Projects]", Mode.ToString()));
}
}
// now show the projects in this mode
ProjectNames.AddRange(ProjectsByType[CurrentMode].Select(x => (CurrentMode == ProjectMode.Recent) ? x.FullName : x.GetFileNameWithoutAnyExtensions()));
// and finally a manual entry option
if (OperatingSystem.IsWindows())
{
ProjectNames.Add("[Browse...]");
}
else
{
ProjectNames.Add("[Enter Path To .uproject...]");
}
int Default = LastProject == null ? -1 : ProjectNames.FindIndex(x => x.Equals(LastProject, StringComparison.OrdinalIgnoreCase));
int Choice = TurnkeyUtils.ReadInputInt(string.Format("Choose a {0} project to execute, or select a set of projects to list. (You can skip this by specifying -project=XXX on the commandline)", CurrentMode), ProjectNames, true, Default == -1 ? -1 : Default + 1);
if (Choice == 0)
{
return null;
}
// skip cancel
Choice = Choice - 1;
// look for manual entry option
if (Choice == ProjectNames.Count - 1)
{
if (OperatingSystem.IsWindows())
{
string ChosenFile = null;
System.Threading.Thread t = new System.Threading.Thread(x =>
{
Debug.Assert(OperatingSystem.IsWindowsVersionAtLeast(7));
ChosenFile = UnrealWindowsForms.Utils.ShowOpenFileDialogAndReturnFilename("Project Files (*.uproject)|*.uproject");
});
t.SetApartmentState(System.Threading.ApartmentState.STA);
t.Start();
t.Join();
if (ChosenFile == null)
{
continue;
}
}
else
{
while (true)
{
string ProjectChoice = TurnkeyUtils.ReadInput("Enter path to .uproject file:");
if (!File.Exists(ProjectChoice))
{
bool bResponse = TurnkeyUtils.GetUserConfirmation(string.Format("'{0}' doesn't exist. Would you like to enter another path?", ProjectChoice), false);
if (bResponse == false)
{
break;
}
}
else
{
Project = ProjectChoice;
break;
}
}
}
// if we got a valid vhoice, remember it
if (!string.IsNullOrEmpty(Project))
{
// remember this for next run
// case insensitive Contains
if (!ManualProjects.Any(x => x.Equals(Project, StringComparison.OrdinalIgnoreCase)))
{
ManualProjects.Add(Project);
}
TurnkeySettings.SetUserSetting("User_ManualProjects", string.Join(";", ManualProjects));
}
}
else
{
// if the choice was a mode switch, get the mode
if (IndexToMode.TryGetValue(Choice, out CurrentMode))
{
continue;
}
Project = ProjectNames[Choice];
}
}
// remember project for next run
TurnkeySettings.SetUserSetting("User_LastSelectedProject", Project);
return ProjectUtils.FindProjectFileFromName(Project);
}
private static DeviceInfo GetDeviceByPlatformAndName(UnrealTargetPlatform Platform, string DeviceName)
{
try
{
return AutomationTool.Platform.GetPlatform(Platform).GetDeviceByName(DeviceName);
}
catch (Exception Ex)
{
TurnkeyUtils.Log($"An error occurred trying to access device {DeviceName} for platform {Platform}: {Ex.Message}");
// swallow errors and just return no device
return null;
}
}
private static DeviceInfo[] GetDevicesForPlatform(UnrealTargetPlatform Platform)
{
try
{
DeviceInfo[] Devices = AutomationTool.Platform.GetPlatform(Platform).GetDevices();
// in the null case, return an empty array, for cleaner code for callers
return Devices != null ? Devices : new DeviceInfo[] { };
}
catch (Exception Ex)
{
TurnkeyUtils.Log($"An error occurred trying to access all devices for platform {Platform}: {Ex.Message}");
// swallow errors and just return no devices
return new DeviceInfo[] { } ;
}
}
public static List<DeviceInfo> GetDevicesFromCommandLineOrUser(string[] CommandOptions, UnrealTargetPlatform Platform)
{
return GetDevicesFromCommandLineOrUser(CommandOptions, new List<UnrealTargetPlatform>() { Platform });
}
//public static DeviceInfo GetDeviceFromCommandLineOrUser(string[] CommandOptions, UnrealTargetPlatform Platform)
//{
// Dictionary<UnrealTargetPlatform, List<DeviceInfo>> PlatformsAndDevices = GetDevicesFromCommandLineOrUser(CommandOptions, Platform);
//}
public static List<DeviceInfo> GetDevicesFromCommandLineOrUser(string[] CommandOptions, List<UnrealTargetPlatform> PossiblePlatforms)
{
List<DeviceInfo> ChosenDevices = null;
// look at any devices on the commandline, and see if they have platforms or not
string DeviceList = TurnkeyUtils.ParseParamValue("Device", null, CommandOptions);
List<string> SplitDeviceList = null;
if (DeviceList != null && DeviceList.ToLower() != "all")
{
SplitDeviceList = DeviceList.Split("+".ToCharArray()).ToList();
// look if they have platform@ tags
bool bAnyHavePlatform = SplitDeviceList.Any(x => x.Contains("@"));
if (bAnyHavePlatform)
{
if (!SplitDeviceList.All(x => x.Contains("@")))
{
throw new AutomationException("If any device in -device has a platform indicator ('Platform@Device'), they must all have a platform indicator");
}
// now split it up for devices for each platform
foreach (string DeviceToken in SplitDeviceList)
{
string[] Tokens = DeviceToken.Split("@".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
if (Tokens.Length != 2)
{
throw new AutomationException("{0} did not have the Platform@Device format", DeviceToken);
}
UnrealTargetPlatform Platform;
if (!UnrealTargetPlatform.TryParse(Tokens[0], out Platform))
{
TurnkeyUtils.Log("Platform indicator {0} is an invalid platform, skipping", Tokens[0]);
continue;
}
string DeviceName = Tokens[1];
// track it
if (ChosenDevices == null)
{
ChosenDevices = new List<DeviceInfo>();
}
if (DeviceName.ToLower() == "all")
{
ChosenDevices.AddRange(GetDevicesForPlatform(Platform));
}
else
{
DeviceInfo Device = GetDeviceByPlatformAndName(Platform, DeviceName);
if (Device != null)
{
ChosenDevices.Add(Device);
}
}
}
SplitDeviceList = null;
}
}
// if we didn't get some platforms already from -device list, then get or ask the user for platforms
if (ChosenDevices == null)
{
ChosenDevices = new List<DeviceInfo>();
List<UnrealTargetPlatform> ChosenPlatforms;
// use all platforms (with -device=all), or ask user if needed (GetPlatformsFromCommandLineOrUser would look at -platform=all, not -device=all)
// if -platform was specified, use the function to get just the specified ones
if (DeviceList != null && DeviceList.ToLower() == "all" && TurnkeyUtils.ParseParamValue("Platform", null, CommandOptions) == null)
{
ChosenPlatforms = GetAllValidPlatforms(PossiblePlatforms);
}
// if there's only one platform possible, use it
else if (PossiblePlatforms != null && PossiblePlatforms.Count == 1)
{
ChosenPlatforms = PossiblePlatforms;
}
else
{
ChosenPlatforms = TurnkeyUtils.GetPlatformsFromCommandLineOrUser(CommandOptions, PossiblePlatforms);
}
if (ChosenPlatforms == null)
{
return null;
}
if (ChosenPlatforms.Count > 1 && SplitDeviceList != null && !(SplitDeviceList.Count == 1 && SplitDeviceList[0].ToLower() == "all"))
{
throw new AutomationException("When using -Device without platform specifiers ('Platform@Device'), a single platform must be specified (unless -Device=All is used)");
}
// get all the devices for the platforms
if (!string.IsNullOrEmpty(DeviceList) && DeviceList.ToLower() == "all")
{
foreach (UnrealTargetPlatform Platform in ChosenPlatforms)
{
ChosenDevices.AddRange(GetDevicesForPlatform(Platform));
}
}
// now if the list of devices was given, then attempt to find them in the platform
else if (SplitDeviceList != null && SplitDeviceList.Count > 0)
{
foreach (UnrealTargetPlatform Platform in ChosenPlatforms)
{
foreach (string DeviceName in SplitDeviceList)
{
DeviceInfo Device = GetDeviceByPlatformAndName(Platform, DeviceName);
if (Device != null)
{
ChosenDevices.Add(Device);
}
}
}
}
// otherwise ask user for device
else
{
List<string> Options = new List<string>();
List<DeviceInfo> PossibleDevices = new List<DeviceInfo>();
foreach (UnrealTargetPlatform Platform in ChosenPlatforms)
{
foreach (DeviceInfo Device in GetDevicesForPlatform(Platform))
{
PossibleDevices.Add(Device);
Options.Add(string.Format("[{0} {1}] {2}", Platform, Device.Type, Device.Name));
}
}
if (PossibleDevices.Count == 0)
{
Log("Unable to find any devices for platform(s): {0}", string.Join(", ", ChosenPlatforms));
return null;
}
// get the choice
int Choice = TurnkeyUtils.ReadInputInt("Select a device:", Options, true);
if (Choice == 0)
{
return null;
}
// finally, add it to the proper list
ChosenDevices.Add(PossibleDevices[Choice - 1]);
}
}
// if we ended up with some platforms, but no devices, just return null
return (ChosenDevices != null && ChosenDevices.Count > 0) ? ChosenDevices : null;
}
public static void GetPlatformsAndDevicesFromCommandLineOrUser(string[] CommandOptions, bool bSkipAskUserForDevice, out List<UnrealTargetPlatform> Platforms, out List<DeviceInfo> Devices, List<UnrealTargetPlatform> AllowedPlatforms=null)
{
Platforms = new List<UnrealTargetPlatform>();
Devices = new List<DeviceInfo>();
string DeviceString = TurnkeyUtils.ParseParamValue("Device", null, CommandOptions);
if (string.IsNullOrEmpty(DeviceString))
{
Platforms.AddRange(TurnkeyUtils.GetPlatformsFromCommandLineOrUser(CommandOptions, AllowedPlatforms));
// restrict devices to this platform
AllowedPlatforms = Platforms;
}
// if there's no -device param, and we don't want to ask user for a device, skip this
if (!string.IsNullOrEmpty(DeviceString) || !bSkipAskUserForDevice)
{
List<DeviceInfo> ChosenDevices = TurnkeyUtils.GetDevicesFromCommandLineOrUser(CommandOptions, AllowedPlatforms);
if (ChosenDevices != null)
{
Devices = ChosenDevices;
// pull the platforms out of the devices we have chosen
Platforms = Devices.Select(x => x.Platform).ToHashSet().ToList();
}
}
}
public static string GetGenericOption(string[] CommandOptions, List<string> Options, string CommandLineOption)
{
return GetGenericOption(CommandOptions, Options, CommandLineOption, out _);
}
public static string GetGenericOption(string[] CommandOptions, List<string> Options, string CommandLineOption, out bool bWasOnCommandLine)
{
string ChosenValue = TurnkeyUtils.ParseParamValue(CommandLineOption, null, CommandOptions);
if (ChosenValue != null)
{
bWasOnCommandLine = true;
}
else
{
bWasOnCommandLine = false;
// default to previous selection if any
string CachedOptionName = "User_LastSelectedGeneric_" + CommandLineOption;
string LastSelectedOption = TurnkeySettings.GetUserSettingIfSet(CachedOptionName, "");
int Default = Options.FindIndex(x => x.Equals(LastSelectedOption, StringComparison.OrdinalIgnoreCase));
int Choice = ReadInputInt($"Choose the {CommandLineOption}:", Options, true, Default == -1 ? -1 : Default + 1);
if (Choice == 0)
{
return null;
}
ChosenValue = Options[Choice - 1];
// remember for next time
TurnkeySettings.SetUserSetting(CachedOptionName, ChosenValue);
}
return ChosenValue;
}
#endregion
#region Env vars
private static IDictionary[] SavedEnvVars = new System.Collections.IDictionary[2];
private static Dictionary<string, string> EnvVarsToSaveToBatchFile = new Dictionary<string, string>();
public static void StartTrackingExternalEnvVarChanges()
{
SavedEnvVars[0] = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User);
SavedEnvVars[1] = Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine);
}
public static void EndTrackingExternalEnvVarChanges()
{
System.Collections.IDictionary[] NewEnvVars =
{
Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine),
};
TurnkeyUtils.Log("Scanning for envvar changes...");
// look for differences
for (int Index = 0; Index < NewEnvVars.Length; Index++)
{
IDictionary NewSet = NewEnvVars[Index];
IDictionary PreviousSet = SavedEnvVars[Index];
foreach (DictionaryEntry Pair in NewSet)
{
Object PrevValue = PreviousSet[Pair.Key];
string NewKey = Pair.Key as string;
string NewValue= Pair.Value as string;
// if we have a new or changed value, apply it to the process
if (PrevValue == null || string.Compare(PrevValue as string, NewValue) != 0)
{
TurnkeyUtils.Log(" Updating process env var {0} to {1}", Pair.Key, Pair.Value);
Environment.SetEnvironmentVariable(NewKey, NewValue, EnvironmentVariableTarget.Process);
// remember to save to batch file
EnvVarsToSaveToBatchFile[NewKey] = NewValue;
}
}
}
TurnkeyUtils.Log("... done! ");
if (EnvVarsToSaveToBatchFile.Count > 0)
{
StringBuilder BatchContents = new StringBuilder();
foreach (var Pair in EnvVarsToSaveToBatchFile)
{
BatchContents.AppendLine("set {0}={1}", Pair.Key, Pair.Value);
TurnkeyUtils.Log(" Recording variable to set by caller {0}={1}", Pair.Key, Pair.Value);
}
// write out a batch file for the caller of this to call to update vars, including all changes from previous commands
string BatchPath = ExpandVariables("$(EngineDir)/Intermediate/Turnkey/PostTurnkeyVariables.bat");
TurnkeyUtils.Log(" Writing updated envvars to {0}", BatchPath);
Directory.CreateDirectory(Path.GetDirectoryName(BatchPath));
File.WriteAllText(BatchPath, BatchContents.ToString());
}
}
#endregion
#region Regex Matching
static bool TryConvertToUint64(string InValue, out UInt64 OutValue)
{
if (InValue.StartsWith("0x"))
{
// must skip ovr the 0x
return UInt64.TryParse(InValue.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out OutValue);
}
return UInt64.TryParse(InValue, out OutValue);
}
public static bool IsValueValid(string Value, string AllowedValues, AutomationTool.Platform Platform)
{
if (string.IsNullOrEmpty(Value))
{
return false;
}
if (string.IsNullOrEmpty(AllowedValues))
{
return true;
}
// use a regex if the allowed string starts with "regex:"
if (AllowedValues.StartsWith("regex:", StringComparison.InvariantCultureIgnoreCase))
{
return Regex.IsMatch(Value, AllowedValues.Substring(6));
}
// range type needs to have values that can convert to an integer (in base 10 or 16 with 0x)
if (AllowedValues.StartsWith("range:", StringComparison.InvariantCultureIgnoreCase))
{
// match for "[Min]-[Max]" ([] meaning optional), and Min or Max can be a hex or decimal number,
// and it must match the entire string, so, 0-10.0 would not match
Match StreamMatch = new Regex(@"^(0x[0-9a-fA-F]*|[0-9]*)-(0x[0-9a-fA-F]*|[0-9]*)$").Match(AllowedValues.Substring(6));
if (!StreamMatch.Success)
{
TurnkeyUtils.Log("Warning: range: type [{0}] was in a bad format. Must be a range of one or two positive integers in decimal or hex (ex: '0x1000-0x2000', or '435-')");
return false;
}
// convert inputs to uint (unless already converted above by platform)
UInt64 ValueInt = 0;
if (!TryConvertToUint64(Value, out ValueInt) && !UEBuildPlatformSDK.GetSDKForPlatform(Platform.PlatformType.ToString()).TryConvertVersionToInt(Value, out ValueInt))
{
TurnkeyUtils.Log("Warning: range: input value [{0}] was not an unsigned integer, and platform couldn't convert it", Value);
return false;
}
// min and max are optional, so use 0 and MaxValue if they aren't
string MinString = StreamMatch.Groups[1].Value;
string MaxString = StreamMatch.Groups[2].Value;
UInt64 Min = 0, Max = UInt64.MaxValue;
if (!string.IsNullOrEmpty(MinString))
{
// Regex verified they are in a good format, so we can use Parse
TryConvertToUint64(MinString, out Min);
}
if (!string.IsNullOrEmpty(MaxString))
{
TryConvertToUint64(MaxString, out Max);
}
// finally perform the comparison
return ValueInt >= Min && ValueInt <= Max;
}
// otherwise, perform a string comparison
return string.Compare(Value, AllowedValues, true) == 0;
}
#endregion
#region IO
static private IOProvider IOProvider;
public static void Log(string Message)
{
IOProvider.Log(Message, bAppendNewLine: true);
}
public static void Log(ref StringBuilder Message)
{
IOProvider.Log(Message.ToString(), bAppendNewLine: false);
Message.Clear();
}
public static void Log(string Message, params object[] Params)
{
IOProvider.Log(string.Format(Message, Params), bAppendNewLine: true);
}
public static void Report(string Message)
{
IOProvider.Report(Message, bAppendNewLine: true);
}
public static void Report(ref StringBuilder Message)
{
IOProvider.Report(Message.ToString(), bAppendNewLine: false);
Message.Clear();
}
public static void Report(string Message, params object[] Params)
{
IOProvider.Report(string.Format(Message, Params), bAppendNewLine: true);
}
public static void PauseForUser(string Message, params object[] Params)
{
IOProvider.PauseForUser(string.Format(Message, Params), bAppendNewLine: true);
}
public static string ReadInput(string Prompt, string Default = "")
{
return IOProvider.ReadInput(Prompt, Default, bAppendNewLine: true);
}
public static int ReadInputInt(ref StringBuilder Prompt, List<string> Options, bool bIsCancellable, int DefaultValue = -1)
{
string PromptString = Prompt.ToString();
Prompt.Clear();
return IOProvider.ReadInputInt(PromptString, Options, bIsCancellable, DefaultValue, bAppendNewLine: false);
}
public static int ReadInputInt(string Prompt, List<string> Options, bool bIsCancellable, int DefaultValue = -1)
{
return IOProvider.ReadInputInt(Prompt, Options, bIsCancellable, DefaultValue, bAppendNewLine: true);
}
public static bool GetUserConfirmation(string Message, bool bDefaultValue)
{
return IOProvider.GetUserConfirmation(Message, bDefaultValue, true );
}
#endregion
#region Temp files [move to LocalCache]
static List<string> PathsToCleanup = new List<string>();
public static void AddPathToCleanup(string Path)
{
PathsToCleanup.Add(Path);
}
public static void CleanupPaths()
{
TurnkeyUtils.Log("Cleaning Temp Paths...");
// cleanup any delay-cleanup files and directories
foreach (string Path in PathsToCleanup)
{
if (Directory.Exists(Path))
{
InternalUtils.SafeDeleteDirectory(Path);
}
else if (File.Exists(Path))
{
InternalUtils.SafeDeleteFile(Path);
}
}
PathsToCleanup.Clear();
}
#endregion
}
}