Update CPANPLUS::Dist::Build to 0.08
[perl.git] / lib / CPANPLUS / Dist / Build.pm
1 package CPANPLUS::Dist::Build;
2
3 use strict;
4 use warnings;
5 use vars    qw[@ISA $STATUS $VERSION];
6 @ISA =      qw[CPANPLUS::Dist];
7
8 use CPANPLUS::inc;
9 use CPANPLUS::Internals::Constants;
10
11 ### these constants were exported by CPANPLUS::Internals::Constants
12 ### in previous versions.. they do the same though. If we want to have
13 ### a normal 'use' here, up the dependency to CPANPLUS 0.056 or higher
14 BEGIN { 
15     require CPANPLUS::Dist::Build::Constants;
16     CPANPLUS::Dist::Build::Constants->import()
17         if not __PACKAGE__->can('BUILD') && __PACKAGE__->can('BUILD_DIR');
18 }
19
20 use CPANPLUS::Error;
21
22 use Config;
23 use FileHandle;
24 use Cwd;
25 use version;
26
27 use IPC::Cmd                    qw[run];
28 use Params::Check               qw[check];
29 use Module::Load::Conditional   qw[can_load check_install];
30 use Locale::Maketext::Simple    Class => 'CPANPLUS', Style => 'gettext';
31
32 local $Params::Check::VERBOSE = 1;
33
34 $VERSION = '0.08';
35
36 =pod
37
38 =head1 NAME
39
40 CPANPLUS::Dist::Build - CPANPLUS plugin to install packages that use Build.PL
41
42 =head1 SYNOPSIS
43
44     my $build = CPANPLUS::Dist->new(
45                                 format  => 'CPANPLUS::Dist::Build',
46                                 module  => $modobj,
47                             );
48                             
49     $build->prepare;    # runs Build.PL                            
50     $build->create;     # runs build && build test
51     $build->install;    # runs build install
52
53
54 =head1 DESCRIPTION
55
56 C<CPANPLUS::Dist::Build> is a distribution class for C<Module::Build>
57 related modules.
58 Using this package, you can create, install and uninstall perl
59 modules. It inherits from C<CPANPLUS::Dist>.
60
61 Normal users won't have to worry about the interface to this module,
62 as it functions transparently as a plug-in to C<CPANPLUS> and will 
63 just C<Do The Right Thing> when it's loaded.
64
65 =head1 ACCESSORS
66
67 =over 4
68
69 =item C<parent()>
70
71 Returns the C<CPANPLUS::Module> object that parented this object.
72
73 =item C<status()>
74
75 Returns the C<Object::Accessor> object that keeps the status for
76 this module.
77
78 =back
79
80 =head1 STATUS ACCESSORS
81
82 All accessors can be accessed as follows:
83     $build->status->ACCESSOR
84
85 =over 4
86
87 =item C<build_pl ()>
88
89 Location of the Build file.
90 Set to 0 explicitly if something went wrong.
91
92 =item C<build ()>
93
94 BOOL indicating if the C<Build> command was successful.
95
96 =item C<test ()>
97
98 BOOL indicating if the C<Build test> command was successful.
99
100 =item C<prepared ()>
101
102 BOOL indicating if the C<prepare> call exited succesfully
103 This gets set after C<perl Build.PL>
104
105 =item C<distdir ()>
106
107 Full path to the directory in which the C<prepare> call took place,
108 set after a call to C<prepare>. 
109
110 =item C<created ()>
111
112 BOOL indicating if the C<create> call exited succesfully. This gets
113 set after C<Build> and C<Build test>.
114
115 =item C<installed ()>
116
117 BOOL indicating if the module was installed. This gets set after
118 C<Build install> exits successfully.
119
120 =item uninstalled ()
121
122 BOOL indicating if the module was uninstalled properly.
123
124 =item C<_create_args ()>
125
126 Storage of the arguments passed to C<create> for this object. Used
127 for recursive calls when satisfying prerequisites.
128
129 =item C<_install_args ()>
130
131 Storage of the arguments passed to C<install> for this object. Used
132 for recursive calls when satisfying prerequisites.
133
134 =back
135
136 =cut
137
138 =head1 METHODS
139
140 =head2 $bool = CPANPLUS::Dist::Build->format_available();
141
142 Returns a boolean indicating whether or not you can use this package
143 to create and install modules in your environment.
144
145 =cut
146
147 ### check if the format is available ###
148 sub format_available {
149     my $mod = "Module::Build";
150     unless( can_load( modules => { $mod => '0.2611' } ) ) {
151         error( loc( "You do not have '%1' -- '%2' not available",
152                     $mod, __PACKAGE__ ) );
153         return;
154     }
155
156     return 1;
157 }
158
159
160 =head2 $bool = $dist->init();
161
162 Sets up the C<CPANPLUS::Dist::Build> object for use.
163 Effectively creates all the needed status accessors.
164
165 Called automatically whenever you create a new C<CPANPLUS::Dist> object.
166
167 =cut
168
169 sub init {
170     my $dist    = shift;
171     my $status  = $dist->status;
172
173     $status->mk_accessors(qw[build_pl build test created installed uninstalled
174                              _create_args _install_args _prepare_args
175                              _mb_object _buildflags
176                             ]);
177
178     ### just in case 'format_available' didn't get called
179     require Module::Build;
180
181     return 1;
182 }
183
184 =pod
185
186 =head2 $bool = $dist->prepare([perl => '/path/to/perl', buildflags => 'EXTRA=FLAGS', force => BOOL, verbose => BOOL])
187
188 C<prepare> prepares a distribution, running C<Build.PL> 
189 and establishing any prerequisites this
190 distribution has.
191
192 The variable C<PERL5_CPANPLUS_IS_EXECUTING> will be set to the full path 
193 of the C<Build.PL> that is being executed. This enables any code inside
194 the C<Build.PL> to know that it is being installed via CPANPLUS.
195
196 After a succcesfull C<prepare> you may call C<create> to create the
197 distribution, followed by C<install> to actually install it.
198
199 Returns true on success and false on failure.
200
201 =cut
202
203 sub prepare {
204     ### just in case you already did a create call for this module object
205     ### just via a different dist object
206     my $dist = shift;
207     my $self = $dist->parent;
208
209     ### we're also the cpan_dist, since we don't need to have anything
210     ### prepared from another installer
211     $dist    = $self->status->dist_cpan if      $self->status->dist_cpan;
212     $self->status->dist_cpan( $dist )   unless  $self->status->dist_cpan;
213
214     my $cb   = $self->parent;
215     my $conf = $cb->configure_object;
216     my %hash = @_;
217
218     my $dir;
219     unless( $dir = $self->status->extract ) {
220         error( loc( "No dir found to operate on!" ) );
221         return;
222     }
223
224     my $args;
225     my( $force, $verbose, $buildflags, $perl);
226     {   local $Params::Check::ALLOW_UNKNOWN = 1;
227         my $tmpl = {
228             force           => {    default => $conf->get_conf('force'),
229                                     store   => \$force },
230             verbose         => {    default => $conf->get_conf('verbose'),
231                                     store   => \$verbose },
232             perl            => {    default => $^X, store => \$perl },
233             buildflags      => {    default => $conf->get_conf('buildflags'),
234                                     store   => \$buildflags },
235         };
236
237         $args = check( $tmpl, \%hash ) or return;
238     }
239
240     return 1 if $dist->status->prepared && !$force;
241
242     $dist->status->_prepare_args( $args );
243
244     ### chdir to work directory ###
245     my $orig = cwd();
246     unless( $cb->_chdir( dir => $dir ) ) {
247         error( loc( "Could not chdir to build directory '%1'", $dir ) );
248         return;
249     }
250
251     ### by now we've loaded module::build, and we're using the API, so
252     ### it's safe to remove CPANPLUS::inc from our inc path, especially
253     ### because it can trip up tests run under taint (just like EU::MM).
254     ### turn off our PERL5OPT so no modules from CPANPLUS::inc get
255     ### included in make test -- it should build without.
256     ### also, modules that run in taint mode break if we leave
257     ### our code ref in perl5opt
258     ### XXX we've removed the ENV settings from cp::inc, so only need
259     ### to reset the @INC
260     #local $ENV{PERL5OPT} = CPANPLUS::inc->original_perl5opt;
261     #local $ENV{PERL5LIB} = CPANPLUS::inc->original_perl5lib;
262     local @INC           = CPANPLUS::inc->original_inc;
263
264     ### this will generate warnings under anything lower than M::B 0.2606
265     my %buildflags = $dist->_buildflags_as_hash( $buildflags );
266     $dist->status->_buildflags( $buildflags );
267
268     my $fail;
269     RUN: {
270         # Wrap the exception that may be thrown here (should likely be
271         # done at a much higher level).
272         my $prep_output;
273
274         my $env = 'ENV_CPANPLUS_IS_EXECUTING';
275         local $ENV{$env} = BUILD_PL->( $dir );
276
277         unless ( scalar run(    command => [$perl, BUILD_PL->($dir), $buildflags],
278                                 buffer  => \$prep_output,
279                                 verbose => $verbose ) 
280         ) {
281             error( loc( "Build.PL failed: %1", $prep_output ) );
282             $fail++; last RUN;
283         }
284
285         msg( $prep_output, 0 );
286
287         $self->status->prereqs( $dist->_find_prereqs( verbose => $verbose, 
288                                                       dir => $dir, 
289                                                       perl => $perl,
290                                                       buildflags => $buildflags ) );
291
292     }
293     
294     ### send out test report? ###
295     if( $fail and $conf->get_conf('cpantest') ) {
296            $cb->_send_report( 
297             module  => $self,
298             failed  => $fail,
299             buffer  => CPANPLUS::Error->stack_as_string,
300             verbose => $verbose,
301             force   => $force,
302         ) or error(loc("Failed to send test report for '%1'",
303                     $self->module ) );
304     }
305
306     unless( $cb->_chdir( dir => $orig ) ) {
307         error( loc( "Could not chdir back to start dir '%1'", $orig ) );
308     }
309
310     ### save where we wrote this stuff -- same as extract dir in normal
311     ### installer circumstances
312     $dist->status->distdir( $self->status->extract );
313
314     return $dist->status->prepared( $fail ? 0 : 1 );
315 }
316
317 sub _find_prereqs {
318     my $dist = shift;
319     my $self = $dist->parent;
320     my $cb   = $self->parent;
321     my $conf = $cb->configure_object;
322     my %hash = @_;
323
324     my ($verbose, $dir, $buildflags, $perl);
325     my $tmpl = {
326         verbose => { default => $conf->get_conf('verbose'), store => \$verbose },
327         dir     => { default => $self->status->extract, store => \$dir },
328         perl    => { default => $^X, store => \$perl },
329         buildflags => { default => $conf->get_conf('buildflags'),
330                         store   => \$buildflags },
331     };
332     
333     my $args = check( $tmpl, \%hash ) or return;
334
335     my $prereqs = {};
336
337     my $safe_ver = version->new('0.31_03');
338
339     my $content;
340
341     if ( version->new( $Module::Build::VERSION ) >= $safe_ver ) {
342         # Use the new Build action 'prereq_data'
343         
344         unless ( scalar run(    command => [$perl, BUILD->($dir), 'prereq_data', $buildflags],
345                                 buffer  => \$content,
346                                 verbose => 0 ) 
347         ) {
348             error( loc( "Build 'prereq_data' failed: %1 %2", $!, $content ) );
349             return;
350         }
351
352     }
353     else {
354         my $file = File::Spec->catfile( $dir, '_build', 'prereqs' );
355         return unless -f $file;
356
357         my $fh = FileHandle->new();
358
359         unless( $fh->open( $file ) ) {
360            error( loc( "Cannot open '%1': %2", $file, $! ) );
361            return;
362         }
363         
364         $content = do { local $/; <$fh> };
365     }
366
367     my $bphash = eval $content;
368     return unless $bphash and ref $bphash eq 'HASH';
369     foreach my $type ('requires', 'build_requires') {
370        next unless $bphash->{$type} and ref $bphash->{$type} eq 'HASH';
371        $prereqs->{$_} = $bphash->{$type}->{$_} for keys %{ $bphash->{$type} };
372     }
373
374     # Temporary fix
375     delete $prereqs->{'perl'};
376
377     ### allows for a user defined callback to filter the prerequisite
378     ### list as they see fit, to remove (or add) any prereqs they see
379     ### fit. The default installed callback will return the hashref in
380     ### an unmodified form
381     ### this callback got added after cpanplus 0.0562, so use a 'can'
382     ### to find out if it's supported. For older versions, we'll just
383     ### return the hashref as is ourselves.
384     my $href    = $cb->_callbacks->can('filter_prereqs')
385                     ? $cb->_callbacks->filter_prereqs->( $cb, $prereqs )
386                     : $prereqs;
387
388     $self->status->prereqs( $href );
389
390     ### make sure it's not the same ref
391     return { %$href };
392 }
393
394 =pod
395
396 =head2 $dist->create([perl => '/path/to/perl', buildflags => 'EXTRA=FLAGS', prereq_target => TARGET, force => BOOL, verbose => BOOL, skiptest => BOOL])
397
398 C<create> preps a distribution for installation. This means it will
399 run C<Build> and C<Build test>.
400 This will also satisfy any prerequisites the module may have.
401
402 If you set C<skiptest> to true, it will skip the C<Build test> stage.
403 If you set C<force> to true, it will go over all the stages of the
404 C<Build> process again, ignoring any previously cached results. It
405 will also ignore a bad return value from C<Build test> and still allow
406 the operation to return true.
407
408 Returns true on success and false on failure.
409
410 You may then call C<< $dist->install >> on the object to actually
411 install it.
412
413 =cut
414
415 sub create {
416     ### just in case you already did a create call for this module object
417     ### just via a different dist object
418     my $dist = shift;
419     my $self = $dist->parent;
420
421     ### we're also the cpan_dist, since we don't need to have anything
422     ### prepared from another installer
423     $dist    = $self->status->dist_cpan if      $self->status->dist_cpan;
424     $self->status->dist_cpan( $dist )   unless  $self->status->dist_cpan;
425
426     my $cb   = $self->parent;
427     my $conf = $cb->configure_object;
428     my %hash = @_;
429
430     my $dir;
431     unless( $dir = $self->status->extract ) {
432         error( loc( "No dir found to operate on!" ) );
433         return;
434     }
435
436     my $args;
437     my( $force, $verbose, $buildflags, $skiptest, $prereq_target,
438         $perl, $prereq_format, $prereq_build);
439     {   local $Params::Check::ALLOW_UNKNOWN = 1;
440         my $tmpl = {
441             force           => {    default => $conf->get_conf('force'),
442                                     store   => \$force },
443             verbose         => {    default => $conf->get_conf('verbose'),
444                                     store   => \$verbose },
445             perl            => {    default => $^X, store => \$perl },
446             buildflags      => {    default => $conf->get_conf('buildflags'),
447                                     store   => \$buildflags },
448             skiptest        => {    default => $conf->get_conf('skiptest'),
449                                     store   => \$skiptest },
450             prereq_target   => {    default => '', store => \$prereq_target },
451             ### don't set the default format to 'build' -- that is wrong!
452             prereq_format   => {    #default => $self->status->installer_type,
453                                     default => '',
454                                     store   => \$prereq_format },
455             prereq_build    => {    default => 0, store => \$prereq_build },                                    
456         };
457
458         $args = check( $tmpl, \%hash ) or return;
459     }
460
461     return 1 if $dist->status->created && !$force;
462
463     $dist->status->_create_args( $args );
464
465     ### is this dist prepared?
466     unless( $dist->status->prepared ) {
467         error( loc( "You have not successfully prepared a '%2' distribution ".
468                     "yet -- cannot create yet", __PACKAGE__ ) );
469         return;
470     }
471
472     ### chdir to work directory ###
473     my $orig = cwd();
474     unless( $cb->_chdir( dir => $dir ) ) {
475         error( loc( "Could not chdir to build directory '%1'", $dir ) );
476         return;
477     }
478
479     ### by now we've loaded module::build, and we're using the API, so
480     ### it's safe to remove CPANPLUS::inc from our inc path, especially
481     ### because it can trip up tests run under taint (just like EU::MM).
482     ### turn off our PERL5OPT so no modules from CPANPLUS::inc get
483     ### included in make test -- it should build without.
484     ### also, modules that run in taint mode break if we leave
485     ### our code ref in perl5opt
486     ### XXX we've removed the ENV settings from cp::inc, so only need
487     ### to reset the @INC
488     #local $ENV{PERL5OPT} = CPANPLUS::inc->original_perl5opt;
489     #local $ENV{PERL5LIB} = CPANPLUS::inc->original_perl5lib;
490     local @INC           = CPANPLUS::inc->original_inc;
491
492     ### but do it *before* the new_from_context, as M::B seems
493     ### to be actually running the file...
494     ### an unshift in the block seems to be ignored.. somehow...
495     #{   my $lib = $self->best_path_to_module_build;
496     #    unshift @INC, $lib if $lib;
497     #}
498     unshift @INC, $self->best_path_to_module_build
499                 if $self->best_path_to_module_build;
500
501     ### this will generate warnings under anything lower than M::B 0.2606
502     my %buildflags = $dist->_buildflags_as_hash( $buildflags );
503     $dist->status->_buildflags( $buildflags );
504
505     my $fail; my $prereq_fail; my $test_fail;
506     RUN: {
507
508         ### this will set the directory back to the start
509         ### dir, so we must chdir /again/
510         my $ok = $dist->_resolve_prereqs(
511                         force           => $force,
512                         format          => $prereq_format,
513                         verbose         => $verbose,
514                         prereqs         => $self->status->prereqs,
515                         target          => $prereq_target,
516                         prereq_build    => $prereq_build,
517                     );
518
519         unless( $cb->_chdir( dir => $dir ) ) {
520             error( loc( "Could not chdir to build directory '%1'", $dir ) );
521             return;
522         }
523
524         unless( $ok ) {
525             #### use $dist->flush to reset the cache ###
526             error( loc( "Unable to satisfy prerequisites for '%1' " .
527                         "-- aborting install", $self->module ) );
528             $dist->status->build(0);
529             $fail++; $prereq_fail++;
530             last RUN;
531         }
532
533         my $captured;
534
535         unless ( scalar run(    command => [$perl, BUILD->($dir), $buildflags],
536                                 buffer  => \$captured,
537                                 verbose => $verbose ) 
538         ) {
539             error( loc( "MAKE failed:\n%1", $captured ) );
540             $dist->status->build(0);
541             $fail++; last RUN;
542         }
543
544         msg( $captured, 0 );
545
546         $dist->status->build(1);
547
548         ### add this directory to your lib ###
549         $self->add_to_includepath();
550
551         ### this buffer will not include what tests failed due to a 
552         ### M::B/Test::Harness bug. Reported as #9793 with patch 
553         ### against 0.2607 on 26/1/2005
554         unless( $skiptest ) {
555             my $test_output;
556             my $flag    = ON_VMS ? '"test"' : 'test';
557             my $cmd     = [$perl, BUILD->($dir), $flag, $buildflags];
558             unless ( scalar run(    command => $cmd,
559                                     buffer  => \$test_output,
560                                     verbose => $verbose ) 
561             ) {
562                 error( loc( "MAKE TEST failed:\n%1 ", $test_output ) );
563
564                 ### mark specifically *test* failure.. so we dont
565                 ### send success on force...
566                 $test_fail++;
567
568                 if( !$force and !$cb->_callbacks->proceed_on_test_failure->(
569                                       $self, $@ )
570                 ) {
571                     $dist->status->test(0);
572                     $fail++; last RUN;
573                 }
574
575             } 
576             else {
577                 msg( $test_output, 0 );
578                 $dist->status->test(1);
579             }
580         } 
581         else {
582             msg(loc("Tests skipped"), $verbose);
583         }
584     }
585
586     unless( $cb->_chdir( dir => $orig ) ) {
587         error( loc( "Could not chdir back to start dir '%1'", $orig ) );
588     }
589
590     ### send out test report? ###
591     if( $conf->get_conf('cpantest') and not $prereq_fail ) {
592         $cb->_send_report(
593             module          => $self,
594             failed          => $test_fail || $fail,
595             buffer          => CPANPLUS::Error->stack_as_string,
596             verbose         => $verbose,
597             force           => $force,
598             tests_skipped   => $skiptest,
599         ) or error(loc("Failed to send test report for '%1'",
600                     $self->module ) );
601     }
602
603     return $dist->status->created( $fail ? 0 : 1 );
604 }
605
606 =head2 $dist->install([verbose => BOOL, perl => /path/to/perl])
607
608 Actually installs the created dist.
609
610 Returns true on success and false on failure.
611
612 =cut
613
614 sub install {
615     ### just in case you already did a create call for this module object
616     ### just via a different dist object
617     my $dist = shift;
618     my $self = $dist->parent;
619
620     ### we're also the cpan_dist, since we don't need to have anything
621     ### prepared from another installer
622     $dist    = $self->status->dist_cpan if $self->status->dist_cpan;
623
624     my $cb   = $self->parent;
625     my $conf = $cb->configure_object;
626     my %hash = @_;
627
628     
629     my $verbose; my $perl; my $force;
630     {   local $Params::Check::ALLOW_UNKNOWN = 1;
631         my $tmpl = {
632             verbose => { default => $conf->get_conf('verbose'),
633                          store   => \$verbose },
634             force   => { default => $conf->get_conf('force'),
635                          store   => \$force },
636             perl    => { default => $^X, store   => \$perl },
637         };
638     
639         my $args = check( $tmpl, \%hash ) or return;
640         $dist->status->_install_args( $args );
641     }
642
643     my $dir;
644     unless( $dir = $self->status->extract ) {
645         error( loc( "No dir found to operate on!" ) );
646         return;
647     }
648
649     my $orig = cwd();
650
651     unless( $cb->_chdir( dir => $dir ) ) {
652         error( loc( "Could not chdir to build directory '%1'", $dir ) );
653         return;
654     }
655
656     ### value set and false -- means failure ###
657     if( defined $self->status->installed && 
658         !$self->status->installed && !$force
659     ) {
660         error( loc( "Module '%1' has failed to install before this session " .
661                     "-- aborting install", $self->module ) );
662         return;
663     }
664
665     my $fail;
666     my $buildflags = $dist->status->_buildflags;
667     ### hmm, how is this going to deal with sudo?
668     ### for now, check effective uid, if it's not root,
669     ### shell out, otherwise use the method
670     if( $> ) {
671
672         ### don't worry about loading the right version of M::B anymore
673         ### the 'new_from_context' already added the 'right' path to
674         ### M::B at the top of the build.pl
675         ### On VMS, flags need to be quoted
676         my $flag    = ON_VMS ? '"install"' : 'install';
677         my $cmd     = [$perl, BUILD->($dir), $flag, $buildflags];
678         my $sudo    = $conf->get_program('sudo');
679         unshift @$cmd, $sudo if $sudo;
680
681
682         my $buffer;
683         unless( scalar run( command => $cmd,
684                             buffer  => \$buffer,
685                             verbose => $verbose )
686         ) {
687             error(loc("Could not run '%1': %2", 'Build install', $buffer));
688             $fail++;
689         }
690     } else {
691         my %buildflags = $dist->_buildflags_as_hash($buildflags);
692
693         my $install_output;
694         my $flag    = ON_VMS ? '"install"' : 'install';
695         my $cmd     = [$perl, BUILD->($dir), $flag, $buildflags];
696         unless( scalar run( command => $cmd,
697                             buffer  => \$install_output,
698                             verbose => $verbose )
699         ) {
700             error(loc("Could not run '%1': %2", 'Build install', $install_output));
701             $fail++;
702         }
703         else {
704             msg( $install_output, 0 );
705         }
706     }
707
708
709     unless( $cb->_chdir( dir => $orig ) ) {
710         error( loc( "Could not chdir back to start dir '%1'", $orig ) );
711     }
712
713     return $dist->status->installed( $fail ? 0 : 1 );
714 }
715
716 ### returns the string 'foo=bar zot=quux' as (foo => bar, zot => quux)
717 sub _buildflags_as_hash {
718     my $self    = shift;
719     my $flags   = shift or return;
720
721     my @argv    = Module::Build->split_like_shell($flags);
722     my ($argv)  = Module::Build->read_args(@argv);
723
724     return %$argv;
725 }
726
727 =head1 AUTHOR
728
729 Originally by Jos Boumans E<lt>kane@cpan.orgE<gt>.  Brought to working
730 condition by Ken Williams E<lt>kwilliams@cpan.orgE<gt>.
731
732 Other hackery and currently maintained by Chris 'BinGOs' Williams ( no relation ). E<lt>bingos@cpan.orgE<gt>.
733
734 =head1 LICENSE
735
736 The CPAN++ interface (of which this module is a part of) is
737 copyright (c) 2001, 2002, 2003, 2004, 2005 Jos Boumans E<lt>kane@cpan.orgE<gt>.
738 All rights reserved.
739
740 This library is free software;
741 you may redistribute and/or modify it under the same
742 terms as Perl itself.
743
744 =cut
745
746 1;
747
748
749 # Local variables:
750 # c-indentation-style: bsd
751 # c-basic-offset: 4
752 # indent-tabs-mode: nil
753 # End:
754 # vim: expandtab shiftwidth=4:
755