1 | #line 1 |
---|
2 | package Module::AutoInstall; |
---|
3 | |
---|
4 | use strict; |
---|
5 | use Cwd (); |
---|
6 | use ExtUtils::MakeMaker (); |
---|
7 | |
---|
8 | use vars qw{$VERSION}; |
---|
9 | BEGIN { |
---|
10 | $VERSION = '1.03'; |
---|
11 | } |
---|
12 | |
---|
13 | # special map on pre-defined feature sets |
---|
14 | my %FeatureMap = ( |
---|
15 | '' => 'Core Features', # XXX: deprecated |
---|
16 | '-core' => 'Core Features', |
---|
17 | ); |
---|
18 | |
---|
19 | # various lexical flags |
---|
20 | my ( @Missing, @Existing, %DisabledTests, $UnderCPAN, $HasCPANPLUS ); |
---|
21 | my ( |
---|
22 | $Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly, $AllDeps |
---|
23 | ); |
---|
24 | my ( $PostambleActions, $PostambleUsed ); |
---|
25 | |
---|
26 | # See if it's a testing or non-interactive session |
---|
27 | _accept_default( $ENV{AUTOMATED_TESTING} or ! -t STDIN ); |
---|
28 | _init(); |
---|
29 | |
---|
30 | sub _accept_default { |
---|
31 | $AcceptDefault = shift; |
---|
32 | } |
---|
33 | |
---|
34 | sub missing_modules { |
---|
35 | return @Missing; |
---|
36 | } |
---|
37 | |
---|
38 | sub do_install { |
---|
39 | __PACKAGE__->install( |
---|
40 | [ |
---|
41 | $Config |
---|
42 | ? ( UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) |
---|
43 | : () |
---|
44 | ], |
---|
45 | @Missing, |
---|
46 | ); |
---|
47 | } |
---|
48 | |
---|
49 | # initialize various flags, and/or perform install |
---|
50 | sub _init { |
---|
51 | foreach my $arg ( |
---|
52 | @ARGV, |
---|
53 | split( |
---|
54 | /[\s\t]+/, |
---|
55 | $ENV{PERL_AUTOINSTALL} || $ENV{PERL_EXTUTILS_AUTOINSTALL} || '' |
---|
56 | ) |
---|
57 | ) |
---|
58 | { |
---|
59 | if ( $arg =~ /^--config=(.*)$/ ) { |
---|
60 | $Config = [ split( ',', $1 ) ]; |
---|
61 | } |
---|
62 | elsif ( $arg =~ /^--installdeps=(.*)$/ ) { |
---|
63 | __PACKAGE__->install( $Config, @Missing = split( /,/, $1 ) ); |
---|
64 | exit 0; |
---|
65 | } |
---|
66 | elsif ( $arg =~ /^--default(?:deps)?$/ ) { |
---|
67 | $AcceptDefault = 1; |
---|
68 | } |
---|
69 | elsif ( $arg =~ /^--check(?:deps)?$/ ) { |
---|
70 | $CheckOnly = 1; |
---|
71 | } |
---|
72 | elsif ( $arg =~ /^--skip(?:deps)?$/ ) { |
---|
73 | $SkipInstall = 1; |
---|
74 | } |
---|
75 | elsif ( $arg =~ /^--test(?:only)?$/ ) { |
---|
76 | $TestOnly = 1; |
---|
77 | } |
---|
78 | elsif ( $arg =~ /^--all(?:deps)?$/ ) { |
---|
79 | $AllDeps = 1; |
---|
80 | } |
---|
81 | } |
---|
82 | } |
---|
83 | |
---|
84 | # overrides MakeMaker's prompt() to automatically accept the default choice |
---|
85 | sub _prompt { |
---|
86 | goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault; |
---|
87 | |
---|
88 | my ( $prompt, $default ) = @_; |
---|
89 | my $y = ( $default =~ /^[Yy]/ ); |
---|
90 | |
---|
91 | print $prompt, ' [', ( $y ? 'Y' : 'y' ), '/', ( $y ? 'n' : 'N' ), '] '; |
---|
92 | print "$default\n"; |
---|
93 | return $default; |
---|
94 | } |
---|
95 | |
---|
96 | # the workhorse |
---|
97 | sub import { |
---|
98 | my $class = shift; |
---|
99 | my @args = @_ or return; |
---|
100 | my $core_all; |
---|
101 | |
---|
102 | print "*** $class version " . $class->VERSION . "\n"; |
---|
103 | print "*** Checking for Perl dependencies...\n"; |
---|
104 | |
---|
105 | my $cwd = Cwd::cwd(); |
---|
106 | |
---|
107 | $Config = []; |
---|
108 | |
---|
109 | my $maxlen = length( |
---|
110 | ( |
---|
111 | sort { length($b) <=> length($a) } |
---|
112 | grep { /^[^\-]/ } |
---|
113 | map { |
---|
114 | ref($_) |
---|
115 | ? ( ( ref($_) eq 'HASH' ) ? keys(%$_) : @{$_} ) |
---|
116 | : '' |
---|
117 | } |
---|
118 | map { +{@args}->{$_} } |
---|
119 | grep { /^[^\-]/ or /^-core$/i } keys %{ +{@args} } |
---|
120 | )[0] |
---|
121 | ); |
---|
122 | |
---|
123 | # We want to know if we're under CPAN early to avoid prompting, but |
---|
124 | # if we aren't going to try and install anything anyway then skip the |
---|
125 | # check entirely since we don't want to have to load (and configure) |
---|
126 | # an old CPAN just for a cosmetic message |
---|
127 | |
---|
128 | $UnderCPAN = _check_lock(1) unless $SkipInstall; |
---|
129 | |
---|
130 | while ( my ( $feature, $modules ) = splice( @args, 0, 2 ) ) { |
---|
131 | my ( @required, @tests, @skiptests ); |
---|
132 | my $default = 1; |
---|
133 | my $conflict = 0; |
---|
134 | |
---|
135 | if ( $feature =~ m/^-(\w+)$/ ) { |
---|
136 | my $option = lc($1); |
---|
137 | |
---|
138 | # check for a newer version of myself |
---|
139 | _update_to( $modules, @_ ) and return if $option eq 'version'; |
---|
140 | |
---|
141 | # sets CPAN configuration options |
---|
142 | $Config = $modules if $option eq 'config'; |
---|
143 | |
---|
144 | # promote every features to core status |
---|
145 | $core_all = ( $modules =~ /^all$/i ) and next |
---|
146 | if $option eq 'core'; |
---|
147 | |
---|
148 | next unless $option eq 'core'; |
---|
149 | } |
---|
150 | |
---|
151 | print "[" . ( $FeatureMap{ lc($feature) } || $feature ) . "]\n"; |
---|
152 | |
---|
153 | $modules = [ %{$modules} ] if UNIVERSAL::isa( $modules, 'HASH' ); |
---|
154 | |
---|
155 | unshift @$modules, -default => &{ shift(@$modules) } |
---|
156 | if ( ref( $modules->[0] ) eq 'CODE' ); # XXX: bugward combatability |
---|
157 | |
---|
158 | while ( my ( $mod, $arg ) = splice( @$modules, 0, 2 ) ) { |
---|
159 | if ( $mod =~ m/^-(\w+)$/ ) { |
---|
160 | my $option = lc($1); |
---|
161 | |
---|
162 | $default = $arg if ( $option eq 'default' ); |
---|
163 | $conflict = $arg if ( $option eq 'conflict' ); |
---|
164 | @tests = @{$arg} if ( $option eq 'tests' ); |
---|
165 | @skiptests = @{$arg} if ( $option eq 'skiptests' ); |
---|
166 | |
---|
167 | next; |
---|
168 | } |
---|
169 | |
---|
170 | printf( "- %-${maxlen}s ...", $mod ); |
---|
171 | |
---|
172 | if ( $arg and $arg =~ /^\D/ ) { |
---|
173 | unshift @$modules, $arg; |
---|
174 | $arg = 0; |
---|
175 | } |
---|
176 | |
---|
177 | # XXX: check for conflicts and uninstalls(!) them. |
---|
178 | my $cur = _load($mod); |
---|
179 | if (_version_cmp ($cur, $arg) >= 0) |
---|
180 | { |
---|
181 | print "loaded. ($cur" . ( $arg ? " >= $arg" : '' ) . ")\n"; |
---|
182 | push @Existing, $mod => $arg; |
---|
183 | $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; |
---|
184 | } |
---|
185 | else { |
---|
186 | if (not defined $cur) # indeed missing |
---|
187 | { |
---|
188 | print "missing." . ( $arg ? " (would need $arg)" : '' ) . "\n"; |
---|
189 | } |
---|
190 | else |
---|
191 | { |
---|
192 | # no need to check $arg as _version_cmp ($cur, undef) would satisfy >= above |
---|
193 | print "too old. ($cur < $arg)\n"; |
---|
194 | } |
---|
195 | |
---|
196 | push @required, $mod => $arg; |
---|
197 | } |
---|
198 | } |
---|
199 | |
---|
200 | next unless @required; |
---|
201 | |
---|
202 | my $mandatory = ( $feature eq '-core' or $core_all ); |
---|
203 | |
---|
204 | if ( |
---|
205 | !$SkipInstall |
---|
206 | and ( |
---|
207 | $CheckOnly |
---|
208 | or ($mandatory and $UnderCPAN) |
---|
209 | or $AllDeps |
---|
210 | or _prompt( |
---|
211 | qq{==> Auto-install the } |
---|
212 | . ( @required / 2 ) |
---|
213 | . ( $mandatory ? ' mandatory' : ' optional' ) |
---|
214 | . qq{ module(s) from CPAN?}, |
---|
215 | $default ? 'y' : 'n', |
---|
216 | ) =~ /^[Yy]/ |
---|
217 | ) |
---|
218 | ) |
---|
219 | { |
---|
220 | push( @Missing, @required ); |
---|
221 | $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; |
---|
222 | } |
---|
223 | |
---|
224 | elsif ( !$SkipInstall |
---|
225 | and $default |
---|
226 | and $mandatory |
---|
227 | and |
---|
228 | _prompt( qq{==> The module(s) are mandatory! Really skip?}, 'n', ) |
---|
229 | =~ /^[Nn]/ ) |
---|
230 | { |
---|
231 | push( @Missing, @required ); |
---|
232 | $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; |
---|
233 | } |
---|
234 | |
---|
235 | else { |
---|
236 | $DisabledTests{$_} = 1 for map { glob($_) } @tests; |
---|
237 | } |
---|
238 | } |
---|
239 | |
---|
240 | if ( @Missing and not( $CheckOnly or $UnderCPAN ) ) { |
---|
241 | require Config; |
---|
242 | print |
---|
243 | "*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n"; |
---|
244 | |
---|
245 | # make an educated guess of whether we'll need root permission. |
---|
246 | print " (You may need to do that as the 'root' user.)\n" |
---|
247 | if eval '$>'; |
---|
248 | } |
---|
249 | print "*** $class configuration finished.\n"; |
---|
250 | |
---|
251 | chdir $cwd; |
---|
252 | |
---|
253 | # import to main:: |
---|
254 | no strict 'refs'; |
---|
255 | *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main'; |
---|
256 | } |
---|
257 | |
---|
258 | sub _running_under { |
---|
259 | my $thing = shift; |
---|
260 | print <<"END_MESSAGE"; |
---|
261 | *** Since we're running under ${thing}, I'll just let it take care |
---|
262 | of the dependency's installation later. |
---|
263 | END_MESSAGE |
---|
264 | return 1; |
---|
265 | } |
---|
266 | |
---|
267 | # Check to see if we are currently running under CPAN.pm and/or CPANPLUS; |
---|
268 | # if we are, then we simply let it taking care of our dependencies |
---|
269 | sub _check_lock { |
---|
270 | return unless @Missing or @_; |
---|
271 | |
---|
272 | my $cpan_env = $ENV{PERL5_CPAN_IS_RUNNING}; |
---|
273 | |
---|
274 | if ($ENV{PERL5_CPANPLUS_IS_RUNNING}) { |
---|
275 | return _running_under($cpan_env ? 'CPAN' : 'CPANPLUS'); |
---|
276 | } |
---|
277 | |
---|
278 | require CPAN; |
---|
279 | |
---|
280 | if ($CPAN::VERSION > '1.89') { |
---|
281 | if ($cpan_env) { |
---|
282 | return _running_under('CPAN'); |
---|
283 | } |
---|
284 | return; # CPAN.pm new enough, don't need to check further |
---|
285 | } |
---|
286 | |
---|
287 | # last ditch attempt, this -will- configure CPAN, very sorry |
---|
288 | |
---|
289 | _load_cpan(1); # force initialize even though it's already loaded |
---|
290 | |
---|
291 | # Find the CPAN lock-file |
---|
292 | my $lock = MM->catfile( $CPAN::Config->{cpan_home}, ".lock" ); |
---|
293 | return unless -f $lock; |
---|
294 | |
---|
295 | # Check the lock |
---|
296 | local *LOCK; |
---|
297 | return unless open(LOCK, $lock); |
---|
298 | |
---|
299 | if ( |
---|
300 | ( $^O eq 'MSWin32' ? _under_cpan() : <LOCK> == getppid() ) |
---|
301 | and ( $CPAN::Config->{prerequisites_policy} || '' ) ne 'ignore' |
---|
302 | ) { |
---|
303 | print <<'END_MESSAGE'; |
---|
304 | |
---|
305 | *** Since we're running under CPAN, I'll just let it take care |
---|
306 | of the dependency's installation later. |
---|
307 | END_MESSAGE |
---|
308 | return 1; |
---|
309 | } |
---|
310 | |
---|
311 | close LOCK; |
---|
312 | return; |
---|
313 | } |
---|
314 | |
---|
315 | sub install { |
---|
316 | my $class = shift; |
---|
317 | |
---|
318 | my $i; # used below to strip leading '-' from config keys |
---|
319 | my @config = ( map { s/^-// if ++$i; $_ } @{ +shift } ); |
---|
320 | |
---|
321 | my ( @modules, @installed ); |
---|
322 | while ( my ( $pkg, $ver ) = splice( @_, 0, 2 ) ) { |
---|
323 | |
---|
324 | # grep out those already installed |
---|
325 | if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { |
---|
326 | push @installed, $pkg; |
---|
327 | } |
---|
328 | else { |
---|
329 | push @modules, $pkg, $ver; |
---|
330 | } |
---|
331 | } |
---|
332 | |
---|
333 | return @installed unless @modules; # nothing to do |
---|
334 | return @installed if _check_lock(); # defer to the CPAN shell |
---|
335 | |
---|
336 | print "*** Installing dependencies...\n"; |
---|
337 | |
---|
338 | return unless _connected_to('cpan.org'); |
---|
339 | |
---|
340 | my %args = @config; |
---|
341 | my %failed; |
---|
342 | local *FAILED; |
---|
343 | if ( $args{do_once} and open( FAILED, '.#autoinstall.failed' ) ) { |
---|
344 | while (<FAILED>) { chomp; $failed{$_}++ } |
---|
345 | close FAILED; |
---|
346 | |
---|
347 | my @newmod; |
---|
348 | while ( my ( $k, $v ) = splice( @modules, 0, 2 ) ) { |
---|
349 | push @newmod, ( $k => $v ) unless $failed{$k}; |
---|
350 | } |
---|
351 | @modules = @newmod; |
---|
352 | } |
---|
353 | |
---|
354 | if ( _has_cpanplus() and not $ENV{PERL_AUTOINSTALL_PREFER_CPAN} ) { |
---|
355 | _install_cpanplus( \@modules, \@config ); |
---|
356 | } else { |
---|
357 | _install_cpan( \@modules, \@config ); |
---|
358 | } |
---|
359 | |
---|
360 | print "*** $class installation finished.\n"; |
---|
361 | |
---|
362 | # see if we have successfully installed them |
---|
363 | while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { |
---|
364 | if ( _version_cmp( _load($pkg), $ver ) >= 0 ) { |
---|
365 | push @installed, $pkg; |
---|
366 | } |
---|
367 | elsif ( $args{do_once} and open( FAILED, '>> .#autoinstall.failed' ) ) { |
---|
368 | print FAILED "$pkg\n"; |
---|
369 | } |
---|
370 | } |
---|
371 | |
---|
372 | close FAILED if $args{do_once}; |
---|
373 | |
---|
374 | return @installed; |
---|
375 | } |
---|
376 | |
---|
377 | sub _install_cpanplus { |
---|
378 | my @modules = @{ +shift }; |
---|
379 | my @config = _cpanplus_config( @{ +shift } ); |
---|
380 | my $installed = 0; |
---|
381 | |
---|
382 | require CPANPLUS::Backend; |
---|
383 | my $cp = CPANPLUS::Backend->new; |
---|
384 | my $conf = $cp->configure_object; |
---|
385 | |
---|
386 | return unless $conf->can('conf') # 0.05x+ with "sudo" support |
---|
387 | or _can_write($conf->_get_build('base')); # 0.04x |
---|
388 | |
---|
389 | # if we're root, set UNINST=1 to avoid trouble unless user asked for it. |
---|
390 | my $makeflags = $conf->get_conf('makeflags') || ''; |
---|
391 | if ( UNIVERSAL::isa( $makeflags, 'HASH' ) ) { |
---|
392 | # 0.03+ uses a hashref here |
---|
393 | $makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST}; |
---|
394 | |
---|
395 | } else { |
---|
396 | # 0.02 and below uses a scalar |
---|
397 | $makeflags = join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) |
---|
398 | if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); |
---|
399 | |
---|
400 | } |
---|
401 | $conf->set_conf( makeflags => $makeflags ); |
---|
402 | $conf->set_conf( prereqs => 1 ); |
---|
403 | |
---|
404 | |
---|
405 | |
---|
406 | while ( my ( $key, $val ) = splice( @config, 0, 2 ) ) { |
---|
407 | $conf->set_conf( $key, $val ); |
---|
408 | } |
---|
409 | |
---|
410 | my $modtree = $cp->module_tree; |
---|
411 | while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { |
---|
412 | print "*** Installing $pkg...\n"; |
---|
413 | |
---|
414 | MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; |
---|
415 | |
---|
416 | my $success; |
---|
417 | my $obj = $modtree->{$pkg}; |
---|
418 | |
---|
419 | if ( $obj and _version_cmp( $obj->{version}, $ver ) >= 0 ) { |
---|
420 | my $pathname = $pkg; |
---|
421 | $pathname =~ s/::/\\W/; |
---|
422 | |
---|
423 | foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { |
---|
424 | delete $INC{$inc}; |
---|
425 | } |
---|
426 | |
---|
427 | my $rv = $cp->install( modules => [ $obj->{module} ] ); |
---|
428 | |
---|
429 | if ( $rv and ( $rv->{ $obj->{module} } or $rv->{ok} ) ) { |
---|
430 | print "*** $pkg successfully installed.\n"; |
---|
431 | $success = 1; |
---|
432 | } else { |
---|
433 | print "*** $pkg installation cancelled.\n"; |
---|
434 | $success = 0; |
---|
435 | } |
---|
436 | |
---|
437 | $installed += $success; |
---|
438 | } else { |
---|
439 | print << "."; |
---|
440 | *** Could not find a version $ver or above for $pkg; skipping. |
---|
441 | . |
---|
442 | } |
---|
443 | |
---|
444 | MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; |
---|
445 | } |
---|
446 | |
---|
447 | return $installed; |
---|
448 | } |
---|
449 | |
---|
450 | sub _cpanplus_config { |
---|
451 | my @config = (); |
---|
452 | while ( @_ ) { |
---|
453 | my ($key, $value) = (shift(), shift()); |
---|
454 | if ( $key eq 'prerequisites_policy' ) { |
---|
455 | if ( $value eq 'follow' ) { |
---|
456 | $value = CPANPLUS::Internals::Constants::PREREQ_INSTALL(); |
---|
457 | } elsif ( $value eq 'ask' ) { |
---|
458 | $value = CPANPLUS::Internals::Constants::PREREQ_ASK(); |
---|
459 | } elsif ( $value eq 'ignore' ) { |
---|
460 | $value = CPANPLUS::Internals::Constants::PREREQ_IGNORE(); |
---|
461 | } else { |
---|
462 | die "*** Cannot convert option $key = '$value' to CPANPLUS version.\n"; |
---|
463 | } |
---|
464 | } else { |
---|
465 | die "*** Cannot convert option $key to CPANPLUS version.\n"; |
---|
466 | } |
---|
467 | } |
---|
468 | return @config; |
---|
469 | } |
---|
470 | |
---|
471 | sub _install_cpan { |
---|
472 | my @modules = @{ +shift }; |
---|
473 | my @config = @{ +shift }; |
---|
474 | my $installed = 0; |
---|
475 | my %args; |
---|
476 | |
---|
477 | _load_cpan(); |
---|
478 | require Config; |
---|
479 | |
---|
480 | if (CPAN->VERSION < 1.80) { |
---|
481 | # no "sudo" support, probe for writableness |
---|
482 | return unless _can_write( MM->catfile( $CPAN::Config->{cpan_home}, 'sources' ) ) |
---|
483 | and _can_write( $Config::Config{sitelib} ); |
---|
484 | } |
---|
485 | |
---|
486 | # if we're root, set UNINST=1 to avoid trouble unless user asked for it. |
---|
487 | my $makeflags = $CPAN::Config->{make_install_arg} || ''; |
---|
488 | $CPAN::Config->{make_install_arg} = |
---|
489 | join( ' ', split( ' ', $makeflags ), 'UNINST=1' ) |
---|
490 | if ( $makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' } ); |
---|
491 | |
---|
492 | # don't show start-up info |
---|
493 | $CPAN::Config->{inhibit_startup_message} = 1; |
---|
494 | |
---|
495 | # set additional options |
---|
496 | while ( my ( $opt, $arg ) = splice( @config, 0, 2 ) ) { |
---|
497 | ( $args{$opt} = $arg, next ) |
---|
498 | if $opt =~ /^force$/; # pseudo-option |
---|
499 | $CPAN::Config->{$opt} = $arg; |
---|
500 | } |
---|
501 | |
---|
502 | local $CPAN::Config->{prerequisites_policy} = 'follow'; |
---|
503 | |
---|
504 | while ( my ( $pkg, $ver ) = splice( @modules, 0, 2 ) ) { |
---|
505 | MY::preinstall( $pkg, $ver ) or next if defined &MY::preinstall; |
---|
506 | |
---|
507 | print "*** Installing $pkg...\n"; |
---|
508 | |
---|
509 | my $obj = CPAN::Shell->expand( Module => $pkg ); |
---|
510 | my $success = 0; |
---|
511 | |
---|
512 | if ( $obj and _version_cmp( $obj->cpan_version, $ver ) >= 0 ) { |
---|
513 | my $pathname = $pkg; |
---|
514 | $pathname =~ s/::/\\W/; |
---|
515 | |
---|
516 | foreach my $inc ( grep { m/$pathname.pm/i } keys(%INC) ) { |
---|
517 | delete $INC{$inc}; |
---|
518 | } |
---|
519 | |
---|
520 | my $rv = $args{force} ? CPAN::Shell->force( install => $pkg ) |
---|
521 | : CPAN::Shell->install($pkg); |
---|
522 | $rv ||= eval { |
---|
523 | $CPAN::META->instance( 'CPAN::Distribution', $obj->cpan_file, ) |
---|
524 | ->{install} |
---|
525 | if $CPAN::META; |
---|
526 | }; |
---|
527 | |
---|
528 | if ( $rv eq 'YES' ) { |
---|
529 | print "*** $pkg successfully installed.\n"; |
---|
530 | $success = 1; |
---|
531 | } |
---|
532 | else { |
---|
533 | print "*** $pkg installation failed.\n"; |
---|
534 | $success = 0; |
---|
535 | } |
---|
536 | |
---|
537 | $installed += $success; |
---|
538 | } |
---|
539 | else { |
---|
540 | print << "."; |
---|
541 | *** Could not find a version $ver or above for $pkg; skipping. |
---|
542 | . |
---|
543 | } |
---|
544 | |
---|
545 | MY::postinstall( $pkg, $ver, $success ) if defined &MY::postinstall; |
---|
546 | } |
---|
547 | |
---|
548 | return $installed; |
---|
549 | } |
---|
550 | |
---|
551 | sub _has_cpanplus { |
---|
552 | return ( |
---|
553 | $HasCPANPLUS = ( |
---|
554 | $INC{'CPANPLUS/Config.pm'} |
---|
555 | or _load('CPANPLUS::Shell::Default') |
---|
556 | ) |
---|
557 | ); |
---|
558 | } |
---|
559 | |
---|
560 | # make guesses on whether we're under the CPAN installation directory |
---|
561 | sub _under_cpan { |
---|
562 | require Cwd; |
---|
563 | require File::Spec; |
---|
564 | |
---|
565 | my $cwd = File::Spec->canonpath( Cwd::cwd() ); |
---|
566 | my $cpan = File::Spec->canonpath( $CPAN::Config->{cpan_home} ); |
---|
567 | |
---|
568 | return ( index( $cwd, $cpan ) > -1 ); |
---|
569 | } |
---|
570 | |
---|
571 | sub _update_to { |
---|
572 | my $class = __PACKAGE__; |
---|
573 | my $ver = shift; |
---|
574 | |
---|
575 | return |
---|
576 | if _version_cmp( _load($class), $ver ) >= 0; # no need to upgrade |
---|
577 | |
---|
578 | if ( |
---|
579 | _prompt( "==> A newer version of $class ($ver) is required. Install?", |
---|
580 | 'y' ) =~ /^[Nn]/ |
---|
581 | ) |
---|
582 | { |
---|
583 | die "*** Please install $class $ver manually.\n"; |
---|
584 | } |
---|
585 | |
---|
586 | print << "."; |
---|
587 | *** Trying to fetch it from CPAN... |
---|
588 | . |
---|
589 | |
---|
590 | # install ourselves |
---|
591 | _load($class) and return $class->import(@_) |
---|
592 | if $class->install( [], $class, $ver ); |
---|
593 | |
---|
594 | print << '.'; exit 1; |
---|
595 | |
---|
596 | *** Cannot bootstrap myself. :-( Installation terminated. |
---|
597 | . |
---|
598 | } |
---|
599 | |
---|
600 | # check if we're connected to some host, using inet_aton |
---|
601 | sub _connected_to { |
---|
602 | my $site = shift; |
---|
603 | |
---|
604 | return ( |
---|
605 | ( _load('Socket') and Socket::inet_aton($site) ) or _prompt( |
---|
606 | qq( |
---|
607 | *** Your host cannot resolve the domain name '$site', which |
---|
608 | probably means the Internet connections are unavailable. |
---|
609 | ==> Should we try to install the required module(s) anyway?), 'n' |
---|
610 | ) =~ /^[Yy]/ |
---|
611 | ); |
---|
612 | } |
---|
613 | |
---|
614 | # check if a directory is writable; may create it on demand |
---|
615 | sub _can_write { |
---|
616 | my $path = shift; |
---|
617 | mkdir( $path, 0755 ) unless -e $path; |
---|
618 | |
---|
619 | return 1 if -w $path; |
---|
620 | |
---|
621 | print << "."; |
---|
622 | *** You are not allowed to write to the directory '$path'; |
---|
623 | the installation may fail due to insufficient permissions. |
---|
624 | . |
---|
625 | |
---|
626 | if ( |
---|
627 | eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt( |
---|
628 | qq( |
---|
629 | ==> Should we try to re-execute the autoinstall process with 'sudo'?), |
---|
630 | ((-t STDIN) ? 'y' : 'n') |
---|
631 | ) =~ /^[Yy]/ |
---|
632 | ) |
---|
633 | { |
---|
634 | |
---|
635 | # try to bootstrap ourselves from sudo |
---|
636 | print << "."; |
---|
637 | *** Trying to re-execute the autoinstall process with 'sudo'... |
---|
638 | . |
---|
639 | my $missing = join( ',', @Missing ); |
---|
640 | my $config = join( ',', |
---|
641 | UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) |
---|
642 | if $Config; |
---|
643 | |
---|
644 | return |
---|
645 | unless system( 'sudo', $^X, $0, "--config=$config", |
---|
646 | "--installdeps=$missing" ); |
---|
647 | |
---|
648 | print << "."; |
---|
649 | *** The 'sudo' command exited with error! Resuming... |
---|
650 | . |
---|
651 | } |
---|
652 | |
---|
653 | return _prompt( |
---|
654 | qq( |
---|
655 | ==> Should we try to install the required module(s) anyway?), 'n' |
---|
656 | ) =~ /^[Yy]/; |
---|
657 | } |
---|
658 | |
---|
659 | # load a module and return the version it reports |
---|
660 | sub _load { |
---|
661 | my $mod = pop; # class/instance doesn't matter |
---|
662 | my $file = $mod; |
---|
663 | |
---|
664 | $file =~ s|::|/|g; |
---|
665 | $file .= '.pm'; |
---|
666 | |
---|
667 | local $@; |
---|
668 | return eval { require $file; $mod->VERSION } || ( $@ ? undef: 0 ); |
---|
669 | } |
---|
670 | |
---|
671 | # Load CPAN.pm and it's configuration |
---|
672 | sub _load_cpan { |
---|
673 | return if $CPAN::VERSION and $CPAN::Config and not @_; |
---|
674 | require CPAN; |
---|
675 | if ( $CPAN::HandleConfig::VERSION ) { |
---|
676 | # Newer versions of CPAN have a HandleConfig module |
---|
677 | CPAN::HandleConfig->load; |
---|
678 | } else { |
---|
679 | # Older versions had the load method in Config directly |
---|
680 | CPAN::Config->load; |
---|
681 | } |
---|
682 | } |
---|
683 | |
---|
684 | # compare two versions, either use Sort::Versions or plain comparison |
---|
685 | # return values same as <=> |
---|
686 | sub _version_cmp { |
---|
687 | my ( $cur, $min ) = @_; |
---|
688 | return -1 unless defined $cur; # if 0 keep comparing |
---|
689 | return 1 unless $min; |
---|
690 | |
---|
691 | $cur =~ s/\s+$//; |
---|
692 | |
---|
693 | # check for version numbers that are not in decimal format |
---|
694 | if ( ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./ ) { |
---|
695 | if ( ( $version::VERSION or defined( _load('version') )) and |
---|
696 | version->can('new') |
---|
697 | ) { |
---|
698 | |
---|
699 | # use version.pm if it is installed. |
---|
700 | return version->new($cur) <=> version->new($min); |
---|
701 | } |
---|
702 | elsif ( $Sort::Versions::VERSION or defined( _load('Sort::Versions') ) ) |
---|
703 | { |
---|
704 | |
---|
705 | # use Sort::Versions as the sorting algorithm for a.b.c versions |
---|
706 | return Sort::Versions::versioncmp( $cur, $min ); |
---|
707 | } |
---|
708 | |
---|
709 | warn "Cannot reliably compare non-decimal formatted versions.\n" |
---|
710 | . "Please install version.pm or Sort::Versions.\n"; |
---|
711 | } |
---|
712 | |
---|
713 | # plain comparison |
---|
714 | local $^W = 0; # shuts off 'not numeric' bugs |
---|
715 | return $cur <=> $min; |
---|
716 | } |
---|
717 | |
---|
718 | # nothing; this usage is deprecated. |
---|
719 | sub main::PREREQ_PM { return {}; } |
---|
720 | |
---|
721 | sub _make_args { |
---|
722 | my %args = @_; |
---|
723 | |
---|
724 | $args{PREREQ_PM} = { %{ $args{PREREQ_PM} || {} }, @Existing, @Missing } |
---|
725 | if $UnderCPAN or $TestOnly; |
---|
726 | |
---|
727 | if ( $args{EXE_FILES} and -e 'MANIFEST' ) { |
---|
728 | require ExtUtils::Manifest; |
---|
729 | my $manifest = ExtUtils::Manifest::maniread('MANIFEST'); |
---|
730 | |
---|
731 | $args{EXE_FILES} = |
---|
732 | [ grep { exists $manifest->{$_} } @{ $args{EXE_FILES} } ]; |
---|
733 | } |
---|
734 | |
---|
735 | $args{test}{TESTS} ||= 't/*.t'; |
---|
736 | $args{test}{TESTS} = join( ' ', |
---|
737 | grep { !exists( $DisabledTests{$_} ) } |
---|
738 | map { glob($_) } split( /\s+/, $args{test}{TESTS} ) ); |
---|
739 | |
---|
740 | my $missing = join( ',', @Missing ); |
---|
741 | my $config = |
---|
742 | join( ',', UNIVERSAL::isa( $Config, 'HASH' ) ? %{$Config} : @{$Config} ) |
---|
743 | if $Config; |
---|
744 | |
---|
745 | $PostambleActions = ( |
---|
746 | ($missing and not $UnderCPAN) |
---|
747 | ? "\$(PERL) $0 --config=$config --installdeps=$missing" |
---|
748 | : "\$(NOECHO) \$(NOOP)" |
---|
749 | ); |
---|
750 | |
---|
751 | return %args; |
---|
752 | } |
---|
753 | |
---|
754 | # a wrapper to ExtUtils::MakeMaker::WriteMakefile |
---|
755 | sub Write { |
---|
756 | require Carp; |
---|
757 | Carp::croak "WriteMakefile: Need even number of args" if @_ % 2; |
---|
758 | |
---|
759 | if ($CheckOnly) { |
---|
760 | print << "."; |
---|
761 | *** Makefile not written in check-only mode. |
---|
762 | . |
---|
763 | return; |
---|
764 | } |
---|
765 | |
---|
766 | my %args = _make_args(@_); |
---|
767 | |
---|
768 | no strict 'refs'; |
---|
769 | |
---|
770 | $PostambleUsed = 0; |
---|
771 | local *MY::postamble = \&postamble unless defined &MY::postamble; |
---|
772 | ExtUtils::MakeMaker::WriteMakefile(%args); |
---|
773 | |
---|
774 | print << "." unless $PostambleUsed; |
---|
775 | *** WARNING: Makefile written with customized MY::postamble() without |
---|
776 | including contents from Module::AutoInstall::postamble() -- |
---|
777 | auto installation features disabled. Please contact the author. |
---|
778 | . |
---|
779 | |
---|
780 | return 1; |
---|
781 | } |
---|
782 | |
---|
783 | sub postamble { |
---|
784 | $PostambleUsed = 1; |
---|
785 | |
---|
786 | return <<"END_MAKE"; |
---|
787 | |
---|
788 | config :: installdeps |
---|
789 | \t\$(NOECHO) \$(NOOP) |
---|
790 | |
---|
791 | checkdeps :: |
---|
792 | \t\$(PERL) $0 --checkdeps |
---|
793 | |
---|
794 | installdeps :: |
---|
795 | \t$PostambleActions |
---|
796 | |
---|
797 | END_MAKE |
---|
798 | |
---|
799 | } |
---|
800 | |
---|
801 | 1; |
---|
802 | |
---|
803 | __END__ |
---|
804 | |
---|
805 | #line 1056 |
---|