Data::Hopen::G::DAG - A hopen build graph


This class encapsulates the DAG for a particular set of one or more goals. It is itself a Data::Hopen::G::Op so that it can be composed into other DAGs.



Arrayref of the goals for this DAG.


The default goal for this DAG.


When a node has multiple predecessors, their outputs are combined using Hash::Merge to form the input to that node. This sets the Hash::Merge precedence. Valid values (case-insensitive) are:

undef or 'combine'

(the default): "Retainment Precedence" in Hash::Merge. Same-name keys are merged, so no data is lost.

'first' or 'keep'

"Left Precedence" in Hash::Merge. The first predecessor to add a value under a particular key will win.

'last' or 'replace'

"Right Precedence" in Hash::Merge. The last predecessor to add a value under a particular key will win.


The actual Graph. If you find that you have to use it, please open an issue so we can see about providing a documented API for your use case!


The node to which all goals are connected.


A separate Graph of operations that will run before all the operations in "_graph". This is because I don't want to add an edge to every single node just to force the topological sort to work out.


The first node to be run in _init_graph.



Traverses the graph. The DAG is similar to a subroutine in this respect. The outputs from all the goals of the DAG are aggregated and provided as the outputs of the DAG. The output is a hash keyed by the name of each goal, with each goal's outputs as the values under that name. Usage:

    my $hrOutputs = $dag->run([-context=>$scope][, other options])

$scope must be a Data::Hopen::Scope or subclass if provided. Other options are as "run" in Data::Hopen::G::Runnable.

When evaluating a node, the edges from its predecessors are traversed in the order those predecessors were added to the graph.



Creates a goal of the DAG. Goals are names for sequences of operations, akin to top-level Makefile targets. Usage:

    my $goalOp = $dag->goal('name')

Returns the Data::Hopen::G::Goal node that is the goal. By default, any inputs passed into a goal are provided as outputs of that goal, and are saved as outputs of the DAG under the goal's name.

The first call to goal() also sets "default_goal".


- DAG:connect(<op1>, <out-edge>, <in-edge>, <op2>)

Not yet implemented. Connects output out-edge of operation op1 as input in-edge of operation op2. No processing is done between output and input. out-edge and in-edge can be anything usable as a table index, provided that table index appears in the corresponding operation's descriptor.

- DAG:connect(<op1>, <op2>)

Creates a dependency edge from op1 to op2, indicating that op1 must be run before op2. Does not transfer any data from op1 to op2.

- DAG:connect(<op1>, <Link>, <op2>)

Connects op1 to op2 via Data::Hopen::G::Link Link. Link may be undef, in which case this is treated as the two-parameter form.

If there are already link(s) on the edge from op1 to op2, the new link is added after the last existing link.

TODO return the name of the edge? The edge instance itself? Maybe a fluent interface to the DAG for chaining connect calls?

TODO remove the out-edge and in-edge parameters?


Add a regular node to the graph. An attempt to add the same node twice will be ignored. Usage:

    my $node = Data::Hopen::G::Op->new(name=>"whatever");

Returns the node, for the sake of chaining.


Add an initialization operation to the graph. Initialization operations run before all other operations. An attempt to add the same initialization operation twice will be ignored. Usage:

    my $op = Data::Hopen::G::Op->new(name=>"whatever");
    $dag->init($op[, $first]);

If $first is truthy, the op will be run before anything already in the graph. However, later calls to init() with $first set will push operations even before $op.

Returns the node, for the sake of chaining.



Returns truthy if the only nodes in the graph are internal nodes. Intended for use by hopen files.



Initialize the instance.


Each DAG has a hidden "_final" node. All outputs have edges from the _final node. The traversal order is reverse topological from the root node, but is not constrained beyond that.

The DAG is built backwards from the outputs toward the inputs, although calls to "output" and "connect" can appear in any order as long as everything is hooked in before the DAG is run.

The following is TODO:

- DAG::inject(<op1>,<op2>[, after/before])

Returns an operation that lives on the edge between op1 and op2. If the third parameter is false, 'before', or omitted, the new operation will be the first operation on that edge. If the third parameter is true or 'after', the new operation will be the last operation on that edge. Any number of operations can be injected on any edge.