From: Steve Peters Date: Mon, 13 Nov 2006 15:10:16 +0000 (+0000) Subject: Upgrade to CPAN-1.88_62 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=05bab18efbb4de63339671e0a2623b4e3e26cb88;p=p5sagit%2Fp5-mst-13.2.git Upgrade to CPAN-1.88_62 p4raw-id: //depot/perl@29264 --- diff --git a/lib/CPAN.pm b/lib/CPAN.pm index 25b6e73..e618190 100644 --- a/lib/CPAN.pm +++ b/lib/CPAN.pm @@ -1,7 +1,7 @@ # -*- Mode: cperl; coding: utf-8; cperl-indent-level: 4 -*- use strict; package CPAN; -$CPAN::VERSION = '1.88_57'; +$CPAN::VERSION = '1.88_62'; $CPAN::VERSION = eval $CPAN::VERSION; use CPAN::HandleConfig; @@ -23,6 +23,7 @@ use File::Find; use File::Path (); use File::Spec (); use FileHandle (); +use Fcntl qw(:flock); use Safe (); use Sys::Hostname qw(hostname); use Text::ParseWords (); @@ -57,7 +58,7 @@ $CPAN::Perl ||= CPAN::find_perl(); $CPAN::Defaultdocs ||= "http://search.cpan.org/perldoc?"; $CPAN::Defaultrecent ||= "http://search.cpan.org/recent"; - +# our globals are getting a mess use vars qw( $AUTOLOAD $Be_Silent @@ -70,6 +71,7 @@ use vars qw( $HAS_USABLE $Have_warned $META + $RUN_DEGRADED $Signal $Suppress_readline $VERSION @@ -92,6 +94,7 @@ use vars qw( force get install + install_tested make mkmyconfig notest @@ -350,10 +353,24 @@ Trying to chdir to "$cwd->[1]" instead. } } +sub _yaml_module { + my $yaml_module = $CPAN::Config->{yaml_module} || "YAML"; + if ( + $yaml_module ne "YAML" + && + !$CPAN::META->has_inst($yaml_module) + ) { + # $CPAN::Frontend->mywarn("'$yaml_module' not installed, falling back to 'YAML'\n"); + $yaml_module = "YAML"; + } + return $yaml_module; +} + # CPAN::_yaml_loadfile sub _yaml_loadfile { my($self,$local_file) = @_; - my $yaml_module = $CPAN::Config->{yaml_module} || "YAML"; + return +[] unless -s $local_file; + my $yaml_module = $self->_yaml_module; if ($CPAN::META->has_inst($yaml_module)) { my $code = UNIVERSAL::can($yaml_module, "LoadFile"); my @yaml; @@ -372,6 +389,30 @@ sub _yaml_loadfile { return +[]; } +# CPAN::_yaml_dumpfile +sub _yaml_dumpfile { + my($self,$to_local_file,@what) = @_; + my $yaml_module = $self->_yaml_module; + if ($CPAN::META->has_inst($yaml_module)) { + if (UNIVERSAL::isa($to_local_file, "FileHandle")) { + my $code = UNIVERSAL::can($yaml_module, "Dump"); + eval { print $to_local_file $code->(@what) }; + } else { + my $code = UNIVERSAL::can($yaml_module, "DumpFile"); + eval { $code->($to_local_file,@what); }; + } + if ($@) { + $CPAN::Frontend->mydie("Alert: While trying to dump YAML file\n". + " $to_local_file\n". + "with $yaml_module the following error was encountered:\n". + " $@\n" + ); + } + } else { + $CPAN::Frontend->myprint("Note (usually harmless): '$yaml_module' not installed, not dumping to '$to_local_file'\n"); + } +} + package CPAN::CacheMgr; use strict; @CPAN::CacheMgr::ISA = qw(CPAN::InfoObj CPAN); @@ -379,6 +420,7 @@ use File::Find; package CPAN::FTP; use strict; +use Fcntl qw(:flock); use vars qw($Ua $Thesite $ThesiteURL $Themethod); @CPAN::FTP::ISA = qw(CPAN::Debug); @@ -390,6 +432,8 @@ use vars qw(@ISA $USER $PASSWD $SETUPDONE); package CPAN::Complete; use strict; @CPAN::Complete::ISA = qw(CPAN::Debug); +# Q: where is the "How do I add a new command" HOWTO? +# A: svn diff -r 1048:1049 where andk added the report command @CPAN::Complete::COMMANDS = sort qw( ! a b d h i m o q r u autobundle @@ -397,7 +441,9 @@ use strict; cvs_import dump force + hosts install + install_tested look ls make @@ -416,7 +462,7 @@ use strict; package CPAN::Index; use strict; -use vars qw($LAST_TIME $DATE_OF_02 $DATE_OF_03); +use vars qw($LAST_TIME $DATE_OF_02 $DATE_OF_03 $HAVE_REANIMATED); @CPAN::Index::ISA = qw(CPAN::Debug); $LAST_TIME ||= 0; $DATE_OF_03 ||= 0; @@ -474,10 +520,14 @@ sub new { bless {}, shift; } sub as_string { + my $word = "cpan"; + unless ($CPAN::META->{LOCK}) { + $word = "nolock_cpan"; + } if ($CPAN::Config->{commandnumber_in_prompt}) { - sprintf "cpan[%d]> ", $CPAN::CurrentCommandId; + sprintf "$word\[%d]> ", $CPAN::CurrentCommandId; } else { - "cpan> "; + "$word> "; } } @@ -541,7 +591,6 @@ use vars qw( $COLOR_REGISTERED ||= 0; { - # $GLOBAL_AUTOLOAD_RECURSION = 12; $autoload_recursion ||= 0; #-> sub CPAN::Shell::AUTOLOAD ; @@ -591,6 +640,33 @@ $META ||= CPAN->new; # In case we re-eval ourselves we need the || # from here on only subs. ################################################################################ +sub _perl_fingerprint { + my($self,$other_fingerprint) = @_; + my $dll = eval {OS2::DLLname()}; + my $mtime_dll = 0; + if (defined $dll) { + $mtime_dll = (-f $dll ? (stat(_))[9] : '-1'); + } + my $this_fingerprint = { + '$^X' => $^X, + sitearchexp => $Config::Config{sitearchexp}, + 'mtime_$^X' => (stat $^X)[9], + 'mtime_dll' => $mtime_dll, + }; + if ($other_fingerprint) { + if (exists $other_fingerprint->{'stat($^X)'}) { # repair fp from rev. 1.88_57 + $other_fingerprint->{'mtime_$^X'} = $other_fingerprint->{'stat($^X)'}[9]; + } + # mandatory keys since 1.88_57 + for my $key (qw($^X sitearchexp mtime_dll mtime_$^X)) { + return unless $other_fingerprint->{$key} eq $this_fingerprint->{$key}; + } + return 1; + } else { + return $this_fingerprint; + } +} + sub suggest_myconfig () { SUGGEST_MYCONFIG: if(!$INC{'CPAN/MyConfig.pm'}) { $CPAN::Frontend->myprint("You don't seem to have a user ". @@ -645,19 +721,37 @@ sub checklock { "reports other host $otherhost and other ". "process $otherpid.\n". "Cannot proceed.\n")); - } - elsif (defined $otherpid && $otherpid) { + } elsif ($RUN_DEGRADED) { + $CPAN::Frontend->mywarn("Running in degraded mode (experimental)\n"); + } elsif (defined $otherpid && $otherpid) { return if $$ == $otherpid; # should never happen $CPAN::Frontend->mywarn( qq{ There seems to be running another CPAN process (pid $otherpid). Contacting... }); if (kill 0, $otherpid) { - $CPAN::Frontend->mydie(qq{Other job is running. -You may want to kill it and delete the lockfile, maybe. On UNIX try: + $CPAN::Frontend->mywarn(qq{Other job is running.\n}); + my($ans) = + CPAN::Shell::colorable_makemaker_prompt + (qq{Shall I try to run in degraded }. + qq{mode? (Y/n)},"y"); + if ($ans =~ /^y/i) { + $CPAN::Frontend->mywarn("Running in degraded mode (experimental). +Please report if something unexpected happens\n"); + $RUN_DEGRADED = 1; + for ($CPAN::Config) { + $_->{build_dir_reuse} = 0; + $_->{commandnumber_in_prompt} = 0; + $_->{histfile} = ""; + $_->{cache_metadata} = 0; + } + } else { + $CPAN::Frontend->mydie(" +You may want to kill the other job and delete the lockfile. On UNIX try: kill $otherpid rm $lockfile -}); +"); + } } elsif (-w $lockfile) { my($ans) = CPAN::Shell::colorable_makemaker_prompt @@ -675,9 +769,8 @@ You may want to kill it and delete the lockfile, maybe. On UNIX try: ); } } else { - $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Lockfile '$lockfile'\n". - "reports other process with ID ". - "$otherpid. Cannot proceed.\n")); + $CPAN::Frontend->mydie(sprintf("CPAN.pm panic: Found invalid lockfile ". + "'$lockfile', please remove. Cannot proceed.\n")); } } my $dotcpan = $CPAN::Config->{cpan_home}; @@ -715,10 +808,18 @@ Please make sure the directory exists and is writable. return suggest_myconfig; } } # $@ after eval mkpath $dotcpan - my $fh; - unless ($fh = FileHandle->new(">$lockfile")) { - if ($! =~ /Permission/) { - $CPAN::Frontend->myprint(qq{ + if (0) { # to test what happens when a race condition occurs + for (reverse 1..10) { + print $_, "\n"; + sleep 1; + } + } + # locking + if (!$RUN_DEGRADED && !$self->{LOCKFH}) { + my $fh; + unless ($fh = FileHandle->new("+>>$lockfile")) { + if ($! =~ /Permission/) { + $CPAN::Frontend->myprint(qq{ Your configuration suggests that CPAN.pm should use a working directory of @@ -733,13 +834,25 @@ points to a directory where you can write a .lock file. You can set this variable in either a CPAN/MyConfig.pm or a CPAN/Config.pm in your \@INC path; }); - return suggest_myconfig; - } + return suggest_myconfig; + } + } + my $sleep = 1; + while (!flock $fh, LOCK_EX|LOCK_NB) { + if ($sleep>10) { + $CPAN::Frontend->mydie("Giving up\n"); + } + $CPAN::Frontend->mysleep($sleep++); + $CPAN::Frontend->mywarn("Could not lock lockfile with flock: $!; retrying\n"); + } + + seek $fh, 0, 0; + truncate $fh, 0; + $fh->print($$, "\n"); + $fh->print(hostname(), "\n"); + $self->{LOCK} = $lockfile; + $self->{LOCKFH} = $fh; } - $fh->print($$, "\n"); - $fh->print(hostname(), "\n"); - $self->{LOCK} = $lockfile; - $fh->close; $SIG{TERM} = sub { my $sig = shift; &cleanup; @@ -1222,6 +1335,7 @@ sub force_clean_cache { $self->debug("have to rmtree $dir, will free $self->{SIZE}{$dir}") if $CPAN::DEBUG; File::Path::rmtree($dir); + unlink "$dir.yml"; # may fail $self->{DU} -= $self->{SIZE}{$dir}; delete $self->{SIZE}{$dir}; } @@ -1391,9 +1505,9 @@ sub globls { # more than one # author for my $pragma (@$pragmas) { - my $meth = "un$pragma"; - if ($author->can($meth)) { - $author->$meth(); + my $unpragma = "un$pragma"; + if ($author->can($unpragma)) { + $author->$unpragma(); } } } @@ -1498,9 +1612,16 @@ sub o { CPAN::HandleConfig->prettyprint($k); } $CPAN::Frontend->myprint("\n"); - } elsif (!CPAN::HandleConfig->edit(@o_what)) { - $CPAN::Frontend->myprint(qq{Type 'o conf' to view all configuration }. - qq{items\n\n}); + } else { + if (CPAN::HandleConfig->edit(@o_what)) { + unless ($o_what[0] eq "init") { + $CPAN::Frontend->myprint("Please use 'o conf commit' to ". + "make the config permanent!\n\n"); + } + } else { + $CPAN::Frontend->myprint(qq{Type 'o conf' to view all configuration }. + qq{items\n\n}); + } } } elsif ($o_type eq 'debug') { my(%valid); @@ -1587,6 +1708,71 @@ sub paintdots_onreload { }; } +#-> sub CPAN::Shell::hosts ; +sub hosts { + my($self) = @_; + my $fullstats = CPAN::FTP->_ftp_statistics(); + my $history = $fullstats->{history} || []; + my %S; # statistics + while (my $last = pop @$history) { + my $attempts = $last->{attempts} or next; + my $start; + if (@$attempts) { + $start = $attempts->[-1]{start}; + if ($#$attempts > 0) { + for my $i (0..$#$attempts-1) { + my $url = $attempts->[$i]{url} or next; + $S{no}{$url}++; + } + } + } else { + $start = $last->{start}; + } + next unless $last->{thesiteurl}; # C-C? bad filenames? + $S{start} = $start; + $S{end} ||= $last->{end}; + my $dltime = $last->{end} - $start; + my $dlsize = $last->{filesize} || 0; + my $url = $last->{thesiteurl}->text; + my $s = $S{ok}{$url} ||= {}; + $s->{n}++; + $s->{dlsize} ||= 0; + $s->{dlsize} += $dlsize/1024; + $s->{dltime} ||= 0; + $s->{dltime} += $dltime; + } + my $res; + for my $url (keys %{$S{ok}}) { + next if $S{ok}{$url}{dltime} == 0; # div by zero + push @{$res->{ok}}, [@{$S{ok}{$url}}{qw(n dlsize dltime)}, + $S{ok}{$url}{dlsize}/$S{ok}{$url}{dltime}, + $url, + ]; + } + for my $url (keys %{$S{no}}) { + push @{$res->{no}}, [$S{no}{$url}, + $url, + ]; + } + my $R = ""; # report + $R .= sprintf "Log starts: %s\n", scalar(localtime $S{start}) || "unknown"; + $R .= sprintf "Log ends : %s\n", scalar(localtime $S{end}) || "unknown"; + if ($res->{ok} && @{$res->{ok}}) { + $R .= sprintf "\nSuccessful downloads: + N kB secs kB/s url\n"; + for (sort { $b->[3] <=> $a->[3] } @{$res->{ok}}) { + $R .= sprintf "%4d %8d %5d %9.1f %s\n", @$_; + } + } + if ($res->{no} && @{$res->{no}}) { + $R .= sprintf "\nUnsuccessful downloads:\n"; + for (sort { $b->[0] <=> $a->[0] } @{$res->{no}}) { + $R .= sprintf "%4d %s\n", @$_; + } + } + $CPAN::Frontend->myprint($R); +} + #-> sub CPAN::Shell::reload ; sub reload { my($self,$command,@arg) = @_; @@ -1826,6 +2012,39 @@ sub report { # re-run (as documented) } +#-> sub CPAN::Shell::install_tested +sub install_tested { + my($self,@some) = @_; + $CPAN::Frontend->mywarn("install_tested() requires no arguments.\n"), + return if @some; + CPAN::Index->reload; + + for my $d (%{$CPAN::META->{readwrite}{'CPAN::Distribution'}}) { + my $do = CPAN::Shell->expandany($d); + next unless $do->{build_dir}; + push @some, $do; + } + + $CPAN::Frontend->mywarn("No tested distributions found.\n"), + return unless @some; + + @some = grep { $_->{make_test} && ! $_->{make_test}->failed } @some; + $CPAN::Frontend->mywarn("No distributions tested with this build of perl found.\n"), + return unless @some; + + @some = grep { not $_->uptodate } @some; + $CPAN::Frontend->mywarn("No non-uptodate distributions tested with this build of perl found.\n"), + return unless @some; + + CPAN->debug("some[@some]"); + for my $d (@some) { + my $id = $d->can("pretty_id") ? $d->pretty_id : $d->id; + $CPAN::Frontend->myprint("install_tested: Running for $id\n"); + $CPAN::Frontend->sleep(1); + $self->install($d); + } +} + #-> sub CPAN::Shell::upgrade ; sub upgrade { my($self,@args) = @_; @@ -2144,6 +2363,7 @@ sub expand { $self->expand_by_method($class,$methods,@args); } +#-> sub CPAN::Shell::expand_by_method ; sub expand_by_method { my $self = shift; my($class,$methods,@args) = @_; @@ -2302,6 +2522,7 @@ installed. To activate colorized output, please install Term::ANSIColor.\n\n"; } +#-> sub CPAN::Shell::print_ornamented ; sub print_ornamented { my($self,$what,$ornament) = @_; return unless defined $what; @@ -2335,6 +2556,8 @@ Please choose a different color (Hint: try 'o conf init color.*')\n"; } } +#-> sub CPAN::Shell::myprint ; + # where is myprint/mywarn/Frontend/etc. documented? We need guidelines # where to use what! I think, we send everything to STDOUT and use # print for normal/good news and warn for news that need more @@ -2345,18 +2568,21 @@ sub myprint { $self->print_ornamented($what, $CPAN::Config->{colorize_print}||'bold blue on_white'); } +#-> sub CPAN::Shell::myexit ; sub myexit { my($self,$what) = @_; $self->myprint($what); exit; } +#-> sub CPAN::Shell::mywarn ; sub mywarn { my($self,$what) = @_; $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white'); } # only to be used for shell commands +#-> sub CPAN::Shell::mydie ; sub mydie { my($self,$what) = @_; $self->print_ornamented($what, $CPAN::Config->{colorize_warn}||'bold red on_white'); @@ -2369,7 +2595,7 @@ sub mydie { die "\n"; } -# sub CPAN::Shell::colorable_makemaker_prompt +# sub CPAN::Shell::colorable_makemaker_prompt ; sub colorable_makemaker_prompt { my($foo,$bar) = @_; if (CPAN::Shell->colorize_output) { @@ -2385,6 +2611,7 @@ sub colorable_makemaker_prompt { } # use this only for unrecoverable errors! +#-> sub CPAN::Shell::unrecoverable_error ; sub unrecoverable_error { my($self,$what) = @_; my @lines = split /\n/, $what; @@ -2408,11 +2635,13 @@ sub unrecoverable_error { $self->mydie(join "", @lines); } +#-> sub CPAN::Shell::mysleep ; sub mysleep { my($self, $sleep) = @_; sleep $sleep; } +#-> sub CPAN::Shell::setup_output ; sub setup_output { return if -t STDOUT; my $odef = select STDERR; @@ -2544,14 +2773,11 @@ to find objects with matching identifiers. for my $pragma (@pragma) { if ($pragma && - ($] < 5.00303 || $obj->can($pragma))){ - ### compatibility with 5.003 - $obj->$pragma($meth); # the pragma "force" in - # "CPAN::Distribution" must know - # what we are intending + $obj->can($pragma)){ + $obj->$pragma($meth); } } - if ($]>=5.00303 && $obj->can('called_for')) { + if ($obj->can('called_for')) { $obj->called_for($s); } CPAN->debug(qq{pragma[@pragma]meth[$meth]}. @@ -2565,6 +2791,12 @@ to find objects with matching identifiers. } $obj->undelay; + for my $pragma (@pragma) { + my $unpragma = "un$pragma"; + if ($obj->can($unpragma)) { + $obj->$unpragma(); + } + } CPAN::Queue->delete_first($s); } for my $obj (@qcopy) { @@ -2669,7 +2901,6 @@ sub get_non_proxy_credentials { } sub _get_username_and_password_from_user { - my $self = shift; my $username_message = shift; my ($username,$password); @@ -2736,6 +2967,111 @@ sub mirror { package CPAN::FTP; use strict; +#-> sub CPAN::FTP::ftp_statistics +# if they want to rewrite, they need to pass in a filehandle +sub _ftp_statistics { + my($self,$fh) = @_; + my $locktype = $fh ? LOCK_EX : LOCK_SH; + $fh ||= FileHandle->new; + my $file = File::Spec->catfile($CPAN::Config->{cpan_home},"FTPstats.yml"); + open $fh, "+>>$file" or $CPAN::Frontend->mydie("Could not open '$file': $!"); + my $sleep = 1; + while (!flock $fh, $locktype|LOCK_NB) { + if ($sleep>3) { + die; + } + $CPAN::Frontend->mysleep($sleep++); + } + my $stats = CPAN->_yaml_loadfile($file); + if ($locktype == LOCK_SH) { + } else { + seek $fh, 0, 0; + if (@$stats){ # no yaml no write + truncate $fh, 0; + } + } + return $stats->[0]; +} + +sub _mytime () { + if (CPAN->has_inst("Time::HiRes")) { + return Time::HiRes::time(); + } else { + return time; + } +} + +sub _new_stats { + my($self,$file) = @_; + my $ret = { + file => $file, + attempts => [], + start => _mytime, + }; + $ret; +} + +sub _add_to_statistics { + my($self,$stats) = @_; + $stats->{thesiteurl} = $ThesiteURL; + if (CPAN->has_inst("Time::HiRes")) { + $stats->{end} = Time::HiRes::time(); + } else { + $stats->{end} = time; + } + my $fh = FileHandle->new; + my $fullstats = $self->_ftp_statistics($fh); + push @{$fullstats->{history}}, $stats; + my $time = time; + shift @{$fullstats->{history}} + while $time - $fullstats->{history}[0]{start} > 30*86400; # one month too much? + CPAN->_yaml_dumpfile($fh,$fullstats); +} + +# if file is CHECKSUMS, suggest the place where we got the file to be +# checked from, maybe only for young files? +sub _recommend_url_for { + my($self, $file) = @_; + my $urllist = $self->_get_urllist; + if ($file =~ s|/CHECKSUMS(.gz)?$||) { + my $fullstats = $self->_ftp_statistics(); + my $history = $fullstats->{history} || []; + while (my $last = pop @$history) { + last if $last->{end} - time > 3600; # only young results are interesting + next unless $file eq File::Basename::dirname($last->{file}); + return $last->{thesiteurl}; + } + } + if ($CPAN::Config->{randomize_urllist} + && + rand(1) < $CPAN::Config->{randomize_urllist} + ) { + $urllist->[int rand scalar @$urllist]; + } else { + return (); + } +} + +sub _get_urllist { + my($self) = @_; + $CPAN::Config->{urllist} ||= []; + unless (ref $CPAN::Config->{urllist} eq 'ARRAY') { + $CPAN::Frontend->mywarn("Malformed urllist; ignoring. Configuration file corrupt?\n"); + $CPAN::Config->{urllist} = []; + } + my @urllist = grep { defined $_ and length $_ } @{$CPAN::Config->{urllist}}; + for my $u (@urllist) { + CPAN->debug("u[$u]") if $CPAN::DEBUG; + if (UNIVERSAL::can($u,"text")) { + $u->{TEXT} .= "/" unless substr($u->{TEXT},-1) eq "/"; + } else { + $u .= "/" unless substr($u,-1) eq "/"; + $u = CPAN::URL->new(TEXT => $u, FROM => "USER"); + } + } + \@urllist; +} + #-> sub CPAN::FTP::ftp_get ; sub ftp_get { my($class,$host,$dir,$file,$target) = @_; @@ -2845,10 +3181,10 @@ sub localize { "could not remove."); } } - my($restore) = 0; + my($maybe_restore) = 0; if (-f $aslocal){ - rename $aslocal, "$aslocal.bak"; - $restore++; + rename $aslocal, "$aslocal.bak$$"; + $maybe_restore++; } my($aslocal_dir) = File::Basename::dirname($aslocal); @@ -2898,31 +3234,27 @@ sub localize { # Try the list of urls for each single object. We keep a record # where we did get a file from my(@reordered,$last); - $CPAN::Config->{urllist} ||= []; - unless (ref $CPAN::Config->{urllist} eq 'ARRAY') { - $CPAN::Frontend->mywarn("Malformed urllist; ignoring. Configuration file corrupt?\n"); - $CPAN::Config->{urllist} = []; - } - $last = $#{$CPAN::Config->{urllist}}; + my $ccurllist = $self->_get_urllist; + $last = $#$ccurllist; if ($force & 2) { # local cpans probably out of date, don't reorder @reordered = (0..$last); } else { @reordered = sort { - (substr($CPAN::Config->{urllist}[$b],0,4) eq "file") + (substr($ccurllist->[$b],0,4) eq "file") <=> - (substr($CPAN::Config->{urllist}[$a],0,4) eq "file") + (substr($ccurllist->[$a],0,4) eq "file") or defined($ThesiteURL) and - ($CPAN::Config->{urllist}[$b] eq $ThesiteURL) + ($ccurllist->[$b] eq $ThesiteURL) <=> - ($CPAN::Config->{urllist}[$a] eq $ThesiteURL) + ($ccurllist->[$a] eq $ThesiteURL) } 0..$last; } my(@levels); $Themethod ||= ""; - $self->debug("Themethod[$Themethod]") if $CPAN::DEBUG; + $self->debug("Themethod[$Themethod]reordered[@reordered]") if $CPAN::DEBUG; if ($Themethod) { @levels = ($Themethod, grep {$_ ne $Themethod} qw/easy hard hardest/); } else { @@ -2933,37 +3265,53 @@ sub localize { local $ENV{FTP_PASSIVE} = exists $CPAN::Config->{ftp_passive} ? $CPAN::Config->{ftp_passive} : 1; - for $levelno (0..$#levels) { + my $ret; + my $stats = $self->_new_stats($file); + LEVEL: for $levelno (0..$#levels) { my $level = $levels[$levelno]; my $method = "host$level"; my @host_seq = $level eq "easy" ? @reordered : 0..$last; # reordered has CDROM up front - my @urllist = map { $CPAN::Config->{urllist}[$_] } @host_seq; - for my $u (@urllist) { - if ($u->can("text")) { - $u->{TEXT} .= "/" unless substr($u->{TEXT},-1) eq "/"; - } else { - $u .= "/" unless substr($u,-1) eq "/"; - $u = CPAN::URL->new(TEXT => $u, FROM => "USER"); - } - } + my @urllist = map { $ccurllist->[$_] } @host_seq; for my $u (@CPAN::Defaultsites) { push @urllist, $u unless grep { $_ eq $u } @urllist; } $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; - my $ret = $self->$method(\@urllist,$file,$aslocal); + my $aslocal_tempfile = $aslocal . ".tmp" . $$; + if (my $recommend = $self->_recommend_url_for($file)) { + @urllist = grep { $_ ne $recommend } @urllist; + unshift @urllist, $recommend; + } + $self->debug("synth. urllist[@urllist]") if $CPAN::DEBUG; + $ret = $self->$method(\@urllist,$file,$aslocal_tempfile,$stats); if ($ret) { - $Themethod = $level; - my $now = time; - # utime $now, $now, $aslocal; # too bad, if we do that, we - # might alter a local mirror - $self->debug("level[$level]") if $CPAN::DEBUG; - return $ret; + CPAN->debug("ret[$ret]aslocal[$aslocal]") if $CPAN::DEBUG; + if ($ret eq $aslocal_tempfile) { + # if we got it exactly as we asked for, only then we + # want to rename + rename $aslocal_tempfile, $aslocal + or $CPAN::Frontend->mydie("Error while trying to rename ". + "'$ret' to '$aslocal': $!"); + $ret = $aslocal; + } + $Themethod = $level; + my $now = time; + # utime $now, $now, $aslocal; # too bad, if we do that, we + # might alter a local mirror + $self->debug("level[$level]") if $CPAN::DEBUG; + last LEVEL; } else { - unlink $aslocal; - last if $CPAN::Signal; # need to cleanup + unlink $aslocal_tempfile; + last if $CPAN::Signal; # need to cleanup } } + if ($ret) { + $stats->{filesize} = -s $ret; + } + $self->_add_to_statistics($stats); + if ($ret) { + return $ret; + } unless ($CPAN::Signal) { my(@mess); local $" = " "; @@ -2981,8 +3329,8 @@ sub localize { $CPAN::Frontend->mywarn("Could not fetch $file\n"); $CPAN::Frontend->mysleep(2); } - if ($restore) { - rename "$aslocal.bak", $aslocal; + if ($maybe_restore) { + rename "$aslocal.bak$$", $aslocal; $CPAN::Frontend->myprint("Trying to get away with old file:\n" . $self->ls($aslocal)); return $aslocal; @@ -2990,11 +3338,21 @@ sub localize { return; } +sub _set_attempt { + my($self,$stats,$method,$url) = @_; + push @{$stats->{attempts}}, { + method => $method, + start => _mytime, + url => $url, + }; +} + # package CPAN::FTP; sub hosteasy { - my($self,$host_seq,$file,$aslocal) = @_; + my($self,$host_seq,$file,$aslocal,$stats) = @_; my($ro_url); HOSTEASY: for $ro_url (@$host_seq) { + $self->_set_attempt($stats,"easy",$ro_url); my $url .= "$ro_url$file"; $self->debug("localizing perlish[$url]") if $CPAN::DEBUG; if ($url =~ /^file:/) { @@ -3037,6 +3395,7 @@ sub hosteasy { } } } + $self->debug("it was not a file URL") if $CPAN::DEBUG; if ($CPAN::META->has_usable('LWP')) { $CPAN::Frontend->myprint("Fetching with LWP: $url @@ -3078,19 +3437,13 @@ sub hosteasy { # Net::FTP can still succeed where LWP fails. So we do not # skip Net::FTP anymore when LWP is available. } - } elsif ( - $ro_url->can("text") - and - $ro_url->{FROM} eq "USER" - ){ - my $ret = $self->hosthard([$ro_url],$file,$aslocal); - return $ret if $ret; } else { $CPAN::Frontend->mywarn(" LWP not available\n"); } return if $CPAN::Signal; if ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { # that's the nice and easy way thanks to Graham + $self->debug("recognized ftp") if $CPAN::DEBUG; my($host,$dir,$getfile) = ($1,$2,$3); if ($CPAN::META->has_usable('Net::FTP')) { $dir =~ s|/+|/|g; @@ -3119,15 +3472,27 @@ sub hosteasy { } } # next HOSTEASY; - } + } else { + CPAN->debug("Net::FTP does not count as usable atm") if $CPAN::DEBUG; + } } + if ( + UNIVERSAL::can($ro_url,"text") + and + $ro_url->{FROM} eq "USER" + ){ + ##address #17973: default URLs should not try to override + ##user-defined URLs just because LWP is not available + my $ret = $self->hosthard([$ro_url],$file,$aslocal,$stats); + return $ret if $ret; + } return if $CPAN::Signal; } } # package CPAN::FTP; sub hosthard { - my($self,$host_seq,$file,$aslocal) = @_; + my($self,$host_seq,$file,$aslocal,$stats) = @_; # Came back if Net::FTP couldn't establish connection (or # failed otherwise) Maybe they are behind a firewall, but they @@ -3139,6 +3504,7 @@ sub hosthard { my($aslocal_dir) = File::Basename::dirname($aslocal); File::Path::mkpath($aslocal_dir); HOSTHARD: for $ro_url (@$host_seq) { + $self->_set_attempt($stats,"hard",$ro_url); my $url = "$ro_url$file"; my($proto,$host,$dir,$getfile); @@ -3197,8 +3563,11 @@ Trying with "$funkyftp$src_switch" to get if ($f eq "lynx") { # lynx returns 0 when it fails somewhere if (-s $asl_ungz) { - my $content = do { local *FH; open FH, $asl_ungz or die; local $/; }; - if ($content =~ /^<.*[45]/si) { + my $content = do { local *FH; + open FH, $asl_ungz or die; + local $/; + <FH> }; + if ($content =~ /^<.*(<title>[45]|Error [45])/si) { $CPAN::Frontend->mywarn(qq{ No success, the file that lynx has has downloaded looks like an error message: $content @@ -3274,7 +3643,7 @@ returned status $estatus (wstat $wstatus)$size # package CPAN::FTP; sub hosthardest { - my($self,$host_seq,$file,$aslocal) = @_; + my($self,$host_seq,$file,$aslocal,$stats) = @_; my($ro_url); my($aslocal_dir) = File::Basename::dirname($aslocal); @@ -3299,6 +3668,7 @@ config variable with }); $CPAN::Frontend->mysleep(2); HOSTHARDEST: for $ro_url (@$host_seq) { + $self->_set_attempt($stats,"hardest",$ro_url); my $url = "$ro_url$file"; $self->debug("localizing ftpwise[$url]") if $CPAN::DEBUG; unless ($url =~ m|^ftp://(.*?)/(.*)/(.*)|) { @@ -3677,7 +4047,7 @@ sub force_reload { #-> sub CPAN::Index::reload ; sub reload { - my($cl,$force) = @_; + my($self,$force) = @_; my $time = time; # XXX check if a newer one is available. (We currently read it @@ -3691,29 +4061,30 @@ sub reload { Carp::cluck("META-PROTOCOL[$CPAN::META->{PROTOCOL}]"); } unless ($CPAN::META->{PROTOCOL}) { - $cl->read_metadata_cache; + $self->read_metadata_cache; $CPAN::META->{PROTOCOL} ||= "1.0"; } if ( $CPAN::META->{PROTOCOL} < PROTOCOL ) { # warn "Setting last_time to 0"; $LAST_TIME = 0; # No warning necessary } - return if $LAST_TIME + $CPAN::Config->{index_expire}*86400 > $time - and ! $force; - if (0) { + if ($LAST_TIME + $CPAN::Config->{index_expire}*86400 > $time + and ! $force){ + # called too often + # CPAN->debug("LAST_TIME[$LAST_TIME]index_expire[$CPAN::Config->{index_expire}]time[$time]"); + } elsif (0) { # IFF we are developing, it helps to wipe out the memory # between reloads, otherwise it is not what a user expects. undef $CPAN::META; # Neue Gruendlichkeit since v1.52(r1.274) $CPAN::META = CPAN->new; - } - { + } else { my($debug,$t2); local $LAST_TIME = $time; local $CPAN::META->{PROTOCOL} = PROTOCOL; my $needshort = $^O eq "dos"; - $cl->rd_authindex($cl + $self->rd_authindex($self ->reload_x( "authors/01mailrc.txt.gz", $needshort ? @@ -3724,7 +4095,7 @@ sub reload { $debug = "timing reading 01[".($t2 - $time)."]"; $time = $t2; return if $CPAN::Signal; # this is sometimes lengthy - $cl->rd_modpacks($cl + $self->rd_modpacks($self ->reload_x( "modules/02packages.details.txt.gz", $needshort ? @@ -3735,23 +4106,77 @@ sub reload { $debug .= "02[".($t2 - $time)."]"; $time = $t2; return if $CPAN::Signal; # this is sometimes lengthy - $cl->rd_modlist($cl + $self->rd_modlist($self ->reload_x( "modules/03modlist.data.gz", $needshort ? File::Spec->catfile('modules', '03mlist.gz') : File::Spec->catfile('modules', '03modlist.data.gz'), $force)); - $cl->write_metadata_cache; + $self->write_metadata_cache; $t2 = time; $debug .= "03[".($t2 - $time)."]"; $time = $t2; CPAN->debug($debug) if $CPAN::DEBUG; } + if ($CPAN::Config->{build_dir_reuse}) { + $self->reanimate_build_dir; + } $LAST_TIME = $time; $CPAN::META->{PROTOCOL} = PROTOCOL; } +#-> sub CPAN::Index::reanimate_build_dir ; +sub reanimate_build_dir { + my($self) = @_; + unless ($CPAN::META->has_inst($CPAN::Config->{yaml_module}||"YAML")) { + return; + } + return if $HAVE_REANIMATED++; + my $d = $CPAN::Config->{build_dir}; + my $dh = DirHandle->new; + opendir $dh, $d or return; # does not exist + my $dirent; + my $i = 0; + my $painted = 0; + my $restored = 0; + $CPAN::Frontend->myprint("Going to read $CPAN::Config->{build_dir}/\n"); + my @candidates = grep {/\.yml$/} readdir $dh; + DISTRO: for $dirent (@candidates) { + my $c = CPAN->_yaml_loadfile(File::Spec->catfile($d,$dirent))->[0]; + if ($c && CPAN->_perl_fingerprint($c->{perl})) { + my $key = $c->{distribution}{ID}; + for my $k (keys %{$c->{distribution}}) { + if ($c->{distribution}{$k} + && ref $c->{distribution}{$k} + && UNIVERSAL::isa($c->{distribution}{$k},"CPAN::Distrostatus")) { + # the correct algorithm would be a + # two-pass and we would subtract the + # maximum of all old commands minus 2 + $c->{distribution}{$k}{COMMANDID} -= scalar @candidates - 2 ; + } + } + + #we tried to restore only if element already + #exists; but then we do not work with metadata + #turned off. + $CPAN::META->{readwrite}{'CPAN::Distribution'}{$key} = $c->{distribution}; + $restored++; + } + $i++; + while (($painted/76) < ($i/@candidates)) { + $CPAN::Frontend->myprint("."); + $painted++; + } + } + $CPAN::Frontend->myprint(sprintf( + "DONE\nFound %s old builds, restored the state of %s\n", + @candidates ? sprintf("%d",scalar @candidates) : "no", + $restored || "none", + )); +} + + #-> sub CPAN::Index::reload_x ; sub reload_x { my($cl,$wanted,$localname,$force) = @_; @@ -4310,7 +4735,7 @@ sub as_string { for my $y (sort keys %{$v->{$x}}) { push @svalue, "$y=>$v->{$x}{$y}"; } - push @value, "$x\:" . join ",", @svalue; + push @value, "$x\:" . join ",", @svalue if @svalue; } $value = join ";", @value; } else { @@ -4562,6 +4987,15 @@ sub normalize { my($self,$s) = @_; $s = $self->id unless defined $s; if (substr($s,-1,1) eq ".") { + # using a global because we are sometimes called as static method + if (!$CPAN::META->{LOCK} + && !$CPAN::Have_warned->{"$s is unlocked"}++ + ) { + $CPAN::Frontend->mywarn("You are visiting the local directory + '$s' + without lock, take care that concurrent processes do not do likewise.\n"); + $CPAN::Frontend->mysleep(1); + } if ($s eq ".") { $s = "$CPAN::iCwd/."; } elsif (File::Spec->file_name_is_absolute($s)) { @@ -4756,6 +5190,13 @@ sub get { EXCUSE: { my @e; + if ($self->prefs->{disabled}) { + push @e, sprintf( + "disabled via prefs file '%s' doc %d", + $self->{prefs_file}, + $self->{prefs_file_doc}, + ); + } exists $self->{build_dir} and push @e, "Is already unwrapped into directory $self->{build_dir}"; @@ -4816,15 +5257,15 @@ sub get { $CPAN::META->{cachemgr} ||= CPAN::CacheMgr->new(); # unsafe meta access, ok my $builddir = $CPAN::META->{cachemgr}->dir; # unsafe meta access, ok $self->safe_chdir($builddir); - $self->debug("Removing tmp") if $CPAN::DEBUG; - File::Path::rmtree("tmp"); - unless (mkdir "tmp", 0755) { + $self->debug("Removing tmp-$$") if $CPAN::DEBUG; + File::Path::rmtree("tmp-$$"); + unless (mkdir "tmp-$$", 0755) { $CPAN::Frontend->unrecoverable_error(<<EOF); -Couldn't mkdir '$builddir/tmp': $! +Couldn't mkdir '$builddir/tmp-$$': $! Cannot continue: Please find the reason why I cannot make the directory -$builddir/tmp +$builddir/tmp-$$ and fix the problem, then retry. EOF @@ -4833,7 +5274,7 @@ EOF $self->safe_chdir($sub_wd); return; } - $self->safe_chdir("tmp"); + $self->safe_chdir("tmp-$$"); # # Unpack the goods @@ -4859,49 +5300,84 @@ EOF or Carp::croak("Couldn't opendir .: $!"); my @readdir = grep $_ !~ /^\.\.?(?!\n)\Z/s, $dh->read; ### MAC?? $dh->close; - my ($distdir,$packagedir); - if (@readdir == 1 && -d $readdir[0]) { - $distdir = $readdir[0]; - $packagedir = File::Spec->catdir($builddir,$distdir); - $self->debug("packagedir[$packagedir]builddir[$builddir]distdir[$distdir]") - if $CPAN::DEBUG; - -d $packagedir and $CPAN::Frontend->myprint("Removing previously used ". - "$packagedir\n"); - File::Path::rmtree($packagedir); - unless (File::Copy::move($distdir,$packagedir)) { - $CPAN::Frontend->unrecoverable_error(<<EOF); + my ($packagedir); + # XXX here we want in each branch File::Temp to protect all build_dir directories + if (CPAN->has_inst("File::Temp")) { + my $tdir_base; + my $from_dir; + my @dirents; + if (@readdir == 1 && -d $readdir[0]) { + $tdir_base = $readdir[0]; + $from_dir = File::Spec->catdir(File::Spec->curdir,$readdir[0]); + my $dh2 = DirHandle->new($from_dir) + or Carp::croak("Couldn't opendir $from_dir: $!"); + @dirents = grep $_ !~ /^\.\.?(?!\n)\Z/s, $dh2->read; ### MAC?? + } else { + my $userid = $self->cpan_userid; + CPAN->debug("userid[$userid]"); + if (!$userid or $userid eq "N/A") { + $userid = "anon"; + } + $tdir_base = $userid; + $from_dir = File::Spec->curdir; + @dirents = @readdir; + } + $packagedir = File::Temp::tempdir( + "$tdir_base-XXXXXX", + DIR => $builddir, + CLEANUP => 0, + ); + my $f; + for $f (@dirents) { # is already without "." and ".." + my $from = File::Spec->catdir($from_dir,$f); + my $to = File::Spec->catdir($packagedir,$f); + File::Copy::move($from,$to) or Carp::confess("Couldn't move $from to $to: $!"); + } + } else { # older code below, still better than nothing when there is no File::Temp + my($distdir); + if (@readdir == 1 && -d $readdir[0]) { + $distdir = $readdir[0]; + $packagedir = File::Spec->catdir($builddir,$distdir); + $self->debug("packagedir[$packagedir]builddir[$builddir]distdir[$distdir]") + if $CPAN::DEBUG; + -d $packagedir and $CPAN::Frontend->myprint("Removing previously used ". + "$packagedir\n"); + File::Path::rmtree($packagedir); + unless (File::Copy::move($distdir,$packagedir)) { + $CPAN::Frontend->unrecoverable_error(<<EOF); Couldn't move '$distdir' to '$packagedir': $! Cannot continue: Please find the reason why I cannot move -$builddir/tmp/$distdir +$builddir/tmp-$$/$distdir to $packagedir and fix the problem, then retry EOF - } - $self->debug(sprintf("moved distdir[%s] to packagedir[%s] -e[%s]-d[%s]", - $distdir, - $packagedir, - -e $packagedir, - -d $packagedir, - )) if $CPAN::DEBUG; - } else { - my $userid = $self->cpan_userid; - CPAN->debug("userid[$userid]"); - if (!$userid or $userid eq "N/A") { - $userid = "anon"; - } - my $pragmatic_dir = $userid . '000'; - $pragmatic_dir =~ s/\W_//g; - $pragmatic_dir++ while -d "../$pragmatic_dir"; - $packagedir = File::Spec->catdir($builddir,$pragmatic_dir); - $self->debug("packagedir[$packagedir]") if $CPAN::DEBUG; - File::Path::mkpath($packagedir); - my($f); - for $f (@readdir) { # is already without "." and ".." - my $to = File::Spec->catdir($packagedir,$f); - File::Copy::move($f,$to) or Carp::confess("Couldn't move $f to $to: $!"); + } + $self->debug(sprintf("moved distdir[%s] to packagedir[%s] -e[%s]-d[%s]", + $distdir, + $packagedir, + -e $packagedir, + -d $packagedir, + )) if $CPAN::DEBUG; + } else { + my $userid = $self->cpan_userid; + CPAN->debug("userid[$userid]"); + if (!$userid or $userid eq "N/A") { + $userid = "anon"; + } + my $pragmatic_dir = $userid . '000'; + $pragmatic_dir =~ s/\W_//g; + $pragmatic_dir++ while -d "../$pragmatic_dir"; + $packagedir = File::Spec->catdir($builddir,$pragmatic_dir); + $self->debug("packagedir[$packagedir]") if $CPAN::DEBUG; + File::Path::mkpath($packagedir); + my($f); + for $f (@readdir) { # is already without "." and ".." + my $to = File::Spec->catdir($packagedir,$f); + File::Copy::move($f,$to) or Carp::confess("Couldn't move $f to $to: $!"); + } } } if ($CPAN::Signal){ @@ -4911,7 +5387,7 @@ EOF $self->{'build_dir'} = $packagedir; $self->safe_chdir($builddir); - File::Path::rmtree("tmp"); + File::Path::rmtree("tmp-$$"); $self->safe_chdir($packagedir); $self->_signature_business(); @@ -4946,10 +5422,30 @@ EOF } elsif (! $mpl_exists) { $self->_edge_cases($mpl,$packagedir,$local_file); } + if ($self->{build_dir} + && + $CPAN::Config->{build_dir_reuse} + ) { + $self->store_persistent_state; + } return $self; } +#-> CPAN::Distribution::store_persistent_state +sub store_persistent_state { + my($self) = @_; + my $file = sprintf "%s.yml", $self->{build_dir}; + CPAN->_yaml_dumpfile( + $file, + { + time => time, + perl => CPAN::_perl_fingerprint, + distribution => $self, + } + ); +} + #-> CPAN::Distribution::patch sub try_download { my($self,$patch) = @_; @@ -4983,7 +5479,10 @@ sub patch { "Please run 'o conf init /patch/'\n\n"); } $patchbin = CPAN::HandleConfig->safe_quote($patchbin); - my $args = "-b -g0 -p1 -N --fuzz=3"; + local $ENV{PATCH_GET} = 0; # shall replace -g0 which is not + # supported everywhere (and then, + # not ever necessary there) + my $stdpatchargs = "-N --fuzz=3"; my $countedpatches = @$patches == 1 ? "1 patch" : (scalar @$patches . " patches"); $CPAN::Frontend->myprint("Going to apply $countedpatches:\n"); for my $patch (@$patches) { @@ -5000,9 +5499,11 @@ sub patch { } $CPAN::Frontend->myprint(" $patch\n"); my $readfh = CPAN::Tarzip->TIEHANDLE($patch); + my $thispatchargs = join " ", $stdpatchargs, $self->_patch_p_parameter($readfh); + $readfh = CPAN::Tarzip->TIEHANDLE($patch); my $writefh = FileHandle->new; - unless (open $writefh, "|$patchbin $args") { - my $fail = "Could not fork '$patchbin $args'"; + unless (open $writefh, "|$patchbin $thispatchargs") { + my $fail = "Could not fork '$patchbin $thispatchargs'"; $CPAN::Frontend->mywarn("$fail; cannot continue\n"); $self->{unwrapped} = CPAN::Distrostatus->new("NO -- $fail"); delete $self->{build_dir}; @@ -5024,6 +5525,19 @@ sub patch { return 1; } +sub _patch_p_parameter { + my($self,$fh) = @_; + my($cnt_files,$cnt_p0files); + local($_); + while ($_ = $fh->READLINE) { + next unless /^[\*\+]{3}\s(\S+)/; + my $file = $1; + $cnt_files++; + $cnt_p0files++ if -f $file; + } + return $cnt_files==$cnt_p0files ? "-p0" : "-p1"; +} + #-> sub CPAN::Distribution::_edge_cases # with "configure" or "Makefile" or single file scripts sub _edge_cases { @@ -5151,16 +5665,11 @@ sub _signature_business { my $rv = Module::Signature::verify(); if ($rv != Module::Signature::SIGNATURE_OK() and $rv != Module::Signature::SIGNATURE_MISSING()) { - $CPAN::Frontend->myprint( - qq{\nSignature invalid for }. - qq{distribution file. }. - qq{Please investigate.\n\n}. - $self->as_string, - $CPAN::META->instance( - 'CPAN::Author', - $self->cpan_userid, - )->as_string - ); + $CPAN::Frontend->mywarn( + qq{\nSignature invalid for }. + qq{distribution file. }. + qq{Please investigate.\n\n} + ); my $wrap = sprintf(qq{I'd recommend removing %s. Its signature @@ -5593,6 +6102,7 @@ sub force { for my $att (qw( CHECKSUM_STATUS archived + badtestcnt build_dir install localfile @@ -5617,12 +6127,14 @@ sub force { } } +#-> sub CPAN::Distribution::notest ; sub notest { my($self, $method) = @_; # warn "XDEBUG: set notest for $self $method"; $self->{"notest"}++; # name should probably have been force_install } +#-> sub CPAN::Distribution::unnotest ; sub unnotest { my($self) = @_; # warn "XDEBUG: deleting notest"; @@ -5648,7 +6160,7 @@ sub isa_perl { | \d+\.\d+ ) - \.tar[._-]gz + \.tar[._-](?:gz|bz2) (?!\n)\Z }xs){ return "$1.$3"; @@ -5876,8 +6388,14 @@ is part of the perl-%s distribution. To install that, you need to run return; } } else { - if (my $expect = $self->prefs->{pl}{expect}) { - $ret = $self->_run_via_expect($system,$expect); + if (my $expect_model = $self->_prefs_with_expect("pl")) { + $ret = $self->_run_via_expect($system,$expect_model); + if (! defined $ret + && $self->{writemakefile} + && $self->{writemakefile}->failed) { + # timeout + return; + } } else { $ret = system($system); } @@ -5885,6 +6403,8 @@ is part of the perl-%s distribution. To install that, you need to run $self->{writemakefile} = CPAN::Distrostatus ->new("NO '$system' returned status $ret"); $CPAN::Frontend->mywarn("Warning: No success on command[$system]\n"); + $self->store_persistent_state; + $self->store_persistent_state; return; } } @@ -5893,7 +6413,7 @@ is part of the perl-%s distribution. To install that, you need to run delete $self->{make_clean}; # if cleaned before, enable next } else { $self->{writemakefile} = CPAN::Distrostatus - ->new(qq{NO -- Unknown reason.}); + ->new(qq{NO -- Unknown reason}); } } if ($CPAN::Signal){ @@ -5906,6 +6426,7 @@ is part of the perl-%s distribution. To install that, you need to run my $id = $self->pretty_id; $CPAN::Frontend->mywarn("$id $need; you have only $]; giving up\n"); $self->{make} = CPAN::Distrostatus->new("NO $need"); + $self->store_persistent_state; return; } else { return 1 if $self->follow_prereqs(@prereq); # signal success to the queuerunner @@ -5939,7 +6460,25 @@ is part of the perl-%s distribution. To install that, you need to run $ENV{$e} = $env->{$e}; } } - if (system($system) == 0) { + my $expect_model = $self->_prefs_with_expect("make"); + my $want_expect = 0; + if ( $expect_model && @{$expect_model->{talk}} ) { + my $can_expect = $CPAN::META->has_inst("Expect"); + if ($can_expect) { + $want_expect = 1; + } else { + $CPAN::Frontend->mywarn("Expect not installed, falling back to ". + "system\n"); + } + } + my $system_ok; + if ($want_expect) { + $system_ok = $self->_run_via_expect($system,$expect_model) == 0; + } else { + $system_ok = system($system) == 0; + } + $self->introduce_myself; + if ( $system_ok ) { $CPAN::Frontend->myprint(" $system -- OK\n"); $self->{make} = CPAN::Distrostatus->new("YES"); } else { @@ -5947,50 +6486,116 @@ is part of the perl-%s distribution. To install that, you need to run $self->{make} = CPAN::Distrostatus->new("NO"); $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); } + $self->store_persistent_state; } # CPAN::Distribution::_run_via_expect sub _run_via_expect { - my($self,$system,$expect) = @_; - CPAN->debug("system[$system]expect[$expect]") if $CPAN::DEBUG; + my($self,$system,$expect_model) = @_; + CPAN->debug("system[$system]expect_model[$expect_model]") if $CPAN::DEBUG; if ($CPAN::META->has_inst("Expect")) { - my $expo = Expect->new; + my $expo = Expect->new; # expo Expect object; $expo->spawn($system); - EXPECT: for (my $i = 0; $i < $#$expect; $i+=2) { - my $next = $expect->[$i]; - my($timeout,$re); - if (ref $next) { - $timeout = $next->{timeout}; - $re = $next->{expect}; - } else { - $timeout = 15; - $re = $next; - } - my $regex = eval "qr{$re}"; - my $send = $expect->[$i+1]; - $expo->expect($timeout, - [ eof => sub { - my $but = $expo->clear_accum; - $CPAN::Frontend->mywarn("EOF (maybe harmless) system[$system] -expected[$regex]\nbut[$but]\n\n"); - last EXPECT; - } ], - [ timeout => sub { - my $but = $expo->clear_accum; - $CPAN::Frontend->mydie("TIMEOUT system[$system] -expected[$regex]\nbut[$but]\n\n"); - } ], - -re => $regex); - $expo->send($send); + my $expecta = $expect_model->{talk}; + if ($expect_model->{mode} eq "expect") { + return $self->_run_via_expect_deterministic($expo,$expecta); + } elsif ($expect_model->{mode} eq "expect-in-any-order") { + return $self->_run_via_expect_anyorder($expo,$expecta); + } else { + die "Panic: Illegal expect mode: $expect_model->{mode}"; } - $expo->soft_close; - return $expo->exitstatus(); } else { $CPAN::Frontend->mywarn("Expect not installed, falling back to system()\n"); return system($system); } } +sub _run_via_expect_anyorder { + my($self,$expo,$expecta) = @_; + my $timeout = 3; # currently unsettable + my @expectacopy = @$expecta; # we trash it! + my $but = ""; + EXPECT: while () { + my($eof,$ran_into_timeout); + my @match = $expo->expect($timeout, + [ eof => sub { + $eof++; + } ], + [ timeout => sub { + $ran_into_timeout++; + } ], + -re => eval"qr{.}", + ); + if ($match[2]) { + $but .= $match[2]; + } + $but .= $expo->clear_accum; + if ($eof) { + $expo->soft_close; + return $expo->exitstatus(); + } elsif ($ran_into_timeout) { + # warn "DEBUG: they are asking a question, but[$but]"; + for (my $i = 0; $i <= $#expectacopy; $i+=2) { + my($next,$send) = @expectacopy[$i,$i+1]; + my $regex = eval "qr{$next}"; + # warn "DEBUG: will compare with regex[$regex]."; + if ($but =~ /$regex/) { + # warn "DEBUG: will send send[$send]"; + $expo->send($send); + splice @expectacopy, $i, 2; # never allow reusing an QA pair + next EXPECT; + } + } + my $why = "could not answer a question during the dialog"; + $CPAN::Frontend->mywarn("Failing: $why\n"); + $self->{writemakefile} = + CPAN::Distrostatus->new("NO $why"); + return; + } + } +} + +sub _run_via_expect_deterministic { + my($self,$expo,$expecta) = @_; + my $ran_into_timeout; + EXPECT: for (my $i = 0; $i <= $#$expecta; $i+=2) { + my($next,$send) = @$expecta[$i,$i+1]; + my($timeout,$re); + if (ref $next) { + $timeout = $next->{timeout}; + $re = $next->{expect}; + } else { + $timeout = 15; + $re = $next; + } + CPAN->debug("timeout[$timeout]re[$re]") if $CPAN::DEBUG; + my $regex = eval "qr{$re}"; + $expo->expect($timeout, + [ eof => sub { + my $but = $expo->clear_accum; + $CPAN::Frontend->mywarn("EOF (maybe harmless) +expected[$regex]\nbut[$but]\n\n"); + last EXPECT; + } ], + [ timeout => sub { + my $but = $expo->clear_accum; + $CPAN::Frontend->mywarn("TIMEOUT +expected[$regex]\nbut[$but]\n\n"); + $ran_into_timeout++; + } ], + -re => $regex); + if ($ran_into_timeout){ + # note that the caller expects 0 for success + $self->{writemakefile} = + CPAN::Distrostatus->new("NO timeout during expect dialog"); + return; + } + $expo->send($send); + } + $expo->soft_close; + return $expo->exitstatus(); +} + # CPAN::Distribution::_find_prefs sub _find_prefs { my($self) = @_; @@ -6001,7 +6606,7 @@ sub _find_prefs { if ($@) { $CPAN::Frontend->mydie("Cannot create directory $prefs_dir"); } - my $yaml_module = $CPAN::Config->{yaml_module} || "YAML"; + my $yaml_module = CPAN->_yaml_module; if ($CPAN::META->has_inst($yaml_module)) { my $dh = DirHandle->new($prefs_dir) or die Carp::croak("Couldn't open '$prefs_dir': $!"); @@ -6051,7 +6656,7 @@ sub _find_prefs { return { prefs => $yaml, prefs_file => $abs, - prefs_file_section => $y, + prefs_file_doc => $y, }; } @@ -6076,13 +6681,13 @@ sub prefs { CPAN->debug("prefs_dir[$CPAN::Config->{prefs_dir}]") if $CPAN::DEBUG; my $prefs = $self->_find_prefs(); if ($prefs) { - for my $x (qw(prefs prefs_file prefs_file_section)) { + for my $x (qw(prefs prefs_file prefs_file_doc)) { $self->{$x} = $prefs->{$x}; } my $bs = sprintf( "%s[%s]", File::Basename::basename($self->{prefs_file}), - $self->{prefs_file_section}, + $self->{prefs_file_doc}, ); my $filler1 = "_" x 22; my $filler2 = int(66 - length($bs))/2; @@ -6522,13 +7127,6 @@ sub test { } } - local $ENV{PERL5LIB} = defined($ENV{PERL5LIB}) - ? $ENV{PERL5LIB} - : ($ENV{PERLLIB} || ""); - - $CPAN::META->set_perl5lib; - local $ENV{MAKEFLAGS}; # protect us from outer make calls - my $system; if ($self->{modulebuild}) { $system = sprintf "%s test", $self->_build_command(); @@ -6547,10 +7145,10 @@ sub test { $ENV{$e} = $env->{$e}; } } - my $expect = $self->prefs->{test}{expect}; - my $can_expect = $CPAN::META->has_inst("Expect"); + my $expect_model = $self->_prefs_with_expect("test"); my $want_expect = 0; - if ( $expect && @$expect ) { + if ( $expect_model && @{$expect_model->{talk}} ) { + my $can_expect = $CPAN::META->has_inst("Expect"); if ($can_expect) { $want_expect = 1; } else { @@ -6560,8 +7158,16 @@ sub test { } my $test_report = CPAN::HandleConfig->prefs_lookup($self, q{test_report}); - my $can_report = $CPAN::META->has_inst("CPAN::Reporter"); - my $want_report = $test_report && $can_report; + my $want_report; + if ($test_report) { + my $can_report = $CPAN::META->has_inst("CPAN::Reporter"); + if ($can_report) { + $want_report = 1; + } else { + $CPAN::Frontend->mywarn->("CPAN::Reporter not installed, falling back to ". + "testing without\n"); + } + } my $ready_to_report = $want_report; if ($ready_to_report && ( @@ -6571,7 +7177,7 @@ sub test { ) ) { $CPAN::Frontend->mywarn("Reporting via CPAN::Reporter is disabled ". - "for for local directories\n"); + "for local directories\n"); $ready_to_report = 0; } if ($ready_to_report @@ -6592,12 +7198,13 @@ sub test { "not supported when distroprefs specify ". "an interactive test\n"); } - $tests_ok = $self->_run_via_expect($system,$expect) == 0; + $tests_ok = $self->_run_via_expect($system,$expect_model) == 0; } elsif ( $ready_to_report ) { $tests_ok = CPAN::Reporter::test($self, $system); } else { $tests_ok = system($system) == 0; } + $self->introduce_myself; if ( $tests_ok ) { { my @prereq; @@ -6620,6 +7227,7 @@ sub test { "$cnt dependencies missing ($which)"; $CPAN::Frontend->mywarn("Tests succeeded but $verb\n"); $self->{make_test} = CPAN::Distrostatus->new("NO $verb"); + $self->store_persistent_state; return; } } @@ -6632,6 +7240,25 @@ sub test { $self->{badtestcnt}++; $CPAN::Frontend->mywarn(" $system -- NOT OK\n"); } + $self->store_persistent_state; +} + +sub _prefs_with_expect { + my($self,$where) = @_; + return unless my $prefs = $self->prefs; + return unless my $where_prefs = $prefs->{$where}; + if ($where_prefs->{expect}) { + return { + mode => "expect", + talk => $where_prefs->{expect}, + }; + } elsif ($where_prefs->{"expect-in-any-order"}) { + return { + mode => "expect-in-any-order", + talk => $where_prefs->{"expect-in-any-order"}, + }; + } + return; } #-> sub CPAN::Distribution::clean ; @@ -6675,7 +7302,9 @@ sub clean { } else { $system = join " ", $self->_make_command(), "clean"; } - if (system($system) == 0) { + my $system_ok = system($system) == 0; + $self->introduce_myself; + if ( $system_ok ) { $CPAN::Frontend->myprint(" $system -- OK\n"); # $self->force; @@ -6706,6 +7335,7 @@ sub clean { # $self->force("make"); # so that this directory won't be used again } + $self->store_persistent_state; } #-> sub CPAN::Distribution::install ; @@ -6837,7 +7467,9 @@ sub install { $makeout .= $_; } $pipe->close; - if ($?==0) { + my $close_ok = $? == 0; + $self->introduce_myself; + if ( $close_ok ) { $CPAN::Frontend->myprint(" $system -- OK\n"); $CPAN::META->is_installed($self->{build_dir}); return $self->{install} = CPAN::Distrostatus->new("YES"); @@ -6867,6 +7499,12 @@ sub install { } } delete $self->{force_update}; + $self->store_persistent_state; +} + +sub introduce_myself { + my($self) = @_; + $CPAN::Frontend->myprint(sprintf(" %s\n",$self->pretty_id)); } #-> sub CPAN::Distribution::dir ; @@ -6892,13 +7530,20 @@ sub _check_binary { $CPAN::Frontend->myprint(qq{ + _check_binary($binary)\n}) if $CPAN::DEBUG; - local *README; - $pid = open README, "which $binary|" - or $CPAN::Frontend->mydie(qq{Could not fork 'which $binary': $!}); - while (<README>) { - $out .= $_; + if ($CPAN::META->has_inst("File::Which")) { + return File::Which::which($binary); + } else { + local *README; + $pid = open README, "which $binary|" + or $CPAN::Frontend->mywarn(qq{Could not fork 'which $binary': $!\n}); + return unless $pid; + while (<README>) { + $out .= $_; + } + close README + or $CPAN::Frontend->mywarn("Could not run 'which $binary': $!\n") + and return; } - close README or die "Could not run 'which $binary': $!"; $CPAN::Frontend->myprint(qq{ + $out \n}) if $CPAN::DEBUG && $out; @@ -8209,6 +8854,19 @@ a list of all modules that are both available from CPAN and currently installed within @INC. The name of the bundle file is based on the current date and a counter. +=head2 hosts + +This commands provides a statistical overview over recent download +activities. The data for this is collected in the YAML file +C<FTPstats.yml> in your C<cpan_home> directory. If no YAML module is +configured or YAML not installed, then no stats are provided. + +=head2 mkmyconfig + +mkmyconfig() writes your own CPAN::MyConfig file into your ~/.cpan/ +directory so that you can save your own preferences instead of the +system wide ones. + =head2 recompile recompile() is a very special command in that it takes no argument and @@ -8241,12 +8899,6 @@ The C<upgrade> command first runs an C<r> command with the given arguments and then installs the newest versions of all modules that were listed by that. -=head2 mkmyconfig - -mkmyconfig() writes your own CPAN::MyConfig file into your ~/.cpan/ -directory so that you can save your own preferences instead of the -system wide ones. - =head2 The four C<CPAN::*> Classes: Author, Bundle, Module, Distribution Although it may be considered internal, the class hierarchy does matter @@ -8313,7 +8965,14 @@ module or not. If you do not enter the shell, the available shell commands are both available as methods (C<CPAN::Shell-E<gt>install(...)>) and as -functions in the calling package (C<install(...)>). +functions in the calling package (C<install(...)>). Before calling low-level +commands it makes sense to initialize components of CPAN you need, e.g.: + + CPAN::HandleConfig->load; + CPAN::Shell::setup_output; + CPAN::Index->reload; + +High-level commands do such initializations automatically. There's currently only one class that has a stable interface - CPAN::Shell. All commands that are available in the CPAN shell are @@ -8987,18 +9646,95 @@ The default values defined in the CPAN/Config.pm file can be overridden in a user specific file: CPAN/MyConfig.pm. Such a file is best placed in $HOME/.cpan/CPAN/MyConfig.pm, because $HOME/.cpan is added to the search path of the CPAN module before the use() or -require() statements. +require() statements. The mkmyconfig command writes this file for you. + +The C<o conf> command has various bells and whistles: + +=over + +=item completion support + +If you have a ReadLine module installed, you can hit TAB at any point +of the commandline and C<o conf> will offer you completion for the +built-in subcommands and/or config variable names. + +=item displaying some help: o conf help + +Displays a short help + +=item displaying current values: o conf [KEY] + +Displays the current value(s) for this config variable. Without KEY +displays all subcommands and config variables. + +Example: + + o conf shell + +=item changing of scalar values: o conf KEY VALUE + +Sets the config variable KEY to VALUE. The empty string can be +specified as usual in shells, with C<''> or C<""> + +Example: + + o conf wget /usr/bin/wget + +=item changing of list values: o conf KEY SHIFT|UNSHIFT|PUSH|POP|SPLICE|LIST + +If a config variable name ends with C<list>, it is a list. C<o conf +KEY shift> removes the first element of the list, C<o conf KEY pop> +removes the last element of the list. C<o conf KEYS unshift LIST> +prepends a list of values to the list, C<o conf KEYS push LIST> +appends a list of valued to the list. + +Likewise, C<o conf KEY splice LIST> passes the LIST to the according +splice command. + +Finally, any other list of arguments is taken as a new list value for +the KEY variable discarding the previous value. + +Examples: + + o conf urllist unshift http://cpan.dev.local/CPAN + o conf urllist splice 3 1 + o conf urllist http://cpan1.local http://cpan2.local ftp://ftp.perl.org + +=item interactive editing: o conf init [MATCH|LIST] + +Runs an interactive configuration dialog for matching variables. +Without argument runs the dialog over all supported config variables. +To specify a MATCH the argument must be enclosed by slashes. + +Examples: + + o conf init ftp_passive ftp_proxy + o conf init /color/ + +=item reverting to saved: o conf defaults + +Reverts all config variables to the state in the saved config file. + +=item saving the config: o conf commit + +Saves all config variables to the current config file (CPAN/Config.pm +or CPAN/MyConfig.pm that was loaded at start). + +=back The configuration dialog can be started any time later again by issuing the command C< o conf init > in the CPAN shell. A subset of the configuration dialog can be run by issuing C<o conf init WORD> where WORD is any valid config variable or a regular expression. +=head2 Config Variables + Currently the following keys in the hash reference $CPAN::Config are defined: build_cache size of cache for directories to build modules build_dir locally accessible directory to build modules + build_dir_reuse boolean if distros in build_dir are persistent build_requires_install_policy to install or not to install: when a module is only needed for building. yes|no|ask/yes|ask/no @@ -9066,6 +9802,7 @@ defined: prefs_dir local directory to store per-distro build options proxy_user username for accessing an authenticating proxy proxy_pass password for accessing an authenticating proxy + randomize_urllist add some randomness to the sequence of the urllist scan_cache controls scanning of cache ('atstart' or 'never') shell your favorite shell show_upload_date boolean if commands should try to determine upload date @@ -9136,11 +9873,11 @@ Calls the external command cwd. =back -=head2 Note on urllist parameter's format +=head2 Note on the format of the urllist parameter urllist parameters are URLs according to RFC 1738. We do a little guessing if your URL is not compliant, but if you have problems with -file URLs, please try the correct format. Either: +C<file> URLs, please try the correct format. Either: file://localhost/whatever/ftp/pub/CPAN/ @@ -9171,6 +9908,18 @@ site will be tried another time. This means that if you want to disallow a site for the next transfer, it must be explicitly removed from urllist. +=head2 Maintaining the urllist parameter + +If you have YAML.pm (or some other YAML module configured in +C<yaml_module>) installed, CPAN.pm collects a few statistical data +about recent downloads. You can view the statistics with the C<hosts> +command or inspect them directly by looking into the C<FTPstats.yml> +file in your C<cpan_home> directory. + +To get some interesting statistics it is recommended to set the +C<randomize_urllist> parameter that introduces some amount of +randomness into the URL selection. + =head2 prefs_dir for avoiding interactive questions (ALPHA) (B<Note:> This feature has been introduced in CPAN.pm 1.8854 and is @@ -9589,13 +10338,26 @@ nice about obeying that variable as well): =item 14) -How do I create a Module::Build based Build.PL derived from an +How do I create a Module::Build based Build.PL derived from an ExtUtils::MakeMaker focused Makefile.PL? http://search.cpan.org/search?query=Module::Build::Convert http://accognoscere.org/papers/perl-module-build-convert/module-build-convert.html +=item 15) + +What's the best CPAN site for me? + +The urllist config parameter is yours. You can add and remove sites at +will. You should find out which sites have the best uptodateness, +bandwidth, reliability, etc. and are topologically close to you. Some +people prefer fast downloads, others uptodateness, others reliability. +You decide which to try in which order. + +Henk P. Penning maintains a site that collects data about CPAN sites: + + http://www.cs.uu.nl/people/henkp/mirmon/cpan.html =back diff --git a/lib/CPAN/FirstTime.pm b/lib/CPAN/FirstTime.pm index 256980a..7248958 100644 --- a/lib/CPAN/FirstTime.pm +++ b/lib/CPAN/FirstTime.pm @@ -19,7 +19,7 @@ use File::Basename (); use File::Path (); use File::Spec (); use vars qw($VERSION $urllist); -$VERSION = sprintf "%.6f", substr(q$Rev: 1086 $,4)/1000000 + 5.4; +$VERSION = sprintf "%.6f", substr(q$Rev: 1257 $,4)/1000000 + 5.4; =head1 NAME @@ -62,7 +62,7 @@ sub init { # case WORD... => all arguments must be valid for my $arg (@{$args{args}}) { unless (exists $CPAN::HandleConfig::keys{$arg}) { - $CPAN::Frontend->mywarn("'$arg' is not a valid configuration variable"); + $CPAN::Frontend->mywarn("'$arg' is not a valid configuration variable\n"); return; } } @@ -116,7 +116,7 @@ sub init { my $current_second = time; my $current_second_count = 0; my $i_am_mad = 0; - *_real_prompt = sub ($;$) { + *_real_prompt = sub { my($q,$a) = @_; my($ret) = defined $a ? $a : ""; $CPAN::Frontend->myprint(sprintf qq{%s [%s]\n\n}, $q, $ret); @@ -144,7 +144,13 @@ sub init { } } - if (!$matcher or 'cpan_home keep_source_where build_dir prefs_dir' =~ /$matcher/){ + if (!$matcher or q{ + build_dir + build_dir_reuse + cpan_home + keep_source_where + prefs_dir + } =~ /$matcher/){ $CPAN::Frontend->myprint($prompts{config_intro}); if (!$matcher or 'cpan_home' =~ /$matcher/) { @@ -166,8 +172,21 @@ Shall we use it as the general CPAN build and cache directory? $default = $cpan_home; my $loop = 0; - while ($ans = prompt("CPAN build and cache directory?",$default)) { - unless (File::Spec->file_name_is_absolute($ans)) { + my $last_ans; + PROMPT: while ($ans = prompt("CPAN build and cache directory?",$default)) { + if (File::Spec->file_name_is_absolute($ans)) { + my @cpan_home = split /[\/\\]/, $ans; + DIR: for my $dir (@cpan_home) { + if ($dir =~ /^~/ and (!$last_ans or $ans ne $last_ans)) { + $CPAN::Frontend + ->mywarn("Warning: a tilde in the path will be ". + "taken as a literal tilde. Please ". + "confirm again if you want to keep it\n"); + $last_ans = $default = $ans; + next PROMPT; + } + } + } else { require Cwd; my $cwd = Cwd::cwd(); my $absans = File::Spec->catdir($cwd,$ans); @@ -210,6 +229,10 @@ Shall we use it as the general CPAN build and cache directory? ); } + if (!$matcher or 'build_dir_reuse' =~ /$matcher/) { + my_yn_prompt(build_dir_reuse => "y", $matcher); + } + if (!$matcher or 'prefs_dir' =~ /$matcher/) { my_dflt_prompt("prefs_dir", File::Spec->catdir($CPAN::Config->{cpan_home},"prefs"), @@ -558,6 +581,9 @@ Shall we use it as the general CPAN build and cache directory? *_real_prompt = \&CPAN::Shell::colorable_makemaker_prompt; conf_sites(); } + if ("randomize_urllist" =~ $matcher) { + my_dflt_prompt(randomize_urllist => 0, $matcher); + } } elsif ($fastread) { $CPAN::Frontend->myprint("Autoconfigured everything but 'urllist'.\n". "Please call 'o conf init urllist' to configure ". @@ -983,6 +1009,27 @@ build_dir => "Directory where the build process takes place?", +build_dir_reuse_intro => + +qq{Until version 1.88 CPAN.pm never trusted the contents of the +build_dir directory between sessions. Since 1.88_58 CPAN.pm has a +YAML-based mechanism that makes it possible to share the contents of +the build_dir/ directory between different sessions with the same +version of perl. People who prefer to test things several days before +installing will like this feature because it safes a lot of time. + +If you say yes to the following question, CPAN will try to store +enough information about the build process so that it can pick up in +future sessions at the same state of affairs as it left a previous +session. + +}, + +build_dir_reuse => + +qq{Store and re-use state information about distributions between +CPAN.pm sessions?}, + prefs_dir_intro => qq{ CPAN.pm can store customized build environments based on regular @@ -1396,6 +1443,20 @@ player, YAML::Tiny, is not yet sufficiently similar to the other two. yaml_module => qq{Which YAML implementation would you prefer?}, +randomize_urllist_intro => qq{ + +CPAN.pm can introduce some randomness when using hosts for download +that are configured in the urllist parameter. Enter a numeric value +between 0 and 1 to indicate how often you want to let CPAN.pm try a +random host from the urllist. A value of one specifies to always use a +random host as the first try. A value of zero means no randomness at +all. Anything in between specifies how often, on average, a random +host should be tried first. + +}, + +randomize_urllist => "Randomize parameter", + ); die "Coding error in \@prompts declaration. Odd number of elements, above" diff --git a/lib/CPAN/HandleConfig.pm b/lib/CPAN/HandleConfig.pm index a51f557..4f4b5a3 100644 --- a/lib/CPAN/HandleConfig.pm +++ b/lib/CPAN/HandleConfig.pm @@ -2,7 +2,7 @@ package CPAN::HandleConfig; use strict; use vars qw(%can %keys $VERSION); -$VERSION = sprintf "%.6f", substr(q$Rev: 1128 $,4)/1000000 + 5.4; +$VERSION = sprintf "%.6f", substr(q$Rev: 1264 $,4)/1000000 + 5.4; %can = ( commit => "Commit changes to disk", @@ -18,6 +18,7 @@ $VERSION = sprintf "%.6f", substr(q$Rev: 1128 $,4)/1000000 + 5.4; ( "build_cache", "build_dir", + "build_dir_reuse", "build_requires_install_policy", "bzip2", "cache_metadata", @@ -65,6 +66,7 @@ $VERSION = sprintf "%.6f", substr(q$Rev: 1128 $,4)/1000000 + 5.4; "prefs_dir", "proxy_pass", "proxy_user", + "randomize_urllist", "scan_cache", "shell", "show_upload_date", @@ -120,10 +122,12 @@ sub edit { unless (exists $keys{$o}) { $CPAN::Frontend->mywarn("Warning: unknown configuration variable '$o'\n"); } - if ($o =~ /list$/) { + # one day I used randomize_urllist for a boolean, so we must + # list them explicitly --ak + if ($o =~ /^(wait_list|urllist|dontload_list)$/) { $func = shift @args; $func ||= ""; - CPAN->debug("func[$func]") if $CPAN::DEBUG; + CPAN->debug("func[$func]args[@args]") if $CPAN::DEBUG; my $changed; # Let's avoid eval, it's easier to comprehend without. if ($func eq "push") { @@ -139,10 +143,12 @@ sub edit { unshift @{$CPAN::Config->{$o}}, @args; $changed = 1; } elsif ($func eq "splice") { - splice @{$CPAN::Config->{$o}}, @args; + my $offset = shift @args || 0; + my $length = shift @args || 0; + splice @{$CPAN::Config->{$o}}, $offset, $length, @args; # may warn $changed = 1; - } elsif (@args) { - $CPAN::Config->{$o} = [@args]; + } elsif ($func) { + $CPAN::Config->{$o} = [$func, @args]; $changed = 1; } else { $self->prettyprint($o); @@ -185,7 +191,7 @@ sub prettyprint { if (ref $v) { my(@report); if (ref $v eq "ARRAY") { - @report = map {"\t[$_]\n"} @$v; + @report = map {"\t$_ \[$v->[$_]]\n"} 0..$#$v; } else { @report = map { sprintf("\t%-18s => %s\n", map { "[$_]" } $_, @@ -212,6 +218,13 @@ sub prettyprint { sub commit { my($self,@args) = @_; CPAN->debug("args[@args]") if $CPAN::DEBUG; + if ($CPAN::RUN_DEGRADED) { + $CPAN::Frontend->mydie( + "'o conf commit' disabled in ". + "degraded mode. Maybe try\n". + " !undef \$CPAN::RUN_DEGRADED\n" + ); + } my $configpm; if (@args) { if ($args[0] eq "args") { @@ -308,6 +321,13 @@ sub neatvalue { sub defaults { my($self) = @_; + if ($CPAN::RUN_DEGRADED) { + $CPAN::Frontend->mydie( + "'o conf defaults' disabled in ". + "degraded mode. Maybe try\n". + " !undef \$CPAN::RUN_DEGRADED\n" + ); + } my $done; for my $config (qw(CPAN/MyConfig.pm CPAN/Config.pm)) { if ($INC{$config}) { @@ -624,7 +644,7 @@ sub prefs_lookup { use strict; use vars qw($AUTOLOAD $VERSION); - $VERSION = sprintf "%.2f", substr(q$Rev: 1128 $,4)/100; + $VERSION = sprintf "%.2f", substr(q$Rev: 1264 $,4)/100; # formerly CPAN::HandleConfig was known as CPAN::Config sub AUTOLOAD { diff --git a/lib/CPAN/SIGNATURE b/lib/CPAN/SIGNATURE index 26dbe81..76e6346 100644 --- a/lib/CPAN/SIGNATURE +++ b/lib/CPAN/SIGNATURE @@ -14,70 +14,84 @@ not run its Makefile.PL or Build.PL. -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 -SHA1 50c2f08483563a4ecf5ce52d4dc6e1170344649b ChangeLog +SHA1 da39a3ee5e6b4b0d3255bfef95601890afd80709 ChangeLog SHA1 9b97524a7a91c815e46b19302a33829d3c26bbbf ChangeLog.old -SHA1 bffe9c0880ee43829f4d9ccc89385e93b765046d Changes +SHA1 92f23a7b43b9e75436a475ce818b70ac8daf2cb3 Changes SHA1 a029ffa2f2252bb8914eb658666244710994d256 Changes.old -SHA1 cbbb5e95810bbd22148c1de7960750d04818a49f MANIFEST -SHA1 b71a24cdb69d391a6fb1aae4c684b08387319dc4 MANIFEST.SKIP -SHA1 18e6488a210c111206817ef1c2da20f993536b18 META.yml -SHA1 d96dd2c08f9f1eb9211a1f2e4dc77ab64e770e9c Makefile.PL +SHA1 15abd23a01cd74718afa3334034095eca0971633 MANIFEST +SHA1 059205e949903fc21d5a8285453d856356316b87 MANIFEST.SKIP +SHA1 38ffd77bfd2373ed38f6075a309a0485ec80e419 META.yml +SHA1 55ef1515ca05499ddf1d8b51f4982114643a8bd4 Makefile.PL SHA1 37e858c51409a297ef5d3fb35dc57cd3b57f9a4d PAUSE2003.pub SHA1 af016003ad503ed078c5f8254521d13a3e0c494f PAUSE2005.pub -SHA1 cf9e7e46c1e5edde1b3918f41712d4bd7759ea97 README -SHA1 f14b1344edbb2a66d7d42f367e4318295d983894 Todo -SHA1 932277e2bfe93e15a50bb0beb84b1d38aff7c032 distroprefs/ABW.Template-Toolkit.01.yml -SHA1 43d244a0dce7dc14d41b9b97f156291e0250aac1 distroprefs/AUDREYT.Module-Signature.01.yml -SHA1 b9213e79239022a0f55ddc43d36b299f5a0a2b26 distroprefs/BTROTT.Convert-PEM.01.yml -SHA1 9ab8285183aa556cb89941708a20b4182644ff1f distroprefs/BTROTT.Crypt-DSA.01.yml -SHA1 170d7a16d7cdac6524dc685fe2abcfacfa5ad1bd distroprefs/BTROTT.Feed-Find.01.yml -SHA1 3ca4097da44acb77bd726249ddbfd0cdfbe8658e distroprefs/BTROTT.URI-Fetch-0.01.yml -SHA1 42e46487c9bc79e5503060ec69996aaa963de81c distroprefs/BTROTT.XML-Feed.01.yml -SHA1 c767fe6635991af2219f84121d7b18ad63311d64 distroprefs/BYRNE.SOAP-Lite.01.yml -SHA1 a7913ce41d3101ef07210f78610ffc14c4533f19 distroprefs/CHAMAS.Crypt-SSLeay.01.yml -SHA1 3a4da73826cad9375547ba49283abda51b74a62f distroprefs/DBD-mysql.yml -SHA1 fce1b0543af844fde7d303cac4bd3ddf05caab20 distroprefs/DBROBINS.Net-SSH-Perl.01.yml -SHA1 e14a98da8bee5f37c2692f8bf7b609844c466979 distroprefs/EESTABROO.IMAP-Admin.01.yml -SHA1 1a696ce324ed86d2cc582cc296ffe3b941f57231 distroprefs/FDALY.Test-Deep.01.yml -SHA1 56b5beca3c7eb33d7976a409daa44ec2106eee60 distroprefs/GAAS.libwww.01.yml -SHA1 b182afae2709fbd424678f59046c7bc4a7e59623 distroprefs/GBARR.Authen-SASL.01.yml -SHA1 29e0a5387bae0580c2bddbfe0623c19449a5ea1d distroprefs/GBARR.perl-ldap.01.yml -SHA1 ab26131aac57b547c5fb83838f02df5a905797e4 distroprefs/GEOFF.Apache-Test.01.yml -SHA1 e911c1acfe28ae5e93d034e95da70756de28e9f0 distroprefs/ILYAZ.Math-Pari.01.yml -SHA1 95d003e0db14776761c1c97935d22efc00d83b3e distroprefs/ILYAZ.Term-ReadLine-Perl.01.yml -SHA1 437aa0353e49ee2af4f32f95ac18a7e0757851eb distroprefs/INGY.Inline.01.yml -SHA1 212fecda3240d87886cc0d05a803a1b04dd82365 distroprefs/INGY.YAML.01.yml -SHA1 a8c60f0f9c9f14af29a1e70e9046fce2040b9afb distroprefs/JJORE.Carp-Clan.01.yml -SHA1 4a2eb79f201a0e4ada6ff6dcabcf3f7c47b5d244 distroprefs/Jifty.yml -SHA1 7a31b880a7dceabc3b1664a67900843970041b85 distroprefs/KWILLIAMS.AI-Categorizer.01.yml +SHA1 54c0c4131e1c90c011766679402c1173ce63554b README +SHA1 de42df72474c079f9483092d1996bdd311dd5b08 Todo +SHA1 b7fb1ec2ac8d5e277374b609805b9acb06c4fa83 distroprefs/00.README +SHA1 d42035258f9a0c2f6ed4176f01f7449ff012c673 distroprefs/ABW.Template-Toolkit.yml +SHA1 5bb6703dbc7b00b39f999a2c0e2bb16a331febdb distroprefs/ANDK.CPAN-Test-Dummy-Perl5-Build-Fails.yml +SHA1 2d70060c58ce4c97d94d5b04ff38e7cf8f42fb08 distroprefs/ANDK.CPAN-Test-Dummy-Perl5-Make-Expect.yml +SHA1 dd1623cba2eaef34189f4b2893038ed78974b579 distroprefs/AUDREYT.Module-Install.yml +SHA1 43d244a0dce7dc14d41b9b97f156291e0250aac1 distroprefs/AUDREYT.Module-Signature.yml +SHA1 f070d5f16e1fbac9af26ee20a0ffe1b7d1fec692 distroprefs/AUDREYT.PDF-FromHTML.yml +SHA1 b4435f7aa20bdda7dd68f5fdae7939a06d89dd05 distroprefs/AUDREYT.YAML-Syck.yml +SHA1 b9213e79239022a0f55ddc43d36b299f5a0a2b26 distroprefs/BTROTT.Convert-PEM.yml +SHA1 9ab8285183aa556cb89941708a20b4182644ff1f distroprefs/BTROTT.Crypt-DSA.yml +SHA1 170d7a16d7cdac6524dc685fe2abcfacfa5ad1bd distroprefs/BTROTT.Feed-Find.yml +SHA1 3ca4097da44acb77bd726249ddbfd0cdfbe8658e distroprefs/BTROTT.URI-Fetch.yml +SHA1 42e46487c9bc79e5503060ec69996aaa963de81c distroprefs/BTROTT.XML-Feed.yml +SHA1 ea747c753d2660eb9fa00ae2a33cb555f4a77f55 distroprefs/BYRNE.SOAP-Lite.yml +SHA1 3a4da73826cad9375547ba49283abda51b74a62f distroprefs/CAPTTOFU.DBD-mysql.yml +SHA1 e15b26ccb5580a226e8dad1ce33894406603aef3 distroprefs/CHAMAS.Crypt-SSLeay.yml +SHA1 d266170b288cb07f586448eb4b5278504c1ba0e2 distroprefs/CLKAO.SVN-Mirror.yml +SHA1 fce1b0543af844fde7d303cac4bd3ddf05caab20 distroprefs/DBROBINS.Net-SSH-Perl.yml +SHA1 480720e8c9068b1437ce64cb7e69d398a2e76666 distroprefs/DCONWAY.Parse-RecDescent.yml +SHA1 47bf06fd295ae32fcd87fb78bb92adeeaeaa2341 distroprefs/EESTABROO.IMAP-Admin.yml +SHA1 1a696ce324ed86d2cc582cc296ffe3b941f57231 distroprefs/FDALY.Test-Deep.yml +SHA1 d4a0be24cd4363c96eb546ee43c67fe8f267215a distroprefs/GAAS.libwww.yml +SHA1 b182afae2709fbd424678f59046c7bc4a7e59623 distroprefs/GBARR.Authen-SASL.yml +SHA1 ff6eb4e4887f137049aff6d8e338cae8c8652a37 distroprefs/GBARR.libnet.yml +SHA1 d27f83106d5b5f0274c0640b3ffe10177929667d distroprefs/GBARR.perl-ldap.yml +SHA1 074e251c7a5c3ed2e094153b615e7910ea4ef865 distroprefs/GEOFF.Apache-Test.yml +SHA1 d452b5b97464722eac9d0b7f7f2c295f84f623d8 distroprefs/ILYAZ.Math-Pari.yml +SHA1 b990fbd9e67d6249a92eb73a0b78d4e26d5f363d distroprefs/ILYAZ.Term-ReadLine-Perl.yml +SHA1 98fa048b94ecff379d22fa5cb134a9fb2d4111a6 distroprefs/INGY.Inline.yml +SHA1 212fecda3240d87886cc0d05a803a1b04dd82365 distroprefs/INGY.YAML.yml +SHA1 08bb2709bf24a070983b0c975c56b283e199519a distroprefs/JCRISTY.PerlMagick.yml +SHA1 d4dc64268f79a0b9d87fdade9ba2b6ca04d6cbba distroprefs/JESSE.Jifty.yml +SHA1 a8c60f0f9c9f14af29a1e70e9046fce2040b9afb distroprefs/JJORE.Carp-Clan.yml +SHA1 ebbb0110e897b4837ca038c1dfe7b81ebd8d4f6f distroprefs/JPEACOCK.SVN-Notify-Mirror.yml +SHA1 4aeb3d2f054d46ddda79037479cd4fef15e2e040 distroprefs/KASEI.Class-Accessor.yml +SHA1 633cdf46c0f756288284ab7a769e28c72f4c59ee distroprefs/KBROWN.SOAP.yml +SHA1 7a31b880a7dceabc3b1664a67900843970041b85 distroprefs/KWILLIAMS.AI-Categorizer.yml +SHA1 d02c0a6f47b1a61e7cb28f6ae7c5aa3b3af84785 distroprefs/KWILLIAMS.Module-Build.yml +SHA1 e9bd2016d5029e7c5359569bddb9946f2f171de9 distroprefs/LEAKIN.File-Rsync.yml SHA1 45ed7e3908cecea98939e6c3f30e72377bb1a035 distroprefs/LOCAL.trailing_dot_distros.yml +SHA1 0b35e9f0c9c9397a8e3fe0f58196ea1b8db21208 distroprefs/MARKOV.MailTools.yml +SHA1 713aefbcda0e1c0f2d6c20f347e0c2eb046f2222 distroprefs/MAURICE.IPC-ShareLite.yml +SHA1 ce39e72eb1d10d968193d7eb3aba216f6bf5a254 distroprefs/MIROD.XML-Twig.yml SHA1 098c0b2c697f389f4debd65cb9e5676c94cbcc5c distroprefs/MIYAGAWA.Plagger.yml SHA1 8b24d26e239fc2dc5e8cd40158f72806b7b6b679 distroprefs/MIYAGAWA.XML-Atom.yml -SHA1 223423e181aad486357599da47ee7dde05cc7b12 distroprefs/MLEHMANN.Coro.yml -SHA1 dd1623cba2eaef34189f4b2893038ed78974b579 distroprefs/Module-Install.yml -SHA1 6e678a41dd8e0f7b1801cea22a316d54e2f386d4 distroprefs/NIKIP.Authen-PAM.01.yml -SHA1 1611017f8fb4e16aeefee6b54c65185bb5640844 distroprefs/OLAF.Net-DNS.01.yml -SHA1 d20084e5fe455b21f3d2fa9fb961df990277ee19 distroprefs/PCIMPRICH.XML-SAX-ExpatXS.01.yml -SHA1 f070d5f16e1fbac9af26ee20a0ffe1b7d1fec692 distroprefs/PDF-FromHTML.yml -SHA1 90858ffc05805be1072966981a8fa313f3418edc distroprefs/PETDANCE.WWW-Mechanize.01.yml -SHA1 82618c54cd5913d77898c0ef8d9e5be43f6942bc distroprefs/RCAPUTO.POE.01.yml -SHA1 fda2bcfb03e39300260ca45d491ff3c786bba819 distroprefs/README +SHA1 719bbf2f564e5eaec889907f1292326949b708bd distroprefs/MLEHMANN.Coro.yml +SHA1 6e678a41dd8e0f7b1801cea22a316d54e2f386d4 distroprefs/NIKIP.Authen-PAM.yml +SHA1 1611017f8fb4e16aeefee6b54c65185bb5640844 distroprefs/OLAF.Net-DNS.yml +SHA1 d20084e5fe455b21f3d2fa9fb961df990277ee19 distroprefs/PCIMPRICH.XML-SAX-ExpatXS.yml +SHA1 bf0a0dee633342002ddded0bd367c1dc47d3f0cc distroprefs/PETDANCE.WWW-Mechanize.yml +SHA1 592d3a8ef549954f9338ea40c2a6c710c796ee09 distroprefs/RCAPUTO.POE.yml SHA1 4aec360d2b5ea2172b47892135327ade1565cb13 distroprefs/RGIERSIG.Expect.yml +SHA1 faff33ebc17d0bf928933f25a3f8d94ced7f9686 distroprefs/RJBS.Data-UUID.yml SHA1 0219d681658dcab42a21c089658c74b7d5cb6e2b distroprefs/SCHWERN.Exporter-Lite.yml SHA1 1355fbad3086a5f8d969c70754bc9e31e029a276 distroprefs/SCHWERN.UNIVERSAL-require.yml -SHA1 611ccc9663349408320f477737070c4c7f504770 distroprefs/SREZIC.Tk-Autoscroll.01.yml +SHA1 611ccc9663349408320f477737070c4c7f504770 distroprefs/SREZIC.Tk-Autoscroll.yml SHA1 f3f9f97e46f9581a534fde5cd1bbd8bab1ab0290 distroprefs/TIMB.DBI.yml -SHA1 5bb6703dbc7b00b39f999a2c0e2bb16a331febdb distroprefs/Test-CTDPBF.yml -SHA1 87a1dec7aaff62d81709a11b85aba6c0ed1df63e distroprefs/URI.File-Slurp.yml -SHA1 ce39e72eb1d10d968193d7eb3aba216f6bf5a254 distroprefs/XML-Twig.yml -SHA1 3b10aeaedfa9d8ddcf0faa0818b9e7db7d4b70b8 distroprefs/ZEV.Test-Dependencies.01.yml +SHA1 4266028c2eb988c0e6b483e65bf6bf21df5c20fb distroprefs/URI.File-Slurp.yml +SHA1 3b10aeaedfa9d8ddcf0faa0818b9e7db7d4b70b8 distroprefs/ZEV.Test-Dependencies.yml SHA1 efbe8e6882a2caa0d741b113959a706830ab5882 inc/Test/Builder.pm SHA1 ae1d68262bedc2475e2c6fd478d99b259b4fb109 inc/Test/More.pm -SHA1 19799f32e70bdd961536fd7b7b8bf294158e10d3 lib/CPAN.pm +SHA1 d8faef0bb0d1de3032a8a2b80d907888605ce4d2 lib/CPAN.pm SHA1 e093af1fcd72420fe4bdc85a5bec2b92a301ab97 lib/CPAN/Admin.pm SHA1 aa9e4d9384c88c55f9f457e2c2123242d7989406 lib/CPAN/Debug.pm -SHA1 b0461d7d86faed00d1b063baedaa168dd0109459 lib/CPAN/FirstTime.pm -SHA1 110af59d79e04f4c9353c8c8b1bc539c0b17bd65 lib/CPAN/HandleConfig.pm +SHA1 5346c722756773bbb3226d86f668f1e32a40a486 lib/CPAN/FirstTime.pm +SHA1 52bfa1430eaed8c8b34d95c7b07d8d71f75d6d3b lib/CPAN/HandleConfig.pm SHA1 17a1ad839531642ace9bf198bf52964c252f3318 lib/CPAN/Nox.pm SHA1 caeaa439558a19b64119321aa9105980928862f5 lib/CPAN/Queue.pm SHA1 fc6de4175a275a4c6791091f2ffcee2636a4a0f2 lib/CPAN/Tarzip.pm @@ -91,17 +105,17 @@ SHA1 18368a653b17c7166f43686f8e315fd5e88bbcfa t/10version.t SHA1 325d8a2f72d59c4cd2400c72403c05cd614c3abc t/11mirroredby.t SHA1 281dfbbd397314ae740c7cf362218da675960b75 t/12cpan.t SHA1 fa075e989a5923e73684d13d5e94baa0711bb360 t/30shell.coverage -SHA1 e71f6ec4ddbdab2362c4ed85a00c82a4a54b42c9 t/30shell.t +SHA1 c1cd8442f7968f96197f78af62b021cb88af8ec8 t/30shell.t SHA1 6a79f15a10337bd3450604abf39d4462df2a550b t/50pod.t -SHA1 6c194eb30cce245737fe5e1a35118ed78abae0d1 t/51pod.t -SHA1 c98f4c2aa680bb0e88569f6ab4a9ca4e8deb5c1e t/52podcover.t +SHA1 5d5d4a23a6fc5238d05aefc62410dcea7a7e8cd8 t/51pod.t +SHA1 6451b15b99462cea12120036ffce3c6c322bd489 t/52podcover.t SHA1 413dd29cf8968e69292a2d652e0e0496a8137a01 t/60credentials.t SHA1 7efe930efd0a07d8101679ed15d4700dcf208137 t/CPAN/CpanTestDummies-1.55.pm SHA1 310b5562df76ff28ab05d741e144d84fb5b5369b t/CPAN/TestConfig.pm SHA1 081ed556ae14a75c43ca31e67cfc99d180c9ef41 t/CPAN/TestMirroredBy SHA1 455480f7053abe4ac853a4c456d52b83e8b922e8 t/CPAN/TestPatch.txt SHA1 b4fd27234696da334ac6a1716222c70610a98c3a t/CPAN/authors/01mailrc.txt -SHA1 c00fc12776a5cc317828630610a6e8e5d977be04 t/CPAN/authors/id/A/AN/ANDK/CHECKSUMS +SHA1 fe133b45e54951f7fb413d89556e34947bd989fb t/CPAN/authors/id/A/AN/ANDK/CHECKSUMS SHA1 d1a101f24d2d0719c9991df28ede729d58005bb4 t/CPAN/authors/id/A/AN/ANDK/CHECKSUMS.2nd SHA1 34cf1bf9c95007fe02a4b4f4977eb017516b0cdc t/CPAN/authors/id/A/AN/ANDK/CPAN-Test-Dummy-Perl5-Build-1.03.tar.gz SHA1 3f66b598a79d5b120205715e86a5eed19251cd13 t/CPAN/authors/id/A/AN/ANDK/CPAN-Test-Dummy-Perl5-Build-DepeFails-1.02.tar.gz @@ -111,18 +125,20 @@ SHA1 7378a536ffa854a49a4fd6082a8d9f924be23d8d t/CPAN/authors/id/A/AN/ANDK/CPAN-T SHA1 f82f789dfdaa4cf3f34fad2d0f8c97f0f0bd9941 t/CPAN/authors/id/A/AN/ANDK/CPAN-Test-Dummy-Perl5-Make-CircDepeOne-1.00.tar.gz SHA1 faf5f6c6218c8d862a2e807538a468049cb2263f t/CPAN/authors/id/A/AN/ANDK/CPAN-Test-Dummy-Perl5-Make-CircDepeThree-1.00.tar.gz SHA1 ffffd32d5e63075ce47527d5af689e76f7421b13 t/CPAN/authors/id/A/AN/ANDK/CPAN-Test-Dummy-Perl5-Make-CircDepeTwo-1.00.tar.gz +SHA1 b992aebcd2fa43e3d83113247d2310abf028068f t/CPAN/authors/id/A/AN/ANDK/CPAN-Test-Dummy-Perl5-Make-Expect-1.00.tar.gz SHA1 522f39ed6921d9704b38bd7dd0c3559815f45a68 t/CPAN/authors/id/A/AN/ANDK/CPAN-Test-Dummy-Perl5-Make-Failearly-1.02.tar.gz SHA1 a424441767925cd6eb4db35098a896ac15b42991 t/CPAN/authors/id/A/AN/ANDK/CPAN-Test-Dummy-Perl5-Make-Zip-1.03.zip SHA1 541ac9311d4dbabe9bb99d770b221456798be688 t/CPAN/authors/id/A/AN/ANDK/NotInChecksums-0.000.tar.gz +SHA1 5bc14cda7abdb6306caec36f804dfba54b113e80 t/CPAN/authors/id/A/AN/ANDK/patches/CHECKSUMS SHA1 1aee1bed21f0e9755d693419e810ec75543eb0b7 t/CPAN/authors/id/A/AN/CHECKSUMS SHA1 1f3304f219bf0da4db6a60f638e11b61c2c2f4c0 t/CPAN/authors/id/A/CHECKSUMS SHA1 dfc900f5bfbc9683fa91977a1c7198222fbd4452 t/CPAN/authors/id/CHECKSUMS -SHA1 27b047070286839944a64460d94821f84b9bd735 t/CPAN/modules/02packages.details.txt +SHA1 14d9a66cd707d46ac06b83672aace75ce2bd66cd t/CPAN/modules/02packages.details.txt SHA1 f4c1a524de16347b37df6427ca01f98dd27f3c81 t/CPAN/modules/03modlist.data -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.5 (GNU/Linux) -iD8DBQFFPbzb7IA58KMXwV0RApkOAKC2uwwgcroM6ABYBrM8z20ULMKAPwCgoIB4 -6kKlP0+qbcbfqEXq22QW+mQ= -=Wbtc +iD8DBQFFWCGG7IA58KMXwV0RAilVAJ9kEmzkSPX2jS1MHFK8/iP/YUaoxQCdGP3T +DqNQkblBD/SwzPzZ3zN82Kw= +=lL4b -----END PGP SIGNATURE-----