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