# --8<--8<--8<--8<--
#
# Copyright (C) 2008 Smithsonian Astrophysical Observatory
#
# This file is part of Decision::Depends
#
# Decision-Depends is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# -->8-->8-->8-->8--
package Decision::Depends;
require 5.005_62;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use Decision::Depends ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw(
if_dep
action
test_dep
);
our $VERSION = '0.20';
use Carp;
use Decision::Depends::OO;
our $self = Decision::Depends::OO->new();
## no critic ( ProhibitSubroutinePrototypes )
sub if_dep(&@)
{
my ( $deps, $run ) = @_;
my @args = &$deps;
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
$self->if_dep( \@args, $run );
}
sub action(&) { $_[0] }
sub test_dep
{
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
$self->test_dep( @_ );
}
sub Configure
{
local $Carp::CarpLevel = $Carp::CarpLevel + 1;
$self->configure( @_ );
}
sub init
{
my ( $state_file, $attr ) = @_;
print STDERR "Decision::Depends::init is obsolete. Please use Decision::Depends::Configure instead\n";
Configure( { File => $state_file, $attr ? %$attr : () } );
}
sub renew
{
undef $self;
$self = Decision::Depends::OO->new();
}
1;
__END__
=head1 NAME
Decision::Depends - Perform actions based upon file dependencies
=head1 SYNOPSIS
use Decision::Depends;
Decision::Depends::Configure( { File => $depfile } );
if_dep { @targ_dep_list }
action { action };
=head1 DESCRIPTION
B is a module which simplifies the creation of
procedures with intermediate steps which can be skipped if certain
dependencies are met. Think of it as a procedural version of B.
B is useful when there are several steps in a
process, each of which depends upon the last. If the process is
interrupted, or if it is to be redone with changes to parameters in
later steps, and if intermediate results can be kept, then
B can insure that only the minimal number of steps
be redone.
Each step must result in a tangible product (a file). For complicated
steps with many products the step's successful completion may be
indicated by creating an empty file whose existance indicates
completion. This file (a C file in B
terminology) can be automatically created if requested.
B determines if the product for a given step is
older than any files required to produce it. It can also check
whether the contents of a file have changed since the product was last
created. This is useful in the case where a configuration file must
be created anew each time, but results in action only if changed since
the product was last created. Finally, it can determine if a
variable's value has changed since the product was last created.
=head2 Dependency history
B must keep some dependency information between
runs (for signature and variable dependencies). It stores this in a
file, which must be named by the application. The application
indicates the file by calling the B
subroutine.
This file is updated after completion of successful actions and
when the program is exited.
=head2 Dry Runs and Changing other behavior
B can be put into a state where it checks
dependencies and pretends to update targets in order to check what
actions might need to be taken. This is done by passing the
C attribute to B. In this mode
no actions are actually performed, but are assumed to have
successfully created their products.
B will output to STDOUT its musings if the
C attribute is passed to B.
To simply test if a dependency exists, without requiring that an
action be performed, use the B function.
=head2 Targets and Dependencies List
Each step must construct a single Perl list of products, also called
targets (as in B), and dependencies. The list has a simple
syntax - it is a sequence of values, each of which may have one or
more attributes. Attributes precede values and apply only to the next
value (unless values are grouped), and always begin with a C<->
character. Multiple attributes may be applied to a single value.
-target => $file, -depend => -sig => $dep
(Note the use of the perl C<< => >> operator to avoid quoting of
attributes.) Values which begin with the C<-> character (which may be
confused with attributes) may be passed by reference. B
recognizes negative numbers, so those need not be handled specially.
-target => \'-strange_file', -target => -33.99e24
Values may be grouped by placing them in anonymous arrays:
-target => [ $file1, $file2 ]
Attributes are applied to all elements of the group; additional attributes
may modify individual group members:
-target => [ -sfile => $file1, $file2 ]
Groups may be nested.
To negate an attribute, introduce the same attribute with a prefix of
C<-no_>:
-target => -sfile => [ $file1, -no_sfile => $file2 ]
Attributes may have values, although they are in general boolean values.
The syntax is '-attr=value'. Note that because of the C<=> character,
Perl's automatic quoting rules when using the C<< => >> operator are
insufficient to ensure appropriate quoting. For example
'-slink=foo' => $target
assigns the C<-slink> attribute to C<$target> and gives the attribute
the value C. If no value is specified, a default value of C<1>
is assigned. Most attributes are boolean, so no value need be assigned
them.
Hash references may be used to pair attribute values with ordinary
values. For example, the following
-var => { $attr1 => $val1, $attr2 => $val2 }
assigns C<$val1> the attribute C<-var> with the value C<$attr1>,
C<$val2> the attribute C<-var> with the value C<$attr2>, etc. This is
most useful when specifying variable dependencies (see L).
=head2 Targets
Targets are identified either by having the C<-target> or C<-targets>
attributes, or by being the first value (or group) in the
target-dependency list and not having the C<-depend> attribute. For
example, the following are equivalent
( -target => $targ1, -target => $targ2, ... )
( -target => [ $targ1, $targ2 ], ... )
( [ $targ1, $targ2 ], ... )
There must be at least one target. Target values may have the
following attributes:
=over 8
=item B<-target>
This indicates the value is a target.
=item B<-sfile>
This indicates that the target is a status file. It will be automatically
created upon successful completion of the step.
=item B<-slink=>
This indicates that the target is a status file which is linked to an
imaginary file C. Any step which explicitly depends upon
C will instead depend upon the target file instead.
Multiple links to C may be created. Links are checked in
order of appearance, and are useful only as time dependencies. For
example, rather than depending upon the target of the previous step, a
step might depend upon the C. It's then possible to
introduce new intermediate steps which link their status files to
C without having to rewrite the current step. For example
( -target => '-slink=step1' => 'step1a', ... )
( -target => '-slink=step1' => 'step1b', ... )
( -target => $result, -depend => 'step1' )
In this case, the final step will depend upon F and F.
One could later add a F and not have to change the dependencies
for the final step.
The target status file will be automatically created upon successful
completion of the step.
=item C<-force>
If set to non-zero (the default if no value is specified), this will
force the target to always be out-of-date. This can be used to
override a global forcing of out-of-dateness (done via the
B function) by setting it to zero. It is probably
most useful for targets which have no dependencies.
=back
=head2 Dependencies
Dependencies are identified either as I being the first value (or
group) in the list and not having the C<-target> attribute, or by
having the attributes C<-depend> or C<-depends>. There need not be
any dependencies.
There are three types of dependencies: I