File Coverage

blib/lib/ExtUtils/MakeMaker/BigHelper.pm
Criterion Covered Total %
statement 12 292 4.1
branch 0 158 0.0
condition 0 69 0.0
subroutine 4 21 19.0
pod 10 12 83.3
total 26 552 4.7


line stmt bran cond sub pod time code
1             package ExtUtils::MakeMaker::BigHelper;
2              
3 1     1   41844 use strict;
  1         2  
  1         39  
4 1     1   5 use warnings;
  1         1  
  1         53  
5              
6             require File::Find;
7             require Data::Dumper;
8              
9 1     1   1089 use ExtUtils::MakeMaker::Config qw(Config);
  1         74759  
  1         10  
10 1     1   154 use base qw(Exporter);
  1         2  
  1         7450  
11              
12             our %EXPORT_TAGS = (
13             'all' => [ qw(
14             find_files
15             find_directories
16             init_dirscan
17             init_xs
18             init_PM
19             clean_subdirs
20             clean
21             postamble
22             dynamic_bs
23             dynamic_lib
24             test
25             %Is
26             ) ],
27             'find' => [ qw(
28             find_files
29             find_directories
30             ) ],
31             'MY' => [ qw(
32             init_dirscan
33             init_xs
34             init_PM
35             clean_subdirs
36             clean
37             postamble
38             dynamic_bs
39             dynamic_lib
40             test
41             %Is
42             ) ],
43             );
44              
45             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
46              
47             our @EXPORT = qw(
48             );
49              
50             our $VERSION = '0.92';
51              
52             my $debugf;
53              
54             our %Is;
55             $Is{OS2} = $^O eq 'os2';
56             $Is{Win32} = $^O eq 'MSWin32' || $Config{osname} eq 'NetWare';
57             $Is{Dos} = $^O eq 'dos';
58             $Is{VMS} = $^O eq 'VMS';
59             $Is{OSF} = $^O eq 'dec_osf';
60             $Is{IRIX} = $^O eq 'irix';
61             $Is{NetBSD} = $^O eq 'netbsd';
62             $Is{Interix} = $^O eq 'interix';
63             $Is{SunOS4} = $^O eq 'sunos';
64             $Is{Solaris} = $^O eq 'solaris';
65             $Is{SunOS} = $Is{SunOS4} || $Is{Solaris};
66             $Is{BSD} = ($^O =~ /^(?:free|net|open)bsd$/ or grep( $^O eq $_, qw(bsdos interix dragonfly) ));
67              
68             =head1 NAME
69              
70             ExtUtils::MakeMaker::BigHelper - for helping ExtUtils::MakeMaker with big XS
71             projects.
72              
73             =head1 SYNOPSIS
74              
75             use ExtUtils::MakeMaker::BigHelper qw(:find);
76              
77             This exports find_files and find_directories, which might be useful with
78             WriteMakefile from ExtUtils::MakeMaker.
79              
80             use ExtUtils::MakeMaker::BigHelper qw(:MY);
81              
82             Use this after a "package MY;" statement. It will export methods that
83             are used by ExtUtils::MakeMaker and available for customization. These
84             customized methods will alter the behaviour of ExtUtils::MakeMaker as
85             documented below.
86              
87             =head1 DESCRIPTION
88              
89             This package extends or alters the functionality of ExtUtils::MakeMaker,
90             in a way more suitable perhaps for large projects using a lot of perl XS.
91              
92             This allows multiple .xs files in your project, strewn about the lib directory
93             hierarchy, hopefully side by each with the corresponding .pm file. Multiple t
94             directories are also allowed, hopefully located next to the .pm files they test.
95              
96             See the man page for perlxs for more information about perl XS.
97              
98             With ExtUtils::MakeMaker there can only be one .xs file, which limits the size
99             of the project. Well, there is a way to have more, but you're left with one
100             master .xs file responsible for bootstrapping all the other ones. The way
101             to have two .xs files is documented, but not easy, and the way to have more
102             than two is, well, unnatural.
103              
104             With ExtUtils::MakeMaker you're allowed to provide customizations for various
105             MakeMaker methods in the package MY. This gives you the ability to make a
106             total overhaul of ExtUtils::MakeMaker.
107              
108             This package uses the customization facility built in to ExtUtils::MakeMaker
109             to allow multiple .xs files, multiple t files, and multiple subproject
110             (Makefile.PL and Build.PL) directories in the lib hierarchy rather than at
111             the top level of the project. This allows you to more conveniently use
112             perlxs in your project, without having to package up multiple Makefile.PL
113             projects embedded in your directories. It also allows you to convert all
114             those .pm files with Inline code into real perlxs without using dynamite.
115              
116             The methods here are meant to be exported into the package MY, to provide
117             customizations of the ExtUtils::MakeMaker methods.
118              
119             For example, here's a possible project layout:
120              
121             Changes
122             lib/Big/Project.pm
123             lib/Big/Project/Collections/MyLRU.xs
124             lib/Big/Project/Collections/MyLRU.pm
125             lib/Big/Project/Collections/t/00-usage.t
126             lib/Big/Project/Collections/t/10-testlru.t
127             lib/Big/Project/Worker.pm
128             lib/Big/Project/Slave.pm
129             lib/Big/Project/Dispatcher/Scheduler.xs
130             lib/Big/Project/Dispatcher/Scheduler.pm
131             lib/Big/Project/Dispatcher/t/00-usage.t
132             lib/Big/Project/Dispatcher/t/10-roundrobin.t
133             lib/Big/Project/clib/Makefile
134             lib/Big/Project/clib/toolbox.c
135             lib/Big/Project/clib/toolbox.h
136             lib/Big/Project/clib/hashmaker.c
137             lib/Big/Project/clib/hashmaker.h
138             Makefile.PL
139             MANIFEST
140             README
141              
142             The Makefile.PL would look like this:
143              
144             use ExtUtils::MakeMaker;
145              
146             WriteMakefile(
147             NAME => "Big::Project",
148             VERSION_FROM => "lib/Big/Project.pm",
149             ($] >= 5.005 ? ## Add these new keywords supported since 5.005
150             (ABSTRACT => 'Big Project',
151             AUTHOR => 'John Bigbooty ') : ()),
152             DEFINE => '',
153             LIBS => ['-lm' ], # e.g., '-lm'
154             INC => '-I. -Ilib/Big/Project/clib',
155             MYEXTLIB => 'lib/Big/Project/clib/libmyextlib.a',
156             );
157              
158             package MY;
159              
160             use ExtUtils::MakeMaker::BigHelper qw(:MY);
161              
162             That should do it. It uses File::Find to descend the hierarchy and find all
163             the .xs files and t directories.
164              
165             =head2 B<$found = find_files($regex, @directories)>
166              
167             This function is exported by the :find tag, and looks up all files matching
168             the regex, under the listed directories. It returns them in a hashref where
169             the keys are the files found and the value is 1.
170              
171             =cut
172              
173             sub find_files {
174 0     0 1   my ($pat, @dirs) = @_;
175              
176 0           my %found;
177 0 0 0 0     File::Find::find({wanted => sub { $found{$File::Find::name}=1 if /$pat/ && -f $File::Find::name }, no_chdir => 1}, @dirs);
  0            
178              
179 0           return \%found;
180             }
181              
182             =head2 B<$found = find_directories($regex, @directories)>
183              
184             This function is exported by the :find tag, and looks up all directories
185             matching the regex, under the listed directories. It returns them in a
186             hashref where the keys are the directories found and the value is 1.
187              
188             =cut
189              
190             sub find_directories {
191 0     0 1   my ($pat, @dirs) = @_;
192              
193 0           my %found;
194 0 0 0 0     File::Find::find({wanted => sub { $found{$File::Find::name}=1 if /$pat/ && -d $File::Find::name }, no_chdir => 1}, @dirs);
  0            
195              
196 0           return \%found;
197             }
198              
199             sub make_hashref_of_found_files {
200 0     0 0   my ($pat, @dirs) = @_;
201              
202 0           my %found;
203 0 0   0     File::Find::find({wanted => sub { $found{$File::Find::name}=1 if /$pat/ }, no_chdir => 1}, @dirs);
  0            
204              
205 0           return \%found;
206             }
207              
208             =head2 B<$obj->init_dirscan>
209              
210             This function is exported by the :MY tag. It extends the MM init_dirscan.
211              
212             The MM init_dirscan only looks for .xs files in the top level directory.
213             This extension descends into lib to find all .xs files, and sets them into
214             the $self XS setting.
215              
216             Note that the XS setting is supposed to be a hashref of .xs files,
217             where the value is the corresponding .c file. However, with these
218             overrides, the value need only be 1 if the .c file is as expected.
219              
220             =cut
221              
222             sub init_dirscan {
223 0     0 1   my ($self) = @_;
224              
225             # the init_dirscan in MM_Unix does not recursively search directories
226             # here we want to recursively search for .xs files
227              
228 0   0       $self->{XS} ||= ExtUtils::MakeMaker::BigHelper::make_hashref_of_found_files(qr/\.xs$/, 'lib');
229              
230 0           my $result = $self->MM::init_dirscan;
231 0 0         print STDERR "debug: after init_dirscan PM is ", Data::Dumper::Dumper($self->{PM}) if $debugf;
232 0           return $result;
233             }
234              
235             =head2 B<$obj->test>
236              
237             This function is exported by the :MY tag. It extends the MM test.
238              
239             The MM test only looks for t directories at the top level directory. This
240             extension descends into lib to find all t directories, and sets them into
241             the $self TESTS setting.
242              
243             The $self TESTS setting is supposed to be a blank separated list
244             of t files (with shell wildcards) to run.
245              
246             Note that the XS setting is supposed to be a hashref of .xs files,
247             where the value is the corresponding .c file. However, with these
248             overrides, the value need only be 1 if the .c file is as expected.
249              
250             =cut
251              
252             sub test {
253 0     0 1   my ($self, %attribs) = @_;
254              
255             # recurses to find deeper t directories
256             # allows you to put the t/*.t tests next to any deeper xs or pm files that need testing.
257             # todo: filter out DIR Makefile.PL Build.PL directories
258             # todo: filter out MYLIBEXT directories
259              
260 0 0         unless ($attribs{TESTS}) {
261 0           my %dir = map { ($_ => $_) } @{$self->{DIR}};
  0            
  0            
262 0           my %found;
263             File::Find::find(
264             {
265             no_chdir => 1,
266             wanted => sub {
267 0 0   0     if (-d $_) {
268 0 0         if ($dir{$_}) {
269 0           $File::Find::prune = 1;
270             }
271              
272 0           return;
273             }
274              
275 0 0 0       if (/\.t$/ && -f $File::Find::name) {
276 0 0         if ($File::Find::dir =~ /^t$/) {
    0          
    0          
    0          
277 0           $found{File::Spec->catfile('t', '*.t')}=1;
278             } elsif ($File::Find::dir =~ /^t\//) {
279 0           $found{File::Spec->catfile('t', $', '*.t')}=1;
280             } elsif ($File::Find::dir =~ /\/t$/) {
281 0           $found{File::Spec->catfile($`, 't', '*.t')}=1;
282             } elsif ($File::Find::dir =~ /\/t\//) {
283 0           $found{File::Spec->catfile($`, 't', $', '*.t')}=1;
284             }
285             }
286             },
287             },
288 0           grep -d $_, 't', 'lib', 'bin');
289              
290 0           $attribs{TESTS} = join(' ', sort keys %found);
291             }
292              
293 0 0         print STDERR "debug: test attribs TESTS = $attribs{TESTS}\n" if $debugf;
294              
295 0           $self->MM::test(%attribs);
296             }
297              
298             #sub init_PM {
299             # my ($self) = @_;
300             # $self->MM::init_PM;
301             #}
302              
303             =head2 B<$obj->init_PM>
304              
305             This function is exported by the :MY tag. It extends/replaces the MM
306             init_PM.
307              
308             This method only puts .pm files into the $self PM setting.
309              
310             The default init_PM puts all files into the $self PM setting.
311              
312             =cut
313              
314             sub init_PM {
315 0     0 1   my ($self) = @_;
316              
317             # Some larger extensions often wish to install a number of *.pm/pl
318             # files into the library in various locations.
319              
320             # The attribute PMLIBDIRS holds an array reference which lists
321             # subdirectories which we should search for library files to
322             # install. PMLIBDIRS defaults to [ 'lib', $self->{BASEEXT} ]. We
323             # recursively search through the named directories (skipping any
324             # which don't exist or contain Makefile.PL files).
325              
326             # For each *.pm or *.pl file found $self->libscan() is called with
327             # the default installation path in $_[1]. The return value of
328             # libscan defines the actual installation location. The default
329             # libscan function simply returns the path. The file is skipped
330             # if libscan returns false.
331              
332             # The default installation location passed to libscan in $_[1] is:
333             #
334             # ./*.pm => $(INST_LIBDIR)/*.pm
335             # ./xyz/... => $(INST_LIBDIR)/xyz/...
336             # ./lib/... => $(INST_LIB)/...
337             #
338             # In this way the 'lib' directory is seen as the root of the actual
339             # perl library whereas the others are relative to INST_LIBDIR
340             # (which includes PARENT_NAME). This is a subtle distinction but one
341             # that's important for nested modules.
342              
343 0 0         unless( $self->{PMLIBDIRS} ) {
344 0 0         if( $Is{VMS} ) {
345             # Avoid logical name vs directory collisions
346 0           $self->{PMLIBDIRS} = ['./lib', "./$self->{BASEEXT}"];
347             }
348             else {
349 0           $self->{PMLIBDIRS} = ['lib', $self->{BASEEXT}];
350             }
351             }
352              
353             #only existing directories that aren't in $dir are allowed
354              
355             # Avoid $_ wherever possible:
356 0           my %dir = map { ($_ => 1) } @{$self->{DIR}};
  0            
  0            
357 0           my @todel;
358 0           for (my $ii = 0; $ii < @{$self->{PMLIBDIRS}}; $ii++) {
  0            
359 0           my $pmlibdir = $self->{PMLIBDIRS}[$ii];
360 0 0 0       push @todel, $ii if !-d $pmlibdir || $dir{$pmlibdir};
361             }
362 0 0         delete @{$self->{PMLIBDIRS}}[@todel] if @todel;
  0            
363              
364 0 0 0       @{$self->{PMLIBPARENTDIRS}} = ('lib') unless $self->{PMLIBPARENTDIRS} && @{$self->{PMLIBPARENTDIRS}};
  0            
  0            
365              
366 0 0 0       return if ($self->{PM} && $self->{ARGS}{PM}) || !@{$self->{PMLIBDIRS}};
  0   0        
367              
368 0 0         print "Searching PMLIBDIRS: @{$self->{PMLIBDIRS}}\n" if 2 <= $ExtUtils::MakeMaker::Verbose;
  0            
369              
370 0           my $parentlibs_re = join '|', @{$self->{PMLIBPARENTDIRS}};
  0            
371 0           $parentlibs_re = qr/$parentlibs_re/;
372              
373             File::Find::find(sub {
374 0 0   0     if (-d $_){
375 0 0         unless ($self->libscan($_)){
376 0           $File::Find::prune = 1;
377             }
378 0           return;
379             }
380              
381 0 0 0       return if /\#/ ||
      0        
      0        
382             /~$/ || # emacs temp files
383             /,v$/ || # RCS files
384             /\.swp$/; # vim swap files
385              
386 0 0         return unless /\.pm$/;
387              
388 0           my $path = $File::Find::name;
389 0           my $prefix = $self->{INST_LIBDIR};
390 0           my $striplibpath;
391              
392 0 0         $prefix = $self->{INST_LIB} if ($striplibpath = $path) =~ s{^(\W*)(?:$parentlibs_re)\W}{$1}i;
393              
394 0           my($inst) = $self->catfile($prefix,$striplibpath);
395 0           local($_) = $inst; # for backwards compatibility
396 0           $inst = $self->libscan($inst);
397 0 0         print "libscan($path) => '$inst'\n" if 2 <= $ExtUtils::MakeMaker::Verbose;
398              
399 0 0         return unless $inst;
400              
401 0           $self->{PM}{$path} = $inst;
402 0           }, @{$self->{PMLIBDIRS}});
  0            
403             }
404              
405             =head2 B<$obj->init_XS>
406              
407             This function is exported by the :MY tag. It extends the MM init_XS.
408              
409             This sets up macros INST_STATIC, INST_DYNAMIC, INST_BOOT, and uses
410             INST_ARCHLIB.
411              
412             note: init_dirscan will set XS and DIR to defaults. However, init_dirscan
413             has limitations, mostly that it cannot handle large projects.
414              
415             input and output:
416              
417             XS as documented in ExtUtils::MakeMaker, this is a hashref.
418             the key is the relative path of the xs file.
419             the value can be the target c file as documented.
420             EXTENSION: the value can be simply 1 if the c file can be
421             simply generated from the xs file.
422             EXTENSION: the value can be the package name XXX::YYY::ZZZ for
423             the xs code.
424             EXTENSION: the value can be an array of c dependencies
425             EXTENSION: the XS hash is reworked by this code.
426              
427             DIR as documented, an arrayref of subdirectories containing
428             Makefile.PL
429             EXTENSION: may be an arrayref of pathnames for Makefile.PLs
430             and Build.PLs init_dirscan only looks one deep for
431             Makefile.PLs, and does not look for Build.PLs.
432             With this extension, you could do a recursive file
433             find for all Makefile.PLs and Build.PLs no matter
434             how deep.
435            
436             MYEXTLIB
437              
438             output
439              
440             MY_XS_DEPENDENCIES
441             MY_SUBDIRS
442             MY_EXTENSION_LIBS
443             MY_XS_TARGETS
444              
445             =cut
446              
447             sub init_xs {
448 0     0 0   my $self = shift;
449              
450 0 0         print STDERR "debug: ", Data::Dumper::Dumper($self), "\n" if $debugf;
451              
452 0 0         my %c_files = $self->{C} ? map { ( $_ => 1 ) } (ref($self->{C}) ? @{ $self->{C} } : split(/\s+/, $self->{C})) : ();
  0 0          
  0            
453              
454 0           foreach my $xs ( keys %{ $self->{XS} } ) {
  0            
455 0           my $c_file = $self->{XS}{$xs};
456 0           my $package;
457              
458 0 0         if (ref($c_file) eq 'ARRAY') {
459             ## $c_file is a list of dependencies
460 0           $self->{MY_XS_DEPENDENCIES}{$xs} = $c_file;
461 0           undef $c_file;
462             }
463              
464             # we need the package name so that we can generate the correct bootstrap
465              
466 0 0 0       $package = $c_file if $c_file && $c_file =~ /::/;
467              
468 0 0 0       if (!$c_file || $c_file !~ /\.c$/) {
469             # ignore the value from XS, generate the c file name from the xs file name
470 0           ($c_file = $xs) =~ s/\.xs$/.c/;
471 0           $self->{XS}{$xs} = $c_file;
472             }
473              
474 0 0         $c_files{$c_file}++ if $c_file;
475              
476 0 0 0       if (!$package && (!$c_file || $c_file =~ /\.c$/)) {
      0        
477             # scan the xs file for the package name
478              
479 0 0         if (open(my $fh, $xs)) {
480 0           while (<$fh>) {
481 0           chomp;
482 0 0         if (/^\s*MODULE\s*=\s*(\S+)\s+PACKAGE\s*=\s*(\S+)/) {
483 0           $package = $1;
484 0           last;
485             }
486             }
487 0           close($fh);
488             }
489             }
490              
491 0 0 0       if (!$package && $xs =~ /^lib\//) {
492             # generate the package name from the file name since the file name looks sane
493              
494 0           ($package = $') =~ s/\.xs//;
495 0           $package =~ s/\//::/g;
496             }
497              
498 0 0         $self->{MY_XS_TARGETS}{$xs} = $package unless $self->{MY_XS_TARGETS}{$xs};
499             }
500              
501 0           $self->{C} = [ sort keys %c_files ]; # has_link_code may rely on this
502              
503 0 0 0       if ($self->{DIR} || $self->{MYEXTLIB}) {
504 0           my @myextlib = ref($self->{MYEXTLIB}) eq 'ARRAY'
505 0 0         ? @{ $self->{MYEXTLIB} }
506             : split /\s+/, $self->{MYEXTLIB};
507              
508 0           my %PL_dirs;
509             my %cleanup_makefile_dirs;
510 0           my %c_extension_libs;
511 0           my @c_extension_libs;
512 0           my @c_myextlib_replacement;
513              
514 0           foreach my $subdir ( @{ $self->{DIR} }, @myextlib ) {
  0            
515 0 0         if ($subdir =~ /^(.+)\/lib([^\/]+)\.(?:a|so)$/) {
    0          
516             # $subdir is actually the pathname of a lib
517 0           $c_extension_libs{$1}{$subdir} = 1;
518 0           $cleanup_makefile_dirs{$1} = 1;
519 0           push @c_extension_libs, $2;
520 0           push @c_myextlib_replacement, $subdir;
521             } elsif ($subdir =~ /\/(?:Makefile|Build)\.PL$/) {
522 0           $PL_dirs{$`} = 1;
523 0 0         $cleanup_makefile_dirs{$`} = 1 if -f "$`/Makefile.PL";
524             }
525             }
526              
527 0           $self->{MY_CLEANUP_MAKEFILE_DIRS} = [ keys %cleanup_makefile_dirs ];
528              
529 0 0         if (%c_extension_libs) {
530             # have to re-write the DIR setting
531 0 0         $self->{DIR} = %PL_dirs ? [ keys %PL_dirs ] : [ ];
532              
533 0           $self->{MY_EXTENSION_LIBS} = \%c_extension_libs;
534 0           $self->{MY_EXTENSION_LIBS_IN_ORDER} = \@c_extension_libs;
535 0           $self->{MYEXTLIB} = join(' ', @c_myextlib_replacement);
536             }
537             }
538              
539 0           $self->MM::init_xs;
540              
541 0 0         print STDERR "debug: my init_xs, ", join(',', sort keys %{ $self->{MY_XS_TARGETS} }), "\n" if $debugf;
  0            
542 0 0         print STDERR "debug: my init_xs, before INST_DYNAMIC=$self->{INST_DYNAMIC}\n" if $debugf;
543              
544 0 0         if ($self->has_link_code) {
545 0 0         if ($self->{MY_XS_TARGETS}) {
546 0           my @so_targets;
547             my @bs_targets;
548 0           while (my ($xs, $place) = each %{ $self->{MY_XS_TARGETS} }) {
  0            
549 0           $place =~ s/::/\//g;
550 0           (my $basename = $place) =~ s/^.*\///;
551 0           push @so_targets, $self->catdir('$(INST_ARCHLIB)', 'auto', $place, $basename.'.$(DLEXT)');
552 0           push @bs_targets, $self->catdir('$(INST_ARCHLIB)', 'auto', $place, $basename.'.bs');
553             }
554 0           $self->{INST_DYNAMIC} = join(' ', @so_targets);
555 0           $self->{INST_BOOT} = join(' ', @bs_targets);
556             }
557             }
558              
559 0 0         print STDERR "debug: my init_xs, after INST_DYNAMIC=$self->{INST_DYNAMIC}\n" if $debugf;
560             }
561              
562             =head2 B<$obj->clean_subdirs>
563              
564             This function is exported by the :MY tag. It replaces the MM clean_subdirs.
565              
566             This handles cleaning up subdirectories with their own Makefile, such
567             as Makefile.PL, Build.PL, and extension libraries.
568              
569             =cut
570              
571             sub clean_subdirs {
572 0     0 1   my $self = shift;
573              
574             #$self->MM::clean_subdirs_target(@_);
575              
576 0           my $make = '
577             clean_subdirs :
578             ';
579 0           foreach my $subdir (@{ $self->{MY_CLEANUP_MAKEFILE_DIRS} } ) {
  0            
580 0           $make .= '
581             cd '.$subdir.' && $(MAKE) clean
582             ';
583             }
584              
585 0           return $make;
586             }
587              
588             =head2 B<$obj->clean>
589              
590             This function is exported by the :MY tag. It extends the MM clean.
591              
592             This handles cleaning up all the debris left by building .xs files,
593             without having to have the .c files named explicitly in the $self
594             XS setting.
595              
596             =cut
597              
598             sub clean {
599 0     0 1   my ($self, %attribs) = @_;
600              
601 0           my $make = $self->MM::clean(%attribs);
602 0 0         return $make unless $self->{XS};
603              
604 0           my @objects;
605 0           while (my ($xs, $c_file) = each %{ $self->{XS} }) {
  0            
606 0           (my $object = $c_file) =~ s/\.c$/\$(OBJ_EXT)/;
607 0           (my $bootstrap = $c_file) =~ s/\.c$/.bs/;
608 0           push @objects, "$object $bootstrap";
609             }
610              
611 0 0         if (@objects) {
612 0           my $objects = join(" \\\n\t ", @objects);
613 0           $make =~ s/\*\$\(OBJ_EXT\)/$& \\\n\t $objects \\\n\t /;
614             }
615              
616 0           return $make;
617             }
618              
619             =head2 B<$obj->postamble>
620              
621             This function is exported by the :MY tag. It extends the MM postamble.
622              
623             This handles setting up the compilation of the .xs files with a VERSION
624             for each. The bootstrap code checks the VERSION, and it has to match.
625              
626             =cut
627              
628             sub postamble {
629 0     0 1   my $self = shift;
630              
631 0 0         print STDERR "debug: my postamble, ", join(',', sort keys %{ $self->{MY_XS_TARGETS} }), "\n" if $debugf;
  0            
632 0 0         print STDERR "debug: ", Data::Dumper::Dumper($self), "\n" if $debugf;
633              
634 0           my $make = '';
635 0           foreach my $subdir (keys %{ $self->{MY_EXTENSION_LIBS} }) {
  0            
636 0           foreach my $extlib (keys %{ $self->{MY_EXTENSION_LIBS}{$subdir} }) {
  0            
637 0           $make .= '
638              
639             '.$extlib.':
640             BUILDDIR=`pwd`/; \
641             cd '.$subdir.' && \
642             $(MAKE) AR="$(FULL_AR)" ARFLAGS="$(AR_STATIC_ARGS)" RANLIB="$(RANLIB)" CC="$(CCCMD)" CPPFLAGS="-I$(PERL_INC) $(PASTHRU_DEFINE) $(DEFINE)" CFLAGS="$(CCCDLFLAGS)" LD="$(LD)" LDFLAGS="$(LDDLFLAGS)" all
643              
644             subdirs-test ::
645             -BUILDDIR=`pwd`/; \
646             cd '.$subdir.' && \
647             $(MAKE) AR="$(FULL_AR)" ARFLAGS="$(AR_STATIC_ARGS)" RANLIB="$(RANLIB)" CC="$(CCCMD)" CPPFLAGS="-I$(PERL_INC) $(PASTHRU_DEFINE) $(DEFINE)" CFLAGS="$(CCCDLFLAGS)" LD="$(LD)" LDFLAGS="$(LDDLFLAGS)" test
648              
649             pure_perl_install ::
650             -BUILDDIR=`pwd`/; \
651             cd '.$subdir.' && \
652             $(MAKE) AR="$(FULL_AR)" ARFLAGS="$(AR_STATIC_ARGS)" RANLIB="$(RANLIB)" CC="$(CCCMD)" CPPFLAGS="-I$(PERL_INC) $(PASTHRU_DEFINE) $(DEFINE)" CFLAGS="$(CCCDLFLAGS)" LD="$(LD)" LDFLAGS="$(LDDLFLAGS)" \
653             INSTALLPRIVLIB=$(DESTINSTALLPRIVLIB) INSTALLARCHLIB=$(DESTINSTALLARCHLIB) INSTALLBIN=$(DESTINSTALLBIN) INSTALLSCRIPT=$(DESTINSTALLSCRIPT) INSTALLMAN1DIR=$(DESTINSTALLMAN1DIR) INSTALLMAN3DIR=$(DESTINSTALLMAN3DIR) \
654             install
655              
656             pure_site_install ::
657             -BUILDDIR=`pwd`/; \
658             cd '.$subdir.' && \
659             $(MAKE) AR="$(FULL_AR)" ARFLAGS="$(AR_STATIC_ARGS)" RANLIB="$(RANLIB)" CC="$(CCCMD)" CPPFLAGS="-I$(PERL_INC) $(PASTHRU_DEFINE) $(DEFINE)" CFLAGS="$(CCCDLFLAGS)" LD="$(LD)" LDFLAGS="$(LDDLFLAGS)" \
660             INSTALLPRIVLIB=$(DESTINSTALLSITELIB) INSTALLARCHLIB=$(DESTINSTALLSITEARCHLIB) INSTALLBIN=$(DESTINSTALLSITEBIN) INSTALLSCRIPT=$(DESTINSTALLSITESCRIPT) INSTALLMAN1DIR=$(DESTINSTALLSITEMAN1DIR) INSTALLMAN3DIR=$(DESTINSTALLSITEMAN3DIR) \
661             install
662              
663             pure_vendor_install ::
664             -BUILDDIR=`pwd`/; \
665             cd '.$subdir.' && \
666             $(MAKE) AR="$(FULL_AR)" ARFLAGS="$(AR_STATIC_ARGS)" RANLIB="$(RANLIB)" CC="$(CCCMD)" CPPFLAGS="-I$(PERL_INC) $(PASTHRU_DEFINE) $(DEFINE)" CFLAGS="$(CCCDLFLAGS)" LD="$(LD)" LDFLAGS="$(LDDLFLAGS)" \
667             INSTALLPRIVLIB=$(DESTINSTALLVENDORLIB) INSTALLARCHLIB=$(DESTINSTALLVENDORARCHLIB) INSTALLBIN=$(DESTINSTALLVENDORBIN) INSTALLSCRIPT=$(DESTINSTALLVENDORSCRIPT) INSTALLMAN1DIR=$(DESTINSTALLVENDORMAN1DIR) INSTALLMAN3DIR=$(DESTINSTALLVENDORMAN3DIR) \
668             install
669             ';
670              
671             }
672             }
673            
674 0 0         return $make unless $self->{MY_XS_TARGETS};
675              
676 0           my @dirs;
677             my @compile;
678 0           while (my ($xs, $place) = each %{ $self->{MY_XS_TARGETS} }) {
  0            
679 0 0         print STDERR "debug: postamble $xs $place\n" if $debugf;
680             #next if $place eq $self->{NAME};
681              
682 0           (my $place_dir = $place) =~ s/::/\//g;
683 0           (my $basename = $place_dir) =~ s/^.*\///;
684 0           (my $object = $xs) =~ s/\..*?$/.o/;
685 0           (my $c_file = $xs) =~ s/\..*?$/.c/;
686 0           (my $xs_basename = $xs) =~ s/\..*?$//;
687 0           (my $bootstrap = $xs) =~ s/\..*?$/.bs/;
688 0           my $INST_ARCHAUTODIR = $self->catdir('$(INST_ARCHLIB)', 'auto', $place_dir);
689              
690 0 0         push @dirs, $INST_ARCHAUTODIR unless $place eq $self->{NAME};
691              
692 0           my $version = $self->parse_version("lib/${place_dir}.pm");
693 0 0         print STDERR "debug: place_dir=$place_dir\n" if $debugf;
694 0 0         print STDERR "debug: $place is $version\n" if $debugf;
695 0 0         print STDERR "debug: CONST_CCCMD=$$self{CONST_CCCMD}\n" if $debugf;
696 0           my $cccmd = $self->{CONST_CCCMD};
697 0           $cccmd =~ s/^\s*CCCMD\s*=\s*//;
698 0           $cccmd =~ s/\$\(DEFINE_VERSION\)/-DVERSION=\\"$version\\"/;
699 0           $cccmd =~ s/\$\(XS_DEFINE_VERSION\)/-DXS_VERSION=\\"$version\\"/;
700 0 0         print STDERR "debug: new CCCMD=$cccmd\n" if $debugf;
701              
702 0           my $tmp_str = '
703              
704             '.$object.' : '.$xs.'
705             $(XSUBPPRUN) $(XSPROTOARG) $(XSUBPPARGS) $*.xs > $*.xsc && $(MV) $*.xsc $*.c
706             '.$cccmd.' $(CCCDLFLAGS) "-I$(PERL_INC)" $(PASTHRU_DEFINE) $(DEFINE) -o $@ $*.c
707             ';
708 0           push @compile, $tmp_str;
709             }
710              
711 0           my @exists = map { $_.'$(DFSEP).exists' } @dirs;
  0            
712              
713 0           $make .= sprintf <<'MAKE_FRAG', join(' ', @exists);
714              
715             blibdirs : %s
716              
717             MAKE_FRAG
718              
719 0           $make .= $self->dir_target(@dirs);
720 0           $make .= join('', @compile);
721              
722 0           return $make;
723             }
724              
725             =head2 B<$obj->dynamic_bs>
726              
727             This function is exported by the :MY tag. It extends the MM dynamic_bs.
728              
729             This handles the .bs files for multiple non-chained .xs files.
730              
731             =cut
732              
733             sub dynamic_bs {
734 0     0 1   my($self, %attribs) = @_;
735              
736 0 0         return $self->MM::dynamic_bs(%attribs) unless $self->{MY_XS_TARGETS};
737 0 0         print STDERR "debug: my dynamic_bs, ", join(',', sort keys %{ $self->{MY_XS_TARGETS} }), "\n" if $debugf;
  0            
738              
739 0 0         return '
740             BOOTSTRAP =
741             ' unless $self->has_link_code();
742              
743 0           my $str = '
744             BOOTSTRAP = $(BASEEXT).bs
745              
746             # As Mkbootstrap might not write a file (if none is required)
747             # we use touch to prevent make continually trying to remake it.
748             # The DynaLoader only reads a non-empty file.
749             ';
750              
751 0           while (my ($xs, $place) = each %{ $self->{MY_XS_TARGETS} }) {
  0            
752 0           $place =~ s/::/\//g;
753 0           (my $basename = $place) =~ s/^.*\///;
754 0           (my $object = $xs) =~ s/\..*?$/.o/;
755 0           (my $xs_basename = $xs) =~ s/\..*?$//;
756 0           (my $bootstrap = $xs) =~ s/\..*?$/.bs/;
757 0           my $INST_ARCHAUTODIR = $self->catdir('$(INST_ARCHLIB)', 'auto', $place);
758 0           my $install_target = $self->catdir($INST_ARCHAUTODIR, $basename.'.bs');
759 0           my $exists = $self->catdir($INST_ARCHAUTODIR, '.exists');
760              
761             #my $make_target = $Is{VMS} ? '$(MMS$TARGET)' : '$@';
762 0           my $make_target = '$@';
763              
764 0           my $tmp_str = '
765             '.$bootstrap.' : $(FIRST_MAKEFILE) $(BOOTDEP) '.$exists.'
766             $(NOECHO) $(ECHO) "Running Mkbootstrap for '.$xs.' ($(BSLOADLIBS))"
767             $(NOECHO) $(PERLRUN) "-MExtUtils::Mkbootstrap" -e "Mkbootstrap(\''.$xs_basename.'\',\'$(BSLOADLIBS)\');"
768             $(NOECHO) $(TOUCH) %s
769             $(CHMOD) $(PERM_RW) %s
770              
771             '.$install_target.' : '.$bootstrap.' '.$exists.'
772             $(NOECHO) $(RM_RF) %s
773             - $(CP) '.$bootstrap.' %s
774             $(CHMOD) $(PERM_RW) %s
775             ';
776              
777 0           $str .= sprintf $tmp_str, ($make_target) x 5;
778             }
779              
780 0           return $str;
781             }
782              
783             =head2 B<$obj->dynamic_lib>
784              
785             This function is exported by the :MY tag. It extends the MM dynamic_lib.
786              
787             This handles building multiple .xs files into .so.
788              
789             =cut
790              
791             sub dynamic_lib {
792 0     0 1   my($self, %attribs) = @_;
793              
794 0 0         return $self->MM::dynamic_lib(%attribs) unless $self->{MY_XS_TARGETS};
795              
796 0 0         print STDERR "debug: my dynamic_lib, ", join(',', sort keys %{ $self->{MY_XS_TARGETS} }), "\n" if $debugf;
  0            
797              
798 0 0         return '' unless $self->needs_linking(); #might be because of a subdir
799              
800 0 0         return '' unless $self->has_link_code;
801              
802 0           my(@m);
803 0   0       my($otherldflags) = $attribs{OTHERLDFLAGS} || "";
804 0   0       my($inst_dynamic_dep) = $attribs{INST_DYNAMIC_DEP} || "";
805 0   0       my($armaybe) = $attribs{ARMAYBE} || $self->{ARMAYBE} || ":";
806 0           my($ldfrom) = '$(LDFROM)';
807 0 0 0       $armaybe = 'ar' if ($Is{OSF} and $armaybe eq ':');
808 0 0         my $ld_opt = $Is{OS2} ? '$(OPTIMIZE) ' : ''; # Useful on other systems too?
809 0 0         my $ld_fix = $Is{OS2} ? '|| ( $(RM_F) $@ && sh -c false )' : '';
810 0           push(@m,'
811             # This section creates the dynamically loadable $(INST_DYNAMIC)
812             # from $(OBJECT) and possibly $(MYEXTLIB).
813             ARMAYBE = '.$armaybe.'
814             OTHERLDFLAGS = '.$ld_opt.$otherldflags.'
815             INST_DYNAMIC_DEP = '.$inst_dynamic_dep.'
816             INST_DYNAMIC_FIX = '.$ld_fix.'
817             ');
818 0           while (my ($xs, $place) = each %{ $self->{MY_XS_TARGETS} }) {
  0            
819 0           $place =~ s/::/\//g;
820 0           (my $basename = $place) =~ s/^.*\///;
821 0           (my $object = $xs) =~ s/\..*?$/.o/;
822 0           (my $bootstrap = $xs) =~ s/\..*?$/.bs/;
823 0           my $INST_ARCHAUTODIR = $self->catdir('$(INST_ARCHLIB)', 'auto', $place);
824 0           my $target = $self->catdir($INST_ARCHAUTODIR, $basename.'.$(DLEXT)');
825 0           my $exists = $self->catdir($INST_ARCHAUTODIR, '.exists');
826              
827 0           push(@m,'
828              
829             '.$target.': '.$object.' $(MYEXTLIB) '.$bootstrap.' '.$exists.' $(EXPORT_LIST) $(PERL_ARCHIVE) $(PERL_ARCHIVE_AFTER) $(INST_DYNAMIC_DEP)
830             ');
831              
832 0 0         if ($armaybe ne ':') {
833 0           $ldfrom = 'tmp$(LIB_EXT)';
834 0           push(@m,' $(ARMAYBE) cr '.$ldfrom.' $(OBJECT)'."\n");
835 0           push(@m,' $(RANLIB) '."$ldfrom\n");
836             }
837 0 0         $ldfrom = "-all $ldfrom -none" if $Is{OSF};
838              
839             # The IRIX linker doesn't use LD_RUN_PATH
840 0 0 0       my $ldrun = $Is{IRIX} && $self->{LD_RUN_PATH} ? '-rpath "'.$self->{LD_RUN_PATH}.'"' : '';
841              
842             # For example in AIX the shared objects/libraries from previous builds
843             # linger quite a while in the shared dynalinker cache even when nobody
844             # is using them. This is painful if one for instance tries to restart
845             # a failed build because the link command will fail unnecessarily 'cos
846             # the shared object/library is 'busy'.
847 0           push(@m,' $(RM_F) $@
848             ');
849              
850 0           my $libs = '$(LDLOADLIBS)';
851              
852 0 0 0       if (($Is{NetBSD} || $Is{Interix}) && $Config{'useshrplib'} eq 'true') {
      0        
853             # Use nothing on static perl platforms, and to the flags needed
854             # to link against the shared libperl library on shared perl
855             # platforms. We peek at lddlflags to see if we need -Wl,-R
856             # or -R to add paths to the run-time library search path.
857 0 0         if ($Config{'lddlflags'} =~ /-Wl,-R/) {
    0          
858 0           $libs .= ' -L$(PERL_INC) -Wl,-R$(INSTALLARCHLIB)/CORE -Wl,-R$(PERL_ARCHLIB)/CORE -lperl';
859             } elsif ($Config{'lddlflags'} =~ /-R/) {
860 0           $libs .= ' -L$(PERL_INC) -R$(INSTALLARCHLIB)/CORE -R$(PERL_ARCHLIB)/CORE -lperl';
861             }
862             }
863              
864 0           my $ld_run_path_shell = "";
865 0 0         if ($self->{LD_RUN_PATH} ne "") {
866 0           $ld_run_path_shell = 'LD_RUN_PATH="$(LD_RUN_PATH)" ';
867             }
868              
869 0           my @extlibs;
870 0           push @extlibs, map { "-L$_" } sort keys %{ $self->{MY_EXTENSION_LIBS} };
  0            
  0            
871 0           push @extlibs, map { "-l$_" } @{ $self->{MY_EXTENSION_LIBS_IN_ORDER} };
  0            
  0            
872              
873 0           push @m, sprintf <<'MAKE', $ld_run_path_shell, $ldrun, '$<', join(' ', @extlibs), $libs;
874             %s$(LD) %s $(LDDLFLAGS) %s $(OTHERLDFLAGS) -o $@ %s \
875             $(PERL_ARCHIVE) %s $(PERL_ARCHIVE_AFTER) $(EXPORT_LIST) \
876             $(INST_DYNAMIC_FIX)
877             MAKE
878              
879 0           push @m, <<'MAKE';
880             $(CHMOD) $(PERM_RWX) $@
881             MAKE
882             }
883              
884 0           return join('',@m);
885             }
886              
887             1;
888              
889             __END__