File Coverage

blib/lib/ExtUtils/Builder/Planner.pm
Criterion Covered Total %
statement 140 176 79.5
branch 16 36 44.4
condition 4 16 25.0
subroutine 37 46 80.4
pod 16 16 100.0
total 213 290 73.4


line stmt bran cond sub pod time code
1             package ExtUtils::Builder::Planner;
2             $ExtUtils::Builder::Planner::VERSION = '0.020';
3 5     5   645003 use strict;
  5         14  
  5         192  
4 5     5   28 use warnings;
  5         10  
  5         307  
5              
6 5     5   32 use Carp ();
  5         9  
  5         125  
7 5     5   28 use File::Basename;
  5         22  
  5         603  
8 5     5   35 use File::Spec;
  5         13  
  5         160  
9 5     5   27 use List::Util 1.45 ();
  5         176  
  5         120  
10 5     5   25 use Scalar::Util ();
  5         10  
  5         133  
11              
12 5     5   2777 use ExtUtils::Builder::Plan;
  5         17  
  5         200  
13 5     5   2740 use ExtUtils::Builder::Node;
  5         37  
  5         243  
14 5     5   2858 use ExtUtils::Builder::Util;
  5         24  
  5         366  
15              
16 5     5   2983 use ExtUtils::Builder::FileSet::Free;
  5         18  
  5         229  
17 5     5   2729 use ExtUtils::Builder::FileSet::Filter;
  5         18  
  5         186  
18 5     5   2794 use ExtUtils::Builder::FileSet::Subst;
  5         54  
  5         902  
