// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using EpicGames.Core;
namespace EpicGames.BuildGraph.Expressions
{
///
/// Exception for constructing nodes
///
public sealed class BgNodeException : Exception
{
///
/// Constructor
///
///
public BgNodeException(string message) : base(message)
{
}
}
///
/// Speecifies the node name for a method. Parameters from the method may be embedded in the name using the {ParamName} syntax.
///
[AttributeUsage(AttributeTargets.Method)]
public sealed class BgNodeNameAttribute : Attribute
{
///
/// The format string
///
public string Template { get; }
///
/// Constructor
///
/// Format string for the name
public BgNodeNameAttribute(string template)
{
Template = template;
}
}
///
/// Specification for a node to execute
///
public class BgNode : BgExpr
{
///
/// Name of the node
///
public BgString Name { get; }
///
/// Thunk to native code to execute the node
///
public BgThunk Thunk { get; }
///
/// Number of outputs from this node
///
public int OutputCount { get; protected set; }
///
/// The default output of this node. Includes all other outputs.
///
public BgFileSet DefaultOutput { get; }
///
/// Agent for the node to be run on
///
public BgAgent Agent { get; }
///
/// Tokens for inputs of this node
///
public BgList Inputs { get; private set; } = BgList.Empty();
///
/// Weak dependency on outputs that must be generated for the node to run, without making those dependencies inputs.
///
public BgList Fences { get; private set; } = BgList.Empty();
///
/// Whether this node should start running as soon as its dependencies are ready, even if other nodes in the same agent are not.
///
public BgBool RunEarly { get; private set; } = BgBool.False;
///
/// Labels that this node contributes to
///
public BgList Labels { get; private set; } = BgList.Empty();
///
/// Constructor
///
public BgNode(BgThunk thunk, BgAgent agent)
: base(BgExprFlags.ForceFragment)
{
Name = GetDefaultNodeName(thunk);
Thunk = thunk;
Agent = agent;
DefaultOutput = new BgFileSetFromNodeOutputExpr(this, 0);
}
///
/// Copy constructor
///
///
public BgNode(BgNode node)
: base(BgExprFlags.ForceFragment)
{
Name = node.Name;
Thunk = node.Thunk;
Agent = node.Agent;
DefaultOutput = new BgFileSetFromNodeOutputExpr(this, 0);
Inputs = node.Inputs;
Fences = node.Fences;
RunEarly = node.RunEarly;
Labels = node.Labels;
}
///
public override void Write(BgBytecodeWriter writer)
{
BgObject obj = BgObject.Empty;
obj = obj.Set(x => x.Name, Name);
obj = obj.Set(x => x.Agent, Agent);
obj = obj.Set(x => x.Thunk, Thunk);
obj = obj.Set(x => x.OutputCount, (BgInt)OutputCount);
obj = obj.Set(x => x.InputExprs, Inputs);
obj = obj.Set(x => x.OrderDependencies, Fences);
obj = obj.Set(x => x.RunEarly, RunEarly);
obj = obj.Set(x => x.Labels, Labels);
writer.WriteExpr(obj);
}
///
/// Creates a copy of this node and updates the given parameters
///
///
///
///
///
///
internal BgNode Modify(BgList? inputs = null, BgList? fences = null, BgBool? runEarly = null, BgList? labels = null)
{
BgNode node = Clone();
if (inputs is not null)
{
node.Inputs = inputs;
}
if (fences is not null)
{
node.Fences = fences;
}
if (runEarly is not null)
{
node.RunEarly = runEarly;
}
if (labels is not null)
{
node.Labels = labels;
}
return node;
}
///
/// Clone this node
///
/// Clone of this node
protected virtual BgNode Clone() => new BgNode(this);
///
/// Gets the default tag name for the numbered output index
///
/// Name of the node
/// Index of the output. Index zero is the default, others are explicit.
///
internal static string GetDefaultTagName(string name, int index)
{
return $"#{name}${index}";
}
static BgString GetDefaultNodeName(BgThunk thunk)
{
// Check if it's got an attribute override for the node name
BgNodeNameAttribute? nameAttr = thunk.Method.GetCustomAttribute();
if (nameAttr != null)
{
return GetNodeNameFromTemplate(nameAttr.Template, thunk.Method.GetParameters(), thunk.Arguments);
}
else
{
return GetNodeNameFromMethodName(thunk.Method.Name);
}
}
static BgString GetNodeNameFromTemplate(string template, ParameterInfo[] parameters, IReadOnlyList