// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EpicGames.Core;
using UnrealBuildBase;
using Microsoft.Extensions.Logging;
using System.Diagnostics.CodeAnalysis;
#nullable enable
namespace AutomationTool
{
///
/// Base class for buildcommands.
///
public abstract class BuildCommand : CommandUtils
{
///
/// Command line parameters for this command (empty by non-null by default)
///
public string[] Params
{
get { return _commandLineParams; }
set { _commandLineParams = value; }
}
private string[] _commandLineParams = new string[0];
///
/// Parses the command's Params list for a parameter and returns whether it is defined or not.
///
/// Param to check for.
/// True if param was found, false otherwise.
public bool ParseParam(string Param)
{
return ParseParam(Params, Param);
}
///
/// Parses the command's Params list for a parameter and reads its value.
/// Ex. ParseParamValue(Args, "map=")
///
/// Param to read its value.
///
///
/// Returns the value or Default if the parameter was not found.
[return: NotNullIfNotNull("Default")]
public string? ParseParamValue(string Param, string? Default = null, string? ObsoleteParam = null)
{
string ParamValue = ParseParamValue(Params, Param, null);
if (ObsoleteParam != null)
{
string ObsoleteParamValue = ParseParamValue(Params, ObsoleteParam, null);
if (ObsoleteParamValue != null)
{
if (ParamValue == null)
{
Logger.LogWarning("Param name \"{ObsoleteParam}\" is deprecated, use \"{Param}\" instead.", ObsoleteParam, Param);
}
else
{
Logger.LogWarning("Deprecated param name \"{ObsoleteParam}\" was ignored because \"{Param}\" was set.", ObsoleteParam, Param);
}
}
}
return ParamValue ?? Default;
}
///
/// Parses an argument.
///
///
///
public string? ParseOptionalStringParam(string Param)
{
return ParseParamValue(Param, null);
}
///
/// Parses an argument. Throws an exception if the parameter is not specified.
///
/// Name of the argument
/// Value of the argument
public string ParseRequiredStringParam(string Param)
{
string? value = ParseOptionalStringParam(Param);
if(value == null)
{
throw new AutomationException("Missing -{0}=... parameter", Param);
}
return value;
}
///
/// Parses an file reference argument.
///
/// Name of the argument
/// Value of the argument
public FileReference? ParseOptionalFileReferenceParam(string Param)
{
string? stringValue = ParseParamValue(Param);
if(stringValue == null)
{
return null;
}
else
{
return new FileReference(stringValue);
}
}
///
/// Parses an file reference argument. Throws an exception if the parameter is not specified.
///
/// Name of the argument
/// Value of the argument
public FileReference ParseRequiredFileReferenceParam(string Param)
{
FileReference? value = ParseOptionalFileReferenceParam(Param);
if(value == null)
{
throw new AutomationException("Missing -{0}=... parameter", Param);
}
return value;
}
///
/// Parses a directory reference argument.
///
/// Name of the argument
/// Value of the argument
public DirectoryReference? ParseOptionalDirectoryReferenceParam(string Param)
{
string? stringValue = ParseOptionalStringParam(Param);
if(stringValue == null)
{
return null;
}
else
{
return new DirectoryReference(stringValue);
}
}
///
/// Parses a directory reference argument. Throws an exception if the parameter is not specified.
///
/// Name of the argument
/// Value of the argument
public DirectoryReference ParseRequiredDirectoryReferenceParam(string Param)
{
DirectoryReference? value = ParseOptionalDirectoryReferenceParam(Param);
if(value == null)
{
throw new AutomationException("Missing -{0}=... parameter", Param);
}
return value;
}
///
/// Parses an argument as an enum.
///
/// Name of the parameter to read.
/// Returns the value that was parsed.
public Nullable ParseOptionalEnumParam(string Param) where T : struct
{
string? valueString = ParseParamValue(Param);
if(valueString == null)
{
return null;
}
else
{
T Value;
if(!Enum.TryParse(valueString, out Value))
{
throw new AutomationException("'{0}' is not a valid value for {1}", valueString, typeof(T).Name);
}
return Value;
}
}
///
/// Parses an argument as an enum. Throws an exception if the parameter is not specified.
///
/// Name of the parameter to read.
/// Returns the value that was parsed.
public T ParseRequiredEnumParamEnum(string Param) where T : struct
{
Nullable value = ParseOptionalEnumParam(Param);
if(!value.HasValue)
{
throw new AutomationException("Missing -{0}=... parameter", Param);
}
return value.Value;
}
///
/// Parses the argument list for any number of parameters.
///
/// Param to read its value.
/// Returns an array of values for this parameter (or an empty array if one was not found.
public string[] ParseParamValues(string Param)
{
return ParseParamValues(Params, Param);
}
///
/// Parses the command's Params list for a parameter and reads its value.
/// Ex. ParseParamValue(Args, "map=")
///
/// Param to read its value.
///
/// Returns the value or Default if the parameter was not found.
public bool ParseParamBool(string Param, bool Default = false)
{
string boolValue = ParseParamValue(Params, Param, Default.ToString());
return bool.Parse(boolValue);
}
///
/// Parses the command's Params list for a parameter and reads its value.
/// Ex. ParseParamValue(Args, "map=")
///
/// Param to read its value.
///
/// Returns the value or Default if the parameter was not found.
public int ParseParamInt(string Param, int Default = 0)
{
string num = ParseParamValue(Params, Param, Default.ToString());
return int.Parse(num);
}
///
/// Parses the command's Params list for a parameter and reads its value.
/// Ex. ParseParamValue(Args, "map=")
///
/// Param to read its value.
/// Returns the value or Default if the parameter was not found.
public int? ParseParamNullableInt(string Param)
{
string value = ParseParamValue(Params, Param, null);
if(value == null)
{
return null;
}
else
{
return int.Parse(value);
}
}
///
/// Parses project name string into a FileReference. Can be "Game" or "Game.uproject" or "Path/To/Game.uproject"
///
/// In project string to parse
/// FileReference to uproject
public FileReference? ParseProjectString(string originalProjectName)
{
FileReference? projectFullPath = null;
if (string.IsNullOrEmpty(originalProjectName))
{
return null;
}
string projectName = originalProjectName;
projectName = projectName.Trim(new char[] { '\"' });
if (projectName.IndexOfAny(new char[] { '\\', '/' }) < 0)
{
projectName = CombinePaths(CmdEnv.LocalRoot, projectName, projectName + ".uproject");
}
else if (!FileExists_NoExceptions(projectName))
{
projectName = CombinePaths(CmdEnv.LocalRoot, projectName);
}
if (FileExists_NoExceptions(projectName))
{
projectFullPath = new FileReference(projectName);
}
else
{
var Branch = new BranchInfo();
var GameProj = Branch.FindGame(originalProjectName);
if (GameProj != null)
{
projectFullPath = GameProj.FilePath;
}
}
return projectFullPath;
}
public FileReference? ParseProjectParam()
{
FileReference? projectFullPath = null;
bool bForeign = ParseParam("foreign");
bool bForeignCode = ParseParam("foreigncode");
if (bForeign)
{
string destSample = ParseParamValue("DestSample", "CopiedHoverShip");
string dest = ParseParamValue("ForeignDest", CombinePaths(@"C:\testue\foreign\", destSample + "_ _Dir"));
projectFullPath = new FileReference(CombinePaths(dest, destSample + ".uproject"));
}
else if (bForeignCode)
{
string destSample = ParseParamValue("DestSample", "PlatformerGame");
string dest = ParseParamValue("ForeignDest", CombinePaths(@"C:\testue\foreign\", destSample + "_ _Dir"));
projectFullPath = new FileReference(CombinePaths(dest, destSample + ".uproject"));
}
else
{
string originalProjectName = ParseParamValue("project", "");
if (string.IsNullOrEmpty(originalProjectName))
{
return null;
}
projectFullPath = ParseProjectString(originalProjectName);
if (projectFullPath == null || !FileExists_NoExceptions(projectFullPath.FullName))
{
throw new AutomationException("Could not find a project file {0}.", originalProjectName);
}
}
return projectFullPath;
}
///
/// Checks that all of the required params are present, throws an exception if not
///
///
public void CheckParamsArePresent(params string[] args)
{
List missingParams = new List();
foreach (string arg in args)
{
if (ParseParamValue(arg, null) == null)
{
missingParams.Add(arg);
}
}
if (missingParams.Count > 0)
{
throw new AutomationException("Params {0} are missing but required. Required params are {1}", string.Join(",", missingParams), string.Join(",", args));
}
}
///
/// Build command entry point. Throws AutomationExceptions on failure.
///
public virtual void ExecuteBuild()
{
throw new AutomationException("Either Execute() or ExecuteBuild() should be implemented for {0}", GetType().Name);
}
///
/// Command entry point.
///
public virtual ExitCode Execute()
{
ExecuteBuild();
return ExitCode.Success;
}
///
/// Async command entry point.
///
public virtual Task ExecuteAsync()
{
return Task.FromResult(Execute());
}
///
/// Executes a new command as a child of another command.
///
///
///
public static ExitCode Execute(BuildCommand ParentCommand) where T : BuildCommand, new()
{
T Command = new T();
if (ParentCommand != null)
{
Command.Params = ParentCommand.Params;
}
return Command.Execute();
}
}
}