// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using EpicGames.Core;
namespace EpicGames.BuildGraph
{
///
/// Reference to an output tag from a particular node
///
public class BgNodeOutput
{
///
/// The node which produces the given output
///
public BgNode ProducingNode { get; }
///
/// Name of the tag
///
public string TagName { get; }
///
/// Constructor
///
/// Node which produces the given output
/// Name of the tag
public BgNodeOutput(BgNode inProducingNode, string inTagName)
{
ProducingNode = inProducingNode;
TagName = inTagName;
}
///
/// Returns a string representation of this output for debugging purposes
///
/// The name of this output
public override string ToString()
{
return String.Format("{0}: {1}", ProducingNode.Name, TagName);
}
}
///
/// Defines a node, a container for tasks and the smallest unit of execution that can be run as part of a build graph.
///
public class BgNode
{
///
/// The node's name
///
public string Name { get; }
///
/// Array of inputs which this node requires to run
///
public BgNodeOutput[] Inputs { get; }
///
/// Array of outputs produced by this node
///
public BgNodeOutput[] Outputs { get; }
///
/// Nodes which this node has input dependencies on
///
public BgNode[] InputDependencies { get; set; }
///
/// Nodes which this node needs to run after
///
public BgNode[] OrderDependencies { get; set; }
///
/// Tokens which must be acquired for this node to run
///
public FileReference[] RequiredTokens { get; }
///
/// List of tasks to execute
///
public List Tasks { get; } = new List();
///
/// List of email addresses to notify if this node fails.
///
public HashSet NotifyUsers { get; set; } = new HashSet(StringComparer.InvariantCultureIgnoreCase);
///
/// If set, anyone that has submitted to one of the given paths will be notified on failure of this node
///
public HashSet NotifySubmitters { get; set; } = new HashSet(StringComparer.InvariantCultureIgnoreCase);
///
/// Whether to start this node as soon as its dependencies are satisfied, rather than waiting for all of its agent's dependencies to be met.
///
public bool RunEarly { get; set; } = false;
///
/// Whether to ignore warnings produced by this node
///
public bool NotifyOnWarnings { get; set; } = true;
///
/// Custom annotations for this node
///
public Dictionary Annotations { get; } = new Dictionary(StringComparer.OrdinalIgnoreCase);
///
/// Constructor
///
/// The name of this node
/// Inputs that this node depends on
/// Names of the outputs that this node produces
/// Nodes which this node is dependent on for its inputs
/// Nodes which this node needs to run after. Should include all input dependencies.
/// Optional tokens which must be required for this node to run
public BgNode(string inName, BgNodeOutput[] inInputs, string[] inOutputNames, BgNode[] inInputDependencies, BgNode[] inOrderDependencies, FileReference[] inRequiredTokens)
{
Name = inName;
Inputs = inInputs;
List allOutputs = new List();
allOutputs.Add(new BgNodeOutput(this, "#" + Name));
allOutputs.AddRange(inOutputNames.Where(x => String.Compare(x, Name, StringComparison.InvariantCultureIgnoreCase) != 0).Select(x => new BgNodeOutput(this, x)));
Outputs = allOutputs.ToArray();
InputDependencies = inInputDependencies;
OrderDependencies = inOrderDependencies;
RequiredTokens = inRequiredTokens;
}
///
/// Returns the default output for this node, which includes all build products
///
public BgNodeOutput DefaultOutput => Outputs[0];
///
/// Determines the minimal set of direct input dependencies for this node to run
///
/// Sequence of nodes that are direct inputs to this node
public IEnumerable GetDirectInputDependencies()
{
HashSet directDependencies = new HashSet(InputDependencies);
foreach (BgNode inputDependency in InputDependencies)
{
directDependencies.ExceptWith(inputDependency.InputDependencies);
}
return directDependencies;
}
///
/// Determines the minimal set of direct order dependencies for this node to run
///
/// Sequence of nodes that are direct order dependencies of this node
public IEnumerable GetDirectOrderDependencies()
{
HashSet directDependencies = new HashSet(OrderDependencies);
foreach (BgNode orderDependency in OrderDependencies)
{
directDependencies.ExceptWith(orderDependency.OrderDependencies);
}
return directDependencies;
}
///
/// Write this node to an XML writer
///
/// The writer to output the node to
public void Write(XmlWriter writer)
{
writer.WriteStartElement("Node");
writer.WriteAttributeString("Name", Name);
string[] requireNames = Inputs.Select(x => x.TagName).ToArray();
if (requireNames.Length > 0)
{
writer.WriteAttributeString("Requires", String.Join(";", requireNames));
}
string[] producesNames = Outputs.Where(x => x != DefaultOutput).Select(x => x.TagName).ToArray();
if (producesNames.Length > 0)
{
writer.WriteAttributeString("Produces", String.Join(";", producesNames));
}
string[] afterNames = GetDirectOrderDependencies().Except(InputDependencies).Select(x => x.Name).ToArray();
if (afterNames.Length > 0)
{
writer.WriteAttributeString("After", String.Join(";", afterNames));
}
if (!NotifyOnWarnings)
{
writer.WriteAttributeString("NotifyOnWarnings", NotifyOnWarnings.ToString());
}
if (RunEarly)
{
writer.WriteAttributeString("RunEarly", RunEarly.ToString());
}
foreach (BgTask task in Tasks)
{
task.Write(writer);
}
writer.WriteEndElement();
}
///
/// Returns the name of this node
///
/// The name of this node
public override string ToString()
{
return Name;
}
}
}