// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml; using EpicGames.Core; using OpenTracing; using OpenTracing.Util; using Microsoft.Extensions.Logging; using System.Reflection; using System.ComponentModel; 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 bRunEarly { get; set; } = false; /// /// Whether to ignore warnings produced by this node /// public bool bNotifyOnWarnings { get; set; } = true; /// /// 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 { get { return 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 (!bNotifyOnWarnings) { Writer.WriteAttributeString("NotifyOnWarnings", bNotifyOnWarnings.ToString()); } if(bRunEarly) { Writer.WriteAttributeString("RunEarly", bRunEarly.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; } } }