// Copyright Epic Games, Inc. All Rights Reserved.
using AutomationTool;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using EpicGames.Core;
using UnrealBuildTool;
using EpicGames.BuildGraph;
using AutomationTool.Tasks;
namespace AutomationTool.Tasks
{
///
/// Parameters for a spawn task
///
public class SpawnTaskParameters
{
///
/// Executable to spawn.
///
[TaskParameter]
public string Exe;
///
/// Arguments for the newly created process.
///
[TaskParameter(Optional = true)]
public string Arguments;
///
/// Working directory for spawning the new task
///
[TaskParameter(Optional = true)]
public string WorkingDir;
///
/// Environment variables to set
///
[TaskParameter(Optional = true)]
public string Environment;
///
/// File to read environment from
///
[TaskParameter(Optional = true)]
public string EnvironmentFile;
///
/// Write output to the log
///
[TaskParameter(Optional = true)]
public bool LogOutput = true;
///
/// The minimum exit code, which is treated as an error.
///
[TaskParameter(Optional = true)]
public int ErrorLevel = 1;
}
///
/// Base class for tasks that run an external tool
///
public abstract class SpawnTaskBase : BgTaskImpl
{
///
/// Execute a command
///
protected static Task ExecuteAsync(string Exe, string Arguments, string WorkingDir = null, Dictionary EnvVars = null, bool LogOutput = true, int ErrorLevel = 1, string Input = null)
{
if (WorkingDir != null)
{
WorkingDir = ResolveDirectory(WorkingDir).FullName;
}
CommandUtils.ERunOptions Options = CommandUtils.ERunOptions.Default;
if (!LogOutput)
{
Options &= ~CommandUtils.ERunOptions.AllowSpew;
}
IProcessResult Result = CommandUtils.Run(Exe, Arguments, Env: EnvVars, WorkingDir: WorkingDir, Options: Options, Input: Input);
if (Result.ExitCode < 0 || Result.ExitCode >= ErrorLevel)
{
throw new AutomationException("{0} terminated with an exit code indicating an error ({1})", Path.GetFileName(Exe), Result.ExitCode);
}
return Task.FromResult(Result);
}
///
/// Parses environment from a property and file
///
///
///
///
protected static Dictionary ParseEnvVars(string Environment, string EnvironmentFile)
{
Dictionary EnvVars = new Dictionary();
if (Environment != null)
{
ParseEnvironment(Environment, ';', EnvVars);
}
if (EnvironmentFile != null)
{
ParseEnvironment(FileUtils.ReadAllText(ResolveFile(EnvironmentFile)), '\n', EnvVars);
}
return EnvVars;
}
///
/// Parse environment from a string
///
///
///
///
static void ParseEnvironment(string Environment, char Separator, Dictionary EnvVars)
{
for (int BaseIdx = 0; BaseIdx < Environment.Length;)
{
int EqualsIdx = Environment.IndexOf('=', BaseIdx);
if (EqualsIdx == -1)
{
throw new AutomationException("Missing value in environment variable string '{0}'", Environment);
}
int EndIdx = Environment.IndexOf(Separator, EqualsIdx + 1);
if (EndIdx == -1)
{
EndIdx = Environment.Length;
}
string Name = Environment.Substring(BaseIdx, EqualsIdx - BaseIdx).Trim();
string Value = Environment.Substring(EqualsIdx + 1, EndIdx - (EqualsIdx + 1)).Trim();
EnvVars[Name] = Value;
BaseIdx = EndIdx + 1;
}
}
}
///
/// Spawns an external executable and waits for it to complete.
///
[TaskElement("Spawn", typeof(SpawnTaskParameters))]
public class SpawnTask : SpawnTaskBase
{
///
/// Parameters for this task
///
SpawnTaskParameters Parameters;
///
/// Construct a spawn task
///
/// Parameters for the task
public SpawnTask(SpawnTaskParameters InParameters)
{
Parameters = InParameters;
}
///
/// Execute the task.
///
/// Information about the current job
/// Set of build products produced by this node.
/// Mapping from tag names to the set of files they include
public override async Task ExecuteAsync(JobContext Job, HashSet BuildProducts, Dictionary> TagNameToFileSet)
{
await ExecuteAsync(Parameters.Exe, Parameters.Arguments, Parameters.WorkingDir, EnvVars: ParseEnvVars(Parameters.Environment, Parameters.EnvironmentFile), LogOutput: Parameters.LogOutput, ErrorLevel: Parameters.ErrorLevel);
}
///
/// Output this task out to an XML writer.
///
public override void Write(XmlWriter Writer)
{
Write(Writer, Parameters);
}
///
/// Find all the tags which are used as inputs to this task
///
/// The tag names which are read by this task
public override IEnumerable FindConsumedTagNames()
{
yield break;
}
///
/// Find all the tags which are modified by this task
///
/// The tag names which are modified by this task
public override IEnumerable FindProducedTagNames()
{
yield break;
}
}
public static partial class StandardTasks
{
///
/// Execute an external program
///
/// Execution state
/// Executable to spawn.
/// Arguments for the newly created process.
/// Working directory for spawning the new task.
/// Environment variables to set.
/// File to read environment from.
/// Write output to the log.
/// The minimum exit code which is treated as an error.
public static async Task SpawnAsync(string Exe, string Arguments = null, string WorkingDir = null, string Environment = null, string EnvironmentFile = null, bool? LogOutput = null, int? ErrorLevel = null)
{
SpawnTaskParameters Parameters = new SpawnTaskParameters();
Parameters.Exe = Exe;
Parameters.Arguments = Arguments;
Parameters.WorkingDir = WorkingDir;
Parameters.Environment = Environment;
Parameters.EnvironmentFile = EnvironmentFile;
Parameters.LogOutput = LogOutput ?? Parameters.LogOutput;
Parameters.ErrorLevel = ErrorLevel ?? Parameters.ErrorLevel;
await ExecuteAsync(new SpawnTask(Parameters));
}
}
}