// 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; } } }