CPAN.pm beta 1.57_57 for the core
[perl.git] / lib / CPAN / FirstTime.pm
1 package CPAN::Mirrored::By;
2
3 sub new { 
4     my($self,@arg) = @_;
5     bless [@arg], $self;
6 }
7 sub continent { shift->[0] }
8 sub country { shift->[1] }
9 sub url { shift->[2] }
10
11 package CPAN::FirstTime;
12
13 use strict;
14 use ExtUtils::MakeMaker qw(prompt);
15 use FileHandle ();
16 use File::Basename ();
17 use File::Path ();
18 use vars qw($VERSION);
19 $VERSION = substr q$Revision: 1.43 $, 10;
20
21 =head1 NAME
22
23 CPAN::FirstTime - Utility for CPAN::Config file Initialization
24
25 =head1 SYNOPSIS
26
27 CPAN::FirstTime::init()
28
29 =head1 DESCRIPTION
30
31 The init routine asks a few questions and writes a CPAN::Config
32 file. Nothing special.
33
34 =cut
35
36
37 sub init {
38     my($configpm) = @_;
39     use Config;
40     unless ($CPAN::VERSION) {
41         require CPAN::Nox;
42     }
43     eval {require CPAN::Config;};
44     $CPAN::Config ||= {};
45     local($/) = "\n";
46     local($\) = "";
47     local($|) = 1;
48
49     my($ans,$default,$local,$cont,$url,$expected_size);
50
51     #
52     # Files, directories
53     #
54
55     print qq[
56
57 CPAN is the world-wide archive of perl resources. It consists of about
58 100 sites that all replicate the same contents all around the globe.
59 Many countries have at least one CPAN site already. The resources
60 found on CPAN are easily accessible with the CPAN.pm module. If you
61 want to use CPAN.pm, you have to configure it properly.
62
63 If you do not want to enter a dialog now, you can answer 'no' to this
64 question and I\'ll try to autoconfigure. (Note: you can revisit this
65 dialog anytime later by typing 'o conf init' at the cpan prompt.)
66
67 ];
68
69     my $manual_conf =
70         ExtUtils::MakeMaker::prompt("Are you ready for manual configuration?",
71                                     "yes");
72     my $fastread;
73     {
74       local $^W;
75       if ($manual_conf =~ /^\s*y/i) {
76         $fastread = 0;
77         *prompt = \&ExtUtils::MakeMaker::prompt;
78       } else {
79         $fastread = 1;
80         $CPAN::Config->{urllist} ||= [];
81         # prototype should match that of &MakeMaker::prompt
82         *prompt = sub ($;$) {
83           my($q,$a) = @_;
84           my($ret) = defined $a ? $a : "";
85           printf qq{%s [%s]\n\n}, $q, $ret;
86           $ret;
87         };
88       }
89     }
90     print qq{
91
92 The following questions are intended to help you with the
93 configuration. The CPAN module needs a directory of its own to cache
94 important index files and maybe keep a temporary mirror of CPAN files.
95 This may be a site-wide directory or a personal directory.
96
97 };
98
99     my $cpan_home = $CPAN::Config->{cpan_home} || MM->catdir($ENV{HOME}, ".cpan");
100     if (-d $cpan_home) {
101         print qq{
102
103 I see you already have a  directory
104     $cpan_home
105 Shall we use it as the general CPAN build and cache directory?
106
107 };
108     } else {
109         print qq{
110
111 First of all, I\'d like to create this directory. Where?
112
113 };
114     }
115
116     $default = $cpan_home;
117     while ($ans = prompt("CPAN build and cache directory?",$default)) {
118       eval { File::Path::mkpath($ans); }; # dies if it can't
119       if ($@) {
120         warn "Couldn't create directory $ans.
121 Please retry.\n";
122         next;
123       }
124       if (-d $ans && -w _) {
125         last;
126       } else {
127         warn "Couldn't find directory $ans
128   or directory is not writable. Please retry.\n";
129       }
130     }
131     $CPAN::Config->{cpan_home} = $ans;
132
133     print qq{
134
135 If you want, I can keep the source files after a build in the cpan
136 home directory. If you choose so then future builds will take the
137 files from there. If you don\'t want to keep them, answer 0 to the
138 next question.
139
140 };
141
142     $CPAN::Config->{keep_source_where} = MM->catdir($CPAN::Config->{cpan_home},"sources");
143     $CPAN::Config->{build_dir} = MM->catdir($CPAN::Config->{cpan_home},"build");
144
145     #
146     # Cache size, Index expire
147     #
148
149     print qq{
150
151 How big should the disk cache be for keeping the build directories
152 with all the intermediate files\?
153
154 };
155
156     $default = $CPAN::Config->{build_cache} || 10;
157     $ans = prompt("Cache size for build directory (in MB)?", $default);
158     $CPAN::Config->{build_cache} = $ans;
159
160     # XXX This the time when we refetch the index files (in days)
161     $CPAN::Config->{'index_expire'} = 1;
162
163     print qq{
164
165 By default, each time the CPAN module is started, cache scanning
166 is performed to keep the cache size in sync. To prevent from this,
167 disable the cache scanning with 'never'.
168
169 };
170
171     $default = $CPAN::Config->{scan_cache} || 'atstart';
172     do {
173         $ans = prompt("Perform cache scanning (atstart or never)?", $default);
174     } while ($ans ne 'atstart' && $ans ne 'never');
175     $CPAN::Config->{scan_cache} = $ans;
176
177     print qq{
178
179 To speed up the initial CPAN shell startup, it is possible to use
180 Storable to create an cache of metadata. If Storable is not available,
181 the normal index mechanism will be used. This feature is still
182 considered experimantal and not recommended for production use.
183
184 };
185
186     defined($default = $CPAN::Config->{cache_metadata}) or $default = 0;
187     do {
188         $ans = prompt("Cache metadata (yes/no)?", ($default ? 'yes' : 'no'));
189     } while ($ans !~ /^\s*[yn]/i);
190     $CPAN::Config->{cache_metadata} = ($ans =~ /^\s*y/i ? 1 : 0);
191
192     #
193     # prerequisites_policy
194     # Do we follow PREREQ_PM?
195     #
196     print qq{
197
198 The CPAN module can detect when a module that which you are trying to
199 build depends on prerequisites. If this happens, it can build the
200 prerequisites for you automatically ('follow'), ask you for
201 confirmation ('ask'), or just ignore them ('ignore'). Please set your
202 policy to one of the three values.
203
204 };
205
206     $default = $CPAN::Config->{prerequisites_policy} || 'ask';
207     do {
208       $ans =
209           prompt("Policy on building prerequisites (follow, ask or ignore)?",
210                  $default);
211     } while ($ans ne 'follow' && $ans ne 'ask' && $ans ne 'ignore');
212     $CPAN::Config->{prerequisites_policy} = $ans;
213
214     #
215     # External programs
216     #
217
218     print qq{
219
220 The CPAN module will need a few external programs to work
221 properly. Please correct me, if I guess the wrong path for a program.
222 Don\'t panic if you do not have some of them, just press ENTER for
223 those.
224
225 };
226
227     my $old_warn = $^W;
228     local $^W if $^O eq 'MacOS';
229     my(@path) = split /$Config{'path_sep'}/, $ENV{'PATH'};
230     local $^W = $old_warn;
231     my $progname;
232     for $progname (qw/gzip tar unzip make lynx ncftpget ncftp ftp/){
233       if ($^O eq 'MacOS') {
234           $CPAN::Config->{$progname} = 'not_here';
235           next;
236       }
237       my $progcall = $progname;
238       # we don't need ncftp if we have ncftpget
239       next if $progname eq "ncftp" && $CPAN::Config->{ncftpget} gt " ";
240       my $path = $CPAN::Config->{$progname} 
241           || $Config::Config{$progname}
242               || "";
243       if (MM->file_name_is_absolute($path)) {
244         # testing existence is not good enough, some have these exe
245         # extensions
246
247         # warn "Warning: configured $path does not exist\n" unless -e $path;
248         # $path = "";
249       } else {
250         $path = '';
251       }
252       unless ($path) {
253         # e.g. make -> nmake
254         $progcall = $Config::Config{$progname} if $Config::Config{$progname};
255       }
256
257       $path ||= find_exe($progcall,[@path]);
258       warn "Warning: $progcall not found in PATH\n" unless
259           $path; # not -e $path, because find_exe already checked that
260       $ans = prompt("Where is your $progname program?",$path) || $path;
261       $CPAN::Config->{$progname} = $ans;
262     }
263     my $path = $CPAN::Config->{'pager'} || 
264         $ENV{PAGER} || find_exe("less",[@path]) || 
265             find_exe("more",[@path]) || ($^O eq 'MacOS' ? $ENV{EDITOR} : 0 )
266             || "more";
267     $ans = prompt("What is your favorite pager program?",$path);
268     $CPAN::Config->{'pager'} = $ans;
269     $path = $CPAN::Config->{'shell'};
270     if (MM->file_name_is_absolute($path)) {
271         warn "Warning: configured $path does not exist\n" unless -e $path;
272         $path = "";
273     }
274     $path ||= $ENV{SHELL};
275     if ($^O eq 'MacOS') {
276         $CPAN::Config->{'shell'} = 'not_here';
277     } else {
278         $path =~ s,\\,/,g if $^O eq 'os2';      # Cosmetic only
279         $ans = prompt("What is your favorite shell?",$path);
280         $CPAN::Config->{'shell'} = $ans;
281     }
282
283     #
284     # Arguments to make etc.
285     #
286
287     print qq{
288
289 Every Makefile.PL is run by perl in a separate process. Likewise we
290 run \'make\' and \'make install\' in processes. If you have any parameters
291 \(e.g. PREFIX, INSTALLPRIVLIB, UNINST or the like\) you want to pass to
292 the calls, please specify them here.
293
294 If you don\'t understand this question, just press ENTER.
295
296 };
297
298     $default = $CPAN::Config->{makepl_arg} || "";
299     $CPAN::Config->{makepl_arg} =
300         prompt("Parameters for the 'perl Makefile.PL' command?",$default);
301     $default = $CPAN::Config->{make_arg} || "";
302     $CPAN::Config->{make_arg} = prompt("Parameters for the 'make' command?",$default);
303
304     $default = $CPAN::Config->{make_install_arg} || $CPAN::Config->{make_arg} || "";
305     $CPAN::Config->{make_install_arg} =
306         prompt("Parameters for the 'make install' command?",$default);
307
308     #
309     # Alarm period
310     #
311
312     print qq{
313
314 Sometimes you may wish to leave the processes run by CPAN alone
315 without caring about them. As sometimes the Makefile.PL contains
316 question you\'re expected to answer, you can set a timer that will
317 kill a 'perl Makefile.PL' process after the specified time in seconds.
318
319 If you set this value to 0, these processes will wait forever. This is
320 the default and recommended setting.
321
322 };
323
324     $default = $CPAN::Config->{inactivity_timeout} || 0;
325     $CPAN::Config->{inactivity_timeout} =
326         prompt("Timeout for inactivity during Makefile.PL?",$default);
327
328     # Proxies
329
330     print qq{
331
332 If you\'re accessing the net via proxies, you can specify them in the
333 CPAN configuration or via environment variables. The variable in
334 the \$CPAN::Config takes precedence.
335
336 };
337
338     for (qw/ftp_proxy http_proxy no_proxy/) {
339         $default = $CPAN::Config->{$_} || $ENV{$_};
340         $CPAN::Config->{$_} = prompt("Your $_?",$default);
341     }
342
343     #
344     # MIRRORED.BY
345     #
346
347     conf_sites() unless $fastread;
348
349     unless (@{$CPAN::Config->{'wait_list'}||[]}) {
350         print qq{
351
352 WAIT support is available as a Plugin. You need the CPAN::WAIT module
353 to actually use it.  But we need to know your favorite WAIT server. If
354 you don\'t know a WAIT server near you, just press ENTER.
355
356 };
357         $default = "wait://ls6.informatik.uni-dortmund.de:1404";
358         $ans = prompt("Your favorite WAIT server?\n  ",$default);
359         push @{$CPAN::Config->{'wait_list'}}, $ans;
360     }
361
362     # We don't ask that now, it will be noticed in time, won't it?
363     $CPAN::Config->{'inhibit_startup_message'} = 0;
364     $CPAN::Config->{'getcwd'} = 'cwd';
365
366     print "\n\n";
367     CPAN::Config->commit($configpm);
368 }
369
370 sub conf_sites {
371   my $m = 'MIRRORED.BY';
372   my $mby = MM->catfile($CPAN::Config->{keep_source_where},$m);
373   File::Path::mkpath(File::Basename::dirname($mby));
374   if (-f $mby && -f $m && -M $m < -M $mby) {
375     require File::Copy;
376     File::Copy::copy($m,$mby) or die "Could not update $mby: $!";
377   }
378   my $loopcount = 0;
379   local $^T = time;
380   while ($mby) {
381     if ( ! -f $mby ){
382       print qq{You have no $mby
383   I\'m trying to fetch one
384 };
385       $mby = CPAN::FTP->localize($m,$mby,3);
386     } elsif (-M $mby > 60 && $loopcount == 0) {
387       print qq{Your $mby is older than 60 days,
388   I\'m trying to fetch one
389 };
390       $mby = CPAN::FTP->localize($m,$mby,3);
391       $loopcount++;
392     } elsif (-s $mby == 0) {
393       print qq{You have an empty $mby,
394   I\'m trying to fetch one
395 };
396       $mby = CPAN::FTP->localize($m,$mby,3);
397     } else {
398       last;
399     }
400   }
401   read_mirrored_by($mby);
402   bring_your_own();
403 }
404
405 sub find_exe {
406     my($exe,$path) = @_;
407     my($dir);
408     #warn "in find_exe exe[$exe] path[@$path]";
409     for $dir (@$path) {
410         my $abs = MM->catfile($dir,$exe);
411         if (($abs = MM->maybe_command($abs))) {
412             return $abs;
413         }
414     }
415 }
416
417 sub picklist {
418     my($items,$prompt,$default,$require_nonempty,$empty_warning)=@_;
419     $default ||= '';
420
421     my ($item, $i);
422     for $item (@$items) {
423         printf "(%d) %s\n", ++$i, $item;
424     }
425
426     my @nums;
427     while (1) {
428         my $num = prompt($prompt,$default);
429         @nums = split (' ', $num);
430         (warn "invalid items entered, try again\n"), next
431             if grep (/\D/ || $_ < 1 || $_ > $i, @nums);
432         if ($require_nonempty) {
433             (warn "$empty_warning\n"), next
434                 unless @nums;
435         }
436         last;
437     }
438     print "\n";
439     for (@nums) { $_-- }
440     @{$items}[@nums];
441 }
442
443 sub read_mirrored_by {
444     my $local = shift or return;
445     my(%all,$url,$expected_size,$default,$ans,$host,$dst,$country,$continent,@location);
446     my $fh = FileHandle->new;
447     $fh->open($local) or die "Couldn't open $local: $!";
448     local $/ = "\012";
449     while (<$fh>) {
450         ($host) = /^([\w\.\-]+)/ unless defined $host;
451         next unless defined $host;
452         next unless /\s+dst_(dst|location)/;
453         /location\s+=\s+\"([^\"]+)/ and @location = (split /\s*,\s*/, $1) and
454             ($continent, $country) = @location[-1,-2];
455         $continent =~ s/\s\(.*//;
456         $continent =~ s/\W+$//; # if Jarkko doesn't know latitude/longitude
457         /dst_dst\s+=\s+\"([^\"]+)/  and $dst = $1;
458         next unless $host && $dst && $continent && $country;
459         $all{$continent}{$country}{$dst} = CPAN::Mirrored::By->new($continent,$country,$dst);
460         undef $host;
461         $dst=$continent=$country="";
462     }
463     $fh->close;
464     $CPAN::Config->{urllist} ||= [];
465     my(@previous_urls);
466     if (@previous_urls = @{$CPAN::Config->{urllist}}) {
467         $CPAN::Config->{urllist} = [];
468     }
469
470     print qq{
471
472 Now we need to know where your favorite CPAN sites are located. Push
473 a few sites onto the array (just in case the first on the array won\'t
474 work). If you are mirroring CPAN to your local workstation, specify a
475 file: URL.
476
477 First, pick a nearby continent and country (you can pick several of
478 each, separated by spaces, or none if you just want to keep your
479 existing selections). Then, you will be presented with a list of URLs
480 of CPAN mirrors in the countries you selected, along with previously
481 selected URLs. Select some of those URLs, or just keep the old list.
482 Finally, you will be prompted for any extra URLs -- file:, ftp:, or
483 http: -- that host a CPAN mirror.
484
485 };
486
487     my (@cont, $cont, %cont, @countries, @urls, %seen);
488     my $no_previous_warn = 
489        "Sorry! since you don't have any existing picks, you must make a\n" .
490        "geographic selection.";
491     @cont = picklist([sort keys %all],
492                      "Select your continent (or several nearby continents)",
493                      '',
494                      ! @previous_urls,
495                      $no_previous_warn);
496
497
498     foreach $cont (@cont) {
499         my @c = sort keys %{$all{$cont}};
500         @cont{@c} = map ($cont, 0..$#c);
501         @c = map ("$_ ($cont)", @c) if @cont > 1;
502         push (@countries, @c);
503     }
504
505     if (@countries) {
506         @countries = picklist (\@countries,
507                                "Select your country (or several nearby countries)",
508                                '',
509                                ! @previous_urls,
510                                $no_previous_warn);
511         %seen = map (($_ => 1), @previous_urls);
512         # hmmm, should take list of defaults from CPAN::Config->{'urllist'}...
513         foreach $country (@countries) {
514             (my $bare_country = $country) =~ s/ \(.*\)//;
515             my @u = sort keys %{$all{$cont{$bare_country}}{$bare_country}};
516             @u = grep (! $seen{$_}, @u);
517             @u = map ("$_ ($bare_country)", @u)
518                if @countries > 1;
519             push (@urls, @u);
520         }
521     }
522     push (@urls, map ("$_ (previous pick)", @previous_urls));
523     my $prompt = "Select as many URLs as you like";
524     if (@previous_urls) {
525        $default = join (' ', ((scalar @urls) - (scalar @previous_urls) + 1) ..
526                              (scalar @urls));
527        $prompt .= "\n(or just hit RETURN to keep your previous picks)";
528     }
529
530     @urls = picklist (\@urls, $prompt, $default);
531     foreach (@urls) { s/ \(.*\)//; }
532     push @{$CPAN::Config->{urllist}}, @urls;
533 }
534
535 sub bring_your_own {
536     my %seen = map (($_ => 1), @{$CPAN::Config->{urllist}});
537     my($ans,@urls);
538     do {
539         my $prompt = "Enter another URL or RETURN to quit:";
540         unless (%seen) {
541             $prompt = qq{CPAN.pm needs at least one URL where it can fetch CPAN files from.
542
543 Please enter your CPAN site:};
544         }
545         $ans = prompt ($prompt, "");
546
547         if ($ans) {
548             $ans =~ s|/?\z|/|; # has to end with one slash
549             $ans = "file:$ans" unless $ans =~ /:/; # without a scheme is a file:
550             if ($ans =~ /^\w+:\/./) {
551                push @urls, $ans unless $seen{$ans}++;
552             } else {
553                 print qq{"$ans" doesn\'t look like an URL at first sight.
554 I\'ll ignore it for now.  You can add it to $INC{'CPAN/MyConfig.pm'}
555 later if you\'re sure it\'s right.\n};
556             }
557         }
558     } while $ans || !%seen;
559
560     push @{$CPAN::Config->{urllist}}, @urls;
561     # xxx delete or comment these out when you're happy that it works
562     print "New set of picks:\n";
563     map { print "  $_\n" } @{$CPAN::Config->{urllist}};
564 }
565
566 1;