19              
20             my $class_counter = 0;
21              
22             sub new {
23 3     3 1 786738 my $base_class = shift;
24 3         43 my $all_files = ExtUtils::Builder::FileSet::Free->new;
25 3         21 return $base_class->_new_scope($base_class, undef, {}, { 'all-files' => $all_files });
26             }
27              
28             sub _new_scope {
29 3     3   11 my ($self, $base_class, $outer, $nodes, $filesets) = @_;
30              
31 3         10 my $class = __PACKAGE__ . '::Anon_' . ++$class_counter;
32 5     5   42 no strict 'refs';
  5         10  
  5         10619  
33 3         7 push @{ "$class\::ISA" }, $base_class;
  3         47  
34              
35 3         18 my $new = bless {
36             nodes => $nodes,
37             filesets => $filesets
38             }, $class;
39              
40 3     0   33 $new->add_delegate('self', sub { $new });
  0     5   0  
41 3     0   15 $new->add_delegate('outer', sub { $outer });
  0     5   0  
42              
43 3         10 return $new;
44             }
45              
46             sub new_scope {
47 0     0 1 0 my ($self) = @_;
48 0         0 return $self->_new_scope(ref($self), $self, $self->{nodes}, $self->{filesets});
49             }
50              
51             sub add_node {
52 11     11 1 22 my ($self, $node) = @_;
53 11         30 my $target = $node->target;
54 11 50       37 if (exists $self->{nodes}{$target}) {
55 0 0 0     0 Carp::croak("Duplicate for target $target") if !$node->mergeable or !$self->{nodes}{$target}->mergeable;
56 0         0 my @dependencies = List::Util::uniq($self->{nodes}{$target}->dependencies, $node->dependencies);
57 0         0 my $new = ExtUtils::Builder::Node->new(target => $target, dependencies => \@dependencies, phony => 1);
58 0         0 $self->{nodes}{$target} = $new;
59             } else {
60 11         28 $self->{nodes}{$target} = $node;
61 11 100       30 $self->{filesets}{'all-files'}->add_input($target) if not $node->phony;
62             }
63 11         35 return $node->target;
64             }
65              
66             sub create_node {
67 11     11 1 101 my ($self, %args) = @_;
68 11         54 my $node = ExtUtils::Builder::Node->new(%args);
69 11         46 return $self->add_node($node);
70             }
71              
72             sub create_phony {
73 0     0 1 0 my ($self, $target, @dependencies) = @_;
74 0         0 return $self->create_node(
75             target => $target,
76             dependencies => \@dependencies,
77             type => 'phony',
78             );
79             }
80              
81             my $counter = 0;
82              
83             sub _create_callback {
84 2     2   8 my ($self, $add_to) = @_;
85 2 50       23 return undef unless $add_to;
86 0         0 my $this = $self;
87 0         0 Scalar::Util::weaken($this);
88             return sub {
89 0     0   0 my ($entry) = @_;
90 0         0 $this->create_phony($add_to, $entry);
91 0         0 };
92             }
93              
94             sub create_filter {
95 1     1 1 6 my ($self, %args) = @_;
96             my $set = ExtUtils::Builder::FileSet::Filter->new(
97             condition => $args{condition},
98 1         8 callback => $self->_create_callback($args{add_to}),
99             );
100 1   50     8 my $on = $args{on} // 'all-files';
101 1 50       5 my @sources = ref($on) eq 'ARRAY' ? @{$on} : $on;
  0         0  
102 1         3 for my $source (@sources) {
103 1 50       9 my $object = $self->{filesets}{$source} or die "No such source $source";
104 1         9 $object->add_dependent($set);
105             }
106 1   33     9 my $name = $args{name} // 'filter-' . $counter++;
107 1         3 $self->{filesets}{$name} = $set;
108 1         6 return $name;
109             }
110              
111             sub _make_pattern {
112 1     1   34 my ($self, %options) = @_;
113 1 50       6 if ($options{file}) {
    0          
114 1         6 my $file = ExtUtils::Builder::Util::glob_to_regex($options{file});
115 1 50       5 if ($options{dir}) {
116 0         0 my $dir = ExtUtils::Builder::Util::native_to_unix_path($options{dir});
117 0         0 $dir =~ s{(?
118             return sub {
119 0     0   0 my ($input) = @_;
120 0         0 my $filename = ExtUtils::Builder::Util::native_to_unix_path($input);
121 0 0       0 return if substr($filename, 0, length $dir) ne $dir;
122 0         0 return File::Basename::basename($filename) =~ $file;
123 0         0 };
124             } else {
125             return sub {
126 5     5   12 my ($filename) = @_;
127 5         233 return File::Basename::basename($filename) =~ $file;
128 1         8 };
129             }
130             } elsif ($options{dir}) {
131 0         0 my $dir = ExtUtils::Builder::Util::native_to_unix_path($options{dir});
132 0         0 $dir =~ s{(?
133             return sub {
134 0     0   0 my ($input) = @_;
135 0         0 my $filename = ExtUtils::Builder::Util::native_to_unix_path($input);
136 0         0 return substr($filename, 0, length $dir) eq $dir;
137 0         0 };
138             } else {
139 0         0 Carp::croak("Unknown pattern type");
140             }
141             }
142              
143             sub create_pattern {
144 1     1 1 15 my ($self, %args) = @_;
145 1         7 my $positive = $self->_make_pattern(%args);
146 1 50   0   4 my $callback = $args{negate} ? sub { !$positive->($_[0]) } : $positive;
  0         0  
147 1         9 return $self->create_filter(%args, condition => $callback);
148             }
149              
150             sub create_subst {
151 1     1 1 33 my ($self, %args) = @_;
152             my $set = ExtUtils::Builder::FileSet::Subst->new(
153             subst => $args{subst},
154 1         5 callback => $self->_create_callback($args{add_to}),
155             );
156 1   50     5 my $on = $args{on} // 'all-files';
157 1 50       16 my @sources = ref($on) eq 'ARRAY' ? @{$on} : $on;
  0         0  
158 1         4 for my $source (@sources) {
159 1 50       6 my $object = $self->{filesets}{$source} or die "No such source $source";
160 1         7 $object->add_dependent($set);
161             }
162 1   33     8 my $name = $args{name} // 'subst-' . $counter++;
163 1         11 $self->{filesets}{$name} = $set;
164 1         5 return $name;
165             }
166              
167             sub add_seen {
168 2     2 1 14 my ($self, $entry) = @_;
169 2         10 $self->{filesets}{'all-files'}->add_input($entry);
170 2         6 return;
171             }
172              
173             sub merge_plan {
174 0     0 1 0 my ($self, $plan) = @_;
175 0         0 $self->add_node($_) for $plan->nodes;
176 0         0 return;
177             }
178              
179             my $set_subname = eval { require Sub::Util; Sub::Util->VERSION('1.40'); \&Sub::Util::set_subname } // sub { $_[1] };
180              
181             sub add_delegate {
182 9     9 1 39 my ($self, $name, $sub) = @_;
183 9         25 my $full_name = ref($self) . '::' . $name;
184 5     5   75 no strict 'refs';
  5         12  
  5         327  
185 5     5   36 no warnings 'redefine';
  5         10  
  5         1937  
186 9         69 *{$full_name} = $set_subname->($full_name, $sub);
  9         46  
187 9         26 return;
188             }
189              
190             sub load_extension {
191 3     3 1 22 my ($self, $plannable, $version, %options) = @_;
192 3         20 ExtUtils::Builder::Util::require_module($plannable);
193 3 50       16 $plannable->VERSION($version) if $version;
194 3         24 return $plannable->add_methods($self, %options);
195             }
196             *load_module = \&load_extension;
197              
198             sub materialize {
199 2     2 1 17 my $self = shift;
200 2         4 my %nodes = %{ $self->{nodes} };
  2         10  
201 2         21 return ExtUtils::Builder::Plan->new(nodes => \%nodes);
202             }
203              
204             my %dsl_commands = (
205             command => \&ExtUtils::Builder::Util::command,
206             code => \&ExtUtils::Builder::Util::code,
207             function => \&ExtUtils::Builder::Util::function,
208             );
209              
210             sub run_dsl {
211 1     1 1 11 my ($self, $filename) = @_;
212              
213 1         3 my $dsl_module = ref($self) . '::DSL';
214              
215 1 50       3 if (not defined &{ "$dsl_module\::AUTOLOAD" }) {
  1         7  
216 5     5   42 no strict 'refs';
  5         11  
  5         2436  
217 1         9 *{ "$dsl_module\::AUTOLOAD" } = sub {
218 3     3   305 my $name = our $AUTOLOAD;
219 3         21 $name =~ s/.*:://;
220 3 50       29 if (my $method = $self->can($name)) {
221             my $delegate = $set_subname->($name, sub {
222 5     5 1 19 $self->$method(@_);
        5 1    
223 3         75 });
224 3         7 *{ "$dsl_module\::$name" } = $delegate;
  3         20  
225 3         14 goto &$delegate;
226             }
227             else {
228 0         0 Carp::croak("No such subroutine $name");
229             }
230 1         7 };
231              
232 1         5 for my $name (keys %dsl_commands) {
233 3 50       25 *{ "$dsl_module\::$name" } = $dsl_commands{$name} if not $dsl_module->can($name);
  3         12  
234             }
235             }
236              
237 1         58 my $path = File::Spec->rel2abs($filename);
238 1 50 0     173 eval "package $dsl_module; my \$ret = do \$path; die \$@ if \$@; defined \$ret || !\$!" or die $@ // Carp::shortmess("Can't run $path: $!");
239 1         9 return;
240             }
241              
242             1;
243              
244             # ABSTRACT: An ExtUtils::Builder Plan builder
245              
246             __END__