1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
package ExtUtils::Builder;
$ExtUtils::Builder::VERSION = '0.019';
use strict;
use warnings;
1;
#ABSTRACT: An overview of the foundations of the ExtUtils::Builder Plan framework
__END__
=pod
=encoding UTF-8
=head1 NAME
ExtUtils::Builder - An overview of the foundations of the ExtUtils::Builder Plan framework
=head1 VERSION
version 0.019
=head1 DESCRIPTION
This document describes the foundations of the ExtUtils::Builder Plan framework, including Actions, Nodes and Plans.
=head1 OVERVIEW
=head2 Action basics
Actions are the cornerstone of the ExtUtils::Builder framework. They provide an interface between build tools (e.g. L<ExtUtils::MakeMaker|ExtUtils::MakeMaker>, L<Module::Build|Module::Build>, ...) and building extensions. This allows producing and consuming sides to be completely independent from each other. It is a flexible abstraction around pieces of work, this work can be a piece of perl code, an external command, a mix of those or possibly other things.
An action can be consumed in many ways.
=over 4
=item * execute(%args)
This is often the simplest way of dealing with an action. It simple performs the action immediately, and will throw an exception on failure.
=item * to_command(%opts)
This converts the action into a list of commands to be executed. The elements of this list are arrayrefs to that can each be executed using along these lines:
for my $command ($work->to_command) {
system(@$command);
}
It can take two optional named arguments: C<perl> for the path to perl, and C<config> for an L<ExtUtils::Config> object used the find the current perl.
=item * to_code()
This converts the action into a list of strings to be C<eval>ed in order to execute them. This can be useful when you want to serialize the work that is to be done but don't want to force it to shell out.
=item * to_code_hash()
This converts the action into a hash that can be used to create a new L<ExtUtils::Builder::Action::Code>.
=item * flatten()
This will return all primitive actions involved in this action. It may return C<$self>, it may return an empty list.
On L<composite|ExtUtils::Builder::Action::Composite> actions, C<flatten> can be called to retrieve the constituent actions, C<flatten> is guaranteed to only return primitive actions.
=item * preference(@options)
If a consumer can consume actions in more than one way, the C<preference> method can be used to choose between options. This function expects a list of options out of C<code>, C<command>, C<execute> and C<flatten>. You probably want to flatten your action first, as different constituents may have different preferences.
for my $action ($work->flatten) {
my $preference = $self->preference('code', 'command');
push @serialized, ($preference eq 'code')
? [ eval => $action->to_code ]
: [ exec => $action->to_command ];
}
=back
=head2 Primitives
On primitive actions, all serialization methods will return a single item list. There are two types of of primitive actions shipped with this dist: L<Command|ExtUtils::Builder::Command> and L<Code|ExtUtils::Builder::Code>. Commands are essentially an abstraction around a call to an external command, Codes are an abstraction around a piece of Perl code. While these are all implementing the same interfaces, they have their own (hopefully obvious) preferences on how to be treated. C<flatten> is just an identity operator for primitive actions.
=head2 Composites
Composite actions are actions that may consist out of multiple actions (though in some cases they may contain only one or even zero actions). C<flatten> will return all its constituents. C<execute>, C<to_code> and C<to_command> will all call their respective method on all those values. C<preference> is of little use, and will always prefer to flatten when given that option.
=head3 Nodes
Nodes are composite Actions. Nodes are a simple class with three attributes:
=over 4
=item * target
This is the filename of the result of this build step.
=item * dependencies
This is an unordered set of zero or more files that must be build (and must be up-to-date) before the target is build.
=item * actions
This is a sequence of zero or more actions that must be performed to build the target.
=back
Essentially, a Node is equivalent to entry in a Makefile
=head2 Plans
Plans are the equivalent of a (piece of a) Makefile. They are a bunch of nodes that should interconnect. It has one attribute.
=over 4
=item * nodes
This is a hash mapping (target) names to nodes.
=back
The C<run> method will perform a topological sort much like C<make>. It will check which steps are necessary and skip the ones which are not.
=head1 RATIONALE
Writing extensions for various build tools can be a daunting task. This module tries to abstract steps of build processes into reusable building blocks for creating platform and build system agnostic executable descriptions of work.
=head1 USAGE
package Frobnicator;
use ExtUtils::Builder::Action::Code;
...
sub add_plans {
my ($self, $planner) = @_;
my $action = ExtUtils::Builder::Action::Code->new(
code => ...,
);
$planner->create_node(
target => 'frob',
actions => [ $action ],
);
$planner->create_node(
target => 'pure_all',
dependencies => [ 'frob' ],
phony => 1,
);
}
...
=head2 Makefile.PL
use ExtUtils::MakeMaker;
use ExtUtils::Builder::MakeMaker;
...
WriteMakeFile(
NAME => 'Foo',
VERSION => 0.001,
...,
);
sub MY::make_plans {
my ($self, $planner) = @_;
Frobnicator->add_plans($planner);
}
=head1 AUTHOR
Leon Timmermans <fawaka@gmail.com>
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2013 by Leon Timmermans.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
|