Bump two module versions after Haiku port
[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
5bc5f6dc 42 my $cb = CPANPLUS::Backend->new;
6aaee015 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 } @_) {
5879cbe1 135
136 ### From John Malmberg: This is failing on VMS
137 ### because ODS-2 does not retain the case of
138 ### filenames that are created.
139 ### The problem is the filename is being converted
140 ### to a module name and then looked up in the
141 ### %$modtree hash.
142 ###
143 ### As a fix, we do a search on VMS instead --
144 ### more cpu cycles, but it gets around the case
145 ### problem --kane
146 my ($modobj) = do {
147 ON_VMS
148 ? $self->search(
149 type => 'module',
150 allow => [qr/^$name$/i],
151 )
152 : $modtree->{$name}
153 };
154
155 push @rv, $modobj || '';
6aaee015 156 }
157 return @rv == 1 ? $rv[0] : @rv;
158 } else {
159 return $modtree;
160 }
161}
162
163=pod
164
165=head2 $href = $cb->author_tree( [@author_names_list] )
166
167Returns a reference to the CPANPLUS author tree.
168
169If you give it any arguments, they will be treated as author names
170and C<author_tree> will try to look up these author names and
171return the corresponding author objects instead.
172
173See L<CPANPLUS::Module::Author> for the operations you can perform on
174an author object.
175
176=cut
177
178sub author_tree {
179 my $self = shift;
180 my $authtree = $self->_author_tree;
181
182 if( @_ ) {
183 my @rv;
184 for my $name (@_) {
185 push @rv, $authtree->{$name} || '';
186 }
187 return @rv == 1 ? $rv[0] : @rv;
188 } else {
189 return $authtree;
190 }
191}
192
193=pod
194
5bc5f6dc 195=head2 $conf = $cb->configure_object;
6aaee015 196
197Returns a copy of the C<CPANPLUS::Configure> object.
198
199See L<CPANPLUS::Configure> for operations you can perform on a
200configure object.
201
202=cut
203
204sub configure_object { return shift->_conf() };
205
206=head2 $su = $cb->selfupdate_object;
207
208Returns a copy of the C<CPANPLUS::Selfupdate> object.
209
210See the L<CPANPLUS::Selfupdate> manpage for the operations
211you can perform on the selfupdate object.
212
213=cut
214
215sub selfupdate_object { return shift->_selfupdate() };
216
217=pod
218
219=head2 @mods = $cb->search( type => TYPE, allow => AREF, [data => AREF, verbose => BOOL] )
220
221C<search> enables you to search for either module or author objects,
222based on their data. The C<type> you can specify is any of the
223accessors specified in C<CPANPLUS::Module::Author> or
224C<CPANPLUS::Module>. C<search> will determine by the C<type> you
225specified whether to search by author object or module object.
226
227You have to specify an array reference of regular expressions or
228strings to match against. The rules used for this array ref are the
229same as in C<Params::Check>, so read that manpage for details.
230
231The search is an C<or> search, meaning that if C<any> of the criteria
232match, the search is considered to be successful.
233
234You can specify the result of a previous search as C<data> to limit
235the new search to these module or author objects, rather than the
236entire module or author tree. This is how you do C<and> searches.
237
238Returns a list of module or author objects on success and false
239on failure.
240
241See L<CPANPLUS::Module> for the operations you can perform on a
242module object.
243See L<CPANPLUS::Module::Author> for the operations you can perform on
244an author object.
245
246=cut
247
248sub search {
249 my $self = shift;
250 my $conf = $self->configure_object;
251 my %hash = @_;
252
5879cbe1 253 my ($type);
254 my $args = do {
255 local $Params::Check::NO_DUPLICATES = 0;
256 local $Params::Check::ALLOW_UNKNOWN = 1;
6aaee015 257
5879cbe1 258 my $tmpl = {
259 type => { required => 1, allow => [CPANPLUS::Module->accessors(),
260 CPANPLUS::Module::Author->accessors()], store => \$type },
261 allow => { required => 1, default => [ ], strict_type => 1 },
262 };
6aaee015 263
5879cbe1 264 check( $tmpl, \%hash )
265 } or return;
6aaee015 266
267 ### figure out whether it was an author or a module search
268 ### when ambiguous, it'll be an author search.
269 my $aref;
270 if( grep { $type eq $_ } CPANPLUS::Module::Author->accessors() ) {
271 $aref = $self->_search_author_tree( %$args );
272 } else {
273 $aref = $self->_search_module_tree( %$args );
274 }
275
276 return @$aref if $aref;
277 return;
278}
279
280=pod
281
282=head2 $backend_rv = $cb->fetch( modules => \@mods )
283
284Fetches a list of modules. C<@mods> can be a list of distribution
285names, module names or module objects--basically anything that
286L<parse_module> can understand.
287
288See the equivalent method in C<CPANPLUS::Module> for details on
289other options you can pass.
290
291Since this is a multi-module method call, the return value is
292implemented as a C<CPANPLUS::Backend::RV> object. Please consult
293that module's documentation on how to interpret the return value.
294
295=head2 $backend_rv = $cb->extract( modules => \@mods )
296
297Extracts a list of modules. C<@mods> can be a list of distribution
298names, module names or module objects--basically anything that
299L<parse_module> can understand.
300
301See the equivalent method in C<CPANPLUS::Module> for details on
302other options you can pass.
303
304Since this is a multi-module method call, the return value is
305implemented as a C<CPANPLUS::Backend::RV> object. Please consult
306that module's documentation on how to interpret the return value.
307
308=head2 $backend_rv = $cb->install( modules => \@mods )
309
310Installs a list of modules. C<@mods> can be a list of distribution
311names, module names or module objects--basically anything that
312L<parse_module> can understand.
313
314See the equivalent method in C<CPANPLUS::Module> for details on
315other options you can pass.
316
317Since this is a multi-module method call, the return value is
318implemented as a C<CPANPLUS::Backend::RV> object. Please consult
319that module's documentation on how to interpret the return value.
320
321=head2 $backend_rv = $cb->readme( modules => \@mods )
322
323Fetches the readme for a list of modules. C<@mods> can be a list of
324distribution names, module names or module objects--basically
325anything that L<parse_module> can understand.
326
327See the equivalent method in C<CPANPLUS::Module> for details on
328other options you can pass.
329
330Since this is a multi-module method call, the return value is
331implemented as a C<CPANPLUS::Backend::RV> object. Please consult
332that module's documentation on how to interpret the return value.
333
334=head2 $backend_rv = $cb->files( modules => \@mods )
335
336Returns a list of files used by these modules if they are installed.
337C<@mods> can be a list of distribution names, module names or module
338objects--basically anything that L<parse_module> can understand.
339
340See the equivalent method in C<CPANPLUS::Module> for details on
341other options you can pass.
342
343Since this is a multi-module method call, the return value is
344implemented as a C<CPANPLUS::Backend::RV> object. Please consult
345that module's documentation on how to interpret the return value.
346
347=head2 $backend_rv = $cb->distributions( modules => \@mods )
348
349Returns a list of module objects representing all releases for this
350module on success.
351C<@mods> can be a list of distribution names, module names or module
352objects, basically anything that L<parse_module> can understand.
353
354See the equivalent method in C<CPANPLUS::Module> for details on
355other options you can pass.
356
357Since this is a multi-module method call, the return value is
358implemented as a C<CPANPLUS::Backend::RV> object. Please consult
359that module's documentation on how to interpret the return value.
360
361=cut
362
363### XXX add direcotry_tree, packlist etc? or maybe remove files? ###
364for my $func (qw[fetch extract install readme files distributions]) {
365 no strict 'refs';
366
367 *$func = sub {
368 my $self = shift;
369 my $conf = $self->configure_object;
370 my %hash = @_;
371
372 local $Params::Check::NO_DUPLICATES = 1;
373 local $Params::Check::ALLOW_UNKNOWN = 1;
374
375 my ($mods);
376 my $tmpl = {
377 modules => { default => [], strict_type => 1,
378 required => 1, store => \$mods },
379 };
380
381 my $args = check( $tmpl, \%hash ) or return;
382
383 ### make them all into module objects ###
384 my %mods = map {$_ => $self->parse_module(module => $_) || ''} @$mods;
385
386 my $flag; my $href;
387 while( my($name,$obj) = each %mods ) {
388 $href->{$name} = IS_MODOBJ->( mod => $obj )
389 ? $obj->$func( %$args )
390 : undef;
391
392 $flag++ unless $href->{$name};
393 }
394
395 return CPANPLUS::Backend::RV->new(
396 function => $func,
397 ok => !$flag,
398 rv => $href,
399 args => \%hash,
400 );
401 }
402}
403
404=pod
405
d0baa00e 406=head2 $mod_obj = $cb->parse_module( module => $modname|$distname|$modobj|URI )
6aaee015 407
408C<parse_module> tries to find a C<CPANPLUS::Module> object that
409matches your query. Here's a list of examples you could give to
410C<parse_module>;
411
412=over 4
413
414=item Text::Bastardize
415
416=item Text-Bastardize
417
418=item Text-Bastardize-1.06
419
420=item AYRNIEU/Text-Bastardize
421
422=item AYRNIEU/Text-Bastardize-1.06
423
424=item AYRNIEU/Text-Bastardize-1.06.tar.gz
425
426=item http://example.com/Text-Bastardize-1.06.tar.gz
427
428=item file:///tmp/Text-Bastardize-1.06.tar.gz
429
430=back
431
432These items would all come up with a C<CPANPLUS::Module> object for
433C<Text::Bastardize>. The ones marked explicitly as being version 1.06
434would give back a C<CPANPLUS::Module> object of that version.
435Even if the version on CPAN is currently higher.
436
437If C<parse_module> is unable to actually find the module you are looking
438for in its module tree, but you supplied it with an author, module
439and version part in a distribution name or URI, it will create a fake
440C<CPANPLUS::Module> object for you, that you can use just like the
441real thing.
442
443See L<CPANPLUS::Module> for the operations you can perform on a
444module object.
445
446If even this fancy guessing doesn't enable C<parse_module> to create
447a fake module object for you to use, it will warn about an error and
448return false.
449
450=cut
451
452sub parse_module {
453 my $self = shift;
454 my $conf = $self->configure_object;
455 my %hash = @_;
456
457 my $mod;
458 my $tmpl = {
459 module => { required => 1, store => \$mod },
460 };
461
462 my $args = check( $tmpl, \%hash ) or return;
463
464 return $mod if IS_MODOBJ->( module => $mod );
465
466 ### ok, so it's not a module object, but a ref nonetheless?
467 ### what are you smoking?
468 if( ref $mod ) {
469 error(loc("Can not parse module string from reference '%1'", $mod ));
470 return;
471 }
472
473 ### check only for allowed characters in a module name
474 unless( $mod =~ /[^\w:]/ ) {
475
476 ### perhaps we can find it in the module tree?
477 my $maybe = $self->module_tree($mod);
478 return $maybe if IS_MODOBJ->( module => $maybe );
479 }
480
481 ### ok, so it looks like a distribution then?
482 my @parts = split '/', $mod;
483 my $dist = pop @parts;
484
485 ### ah, it's a URL
486 if( $mod =~ m|\w+://.+| ) {
487 my $modobj = CPANPLUS::Module::Fake->new(
488 module => $dist,
489 version => 0,
490 package => $dist,
491 path => File::Spec::Unix->catdir(
492 $conf->_get_mirror('base'),
493 UNKNOWN_DL_LOCATION ),
494 author => CPANPLUS::Module::Author::Fake->new
495 );
496
497 ### set the fetch_from accessor so we know to by pass the
498 ### usual mirrors
499 $modobj->status->_fetch_from( $mod );
500
5bc5f6dc 501 ### better guess for the version
502 $modobj->version( $modobj->package_version )
503 if defined $modobj->package_version;
504
505 ### better guess at module name, if possible
506 if ( my $pkgname = $modobj->package_name ) {
507 $pkgname =~ s/-/::/g;
508
509 ### no sense replacing it unless we changed something
510 $modobj->module( $pkgname )
511 if ($pkgname ne $modobj->package_name) || $pkgname !~ /-/;
512 }
513
6aaee015 514 return $modobj;
515 }
516
517 ### perhaps we can find it's a third party module?
518 { my $modobj = CPANPLUS::Module::Fake->new(
519 module => $mod,
520 version => 0,
521 package => $dist,
522 path => File::Spec::Unix->catdir(
523 $conf->_get_mirror('base'),
524 UNKNOWN_DL_LOCATION ),
525 author => CPANPLUS::Module::Author::Fake->new
526 );
527 if( $modobj->is_third_party ) {
528 my $info = $modobj->third_party_information;
529
530 $modobj->author->author( $info->{author} );
531 $modobj->author->email( $info->{author_url} );
532 $modobj->description( $info->{url} );
533
534 return $modobj;
535 }
536 }
537
538 unless( $dist ) {
539 error( loc("%1 is not a proper distribution name!", $mod) );
540 return;
541 }
542
543 ### there's wonky uris out there, like this:
544 ### E/EY/EYCK/Net/Lite/Net-Lite-FTP-0.091
545 ### compensate for that
546 my $author;
547 ### you probably have an A/AB/ABC/....../Dist.tgz type uri
548 if( (defined $parts[0] and length $parts[0] == 1) and
549 (defined $parts[1] and length $parts[1] == 2) and
550 $parts[2] =~ /^$parts[0]/i and $parts[2] =~ /^$parts[1]/i
551 ) {
552 splice @parts, 0, 2; # remove the first 2 entries from the list
553 $author = shift @parts; # this is the actual author name then
554
555 ### we''ll assume a ABC/..../Dist.tgz
556 } else {
557 $author = shift @parts || '';
558 }
559
560 my($pkg, $version, $ext) =
561 $self->_split_package_string( package => $dist );
562
563 ### translate a distribution into a module name ###
564 my $guess = $pkg;
565 $guess =~ s/-/::/g if $guess;
566
567 my $maybe = $self->module_tree( $guess );
568 if( IS_MODOBJ->( module => $maybe ) ) {
569
570 ### maybe you asked for a package instead
571 if ( $maybe->package eq $mod ) {
572 return $maybe;
573
574 ### perhaps an outdated version instead?
575 } elsif ( $version ) {
576 my $auth_obj; my $path;
577
578 ### did you give us an author part? ###
579 if( $author ) {
580 $auth_obj = CPANPLUS::Module::Author::Fake->new(
581 _id => $maybe->_id,
582 cpanid => uc $author,
583 author => uc $author,
584 );
585 $path = File::Spec::Unix->catdir(
586 $conf->_get_mirror('base'),
587 substr(uc $author, 0, 1),
588 substr(uc $author, 0, 2),
589 uc $author,
590 @parts, #possible sub dirs
591 );
592 } else {
593 $auth_obj = $maybe->author;
594 $path = $maybe->path;
595 }
596
597 if( $maybe->package_name eq $pkg ) {
598
599 my $modobj = CPANPLUS::Module::Fake->new(
600 module => $maybe->module,
601 version => $version,
602 package => $pkg . '-' . $version . '.' .
603 $maybe->package_extension,
604 path => $path,
605 author => $auth_obj,
606 _id => $maybe->_id
607 );
608 return $modobj;
609
610 ### you asked for a specific version?
611 ### assume our $maybe is the one you wanted,
612 ### and fix up the version..
613 } else {
614
615 my $modobj = $maybe->clone;
616 $modobj->version( $version );
617 $modobj->package(
618 $maybe->package_name .'-'.
619 $version .'.'.
620 $maybe->package_extension
621 );
622
623 ### you wanted a specific author, but it's not the one
624 ### from the module tree? we'll fix it up
625 if( $author and $author ne $modobj->author->cpanid ) {
626 $modobj->author( $auth_obj );
627 $modobj->path( $path );
628 }
629
630 return $modobj;
631 }
632
633 ### you didn't care about a version, so just return the object then
634 } elsif ( !$version ) {
635 return $maybe;
636 }
637
638 ### ok, so we can't find it, and it's not an outdated dist either
639 ### perhaps we can fake one based on the author name and so on
640 } elsif ( $author and $version ) {
641
642 ### be extra friendly and pad the .tar.gz suffix where needed
643 ### it's just a guess of course, but most dists are .tar.gz
644 $dist .= '.tar.gz' unless $dist =~ /\.[A-Za-z]+$/;
645
646 ### XXX duplication from above for generating author obj + path...
647 my $modobj = CPANPLUS::Module::Fake->new(
648 module => $guess,
649 version => $version,
650 package => $dist,
651 author => CPANPLUS::Module::Author::Fake->new(
652 author => uc $author,
653 cpanid => uc $author,
654 _id => $self->_id,
655 ),
656 path => File::Spec::Unix->catdir(
657 $conf->_get_mirror('base'),
658 substr(uc $author, 0, 1),
659 substr(uc $author, 0, 2),
660 uc $author,
661 @parts, #possible subdirs
662 ),
663 _id => $self->_id,
664 );
665
666 return $modobj;
667
668 ### face it, we have /no/ idea what he or she wants...
669 ### let's start putting the blame somewhere
670 } else {
671
672 unless( $author ) {
673 error( loc( "'%1' does not contain an author part", $mod ) );
674 }
675
676 error( loc( "Cannot find '%1' in the module tree", $mod ) );
677 }
678
679 return;
680}
681
682=pod
683
684=head2 $bool = $cb->reload_indices( [update_source => BOOL, verbose => BOOL] );
685
686This method reloads the source files.
687
688If C<update_source> is set to true, this will fetch new source files
689from your CPAN mirror. Otherwise, C<reload_indices> will do its
690usual cache checking and only update them if they are out of date.
691
692By default, C<update_source> will be false.
693
694The verbose setting defaults to what you have specified in your
695config file.
696
697Returns true on success and false on failure.
698
699=cut
700
701sub reload_indices {
702 my $self = shift;
703 my %hash = @_;
704 my $conf = $self->configure_object;
705
706 my $tmpl = {
707 update_source => { default => 0, allow => [qr/^\d$/] },
708 verbose => { default => $conf->get_conf('verbose') },
709 };
710
711 my $args = check( $tmpl, \%hash ) or return;
712
713 ### make a call to the internal _module_tree, so it triggers cache
714 ### file age
715 my $uptodate = $self->_check_trees( %$args );
716
717
718 return 1 if $self->_build_trees(
719 uptodate => $uptodate,
720 use_stored => 0,
721 verbose => $conf->get_conf('verbose'),
722 );
723
724 error( loc( "Error rebuilding source trees!" ) );
725
726 return;
727}
728
729=pod
730
731=head2 $bool = $cb->flush(CACHE_NAME)
732
733This method allows flushing of caches.
734There are several things which can be flushed:
735
736=over 4
737
738=item * C<methods>
739
740The return status of methods which have been attempted, such as
741different ways of fetching files. It is recommended that automatic
742flushing be used instead.
743
744=item * C<hosts>
745
746The return status of URIs which have been attempted, such as
747different hosts of fetching files. It is recommended that automatic
748flushing be used instead.
749
750=item * C<modules>
751
752Information about modules such as prerequisites and whether
753installation succeeded, failed, or was not attempted.
754
755=item * C<lib>
756
757This resets PERL5LIB, which is changed to ensure that while installing
758modules they are in our @INC.
759
760=item * C<load>
761
762This resets the cache of modules we've attempted to load, but failed.
763This enables you to load them again after a failed load, if they
764somehow have become available.
765
766=item * C<all>
767
768Flush all of the aforementioned caches.
769
770=back
771
772Returns true on success and false on failure.
773
774=cut
775
776sub flush {
777 my $self = shift;
778 my $type = shift or return;
779
780 my $cache = {
781 methods => [ qw( methods load ) ],
782 hosts => [ qw( hosts ) ],
783 modules => [ qw( modules lib) ],
784 lib => [ qw( lib ) ],
785 load => [ qw( load ) ],
786 all => [ qw( hosts lib modules methods load ) ],
787 };
788
789 my $aref = $cache->{$type}
790 or (
791 error( loc("No such cache '%1'", $type) ),
792 return
793 );
794
795 return $self->_flush( list => $aref );
796}
797
798=pod
799
800=head2 @mods = $cb->installed()
801
802Returns a list of module objects of all your installed modules.
803If an error occurs, it will return false.
804
805See L<CPANPLUS::Module> for the operations you can perform on a
806module object.
807
808=cut
809
810sub installed {
811 my $self = shift;
812 my $aref = $self->_all_installed;
813
814 return @$aref if $aref;
815 return;
816}
817
818=pod
819
820=head2 $bool = $cb->local_mirror([path => '/dir/to/save/to', index_files => BOOL, force => BOOL, verbose => BOOL] )
821
822Creates a local mirror of CPAN, of only the most recent sources in a
823location you specify. If you set this location equal to a custom host
824in your C<CPANPLUS::Config> you can use your local mirror to install
825from.
826
827It takes the following arguments:
828
829=over 4
830
831=item path
832
833The location where to create the local mirror.
834
835=item index_files
836
5bc5f6dc 837Enable/disable fetching of index files. You can disable fetching of the
838index files if you don't plan to use the local mirror as your primary
839site, or if you'd like up-to-date index files be fetched from elsewhere.
6aaee015 840
841Defaults to true.
842
843=item force
844
845Forces refetching of packages, even if they are there already.
846
847Defaults to whatever setting you have in your C<CPANPLUS::Config>.
848
849=item verbose
850
851Prints more messages about what its doing.
852
853Defaults to whatever setting you have in your C<CPANPLUS::Config>.
854
855=back
856
857Returns true on success and false on error.
858
859=cut
860
861sub local_mirror {
862 my $self = shift;
863 my $conf = $self->configure_object;
864 my %hash = @_;
865
866 my($path, $index, $force, $verbose);
867 my $tmpl = {
868 path => { default => $conf->get_conf('base'),
869 store => \$path },
870 index_files => { default => 1, store => \$index },
871 force => { default => $conf->get_conf('force'),
872 store => \$force },
873 verbose => { default => $conf->get_conf('verbose'),
874 store => \$verbose },
875 };
876
877 check( $tmpl, \%hash ) or return;
878
879 unless( -d $path ) {
880 $self->_mkdir( dir => $path )
881 or( error( loc( "Could not create '%1', giving up", $path ) ),
882 return
883 );
884 } elsif ( ! -w _ ) {
885 error( loc( "Could not write to '%1', giving up", $path ) );
886 return;
887 }
888
889 my $flag;
890 AUTHOR: {
891 for my $auth ( sort { $a->cpanid cmp $b->cpanid }
892 values %{$self->author_tree}
893 ) {
894
895 MODULE: {
896 my $i;
897 for my $mod ( $auth->modules ) {
898 my $fetchdir = File::Spec->catdir( $path, $mod->path );
899
900 my %opts = (
901 verbose => $verbose,
902 force => $force,
903 fetchdir => $fetchdir,
904 );
905
906 ### only do this the for the first module ###
907 unless( $i++ ) {
908 $mod->_get_checksums_file(
909 %opts
910 ) or (
911 error( loc( "Could not fetch %1 file, " .
912 "skipping author '%2'",
913 CHECKSUMS, $auth->cpanid ) ),
914 $flag++, next AUTHOR
915 );
916 }
917
918 $mod->fetch( %opts )
919 or( error( loc( "Could not fetch '%1'", $mod->module ) ),
920 $flag++, next MODULE
921 );
922 } }
923 } }
924
925 if( $index ) {
926 for my $name (qw[auth dslip mod]) {
927 $self->_update_source(
928 name => $name,
929 verbose => $verbose,
930 path => $path,
931 ) or ( $flag++, next );
932 }
933 }
934
935 return !$flag;
936}
937
938=pod
939
940=head2 $file = $cb->autobundle([path => OUTPUT_PATH, force => BOOL, verbose => BOOL])
941
942Writes out a snapshot of your current installation in C<CPAN> bundle
943style. This can then be used to install the same modules for a
944different or on a different machine.
945
946It will, by default, write to an 'autobundle' directory under your
947cpanplus homedirectory, but you can override that by supplying a
948C<path> argument.
949
950It will return the location of the output file on success and false on
951failure.
952
953=cut
954
955sub autobundle {
956 my $self = shift;
957 my $conf = $self->configure_object;
958 my %hash = @_;
959
960 my($path,$force,$verbose);
961 my $tmpl = {
962 force => { default => $conf->get_conf('force'), store => \$force },
963 verbose => { default => $conf->get_conf('verbose'), store => \$verbose },
964 path => { default => File::Spec->catdir(
965 $conf->get_conf('base'),
966 $self->_perl_version( perl => $^X ),
967 $conf->_get_build('distdir'),
968 $conf->_get_build('autobundle') ),
969 store => \$path },
970 };
971
972 check($tmpl, \%hash) or return;
973
974 unless( -d $path ) {
975 $self->_mkdir( dir => $path )
976 or( error(loc("Could not create directory '%1'", $path ) ),
977 return
978 );
979 }
980
981 my $name; my $file;
982 { ### default filename for the bundle ###
983 my($year,$month,$day) = (localtime)[5,4,3];
984 $year += 1900; $month++;
985
986 my $ext = 0;
987
988 my $prefix = $conf->_get_build('autobundle_prefix');
989 my $format = "${prefix}_%04d_%02d_%02d_%02d";
990
991 BLOCK: {
992 $name = sprintf( $format, $year, $month, $day, $ext);
993
994 $file = File::Spec->catfile( $path, $name . '.pm' );
995
996 -f $file ? ++$ext && redo BLOCK : last BLOCK;
997 }
998 }
999 my $fh;
1000 unless( $fh = FileHandle->new( ">$file" ) ) {
1001 error( loc( "Could not open '%1' for writing: %2", $file, $! ) );
1002 return;
1003 }
5bc5f6dc 1004
1005 ### make sure we load the module tree *before* doing this, as it
1006 ### starts to chdir all over the place
1007 $self->module_tree;
6aaee015 1008
1009 my $string = join "\n\n",
1010 map {
1011 join ' ',
1012 $_->module,
1013 ($_->installed_version(verbose => 0) || 'undef')
1014 } sort {
1015 $a->module cmp $b->module
1016 } $self->installed;
1017
1018 my $now = scalar localtime;
1019 my $head = '=head1';
1020 my $pkg = __PACKAGE__;
1021 my $version = $self->VERSION;
1022 my $perl_v = join '', `$^X -V`;
1023
1024 print $fh <<EOF;
1025package $name
1026
1027\$VERSION = '0.01';
1028
10291;
1030
1031__END__
1032
1033$head NAME
1034
1035$name - Snapshot of your installation at $now
1036
1037$head SYNOPSIS
1038
1039perl -MCPANPLUS -e "install $name"
1040
1041$head CONTENTS
1042
1043$string
1044
1045$head CONFIGURATION
1046
1047$perl_v
1048
1049$head AUTHOR
1050
1051This bundle has been generated autotomatically by
1052 $pkg $version
1053
1054EOF
1055
1056 close $fh;
1057
1058 return $file;
1059}
1060
5bc5f6dc 1061### XXX these wrappers are not individually tested! only the underlying
1062### code through source.t and indirectly trought he CustomSource plugin.
1063=pod
1064
1065=head1 CUSTOM MODULE SOURCES
1066
1067Besides the sources as provided by the general C<CPAN> mirrors, it's
1068possible to add your own sources list to your C<CPANPLUS> index.
1069
1070The methodology behind this works much like C<Debian's apt-sources>.
1071
1072The methods below show you how to make use of this functionality. Also
1073note that most of these methods are available through the default shell
1074plugin command C</cs>, making them available as shortcuts through the
1075shell and via the commandline.
1076
1077=head2 %files = $cb->list_custom_sources
1078
1079Returns a mapping of registered custom sources and their local indices
1080as follows:
1081
1082 /full/path/to/local/index => http://remote/source
1083
1084Note that any file starting with an C<#> is being ignored.
1085
1086=cut
1087
1088sub list_custom_sources {
1089 return shift->__list_custom_module_sources( @_ );
1090}
1091
1092=head2 $local_index = $cb->add_custom_source( uri => URI, [verbose => BOOL] );
1093
1094Adds an C<URI> to your own sources list and mirrors its index. See the
1095documentation on C<< $cb->update_custom_source >> on how this is done.
1096
1097Returns the full path to the local index on success, or false on failure.
1098
1099Note that when adding a new C<URI>, the change to the in-memory tree is
1100not saved until you rebuild or save the tree to disk again. You can do
1101this using the C<< $cb->reload_indices >> method.
1102
1103=cut
1104
1105sub add_custom_source {
1106 return shift->_add_custom_module_source( @_ );
1107}
1108
1109=head2 $local_index = $cb->remove_custom_source( uri => URI, [verbose => BOOL] );
1110
1111Removes an C<URI> from your own sources list and removes its index.
1112
1113To find out what C<URI>s you have as part of your own sources list, use
1114the C<< $cb->list_custom_sources >> method.
1115
1116Returns the full path to the deleted local index file on success, or false
1117on failure.
1118
1119=cut
1120
1121### XXX do clever dispatching based on arg number?
1122sub remove_custom_source {
1123 return shift->_remove_custom_module_source( @_ );
1124}
1125
1126=head2 $bool = $cb->update_custom_source( [remote => URI] );
1127
1128Updates the indexes for all your custom sources. It does this by fetching
1129a file called C<packages.txt> in the root of the custom sources's C<URI>.
1130If you provide the C<remote> argument, it will only update the index for
1131that specific C<URI>.
1132
1133Here's an example of how custom sources would resolve into index files:
1134
1135 file:///path/to/sources => file:///path/to/sources/packages.txt
1136 http://example.com/sources => http://example.com/sources/packages.txt
1137 ftp://example.com/sources => ftp://example.com/sources/packages.txt
1138
1139The file C<packages.txt> simply holds a list of packages that can be found
1140under the root of the C<URI>. This file can be automatically generated for
1141you when the remote source is a C<file:// URI>. For C<http://>, C<ftp://>,
1142and similar, the administrator of that repository should run the method
1143C<< $cb->write_custom_source_index >> on the repository to allow remote
1144users to index it.
1145
1146For details, see the C<< $cb->write_custom_source_index >> method below.
1147
1148All packages that are added via this mechanism will be attributed to the
1149author with C<CPANID> C<LOCAL>. You can use this id to search for all
1150added packages.
1151
1152=cut
1153
1154sub update_custom_source {
1155 my $self = shift;
1156
1157 ### if it mentions /remote/, the request is to update a single uri,
1158 ### not all the ones we have, so dispatch appropriately
1159 my $rv = grep( /remote/i, @_)
1160 ? $self->__update_custom_module_source( @_ )
1161 : $self->__update_custom_module_sources( @_ );
1162
1163 return $rv;
1164}
1165
1166=head2 $file = $cb->write_custom_source_index( path => /path/to/package/root, [to => /path/to/index/file, verbose => BOOL] );
1167
1168Writes the index for a custom repository root. Most users will not have to
1169worry about this, but administrators of a repository will need to make sure
1170their indexes are up to date.
1171
1172The index will be written to a file called C<packages.txt> in your repository
1173root, which you can specify with the C<path> argument. You can override this
1174location by specifying the C<to> argument, but in normal operation, that should
1175not be required.
1176
1177Once the index file is written, users can then add the C<URI> pointing to
1178the repository to their custom list of sources and start using it right away. See the C<< $cb->add_custom_source >> method for user details.
1179
1180=cut
1181
1182sub write_custom_source_index {
1183 return shift->__write_custom_module_index( @_ );
1184}
1185
6aaee015 11861;
1187
1188=pod
1189
1190=head1 BUG REPORTS
1191
1192Please report bugs or other issues to E<lt>bug-cpanplus@rt.cpan.org<gt>.
1193
1194=head1 AUTHOR
1195
1196This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
1197
1198=head1 COPYRIGHT
1199
1200The CPAN++ interface (of which this module is a part of) is copyright (c)
12012001 - 2007, Jos Boumans E<lt>kane@cpan.orgE<gt>. All rights reserved.
1202
1203This library is free software; you may redistribute and/or modify it
1204under the same terms as Perl itself.
1205
1206=head1 SEE ALSO
1207
5bc5f6dc 1208L<CPANPLUS::Configure>, L<CPANPLUS::Module>, L<CPANPLUS::Module::Author>,
1209L<CPANPLUS::Selfupdate>
6aaee015 1210
1211=cut
1212
1213# Local variables:
1214# c-indentation-style: bsd
1215# c-basic-offset: 4
1216# indent-tabs-mode: nil
1217# End:
1218# vim: expandtab shiftwidth=4:
1219
1220__END__
1221
1222todo:
1223sub dist { # not sure about this one -- probably already done
1224 enough in Module.pm
1225sub reports { # in Module.pm, wrapper here
1226
1227