| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package App::pmuninstall; | 
| 2 | 1 |  |  | 1 |  | 21551 | use strict; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 34 |  | 
| 3 | 1 |  |  | 1 |  | 4 | use warnings; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 28 |  | 
| 4 | 1 |  |  | 1 |  | 5 | use File::Spec; | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 29 |  | 
| 5 | 1 |  |  | 1 |  | 6 | use File::Basename qw(dirname); | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 109 |  | 
| 6 | 1 |  |  | 1 |  | 1282 | use Getopt::Long qw(GetOptions :config bundling); | 
|  | 1 |  |  |  |  | 13301 |  | 
|  | 1 |  |  |  |  | 6 |  | 
| 7 | 1 |  |  | 1 |  | 244 | use Config; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 31 |  | 
| 8 | 1 |  |  | 1 |  | 723 | use YAML (); | 
|  | 1 |  |  |  |  | 7738 |  | 
|  | 1 |  |  |  |  | 21 |  | 
| 9 | 1 |  |  | 1 |  | 819 | use CPAN::DistnameInfo; | 
|  | 1 |  |  |  |  | 928 |  | 
|  | 1 |  |  |  |  | 27 |  | 
| 10 | 1 |  |  | 1 |  | 725 | use version; | 
|  | 1 |  |  |  |  | 2330 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 11 | 1 |  |  | 1 |  | 1150 | use HTTP::Tiny; | 
|  | 1 |  |  |  |  | 58259 |  | 
|  | 1 |  |  |  |  | 45 |  | 
| 12 | 1 |  |  | 1 |  | 1244 | use Term::ANSIColor qw(colored); | 
|  | 1 |  |  |  |  | 8473 |  | 
|  | 1 |  |  |  |  | 467 |  | 
| 13 | 1 |  |  | 1 |  | 10 | use Cwd (); | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 18 |  | 
| 14 | 1 |  |  | 1 |  | 1296 | use JSON::PP qw(decode_json); | 
|  | 1 |  |  |  |  | 13057 |  | 
|  | 1 |  |  |  |  | 3587 |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | our $VERSION = "0.30"; | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | my $perl_version     = version->new($])->numify; | 
| 19 |  |  |  |  |  |  | my $depended_on_by   = 'http://deps.cpantesters.org/depended-on-by.pl?dist='; | 
| 20 |  |  |  |  |  |  | my $cpanmetadb       = 'http://cpanmetadb.plackperl.org/v1.0/package'; | 
| 21 |  |  |  |  |  |  | my @core_modules_dir = do { my %h; grep !$h{$_}++, @Config{qw/archlib archlibexp privlib privlibexp/} }; | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | $ENV{ANSI_COLORS_DISABLED} = 1 if $^O eq 'MSWin32'; | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | our $OUTPUT_INDENT_LEVEL = 0; | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | sub new { | 
| 28 | 0 |  |  | 0 | 0 |  | my ($class, $inc) = @_; | 
| 29 | 0 | 0 |  |  |  |  | $inc = [@INC] unless ref $inc eq 'ARRAY'; | 
| 30 | 0 |  |  |  |  |  | bless { | 
| 31 |  |  |  |  |  |  | check_deps => 1, | 
| 32 |  |  |  |  |  |  | verbose    => 0, | 
| 33 |  |  |  |  |  |  | inc        => $class->prepare_include_paths($inc), | 
| 34 |  |  |  |  |  |  | }, $class; | 
| 35 |  |  |  |  |  |  | } | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | sub run { | 
| 38 | 0 |  |  | 0 | 0 |  | my ($self, @args) = @_; | 
| 39 | 0 |  |  |  |  |  | local @ARGV = @args; | 
| 40 |  |  |  |  |  |  | GetOptions( | 
| 41 |  |  |  |  |  |  | 'f|force'                 => \$self->{force}, | 
| 42 | 0 |  |  | 0 |  |  | 'v|verbose!'              => sub { ++$self->{verbose} }, | 
| 43 |  |  |  |  |  |  | 'c|checkdeps!'            => \$self->{check_deps}, | 
| 44 | 0 |  |  | 0 |  |  | 'n|no-checkdeps!'         => sub { $self->{check_deps} = 0 }, | 
| 45 |  |  |  |  |  |  | 'q|quiet!'                => \$self->{quiet}, | 
| 46 | 0 |  |  | 0 |  |  | 'h|help!'                 => sub { $self->usage }, | 
| 47 |  |  |  |  |  |  | 'V|version!'              => \$self->{version}, | 
| 48 |  |  |  |  |  |  | 'l|local-lib=s'           => \$self->{local_lib}, | 
| 49 |  |  |  |  |  |  | 'L|local-lib-contained=s' => sub { | 
| 50 | 0 |  |  | 0 |  |  | $self->{local_lib}      = $_[1]; | 
| 51 | 0 |  |  |  |  |  | $self->{self_contained} = 1; | 
| 52 |  |  |  |  |  |  | }, | 
| 53 | 0 | 0 |  |  |  |  | ) or $self->usage; | 
| 54 |  |  |  |  |  |  |  | 
| 55 | 0 | 0 |  |  |  |  | if ($self->{version}) { | 
| 56 | 0 |  |  |  |  |  | $self->puts("pm-uninstall (App::pmuninstall) version $App::pmuninstall::VERSION"); | 
| 57 | 0 |  |  |  |  |  | exit; | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 0 | 0 |  |  |  |  | $self->short_usage unless @ARGV; | 
| 61 |  |  |  |  |  |  |  | 
| 62 | 0 |  |  |  |  |  | $self->uninstall(@ARGV); | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | sub uninstall { | 
| 66 | 0 |  |  | 0 | 0 |  | my ($self, @modules) = @_; | 
| 67 |  |  |  |  |  |  |  | 
| 68 | 0 |  |  |  |  |  | $self->setup_local_lib; | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 0 |  |  |  |  |  | my $uninstalled = 0; | 
| 71 | 0 |  |  |  |  |  | for my $module (@modules) { | 
| 72 | 0 | 0 |  |  |  |  | $self->puts("--> Working on $module") unless $self->{quiet}; | 
| 73 | 0 |  |  |  |  |  | my ($packlist, $dist, $vname) = $self->find_packlist($module); | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 0 |  |  |  |  |  | $packlist = File::Spec->catfile($packlist); | 
| 76 | 0 | 0 |  |  |  |  | if ($self->is_core_module($module, $packlist)) { | 
| 77 | 0 |  |  |  |  |  | $self->puts(colored ['red'], "! $module is a core module!! Can't be uninstalled."); | 
| 78 | 0 | 0 |  |  |  |  | $self->puts unless $self->{quiet}; | 
| 79 | 0 |  |  |  |  |  | next; | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 0 | 0 |  |  |  |  | unless ($dist) { | 
| 83 | 0 |  |  |  |  |  | $self->puts(colored ['red'], "! $module not found."); | 
| 84 | 0 | 0 |  |  |  |  | $self->puts unless $self->{quiet}; | 
| 85 | 0 |  |  |  |  |  | next; | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 0 | 0 |  |  |  |  | unless ($packlist) { | 
| 89 | 0 |  |  |  |  |  | $self->puts(colored ['red'], "! $module is not installed."); | 
| 90 | 0 | 0 |  |  |  |  | $self->puts unless $self->{quiet}; | 
| 91 | 0 |  |  |  |  |  | next; | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  |  | 
| 94 | 0 | 0 |  |  |  |  | if ($self->ask_permission($module, $dist, $vname, $packlist)) { | 
| 95 | 0 | 0 |  |  |  |  | if ($self->uninstall_from_packlist($packlist)) { | 
| 96 | 0 |  |  |  |  |  | $self->puts(colored ['green'], "Successfully uninstalled $module"); | 
| 97 | 0 |  |  |  |  |  | ++$uninstalled; | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  | else { | 
| 100 | 0 |  |  |  |  |  | $self->puts(colored ['red'], "! Failed to uninstall $module"); | 
| 101 |  |  |  |  |  |  | } | 
| 102 | 0 | 0 |  |  |  |  | $self->puts unless $self->{quiet}; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  |  | 
| 106 | 0 | 0 |  |  |  |  | if ($uninstalled) { | 
| 107 | 0 | 0 |  |  |  |  | $self->puts if $self->{quiet}; | 
| 108 | 0 |  |  |  |  |  | $self->puts("You may want to rebuild man(1) entries. Try `mandb -c` if needed"); | 
| 109 |  |  |  |  |  |  | } | 
| 110 |  |  |  |  |  |  |  | 
| 111 | 0 |  |  |  |  |  | return $uninstalled; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | sub uninstall_from_packlist { | 
| 115 | 0 |  |  | 0 | 0 |  | my ($self, $packlist) = @_; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 0 |  |  |  |  |  | my $inc = { | 
| 118 | 0 |  |  |  |  |  | map { File::Spec->catfile($_) => 1 } @{$self->{inc}} | 
|  | 0 |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | }; | 
| 120 |  |  |  |  |  |  |  | 
| 121 | 0 |  |  |  |  |  | my $failed; | 
| 122 | 0 |  |  |  |  |  | for my $file ($self->fixup_packlist($packlist)) { | 
| 123 | 0 |  |  |  |  |  | chomp $file; | 
| 124 | 0 | 0 |  |  |  |  | $self->puts(-f $file ? 'unlink   ' : 'not found', " : $file") if $self->{verbose}; | 
|  |  | 0 |  |  |  |  |  | 
| 125 | 0 | 0 | 0 |  |  |  | unlink $file or $self->puts("$file: $!") and $failed++; | 
| 126 | 0 |  |  |  |  |  | $self->rm_empty_dir_from_file($file, $inc); | 
| 127 |  |  |  |  |  |  | } | 
| 128 | 0 | 0 |  |  |  |  | $self->puts("unlink    : $packlist") if $self->{verbose}; | 
| 129 | 0 | 0 | 0 |  |  |  | unlink $packlist or $self->puts("$packlist: $!") and $failed++; | 
| 130 | 0 |  |  |  |  |  | $self->rm_empty_dir_from_file($packlist, $inc); | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 0 | 0 |  |  |  |  | if (my $install_json = $self->{install_json}) { | 
| 133 | 0 | 0 |  |  |  |  | $self->puts("unlink    : $install_json") if $self->{verbose}; | 
| 134 | 0 | 0 | 0 |  |  |  | unlink $install_json or $self->puts("$install_json: $!") and $failed++; | 
| 135 | 0 |  |  |  |  |  | $self->rm_empty_dir_from_file($install_json); | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 | 0 | 0 | 0 |  |  |  | $self->puts unless $self->{quiet} || $self->{force}; | 
| 139 | 0 |  |  |  |  |  | return !$failed; | 
| 140 |  |  |  |  |  |  | } | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | sub rm_empty_dir_from_file { | 
| 143 | 0 |  |  | 0 | 0 |  | my ($self, $file, $inc) = @_; | 
| 144 | 0 |  |  |  |  |  | my $dir = dirname $file; | 
| 145 | 0 | 0 |  |  |  |  | return unless -d $dir; | 
| 146 | 0 | 0 |  |  |  |  | return if $inc->{+File::Spec->catfile($dir)}; | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 0 |  |  |  |  |  | my $failed; | 
| 149 | 0 | 0 |  |  |  |  | if ($self->is_empty_dir($dir)) { | 
| 150 | 0 | 0 |  |  |  |  | $self->puts("rmdir     : $dir") if $self->{verbose}; | 
| 151 | 0 | 0 | 0 |  |  |  | rmdir $dir or $self->puts("$dir: $!") and $failed++; | 
| 152 | 0 |  |  |  |  |  | $self->rm_empty_dir_from_file($dir, $inc); | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 0 |  |  |  |  |  | return !$failed; | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | sub is_empty_dir { | 
| 159 | 0 |  |  | 0 | 0 |  | my ($self, $dir) = @_; | 
| 160 | 0 | 0 |  |  |  |  | opendir my $dh, $dir or die "$dir: $!"; | 
| 161 | 0 |  |  |  |  |  | my @dir = grep !/^\.{1,2}$/, readdir $dh; | 
| 162 | 0 |  |  |  |  |  | closedir $dh; | 
| 163 | 0 | 0 |  |  |  |  | return @dir ? 0 : 1; | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | sub find_packlist { | 
| 167 | 0 |  |  | 0 | 0 |  | my ($self, $module) = @_; | 
| 168 | 0 | 0 |  |  |  |  | $self->puts("Finding $module in your \@INC") if $self->{verbose}; | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | # find with the given name first | 
| 171 | 0 |  |  |  |  |  | (my $try_dist = $module) =~ s!::!-!g; | 
| 172 | 0 | 0 |  |  |  |  | if (my $pl = $self->locate_pack($try_dist)) { | 
| 173 | 0 | 0 |  |  |  |  | $self->puts("-> Found $pl") if $self->{verbose}; | 
| 174 | 0 |  |  |  |  |  | return ($pl, $try_dist); | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 0 | 0 |  |  |  |  | $self->puts("Looking up $module on cpanmetadb") if $self->{verbose}; | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | # map module -> dist and retry | 
| 180 | 0 | 0 |  |  |  |  | my $yaml = $self->fetch("$cpanmetadb/$module") or return; | 
| 181 | 0 |  |  |  |  |  | my $meta = YAML::Load($yaml); | 
| 182 | 0 |  |  |  |  |  | my $info = CPAN::DistnameInfo->new($meta->{distfile}); | 
| 183 |  |  |  |  |  |  |  | 
| 184 | 0 |  | 0 |  |  |  | my $name = $self->find_meta($info->distvname) || $info->dist; | 
| 185 | 0 | 0 |  |  |  |  | if (my $pl = $self->locate_pack($name)) { | 
| 186 | 0 | 0 |  |  |  |  | $self->puts("-> Found $pl") if $self->{verbose}; | 
| 187 | 0 |  |  |  |  |  | return ($pl, $info->dist, $info->distvname); | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | sub find_meta { | 
| 192 | 0 |  |  | 0 | 0 |  | my ($self, $distvname) = @_; | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 0 |  |  |  |  |  | my $name; | 
| 195 | 0 |  |  |  |  |  | for my $lib (@{$self->{inc}}) { | 
|  | 0 |  |  |  |  |  |  | 
| 196 | 0 | 0 |  |  |  |  | next unless $lib =~ /$Config{archname}/; | 
| 197 | 0 |  |  |  |  |  | my $install_json = "$lib/.meta/$distvname/install.json"; | 
| 198 | 0 | 0 | 0 |  |  |  | next unless -f $install_json && -r _; | 
| 199 | 0 |  |  |  |  |  | my $data = decode_json +$self->slurp($install_json); | 
| 200 | 0 |  | 0 |  |  |  | $name = $data->{name} || next; | 
| 201 | 0 | 0 |  |  |  |  | $self->puts("-> Found $install_json") if $self->{verbose}; | 
| 202 | 0 |  |  |  |  |  | $self->{meta} = $install_json; | 
| 203 | 0 |  |  |  |  |  | last; | 
| 204 |  |  |  |  |  |  | } | 
| 205 | 0 |  |  |  |  |  | return $name; | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | sub locate_pack { | 
| 209 | 0 |  |  | 0 | 0 |  | my ($self, $dist) = @_; | 
| 210 | 0 |  |  |  |  |  | $dist =~ s!-!/!g; | 
| 211 | 0 |  |  |  |  |  | for my $lib (@{$self->{inc}}) { | 
|  | 0 |  |  |  |  |  |  | 
| 212 | 0 |  |  |  |  |  | my $packlist = "$lib/auto/$dist/.packlist"; | 
| 213 | 0 | 0 |  |  |  |  | $self->puts("-> Finding .packlist $packlist") if $self->{verbose} > 1; | 
| 214 | 0 | 0 | 0 |  |  |  | return $packlist if -f $packlist && -r _; | 
| 215 |  |  |  |  |  |  | } | 
| 216 | 0 |  |  |  |  |  | return; | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | sub is_core_module { | 
| 220 | 0 |  |  | 0 | 0 |  | my ($self, $dist, $packlist) = @_; | 
| 221 | 0 |  |  |  |  |  | require Module::CoreList; | 
| 222 | 0 | 0 |  |  |  |  | return unless exists $Module::CoreList::version{$perl_version}{$dist}; | 
| 223 | 0 | 0 |  |  |  |  | return 1 unless $packlist; | 
| 224 |  |  |  |  |  |  |  | 
| 225 | 0 |  |  |  |  |  | my $is_core = 0; | 
| 226 | 0 |  |  |  |  |  | for my $dir (@core_modules_dir) { | 
| 227 | 0 |  |  |  |  |  | my $safe_dir = quotemeta $dir; # workaround for MSWin32 | 
| 228 | 0 | 0 |  |  |  |  | if ($packlist =~ /^$safe_dir/) { | 
| 229 | 0 |  |  |  |  |  | $is_core = 1; | 
| 230 | 0 |  |  |  |  |  | last; | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  | } | 
| 233 | 0 |  |  |  |  |  | return $is_core; | 
| 234 |  |  |  |  |  |  | } | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | sub ask_permission { | 
| 237 | 0 |  |  | 0 | 0 |  | my($self, $module, $dist, $vname, $packlist) = @_; | 
| 238 |  |  |  |  |  |  |  | 
| 239 | 0 |  |  |  |  |  | my @deps = $self->find_deps($vname, $module); | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 0 | 0 |  |  |  |  | $self->puts if $self->{verbose}; | 
| 242 | 0 | 0 |  |  |  |  | $self->puts("$module is included in the distribution $dist and contains:\n") | 
| 243 |  |  |  |  |  |  | unless $self->{quiet}; | 
| 244 | 0 |  |  |  |  |  | for my $file ($self->fixup_packlist($packlist)) { | 
| 245 | 0 |  |  |  |  |  | chomp $file; | 
| 246 | 0 | 0 |  |  |  |  | $self->puts("  $file") unless $self->{quiet}; | 
| 247 |  |  |  |  |  |  | } | 
| 248 | 0 | 0 |  |  |  |  | $self->puts unless $self->{quiet}; | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 0 | 0 |  |  |  |  | return 'force uninstall' if $self->{force}; | 
| 251 |  |  |  |  |  |  |  | 
| 252 | 0 |  |  |  |  |  | my $default = 'y'; | 
| 253 | 0 | 0 |  |  |  |  | if (@deps) { | 
| 254 | 0 |  |  |  |  |  | $self->puts("Also, they're depended on by the following installed dists:\n"); | 
| 255 | 0 |  |  |  |  |  | for my $dep (@deps) { | 
| 256 | 0 |  |  |  |  |  | $self->puts("  $dep"); | 
| 257 |  |  |  |  |  |  | } | 
| 258 | 0 |  |  |  |  |  | $self->puts; | 
| 259 | 0 |  |  |  |  |  | $default = 'n'; | 
| 260 |  |  |  |  |  |  | } | 
| 261 |  |  |  |  |  |  |  | 
| 262 | 0 |  |  |  |  |  | return lc($self->prompt("Are you sure you want to uninstall $dist?", $default)) eq 'y'; | 
| 263 |  |  |  |  |  |  | } | 
| 264 |  |  |  |  |  |  |  | 
| 265 |  |  |  |  |  |  | sub find_deps { | 
| 266 | 0 |  |  | 0 | 0 |  | my ($self, $vname, $module) = @_; | 
| 267 |  |  |  |  |  |  |  | 
| 268 | 0 | 0 | 0 |  |  |  | return unless $self->{check_deps} && !$self->{force}; | 
| 269 | 0 | 0 | 0 |  |  |  | $vname ||= $self->vname_for($module) or return; | 
| 270 |  |  |  |  |  |  |  | 
| 271 | 0 | 0 |  |  |  |  | $self->puts("Checking modules depending on $vname") if $self->{verbose}; | 
| 272 | 0 | 0 |  |  |  |  | my $content = $self->fetch("$depended_on_by$vname") or return; | 
| 273 |  |  |  |  |  |  |  | 
| 274 | 0 |  |  |  |  |  | my (@deps, %seen); | 
| 275 | 0 |  |  |  |  |  | for my $dep ($content =~ m|]+>([a-zA-Z0-9_:-]+)|smg) { | 
| 276 | 0 |  |  |  |  |  | $dep =~ s/^\s+|\s+$//smg; # trim | 
| 277 | 0 | 0 |  |  |  |  | next if $seen{$dep}++; | 
| 278 | 0 |  |  |  |  |  | local $OUTPUT_INDENT_LEVEL = $OUTPUT_INDENT_LEVEL + 1; | 
| 279 | 0 | 0 |  |  |  |  | $self->puts("Finding $dep in your \@INC (dependencies)") if $self->{verbose}; | 
| 280 | 0 | 0 |  |  |  |  | push @deps, $dep if $self->locate_pack($dep); | 
| 281 |  |  |  |  |  |  | } | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 0 |  |  |  |  |  | return @deps; | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | sub prompt { | 
| 287 | 0 |  |  | 0 | 0 |  | my ($self, $msg, $default) = @_; | 
| 288 | 0 |  |  |  |  |  | require ExtUtils::MakeMaker; | 
| 289 | 0 |  |  |  |  |  | ExtUtils::MakeMaker::prompt($msg, $default); | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | sub fixup_packlist { | 
| 293 | 0 |  |  | 0 | 0 |  | my ($self, $packlist) = @_; | 
| 294 | 0 |  |  |  |  |  | my @target_list; | 
| 295 | 0 |  |  |  |  |  | my $is_local_lib = $self->is_local_lib($packlist); | 
| 296 | 0 | 0 |  |  |  |  | open my $in, "<", $packlist or die "$packlist: $!"; | 
| 297 | 0 |  |  |  |  |  | while (defined (my $file = <$in>)) { | 
| 298 | 0 | 0 |  |  |  |  | if ($is_local_lib) { | 
| 299 | 0 | 0 |  |  |  |  | next unless $self->is_local_lib($file); | 
| 300 |  |  |  |  |  |  | } | 
| 301 | 0 |  |  |  |  |  | push @target_list, $file; | 
| 302 |  |  |  |  |  |  | } | 
| 303 | 0 |  |  |  |  |  | return @target_list; | 
| 304 |  |  |  |  |  |  | } | 
| 305 |  |  |  |  |  |  |  | 
| 306 |  |  |  |  |  |  | sub is_local_lib { | 
| 307 | 0 |  |  | 0 | 0 |  | my ($self, $file) = @_; | 
| 308 | 0 | 0 |  |  |  |  | return unless $self->{local_lib}; | 
| 309 |  |  |  |  |  |  |  | 
| 310 | 0 |  |  |  |  |  | my $local_lib_base = quotemeta File::Spec->catfile(Cwd::realpath($self->{local_lib})); | 
| 311 | 0 |  |  |  |  |  | $file = File::Spec->catfile($file); | 
| 312 |  |  |  |  |  |  |  | 
| 313 | 0 | 0 |  |  |  |  | return $file =~ /^$local_lib_base/ ? 1 : 0; | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | sub vname_for { | 
| 317 | 0 |  |  | 0 | 0 |  | my ($self, $module) = @_; | 
| 318 |  |  |  |  |  |  |  | 
| 319 | 0 | 0 |  |  |  |  | $self->puts("Fetching $module vname on cpanmetadb") if $self->{verbose}; | 
| 320 | 0 | 0 |  |  |  |  | my $yaml = $self->fetch("$cpanmetadb/$module") or return; | 
| 321 | 0 |  |  |  |  |  | my $meta = YAML::Load($yaml); | 
| 322 | 0 | 0 |  |  |  |  | my $info = CPAN::DistnameInfo->new($meta->{distfile}) or return; | 
| 323 |  |  |  |  |  |  |  | 
| 324 | 0 |  |  |  |  |  | return $info->distvname; | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | # taken from cpan-outdated | 
| 328 |  |  |  |  |  |  | sub setup_local_lib { | 
| 329 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 330 | 0 | 0 |  |  |  |  | return unless $self->{local_lib}; | 
| 331 |  |  |  |  |  |  |  | 
| 332 | 0 | 0 |  |  |  |  | unless (-d $self->{local_lib}) { | 
| 333 | 0 |  |  |  |  |  | $self->puts(colored ['red'], "! $self->{local_lib} : no such directory"); | 
| 334 | 0 |  |  |  |  |  | exit 1; | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  |  | 
| 337 | 0 |  |  | 0 |  |  | local $SIG{__WARN__} = sub { }; # catch 'Attempting to write ...' | 
|  | 0 |  |  |  |  |  |  | 
| 338 | 0 |  |  |  |  |  | $self->{inc} = [ | 
| 339 | 0 |  |  |  |  |  | map { Cwd::realpath($_) } | 
| 340 | 0 |  |  |  |  |  | @{$self->build_active_perl5lib($self->{local_lib}, $self->{self_contained})} | 
| 341 |  |  |  |  |  |  | ]; | 
| 342 | 0 | 0 |  |  |  |  | push @{$self->{inc}}, @INC unless $self->{self_contained}; | 
|  | 0 |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | } | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | sub build_active_perl5lib { | 
| 346 | 0 |  |  | 0 | 0 |  | my ($self, $path, $interpolate) = @_; | 
| 347 | 0 | 0 | 0 |  |  |  | my $perl5libs = [ | 
| 348 |  |  |  |  |  |  | $self->install_base_arch_path($path), | 
| 349 |  |  |  |  |  |  | $self->install_base_perl_path($path), | 
| 350 |  |  |  |  |  |  | $interpolate && $ENV{PERL5LIB} ? $ENV{PERL5LIB} : (), | 
| 351 |  |  |  |  |  |  | ]; | 
| 352 | 0 |  |  |  |  |  | return $perl5libs; | 
| 353 |  |  |  |  |  |  | } | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | sub install_base_perl_path { | 
| 356 | 0 |  |  | 0 | 0 |  | my ($self, $path) = @_; | 
| 357 | 0 |  |  |  |  |  | File::Spec->catdir($path, 'lib', 'perl5'); | 
| 358 |  |  |  |  |  |  | } | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | sub install_base_arch_path { | 
| 361 | 0 |  |  | 0 | 0 |  | my ($self, $path) = @_; | 
| 362 | 0 |  |  |  |  |  | File::Spec->catdir($self->install_base_perl_path($path), $Config{archname}); | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | sub fetch { | 
| 366 | 0 |  |  | 0 | 0 |  | my ($self, $url) = @_; | 
| 367 | 0 | 0 |  |  |  |  | $self->puts("-> Fetching from $url") if $self->{verbose}; | 
| 368 | 0 |  |  |  |  |  | my $res = HTTP::Tiny->new->get($url); | 
| 369 | 0 | 0 |  |  |  |  | return if $res->{status} == 404; | 
| 370 | 0 | 0 |  |  |  |  | die "[$res->{status}] fetch $url failed!!\n" if !$res->{success}; | 
| 371 | 0 |  |  |  |  |  | return $res->{content}; | 
| 372 |  |  |  |  |  |  | } | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | sub slurp { | 
| 375 | 0 |  |  | 0 | 0 |  | my ($self, $file) = @_; | 
| 376 | 0 | 0 |  |  |  |  | open my $fh, '<', $file or die "$file $!"; | 
| 377 | 0 |  |  |  |  |  | do { local $/; <$fh> }; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | } | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | sub puts { | 
| 381 | 0 |  |  | 0 | 0 |  | my ($self, @msg) = @_; | 
| 382 | 0 | 0 |  |  |  |  | push @msg, '' unless @msg; | 
| 383 | 0 | 0 |  |  |  |  | print '  ' x $OUTPUT_INDENT_LEVEL if $OUTPUT_INDENT_LEVEL; | 
| 384 | 0 |  |  |  |  |  | print @msg, "\n"; | 
| 385 |  |  |  |  |  |  | } | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | sub usage { | 
| 388 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 389 | 0 |  |  |  |  |  | $self->puts(<< 'USAGE'); | 
| 390 |  |  |  |  |  |  | Usage: | 
| 391 |  |  |  |  |  |  | pm-uninstall [options] Module [...] | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | options: | 
| 394 |  |  |  |  |  |  | -v,--verbose                  Turns on chatty output | 
| 395 |  |  |  |  |  |  | -f,--force                    Uninstalls without prompts | 
| 396 |  |  |  |  |  |  | -c,--checkdeps                Check dependencies (defaults to on) | 
| 397 |  |  |  |  |  |  | -n,--no-checkdeps             Don't check dependencies | 
| 398 |  |  |  |  |  |  | -q,--quiet                    Suppress some messages | 
| 399 |  |  |  |  |  |  | -h,--help                     This help message | 
| 400 |  |  |  |  |  |  | -V,--version                  Show version | 
| 401 |  |  |  |  |  |  | -l,--local-lib                Additional module path | 
| 402 |  |  |  |  |  |  | -L,--local-lib-contained      Additional module path (don't include non-core modules) | 
| 403 |  |  |  |  |  |  | USAGE | 
| 404 |  |  |  |  |  |  |  | 
| 405 | 0 |  |  |  |  |  | exit 1; | 
| 406 |  |  |  |  |  |  | } | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | sub short_usage { | 
| 409 | 0 |  |  | 0 | 0 |  | my $self = shift; | 
| 410 | 0 |  |  |  |  |  | $self->puts(<< 'USAGE'); | 
| 411 |  |  |  |  |  |  | Usage: pm-uninstall [options] Module [...] | 
| 412 |  |  |  |  |  |  |  | 
| 413 |  |  |  |  |  |  | Try `pm-uninstall --help` or `man pm-uninstall` for more options. | 
| 414 |  |  |  |  |  |  | USAGE | 
| 415 |  |  |  |  |  |  |  | 
| 416 | 0 |  |  |  |  |  | exit 1; | 
| 417 |  |  |  |  |  |  | } | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | sub prepare_include_paths { | 
| 420 | 0 |  |  | 0 | 0 |  | my ($class, $inc) = @_; | 
| 421 | 0 |  |  |  |  |  | my $new_inc = []; | 
| 422 | 0 |  |  |  |  |  | my $archname = quotemeta $Config{archname}; | 
| 423 | 0 |  |  |  |  |  | for my $path (@$inc) { | 
| 424 | 0 |  |  |  |  |  | push @$new_inc, $path; | 
| 425 | 0 | 0 | 0 |  |  |  | next if $path eq '.' or $path =~ /$archname/; | 
| 426 | 0 |  |  |  |  |  | push @$new_inc, File::Spec->catdir($path, $Config{archname}); | 
| 427 |  |  |  |  |  |  | } | 
| 428 | 0 |  |  |  |  |  | return [do { my %h; grep !$h{$_}++, @$new_inc }]; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | } | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | 1; | 
| 432 |  |  |  |  |  |  | __END__ |