1 package CPANPLUS::Backend;
7 use CPANPLUS::Configure;
8 use CPANPLUS::Internals;
9 use CPANPLUS::Internals::Constants;
11 use CPANPLUS::Module::Author;
12 use CPANPLUS::Backend::RV;
16 use File::Spec::Unix ();
17 use File::Basename ();
18 use Params::Check qw[check];
19 use Locale::Maketext::Simple Class => 'CPANPLUS', Style => 'gettext';
21 $Params::Check::VERBOSE = 1;
23 use vars qw[@ISA $VERSION];
25 @ISA = qw[CPANPLUS::Internals];
26 $VERSION = $CPANPLUS::Internals::VERSION;
28 ### mark that we're running under CPANPLUS to spawned processes
29 $ENV{'PERL5_CPANPLUS_IS_RUNNING'} = $$;
31 ### XXX version.pm MAY format this version, if it's in use... :(
32 ### so for consistency, just call ->VERSION ourselves as well.
33 $ENV{'PERL5_CPANPLUS_IS_VERSION'} = __PACKAGE__->VERSION;
43 my $cb = CPANPLUS::Backend->new;
44 my $conf = $cb->configure_object;
46 my $author = $cb->author_tree('KANE');
47 my $mod = $cb->module_tree('Some::Module');
48 my $mod = $cb->parse_module( module => 'Some::Module' );
50 my @objs = $cb->search( type => TYPE,
60 This module provides the programmer's interface to the C<CPANPLUS>
65 When C<CPANPLUS::Backend> is loaded, which is necessary for just
66 about every <CPANPLUS> operation, the environment variable
67 C<PERL5_CPANPLUS_IS_RUNNING> is set to the current process id.
69 Additionally, the environment variable C<PERL5_CPANPLUS_IS_VERSION>
70 will be set to the version of C<CPANPLUS::Backend>.
72 This information might be useful somehow to spawned processes.
76 =head2 $cb = CPANPLUS::Backend->new( [CONFIGURE_OBJ] )
78 This method returns a new C<CPANPLUS::Backend> object.
79 This also initialises the config corresponding to this object.
80 You have two choices in this:
84 =item Provide a valid C<CPANPLUS::Configure> object
86 This will be used verbatim.
90 Your default config will be loaded and used.
94 New will return a C<CPANPLUS::Backend> object on success and die on
103 if( $_[0] && IS_CONFOBJ->( conf => $_[0] ) ) {
106 $conf = CPANPLUS::Configure->new() or return;
109 my $self = $class->SUPER::_init( _conf => $conf );
116 =head2 $href = $cb->module_tree( [@modules_names_list] )
118 Returns a reference to the CPANPLUS module tree.
120 If you give it any arguments, they will be treated as module names
121 and C<module_tree> will try to look up these module names and
122 return the corresponding module objects instead.
124 See L<CPANPLUS::Module> for the operations you can perform on a
131 my $modtree = $self->_module_tree;
135 for my $name ( grep { defined } @_) {
137 ### From John Malmberg: This is failing on VMS
138 ### because ODS-2 does not retain the case of
139 ### filenames that are created.
140 ### The problem is the filename is being converted
141 ### to a module name and then looked up in the
144 ### As a fix, we do a search on VMS instead --
145 ### more cpu cycles, but it gets around the case
151 allow => [qr/^$name$/i],
156 push @rv, $modobj || '';
158 return @rv == 1 ? $rv[0] : @rv;
166 =head2 $href = $cb->author_tree( [@author_names_list] )
168 Returns a reference to the CPANPLUS author tree.
170 If you give it any arguments, they will be treated as author names
171 and C<author_tree> will try to look up these author names and
172 return the corresponding author objects instead.
174 See L<CPANPLUS::Module::Author> for the operations you can perform on
181 my $authtree = $self->_author_tree;
186 push @rv, $authtree->{$name} || '';
188 return @rv == 1 ? $rv[0] : @rv;
196 =head2 $conf = $cb->configure_object;
198 Returns a copy of the C<CPANPLUS::Configure> object.
200 See L<CPANPLUS::Configure> for operations you can perform on a
205 sub configure_object { return shift->_conf() };
207 =head2 $su = $cb->selfupdate_object;
209 Returns a copy of the C<CPANPLUS::Selfupdate> object.
211 See the L<CPANPLUS::Selfupdate> manpage for the operations
212 you can perform on the selfupdate object.
216 sub selfupdate_object { return shift->_selfupdate() };
220 =head2 @mods = $cb->search( type => TYPE, allow => AREF, [data => AREF, verbose => BOOL] )
222 C<search> enables you to search for either module or author objects,
223 based on their data. The C<type> you can specify is any of the
224 accessors specified in C<CPANPLUS::Module::Author> or
225 C<CPANPLUS::Module>. C<search> will determine by the C<type> you
226 specified whether to search by author object or module object.
228 You have to specify an array reference of regular expressions or
229 strings to match against. The rules used for this array ref are the
230 same as in C<Params::Check>, so read that manpage for details.
232 The search is an C<or> search, meaning that if C<any> of the criteria
233 match, the search is considered to be successful.
235 You can specify the result of a previous search as C<data> to limit
236 the new search to these module or author objects, rather than the
237 entire module or author tree. This is how you do C<and> searches.
239 Returns a list of module or author objects on success and false
242 See L<CPANPLUS::Module> for the operations you can perform on a
244 See L<CPANPLUS::Module::Author> for the operations you can perform on
251 my $conf = $self->configure_object;
256 local $Params::Check::NO_DUPLICATES = 0;
257 local $Params::Check::ALLOW_UNKNOWN = 1;
260 type => { required => 1, allow => [CPANPLUS::Module->accessors(),
261 CPANPLUS::Module::Author->accessors()], store => \$type },
262 allow => { required => 1, default => [ ], strict_type => 1 },
265 check( $tmpl, \%hash )
268 ### figure out whether it was an author or a module search
269 ### when ambiguous, it'll be an author search.
271 if( grep { $type eq $_ } CPANPLUS::Module::Author->accessors() ) {
272 $aref = $self->_search_author_tree( %$args );
274 $aref = $self->_search_module_tree( %$args );
277 return @$aref if $aref;
283 =head2 $backend_rv = $cb->fetch( modules => \@mods )
285 Fetches a list of modules. C<@mods> can be a list of distribution
286 names, module names or module objects--basically anything that
287 L<parse_module> can understand.
289 See the equivalent method in C<CPANPLUS::Module> for details on
290 other options you can pass.
292 Since this is a multi-module method call, the return value is
293 implemented as a C<CPANPLUS::Backend::RV> object. Please consult
294 that module's documentation on how to interpret the return value.
296 =head2 $backend_rv = $cb->extract( modules => \@mods )
298 Extracts a list of modules. C<@mods> can be a list of distribution
299 names, module names or module objects--basically anything that
300 L<parse_module> can understand.
302 See the equivalent method in C<CPANPLUS::Module> for details on
303 other options you can pass.
305 Since this is a multi-module method call, the return value is
306 implemented as a C<CPANPLUS::Backend::RV> object. Please consult
307 that module's documentation on how to interpret the return value.
309 =head2 $backend_rv = $cb->install( modules => \@mods )
311 Installs a list of modules. C<@mods> can be a list of distribution
312 names, module names or module objects--basically anything that
313 L<parse_module> can understand.
315 See the equivalent method in C<CPANPLUS::Module> for details on
316 other options you can pass.
318 Since this is a multi-module method call, the return value is
319 implemented as a C<CPANPLUS::Backend::RV> object. Please consult
320 that module's documentation on how to interpret the return value.
322 =head2 $backend_rv = $cb->readme( modules => \@mods )
324 Fetches the readme for a list of modules. C<@mods> can be a list of
325 distribution names, module names or module objects--basically
326 anything that L<parse_module> can understand.
328 See the equivalent method in C<CPANPLUS::Module> for details on
329 other options you can pass.
331 Since this is a multi-module method call, the return value is
332 implemented as a C<CPANPLUS::Backend::RV> object. Please consult
333 that module's documentation on how to interpret the return value.
335 =head2 $backend_rv = $cb->files( modules => \@mods )
337 Returns a list of files used by these modules if they are installed.
338 C<@mods> can be a list of distribution names, module names or module
339 objects--basically anything that L<parse_module> can understand.
341 See the equivalent method in C<CPANPLUS::Module> for details on
342 other options you can pass.
344 Since this is a multi-module method call, the return value is
345 implemented as a C<CPANPLUS::Backend::RV> object. Please consult
346 that module's documentation on how to interpret the return value.
348 =head2 $backend_rv = $cb->distributions( modules => \@mods )
350 Returns a list of module objects representing all releases for this
352 C<@mods> can be a list of distribution names, module names or module
353 objects, basically anything that L<parse_module> can understand.
355 See the equivalent method in C<CPANPLUS::Module> for details on
356 other options you can pass.
358 Since this is a multi-module method call, the return value is
359 implemented as a C<CPANPLUS::Backend::RV> object. Please consult
360 that module's documentation on how to interpret the return value.
364 ### XXX add direcotry_tree, packlist etc? or maybe remove files? ###
365 for my $func (qw[fetch extract install readme files distributions]) {
370 my $conf = $self->configure_object;
375 local $Params::Check::NO_DUPLICATES = 1;
376 local $Params::Check::ALLOW_UNKNOWN = 1;
379 modules => { default => [], strict_type => 1,
380 required => 1, store => \$mods },
383 check( $tmpl, \%hash );
386 ### make them all into module objects ###
387 my %mods = map { $_ => $self->parse_module(module => $_) || '' } @$mods;
390 while( my($name,$obj) = each %mods ) {
391 $href->{$name} = IS_MODOBJ->( mod => $obj )
392 ? $obj->$func( %$args )
395 $flag++ unless $href->{$name};
398 return CPANPLUS::Backend::RV->new(
409 =head2 $mod_obj = $cb->parse_module( module => $modname|$distname|$modobj|URI|PATH )
411 C<parse_module> tries to find a C<CPANPLUS::Module> object that
412 matches your query. Here's a list of examples you could give to
417 =item Text::Bastardize
419 =item Text-Bastardize
421 =item Text-Bastardize-1.06
423 =item AYRNIEU/Text-Bastardize
425 =item AYRNIEU/Text-Bastardize-1.06
427 =item AYRNIEU/Text-Bastardize-1.06.tar.gz
429 =item http://example.com/Text-Bastardize-1.06.tar.gz
431 =item file:///tmp/Text-Bastardize-1.06.tar.gz
433 =item /tmp/Text-Bastardize-1.06
435 =item ./Text-Bastardize-1.06
441 These items would all come up with a C<CPANPLUS::Module> object for
442 C<Text::Bastardize>. The ones marked explicitly as being version 1.06
443 would give back a C<CPANPLUS::Module> object of that version.
444 Even if the version on CPAN is currently higher.
446 The last three are examples of PATH resolution. In the first, we supply
447 an absolute path to the unwrapped distribution. In the second the
448 distribution is relative to the current working directory.
449 In the third, we will use the current working directory.
451 If C<parse_module> is unable to actually find the module you are looking
452 for in its module tree, but you supplied it with an author, module
453 and version part in a distribution name or URI, it will create a fake
454 C<CPANPLUS::Module> object for you, that you can use just like the
457 See L<CPANPLUS::Module> for the operations you can perform on a
460 If even this fancy guessing doesn't enable C<parse_module> to create
461 a fake module object for you to use, it will warn about an error and
468 my $conf = $self->configure_object;
473 module => { required => 1, store => \$mod },
476 my $args = check( $tmpl, \%hash ) or return;
478 return $mod if IS_MODOBJ->( module => $mod );
480 ### ok, so it's not a module object, but a ref nonetheless?
481 ### what are you smoking?
483 error(loc("Can not parse module string from reference '%1'", $mod ));
487 ### check only for allowed characters in a module name
488 unless( $mod =~ /[^\w:]/ ) {
490 ### perhaps we can find it in the module tree?
491 my $maybe = $self->module_tree($mod);
492 return $maybe if IS_MODOBJ->( module => $maybe );
495 ### Special case arbitary file paths such as '.' etc.
496 if (-d File::Spec->rel2abs($mod) ) {
497 my $dir = File::Spec->rel2abs($mod);
498 my $parent = File::Spec->rel2abs( File::Spec->catdir( $dir, '..' ) );
502 $dir = VMS::Filespec::unixify($dir);
503 $parent = VMS::Filespec::unixify($parent);
506 my $dist = $mod = File::Basename::basename($dir);
507 $dist .= '-0' unless $dist =~ /\-[0-9._]+$/;
508 $dist .= '.tar.gz' unless $dist =~ /\.[A-Za-z]+$/;
510 my $modobj = CPANPLUS::Module::Fake->new(
515 author => CPANPLUS::Module::Author::Fake->new
518 ### better guess for the version
519 $modobj->version( $modobj->package_version )
520 if defined $modobj->package_version;
522 ### better guess at module name, if possible
523 if ( my $pkgname = $modobj->package_name ) {
524 $pkgname =~ s/-/::/g;
526 ### no sense replacing it unless we changed something
527 $modobj->module( $pkgname )
528 if ($pkgname ne $modobj->package_name) || $pkgname !~ /-/;
531 $modobj->status->fetch( $parent );
532 $modobj->status->extract( $dir );
533 $modobj->get_installer_type;
537 ### ok, so it looks like a distribution then?
538 my @parts = split '/', $mod;
539 my $dist = pop @parts;
542 if( $mod =~ m|\w+://.+| ) {
543 my $modobj = CPANPLUS::Module::Fake->new(
547 path => File::Spec::Unix->catdir(
548 $conf->_get_mirror('base'),
549 UNKNOWN_DL_LOCATION ),
550 author => CPANPLUS::Module::Author::Fake->new
553 ### set the fetch_from accessor so we know to by pass the
555 $modobj->status->_fetch_from( $mod );
557 ### better guess for the version
558 $modobj->version( $modobj->package_version )
559 if defined $modobj->package_version;
561 ### better guess at module name, if possible
562 if ( my $pkgname = $modobj->package_name ) {
563 $pkgname =~ s/-/::/g;
565 ### no sense replacing it unless we changed something
566 $modobj->module( $pkgname )
567 if ($pkgname ne $modobj->package_name) || $pkgname !~ /-/;
573 ### perhaps we can find it's a third party module?
574 { my $modobj = CPANPLUS::Module::Fake->new(
578 path => File::Spec::Unix->catdir(
579 $conf->_get_mirror('base'),
580 UNKNOWN_DL_LOCATION ),
581 author => CPANPLUS::Module::Author::Fake->new
583 if( $modobj->is_third_party ) {
584 my $info = $modobj->third_party_information;
586 $modobj->author->author( $info->{author} );
587 $modobj->author->email( $info->{author_url} );
588 $modobj->description( $info->{url} );
595 error( loc("%1 is not a proper distribution name!", $mod) );
599 ### there's wonky uris out there, like this:
600 ### E/EY/EYCK/Net/Lite/Net-Lite-FTP-0.091
601 ### compensate for that
603 ### you probably have an A/AB/ABC/....../Dist.tgz type uri
604 if( (defined $parts[0] and length $parts[0] == 1) and
605 (defined $parts[1] and length $parts[1] == 2) and
606 $parts[2] =~ /^$parts[0]/i and $parts[2] =~ /^$parts[1]/i
608 splice @parts, 0, 2; # remove the first 2 entries from the list
609 $author = shift @parts; # this is the actual author name then
611 ### we''ll assume a ABC/..../Dist.tgz
613 $author = shift @parts || '';
616 my($pkg, $version, $ext, $full) =
617 $self->_split_package_string( package => $dist );
619 ### translate a distribution into a module name ###
621 $guess =~ s/-/::/g if $guess;
623 my $maybe = $self->module_tree( $guess );
624 if( IS_MODOBJ->( module => $maybe ) ) {
626 ### maybe you asked for a package instead
627 if ( $maybe->package eq $mod ) {
630 ### perhaps an outdated version instead?
631 } elsif ( $version ) {
632 my $auth_obj; my $path;
634 ### did you give us an author part? ###
636 $auth_obj = CPANPLUS::Module::Author::Fake->new(
638 cpanid => uc $author,
639 author => uc $author,
641 $path = File::Spec::Unix->catdir(
642 $conf->_get_mirror('base'),
643 substr(uc $author, 0, 1),
644 substr(uc $author, 0, 2),
646 @parts, #possible sub dirs
649 $auth_obj = $maybe->author;
650 $path = $maybe->path;
653 if( $maybe->package_name eq $pkg ) {
655 my $modobj = CPANPLUS::Module::Fake->new(
656 module => $maybe->module,
658 ### no extension? use the extension the original package
662 : $full .'.'. $maybe->package_extension
670 ### you asked for a specific version?
671 ### assume our $maybe is the one you wanted,
672 ### and fix up the version..
675 my $modobj = $maybe->clone;
676 $modobj->version( $version );
678 $maybe->package_name .'-'.
680 $maybe->package_extension
683 ### you wanted a specific author, but it's not the one
684 ### from the module tree? we'll fix it up
685 if( $author and $author ne $modobj->author->cpanid ) {
686 $modobj->author( $auth_obj );
687 $modobj->path( $path );
693 ### you didn't care about a version, so just return the object then
694 } elsif ( !$version ) {
698 ### ok, so we can't find it, and it's not an outdated dist either
699 ### perhaps we can fake one based on the author name and so on
700 } elsif ( $author and $version ) {
702 ### be extra friendly and pad the .tar.gz suffix where needed
703 ### it's just a guess of course, but most dists are .tar.gz
704 $dist .= '.tar.gz' unless $dist =~ /\.[A-Za-z]+$/;
706 ### XXX duplication from above for generating author obj + path...
707 my $modobj = CPANPLUS::Module::Fake->new(
711 author => CPANPLUS::Module::Author::Fake->new(
712 author => uc $author,
713 cpanid => uc $author,
716 path => File::Spec::Unix->catdir(
717 $conf->_get_mirror('base'),
718 substr(uc $author, 0, 1),
719 substr(uc $author, 0, 2),
721 @parts, #possible subdirs
728 ### face it, we have /no/ idea what he or she wants...
729 ### let's start putting the blame somewhere
733 error( loc( "'%1' does not contain an author part", $mod ) );
736 error( loc( "Cannot find '%1' in the module tree", $mod ) );
744 =head2 $bool = $cb->reload_indices( [update_source => BOOL, verbose => BOOL] );
746 This method reloads the source files.
748 If C<update_source> is set to true, this will fetch new source files
749 from your CPAN mirror. Otherwise, C<reload_indices> will do its
750 usual cache checking and only update them if they are out of date.
752 By default, C<update_source> will be false.
754 The verbose setting defaults to what you have specified in your
757 Returns true on success and false on failure.
764 my $conf = $self->configure_object;
767 update_source => { default => 0, allow => [qr/^\d$/] },
768 verbose => { default => $conf->get_conf('verbose') },
771 my $args = check( $tmpl, \%hash ) or return;
773 ### make a call to the internal _module_tree, so it triggers cache
775 my $uptodate = $self->_check_trees( %$args );
778 return 1 if $self->_build_trees(
779 uptodate => $uptodate,
781 verbose => $conf->get_conf('verbose'),
784 error( loc( "Error rebuilding source trees!" ) );
791 =head2 $bool = $cb->flush(CACHE_NAME)
793 This method allows flushing of caches.
794 There are several things which can be flushed:
800 The return status of methods which have been attempted, such as
801 different ways of fetching files. It is recommended that automatic
802 flushing be used instead.
806 The return status of URIs which have been attempted, such as
807 different hosts of fetching files. It is recommended that automatic
808 flushing be used instead.
812 Information about modules such as prerequisites and whether
813 installation succeeded, failed, or was not attempted.
817 This resets PERL5LIB, which is changed to ensure that while installing
818 modules they are in our @INC.
822 This resets the cache of modules we've attempted to load, but failed.
823 This enables you to load them again after a failed load, if they
824 somehow have become available.
828 Flush all of the aforementioned caches.
832 Returns true on success and false on failure.
838 my $type = shift or return;
841 methods => [ qw( methods load ) ],
842 hosts => [ qw( hosts ) ],
843 modules => [ qw( modules lib) ],
844 lib => [ qw( lib ) ],
845 load => [ qw( load ) ],
846 all => [ qw( hosts lib modules methods load ) ],
849 my $aref = $cache->{$type}
851 error( loc("No such cache '%1'", $type) ),
855 return $self->_flush( list => $aref );
860 =head2 @mods = $cb->installed()
862 Returns a list of module objects of all your installed modules.
863 If an error occurs, it will return false.
865 See L<CPANPLUS::Module> for the operations you can perform on a
872 my $aref = $self->_all_installed;
874 return @$aref if $aref;
880 =head2 $bool = $cb->local_mirror([path => '/dir/to/save/to', index_files => BOOL, force => BOOL, verbose => BOOL] )
882 Creates a local mirror of CPAN, of only the most recent sources in a
883 location you specify. If you set this location equal to a custom host
884 in your C<CPANPLUS::Config> you can use your local mirror to install
887 It takes the following arguments:
893 The location where to create the local mirror.
897 Enable/disable fetching of index files. You can disable fetching of the
898 index files if you don't plan to use the local mirror as your primary
899 site, or if you'd like up-to-date index files be fetched from elsewhere.
905 Forces refetching of packages, even if they are there already.
907 Defaults to whatever setting you have in your C<CPANPLUS::Config>.
911 Prints more messages about what its doing.
913 Defaults to whatever setting you have in your C<CPANPLUS::Config>.
917 Returns true on success and false on error.
923 my $conf = $self->configure_object;
926 my($path, $index, $force, $verbose);
928 path => { default => $conf->get_conf('base'),
930 index_files => { default => 1, store => \$index },
931 force => { default => $conf->get_conf('force'),
933 verbose => { default => $conf->get_conf('verbose'),
934 store => \$verbose },
937 check( $tmpl, \%hash ) or return;
940 $self->_mkdir( dir => $path )
941 or( error( loc( "Could not create '%1', giving up", $path ) ),
945 error( loc( "Could not write to '%1', giving up", $path ) );
951 for my $auth ( sort { $a->cpanid cmp $b->cpanid }
952 values %{$self->author_tree}
957 for my $mod ( $auth->modules ) {
958 my $fetchdir = File::Spec->catdir( $path, $mod->path );
963 fetchdir => $fetchdir,
966 ### only do this the for the first module ###
968 $mod->_get_checksums_file(
971 error( loc( "Could not fetch %1 file, " .
972 "skipping author '%2'",
973 CHECKSUMS, $auth->cpanid ) ),
979 or( error( loc( "Could not fetch '%1'", $mod->module ) ),
986 for my $name (qw[auth dslip mod]) {
987 $self->_update_source(
991 ) or ( $flag++, next );
1000 =head2 $file = $cb->autobundle([path => OUTPUT_PATH, force => BOOL, verbose => BOOL])
1002 Writes out a snapshot of your current installation in C<CPAN> bundle
1003 style. This can then be used to install the same modules for a
1004 different or on a different machine by issuing the following commands:
1006 ### using the default shell:
1007 CPAN Terminal> i file://path/to/Snapshot_XXYY.pm
1010 $modobj = $cb->parse_module( module => 'file://path/to/Snapshot_XXYY.pm' );
1013 It will, by default, write to an 'autobundle' directory under your
1014 cpanplus homedirectory, but you can override that by supplying a
1017 It will return the location of the output file on success and false on
1024 my $conf = $self->configure_object;
1027 my($path,$force,$verbose);
1029 force => { default => $conf->get_conf('force'), store => \$force },
1030 verbose => { default => $conf->get_conf('verbose'), store => \$verbose },
1031 path => { default => File::Spec->catdir(
1032 $conf->get_conf('base'),
1033 $self->_perl_version( perl => $^X ),
1034 $conf->_get_build('distdir'),
1035 $conf->_get_build('autobundle') ),
1039 check($tmpl, \%hash) or return;
1041 unless( -d $path ) {
1042 $self->_mkdir( dir => $path )
1043 or( error(loc("Could not create directory '%1'", $path ) ),
1049 { ### default filename for the bundle ###
1050 my($year,$month,$day) = (localtime)[5,4,3];
1051 $year += 1900; $month++;
1055 my $prefix = $conf->_get_build('autobundle_prefix');
1056 my $format = "${prefix}_%04d_%02d_%02d_%02d";
1059 $name = sprintf( $format, $year, $month, $day, $ext);
1061 $file = File::Spec->catfile( $path, $name . '.pm' );
1063 -f $file ? ++$ext && redo BLOCK : last BLOCK;
1067 unless( $fh = FileHandle->new( ">$file" ) ) {
1068 error( loc( "Could not open '%1' for writing: %2", $file, $! ) );
1072 ### make sure we load the module tree *before* doing this, as it
1073 ### starts to chdir all over the place
1076 my $string = join "\n\n",
1080 ($_->installed_version(verbose => 0) || 'undef')
1082 $a->module cmp $b->module
1085 my $now = scalar localtime;
1086 my $head = '=head1';
1087 my $pkg = __PACKAGE__;
1088 my $version = $self->VERSION;
1089 my $perl_v = join '', `$^X -V`;
1102 $name - Snapshot of your installation at $now
1106 To install the modules from this snapshot, run:
1108 cpanp -i file://full/path/to/${name}.pm
1120 This bundle has been generated autotomatically by
1130 =head2 $bool = $cb->save_state
1132 Explicit command to save memory state to disk. This can be used to save
1133 information to disk about where a module was extracted, the result of
1134 C<make test>, etc. This will then be re-loaded into memory when a new
1137 The capability of saving state to disk depends on the source engine
1138 being used (See C<CPANPLUS::Config> for the option to choose your
1139 source engine). The default storage engine supports this option.
1141 Most users will not need this command, but it can handy for automated
1142 systems like setting up CPAN smoke testers.
1144 The method will return true if it managed to save the state to disk,
1145 or false if it did not.
1151 return $self->_save_state( @_ );
1155 ### XXX these wrappers are not individually tested! only the underlying
1156 ### code through source.t and indirectly trought he CustomSource plugin.
1159 =head1 CUSTOM MODULE SOURCES
1161 Besides the sources as provided by the general C<CPAN> mirrors, it's
1162 possible to add your own sources list to your C<CPANPLUS> index.
1164 The methodology behind this works much like C<Debian's apt-sources>.
1166 The methods below show you how to make use of this functionality. Also
1167 note that most of these methods are available through the default shell
1168 plugin command C</cs>, making them available as shortcuts through the
1169 shell and via the commandline.
1171 =head2 %files = $cb->list_custom_sources
1173 Returns a mapping of registered custom sources and their local indices
1176 /full/path/to/local/index => http://remote/source
1178 Note that any file starting with an C<#> is being ignored.
1182 sub list_custom_sources {
1183 return shift->__list_custom_module_sources( @_ );
1186 =head2 $local_index = $cb->add_custom_source( uri => URI, [verbose => BOOL] );
1188 Adds an C<URI> to your own sources list and mirrors its index. See the
1189 documentation on C<< $cb->update_custom_source >> on how this is done.
1191 Returns the full path to the local index on success, or false on failure.
1193 Note that when adding a new C<URI>, the change to the in-memory tree is
1194 not saved until you rebuild or save the tree to disk again. You can do
1195 this using the C<< $cb->reload_indices >> method.
1199 sub add_custom_source {
1200 return shift->_add_custom_module_source( @_ );
1203 =head2 $local_index = $cb->remove_custom_source( uri => URI, [verbose => BOOL] );
1205 Removes an C<URI> from your own sources list and removes its index.
1207 To find out what C<URI>s you have as part of your own sources list, use
1208 the C<< $cb->list_custom_sources >> method.
1210 Returns the full path to the deleted local index file on success, or false
1215 ### XXX do clever dispatching based on arg number?
1216 sub remove_custom_source {
1217 return shift->_remove_custom_module_source( @_ );
1220 =head2 $bool = $cb->update_custom_source( [remote => URI] );
1222 Updates the indexes for all your custom sources. It does this by fetching
1223 a file called C<packages.txt> in the root of the custom sources's C<URI>.
1224 If you provide the C<remote> argument, it will only update the index for
1225 that specific C<URI>.
1227 Here's an example of how custom sources would resolve into index files:
1229 file:///path/to/sources => file:///path/to/sources/packages.txt
1230 http://example.com/sources => http://example.com/sources/packages.txt
1231 ftp://example.com/sources => ftp://example.com/sources/packages.txt
1233 The file C<packages.txt> simply holds a list of packages that can be found
1234 under the root of the C<URI>. This file can be automatically generated for
1235 you when the remote source is a C<file:// URI>. For C<http://>, C<ftp://>,
1236 and similar, the administrator of that repository should run the method
1237 C<< $cb->write_custom_source_index >> on the repository to allow remote
1240 For details, see the C<< $cb->write_custom_source_index >> method below.
1242 All packages that are added via this mechanism will be attributed to the
1243 author with C<CPANID> C<LOCAL>. You can use this id to search for all
1248 sub update_custom_source {
1251 ### if it mentions /remote/, the request is to update a single uri,
1252 ### not all the ones we have, so dispatch appropriately
1253 my $rv = grep( /remote/i, @_)
1254 ? $self->__update_custom_module_source( @_ )
1255 : $self->__update_custom_module_sources( @_ );
1260 =head2 $file = $cb->write_custom_source_index( path => /path/to/package/root, [to => /path/to/index/file, verbose => BOOL] );
1262 Writes the index for a custom repository root. Most users will not have to
1263 worry about this, but administrators of a repository will need to make sure
1264 their indexes are up to date.
1266 The index will be written to a file called C<packages.txt> in your repository
1267 root, which you can specify with the C<path> argument. You can override this
1268 location by specifying the C<to> argument, but in normal operation, that should
1271 Once the index file is written, users can then add the C<URI> pointing to
1272 the repository to their custom list of sources and start using it right away. See the C<< $cb->add_custom_source >> method for user details.
1276 sub write_custom_source_index {
1277 return shift->__write_custom_module_index( @_ );
1286 Please report bugs or other issues to E<lt>bug-cpanplus@rt.cpan.org<gt>.
1290 This module by Jos Boumans E<lt>kane@cpan.orgE<gt>.
1294 The CPAN++ interface (of which this module is a part of) is copyright (c)
1295 2001 - 2007, Jos Boumans E<lt>kane@cpan.orgE<gt>. All rights reserved.
1297 This library is free software; you may redistribute and/or modify it
1298 under the same terms as Perl itself.
1302 L<CPANPLUS::Configure>, L<CPANPLUS::Module>, L<CPANPLUS::Module::Author>,
1303 L<CPANPLUS::Selfupdate>
1308 # c-indentation-style: bsd
1310 # indent-tabs-mode: nil
1312 # vim: expandtab shiftwidth=4:
1317 sub dist { # not sure about this one -- probably already done
1319 sub reports { # in Module.pm, wrapper here