File Coverage

blib/lib/ExtUtils/Depends.pm
Criterion Covered Total %
statement 170 186 91.4
branch 30 48 62.5
condition 12 16 75.0
subroutine 32 33 96.9
pod 15 19 78.9
total 259 302 85.7


line stmt bran cond sub pod time code
1             package ExtUtils::Depends;
2              
3 4     4   621550 use strict;
  4         8  
  4         191  
4 4     4   21 use warnings;
  4         9  
  4         287  
5 4     4   26 use Carp;
  4         9  
  4         296  
6 4     4   26 use Config;
  4         6  
  4         253  
7 4     4   26 use File::Find;
  4         10  
  4         279  
8 4     4   23 use File::Spec;
  4         5  
  4         125  
9 4     4   2805 use Data::Dumper;
  4         49535  
  4         3208  
10              
11             our $VERSION = '0.8002';
12              
13             sub import {
14 4     4   52 my $class = shift;
15 4 50       201345 return unless @_;
16 0 0       0 die "$class version $_[0] is required--this is only version $VERSION"
17             if $VERSION < $_[0];
18             }
19              
20             sub new {
21 6     6 1 274337 my ($class, $name, @deps) = @_;
22 6         70 my $self = bless {
23             name => $name,
24             deps => {},
25             inc => [],
26             libs => '',
27              
28             pm => {},
29             typemaps => [],
30             xs => [],
31             c => [],
32             }, $class;
33              
34 6         37 $self->add_deps (@deps);
35              
36             # attempt to load these now, so we'll find out as soon as possible
37             # whether the dependencies are valid. we'll load them again in
38             # get_makefile_vars to catch any added between now and then.
39 6         30 $self->load_deps;
40              
41 6         45 return $self;
42             }
43              
44             sub add_deps {
45 7     7 1 17 my $self = shift;
46 7         24 foreach my $d (@_) {
47 5   50     43 $self->{deps}{$d} ||= undef;
48             }
49             }
50              
51             sub get_deps {
52 2     2 1 12 my $self = shift;
53 2         7 $self->load_deps; # just in case
54              
55 2         5 return %{$self->{deps}};
  2         11  
56             }
57              
58             sub set_inc {
59 1     1 1 9 my $self = shift;
60 1         2 push @{ $self->{inc} }, @_;
  1         3  
61             }
62              
63             sub set_libs {
64 1     1 1 9 my ($self, $newlibs) = @_;
65 1         4 $self->{libs} = $newlibs;
66             }
67              
68             sub add_pm {
69 7     7 1 29 my ($self, %pm) = @_;
70 7         25 while (my ($key, $value) = each %pm) {
71 8         45 $self->{pm}{$key} = $value;
72             }
73             }
74              
75             sub _listkey_add_list {
76 3     3   9 my ($self, $key, @list) = @_;
77 3 50       10 $self->{$key} = [] unless $self->{$key};
78 3         4 push @{ $self->{$key} }, @list;
  3         12  
79             }
80              
81 1     1 1 14 sub add_xs { shift->_listkey_add_list ('xs', @_) }
82 1     1 1 9 sub add_c { shift->_listkey_add_list ('c', @_) }
83             sub add_typemaps {
84 1     1 1 9 my $self = shift;
85 1         4 $self->_listkey_add_list ('typemaps', @_);
86 1         6 $self->install (@_);
87             }
88              
89             # no-op, only used for source back-compat
90 0     0 1 0 sub add_headers { carp "add_headers() is a no-op" }
91              
92             ####### PRIVATE
93 8     8 0 68 sub basename { (File::Spec->splitdir ($_[0]))[-1] }
94             # get the name in Makefile syntax.
95             sub installed_filename {
96 6     6 0 8 my $self = shift;
97 6         16 return '$(INST_ARCHLIB)/$(FULLEXT)/Install/'.basename ($_[0]);
98             }
99              
100             sub install {
101             # install things by adding them to the hash of pm files that gets
102             # passed through WriteMakefile's PM key.
103 2     2 1 6 my $self = shift;
104 2         5 foreach my $f (@_) {
105 4         10 $self->add_pm ($f, $self->installed_filename ($f));
106             }
107             }
108              
109             sub save_config {
110 4     4   46 use Data::Dumper;
  4         16  
  4         351  
111 2     2 1 26 local $Data::Dumper::Terse = 0;
112 2         5 local $Data::Dumper::Sortkeys = 1;
113 4     4   2400 use IO::File;
  4         14997  
  4         2415  
114              
115 2         5 my ($self, $filename) = @_;
116              
117 2 50       21 my $file = IO::File->new (">".$filename)
118             or croak "can't open '$filename' for writing: $!\n";
119              
120 2         502 print $file "package $self->{name}\::Install::Files;\n\n";
121             print $file "".Data::Dumper->Dump([{
122 2         10 inc => join (" ", @{ $self->{inc} }),
123             libs => $self->{libs},
124 2         5 typemaps => [ map { basename $_ } @{ $self->{typemaps} } ],
  2         7  
125 2         7 deps => [sort keys %{ $self->{deps} }],
  2         55  
126             }], ['self']);
127 2         232 print $file <<'EOF';
128              
129             @deps = @{ $self->{deps} };
130             @typemaps = @{ $self->{typemaps} };
131             $libs = $self->{libs};
132             $inc = $self->{inc};
133             EOF
134             # this is ridiculous, but old versions of ExtUtils::Depends take
135             # first $loadedmodule::CORE and then $INC{$file} --- the fallback
136             # includes the Filename.pm, which is not useful. so we must add
137             # this crappy code. we don't worry about portable pathnames,
138             # as the old code didn't either.
139 2         9 (my $mdir = $self->{name}) =~ s{::}{/}g;
140 2         7 print $file <<"EOT";
141              
142             \$CORE = undef;
143             foreach (\@INC) {
144             if ( -f \$_ . "/$mdir/Install/Files.pm") {
145             \$CORE = \$_ . "/$mdir/Install/";
146             last;
147             }
148             }
149              
150             sub deps { \@{ \$self->{deps} }; }
151              
152             sub Inline {
153             my (\$class, \$lang) = \@_;
154             +{ map { (uc(\$_) => \$self->{\$_}) } qw(inc libs typemaps) };
155             }
156             EOT
157              
158 2         15 print $file "\n1;\n";
159              
160 2         108 close $file;
161              
162             # we need to ensure that the file we just created gets put into
163             # the install dir with everything else.
164             #$self->install ($filename);
165 2         13 $self->add_pm ($filename, $self->installed_filename ('Files.pm'));
166             }
167              
168             sub load {
169 10     10 1 253786 my $dep = shift;
170 10         38 my @pieces = split /::/, $dep;
171 10         52 my @suffix = qw/ Install Files /;
172             # not File::Spec - see perldoc -f require
173 10         37 my $relpath = join('/', @pieces, @suffix) . '.pm';
174 10         31 my $depinstallfiles = join "::", @pieces, @suffix;
175 10 50       20 eval {
176 10         2824 require $relpath
177             } or die " *** Can't load dependency information for $dep:\n $@\n";
178             #print Dumper(\%INC);
179              
180             # effectively $instpath = dirname($INC{$relpath})
181 10         242 my ($vol,$dirs,$file) = File::Spec->splitpath($INC{$relpath});
182 10         264 my $instpath = File::Spec->catpath($vol,$dirs,'');
183              
184 4     4   52 no strict;
  4         9  
  4         5517  
185              
186 10 50       80 croak "No dependency information found for $dep"
187             unless $instpath;
188              
189 10 50       55 if (not File::Spec->file_name_is_absolute ($instpath)) {
190 0         0 $instpath = File::Spec->rel2abs ($instpath);
191             }
192              
193             # this will not exist when loading files from old versions
194             # of ExtUtils::Depends.
195 10         233 my @deps = eval { $depinstallfiles->deps };
  10         162  
196 2         5 @deps = @{"$depinstallfiles\::deps"}
197 10 100 100     31 if $@ and exists ${"$depinstallfiles\::"}{deps};
  5         52  
198              
199 10         18 my (@typemaps, $inc, $libs);
200 10         15 my $inline = eval { $depinstallfiles->Inline('C') };
  10         106  
201 10 100       29 if (!$@) {
202 5   100     20 $inc = $inline->{INC} || '';
203 5   100     19 $libs = $inline->{LIBS} || '';
204 5 100       10 @typemaps = @{ $inline->{TYPEMAPS} || [] };
  5         17  
205             } else {
206 5   100     9 $inc = ${"$depinstallfiles\::inc"} || '';
207 5   50     10 $libs = ${"$depinstallfiles\::libs"} || '';
208 5         7 @typemaps = @{"$depinstallfiles\::typemaps"};
  5         28  
209             }
210 10         22 @typemaps = map { File::Spec->rel2abs ($_, $instpath) } @typemaps;
  2         29  
211              
212             {
213 10         32 instpath => $instpath,
214             typemaps => \@typemaps,
215             inc => "-I". _quote_if_space($instpath) ." $inc",
216             libs => $libs,
217             deps => \@deps,
218             }
219             }
220              
221 10 100   10   162 sub _quote_if_space { $_[0] =~ / / ? qq{"$_[0]"} : $_[0] }
222              
223             sub load_deps {
224 12     12 1 36 my $self = shift;
225 12         23 my @load = grep !$self->{deps}{$_}, keys %{ $self->{deps} };
  12         70  
226 12         44 my %in_load; @in_load{@load} = ();
  12         27  
227 12         55 while (@load) {
228 6         14 my $d = shift @load;
229 6         20 $self->{deps}{$d} = my $dep = load($d);
230             my @new_deps = grep !($self->{deps}{$_} || exists $in_load{$_}),
231 6 50 33     12 @{ $dep->{deps} || [] };
  6         31  
232 6         30 push @load, @new_deps;
233 6         29 @in_load{@new_deps} = ();
234             }
235             }
236              
237             sub uniquify {
238 7     7 0 100 my %seen;
239 7         67 grep !$seen{$_}++, @_;
240             }
241              
242             sub get_makefile_vars {
243 3     3 1 14 my $self = shift;
244              
245             # collect and uniquify things from the dependencies.
246             # first, ensure they are completely loaded.
247 3         13 $self->load_deps;
248              
249 3         5 my @incbits = @{ $self->{inc} };
  3         10  
250 3         13 my @libsbits = $self->{libs};
251 3         7 my @typemaps = @{ $self->{typemaps} };
  3         8  
252 3         20 foreach my $d (sort keys %{ $self->{deps} }) {
  3         14  
253 4         8 my $dep = $self->{deps}{$d};
254 4 50       12 push @incbits, @{ $dep->{defines} } if $dep->{defines};
  0         0  
255 4 50       22 push @incbits, $dep->{inc} if $dep->{inc};
256 4 50       9 push @libsbits, $dep->{libs} if $dep->{libs};
257 4 50       12 push @typemaps, @{ $dep->{typemaps} } if $dep->{typemaps};
  4         9  
258             }
259              
260             # we have a fair bit of work to do for the xs files...
261 3         7 my @clean = ();
262 3         5 my @OBJECT = ();
263 3         5 my %XS = ();
264 3         6 foreach my $xs (@{ $self->{xs} }) {
  3         10  
265 2         13 (my $c = $xs) =~ s/\.xs$/\.c/i;
266 2         9 (my $o = $xs) =~ s/\.xs$/\$(OBJ_EXT)/i;
267 2         6 $XS{$xs} = $c;
268 2         5 push @OBJECT, $o;
269             # according to the MakeMaker manpage, the C files listed in
270             # XS will be added automatically to the list of cleanfiles.
271 2         4 push @clean, $o;
272             }
273              
274             # we may have C files, as well:
275 3         6 foreach my $c (@{ $self->{c} }) {
  3         15  
276 2         9 (my $o = $c) =~ s/\.c$/\$(OBJ_EXT)/i;
277 2         4 push @OBJECT, $o;
278 2         9 push @clean, $o;
279             }
280              
281             my %vars = (
282             INC => join (' ', uniquify @incbits),
283             LIBS => join (' ', uniquify @libsbits),
284 3         13 LDFROM => join (' ', '$(OBJECT)', map _quote_if_space($_), find_extra_libs($self->{deps}, \@INC)),
285             TYPEMAPS => \@typemaps,
286             );
287              
288             # we don't want to provide these if there is no data in them;
289             # that way, the caller can still get default behavior out of
290             # MakeMaker when INC, LIBS and TYPEMAPS are all that are required.
291             $vars{PM} = $self->{pm}
292 3 100       7 if %{ $self->{pm} };
  3         12  
293 3 100       12 $vars{clean} = { FILES => join (" ", @clean), }
294             if @clean;
295 3 100       11 $vars{OBJECT} = join (" ", @OBJECT)
296             if @OBJECT;
297 3 100       9 $vars{XS} = \%XS
298             if %XS;
299              
300 3         26 %vars;
301             }
302              
303             # Search for extra library files to link against on Windows (either native
304             # Windows library # files, or Cygwin library files)
305             # NOTE: not meant to be called publicly, so no POD documentation
306             # see https://rt.cpan.org/Ticket/Display.html?id=45224 for discussion
307             my %exts; BEGIN { %exts = (
308 4     4   1743 MSWin32 => [ ".lib", ".$Config{dlext}", $Config{_a} ],
309             cygwin => [ '.dll' ],
310             android => [ ".$Config{dlext}" ],
311             ); }
312             sub find_extra_libs {
313 3     3 0 8 my ($deps, $search) = @_;
314 3 100       19 return () if !keys %$deps;
315 2 50       20 return () unless my $exts = $exts{$^O};
316 0           require File::Spec::Functions;
317 0           my @found_libs = ();
318 0           DEP: foreach my $name (keys %$deps) {
319 0           my @parts = ('auto', split /::/, $name);
320 0 0         my $stem = defined &DynaLoader::mod2fname
321             ? DynaLoader::mod2fname(\@parts) : $parts[-1];
322 0           my @bases = map $stem.$_, @$exts;
323 0           for my $dir (grep -d, @$search) { # only extant dirs
324 0           my ($found) = grep -f, map File::Spec::Functions::catfile($dir, @parts, $_), @bases;
325 0 0         next if !defined $found;
326 0           push @found_libs, $found;
327 0           next DEP;
328             }
329             }
330 0           @found_libs;
331             }
332              
333             1;
334              
335             __END__