Remove unused Module::Build tests
[p5sagit/p5-mst-13.2.git] / lib / CPANPLUS / Backend.pm
CommitLineData
6aaee015 1package CPANPLUS::Backend;
2
3use strict;
4
5
6use CPANPLUS::Error;
7use CPANPLUS::Configure;
8use CPANPLUS::Internals;
9use CPANPLUS::Internals::Constants;
10use CPANPLUS::Module;
11use CPANPLUS::Module::Author;
12use CPANPLUS::Backend::RV;
13
14use FileHandle;
15use File::Spec ();
16use File::Spec::Unix ();
17use Params::Check qw[check];
18use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
19
20$Params::Check::VERBOSE = 1;
21
22use vars qw[@ISA $VERSION];
23
24@ISA = qw[CPANPLUS::Internals];
25$VERSION = $CPANPLUS::Internals::VERSION;
26
27### mark that we're running under CPANPLUS to spawned processes
28$ENV{'PERL5_CPANPLUS_IS_RUNNING'} = $$;
29
30### XXX version.pm MAY format this version, if it's in use... :(
31### so for consistency, just call ->VERSION ourselves as well.
32$ENV{'PERL5_CPANPLUS_IS_VERSION'} = __PACKAGE__->VERSION;
33
34=pod
35
36=head1 NAME
37
38CPANPLUS::Backend
39
40=head1 SYNOPSIS
41
42 my $cb = CPANPLUS::Backend->new( );
43 my $conf = $cb->configure_object;
44
45 my $author = $cb->author_tree('KANE');
46 my $mod = $cb->module_tree('Some::Module');
47 my $mod = $cb->parse_module( module => 'Some::Module' );
48
49 my @objs = $cb->search( type => TYPE,
50 allow => [...] );
51
52 $cb->flush('all');
53 $cb->reload_indices;
54 $cb->local_mirror;
55
56
57=head1 DESCRIPTION
58
59This module provides the programmer's interface to the C<CPANPLUS>
60libraries.
61
62=head1 ENVIRONMENT
63
64When C<CPANPLUS::Backend> is loaded, which is necessary for just
65about every <CPANPLUS> operation, the environment variable
66C<PERL5_CPANPLUS_IS_RUNNING> is set to the current process id.
67
68Additionally, the environment variable C<PERL5_CPANPLUS_IS_VERSION>
69will be set to the version of C<CPANPLUS::Backend>.
70
71This information might be useful somehow to spawned processes.
72
73=head1 METHODS
74
75=head2 $cb = CPANPLUS::Backend->new( [CONFIGURE_OBJ] )
76
77This method returns a new C<CPANPLUS::Backend> object.
78This also initialises the config corresponding to this object.
79You have two choices in this:
80
81=over 4
82
83=item Provide a valid C<CPANPLUS::Configure> object
84
85This will be used verbatim.
86
87=item No arguments
88
89Your default config will be loaded and used.
90
91=back
92
93New will return a C<CPANPLUS::Backend> object on success and die on
94failure.
95
96=cut
97
98sub new {
99 my $class = shift;
100 my $conf;
101
102 if( $_[0] && IS_CONFOBJ->( conf => $_[0] ) ) {
103 $conf = shift;
104 } else {
105 $conf = CPANPLUS::Configure->new() or return;
106 }
107
108 my $self = $class->SUPER::_init( _conf => $conf );
109
110 return $self;
111}
112
113=pod
114
115=head2 $href = $cb->module_tree( [@modules_names_list] )
116
117Returns a reference to the CPANPLUS module tree.
118
119If you give it any arguments, they will be treated as module names
120and C<module_tree> will try to look up these module names and
121return the corresponding module objects instead.
122
123See L<CPANPLUS::Module> for the operations you can perform on a
124module object.
125
126=cut
127
128sub module_tree {
129 my $self = shift;
130 my $modtree = $self->_module_tree;
131
132 if( @_ ) {
133 my @rv;
134 for my $name ( grep { defined } @_) {
135 push @rv, $modtree->{$name} || '';
136 }
137 return @rv == 1 ? $rv[0] : @rv;
138 } else {
139 return $modtree;
140 }
141}
142
143=pod
144
145=head2 $href = $cb->author_tree( [@author_names_list] )
146
147Returns a reference to the CPANPLUS author tree.
148
149If you give it any arguments, they will be treated as author names
150and C<author_tree> will try to look up these author names and
151return the corresponding author objects instead.
152
153See L<CPANPLUS::Module::Author> for the operations you can perform on
154an author object.
155
156=cut
157
158sub author_tree {
159 my $self = shift;
160 my $authtree = $self->_author_tree;
161
162 if( @_ ) {
163 my @rv;
164 for my $name (@_) {
165 push @rv, $authtree->{$name} || '';
166 }
167 return @rv == 1 ? $rv[0] : @rv;
168 } else {
169 return $authtree;
170 }
171}
172
173=pod
174
175=head2 $conf = $cb->configure_object ()
176
177Returns a copy of the C<CPANPLUS::Configure> object.
178
179See L<CPANPLUS::Configure> for operations you can perform on a
180configure object.
181
182=cut
183
184sub configure_object { return shift->_conf() };
185
186=head2 $su = $cb->selfupdate_object;
187
188Returns a copy of the C<CPANPLUS::Selfupdate> object.
189
190See the L<CPANPLUS::Selfupdate> manpage for the operations
191you can perform on the selfupdate object.
192
193=cut
194
195sub selfupdate_object { return shift->_selfupdate() };
196
197=pod
198
199=head2 @mods = $cb->search( type => TYPE, allow => AREF, [data => AREF, verbose => BOOL] )
200
201C<search> enables you to search for either module or author objects,
202based on their data. The C<type> you can specify is any of the
203accessors specified in C<CPANPLUS::Module::Author> or
204C<CPANPLUS::Module>. C<search> will determine by the C<type> you
205specified whether to search by author object or module object.
206
207You have to specify an array reference of regular expressions or
208strings to match against. The rules used for this array ref are the
209same as in C<Params::Check>, so read that manpage for details.
210
211The search is an C<or> search, meaning that if C<any> of the criteria
212match, the search is considered to be successful.
213
214You can specify the result of a previous search as C<data> to limit
215the new search to these module or author objects, rather than the
216entire module or author tree. This is how you do C<and> searches.
217
218Returns a list of module or author objects on success and false
219on failure.
220
221See L<CPANPLUS::Module> for the operations you can perform on a
222module object.
223See L<CPANPLUS::Module::Author> for the operations you can perform on
224an author object.
225
226=cut
227
228sub search {
229 my $self = shift;
230 my $conf = $self->configure_object;
231 my %hash = @_;
232
233 local $Params::Check::ALLOW_UNKNOWN = 1;
234
235 my ($data,$type);
236 my $tmpl = {
237 type => { required => 1, allow => [CPANPLUS::Module->accessors(),
238 CPANPLUS::Module::Author->accessors()], store => \$type },
239 allow => { required => 1, default => [ ], strict_type => 1 },
240 };
241
242 my $args = check( $tmpl, \%hash ) or return;
243
244 ### figure out whether it was an author or a module search
245 ### when ambiguous, it'll be an author search.
246 my $aref;
247 if( grep { $type eq $_ } CPANPLUS::Module::Author->accessors() ) {
248 $aref = $self->_search_author_tree( %$args );
249 } else {
250 $aref = $self->_search_module_tree( %$args );
251 }
252
253 return @$aref if $aref;
254 return;
255}
256
257=pod
258
259=head2 $backend_rv = $cb->fetch( modules => \@mods )
260
261Fetches a list of modules. C<@mods> can be a list of distribution
262names, module names or module objects--basically anything that
263L<parse_module> can understand.
264
265See the equivalent method in C<CPANPLUS::Module> for details on
266other options you can pass.
267
268Since this is a multi-module method call, the return value is
269implemented as a C<CPANPLUS::Backend::RV> object. Please consult
270that module's documentation on how to interpret the return value.
271
272=head2 $backend_rv = $cb->extract( modules => \@mods )
273
274Extracts a list of modules. C<@mods> can be a list of distribution
275names, module names or module objects--basically anything that
276L<parse_module> can understand.
277
278See the equivalent method in C<CPANPLUS::Module> for details on
279other options you can pass.
280
281Since this is a multi-module method call, the return value is
282implemented as a C<CPANPLUS::Backend::RV> object. Please consult
283that module's documentation on how to interpret the return value.
284
285=head2 $backend_rv = $cb->install( modules => \@mods )
286
287Installs a list of modules. C<@mods> can be a list of distribution
288names, module names or module objects--basically anything that
289L<parse_module> can understand.
290
291See the equivalent method in C<CPANPLUS::Module> for details on
292other options you can pass.
293
294Since this is a multi-module method call, the return value is
295implemented as a C<CPANPLUS::Backend::RV> object. Please consult
296that module's documentation on how to interpret the return value.
297
298=head2 $backend_rv = $cb->readme( modules => \@mods )
299
300Fetches the readme for a list of modules. C<@mods> can be a list of
301distribution names, module names or module objects--basically
302anything that L<parse_module> can understand.
303
304See the equivalent method in C<CPANPLUS::Module> for details on
305other options you can pass.
306
307Since this is a multi-module method call, the return value is
308implemented as a C<CPANPLUS::Backend::RV> object. Please consult
309that module's documentation on how to interpret the return value.
310
311=head2 $backend_rv = $cb->files( modules => \@mods )
312
313Returns a list of files used by these modules if they are installed.
314C<@mods> can be a list of distribution names, module names or module
315objects--basically anything that L<parse_module> can understand.
316
317See the equivalent method in C<CPANPLUS::Module> for details on
318other options you can pass.
319
320Since this is a multi-module method call, the return value is
321implemented as a C<CPANPLUS::Backend::RV> object. Please consult
322that module's documentation on how to interpret the return value.
323
324=head2 $backend_rv = $cb->distributions( modules => \@mods )
325
326Returns a list of module objects representing all releases for this
327module on success.
328C<@mods> can be a list of distribution names, module names or module
329objects, basically anything that L<parse_module> can understand.
330
331See the equivalent method in C<CPANPLUS::Module> for details on
332other options you can pass.
333
334Since this is a multi-module method call, the return value is
335implemented as a C<CPANPLUS::Backend::RV> object. Please consult
336that module's documentation on how to interpret the return value.
337
338=cut
339
340### XXX add direcotry_tree, packlist etc? or maybe remove files? ###
341for my $func (qw[fetch extract install readme files distributions]) {
342 no strict 'refs';
343
344 *$func = sub {
345 my $self = shift;
346 my $conf = $self->configure_object;
347 my %hash = @_;
348
349 local $Params::Check::NO_DUPLICATES = 1;
350 local $Params::Check::ALLOW_UNKNOWN = 1;
351
352 my ($mods);
353 my $tmpl = {
354 modules => { default => [], strict_type => 1,
355 required => 1, store => \$mods },
356 };
357
358 my $args = check( $tmpl, \%hash ) or return;
359
360 ### make them all into module objects ###
361 my %mods = map {$_ => $self->parse_module(module => $_) || ''} @$mods;
362
363 my $flag; my $href;
364 while( my($name,$obj) = each %mods ) {
365 $href->{$name} = IS_MODOBJ->( mod => $obj )
366 ? $obj->$func( %$args )
367 : undef;
368
369 $flag++ unless $href->{$name};
370 }
371
372 return CPANPLUS::Backend::RV->new(
373 function => $func,
374 ok => !$flag,
375 rv => $href,
376 args => \%hash,
377 );
378 }
379}
380
381=pod
382
383=head2 $mod_obj = $cb->parse_module( module => $modname|$distname|$modobj )
384
385C<parse_module> tries to find a C<CPANPLUS::Module> object that
386matches your query. Here's a list of examples you could give to
387C<parse_module>;
388
389=over 4
390
391=item Text::Bastardize
392
393=item Text-Bastardize
394
395=item Text-Bastardize-1.06
396
397=item AYRNIEU/Text-Bastardize
398
399=item AYRNIEU/Text-Bastardize-1.06
400
401=item AYRNIEU/Text-Bastardize-1.06.tar.gz
402
403=item http://example.com/Text-Bastardize-1.06.tar.gz
404
405=item file:///tmp/Text-Bastardize-1.06.tar.gz
406
407=back
408
409These items would all come up with a C<CPANPLUS::Module> object for
410C<Text::Bastardize>. The ones marked explicitly as being version 1.06
411would give back a C<CPANPLUS::Module> object of that version.
412Even if the version on CPAN is currently higher.
413
414If C<parse_module> is unable to actually find the module you are looking
415for in its module tree, but you supplied it with an author, module
416and version part in a distribution name or URI, it will create a fake
417C<CPANPLUS::Module> object for you, that you can use just like the
418real thing.
419
420See L<CPANPLUS::Module> for the operations you can perform on a
421module object.
422
423If even this fancy guessing doesn't enable C<parse_module> to create
424a fake module object for you to use, it will warn about an error and
425return false.
426
427=cut
428
429sub parse_module {
430 my $self = shift;
431 my $conf = $self->configure_object;
432 my %hash = @_;
433
434 my $mod;
435 my $tmpl = {
436 module => { required => 1, store => \$mod },
437 };
438
439 my $args = check( $tmpl, \%hash ) or return;
440
441 return $mod if IS_MODOBJ->( module => $mod );
442
443 ### ok, so it's not a module object, but a ref nonetheless?
444 ### what are you smoking?
445 if( ref $mod ) {
446 error(loc("Can not parse module string from reference '%1'", $mod ));
447 return;
448 }
449
450 ### check only for allowed characters in a module name
451 unless( $mod =~ /[^\w:]/ ) {
452
453 ### perhaps we can find it in the module tree?
454 my $maybe = $self->module_tree($mod);
455 return $maybe if IS_MODOBJ->( module => $maybe );
456 }
457
458 ### ok, so it looks like a distribution then?
459 my @parts = split '/', $mod;
460 my $dist = pop @parts;
461
462 ### ah, it's a URL
463 if( $mod =~ m|\w+://.+| ) {
464 my $modobj = CPANPLUS::Module::Fake->new(
465 module => $dist,
466 version => 0,
467 package => $dist,
468 path => File::Spec::Unix->catdir(
469 $conf->_get_mirror('base'),
470 UNKNOWN_DL_LOCATION ),
471 author => CPANPLUS::Module::Author::Fake->new
472 );
473
474 ### set the fetch_from accessor so we know to by pass the
475 ### usual mirrors
476 $modobj->status->_fetch_from( $mod );
477
478 return $modobj;
479 }
480
481 ### perhaps we can find it's a third party module?
482 { my $modobj = CPANPLUS::Module::Fake->new(
483 module => $mod,
484 version => 0,
485 package => $dist,
486 path => File::Spec::Unix->catdir(
487 $conf->_get_mirror('base'),
488 UNKNOWN_DL_LOCATION ),
489 author => CPANPLUS::Module::Author::Fake->new
490 );
491 if( $modobj->is_third_party ) {
492 my $info = $modobj->third_party_information;
493
494 $modobj->author->author( $info->{author} );
495 $modobj->author->email( $info->{author_url} );
496 $modobj->description( $info->{url} );
497
498 return $modobj;
499 }
500 }
501
502 unless( $dist ) {
503 error( loc("%1 is not a proper distribution name!", $mod) );
504 return;
505 }
506
507 ### there's wonky uris out there, like this:
508 ### E/EY/EYCK/Net/Lite/Net-Lite-FTP-0.091
509 ### compensate for that
510 my $author;
511 ### you probably have an A/AB/ABC/....../Dist.tgz type uri
512 if( (defined $parts[0] and length $parts[0] == 1) and
513 (defined $parts[1] and length $parts[1] == 2) and
514 $parts[2] =~ /^$parts[0]/i and $parts[2] =~ /^$parts[1]/i
515 ) {
516 splice @parts, 0, 2; # remove the first 2 entries from the list
517 $author = shift @parts; # this is the actual author name then
518
519 ### we''ll assume a ABC/..../Dist.tgz
520 } else {
521 $author = shift @parts || '';
522 }
523
524 my($pkg, $version, $ext) =
525 $self->_split_package_string( package => $dist );
526
527 ### translate a distribution into a module name ###
528 my $guess = $pkg;
529 $guess =~ s/-/::/g if $guess;
530
531 my $maybe = $self->module_tree( $guess );
532 if( IS_MODOBJ->( module => $maybe ) ) {
533
534 ### maybe you asked for a package instead
535 if ( $maybe->package eq $mod ) {
536 return $maybe;
537
538 ### perhaps an outdated version instead?
539 } elsif ( $version ) {
540 my $auth_obj; my $path;
541
542 ### did you give us an author part? ###
543 if( $author ) {
544 $auth_obj = CPANPLUS::Module::Author::Fake->new(
545 _id => $maybe->_id,
546 cpanid => uc $author,
547 author => uc $author,
548 );
549 $path = File::Spec::Unix->catdir(
550 $conf->_get_mirror('base'),
551 substr(uc $author, 0, 1),
552 substr(uc $author, 0, 2),
553 uc $author,
554 @parts, #possible sub dirs
555 );
556 } else {
557 $auth_obj = $maybe->author;
558 $path = $maybe->path;
559 }
560
561 if( $maybe->package_name eq $pkg ) {
562
563 my $modobj = CPANPLUS::Module::Fake->new(
564 module => $maybe->module,
565 version => $version,
566 package => $pkg . '-' . $version . '.' .
567 $maybe->package_extension,
568 path => $path,
569 author => $auth_obj,
570 _id => $maybe->_id
571 );
572 return $modobj;
573
574 ### you asked for a specific version?
575 ### assume our $maybe is the one you wanted,
576 ### and fix up the version..
577 } else {
578
579 my $modobj = $maybe->clone;
580 $modobj->version( $version );
581 $modobj->package(
582 $maybe->package_name .'-'.
583 $version .'.'.
584 $maybe->package_extension
585 );
586
587 ### you wanted a specific author, but it's not the one
588 ### from the module tree? we'll fix it up
589 if( $author and $author ne $modobj->author->cpanid ) {
590 $modobj->author( $auth_obj );
591 $modobj->path( $path );
592 }
593
594 return $modobj;
595 }
596
597 ### you didn't care about a version, so just return the object then
598 } elsif ( !$version ) {
599 return $maybe;
600 }
601
602 ### ok, so we can't find it, and it's not an outdated dist either
603 ### perhaps we can fake one based on the author name and so on
604 } elsif ( $author and $version ) {
605
606 ### be extra friendly and pad the .tar.gz suffix where needed
607 ### it's just a guess of course, but most dists are .tar.gz
608 $dist .= '.tar.gz' unless $dist =~ /\.[A-Za-z]+$/;
609
610 ### XXX duplication from above for generating author obj + path...
611 my $modobj = CPANPLUS::Module::Fake->new(
612 module => $guess,
613 version => $version,
614 package => $dist,
615 author => CPANPLUS::Module::Author::Fake->new(
616 author => uc $author,
617 cpanid => uc $author,
618 _id => $self->_id,
619 ),
620 path => File::Spec::Unix->catdir(
621 $conf->_get_mirror('base'),
622 substr(uc $author, 0, 1),
623 substr(uc $author, 0, 2),
624 uc $author,
625 @parts, #possible subdirs
626 ),
627 _id => $self->_id,
628 );
629
630 return $modobj;
631
632 ### face it, we have /no/ idea what he or she wants...
633 ### let's start putting the blame somewhere
634 } else {
635
636 unless( $author ) {
637 error( loc( "'%1' does not contain an author part", $mod ) );
638 }
639
640 error( loc( "Cannot find '%1' in the module tree", $mod ) );
641 }
642
643 return;
644}
645
646=pod
647
648=head2 $bool = $cb->reload_indices( [update_source => BOOL, verbose => BOOL] );
649
650This method reloads the source files.
651
652If C<update_source> is set to true, this will fetch new source files
653from your CPAN mirror. Otherwise, C<reload_indices> will do its
654usual cache checking and only update them if they are out of date.
655
656By default, C<update_source> will be false.
657
658The verbose setting defaults to what you have specified in your
659config file.
660
661Returns true on success and false on failure.
662
663=cut
664
665sub reload_indices {
666 my $self = shift;
667 my %hash = @_;
668 my $conf = $self->configure_object;
669
670 my $tmpl = {
671 update_source => { default => 0, allow => [qr/^\d$/] },
672 verbose => { default => $conf->get_conf('verbose') },
673 };
674
675 my $args = check( $tmpl, \%hash ) or return;
676
677 ### make a call to the internal _module_tree, so it triggers cache
678 ### file age
679 my $uptodate = $self->_check_trees( %$args );
680
681
682 return 1 if $self->_build_trees(
683 uptodate => $uptodate,
684 use_stored => 0,
685 verbose => $conf->get_conf('verbose'),
686 );
687
688 error( loc( "Error rebuilding source trees!" ) );
689
690 return;
691}
692
693=pod
694
695=head2 $bool = $cb->flush(CACHE_NAME)
696
697This method allows flushing of caches.
698There are several things which can be flushed:
699
700=over 4
701
702=item * C<methods>
703
704The return status of methods which have been attempted, such as
705different ways of fetching files. It is recommended that automatic
706flushing be used instead.
707
708=item * C<hosts>
709
710The return status of URIs which have been attempted, such as
711different hosts of fetching files. It is recommended that automatic
712flushing be used instead.
713
714=item * C<modules>
715
716Information about modules such as prerequisites and whether
717installation succeeded, failed, or was not attempted.
718
719=item * C<lib>
720
721This resets PERL5LIB, which is changed to ensure that while installing
722modules they are in our @INC.
723
724=item * C<load>
725
726This resets the cache of modules we've attempted to load, but failed.
727This enables you to load them again after a failed load, if they
728somehow have become available.
729
730=item * C<all>
731
732Flush all of the aforementioned caches.
733
734=back
735
736Returns true on success and false on failure.
737
738=cut
739
740sub flush {
741 my $self = shift;
742 my $type = shift or return;
743
744 my $cache = {
745 methods => [ qw( methods load ) ],
746 hosts => [ qw( hosts ) ],
747 modules => [ qw( modules lib) ],
748 lib => [ qw( lib ) ],
749 load => [ qw( load ) ],
750 all => [ qw( hosts lib modules methods load ) ],
751 };
752
753 my $aref = $cache->{$type}
754 or (
755 error( loc("No such cache '%1'", $type) ),
756 return
757 );
758
759 return $self->_flush( list => $aref );
760}
761
762=pod
763
764=head2 @mods = $cb->installed()
765
766Returns a list of module objects of all your installed modules.
767If an error occurs, it will return false.
768
769See L<CPANPLUS::Module> for the operations you can perform on a
770module object.
771
772=cut
773
774sub installed {
775 my $self = shift;
776 my $aref = $self->_all_installed;
777
778 return @$aref if $aref;
779 return;
780}
781
782=pod
783
784=head2 $bool = $cb->local_mirror([path => '/dir/to/save/to', index_files => BOOL, force => BOOL, verbose => BOOL] )
785
786Creates a local mirror of CPAN, of only the most recent sources in a
787location you specify. If you set this location equal to a custom host
788in your C<CPANPLUS::Config> you can use your local mirror to install
789from.
790
791It takes the following arguments:
792
793=over 4
794
795=item path
796
797The location where to create the local mirror.
798
799=item index_files
800
801Enable/disable fetching of index files. This is ok if you don't plan
802to use the local mirror as your primary sites, or if you'd like
803up-to-date index files be fetched from elsewhere.
804
805Defaults to true.
806
807=item force
808
809Forces refetching of packages, even if they are there already.
810
811Defaults to whatever setting you have in your C<CPANPLUS::Config>.
812
813=item verbose
814
815Prints more messages about what its doing.
816
817Defaults to whatever setting you have in your C<CPANPLUS::Config>.
818
819=back
820
821Returns true on success and false on error.
822
823=cut
824
825sub local_mirror {
826 my $self = shift;
827 my $conf = $self->configure_object;
828 my %hash = @_;
829
830 my($path, $index, $force, $verbose);
831 my $tmpl = {
832 path => { default => $conf->get_conf('base'),
833 store => \$path },
834 index_files => { default => 1, store => \$index },
835 force => { default => $conf->get_conf('force'),
836 store => \$force },
837 verbose => { default => $conf->get_conf('verbose'),
838 store => \$verbose },
839 };
840
841 check( $tmpl, \%hash ) or return;
842
843 unless( -d $path ) {
844 $self->_mkdir( dir => $path )
845 or( error( loc( "Could not create '%1', giving up", $path ) ),
846 return
847 );
848 } elsif ( ! -w _ ) {
849 error( loc( "Could not write to '%1', giving up", $path ) );
850 return;
851 }
852
853 my $flag;
854 AUTHOR: {
855 for my $auth ( sort { $a->cpanid cmp $b->cpanid }
856 values %{$self->author_tree}
857 ) {
858
859 MODULE: {
860 my $i;
861 for my $mod ( $auth->modules ) {
862 my $fetchdir = File::Spec->catdir( $path, $mod->path );
863
864 my %opts = (
865 verbose => $verbose,
866 force => $force,
867 fetchdir => $fetchdir,
868 );
869
870 ### only do this the for the first module ###
871 unless( $i++ ) {
872 $mod->_get_checksums_file(
873 %opts
874 ) or (
875 error( loc( "Could not fetch %1 file, " .
876 "skipping author '%2'",
877 CHECKSUMS, $auth->cpanid ) ),
878 $flag++, next AUTHOR
879 );
880 }
881
882 $mod->fetch( %opts )
883 or( error( loc( "Could not fetch '%1'", $mod->module ) ),
884 $flag++, next MODULE
885 );
886 } }
887 } }
888
889 if( $index ) {
890 for my $name (qw[auth dslip mod]) {
891 $self->_update_source(
892 name => $name,
893 verbose => $verbose,
894 path => $path,
895 ) or ( $flag++, next );
896 }
897 }
898
899 return !$flag;
900}
901
902=pod
903
904=head2 $file = $cb->autobundle([path => OUTPUT_PATH, force => BOOL, verbose => BOOL])
905
906Writes out a snapshot of your current installation in C<CPAN> bundle
907style. This can then be used to install the same modules for a
908different or on a different machine.
909
910It will, by default, write to an 'autobundle' directory under your
911cpanplus homedirectory, but you can override that by supplying a
912C<path> argument.
913
914It will return the location of the output file on success and false on
915failure.
916
917=cut
918
919sub autobundle {
920 my $self = shift;
921 my $conf = $self->configure_object;
922 my %hash = @_;
923
924 my($path,$force,$verbose);
925 my $tmpl = {
926 force => { default => $conf->get_conf('force'), store => \$force },
927 verbose => { default => $conf->get_conf('verbose'), store => \$verbose },
928 path => { default => File::Spec->catdir(
929 $conf->get_conf('base'),
930 $self->_perl_version( perl => $^X ),
931 $conf->_get_build('distdir'),
932 $conf->_get_build('autobundle') ),
933 store => \$path },
934 };
935
936 check($tmpl, \%hash) or return;
937
938 unless( -d $path ) {
939 $self->_mkdir( dir => $path )
940 or( error(loc("Could not create directory '%1'", $path ) ),
941 return
942 );
943 }
944
945 my $name; my $file;
946 { ### default filename for the bundle ###
947 my($year,$month,$day) = (localtime)[5,4,3];
948 $year += 1900; $month++;
949
950 my $ext = 0;
951
952 my $prefix = $conf->_get_build('autobundle_prefix');
953 my $format = "${prefix}_%04d_%02d_%02d_%02d";
954
955 BLOCK: {
956 $name = sprintf( $format, $year, $month, $day, $ext);
957
958 $file = File::Spec->catfile( $path, $name . '.pm' );
959
960 -f $file ? ++$ext && redo BLOCK : last BLOCK;
961 }
962 }
963 my $fh;
964 unless( $fh = FileHandle->new( ">$file" ) ) {
965 error( loc( "Could not open '%1' for writing: %2", $file, $! ) );
966 return;
967 }
968
969 my $string = join "\n\n",
970 map {
971 join ' ',
972 $_->module,
973 ($_->installed_version(verbose => 0) || 'undef')
974 } sort {
975 $a->module cmp $b->module
976 } $self->installed;
977
978 my $now = scalar localtime;
979 my $head = '=head1';
980 my $pkg = __PACKAGE__;
981 my $version = $self->VERSION;
982 my $perl_v = join '', `$^X -V`;
983
984 print $fh <<EOF;
985package $name
986
987\$VERSION = '0.01';
988
9891;
990
991__END__
992
993$head NAME
994
995$name - Snapshot of your installation at $now
996
997$head SYNOPSIS
998
999perl -MCPANPLUS -e "install $name"
1000
1001$head CONTENTS
1002
1003$string
1004
1005$head CONFIGURATION
1006
1007$perl_v
1008
1009$head AUTHOR
1010
1011This bundle has been generated autotomatically by
1012 $pkg $version
1013
1014EOF
1015
1016 close $fh;
1017
1018 return $file;
1019}
1020
10211;
1022
1023=pod
1024
1025=head1 BUG REPORTS
1026
1027Please report bugs or other issues to E<lt>bug-cpanplus@rt.cpan.org<gt>.
1028
1029=head1 AUTHOR
1030
1031This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
1032
1033=head1 COPYRIGHT
1034
1035The CPAN++ interface (of which this module is a part of) is copyright (c)
10362001 - 2007, Jos Boumans E<lt>kane@cpan.orgE<gt>. All rights reserved.
1037
1038This library is free software; you may redistribute and/or modify it
1039under the same terms as Perl itself.
1040
1041=head1 SEE ALSO
1042
1043L<CPANPLUS::Configure>, L<CPANPLUS::Module>, L<CPANPLUS::Module::Author>
1044
1045=cut
1046
1047# Local variables:
1048# c-indentation-style: bsd
1049# c-basic-offset: 4
1050# indent-tabs-mode: nil
1051# End:
1052# vim: expandtab shiftwidth=4:
1053
1054__END__
1055
1056todo:
1057sub dist { # not sure about this one -- probably already done
1058 enough in Module.pm
1059sub reports { # in Module.pm, wrapper here
1060
1061