This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Upgrade CPAN from version 2.05 to 2.09-TRIAL
authorSteve Hay <steve.m.hay@googlemail.com>
Sat, 21 Feb 2015 18:06:25 +0000 (18:06 +0000)
committerSteve Hay <steve.m.hay@googlemail.com>
Sat, 21 Feb 2015 18:06:25 +0000 (18:06 +0000)
17 files changed:
MANIFEST
Makefile.SH
Porting/Maintainers.pl
cpan/CPAN/lib/App/Cpan.pm
cpan/CPAN/lib/CPAN.pm
cpan/CPAN/lib/CPAN/Distribution.pm
cpan/CPAN/lib/CPAN/FirstTime.pm
cpan/CPAN/lib/CPAN/HTTP/Credentials.pm
cpan/CPAN/lib/CPAN/HandleConfig.pm
cpan/CPAN/lib/CPAN/Module.pm
cpan/CPAN/lib/CPAN/Plugin.pm [new file with mode: 0644]
cpan/CPAN/lib/CPAN/Plugin/Specfile.pm [new file with mode: 0644]
cpan/CPAN/lib/CPAN/Shell.pm
cpan/CPAN/scripts/cpan
cpan/CPAN/t/03pkgs.t
pod/perldelta.pod
t/porting/customized.dat

index 1974fc9..ec1d4c5 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -232,6 +232,8 @@ cpan/CPAN/lib/CPAN/LWP/UserAgent.pm helper package for CPAN.pm
 cpan/CPAN/lib/CPAN/Mirrors.pm          helper package for CPAN.pm
 cpan/CPAN/lib/CPAN/Module.pm           helper package for CPAN.pm
 cpan/CPAN/lib/CPAN/Nox.pm                      Runs CPAN while avoiding compiled extensions
+cpan/CPAN/lib/CPAN/Plugin.pm
+cpan/CPAN/lib/CPAN/Plugin/Specfile.pm
 cpan/CPAN/lib/CPAN.pm                  Interface to Comprehensive Perl Archive Network
 cpan/CPAN/lib/CPAN/Prompt.pm
 cpan/CPAN/lib/CPAN/Queue.pm            queueing system for CPAN.pm
index 05202ae..19c50e8 100755 (executable)
@@ -1341,10 +1341,10 @@ _cleaner2:
        -rmdir lib/Exporter lib/Encode/Unicode lib/Encode/MIME/Header
        -rmdir lib/Encode/MIME lib/Encode/KR lib/Encode/JP lib/Encode/CN
        -rmdir lib/Encode lib/Digest lib/Devel lib/Data lib/Config/Perl
-       -rmdir lib/Compress/Raw lib/Compress lib/Carp lib/CPAN/Meta/History
-       -rmdir lib/CPAN/Meta lib/CPAN/LWP lib/CPAN/Kwalify lib/CPAN/HTTP
-       -rmdir lib/CPAN/FTP lib/CPAN/Exception lib/CPAN/API lib/CPAN
-       -rmdir lib/Attribute lib/Archive/Tar lib/Archive
+       -rmdir lib/Compress/Raw lib/Compress lib/Carp lib/CPAN/Plugin
+       -rmdir lib/CPAN/Meta/History lib/CPAN/Meta lib/CPAN/LWP
+       -rmdir lib/CPAN/Kwalify lib/CPAN/HTTP lib/CPAN/FTP lib/CPAN/Exception
+       -rmdir lib/CPAN/API lib/CPAN lib/Attribute lib/Archive/Tar lib/Archive
        -rmdir lib/App/Prove/State/Result lib/App/Prove/State lib/App/Prove
        -rmdir lib/App
 
index a622016..a28c2a7 100755 (executable)
@@ -243,13 +243,14 @@ use File::Glob qw(:case);
     },
 
     'CPAN' => {
-        'DISTRIBUTION' => 'ANDK/CPAN-2.05.tar.gz',
+        'DISTRIBUTION' => 'ANDK/CPAN-2.09-TRIAL.tar.gz',
         'FILES'        => q[cpan/CPAN],
         'EXCLUDED'     => [
             qr{^distroprefs/},
             qr{^inc/Test/},
             qr{^t/CPAN/},
             qr{^t/data/},
+            qr{^t/97-},
             qw( lib/CPAN/Admin.pm
                 scripts/cpan-mirrors
                 PAUSE2015.pub
@@ -277,18 +278,6 @@ use File::Glob qw(:case);
                 t/yaml_code.yml
                 ),
         ],
-        # Waiting to be merged upstream: see pull request #83
-        'CUSTOMIZED'   => [
-            qw( lib/CPAN/Author.pm
-                lib/CPAN/CacheMgr.pm
-                lib/CPAN/FTP.pm
-                lib/CPAN/HTTP/Client.pm
-                lib/CPAN/HandleConfig.pm
-                lib/CPAN/Index.pm
-                lib/CPAN/LWP/UserAgent.pm
-                lib/CPAN/Mirrors.pm
-                ),
-        ],
     },
 
     # Note: When updating CPAN-Meta the META.* files will need to be regenerated
index b548bcc..e8c9bb7 100644 (file)
@@ -4,9 +4,9 @@ use strict;
 use warnings;
 use vars qw($VERSION);
 
-use if $] < 5.008 => "IO::Scalar";
+use if $] < 5.008 => 'IO::Scalar';
 
-$VERSION = '1.62';
+$VERSION = '1.63';
 
 =head1 NAME
 
@@ -23,6 +23,9 @@ App::Cpan - easily interact with CPAN from the command line
        # use local::lib
        cpan -I module_name [ module_name ... ]
 
+       # one time mirror override for faster mirrors
+       cpan -p ...
+
        # with just the dot, install from the distribution in the
        # current directory
        cpan .
@@ -135,6 +138,11 @@ List the modules by the specified authors.
 
 Make the specified modules.
 
+=item -M mirror1,mirror2,...
+
+A comma-separated list of mirrors to use for just this run. The C<-P>
+option can find them for you automatically.
+
 =item -n
 
 Do a dry run, but don't actually install anything. (unimplemented)
@@ -145,11 +153,12 @@ Show the out-of-date modules.
 
 =item -p
 
-Ping the configured mirrors
+Ping the configured mirrors and print a report
 
 =item -P
 
-Find the best mirrors you could be using (but doesn't configure them just yet)
+Find the best mirrors you could be using and use them for the current
+session.
 
 =item -r
 
@@ -208,6 +217,51 @@ and tells you about problems you might have.
        # force install modules ( must use -i )
        cpan -fi CGI::Minimal URI
 
+       # install modules but without testing them
+       cpan -Ti CGI::Minimal URI
+
+=head2 Environment variables
+
+There are several components in CPAN.pm that use environment variables.
+The build tools, L<ExtUtils::MakeMaker> and L<Module::Build> use some,
+while others matter to the levels above them. Some of these are specified
+by the Perl Toolchain Gang:
+
+Lancaster Concensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md>
+
+Oslo Concensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/oslo-consensus.md>
+
+=over 4
+
+=item NONINTERACTIVE_TESTING
+
+Assume no one is paying attention and skips prompts for distributions
+that do that correctly. C<cpan(1)> sets this to C<1> unless it already
+has a value (even if that value is false).
+
+=item PERL_MM_USE_DEFAULT
+
+Use the default answer for a prompted questions. C<cpan(1)> sets this
+to C<1> unless it already has a value (even if that value is false).
+
+=item CPAN_OPTS
+
+As with C<PERL5OPTS>, a string of additional C<cpan(1)> options to
+add to those you specify on the command line.
+
+=item CPANSCRIPT_LOGLEVEL
+
+The log level to use, with either the embedded, minimal logger or
+L<Log::Log4perl> if it is installed. Possible values are the same as
+the C<Log::Log4perl> levels: C<TRACE>, C<DEBUG>, C<INFO>, C<WARN>,
+C<ERROR>, and C<FATAL>. The default is C<INFO>.
+
+=item GIT_COMMAND
+
+The path to the C<git> binary to use for the Git features. The default
+is C</usr/local/bin/git>.
+
+=back
 
 =head2 Methods
 
@@ -216,7 +270,7 @@ and tells you about problems you might have.
 =cut
 
 use autouse Carp => qw(carp croak cluck);
-use CPAN ();
+use CPAN 1.80 (); # needs no test
 use Config;
 use autouse Cwd => qw(cwd);
 use autouse 'Data::Dumper' => qw(Dumper);
@@ -245,7 +299,7 @@ BEGIN { # most of this should be in methods
 use vars qw( @META_OPTIONS $Default %CPAN_METHODS @CPAN_OPTIONS  @option_order
        %Method_table %Method_table_index );
 
-@META_OPTIONS = qw( h v V I g G C A D O l L a r p P j: J w T);
+@META_OPTIONS = qw( h v V I g G M: C A D O l L a r p P j: J w T);
 
 $Default = 'default';
 
@@ -257,6 +311,7 @@ $Default = 'default';
        'm'      => 'make',
        't'      => 'test',
        'u'      => 'upgrade',
+       'T'      => 'notest',
        );
 @CPAN_OPTIONS = grep { $_ ne $Default } sort keys %CPAN_METHODS;
 
@@ -283,8 +338,9 @@ sub GOOD_EXIT () { 0 }
        J =>  [ \&_dump_config,       NO_ARGS, GOOD_EXIT, 'Dump configuration to stdout' ],
        F =>  [ \&_lock_lobotomy,     NO_ARGS, GOOD_EXIT, 'Turn off CPAN.pm lock files'  ],
        I =>  [ \&_load_local_lib,    NO_ARGS, GOOD_EXIT, 'Loading local::lib'           ],
+       M =>  [ \&_use_these_mirrors,    ARGS, GOOD_EXIT, 'Setting per session mirrors'  ],
+       P =>  [ \&_find_good_mirrors, NO_ARGS, GOOD_EXIT, 'Finding good mirrors'         ],
     w =>  [ \&_turn_on_warnings,  NO_ARGS, GOOD_EXIT, 'Turning on warnings'          ],
-    T =>  [ \&_turn_off_testing,  NO_ARGS, GOOD_EXIT, 'Turning off testing'          ],
 
        # options that do their one thing
        g =>  [ \&_download,          NO_ARGS, GOOD_EXIT, 'Download the latest distro'        ],
@@ -299,7 +355,6 @@ sub GOOD_EXIT () { 0 }
        L =>  [ \&_show_author_mods,     ARGS, GOOD_EXIT, 'Showing author mods'          ],
        a =>  [ \&_create_autobundle, NO_ARGS, GOOD_EXIT, 'Creating autobundle'          ],
        p =>  [ \&_ping_mirrors,      NO_ARGS, GOOD_EXIT, 'Pinging mirrors'              ],
-       P =>  [ \&_find_good_mirrors, NO_ARGS, GOOD_EXIT, 'Finding good mirrors'         ],
 
        r =>  [ \&_recompile,         NO_ARGS, GOOD_EXIT, 'Recompiling'                  ],
        u =>  [ \&_upgrade,           NO_ARGS, GOOD_EXIT, 'Running `make test`'          ],
@@ -309,6 +364,7 @@ sub GOOD_EXIT () { 0 }
        i =>  [ \&_default,              ARGS, GOOD_EXIT, 'Running `make install`'       ],
    'm' => [ \&_default,              ARGS, GOOD_EXIT, 'Running `make`'               ],
        t =>  [ \&_default,              ARGS, GOOD_EXIT, 'Running `make test`'          ],
+       T =>  [ \&_default,              ARGS, GOOD_EXIT, 'Installing with notest'       ],
        );
 
 %Method_table_index = (
@@ -364,7 +420,9 @@ sub _process_setup_options
                        );
                }
 
