#!perl
my
$CHECK_ONLY
;
my
$SHOW_SKIPS
;
my
$IGNORE_SKIP
;
my
$RECOVER
;
my
%DEBUG
;
my
(
$DUMP
,
$LOAD
);
my
$CONFIG
= {
checker_options
=> [],
updater_distname
=>
'App-cpanminus'
,
updater_options
=> [],
};
my
$APPNAME
;
BEGIN {
$APPNAME
= file($0)->basename;
}
sub
usage {
return
<<
"EO_USAGE"
;
Usage:
$APPNAME
[command | options...]
Commands:
-s, --show-fails Display FAILED MODULES and
exit
-c, --check-only Check outdated modules and
exit
-r, --recover Recover recoding file
-v, --version Show software version
-h, --help Display this message
Options:
-f, --force-
try
Include FAILED MODULES to update
--configure-timeout Set timeout(sec)
for
configure phase
--build-timeout Set timeout(sec)
for
build phase
--test-timeout Set timeout(sec)
for
test phase
-l, --
local
-lib Update modules under
given
path
-L, --
local
-lib-contained
Update non-core modules under
given
path
--mirror Check and update by
given
URL base
-M, --from Check and update only by
given
URL base
--exclude-core Check and update only non-core modules
-S, --sudo Run
with
sudo
--
no
-sudo Run without sudo
EO_USAGE
}
my
$RECFILE_BASE
;
my
$RECFILE_NAME
=
'.ucpandb'
;
sub
process_options {
my
$CPANM_OPT
= [];
my
$CUD_OPT
= [];
my
$set_cpanm_opt
=
sub
{
my
$l
=
length
(
$_
[0] ) > 1 ?
'--'
:
'-'
;
push
@$CPANM_OPT
,
"$l$_[0]"
=>
$_
[1];
};
my
$set_both_opt
=
sub
{
if
(
$_
[0] eq
'mirror'
) {
push
@$CPANM_OPT
,
"--from"
=>
$_
[1];
push
@$CUD_OPT
,
"--mirror"
=>
$_
[1];
}
elsif
(
$_
[0] eq
'exclude-core'
) {
push
@$CUD_OPT
,
"--exclude-core"
;
}
else
{
my
$l
=
length
(
$_
[0] ) > 1 ?
'--'
:
'-'
;
push
@$CPANM_OPT
,
"$l$_[0]"
=>
$_
[1];
push
@$CUD_OPT
,
"$l$_[0]"
=>
$_
[1];
if
(
$_
[0] eq
'l'
||
$_
[0] eq
'L'
) {
$RECFILE_BASE
=
$_
[1];
}
}
};
my
$set_debugging
=
sub
{
shift
;
$DEBUG
{head} = 0;
my
$value
=
shift
;
if
(
$value
) {
my
@item
=
split
','
,
$value
;
my
@bads
;
for
my
$i
(
@item
) {
if
(
$i
=~ /^(?:fl|fakelib)(?:=(.+))?$/ ) {
$DEBUG
{fakelib} = $1 ||
'./fakelib'
;
}
elsif
(
$i
=~ /^(?:h|head)$/ ) {
$DEBUG
{head} = 1;
}
elsif
(
$i
=~ /^(?:i|maxitem)(?:=(\d+))?/ ) {
$DEBUG
{maxitem} = $1 || 10;
}
else
{
push
@bads
,
$i
;
}
}
if
(
@bads
) {
warn
"Unknown DEBUG option: $_\n"
for
@bads
;
warn
"$APPNAME abort.\n"
;
exit
9;
}
}
if
(
$DEBUG
{fakelib} ) {
(
$DEBUG
{fakelib} = dir(
$DEBUG
{fakelib} )->absolute )
=~ s:\\:/:g;
}
$DEBUG
{enable} = 1;
};
Getopt::Long::Configure(
'bundling'
);
warn
( usage() ),
exit
unless
Getopt::Long::GetOptions(
'v|version'
=>
sub
{
our
$VERSION
= version->declare(
'1.13'
);
print
__PACKAGE__->VERSION, $/;
exit
;
},
'h|help'
=>
sub
{
say
usage();
exit
; },
's|show-skips'
=> \
$SHOW_SKIPS
,
'c|check-only'
=> \
$CHECK_ONLY
,
'f|force-try'
=> \
$IGNORE_SKIP
,
'r|recover'
=> \
$RECOVER
,
'configure-timeout=i'
=>
$set_cpanm_opt
,
'build-timeout=i'
=>
$set_cpanm_opt
,
'test-timeout=i'
=>
$set_cpanm_opt
,
'l|local-lib=s'
=>
$set_both_opt
,
'L|local-lib-contained=s'
=>
$set_both_opt
,
'mirror=s'
=>
$set_both_opt
,
'exclude-core'
=>
$set_both_opt
,
'S|sudo=s'
=>
$set_cpanm_opt
,
'no-sudo=s'
=>
$set_cpanm_opt
,
'j|test-jobs=i'
=>
sub
{
$DEBUG
{jobs} =
$_
[1]; },
'D|debug:s'
=>
$set_debugging
,
'dump-to|dump=s'
=> \
$DUMP
,
'load-from|load=s'
=> \
$LOAD
,
);
push
@{
$CONFIG
->{updater_options} },
@$CPANM_OPT
;
push
@{
$CONFIG
->{checker_options} },
@$CUD_OPT
;
{
my
$dir
;
my
$file
;
if
(
$RECFILE_BASE
) {
$dir
= file(
$RECFILE_BASE
,
"lib"
,
@Config
{
qw/package archname/
} );
}
else
{
$dir
=
$INC
[0];
}
$file
= file(
$dir
,
$RECFILE_NAME
);
$file
=~ s!\\!/!g;
$CONFIG
->{cfg_file} =
$file
;
}
}
BEGIN {
if
(WIN32) {
and
eval
{ Win32::Console::ANSI->
import
() };
}
}
my
(
$screenX
,
$screenY
);
if
(
$ENV
{COLUMNS} ) {
$screenX
=
$ENV
{COLUMNS};
$screenY
=
$ENV
{LINES};
}
else
{
(
$screenX
,
$screenY
) =
eval
{ Term::ReadKey::GetTerminalSize() };
}
{
my
$old_fh
=
select
STDOUT;
$| = 1;
select
STDERR;
$| = 1;
select
$old_fh
;
}
sub
_setup_debugger {
if
(
$DEBUG
{fakelib} ) {
my
$FAKELIB
=
$DEBUG
{fakelib};
$ENV
{PERL5LIB} =
"$FAKELIB/lib/perl5"
;
$ENV
{PERL_LOCAL_LIB_ROOT} =
$FAKELIB
;
$ENV
{PERL_MB_OPT} =
"--install_base=$FAKELIB"
;
$ENV
{PERL_MM_OPT} =
"INSTALL_BASE=$FAKELIB"
;
$ENV
{PERL_CPANM_HOME} =
$FAKELIB
;
$CONFIG
->{cfg_file} = file(
$FAKELIB
,
$RECFILE_NAME
);
}
if
(
$DEBUG
{head} ) {
warn
"="
x 20 .
" DEBUGGING MODE "
.
"="
x 20 . $/;
warn
sprintf
"%25s: %s$/"
,
'Debug Option'
,
join
(
' '
,
map
{
"$_=$DEBUG{$_}"
}
grep
{
$_
!~ /^(?:enable|head)/ }
keys
%DEBUG
);
warn
sprintf
"%25s: %s$/"
,
'Checker Option'
,
join
(
' '
, @{
$CONFIG
->{checker_options} } );
warn
sprintf
"%25s: %s$/"
,
'Updater Option'
,
join
(
' '
, @{
$CONFIG
->{updater_options} } );
warn
sprintf
"%25s: %s$/"
,
'Config File'
,
$CONFIG
->{cfg_file};
}
}
sub
_load_yaml {
my
$file
=
shift
;
open
my
$fh
,
'<'
,
$file
or carp($!),
return
;
my
$got
=
do
{
local
$/; <
$fh
>};
close
$fh
;
YAML::Load(
$got
);
}
sub
recover_config_file {
*STDERR
->autoflush;
my
$f
=
$CONFIG
->{cfg_file};
do
{
warn
"$f not exist...ABORT!!"
;
exit
; }
unless
-f
$f
;
my
$c
= _load_yaml(
$f
);
my
$count
;
for
(
keys
%{
$c
->{SKIP} } ) {
if
(
$c
->{SKIP}->{
$_
}->{fail_at} =~ /^(?:UNKNOWN|\?)$/ ) {
delete
$c
->{SKIP}->{
$_
};
$count
++;
}
}
if
(
$count
) {
print
STDERR
"Backup $f...$/"
;
File::Copy::copy(
$f
,
$f
.
'-BACKUP'
)
or
die
"Can't rename $f to -BACKUP"
;
print
STDERR
"Saving $f..."
;
eval
{ YAML::DumpFile
$f
,
$c
}
or
die
"$/Can't save config-file: $f: $@"
;
print
STDERR
"Done!!(purged $count entries)"
;
}
else
{
print
STDERR
"$f: up-to-date."
;
}
}
sub
load_config {
$CONFIG
->{user_setting}
= -r
$CONFIG
->{cfg_file} ? _load_yaml(
$CONFIG
->{cfg_file} ) : {};
}
my
$FH_ORG_STDERR
;
open
$FH_ORG_STDERR
,
'>&STDERR'
;
$FH_ORG_STDERR
->autoflush;
close
STDERR
unless
WIN32;
local
$SIG
{__WARN__} =
sub
{
*STDERR
=
$FH_ORG_STDERR
;
CORE::
warn
(
@_
);
};
my
%outdated
;
my
%added
;
my
$pr_colored
=
sub
{
my
$color
=
join
' '
,
@_
;
return
sub
{
print
colored(
join
( $,,
@_
),
$color
);
};
};
sub
pr_black;
sub
pr_red;
sub
pr_green;
sub
pr_yellow;
sub
pr_blue;
sub
pr_magenta;
sub
pr_cyan;
sub
pr_white;
{
no
strict
'refs'
;
for
my
$color
(
qw/red green yellow blue magenta cyan white /
) {
*{ __PACKAGE__ .
'::pr_'
.
$color
} =
$pr_colored
->(
'bold'
,
$color
);
}
*{ __PACKAGE__ .
'::pr_black'
} =
sub
{
my
$tail
=
pop
@_
;
my
$nl
=
chomp
(
$tail
) ? $/ :
''
;
print
color(
'black on_white'
),
@_
,
$tail
;
print
color(
'reset'
),
$nl
;
};
}
my
$skip_entries
;
my
(
$fn
,
$fc
,
$fl
,
$fs
);
(
$fn
,
$fc
,
$fl
) =
qw/32 10 10/
;
$fs
=
$screenX
- ( 2 * 3 + 1 ) -
$fn
-
$fc
-
$fl
;
my
$output_format_3
=
sprintf
qq{%%%d.%ds %%%d.%ds %%%d.%ds$/}
,
(
$fn
) x 2, (
$fc
) x 2, (
$fl
) x 2;
my
$output_format_4
=
sprintf
qq{%%%d.%ds %%%d.%ds %%%d.%ds %%%d.%ds$/}
,
(
$fn
) x 2, (
$fc
) x 2, (
$fl
) x 2, (
$fs
) x 2;
my
$output_format_fold_head
=
"%s$/"
;
my
$fold_mod
= Foldize->new(
width
=>
$fn
,
delimiter
=>
"::"
);
sub
make_table_row {
my
$e
=
shift
;
my
$r
=
''
;
my
(
$mod
,
$current
,
$new
,
$phase
);
$mod
=
$fold_mod
->parse(
$e
->{module} );
$current
=
$e
->{current};
$new
=
$e
->{new};
$phase
=
$e
->{fail_at};
while
(
$mod
->
length
> 1 ) {
$r
.=
sprintf
(
$output_format_fold_head
,
$mod
->get );
}
my
$format
=
$phase
?
$output_format_4
:
$output_format_3
;
$r
.=
sprintf
(
$format
,
$mod
->get,
$current
=>
$new
,
$phase
);
return
$r
;
}
sub
show_skips {
pr_black
qq|>>> Show FAILED Modules...$/|
;
my
$skips
=
$CONFIG
->{user_setting}->{SKIP};
my
@mods
;
@mods
=
sort
{
$a
->{module} cmp
$b
->{module} }
map
{
my
@a
= @{
$skips
->{
$_
}->{modules} };
my
$f
=
$skips
->{
$_
}->{fail_at};
$_
->{fail_at} =
$f
for
@a
;
@a
}
keys
%$skips
;
pr_cyan
sprintf
(
$output_format_4
,
'Name'
,
'Current'
,
'Latest'
,
'Fail at...'
);
print
make_table_row(
$_
)
for
@mods
;
}
PHASE_1:
sub
setup_checker {
my
@checkers
=
@_
;
my
@avail_checkers
=
grep
{
$_
}
map
{ File::Which::which(
$_
) }
@checkers
;
die
"Cannot find CPAN-update-checker(@checkers)"
unless
@avail_checkers
;
my
$checker
;
for
my
$c
(
@avail_checkers
) {
system
"perl -wc $c >"
. File::Spec->devnull .
" 2>&1"
;
next
if
$?;
$checker
=
$c
;
last
;
}
die
"No CPAN-update-checker is avail"
unless
$checker
;
$CONFIG
->{checker} = _build_pipecmd(
$checker
,
qw/--verbose/
,
@{
$CONFIG
->{checker_options} } );
}
my
@skipped
;
my
$num_of_upgrade
;
(
$fn
,
$fc
,
$fl
) =
qw/32 10 10/
;
$fs
=
$screenX
- ( 2 * 3 + 1 ) -
$fn
-
$fc
-
$fl
;
sub
_load_outdated_from {
my
$file
= file(
$LOAD
);
do
{
warn
"$file not exist...ABORT!!"
;
exit
; }
unless
-f
$file
;
print
"Loading update list from "
.
$file
.
" ..."
;
%outdated
= %{ _load_yaml(
$file
) };
$num_of_upgrade
=
scalar
keys
%outdated
;
print
" done."
. $/;
}
my
$GUARD_SELF_UPGRADE
;
sub
_process_outdated {
my
(
$line
) =
@_
;
chomp
$line
;
my
(
$mod
,
$current
,
$new
,
$file
) =
split
/\s+/,
$line
;
$file
=~ s{([^/]+/){2}}{};
my
(
$dist_name
,
$dist_version
) =
$file
=~ m
$dist_name
=~ s
print
STDERR
"$file:Can't determine FILENAME"
unless
$dist_name
;
$dist_version
= version->parse(
$dist_version
);
$current
= version->parse(
$current
);
$new
= version->parse(
$new
);
my
$info
= +{
module
=>
$mod
,
current
=>
$current
,
new
=>
$new
};
if
(
$dist_name
eq
'App-ucpan'
&& WIN32) {
$info
->{fail_at} =
"self-upgrade"
;
pr_red make_table_row(
$info
);
$GUARD_SELF_UPGRADE
= <<
"EOM"
;
You cannot upgrade yourself from App::ucpan on your
system
.
Please run
"cpanm App::ucpan"
instead.
EOM
return
;
}
if
(
my
$old
=
$skip_entries
->{
$dist_name
} ) {
$info
->{fail_at} =
$skip_entries
->{
$dist_name
}->{fail_at} ||
'?'
;
if
(
$IGNORE_SKIP
or
$old
->{version} <
$dist_version
) {
delete
$skip_entries
->{
$dist_name
};
}
unless
(
$IGNORE_SKIP
) {
print
make_table_row(
$info
);
return
;
}
}
$outdated
{
$dist_name
} //= +{
file
=>
$file
,
modules
=> [],
version
=>
$dist_version
->numify,
};
$num_of_upgrade
++
unless
@{
$outdated
{
$dist_name
}->{modules} };
push
@{
$outdated
{
$dist_name
}->{modules} },
{
module
=>
$mod
,
current
=>
$current
->numify,
new
=>
$new
->numify,
};
pr_yellow make_table_row(
$info
);
return
;
}
sub
run_checker {
$skip_entries
=
$CONFIG
->{user_setting}->{SKIP};
pr_black
qq|>>> Checking Outdated Modules...$/|
;
pr_cyan
sprintf
(
$output_format_4
,
'Name'
,
'Current'
,
'Latest'
,
'Fail at...'
);
my
$start_time
=
time
;
my
$checker_abort_ok
;
if
(
my
$pid
=
open
my
$pipe
,
'-|'
,
join
(
' '
,
map
(
qq{"$_"}
, @{
$CONFIG
->{checker} },
'2>&1'
) ) )
{
while
(<
$pipe
>) {
_process_outdated(
$_
);
if
(
$DEBUG
{maxitem} &&
keys
(
%outdated
) >=
$DEBUG
{maxitem} ) {
kill
TERM
=>
$pid
;
waitpid
$pid
, 0;
$checker_abort_ok
= 1;
last
;
}
}
close
$pipe
;
if
( !
$checker_abort_ok
) {
my
$child_status
= $? >> 8;
if
(
$child_status
) {
warn
"checker returned status $child_status: abort!!"
;
exit
$child_status
;
}
waitpid
$pid
, 0;
}
my
$elapsed
=
time
-
$start_time
;
pr_spent_time(
$elapsed
);
}
elsif
( !
defined
$pid
) {
die
"$CONFIG->{checker} start FAILED!!"
;
}
if
( !
%outdated
) {
say
$/,
q|--- Nothing to upgrade ---|
;
pr_red
$GUARD_SELF_UPGRADE
if
$GUARD_SELF_UPGRADE
;
return
;
}
return
1;
}
PHASE_2:
my
$count_of_upgrade
;
my
$total_upgrade
;
my
$total_added
;
my
%pr
= (
HEADER
=> \
&pr_cyan
,
NOTE
=> \
&pr_yellow
,
FAIL
=> \
&pr_red
,
SUCCESS
=> \
&pr_green
,
INIT
=> \
&pr_yellow
,
FETCH
=> \
&pr_magenta
,
CONFIG
=> \
&pr_magenta
,
BUILD
=> \
&pr_magenta
,
TEST
=> \
&pr_magenta
,
INSTALL
=> \
&pr_magenta
,
IN_PROGRESS
=> \
&pr_magenta
,
WARN
=> \
&pr_magenta
,
DEFAULT
=>
sub
{
print
@_
; },
);
sub
pr {
my
(
$phase
,
@args
) =
@_
;
my
$sub
=
$pr
{
$phase
};
return
$sub
->(
@args
)
if
$sub
;
$pr
{DEFAULT}->(
@args
);
}
my
$state
= +{};
sub
run_cpanm {
my
$ORG_STDERR
= \
*STDERR
;
my
$ispace
=
' '
x 2;
my
$org_m
;
my
$cpanm_file
= File::Which::which(
'cpanm'
);
$cpanm_file
=~ s/\\/\//g;
if
(WIN32) {
no
warnings
'once'
;
*App::cpanminus::script::system
=
sub
{
my
$cmd
=
shift
;
$cmd
.=
' 2>&1'
;
CORE::
system
$cmd
;
};
*CORE::GLOBAL::symlink
=
sub
{
my
(
$org
,
$dest
) =
@_
;
return
1
unless
(
$org
||
$dest
);
(
$org
,
$dest
) =
map
file(
$_
)->stringify,
$org
,
$dest
;
my
$flag
=
''
;
if
( -d
$org
) {
$flag
=
'/J'
;
rmdir
$dest
;
}
!
system
qq{mklink $flag "$dest" "$org" >NUL}
;
};
}
eval
qq{require '$cpanm_file'}
;
my
$app
= App::cpanminus::script->new;
$app
->parse_options( @{
$CONFIG
->{updater_options} },
undef
);
pop
@{
$app
->{argv} };
{
no
strict
'refs'
;
$org_m
= +{
map
{
$_
=> \&{
"App::cpanminus::script::"
.
$_
} }
qw/_diag install_module fetch_module configure build test install/
};
}
my
$pid
;
no
warnings
'once'
;
if
(WIN32) {
*App::cpanminus::script::run_timeout
=
sub
{
my
(
$self
,
$cmd
,
$timeout
) =
@_
;
$cmd
=
$self
->shell_quote(
@$cmd
)
if
ref
$cmd
eq
'ARRAY'
;
my
$cmd_wrap
=
$cmd
.
' >> '
.
$self
->shell_quote(
$self
->{
log
} ) .
' 2>&1'
;
my
(
$pid
,
$pipe
,
$exit_code
);
local
$SIG
{ALRM} =
sub
{
CORE::
die
"alarm\n"
;
};
eval
{
$pid
=
system
1,
$cmd_wrap
;
alarm
$timeout
;
waitpid
$pid
, 0;
$exit_code
= $?;
alarm
0;
};
if
( $@ && $@ eq
"alarm\n"
) {
pr_progress(
$state
);
local
$STDERR
=
$ORG_STDERR
;
$self
->diag_fail(
"Timed out (> ${timeout}s). Use --verbose to retry."
);
CORE::
kill
-KILL
=>
$pid
;
return
;
}
return
!
$exit_code
;
};
}
my
$diag_msg
;
*App::cpanminus::script::_diag
=
sub
{
my
(
$self
,
$m
,
$a
,
$e
) =
@_
;
$state
->{fail} = (
$state
->{phase} ||
'N/A'
)
if
$e
;
if
(
$m
=~ /^! Timed out/ ) {
$state
->{phase} .=
"(Timeout)"
;
}
};
*App::cpanminus::script::install_module
=
sub
{
my
(
$self
,
$m
,
$d
,
$v
) =
@_
;
return
1
if
$self
->{seen}{
$m
};
my
(
$dist
,
$mod
,
$ver
,
$file
)
= @{
$self
->resolve_name(
$m
) }
{
qw/dist module module_version pathname/
};
my
(
$target
,
@mods
);
if
( !
$outdated
{
$dist
} ) {
(
$file
) =
$file
=~ m
$added
{
$dist
} = +{
file
=>
$file
,
version
=>
$ver
,
modules
=> [
+{
module
=>
$mod
,
current
=>
undef
,
new
=>
$ver
,
}
],
};
@mods
= (
$mod
);
$target
= \
$added
{
$dist
};
}
else
{
@mods
=
map
$_
->{module}, @{
$outdated
{
$dist
}->{modules} };
$count_of_upgrade
++;
$target
= \
$outdated
{
$dist
};
}
$state
= +{
prev
=>
$state
,
depth
=>
$d
,
curr
=>
$dist
,
};
if
( (
$d
|| 0 ) > (
$state
->{prev}{depth} || 0 ) ) {
unless
(
$state
->{prev}{in}{
$d
}++ ) {
$state
->{prev}{dependency}++;
pr_progress(
$state
->{prev} );
pr(
IN_PROGRESS
=>
"Dependency found!"
. $/ );
}
}
elsif
( (
$d
|| 0 ) < (
$state
->{prev}{depth} || 0 ) ) {
pr(
DEFAULT
=> $/ );
}
pr(
DEFAULT
=>
$ispace
x
$state
->{depth} );
pr(
HEADER
=>
$dist
);
pr(
NOTE
=>
' ['
,
join
(
', '
,
@mods
),
']'
)
if
@mods
;
pr(
NOTE
=>
sprintf
(
qq{ (%d/%d)}
,
$count_of_upgrade
,
$num_of_upgrade
) )
if
!
$state
->{depth};
pr(
DEFAULT
=> $/ );
my
$elapse_one
;
my
$res
=
do
{
$elapse_one
=
time
;
my
$r
= &{
$org_m
->{install_module} };
$elapse_one
=
time
() -
$elapse_one
;
$r
;
};
if
(
$res
) {
if
(
$diag_msg
=~ /up to date/i ) {
$$target
->{status} = 1;
$total_upgrade
++;
pr(
SUCCESS
=>
$ispace
x
$state
->{depth}
.
"Up to date"
. $/ );
}
elsif
(
$$target
and !
$$target
->{fail_at} ) {
$$target
->{status} = 1;
$$target
->{time_required} =
$elapse_one
;
$total_upgrade
++;
pr_progress(
$state
);
pr(
SUCCESS
=>
"SUCCESS"
);
pr(
DEFAULT
=>
"($elapse_one sec)"
. $/ );
}
elsif
(
$$target
) {
pr( (
$$target
->{status} ?
'SUCCESS'
:
'FAIL'
) =>
$ispace
x
$state
->{depth} .
"Already tried"
. $/ );
}
if
( !
$outdated
{
$dist
} ) {
my
$t
=
delete
$added
{
$dist
};
$t
->{status} = 1;
$t
->{time_required} =
$elapse_one
;
$outdated
{
$dist
} = +{
%$t
};
$total_upgrade
--;
$total_added
++;
}
}
else
{
$$target
->{fail_at}
=
$state
->{dependency} ?
'Dependency'
:
$state
->{fail};
$$target
->{time_required} =
$elapse_one
;
$outdated
{
$dist
} ||=
delete
$added
{
$dist
};
if
(
$state
->{in}{
$d
+ 1 } ) {
$state
->{progress_prev} =
undef
;
pr(
DEFAULT
=>
$ispace
x
$state
->{depth} );
pr(
DEFAULT
=>
'--> '
.
$dist
.
'..'
);
}
else
{
pr_progress(
$state
);
}
pr(
FAIL
=>
"Timeout!!.."
)
if
$state
->{fail} =~ /timeout/i;
pr(
FAIL
=>
"FAIL"
);
pr(
DEFAULT
=>
"($elapse_one sec)"
. $/ );
}
$state
=
$state
->{prev};
return
$res
;
};
*App::cpanminus::script::fetch_module
=
sub
{
$state
->{phase} =
"Fetch"
;
pr(
FETCH
=>
$ispace
x
$state
->{depth},
$state
->{progress_prev} =
"Fetch.."
);
goto
&{
$org_m
->{fetch_module} };
};
*App::cpanminus::script::configure
=
sub
{
if
(
$state
->{in} ) {
$state
->{in} =
$state
->{progress_prev} =
undef
;
pr(
DEFAULT
=>
$ispace
x
$state
->{depth},
'-->'
);
pr(
HEADER
=>
"[$state->{curr}]"
);
}
pr_progress(
$state
);
pr(
CONFIG
=>
$state
->{progress_prev} =
"Configure.."
);
$state
->{phase} =
"Configure"
;
goto
&{
$org_m
->{configure} };
};
*App::cpanminus::script::build
=
sub
{
if
(
$state
->{in} ) {
$state
->{in} =
$state
->{progress_prev} =
undef
;
pr(
DEFAULT
=>
$ispace
x
$state
->{depth},
'-->'
);
pr(
HEADER
=>
"[$state->{curr}]"
);
}
pr_progress(
$state
);
pr(
BUILD
=>
$state
->{progress_prev} =
"Build.."
);
$state
->{phase} =
"Build"
;
goto
&{
$org_m
->{build} };
};
*App::cpanminus::script::test
=
sub
{
pr_progress(
$state
);
pr(
TEST
=>
$state
->{progress_prev} =
"Test.."
);
$state
->{phase} =
"Test"
;
goto
&{
$org_m
->{test} };
};
*App::cpanminus::script::install
=
sub
{
pr_progress(
$state
);
pr(
INSTALL
=>
$state
->{progress_prev} =
"Install.."
);
$state
->{phase} =
"Install"
;
goto
&{
$org_m
->{install} };
};
for
my
$method
(
qw/setup_home init_tools configure_mirrors/
) {
$app
->${method};
}
for
my
$method
(
qw/setup_home init_tools configure_mirrors/
) {
no
strict
'refs'
;
no
warnings
'redefine'
;
*{
'App::cpanminus::script::'
.
$method
} =
sub
{ };
}
local
$ENV
{HARNESS_OPTIONS} =
"j$DEBUG{jobs}"
if
$DEBUG
{jobs};
pr_black
qq|>>> Upgrading outdated modules$/|
;
my
@outdated
=
sort
{
lc
(
$a
) cmp
lc
(
$b
) }
keys
%outdated
;
my
$start_time
=
time
;
for
my
$key
(
@outdated
) {
my
$file
=
$outdated
{
$key
}->{file};
$state
= +{
curr
=>
$file
,
depth
=> 0 };
push
@{
$app
->{argv} },
$file
;
$app
->doit;
pop
@{
$app
->{argv} };
pr(
DEFAULT
=> $/ );
}
my
$elapsed
=
time
-
$start_time
;
pr_spent_time(
$elapsed
);
}
PHASE_3:
sub
show_result {
my
@success
=
map
{ @{
$_
->{modules} } }
delete
(
@outdated
{
grep
{
$outdated
{
$_
}->{status} }
keys
%outdated
} );
pr_black $/ .
qq{**************** SUMMARY ****************}
. $/;
if
(
@success
) {
pr_green
qq|Upgrade Success|
.
q|-|
x 50 . $/;
printf
$output_format_3
,
"Name"
,
"Current"
,
"Latest"
;
for
my
$data
(
@success
) {
my
(
$mod
,
$cur
,
$new
) = @{
$data
}{
qw/module current new/
};
$cur
||=
'~'
;
pr_green make_table_row(
+{
module
=>
$mod
,
current
=>
$cur
,
new
=>
$new
} );
}
$CONFIG
->{user_setting}->{INSTALLED} = \
@success
;
}
my
$total_fail
;
if
(
%outdated
= (
%outdated
,
%added
) ) {
pr_red
qq|Fail to upgrade|
.
q|-|
x 50 . $/;
printf
$output_format_4
,
"Name"
,
"Current"
,
"Latest"
,
"Fail at..."
;
for
my
$fail_mod
(
keys
%outdated
) {
$outdated
{
$fail_mod
}->{fail_at} //=
'UNKNOWN'
;
my
@od
= @{
$outdated
{
$fail_mod
}->{modules} || [] };
my
$info
= {};
$info
->{fail_at} =
$outdated
{
$fail_mod
}->{fail_at};
$total_fail
+=
@od
;
for
my
$mod
(
@od
) {
@{
$info
}{
qw/module current new/
}
= @{
$mod
}{
qw/module current new/
};
$info
->{current} //=
'~'
;
pr_red make_table_row(
$info
);
}
$CONFIG
->{user_setting}->{SKIP}->{
$fail_mod
}
=
$outdated
{
$fail_mod
};
}
}
print
$/;
pr_green
$total_upgrade
,
$total_upgrade
> 1 ?
" modules"
:
" module"
,
" upgraded."
, $/
if
$total_upgrade
;
pr_green
$total_added
,
$total_added
> 1 ?
" modules"
:
" module"
,
" added."
, $/
if
$total_added
;
pr_red
$total_fail
,
" module"
,
$total_fail
> 1 ?
"s"
:
""
,
" FAILURE."
, $/
if
$total_fail
;
say
qq|$/All Done.|
;
}
sub
main {
process_options();
_setup_debugger()
if
$DEBUG
{enable};
recover_config_file(),
exit
()
if
$RECOVER
;
load_config();
show_skips(),
exit
()
if
$SHOW_SKIPS
;
setup_checker(
qw/cpan-outdated/
);
if
(
$LOAD
) {
_load_outdated_from(
$LOAD
);
}
else
{
my
$status
= run_checker();
pr_red
$GUARD_SELF_UPGRADE
if
$CHECK_ONLY
&&
$GUARD_SELF_UPGRADE
;
exit
if
$CHECK_ONLY
|| !
$status
;
}
run_cpanm() && show_result();
pr_red
$GUARD_SELF_UPGRADE
if
$GUARD_SELF_UPGRADE
;
YAML::DumpFile( file(
$DUMP
), \
%outdated
)
if
$DUMP
;
save_config();
exit
;
}
&main
()
unless
caller
;
sub
pr_spent_time {
return
unless
@_
;
my
(
$elapsed
) =
@_
;
my
$str
=
' '
. (
$elapsed
+ 0 ) .
'sec.'
;
printf
(
'%*.*s'
, (
$screenX
-
length
(
$str
) - 2 ) x 2 ,
''
);
pr_black
$str
. $/;
}
sub
pr_progress {
my
(
$state
) =
@_
;
return
unless
my
$prev
=
$state
->{progress_prev};
pr(
DEFAULT
=> (
"\b"
x
length
$prev
) .
$prev
);
}
sub
save_config {
my
$file
= file(
$CONFIG
->{cfg_file});
$file
->parent->mkpath()
unless
-f
$file
->parent;
YAML::DumpFile(
$file
,
$CONFIG
->{user_setting} );
}
sub
_build_pipecmd {
return
[ $^X,
'-e'
,
"\$|=1;do '"
.
shift
.
"';"
,
"--"
,
@_
];
}
{
my
$DEF_WIDTH
= 80;
my
$DEF_DELIM
=
' '
;
sub
new {
my
$class
=
shift
;
my
%args
;
if
(
$_
[0] .
""
eq
'HASH'
) {
%args
= %{
$_
[0] };
}
else
{
%args
=
@_
;
}
$args
{width} //=
$DEF_WIDTH
;
$args
{delimiter} //=
$args
{delim} ||
$DEF_DELIM
;
$args
{delimiter_width} =
length
$args
{delimiter};
bless
\
%args
,
$class
;
}
sub
parse {
my
$self
=
shift
;
my
$width
=
$self
->{width};
my
$delim
=
$self
->{delimiter};
my
$delim_width
=
$self
->{delimiter_width};
my
(
$line
) =
@_
;
if
(
length
(
$line
) <=
$width
) {
$self
->{_pool} = [
$line
];
$self
->{_length} = 1;
}
else
{
my
@pool
;
my
@chunks
=
split
$delim
,
$line
;
$line
=
""
;
for
my
$chunk
(
@chunks
) {
while
(
length
(
$line
) >
$width
) {
push
@pool
,
substr
(
$line
, 0,
$width
- 1 ) .
'-'
;
$line
=
substr
(
$line
,
$width
- 1 );
}
if
(
length
(
$line
) ) {
if
(
length
(
$line
) +
length
(
$chunk
) +
$delim_width
* 2
>
$width
)
{
push
@pool
,
$line
;
$line
=
$chunk
.
$delim
;
}
else
{
$line
.=
$chunk
.
$delim
;
}
}
else
{
$line
=
$chunk
;
}
}
$line
=~ s/::$//;
while
(
length
(
$line
) >
$width
) {
push
@pool
,
substr
(
$line
, 0,
$width
- 1 ) .
'-'
;
$line
=
substr
(
$line
,
$width
- 1 );
}
push
@pool
,
$line
if
$line
ne
""
;
$self
->{_pool} = [
@pool
];
$self
->{_length} =
@pool
+ 0;
}
$self
;
}
sub
length
{
my
$self
=
shift
;
$self
->{_length};
}
sub
get {
my
$self
=
shift
;
$self
->{_length} ||
return
;
my
$value
=
shift
@{
$self
->{_pool} };
$self
->{_length} = @{
$self
->{_pool} } + 0;
$value
;
}
}
Hide Show 332 lines of Pod