File Coverage

blib/lib/ExtUtils/Depends.pm
Criterion Covered Total %
statement 166 203 81.7
branch 20 56 35.7
condition 8 22 36.3
subroutine 31 37 83.7
pod 15 20 75.0
total 240 338 71.0


line stmt bran cond sub pod time code
1             package ExtUtils::Depends;
2              
3 4     4   318205 use strict;
  4         31  
  4         95  
4 4     4   17 use warnings;
  4         5  
  4         92  
5 4     4   24 use Carp;
  4         8  
  4         203  
6 4     4   33 use Config;
  4         6  
  4         134  
7 4     4   18 use File::Find;
  4         6  
  4         174  
8 4     4   20 use File::Spec;
  4         13  
  4         93  
9 4     4   2039 use Data::Dumper;
  4         22267  
  4         2161  
10              
11             our $VERSION = '0.8001';
12              
13             sub import {
14 4     4   35 my $class = shift;
15 4 50       3101 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 4     4 1 2524 my ($class, $name, @deps) = @_;
22 4         30 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 4         13 $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 4         13 $self->load_deps;
40              
41 4         24 return $self;
42             }
43              
44             sub add_deps {
45 5     5 1 24 my $self = shift;
46 5         17 foreach my $d (@_) {
47             $self->{deps}{$d} = undef
48 2 50       7 unless $self->{deps}{$d};
49             }
50             }
51              
52             sub get_deps {
53 2     2 1 7 my $self = shift;
54 2         5 $self->load_deps; # just in case
55              
56 2         16 return %{$self->{deps}};
  2         9  
57             }
58              
59             sub set_inc {
60 1     1 1 5 my $self = shift;
61 1         2 push @{ $self->{inc} }, @_;
  1         3  
62             }
63              
64             sub set_libs {
65 1     1 1 6 my ($self, $newlibs) = @_;
66 1         2 $self->{libs} = $newlibs;
67             }
68              
69             sub add_pm {
70 7     7 1 25 my ($self, %pm) = @_;
71 7         21 while (my ($key, $value) = each %pm) {
72 8         35 $self->{pm}{$key} = $value;
73             }
74             }
75              
76             sub _listkey_add_list {
77 3     3   7 my ($self, $key, @list) = @_;
78 3 50       6 $self->{$key} = [] unless $self->{$key};
79 3         3 push @{ $self->{$key} }, @list;
  3         7  
80             }
81              
82 1     1 1 7 sub add_xs { shift->_listkey_add_list ('xs', @_) }
83 1     1 1 6 sub add_c { shift->_listkey_add_list ('c', @_) }
84             sub add_typemaps {
85 1     1 1 5 my $self = shift;
86 1         3 $self->_listkey_add_list ('typemaps', @_);
87 1         3 $self->install (@_);
88             }
89              
90             # no-op, only used for source back-compat
91 0     0 1 0 sub add_headers { carp "add_headers() is a no-op" }
92              
93             ####### PRIVATE
94 8     8 0 46 sub basename { (File::Spec->splitdir ($_[0]))[-1] }
95             # get the name in Makefile syntax.
96             sub installed_filename {
97 6     6 0 8 my $self = shift;
98 6         13 return '$(INST_ARCHLIB)/$(FULLEXT)/Install/'.basename ($_[0]);
99             }
100              
101             sub install {
102             # install things by adding them to the hash of pm files that gets
103             # passed through WriteMakefile's PM key.
104 2     2 1 7 my $self = shift;
105 2         2 foreach my $f (@_) {
106 4         8 $self->add_pm ($f, $self->installed_filename ($f));
107             }
108             }
109              
110             sub save_config {
111 4     4   28 use Data::Dumper;
  4         13  
  4         234  
112 2     2 1 19 local $Data::Dumper::Terse = 0;
113 2         4 local $Data::Dumper::Sortkeys = 1;
114 4     4   1671 use IO::File;
  4         9416  
  4         1479  
115              
116 2         5 my ($self, $filename) = @_;
117              
118 2 50       13 my $file = IO::File->new (">".$filename)
119             or croak "can't open '$filename' for writing: $!\n";
120              
121 2         262 print $file "package $self->{name}\::Install::Files;\n\n";
122             print $file "".Data::Dumper->Dump([{
123 2         9 inc => join (" ", @{ $self->{inc} }),
124             libs => $self->{libs},
125 2         4 typemaps => [ map { basename $_ } @{ $self->{typemaps} } ],
  2         6  
126 2         7 deps => [sort keys %{ $self->{deps} }],
  2         23  
127             }], ['self']);
128 2         180 print $file <<'EOF';
129              
130             @deps = @{ $self->{deps} };
131             @typemaps = @{ $self->{typemaps} };
132             $libs = $self->{libs};
133             $inc = $self->{inc};
134             EOF
135             # this is ridiculous, but old versions of ExtUtils::Depends take
136             # first $loadedmodule::CORE and then $INC{$file} --- the fallback
137             # includes the Filename.pm, which is not useful. so we must add
138             # this crappy code. we don't worry about portable pathnames,
139             # as the old code didn't either.
140 2         7 (my $mdir = $self->{name}) =~ s{::}{/}g;
141 2         8 print $file <<"EOT";
142              
143             \$CORE = undef;
144             foreach (\@INC) {
145             if ( -f \$_ . "/$mdir/Install/Files.pm") {
146             \$CORE = \$_ . "/$mdir/Install/";
147             last;
148             }
149             }
150              
151             sub deps { \@{ \$self->{deps} }; }
152              
153             sub Inline {
154             my (\$class, \$lang) = \@_;
155             +{ map { (uc(\$_) => \$self->{\$_}) } qw(inc libs typemaps) };
156             }
157             EOT
158              
159 2         4 print $file "\n1;\n";
160              
161 2         83 close $file;
162              
163             # we need to ensure that the file we just created gets put into
164             # the install dir with everything else.
165             #$self->install ($filename);
166 2         13 $self->add_pm ($filename, $self->installed_filename ('Files.pm'));
167             }
168              
169             sub load {
170 5     5 1 8151 my $dep = shift;
171 5         15 my @pieces = split /::/, $dep;
172 5         12 my @suffix = qw/ Install Files /;
173             # not File::Spec - see perldoc -f require
174 5         17 my $relpath = join('/', @pieces, @suffix) . '.pm';
175 5         7 my $depinstallfiles = join "::", @pieces, @suffix;
176 5 50       9 eval {
177 5         1763 require $relpath
178             } or die " *** Can't load dependency information for $dep:\n $@\n";
179             #print Dumper(\%INC);
180              
181             # effectively $instpath = dirname($INC{$relpath})
182 5         40 @pieces = File::Spec->splitdir ($INC{$relpath});
183 5         10 pop @pieces;
184 5         31 my $instpath = File::Spec->catdir (@pieces);
185              
186 4     4   28 no strict;
  4         8  
  4         4988  
187              
188 5 50       14 croak "No dependency information found for $dep"
189             unless $instpath;
190              
191 5 50       33 if (not File::Spec->file_name_is_absolute ($instpath)) {
192 0         0 $instpath = File::Spec->rel2abs ($instpath);
193             }
194              
195 5         8 my (@typemaps, $inc, $libs, @deps);
196              
197             # this will not exist when loading files from old versions
198             # of ExtUtils::Depends.
199 5         9 @deps = eval { $depinstallfiles->deps };
  5         25  
200 1         3 @deps = @{"$depinstallfiles\::deps"}
201 5 50 66     11 if $@ and exists ${"$depinstallfiles\::"}{deps};
  1         6  
202              
203 5         10 my $inline = eval { $depinstallfiles->Inline('C') };
  5         15  
204 5 100       25 if (!$@) {
205 4   100     13 $inc = $inline->{INC} || '';
206 4   100     19 $libs = $inline->{LIBS} || '';
207 4 100       7 @typemaps = @{ $inline->{TYPEMAPS} || [] };
  4         13  
208             } else {
209 1   50     1 $inc = ${"$depinstallfiles\::inc"} || '';
210 1   50     2 $libs = ${"$depinstallfiles\::libs"} || '';
211 1         1 @typemaps = @{"$depinstallfiles\::typemaps"};
  1         4  
212             }
213 5         7 @typemaps = map { File::Spec->rel2abs ($_, $instpath) } @typemaps;
  2         30  
214              
215             {
216 5         13 instpath => $instpath,
217             typemaps => \@typemaps,
218             inc => "-I". _quote_if_space($instpath) ." $inc",
219             libs => $libs,
220             deps => \@deps,
221             }
222             }
223              
224 5 50   5   46 sub _quote_if_space { $_[0] =~ / / ? qq{"$_[0]"} : $_[0] }
225              
226             sub load_deps {
227 8     8 1 12 my $self = shift;
228 8         10 my @load = grep !$self->{deps}{$_}, keys %{ $self->{deps} };
  8         32  
229 8         14 my %in_load; @in_load{@load} = ();
  8         13  
230 8         14 foreach my $d (@load) {
231 2         5 $self->{deps}{$d} = my $dep = load($d);
232             my @new_deps = grep !($self->{deps}{$_} || exists $in_load{$_}),
233 2 50 0     4 @{ $dep->{deps} || [] };
  2         5  
234 2         3 push @load, @new_deps;
235 2         7 @in_load{@new_deps} = ();
236             }
237             }
238              
239             sub uniquify {
240 3     3 0 61 my %seen;
241 3         25 grep !$seen{$_}++, @_;
242             }
243              
244             sub get_makefile_vars {
245 1     1 1 2 my $self = shift;
246              
247             # collect and uniquify things from the dependencies.
248             # first, ensure they are completely loaded.
249 1         3 $self->load_deps;
250              
251             ##my @defbits = map { split } @{ $self->{defines} };
252 1         2 my @incbits = map { split } @{ $self->{inc} };
  1         5  
  1         2  
253 1         7 my @libsbits = split /\s+/, $self->{libs};
254 1         2 my @typemaps = @{ $self->{typemaps} };
  1         3  
255 1         2 foreach my $d (sort keys %{ $self->{deps} }) {
  1         3  
256 0         0 my $dep = $self->{deps}{$d};
257             #push @defbits, @{ $dep->{defines} };
258 0 0       0 push @incbits, @{ $dep->{defines} } if $dep->{defines};
  0         0  
259 0 0       0 push @incbits, split /\s+/, $dep->{inc} if $dep->{inc};
260 0 0       0 push @libsbits, split /\s+/, $dep->{libs} if $dep->{libs};
261 0 0       0 push @typemaps, @{ $dep->{typemaps} } if $dep->{typemaps};
  0         0  
262             }
263              
264             # we have a fair bit of work to do for the xs files...
265 1         2 my @clean = ();
266 1         2 my @OBJECT = ();
267 1         1 my %XS = ();
268 1         2 foreach my $xs (@{ $self->{xs} }) {
  1         2  
269 2         6 (my $c = $xs) =~ s/\.xs$/\.c/i;
270 2         5 (my $o = $xs) =~ s/\.xs$/\$(OBJ_EXT)/i;
271 2         5 $XS{$xs} = $c;
272 2         3 push @OBJECT, $o;
273             # according to the MakeMaker manpage, the C files listed in
274             # XS will be added automatically to the list of cleanfiles.
275 2         4 push @clean, $o;
276             }
277              
278             # we may have C files, as well:
279 1         2 foreach my $c (@{ $self->{c} }) {
  1         2  
280 2         7 (my $o = $c) =~ s/\.c$/\$(OBJ_EXT)/i;
281 2         4 push @OBJECT, $o;
282 2         3 push @clean, $o;
283             }
284              
285 1         3 my %vars = (
286             INC => join (' ', uniquify @incbits),
287             LIBS => join (' ', uniquify $self->find_extra_libs, @libsbits),
288             TYPEMAPS => [@typemaps],
289             );
290              
291 1 50       4 $self->build_dll_lib(\%vars) if $^O =~ /MSWin32/;
292              
293             # we don't want to provide these if there is no data in them;
294             # that way, the caller can still get default behavior out of
295             # MakeMaker when INC, LIBS and TYPEMAPS are all that are required.
296             $vars{PM} = $self->{pm}
297 1 50       1 if %{ $self->{pm} };
  1         5  
298 1 50       13 $vars{clean} = { FILES => join (" ", @clean), }
299             if @clean;
300 1 50       7 $vars{OBJECT} = join (" ", @OBJECT)
301             if @OBJECT;
302 1 50       4 $vars{XS} = \%XS
303             if %XS;
304              
305 1         7 %vars;
306             }
307              
308             sub build_dll_lib {
309 0     0 0 0 my ($self, $vars) = @_;
310 0   0     0 $vars->{macro} ||= {};
311 0         0 $vars->{macro}{'INST_DYNAMIC_LIB'} =
312             '$(INST_ARCHAUTODIR)/$(DLBASE)$(LIB_EXT)';
313             }
314              
315             # Search for extra library files to link against on Windows (either native
316             # Windows library # files, or Cygwin library files)
317             # NOTE: not meant to be called publicly, so no POD documentation
318             sub find_extra_libs {
319 1     1 0 3 my $self = shift;
320              
321             my %mappers = (
322 0     0   0 MSWin32 => sub { $_[0] . '\.(?:lib|a)' },
323 0     0   0 cygwin => sub { $_[0] . '\.dll'},
324 0     0   0 android => sub { $_[0] . '\.' . $Config{dlext} },
325 1         8 );
326 1         3 my $mapper = $mappers{$^O};
327 1 50       8 return () unless defined $mapper;
328              
329 0           my @found_libs = ();
330 0           foreach my $name (keys %{ $self->{deps} }) {
  0            
331 0           (my $stem = $name) =~ s/^.*:://;
332 0 0         if ( defined &DynaLoader::mod2fname ) {
333 0           my @parts = split /::/, $name;
334 0           $stem = DynaLoader::mod2fname([@parts]);
335             }
336 0           my $lib = $mapper->($stem);
337 0           my $pattern = qr/$lib$/;
338              
339 0           my $matching_dir;
340             my $matching_file;
341             find (sub {
342 0 0 0 0     if ((not $matching_file) && /$pattern/) {;
343 0           $matching_dir = $File::Find::dir;
344 0           $matching_file = $File::Find::name;
345             }
346 0 0         }, map { -d $_ ? ($_) : () } @INC); # only extant dirs
  0            
347              
348 0 0 0       if ($matching_file && -f $matching_file) {
349 0           push @found_libs,
350             '-L' . _quote_if_space($matching_dir),
351             '-l' . $stem;
352             # Android's linker ignores the RTLD_GLOBAL flag
353             # and loads everything as if under RTLD_LOCAL.
354             # What this means in practice is that modules need
355             # to explicitly link to their dependencies,
356             # because otherwise they won't be able to locate any
357             # functions they define.
358             # We use the -l:foo.so flag to indicate that the
359             # actual library name to look for is foo.so, not
360             # libfoo.so
361 0 0         if ( $^O eq 'android' ) {
362 0           $found_libs[-1] = "-l:$stem.$Config{dlext}";
363             }
364 0           next;
365             }
366             }
367              
368 0           return @found_libs;
369             }
370              
371             __END__
372              
373             =head1 NAME
374              
375             ExtUtils::Depends - Easily build XS extensions that depend on XS extensions
376              
377             =head1 SYNOPSIS
378              
379             use ExtUtils::Depends;
380             $package = new ExtUtils::Depends ('pkg::name', 'base::package')
381             # set the flags and libraries to compile and link the module
382             $package->set_inc("-I/opt/blahblah");
383             $package->set_libs("-lmylib");
384             # add a .c and an .xs file to compile
385             $package->add_c('code.c');
386             $package->add_xs('module-code.xs');
387             # add the typemaps to use
388             $package->add_typemaps("typemap");
389             # install some extra data files and headers
390             $package->install (qw/foo.h data.txt/);
391             # save the info
392             $package->save_config('Files.pm');
393              
394             WriteMakefile(
395             'NAME' => 'Mymodule',
396             $package->get_makefile_vars()
397             );
398              
399             =head1 DESCRIPTION
400              
401             This module tries to make it easy to build Perl extensions that use
402             functions and typemaps provided by other perl extensions. This means
403             that a perl extension is treated like a shared library that provides
404             also a C and an XS interface besides the perl one.
405              
406             This works as long as the base extension is loaded with the RTLD_GLOBAL
407             flag (usually done with a
408              
409             sub dl_load_flags {0x01}
410              
411             in the main .pm file) if you need to use functions defined in the module.
412              
413             The basic scheme of operation is to collect information about a module
414             in the instance, and then store that data in the Perl library where it
415             may be retrieved later. The object can also reformat this information
416             into the data structures required by ExtUtils::MakeMaker's WriteMakefile
417             function.
418              
419             For information on how to make your module fit into this scheme, see
420             L</"hashref = ExtUtils::Depends::load (name)">.
421              
422             When creating a new Depends object, you give it a name, which is the name
423             of the module you are building. You can also specify the names of modules
424             on which this module depends. These dependencies will be loaded
425             automatically, and their typemaps, header files, etc merged with your new
426             object's stuff. When you store the data for your object, the list of
427             dependencies are stored with it, so that another module depending on your
428             needn't know on exactly which modules yours depends.
429              
430             For example:
431              
432             Gtk2 depends on Glib
433              
434             Gnome2::Canvas depends on Gtk2
435              
436             ExtUtils::Depends->new ('Gnome2::Canvas', 'Gtk2');
437             this command automatically brings in all the stuff needed
438             for Glib, since Gtk2 depends on it.
439              
440             When the configuration information is saved, it also includes a class
441             method called C<Inline>, inheritable by your module. This allows you in
442             your module to simply say at the top:
443              
444             package Mymod;
445             use parent 'Mymod::Install::Files'; # to inherit 'Inline' method
446              
447             And users of C<Mymod> who want to write inline code (using L<Inline>)
448             will simply be able to write:
449              
450             use Inline with => 'Mymod';
451              
452             And all the necessary header files, defines, and libraries will be added
453             for them.
454              
455             The C<Mymod::Install::Files> will also implement a C<deps> method,
456             which will return a list of any modules that C<Mymod> depends on -
457             you will not normally need to use this:
458              
459             require Mymod::Install::Files;
460             @deps = Mymod::Install::Files->deps;
461              
462             =head1 METHODS
463              
464             =over
465              
466             =item $object = ExtUtils::Depends->new($name, @deps)
467              
468             Create a new depends object named I<$name>. Any modules listed in I<@deps>
469             (which may be empty) are added as dependencies and their dependency
470             information is loaded. An exception is raised if any dependency information
471             cannot be loaded.
472              
473             =item $depends->add_deps (@deps)
474              
475             Add modules listed in I<@deps> as dependencies.
476              
477             =item (hashes) = $depends->get_deps
478              
479             Fetch information on the dependencies of I<$depends> as a hash of hashes,
480             which are dependency information indexed by module name. See C<load>.
481              
482             =item $depends->set_inc (@newinc)
483              
484             Add strings to the includes or cflags variables.
485              
486             =item $depends->set_libs (@newlibs)
487              
488             Add strings to the libs (linker flags) variable.
489              
490             =item $depends->add_pm (%pm_files)
491              
492             Add files to the hash to be passed through ExtUtils::WriteMakefile's
493             PM key.
494              
495             =item $depends->add_xs (@xs_files)
496              
497             Add xs files to be compiled.
498              
499             =item $depends->add_c (@c_files)
500              
501             Add C files to be compiled.
502              
503             =item $depends->add_typemaps (@typemaps)
504              
505             Add typemap files to be used and installed.
506              
507             =item $depends->add_headers (list)
508              
509             No-op, for backward compatibility.
510              
511             =item $depends->install (@files)
512              
513             Install I<@files> to the data directory for I<$depends>.
514              
515             This actually works by adding them to the hash of pm files that gets
516             passed through WriteMakefile's PM key.
517              
518             =item $depends->save_config ($filename)
519              
520             Save the important information from I<$depends> to I<$filename>, and
521             set it up to be installed as I<name>::Install::Files.
522              
523             Note: the actual value of I<$filename> is unimportant so long as it
524             doesn't clash with any other local files. It will be installed as
525             I<name>::Install::Files.
526              
527             =item hash = $depends->get_makefile_vars
528              
529             Return the information in I<$depends> in a format digestible by
530             WriteMakefile.
531              
532             This sets at least the following keys:
533              
534             INC
535             LIBS
536             TYPEMAPS
537             PM
538              
539             And these if there is data to fill them:
540              
541             clean
542             OBJECT
543             XS
544              
545             =item hashref = ExtUtils::Depends::load (name)
546              
547             Load and return dependency information for I<name>. Croaks if no such
548             information can be found. The information is returned as an anonymous
549             hash containing these keys:
550              
551             =over
552              
553             =item instpath
554              
555             The absolute path to the data install directory for this module.
556              
557             =item typemaps
558              
559             List of absolute pathnames for this module's typemap files.
560              
561             =item inc
562              
563             CFLAGS string for this module.
564              
565             =item libs
566              
567             LIBS string for this module.
568              
569             =item deps
570              
571             List of modules on which this one depends. This key will not exist when
572             loading files created by old versions of ExtUtils::Depends.
573              
574             =back
575              
576             If you want to make module I<name> support this, you must provide
577             a module I<name>::Install::Files, which on loading will implement the
578             following class methods:
579              
580             $hashref = name::Install::Files->Inline('C');
581             # hash to contain any necessary TYPEMAPS (array-ref), LIBS, INC
582             @deps = name::Install::Files->deps;
583             # any modules on which "name" depends
584              
585             An easy way to achieve this is to use the method
586             L</"$depends-E<gt>save_config ($filename)">, but your package may have
587             different facilities already.
588              
589             =item $depends->load_deps
590              
591             Load I<$depends> dependencies, by calling C<load> on each dependency module.
592             This is usually done for you, and should only be needed if you want to call
593             C<get_deps> after calling C<add_deps> manually.
594              
595             =back
596              
597             =head1 SUPPORT
598              
599             =head2 Bugs/Feature Requests
600              
601             Version 0.2 discards some of the more esoteric features provided by the
602             older versions. As they were completely undocumented, and this module
603             has yet to reach 1.0, this may not exactly be a bug.
604              
605             This module is tightly coupled to the ExtUtils::MakeMaker architecture.
606              
607             You can submit new bugs/feature requests by using one of two bug trackers
608             (below).
609              
610             =over
611              
612             =item CPAN Request Tracker
613              
614             You can submit bugs/feature requests via the web by going to
615             L<https://rt.cpan.org/Public/Bug/Report.html?Queue=ExtUtils-Depends> (requires
616             PAUSE ID or Bitcard), or by sending an e-mail to
617             L<bug-ExtUtils-Depends at rt.cpan.org>.
618              
619             =item Gnome.org Bugzilla
620              
621             Report bugs/feature requests to the 'gnome-perl' product (requires login)
622             L<http://bugzilla.gnome.org/enter_bug.cgi?product=gnome-perl>
623              
624             =back
625              
626             Patches that implement new features with test cases, and/or test cases that
627             exercise existing bugs are always welcome.
628              
629             The Gtk-Perl mailing list is at L<gtk-perl-list at gnome dot org>.
630              
631             =head2 Source Code
632              
633             The source code to L<ExtUtils::Depends> is available at the Gnome.org Git repo
634             (L<https://git.gnome.org/browse/perl-ExtUtils-Depends/>). Create your own
635             copy of the Git repo with:
636              
637             git clone git://git.gnome.org/perl-ExtUtils-Depends (Git protocol)
638             git clone https://git.gnome.org/browse/perl-ExtUtils-Depends/ (HTTPS)
639              
640             =head1 SEE ALSO
641              
642             ExtUtils::MakeMaker.
643              
644             =head1 AUTHOR
645              
646             Paolo Molaro <lupus at debian dot org> wrote the original version for
647             Gtk-Perl. muppet <scott at asofyet dot org> rewrote the innards for
648             version 0.2, borrowing liberally from Paolo's code.
649              
650             =head1 MAINTAINER
651              
652             The Gtk2 project, L<http://gtk2-perl.sf.net>/L<gtk-perl-list at gnome dot org>.
653              
654             =head1 LICENSE
655              
656             This library is free software; you may redistribute it and/or modify it
657             under the same terms as Perl itself.
658              
659             =cut
660