-       foreach my $o ( qw(F I w T) )
+       $class->_turn_off_testing if $options->{T};
+
+       foreach my $o ( qw(F I w P M) )
                {
                next unless exists $options->{$o};
                $Method_table{$o}[ $Method_table_index{code} ]->( $options->{$o} );
@@ -385,13 +443,25 @@ sub _process_setup_options
 
        my $option_count = grep { $options->{$_} } @option_order;
        no warnings 'uninitialized';
-       $option_count -= $options->{'f'}; # don't count force
+
+       # don't count options that imply installation
+       foreach my $opt ( qw(f T) ) { # don't count force or notest
+               $option_count -= $options->{$opt};
+               }
 
        # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
        # if there are no options, set -i (this line fixes RT ticket 16915)
        $options->{i}++ unless $option_count;
        }
 
+sub _setup_environment {
+# should we override or set defaults? If this were a true interactive
+# session, we'd be in the CPAN shell.
+
+# https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md
+       $ENV{NONINTERACTIVE_TESTING} = 1 unless defined $ENV{NONINTERACTIVE_TESTING};
+       $ENV{PERL_MM_USE_DEFAULT}    = 1 unless defined $ENV{PERL_MM_USE_DEFAULT};
+       }
 
 =item run()
 
@@ -424,13 +494,15 @@ sub run
 
        $class->_process_setup_options( $options );
 
+       $class->_setup_environment( $options );
+
        OPTION: foreach my $option ( @option_order )
                {
                next unless $options->{$option};
 
                my( $sub, $takes_args, $description ) =
                        map { $Method_table{$option}[ $Method_table_index{$_} ] }
-                       qw( code takes_args );
+                       qw( code takes_args description );
 
                unless( ref $sub eq ref sub {} )
                        {
@@ -464,6 +536,7 @@ sub _init_logger
 
     unless( $log4perl_loaded )
         {
+        print "Loading internal null logger. Install Log::Log4perl for logging messages\n";
         $logger = Local::Null::Logger->new;
         return $logger;
         }
@@ -494,7 +567,7 @@ sub _default
        # we'll deal with 'f' (force) later, so skip it
        foreach my $option ( @CPAN_OPTIONS )
                {
-               next if $option eq 'f';
+               next if ( $option eq 'f' or $option eq 'T' );
                next unless $options->{$option};
                $switch = $option;
                last;
@@ -512,24 +585,30 @@ sub _default
        my $method = $CPAN_METHODS{$switch};
        die "CPAN.pm cannot $method!\n" unless CPAN::Shell->can( $method );
 
-       # call the CPAN::Shell method, with force if specified
+       # call the CPAN::Shell method, with force or notest if specified
        my $action = do {
-               if( $options->{f} ) { sub { CPAN::Shell->force( $method, @_ ) } }
-               else                { sub { CPAN::Shell->$method( @_ )        } }
+                  if( $options->{f} ) { sub { CPAN::Shell->force( $method, @_ )  } }
+               elsif( $options->{T} ) { sub { CPAN::Shell->notest( $method, @_ ) } }
+               else                   { sub { CPAN::Shell->$method( @_ )         } }
                };
 
        # How do I handle exit codes for multiple arguments?
-       my $errors = 0;
+       my @errors = ();
 
        foreach my $arg ( @$args )
                {
                _clear_cpanpm_output();
                $action->( $arg );
 
-               $errors += defined _cpanpm_output_indicates_failure();
+               my $error = _cpanpm_output_indicates_failure();
+               push @errors, $error if $error;
                }
 
-       $errors ? I_DONT_KNOW_WHAT_HAPPENED : HEY_IT_WORKED;
+       return do {
+               if( @errors ) { $errors[0] }
+               else { HEY_IT_WORKED }
+               };
+
        }
 
 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
@@ -571,21 +650,32 @@ sub _clear_cpanpm_output { $scalar = '' }
 
 sub _get_cpanpm_output   { $scalar }
 
+# These are lines I don't care about in CPAN.pm output. If I can
+# filter out the informational noise, I have a better chance to
+# catch the error signal
 my @skip_lines = (
        qr/^\QWarning \(usually harmless\)/,
        qr/\bwill not store persistent state\b/,
        qr(//hint//),
        qr/^\s+reports\s+/,
+       qr/^Try the command/,
+       qr/^\s+$/,
+       qr/^to find objects/,
+       qr/^\s*Database was generated on/,
+       qr/^Going to read/,
+       qr|^\s+i\s+/|,    # the i /Foo::Whatever/ line when it doesn't know
        );
 
 sub _get_cpanpm_last_line
        {
        my $fh;
-       if ($] < 5.008) {
-               $fh = IO::Scalar->new(\ $scalar);
-        } else {
-               eval q{open $fh, "<", \\ $scalar;};
-        }
+
+       if( $] < 5.008 ) {
+               $fh = IO::Scalar->new( \ $scalar );
+               }
+       else {
+               eval q{ open $fh, '<', \\ $scalar; };
+               }
 
        my @lines = <$fh>;
 
@@ -611,13 +701,16 @@ sub _get_cpanpm_last_line
 
 BEGIN {
 my $epic_fail_words = join '|',
-       qw( Error stop(?:ping)? problems force not unsupported fail(?:ed)? );
+       qw( Error stop(?:ping)? problems force not unsupported
+               fail(?:ed)? Cannot\s+install );
 
 sub _cpanpm_output_indicates_failure
        {
        my $last_line = _get_cpanpm_last_line();
 
        my $result = $last_line =~ /\b(?:$epic_fail_words)\b/i;
+       return A_MODULE_FAILED_TO_INSTALL if $last_line =~ /\b(?:Cannot\s+install)\b/i;
+
        $result || ();
        }
 }
@@ -817,7 +910,6 @@ sub _is_pingable_scheme {
 sub _find_good_mirrors {
        require CPAN::Mirrors;
 
-       my $mirrors = CPAN::Mirrors->new;
        my $file = do {
                my $file = 'MIRRORED.BY';
                my $local_path = File::Spec->catfile(
@@ -830,11 +922,10 @@ sub _find_good_mirrors {
                        $local_path;
                        }
                };
-
-       $mirrors->parse_mirrored_by( $file );
+       my $mirrors = CPAN::Mirrors->new( $file );
 
        my @mirrors = $mirrors->best_mirrors(
-               how_many   => 3,
+               how_many   => 5,
                verbose    => 1,
                );
 
@@ -843,6 +934,9 @@ sub _find_good_mirrors {
                _print_ping_report( $mirror->http );
                }
 
+       $CPAN::Config->{urllist} = [
+               map { $_->http } @mirrors
+               ];
        }
 
 sub _print_inc_dir_report
@@ -859,9 +953,10 @@ sub _print_ping_report
        my( $mirror ) = @_;
 
        my $rtt = eval { _get_ping_report( $mirror ) };
+       my $result = $rtt ? sprintf "+ (%4d ms)", $rtt * 1000 : '!';
 
        $logger->info(
-               sprintf "\t%s (%4d ms) %s", $rtt  ? '+' : '!',  $rtt * 1000, $mirror
+               sprintf "\t%s %s", $result, $mirror
                );
        }
 
@@ -908,6 +1003,19 @@ sub _load_local_lib # -I
        return HEY_IT_WORKED;
        }
 
+sub _use_these_mirrors # -M
+       {
+       $logger->debug( "Setting per session mirrors" );
+       unless( $_[0] ) {
+               $logger->die( "The -M switch requires a comma-separated list of mirrors" );
+               }
+
+       $CPAN::Config->{urllist} = [ split /,/, $_[0] ];
+
+       $logger->debug( "Mirrors are @{$CPAN::Config->{urllist}}" );
+
+       }
+
 sub _create_autobundle
        {
        $logger->info(
@@ -1157,9 +1265,9 @@ sub _show_Details
                print "$arg\n", "-" x 73, "\n\t";
                print join "\n\t",
                        $module->description ? $module->description : "(no description)",
-                       $module->cpan_file,
-                       $module->inst_file,
-                       'Installed: ' . $module->inst_version,
+                       $module->cpan_file ? $module->cpan_file : "(no cpanfile)",
+                       $module->inst_file ? $module->inst_file :"(no installation file)" ,
+                       'Installed: ' . ($module->inst_version ? $module->inst_version : "not installed"),
                        'CPAN:      ' . $module->cpan_version . '  ' .
                                ($module->uptodate ? "" : "Not ") . "up to date",
                        $author->fullname . " (" . $module->userid . ")",
@@ -1306,7 +1414,7 @@ sub _eval_version
 sub _path_to_module
        {
        my( $inc, $path ) = @_;
-       return if length $path< length $inc;
+       return if length $path < length $inc;
 
        my $module_path = substr( $path, length $inc );
        $module_path =~ s/\.pm\z//;
@@ -1348,14 +1456,10 @@ correctly if Log4perl is not installed.
 * When I capture CPAN.pm output, I need to check for errors and
 report them to the user.
 
-* Support local::lib
-
 * Warnings switch
 
 * Check then exit
 
-* ping mirrors support
-
 * no test option
 
 =head1 BUGS
@@ -1364,14 +1468,16 @@ report them to the user.
 
 =head1 SEE ALSO
 
-Most behaviour, including environment variables and configuration,
-comes directly from CPAN.pm.
+L<CPAN>, L<App::cpanminus>
 
 =head1 SOURCE AVAILABILITY
 
-This code is in Github:
+This code is in Github in the CPAN.pm repository:
+
+       https://github.com/andk/cpanpm
 
-       git://github.com/briandfoy/cpan_script.git
+The source used to be tracked separately in another GitHub repo,
+but the canonical source is now in the above repo.
 
 =head1 CREDITS
 
@@ -1391,7 +1497,7 @@ brian d foy, C<< <bdfoy@cpan.org> >>
 
 =head1 COPYRIGHT
 
-Copyright (c) 2001-2013, brian d foy, All Rights Reserved.
+Copyright (c) 2001-2014, brian d foy, All Rights Reserved.
 
 You may redistribute this under the same terms as Perl itself.
 
index 4ed4b6c..db9deaf 100644 (file)
@@ -2,7 +2,7 @@
 # vim: ts=4 sts=4 sw=4:
 use strict;
 package CPAN;
-$CPAN::VERSION = '2.05';
+$CPAN::VERSION = '2.09';
 $CPAN::VERSION =~ s/_//;
 
 # we need to run chdir all over and we would get at wrong libraries
@@ -919,6 +919,9 @@ sub getcwd {Cwd::getcwd();}
 #-> sub CPAN::fastcwd ;
 sub fastcwd {Cwd::fastcwd();}
 
+#-> sub CPAN::getdcwd ;
+sub getdcwd {Cwd::getdcwd();}
+
 #-> sub CPAN::backtickcwd ;
 sub backtickcwd {my $cwd = `cwd`; chomp $cwd; $cwd}
 
@@ -1020,6 +1023,18 @@ sub has_usable {
                             },
                            ],
 
+               'CPAN::Meta::Requirements' => [
+                            sub {
+                                require CPAN::Meta::Requirements;
+                                unless (CPAN::Version->vge(CPAN::Meta::Requirements->VERSION, 2.120920)) {
+                                    for ("Will not use CPAN::Meta::Requirements, need version 2.120920\n") {
+                                        $CPAN::Frontend->mywarn($_);
+                                        die $_;
+                                    }
+                                }
+                            },
+                           ],
+
                LWP => [ # we frequently had "Can't locate object
                         # method "new" via package "LWP::UserAgent" at
                         # (eval 69) line 2006
@@ -1103,6 +1118,20 @@ sub has_usable {
     return $HAS_USABLE->{$mod} = 1;
 }
 
+sub frontend {
+    shift;
+    $CPAN::Frontend = shift if @_;
+    $CPAN::Frontend;
+}
+
+sub use_inst {
+    my ($self, $module) = @_;
+
+    unless ($self->has_inst($module)) {
+        $self->frontend->mydie("$module not installed, cannot continue");
+    }
+}
+
 #-> sub CPAN::has_inst
 sub has_inst {
     my($self,$mod,$message) = @_;
@@ -1616,7 +1645,7 @@ in html or plain text format.
 =item C<ls> globbing_expression
 
 The first form lists all distribution files in and below an author's
-CPAN directory as stored in the CHECKUMS files distributed on
+CPAN directory as stored in the CHECKSUMS files distributed on
 CPAN. The listing recurses into subdirectories.
 
 The second form limits or expands the output with shell
@@ -1865,7 +1894,7 @@ separated):
 
 Modules know their associated Distribution objects. They always refer
 to the most recent official release. Developers may mark their releases
-as unstable development versions (by inserting an unserscore into the
+as unstable development versions (by inserting an underscore into the
 module version number which will also be reflected in the distribution
 name when you run 'make dist'), so the really hottest and newest
 distribution is not always the default.  If a module Foo circulates
@@ -1923,6 +1952,39 @@ by the cpan shell B<only when surrounded by whitespace>. So piping to
 pager or redirecting output into a file works somewhat as in a normal
 shell, with the stipulation that you must type extra spaces.
 
+=head2 Plugin support ***EXPERIMENTAL***
+
+Plugins are objects that implement any of currently eight methods:
+
+  pre_get
+  post_get
+  pre_make
+  post_make
+  pre_test
+  post_test
+  pre_install
+  post_install
+
+The C<plugin_list> configuration parameter holds a list of strings of
+the form
+
+  Modulename=arg0,arg1,arg2,arg3,...
+
+At run time, each listed plugin is instantiated as a singleton object
+by running the equivalent of this pseudo code:
+
+  my $plugin = <string representation from config>;
+  <generate Modulename and arguments from $plugin>;
+  my $p = $instance{$plugin} ||= Modulename->new($arg0,$arg1,...);
+
+The generated singletons are kept around from instantiation until the
+end of the shell session. <plugin_list> can be reconfigured at any
+time at run time. While the cpan shell is running, it checks all
+activated plugins at each of the 8 reference points listed above and
+runs the respective method if it is implemented for that object. The
+method is called with the active CPAN::Distribution object passed in
+as an argument.
+
 =head1 CONFIGURATION
 
 When the CPAN module is used for the first time, a configuration
@@ -2091,6 +2153,8 @@ currently defined:
   patch              path to external prg
   patches_dir        local directory containing patch files
   perl5lib_verbosity verbosity level for PERL5LIB additions
+  plugin_list        list of active hooks (see Plugin support above
+                     and the CPAN::Plugin module)
   prefer_external_tar
                      per default all untar operations are done with
                      Archive::Tar; by setting this variable to true
@@ -2202,6 +2266,10 @@ Calls Cwd::getcwd
 
 Calls Cwd::fastcwd
 
+=item getdcwd
+
+Calls Cwd::getdcwd
+
 =item backtickcwd
 
 Calls the external command cwd.
@@ -2272,8 +2340,7 @@ C<ask/no>, CPAN.pm asks the user and sets the default accordingly.
 
 =head2 Configuration for individual distributions (I<Distroprefs>)
 
-(B<Note:> This feature has been introduced in CPAN.pm 1.8854 and is
-still considered beta quality)
+(B<Note:> This feature has been introduced in CPAN.pm 1.8854)
 
 Distributions on CPAN usually behave according to what we call the
 CPAN mantra. Or since the advent of Module::Build we should talk about
@@ -3373,6 +3440,11 @@ loaded:
 
 See the source for details.
 
+=item use_inst($module)
+
+Similary to L<has_inst()> tries to load optional library but also dies if
+library is not available
+
 =item has_usable($module)
 
 Returns true if the module is installed and in a usable state. Only
@@ -3385,6 +3457,12 @@ The constructor for all the singletons used to represent modules,
 distributions, authors, and bundles. If the object already exists, this
 method returns the object; otherwise, it calls the constructor.
 
+=item frontend()
+
+=item frontend($new_frontend)
+
+Getter/setter for frontend object. Method just allows to subclass CPAN.pm.
+
 =back
 
 =head1 SECURITY
index 9a08707..b2fccc2 100644 (file)
@@ -4,12 +4,37 @@ package CPAN::Distribution;
 use strict;
 use Cwd qw(chdir);
 use CPAN::Distroprefs;
-use CPAN::Meta::Requirements 2;
 use CPAN::InfoObj;
 use File::Path ();
 @CPAN::Distribution::ISA = qw(CPAN::InfoObj);
 use vars qw($VERSION);
-$VERSION = "2.02";
+$VERSION = "2.03";
+
+# no prepare, because prepare is not a command on the shell command line
+# TODO: clear instance cache on reload
+my %instance;
+for my $method (qw(get make test install)) {
+    no strict 'refs';
+    for my $prefix (qw(pre post)) {
+        my $hookname = sprintf "%s_%s", $prefix, $method;
+        *$hookname = sub {
+            my($self) = @_;
+            for my $plugin (@{$CPAN::Config->{plugin_list}}) {
+                my($plugin_proper,$args) = split /=/, $plugin, 2;
+                $args = "" unless defined $args;
+                if ($CPAN::META->has_inst($plugin_proper)){
+                    my @args = split /,/, $args;
+                    $instance{$plugin} ||= $plugin_proper->new(@args);
+                    if ($instance{$plugin}->can($hookname)) {
+                        $instance{$plugin}->$hookname($self);
+                    }
+                } else {
+                    $CPAN::Frontend->mydie("Plugin '$plugin_proper' not found");
+                }
+            }
+        };
+    }
+}
 
 # Accessors
 sub cpan_comment {
@@ -180,6 +205,7 @@ sub color_cmd_tmps {
     return if exists $self->{incommandcolor}
         && $color==1
         && $self->{incommandcolor}==$color;
+    $CPAN::MAX_RECURSION||=0; # silence 'once' warnings
     if ($depth>=$CPAN::MAX_RECURSION) {
         die(CPAN::Exception::RecursiveDependency->new($ancestors));
     }
@@ -187,11 +213,10 @@ sub color_cmd_tmps {
     my $prereq_pm = $self->prereq_pm;
     if (defined $prereq_pm) {
         # XXX also optional_req & optional_breq? -- xdg, 2012-04-01
+        # A: no, optional deps may recurse -- ak, 2014-05-07
       PREREQ: for my $pre (
                 keys %{$prereq_pm->{requires}||{}},
                 keys %{$prereq_pm->{build_requires}||{}},
-                keys %{$prereq_pm->{opt_requires}||{}},
-                keys %{$prereq_pm->{opt_build_requires}||{}}
             ) {
             next PREREQ if $pre eq "perl";
             my $premo;
@@ -332,6 +357,8 @@ sub shortcut_get {
 sub get {
     my($self) = @_;
 
+    $self->pre_get();
+
     $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG;
     if (my $goto = $self->prefs->{goto}) {
         return $self->goto($goto);
@@ -379,6 +406,9 @@ sub get {
     }
     return unless $self->patch;
     $self->store_persistent_state;
+
+    $self->post_get();
+
     return 1; # success
 }
 
@@ -648,6 +678,11 @@ sub parse_meta_yml {
     }
     $self->debug(sprintf("yaml[%s]", $early_yaml || 'UNDEF')) if $CPAN::DEBUG;
     $self->debug($early_yaml) if $CPAN::DEBUG && $early_yaml;
+    if (!ref $early_yaml or ref $early_yaml ne "HASH"){
+        # fix rt.cpan.org #95271
+        $CPAN::Frontend->mywarn("The content of '$yaml' is not a HASH reference. Cannot use it.\n");
+        return {};
+    }
     return $early_yaml || undef;
 }
 
@@ -804,8 +839,16 @@ sub store_persistent_state {
                                     "will not store persistent state\n");
         return;
     }
-    unless (   Cwd::realpath(File::Spec->catdir($dir, File::Spec->updir()) )
-            eq Cwd::realpath($CPAN::Config->{build_dir}                  ) ) {
+    # self-build-dir
+    my $sbd = Cwd::realpath(
+        File::Spec->catdir($dir,                       File::Spec->updir ())
+                           );
+    # config-build-dir
+    my $cbd = Cwd::realpath(
+        # the catdir is a workaround for bug https://rt.cpan.org/Ticket/Display.html?id=101283
+        File::Spec->catdir($CPAN::Config->{build_dir}, File::Spec->curdir())
+    );
+    unless ($sbd eq $cbd) {
         $CPAN::Frontend->mywarnonce("Directory '$dir' not below $CPAN::Config->{build_dir}, ".
                                     "will not store persistent state\n");
         return;
@@ -1835,8 +1878,8 @@ sub prepare {
 
     $self->debug("Changed directory to $builddir") if $CPAN::DEBUG;
 
-    local $ENV{PERL_AUTOINSTALL} = $ENV{PERL_AUTOINSTALL};
-    local $ENV{PERL_EXTUTILS_AUTOINSTALL} = $ENV{PERL_EXTUTILS_AUTOINSTALL};
+    local $ENV{PERL_AUTOINSTALL} = $ENV{PERL_AUTOINSTALL} || '';
+    local $ENV{PERL_EXTUTILS_AUTOINSTALL} = $ENV{PERL_EXTUTILS_AUTOINSTALL} || '';
     $self->choose_MM_or_MB
         or return;
 
@@ -1856,8 +1899,8 @@ sub prepare {
     if ($self->prefs->{pl}) {
         $pl_commandline = $self->prefs->{pl}{commandline};
     }
-    local $ENV{PERL} = $ENV{PERL};
-    local $ENV{PERL5_CPAN_IS_EXECUTING} = $ENV{PERL5_CPAN_IS_EXECUTING};
+    local $ENV{PERL} = defined $ENV{PERL}? $ENV{PERL} : $^X;
+    local $ENV{PERL5_CPAN_IS_EXECUTING} = $ENV{PERL5_CPAN_IS_EXECUTING} || '';
     local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default};
     local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default};
     if ($pl_commandline) {
@@ -2036,6 +2079,8 @@ sub shortcut_make {
 sub make {
     my($self) = @_;
 
+    $self->pre_make();
+
     $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG;
     if (my $goto = $self->prefs->{goto}) {
         return $self->goto($goto);
@@ -2130,12 +2175,19 @@ is part of the perl-%s distribution. To install that, you need to run
         delete $self->{force_update};
         return;
     }
+
+    # need to chdir again, because $self->satisfy_requires might change the directory
+    unless (chdir $builddir) {
+        $CPAN::Frontend->mywarn("Couldn't chdir to '$builddir': $!");
+        return;
+    }
+
     my $system;
     my $make_commandline;
     if ($self->prefs->{make}) {
         $make_commandline = $self->prefs->{make}{commandline};
     }
-    local $ENV{PERL} = $ENV{PERL};
+    local $ENV{PERL} = defined $ENV{PERL}? $ENV{PERL} : $^X;
     local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default};
     local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default};
     if ($make_commandline) {
@@ -2200,6 +2252,9 @@ is part of the perl-%s distribution. To install that, you need to run
         $CPAN::Frontend->mywarn("  $system -- NOT OK\n");
     }
     $self->store_persistent_state;
+
+    $self->post_make();
+
     return !! $system_ok;
 }
 
@@ -2733,6 +2788,8 @@ sub _feature_depends {
 sub prereqs_for_slot {
     my($self,$slot) = @_;
     my($prereq_pm);
+    $CPAN::META->has_usable("CPAN::Meta::Requirements")
+        or die "CPAN::Meta::Requirements not available";
     my $merged = CPAN::Meta::Requirements->new;
     my $prefs_depends = $self->prefs->{depends}||{};
     my $feature_depends = $self->_feature_depends();
@@ -2795,6 +2852,8 @@ sub unsat_prereq {
     my($self,$slot) = @_;
     my($merged_hash,$prereq_pm) = $self->prereqs_for_slot($slot);
     my(@need);
+    $CPAN::META->has_usable("CPAN::Meta::Requirements")
+        or die "CPAN::Meta::Requirements not available";
     my $merged = CPAN::Meta::Requirements->from_string_hash($merged_hash);
     my @merged = $merged->required_modules;
     CPAN->debug("all merged_prereqs[@merged]") if $CPAN::DEBUG;
@@ -3166,6 +3225,7 @@ sub prereq_pm {
 
         # XXX assemble optional_req && optional_breq from recommends/suggests
         # depending on corresponding policies -- xdg, 2012-04-01
+        CPAN->use_inst("CPAN::Meta::Requirements");
         my $opt_runtime = CPAN::Meta::Requirements->new;
         my $opt_build   = CPAN::Meta::Requirements->new;
         if ( $CPAN::Config->{recommends_policy} ) {
@@ -3413,6 +3473,8 @@ sub _exe_files {
 sub test {
     my($self) = @_;
 
+    $self->pre_test();
+
     $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG;
     if (my $goto = $self->prefs->{goto}) {
         return $self->goto($goto);
@@ -3591,6 +3653,8 @@ sub test {
     }
     $self->store_persistent_state;
 
+    $self->post_test();
+
     return $self->{force_update} ? 1 : !! $tests_ok;
 }
 
@@ -3815,6 +3879,8 @@ sub shortcut_install {
 sub install {
     my($self) = @_;
 
+    $self->pre_install();
+
     $self->debug("checking goto id[$self->{ID}]") if $CPAN::DEBUG;
     if (my $goto = $self->prefs->{goto}) {
         return $self->goto($goto);
@@ -3867,7 +3933,6 @@ sub install {
                           $install_directive,
                           $CPAN::Config->{mbuild_install_arg},
                          );
-        
     } else {
         my($make_install_make_command) = $self->_make_install_make_command();
         $system = sprintf("%s install %s",
@@ -3911,8 +3976,7 @@ sub install {
     local $ENV{PERL_MM_USE_DEFAULT} = 1 if $CPAN::Config->{use_prompt_default};
     local $ENV{NONINTERACTIVE_TESTING} = 1 if $CPAN::Config->{use_prompt_default};
 
-    my($pipe) = FileHandle->new("$system $stderr |") || Carp::croak
-("Can't execute $system: $!");
+    my($pipe) = FileHandle->new("$system $stderr |") || Carp::croak("Can't execute $system: $!");
     my($makeout) = "";
     while (<$pipe>) {
         print $_; # intentionally NOT use Frontend->myprint because it
@@ -3954,6 +4018,9 @@ sub install {
     }
     delete $self->{force_update};
     $self->store_persistent_state;
+
+    $self->post_install();
+
     return !! $close_ok;
 }
 
index d1a8eef..918e009 100644 (file)
@@ -10,7 +10,7 @@ use File::Path ();
 use File::Spec ();
 use CPAN::Mirrors ();
 use vars qw($VERSION $auto_config);
-$VERSION = "5.5306";
+$VERSION = "5.5307";
 
 =head1 NAME
 
@@ -198,6 +198,7 @@ alternatives can be configured according to the following table:
     cwd         Cwd::cwd
     getcwd      Cwd::getcwd
     fastcwd     Cwd::fastcwd
+    getdcwd     Cwd::getdcwd
     backtickcwd external command cwd
 
 Preferred method for determining the current working directory?
@@ -1134,6 +1135,17 @@ sub init {
     }
 
     #
+    #= how plugins work
+    #
+
+    # XXX MISSING: my_array_prompt to be used with plugins. We did something like this near
+    #     git log -p fd68f8f5e33f4cecea4fdb7abc5ee19c12f138f0..test-notest-test-dependency
+    # Need to do similar steps for plugin_list. As long as we do not support it here, people
+    # must use the cpan shell prompt to write something like
+    #     o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/foo-20141013,...
+    #     o conf commit
+
+    #
     #= how FTP works
     #
 
@@ -1144,7 +1156,7 @@ sub init {
     #
 
     my_prompt_loop(getcwd => 'cwd', $matcher,
-                   'cwd|getcwd|fastcwd|backtickcwd');
+                   'cwd|getcwd|fastcwd|getdcwd|backtickcwd');
 
     #
     #= the CPAN shell itself (prompt, color)
index 097c67d..96a9880 100644 (file)
@@ -4,11 +4,11 @@ package CPAN::HTTP::Credentials;
 use strict;
 use vars qw($USER $PASSWORD $PROXY_USER $PROXY_PASSWORD);
 
-$CPAN::HTTP::Credentials::VERSION = $CPAN::HTTP::Credentials::VERSION = "1.9600";
+$CPAN::HTTP::Credentials::VERSION = $CPAN::HTTP::Credentials::VERSION = "1.9601";
 
 sub clear_credentials {
-   _clear_non_proxy_credentials();
-   _clear_proxy_credentials();
+   clear_non_proxy_credentials();
+   clear_proxy_credentials();
 }
 
 sub clear_non_proxy_credentials {
index a138128..fabcc0c 100644 (file)
@@ -88,6 +88,7 @@ $VERSION = "5.5005"; # see also CPAN::Config::VERSION at end of file
      "patch",
      "patches_dir",
      "perl5lib_verbosity",
+     "plugin_list",
      "prefer_external_tar",
      "prefer_installer",
      "prefs_dir",
@@ -153,7 +154,7 @@ sub edit {
         # one day I used randomize_urllist for a boolean, so we must
         # list them explicitly --ak
         if (0) {
-        } elsif ($o =~ /^(wait_list|urllist|dontload_list)$/) {
+        } elsif ($o =~ /^(wait_list|urllist|dontload_list|plugin_list)$/) {
 
             #
             # ARRAYS
index 2c0c71a..7d65b34 100644 (file)
@@ -543,9 +543,18 @@ sub uptodate {
 # returns true if installed in privlib or archlib
 sub _in_priv_or_arch {
     my($self,$inst_file) = @_;
-    for my $confdirname (qw(archlibexp privlibexp)) {
-        my $confdir = $Config::Config{$confdirname};
-        if ($confdir eq substr($inst_file,0,length($confdir))) {
+    foreach my $pair (
+        [qw(sitearchexp archlibexp)],
+        [qw(sitelibexp privlibexp)]
+    ) {
+        my ($site, $priv) = @Config::Config{@$pair};
+        if ($^O eq 'VMS') {
+            for my $d ($site, $priv) { $d = VMS::Filespec::unixify($d) };
+        }
+        s!/*$!!g foreach $site, $priv;
+        next if $site eq $priv;
+
+        if ($priv eq substr($inst_file,0,length($priv))) {
             return 1;
         }
     }
diff --git a/cpan/CPAN/lib/CPAN/Plugin.pm b/cpan/CPAN/lib/CPAN/Plugin.pm
new file mode 100644 (file)
index 0000000..646d86b
--- /dev/null
@@ -0,0 +1,145 @@
+package CPAN::Plugin;
+
+use strict;
+use warnings;
+
+our $VERSION = '0.95';
+
+require CPAN;
+
+######################################################################
+
+sub new {                                # ;
+    my ($class, %params) = @_;
+
+    my $self = +{
+        (ref $class ? (%$class) : ()),
+        %params,
+    };
+
+    $self = bless $self, ref $class ? ref $class : $class;
+
+    unless (ref $class) {
+        local $_;
+        no warnings 'once';
+        $CPAN::META->use_inst ($_) for $self->plugin_requires;
+    }
+
+    $self;
+}
+
+######################################################################
+sub plugin_requires {                    # ;
+}
+
+######################################################################
+sub distribution_object {                # ;
+    my ($self) = @_;
+    $self->{distribution_object};
+}
+
+######################################################################
+sub distribution {                       # ;
+    my ($self) = @_;
+
+    my $distribution = $self->distribution_object->id;
+    CPAN::Shell->expand("Distribution",$distribution)
+      or $self->frontend->mydie("Unknowns distribution '$distribution'\n");
+}
+
+######################################################################
+sub distribution_info {                  # ;
+    my ($self) = @_;
+
+    CPAN::DistnameInfo->new ($self->distribution->id);
+}
+
+######################################################################
+sub build_dir {                          # ;
+    my ($self) = @_;
+
+    my $build_dir = $self->distribution->{build_dir}
+      or $self->frontend->mydie("Distribution has not been built yet, cannot proceed");
+}
+
+######################################################################
+sub is_xs {                              #
+    my ($self) = @_;
+
+    my @xs = glob File::Spec->catfile ($self->build_dir, '*.xs'); # quick try
+
+    unless (@xs) {
+        require ExtUtils::Manifest;
+        my $manifest_file = File::Spec->catfile ($self->build_dir, "MANIFEST");
+        my $manifest = ExtUtils::Manifest::maniread($manifest_file);
+        @xs = grep /\.xs$/, keys %$manifest;
+    }
+
+    scalar @xs;
+}
+
+######################################################################
+
+package CPAN::Plugin;
+
+1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+CPAN::Plugin - Base class for CPAN shell extensions
+
+=head1 SYNOPSIS
+
+   package My::Plugin;
+   use base 'CPAN::Plugin';
+
+   sub post_test {
+     my ($self, $distribution_object) = @_;
+     $self = $self->new (distribution_object => $distribution_object);
+     ...;
+   }
+
+=head1 DESCRIPTION
+
+=head2 Alpha Status
+
+The plugin system in the CPAN shell was introduced in version 2.07 and
+is still considered experimental.
+
+=head2 How Plugins work?
+
+See L<CPAN/"Plugin support">.
+
+=head1 METHODS
+
+=head2 plugin_requires
+
+returns list of packages given plugin requires for functionality.
+This list is evaluated using C<CPAN->use_inst> method.
+
+=head2 distribution_object
+
+Get current distribution object.
+
+=head2 distribution
+
+=head2 distribution_info
+
+=head2 build_dir
+
+Simple delegatees for misc parameters derived from distribution
+
+=head2 is_xs
+
+Predicate to detect whether package contains XS.
+
+=head1 AUTHOR
+
+Branislav Zahradnik <barney@cpan.org>
+
+=cut
+
diff --git a/cpan/CPAN/lib/CPAN/Plugin/Specfile.pm b/cpan/CPAN/lib/CPAN/Plugin/Specfile.pm
new file mode 100644 (file)
index 0000000..f63d322
--- /dev/null
@@ -0,0 +1,258 @@
+=head1 NAME
+
+CPAN::Plugin::Specfile - Proof of concept implementation of a trivial CPAN::Plugin
+
+=head1 SYNOPSIS
+
+  # once in the cpan shell
+  o conf plugin_list push CPAN::Plugin::Specfile
+
+  # make permanent
+  o conf commit
+
+  # any time in the cpan shell to write a spec file
+  test Acme::Meta
+
+  # disable
+  o conf plugin_list pop
+
+=head1 DESCRIPTION
+
+Implemented as a post-test hook, this plugin writes a specfile after
+every successful test run. The content is also written to the
+terminal.
+
+As a side effect, the timestamps of the written specfiles reflect the
+linear order of all dependencies.
+
+B<WARNING:> This code is just a small demo how to use the plugin
+system of the CPAN shell, not a full fledged spec file writer. Do not
+expect new features in this plugin.
+
+=head2 OPTIONS
+
+The target directory to store the spec files in can be set using C<dir>
+as in
+
+  o conf plugin_list push CPAN::Plugin::Specfile=dir,/tmp/specfiles-000042
+
+The default directory for this is the
+C<plugins/CPAN::Plugin::Specfile> directory in the I<cpan_home>
+directory.
+
+=head1 AUTHOR
+
+Andreas Koenig <andk@cpan.org>, Branislav Zahradnik <barney@cpan.org>
+
+=cut
+
+package CPAN::Plugin::Specfile;
+
+our $VERSION = '0.01';
+
+use File::Path;
+use File::Spec;
+
+sub __accessor {
+    my ($class, $key) = @_;
+    no strict 'refs';
+    *{$class . '::' . $key} = sub {
+        my $self = shift;
+        if (@_) {
+            $self->{$key} = shift;
+        }
+        return $self->{$key};
+    };
+}
+BEGIN { __PACKAGE__->__accessor($_) for qw(dir dir_default) }
+
+sub new {
+    my($class, @rest) = @_;
+    my $self = bless {}, $class;
+    while (my($arg,$val) = splice @rest, 0, 2) {
+        $self->$arg($val);
+    }
+    $self->dir_default(File::Spec->catdir($CPAN::Config->{cpan_home},"plugins",__PACKAGE__));
+    $self;
+}
+
+sub post_test {
+    my $self = shift;
+    my $distribution_object = shift;
+    my $distribution = $distribution_object->pretty_id;
+    unless ($CPAN::META->has_inst("CPAN::DistnameInfo")){
+        $CPAN::Frontend->mydie("CPAN::DistnameInfo not installed; cannot continue");
+    }
+    my $d = CPAN::Shell->expand("Distribution",$distribution)
+        or $CPAN::Frontend->mydie("Unknowns distribution '$distribution'\n");
+    my $build_dir = $d->{build_dir} or $CPAN::Frontend->mydie("Distribution has not been built yet, cannot proceed");
+    my %contains = map {($_ => undef)} $d->containsmods;
+    my @m;
+    my $width = 16;
+    my $header = sub {
+        my($header,$value) = @_;
+        push @m, sprintf("%-s:%*s%s\n", $header, $width-length($header), "", $value);
+    };
+    my $dni = CPAN::DistnameInfo->new($distribution);
+    my $dist = $dni->dist;
+    my $summary = CPAN::Shell->_guess_manpage($d,\%contains,$dist);
+    $header->("Name", "perl-$dist");
+    my $version = $dni->version;
+    $header->("Version", $version);
+    $header->("Release", "1%{?dist}");
+#Summary:        Template processing system
+#Group:          Development/Libraries
+#License:        GPL+ or Artistic
+#URL:            http://www.template-toolkit.org/
+#Source0:        http://search.cpan.org/CPAN/authors/id/A/AB/ABW/Template-Toolkit-%{version}.tar.gz
+#Patch0:         Template-2.22-SREZIC-01.patch
+#BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+    for my $h_tuple
+        ([Summary    => $summary],
+         [Group      => "Development/Libraries"],
+         [License    =>],
+         [URL        =>],
+         [BuildRoot  => "%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)"],
+         [Requires   => "perl(:MODULE_COMPAT_%(eval \"`%{__perl} -V:version`\"; echo \$version))"],
+        ) {
+        my($h,$v) = @$h_tuple;
+        $v = "unknown" unless defined $v;
+        $header->($h, $v);
+    }
+    $header->("Source0", sprintf(
+                                 "http://search.cpan.org/CPAN/authors/id/%s/%s/%s",
+                                 substr($distribution,0,1),
+                                 substr($distribution,0,2),
+                                 $distribution
+                                ));
+    require POSIX;
+    my @xs = glob "$build_dir/*.xs"; # quick try
+    unless (@xs) {
+        require ExtUtils::Manifest;
+        my $manifest_file = "$build_dir/MANIFEST";
+        my $manifest = ExtUtils::Manifest::maniread($manifest_file);
+        @xs = grep /\.xs$/, keys %$manifest;
+    }
+    if (! @xs) {
+        $header->('BuildArch', 'noarch');
+    }
+    for my $k (sort keys %contains) {
+        my $m = CPAN::Shell->expand("Module",$k);
+        my $v = $contains{$k} = $m->cpan_version;
+        my $vspec = $v eq "undef" ? "" : " = $v";
+        $header->("Provides", "perl($k)$vspec");
+    }
+    if (my $prereq_pm = $d->{prereq_pm}) {
+        my %req;
+        for my $reqkey (keys %$prereq_pm) {
+            while (my($k,$v) = each %{$prereq_pm->{$reqkey}}) {
+                $req{$k} = $v;
+            }
+        }
+        if (-e "$build_dir/Build.PL" && ! exists $req{"Module::Build"}) {
+            $req{"Module::Build"} = 0;
+        }
+        for my $k (sort keys %req) {
+            next if $k eq "perl";
+            my $v = $req{$k};
+            my $vspec = defined $v && length $v && $v > 0 ? " >= $v" : "";
+            $header->(BuildRequires => "perl($k)$vspec");
+            next if $k =~ /^(Module::Build)$/; # MB is always only a
+                                               # BuildRequires; if we
+                                               # turn it into a
+                                               # Requires, then we
+                                               # would have to make it
+                                               # a BuildRequires
+                                               # everywhere we depend
+                                               # on *one* MB built
+                                               # module.
+            $header->(Requires => "perl($k)$vspec");
+        }
+    }
+    push @m, "\n%define _use_internal_dependency_generator     0
+%define __find_requires %{nil}
+%define __find_provides %{nil}
+";
+    push @m, "\n%description\n%{summary}.\n";
+    push @m, "\n%prep\n%setup -q -n $dist-%{version}\n";
+    if (-e "$build_dir/Build.PL") {
+        # see http://www.redhat.com/archives/rpm-list/2002-July/msg00110.html about RPM_BUILD_ROOT vs %{buildroot}
+        push @m, <<'EOF';
+
+%build
+%{__perl} Build.PL --installdirs=vendor --libdoc installvendorman3dir
+./Build
+
+%install
+rm -rf $RPM_BUILD_ROOT
+./Build install destdir=$RPM_BUILD_ROOT create_packlist=0
+find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null \;
+%{_fixperms} $RPM_BUILD_ROOT/*
+
+%check
+./Build test
+EOF
+    } elsif (-e "$build_dir/Makefile.PL") {
+        push @m, <<'EOF';
+
+%build
+%{__perl} Makefile.PL INSTALLDIRS=vendor
+make %{?_smp_mflags}
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make pure_install DESTDIR=$RPM_BUILD_ROOT
+find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
+find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null ';'
+%{_fixperms} $RPM_BUILD_ROOT/*
+
+%check
+make test
+EOF
+    } else {
+        $CPAN::Frontend->mydie("'$distribution' has neither a Build.PL nor a Makefile.PL\n");
+    }
+    push @m, "\n%clean\nrm -rf \$RPM_BUILD_ROOT\n";
+    my $vendorlib = @xs ? "vendorarch" : "vendorlib";
+    my $date = POSIX::strftime("%a %b %d %Y", gmtime);
+    my @doc = grep { -e "$build_dir/$_" } qw(README Changes);
+    my $exe_stanza = "\n";
+    if (my $exe_files = $d->_exe_files) {
+        if (@$exe_files) {
+            $exe_stanza = "%{_mandir}/man1/*.1*\n";
+            for my $e (@$exe_files) {
+                unless (CPAN->has_inst("File::Basename")) {
+                    $CPAN::Frontend->mydie("File::Basename not installed, cannot continue");
+                }
+                my $basename = File::Basename::basename($e);
+                $exe_stanza .= "/usr/bin/$basename\n";
+            }
+        }
+    }
+    push @m, <<EOF;
+
+%files
+%defattr(-,root,root,-)
+%doc @doc
+%{perl_$vendorlib}/*
+%{_mandir}/man3/*.3*
+$exe_stanza
+%changelog
+* $date  <specfile\@specfile.cpan.org> - $version-1
+- autogenerated by CPAN::Plugin::Specfile()
+
+EOF
+
+    my $ret = join "", @m;
+    $CPAN::Frontend->myprint($ret);
+    my $target_dir = $self->dir || $self->dir_default;
+    File::Path::mkpath($target_dir);
+    my $outfile = File::Spec->catfile($target_dir, "perl-$dist.spec");
+    open my $specout, ">", $outfile
+        or $CPAN::Frontend->mydie("Could not open >$outfile: $!");
+    print $specout $ret;
+    $CPAN::Frontend->myprint("Wrote $outfile");
+    $ret;
+}
+
+1;
index 9e0bb14..43e2fb9 100644 (file)
@@ -47,7 +47,7 @@ use vars qw(
              "CPAN/Tarzip.pm",
              "CPAN/Version.pm",
             );
-$VERSION = "5.5004";
+$VERSION = "5.5005";
 # record the initial timestamp for reload.
 $reload = { map {$INC{$_} ? ($_,(stat $INC{$_})[9]) : ()} @relo };
 @CPAN::Shell::ISA = qw(CPAN::Debug);
@@ -374,6 +374,9 @@ sub o {
         if (!@o_what or $cfilter) { # print all things, "o conf"
             $cfilter ||= "";
             my $qrfilter = eval 'qr/$cfilter/';
+            if ($@) {
+                $CPAN::Frontend->mydie("Cannot parse commandline: $@");
+            }
             my($k,$v);
             my $configpm = CPAN::HandleConfig->require_myconfig_or_config;
             $CPAN::Frontend->myprint("\$CPAN::Config options from $configpm\:\n");
@@ -794,177 +797,7 @@ sub _guess_manpage {
 
 #-> sub CPAN::Shell::_specfile ;
 sub _specfile {
-    my $self = shift;
-    my $distribution = shift;
-    unless ($CPAN::META->has_inst("CPAN::DistnameInfo")){
-        $CPAN::Frontend->mydie("CPAN::DistnameInfo not installed; cannot continue");
-    }
-    my $d = CPAN::Shell->expand("Distribution",$distribution)
-        or $CPAN::Frontend->mydie("Unknowns distribution '$distribution'\n");
-    my $build_dir = $d->{build_dir} or $CPAN::Frontend->mydie("Distribution has not been built yet, cannot proceed");
-    my %contains = map {($_ => undef)} $d->containsmods;
-    my @m;
-    my $width = 16;
-    my $header = sub {
-        my($header,$value) = @_;
-        push @m, sprintf("%-s:%*s%s\n", $header, $width-length($header), "", $value);
-    };
-    my $dni = CPAN::DistnameInfo->new($distribution);
-    my $dist = $dni->dist;
-    my $summary = $self->_guess_manpage($d,\%contains,$dist);
-    $header->("Name", "perl-$dist");
-    my $version = $dni->version;
-    $header->("Version", $version);
-    $header->("Release", "1%{?dist}");
-#Summary:        Template processing system
-#Group:          Development/Libraries
-#License:        GPL+ or Artistic
-#URL:            http://www.template-toolkit.org/
-#Source0:        http://search.cpan.org/CPAN/authors/id/A/AB/ABW/Template-Toolkit-%{version}.tar.gz
-#Patch0:         Template-2.22-SREZIC-01.patch
-#BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
-    for my $h_tuple
-        ([Summary    => $summary],
-         [Group      => "Development/Libraries"],
-         [License    =>],
-         [URL        =>],
-         [BuildRoot  => "%{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)"],
-         [Requires   => "perl(:MODULE_COMPAT_%(eval \"`%{__perl} -V:version`\"; echo \$version))"],
-        ) {
-        my($h,$v) = @$h_tuple;
-        $v = "unknown" unless defined $v;
-        $header->($h, $v);
-    }
-    $header->("Source0", sprintf(
-                                 "http://search.cpan.org/CPAN/authors/id/%s/%s/%s",
-                                 substr($distribution,0,1),
-                                 substr($distribution,0,2),
-                                 $distribution
-                                ));
-    require POSIX;
-    my @xs = glob "$build_dir/*.xs"; # quick try
-    unless (@xs) {
-        require ExtUtils::Manifest;
-        my $manifest_file = "$build_dir/MANIFEST";
-        my $manifest = ExtUtils::Manifest::maniread($manifest_file);
-        @xs = grep /\.xs$/, keys %$manifest;
-    }
-    if (! @xs) {
-        $header->('BuildArch', 'noarch');
-    }
-    for my $k (sort keys %contains) {
-        my $m = CPAN::Shell->expand("Module",$k);
-        my $v = $contains{$k} = $m->cpan_version;
-        my $vspec = $v eq "undef" ? "" : " = $v";
-        $header->("Provides", "perl($k)$vspec");
-    }
-    if (my $prereq_pm = $d->{prereq_pm}) {
-        my %req;
-        for my $reqkey (keys %$prereq_pm) {
-            while (my($k,$v) = each %{$prereq_pm->{$reqkey}}) {
-                $req{$k} = $v;
-            }
-        }
-        if (-e "$build_dir/Build.PL" && ! exists $req{"Module::Build"}) {
-            $req{"Module::Build"} = 0;
-        }
-        for my $k (sort keys %req) {
-            next if $k eq "perl";
-            my $v = $req{$k};
-            my $vspec = defined $v && length $v && $v > 0 ? " >= $v" : "";
-            $header->(BuildRequires => "perl($k)$vspec");
-            next if $k =~ /^(Module::Build)$/; # MB is always only a
-                                               # BuildRequires; if we
-                                               # turn it into a
-                                               # Requires, then we
-                                               # would have to make it
-                                               # a BuildRequires
-                                               # everywhere we depend
-                                               # on *one* MB built
-                                               # module.
-            $header->(Requires => "perl($k)$vspec");
-        }
-    }
-    push @m, "\n%define _use_internal_dependency_generator     0
-%define __find_requires %{nil}
-%define __find_provides %{nil}
-";
-    push @m, "\n%description\n%{summary}.\n";
-    push @m, "\n%prep\n%setup -q -n $dist-%{version}\n";
-    if (-e "$build_dir/Build.PL") {
-        # see http://www.redhat.com/archives/rpm-list/2002-July/msg00110.html about RPM_BUILD_ROOT vs %{buildroot}
-        push @m, <<'EOF';
-
-%build
-%{__perl} Build.PL --installdirs=vendor --libdoc installvendorman3dir
-./Build
-
-%install
-rm -rf $RPM_BUILD_ROOT
-./Build install destdir=$RPM_BUILD_ROOT create_packlist=0
-find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null \;
-%{_fixperms} $RPM_BUILD_ROOT/*
-
-%check
-./Build test
-EOF
-    } elsif (-e "$build_dir/Makefile.PL") {
-        push @m, <<'EOF';
-
-%build
-%{__perl} Makefile.PL INSTALLDIRS=vendor
-make %{?_smp_mflags}
-
-%install
-rm -rf $RPM_BUILD_ROOT
-make pure_install DESTDIR=$RPM_BUILD_ROOT
-find $RPM_BUILD_ROOT -type f -name .packlist -exec rm -f {} ';'
-find $RPM_BUILD_ROOT -depth -type d -exec rmdir {} 2>/dev/null ';'
-%{_fixperms} $RPM_BUILD_ROOT/*
-
-%check
-make test
-EOF
-    } else {
-        $CPAN::Frontend->mydie("'$distribution' has neither a Build.PL nor a Makefile.PL\n");
-    }
-    push @m, "\n%clean\nrm -rf \$RPM_BUILD_ROOT\n";
-    my $vendorlib = @xs ? "vendorarch" : "vendorlib";
-    my $date = POSIX::strftime("%a %b %d %Y", gmtime);
-    my @doc = grep { -e "$build_dir/$_" } qw(README Changes);
-    my $exe_stanza = "\n";
-    if (my $exe_files = $d->_exe_files) {
-        if (@$exe_files) {
-            $exe_stanza = "%{_mandir}/man1/*.1*\n";
-            for my $e (@$exe_files) {
-                unless (CPAN->has_inst("File::Basename")) {
-                    $CPAN::Frontend->mydie("File::Basename not installed, cannot continue");
-                }
-                my $basename = File::Basename::basename($e);
-                $exe_stanza .= "/usr/bin/$basename\n";
-            }
-        }
-    }
-    push @m, <<EOF;
-
-%files
-%defattr(-,root,root,-)
-%doc @doc
-%{perl_$vendorlib}/*
-%{_mandir}/man3/*.3*
-$exe_stanza
-%changelog
-* $date  <akoenig\@specfile.cpan.org> - $version-1
-- autogenerated by _specfile() in CPAN.pm
-
-EOF
-
-    my $ret = join "", @m;
-    $CPAN::Frontend->myprint($ret);
-    open my $specout, ">", "perl-$dist.spec" or die;
-    print $specout $ret;
-    $CPAN::Frontend->myprint("Wrote perl-$dist.spec");
-    $ret;
+    die "CPAN::Shell::_specfile() has been moved to CPAN::Plugin::Specfile::post_test()";
 }
 
 #-> sub CPAN::Shell::report ;
index 3b4a5b5..5f4320e 100644 (file)
@@ -26,16 +26,22 @@ cpan - easily interact with CPAN from the command line
        # with just the dot, install from the distribution in the
        # current directory
        cpan .
-       
+
        # without arguments, starts CPAN.pm shell
        cpan
 
+       # force install modules (usually those that fail tests)
+       cpan -f module_name [ module_name ... ]
+
+       # install modules but without testing them
+       cpan -T module_name [ module_name ... ]
+
        # dump the configuration
        cpan -J
-       
+
        # load a different configuration to install Module::Foo
        cpan -j some/other/file Module::Foo
-       
+
        # without arguments, but some switches
        cpan [-ahrvACDlLO]
 
@@ -79,7 +85,7 @@ to install a module even if its tests fail. When you use this option,
 
 =item -F
 
-Turn off CPAN.pm's attempts to lock anything. You should be careful with 
+Turn off CPAN.pm's attempts to lock anything. You should be careful with
 this since you might end up with multiple scripts trying to muck in the
 same directory. This isn't so much of a concern if you're loading a special
 config with C<-j>, and that config sets up its own work directories.
@@ -115,7 +121,7 @@ Load C<local::lib> (think like C<-I> for loading lib paths).
 =item -j Config.pm
 
 Load the file that has the CPAN configuration data. This should have the
-same format as the standard F<CPAN/Config.pm> file, which defines 
+same format as the standard F<CPAN/Config.pm> file, which defines
 C<$CPAN::Config> as an anonymous hash.
 
 =item -J
@@ -173,7 +179,7 @@ Print the script version and CPAN.pm version then exit.
 
 Print detailed information about the cpan client.
 
-=item -w 
+=item -w
 
 UNIMPLEMENTED
 
@@ -209,17 +215,53 @@ and tells you about problems you might have.
 
 =over 4
 
+There are several components in CPAN.pm that use environment variables.
+The build tools, L<ExtUtils::MakeMaker> and L<Module::Build> use some,
+while others matter to the levels above them. Some of these are specified
+by the Perl Toolchain Gang:
+
+Lancaster Concensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/lancaster-consensus.md>
+
+Oslo Concensus: L<https://github.com/Perl-Toolchain-Gang/toolchain-site/blob/master/oslo-consensus.md>
+
+=over 4
+
 =item CPAN_OPTS
 
 C<cpan> splits this variable on whitespace and prepends that list to C<@ARGV>
 before it processes the command-line arguments. For instance, if you always
 want to use C<local:lib>, you can set C<CPAN_OPTS> to C<-I>.
 
+=item CPANSCRIPT_LOGLEVEL
+
+The log level to use, with either the embedded, minimal logger or
+L<Log::Log4perl> if it is installed. Possible values are the same as
+the C<Log::Log4perl> levels: C<TRACE>, C<DEBUG>, C<INFO>, C<WARN>,
+C<ERROR>, and C<FATAL>. The default is C<INFO>.
+
+=item GIT_COMMAND
+
+The path to the C<git> binary to use for the Git features. The default
+is C</usr/local/bin/git>.
+
+=item NONINTERACTIVE_TESTING
+
+Assume no one is paying attention and skips prompts for distributions
+that do that correctly. C<cpan(1)> sets this to C<1> unless it already
+has a value (even if that value is false).
+
+=item PERL_MM_USE_DEFAULT
+
+Use the default answer for a prompted questions. C<cpan(1)> sets this
+to C<1> unless it already has a value (even if that value is false).
+
+=back
+
 =back
 
 =head1 EXIT VALUES
 
-The script exits with zero if it thinks that everything worked, or a 
+The script exits with zero if it thinks that everything worked, or a
 positive number if it thinks that something failed. Note, however, that
 in some cases it has to divine a failure by the output of things it does
 not control. For now, the exit codes are vague:
@@ -247,9 +289,12 @@ comes directly from CPAN.pm.
 
 =head1 SOURCE AVAILABILITY
 
-This code is in Github:
+This code is in Github in the CPAN.pm repository:
+
+       https://github.com/andk/cpanpm
 
-       git://github.com/briandfoy/cpan_script.git
+The source used to be tracked separately in another GitHub repo,
+but the canonical source is now in the above repo.
 
 =head1 CREDITS
 
@@ -267,7 +312,7 @@ brian d foy, C<< <bdfoy@cpan.org> >>
 
 =head1 COPYRIGHT
 
-Copyright (c) 2001-2013, brian d foy, All Rights Reserved.
+Copyright (c) 2001-2014, brian d foy, All Rights Reserved.
 
 You may redistribute this under the same terms as Perl itself.
 
index 1d877fc..e500bda 100644 (file)
@@ -12,6 +12,7 @@ if ($ENV{PERL_CORE}){
                                       FirstTime
                                       Kwalify
                                       Nox
+                                      Plugin
                                       Queue
                                       Tarzip
                                       Version
@@ -27,7 +28,7 @@ plan(tests => scalar @m);
 for my $m (@m) {
   local $^W = 0;
   eval "require $m";
-  ok($m->VERSION >= 1.76, sprintf "Found version > 1.76 for %20s: %s", $m, $m->VERSION);
+  ok($m->VERSION >= 0.95, sprintf "Found version < 0.95 for %s: %s", $m, $m->VERSION);
 }
 
 # Local Variables:
index aed4a0d..d158a36 100644 (file)
@@ -119,7 +119,37 @@ XXX
 
 =item *
 
-L<XXX> has been upgraded from version A.xx to B.yy.
+L<CPAN> has been upgraded from version 2.05 to 2.09-TRIAL.
+
+=over 4
+
+=item *
+
+Add support for C<Cwd::getdcwd()> and introduce workaround for a misbehaviour
+seen on Strawberry Perl 5.20.1.
+
+=item *
+
+Fix C<chdir()> after building dependencies bug.
+
+=item *
+
+Introduce experimental support for plugins/hooks.
+
+=item *
+
+Integrate the App::Cpan sources.
+
+=item *
+
+Do not check recursion on optional dependencies.
+
+=item *
+
+Sanity check META.yml to contain a hash.
+L<[cpan #95271]|https://rt.cpan.org/Ticket/Display.html?id=95271>
+
+=back
 
 =back
 
index b2b33ba..c805c34 100644 (file)
@@ -1,11 +1,3 @@
-CPAN cpan/CPAN/lib/CPAN/Author.pm 792d7c8fbe6ed45e1244e589a8b712878c5dd2a5
-CPAN cpan/CPAN/lib/CPAN/CacheMgr.pm 132adb7f96014ec7ded45457044ed925d3181475
-CPAN cpan/CPAN/lib/CPAN/FTP.pm 3f0d5fc572c8749a566d73ca892c6c89ce3fb676
-CPAN cpan/CPAN/lib/CPAN/HandleConfig.pm e52052b6ef6d1d664f0ffa6cf01d48a8d1321520
-CPAN cpan/CPAN/lib/CPAN/HTTP/Client.pm 242842ca566fd8e3d776deb549ff758a571ca2e3
-CPAN cpan/CPAN/lib/CPAN/Index.pm 73aee30450127c5ac4dc05abc2c10a8accd4b198
-CPAN cpan/CPAN/lib/CPAN/LWP/UserAgent.pm e09525b0c2377c5ac28b7fad1b6d70c57e343913
-CPAN cpan/CPAN/lib/CPAN/Mirrors.pm 580e74746abaf1628d533015d5b529d82a470af4
 Encode cpan/Encode/encoding.pm baa25e197ba4d5fa228dbf7b440afb5ae18ec5ed
 ExtUtils::MakeMaker cpan/ExtUtils-MakeMaker/t/pm_to_blib.t 71ebcee355691ce374fcad251b12d8b2412462b3
 PerlIO::via::QuotedPrint cpan/PerlIO-via-QuotedPrint/t/QuotedPrint.t ca39f0146e89de02c746e199c45dcb3e5edad691