| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #  You may distribute under the terms of either the GNU General Public License | 
| 2 |  |  |  |  |  |  | #  or the Artistic License (the same terms as Perl itself) | 
| 3 |  |  |  |  |  |  | # | 
| 4 |  |  |  |  |  |  | #  (C) Paul Evans, 2020-2022 -- leonerd@leonerd.org.uk | 
| 5 |  |  |  |  |  |  |  | 
| 6 | 1 |  |  | 1 |  | 739 | use v5.26; | 
|  | 1 |  |  |  |  | 4 |  | 
| 7 | 1 |  |  | 1 |  | 755 | use Object::Pad 0.73 ':experimental(init_expr)'; | 
|  | 1 |  |  |  |  | 11198 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | package App::eachperl 0.08; | 
| 10 |  |  |  |  |  |  | class App::eachperl; | 
| 11 |  |  |  |  |  |  |  | 
| 12 | 1 |  |  | 1 |  | 961 | use Config::Tiny; | 
|  | 1 |  |  |  |  | 1262 |  | 
|  | 1 |  |  |  |  | 37 |  | 
| 13 | 1 |  |  | 1 |  | 516 | use Syntax::Keyword::Dynamically; | 
|  | 1 |  |  |  |  | 734 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 14 |  |  |  |  |  |  |  | 
| 15 | 1 |  |  | 1 |  | 596 | use Commandable::Finder::MethodAttributes ':attrs'; | 
|  | 1 |  |  |  |  | 65698 |  | 
|  | 1 |  |  |  |  | 11 |  | 
| 16 | 1 |  |  | 1 |  | 2600 | use Commandable::Invocation; | 
|  | 1 |  |  |  |  | 779 |  | 
|  | 1 |  |  |  |  | 40 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 | 1 |  |  | 1 |  | 484 | use IO::Term::Status; | 
|  | 1 |  |  |  |  | 1184 |  | 
|  | 1 |  |  |  |  | 52 |  | 
| 19 | 1 |  |  | 1 |  | 1120 | use IPC::Run (); | 
|  | 1 |  |  |  |  | 37350 |  | 
|  | 1 |  |  |  |  | 40 |  | 
| 20 | 1 |  |  | 1 |  | 8 | use String::Tagged 0.17; | 
|  | 1 |  |  |  |  | 41 |  | 
|  | 1 |  |  |  |  | 45 |  | 
| 21 | 1 |  |  | 1 |  | 493 | use Convert::Color::XTerm 0.06; | 
|  | 1 |  |  |  |  | 3053 |  | 
|  | 1 |  |  |  |  | 171 |  | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | my $RESET = "\e[m"; | 
| 24 |  |  |  |  |  |  | my $BOLD  = "\e[1m"; | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | my %COL = ( | 
| 27 |  |  |  |  |  |  | ( map { $_ => Convert::Color->new( "vga:$_" ) } qw( red blue green ) ), | 
| 28 |  |  |  |  |  |  | grey => Convert::Color->new( "xterm:grey(70%)" ), | 
| 29 |  |  |  |  |  |  | ); | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | # Allow conversion of signal numbers into names | 
| 32 | 1 |  |  | 1 |  | 7 | use Config; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 456 |  | 
| 33 |  |  |  |  |  |  | my @SIGNAMES = split m/\s+/, $Config{sig_name}; | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | =head1 NAME | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | C - a wrapper script for iterating multiple F binaries | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  | $ eachperl exec -E 'say "Hello"' | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | --- perl5.30.0 --- | 
| 44 |  |  |  |  |  |  | Hello | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | --- bleadperl --- | 
| 47 |  |  |  |  |  |  | Hello | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | ---------- | 
| 50 |  |  |  |  |  |  | perl5.30.0          : 0 | 
| 51 |  |  |  |  |  |  | bleadperl           : 0 | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 54 |  |  |  |  |  |  |  | 
| 55 |  |  |  |  |  |  | For more detail see the manpage for the eachperl(1) script. | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | =cut | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | field $_perls; | 
| 60 |  |  |  |  |  |  | field $_no_system_perl :param; | 
| 61 |  |  |  |  |  |  | field $_no_test        :param; | 
| 62 |  |  |  |  |  |  | field $_since_version  :param; | 
| 63 |  |  |  |  |  |  | field $_until_version  :param; | 
| 64 |  |  |  |  |  |  | field $_use_devel      :param; | 
| 65 |  |  |  |  |  |  | field $_only_if        :param; | 
| 66 |  |  |  |  |  |  | field $_reverse        :param; | 
| 67 |  |  |  |  |  |  | field $_stop_on_fail   :param; | 
| 68 |  |  |  |  |  |  |  | 
| 69 |  |  |  |  |  |  | field $_io_term = IO::Term::Status->new_for_stdout; | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | class App::eachperl::_Perl { | 
| 72 | 0 |  |  | 0 |  |  | field $name         :param :reader; | 
|  | 0 |  |  |  |  |  |  | 
| 73 | 0 |  |  | 0 |  |  | field $fullpath     :param :reader; | 
|  | 0 |  |  |  |  |  |  | 
| 74 | 0 |  |  | 0 |  |  | field $version      :param :reader; | 
|  | 0 |  |  |  |  |  |  | 
| 75 | 0 |  |  | 0 |  |  | field $is_threads   :param :reader; | 
|  | 0 |  |  |  |  |  |  | 
| 76 | 0 |  |  | 0 |  |  | field $is_debugging :param :reader; | 
|  | 0 |  |  |  |  |  |  | 
| 77 | 0 |  |  | 0 |  |  | field $is_devel     :param :reader; | 
|  | 0 |  |  |  |  |  |  | 
| 78 | 0 |  |  | 0 |  |  | field $selected            :mutator; | 
|  | 0 |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | ADJUST | 
| 82 |  |  |  |  |  |  | { | 
| 83 |  |  |  |  |  |  | $self->maybe_apply_config( "./.eachperlrc" ); | 
| 84 |  |  |  |  |  |  | $self->maybe_apply_config( "$ENV{HOME}/.eachperlrc" ); | 
| 85 |  |  |  |  |  |  | $self->postprocess_config; | 
| 86 |  |  |  |  |  |  | } | 
| 87 |  |  |  |  |  |  |  | 
| 88 | 0 |  |  |  |  |  | method maybe_apply_config ( $path ) | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 89 | 0 |  |  | 0 | 0 |  | { | 
| 90 |  |  |  |  |  |  | # Only accept files readable and owned by UID | 
| 91 | 0 | 0 |  |  |  |  | return unless -r $path; | 
| 92 | 0 | 0 |  |  |  |  | return unless -o _; | 
| 93 |  |  |  |  |  |  |  | 
| 94 | 0 |  |  |  |  |  | my $config = Config::Tiny->read( $path ); | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 0 |  | 0 |  |  |  | $_perls         //= $config->{_}{perls}; | 
| 97 | 0 |  | 0 |  |  |  | $_since_version //= $config->{_}{since_version}; | 
| 98 | 0 |  | 0 |  |  |  | $_until_version //= $config->{_}{until_version}; | 
| 99 | 0 |  | 0 |  |  |  | $_only_if       //= $config->{_}{only_if}; | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 0 |  |  |  |  |  | method postprocess_config () | 
|  | 0 |  |  |  |  |  |  | 
| 103 | 0 |  |  | 0 | 0 |  | { | 
| 104 | 0 |  |  |  |  |  | foreach ( $_since_version, $_until_version ) { | 
| 105 | 0 | 0 |  |  |  |  | defined $_ or next; | 
| 106 | 0 | 0 |  |  |  |  | m/^v/ or $_ = "v$_"; | 
| 107 |  |  |  |  |  |  | # E.g. --until 5.14 means until the /end/ of the 5.14 series; so 5.14.999 | 
| 108 | 0 | 0 | 0 |  |  |  | $_ .= ".999" if \$_ == \$_until_version and $_ !~ m/\.\d+\./; | 
| 109 | 0 |  |  |  |  |  | $_ = version->parse( $_ ); | 
| 110 |  |  |  |  |  |  | } | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 0 | 0 |  |  |  |  | if( my $perlnames = $_perls ) { | 
| 113 | 0 |  |  |  |  |  | $_perls = \my @perls; | 
| 114 | 0 |  |  |  |  |  | foreach my $perl ( split m/\s+/, $perlnames ) { | 
| 115 | 0 |  |  |  |  |  | chomp( my $fullpath = `which $perl` ); | 
| 116 | 0 | 0 |  |  |  |  | $? and warn( "Can't find perl at $perl" ), next; | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 0 |  |  |  |  |  | my ( $ver, $usethreads, $ccflags ) = split m/\n/, | 
| 119 |  |  |  |  |  |  | scalar `$fullpath -MConfig -e 'print "\$]\\n\$Config{usethreads}\\n\$Config{ccflags}\\n"'`; | 
| 120 |  |  |  |  |  |  |  | 
| 121 | 0 |  |  |  |  |  | $ver = version->parse( $ver )->normal; | 
| 122 | 0 |  |  |  |  |  | my $threads = ( $usethreads eq "define" ); | 
| 123 | 0 |  |  |  |  |  | my $debug = $ccflags =~ m/-DDEBUGGING\b/; | 
| 124 | 0 |  |  |  |  |  | my $devel = ( $ver =~ m/^v\d+\.(\d+)/ )[0] % 2; | 
| 125 |  |  |  |  |  |  |  | 
| 126 | 0 |  |  |  |  |  | push @perls, App::eachperl::_Perl->new( | 
| 127 |  |  |  |  |  |  | name         => $perl, | 
| 128 |  |  |  |  |  |  | fullpath     => $fullpath, | 
| 129 |  |  |  |  |  |  | version      => $ver, | 
| 130 |  |  |  |  |  |  | is_threads   => $threads, | 
| 131 |  |  |  |  |  |  | is_debugging => $debug, | 
| 132 |  |  |  |  |  |  | is_devel     => $devel, | 
| 133 |  |  |  |  |  |  | ); | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  | } | 
| 137 |  |  |  |  |  |  |  | 
| 138 | 0 |  |  |  |  |  | method perls () | 
|  | 0 |  |  |  |  |  |  | 
| 139 | 0 |  |  | 0 | 0 |  | { | 
| 140 | 0 |  |  |  |  |  | my @perls = @$_perls; | 
| 141 | 0 | 0 |  |  |  |  | @perls = reverse @perls if $_reverse; | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | return map { | 
| 144 | 0 |  |  |  |  |  | my $perl = $_; | 
|  | 0 |  |  |  |  |  |  | 
| 145 | 0 |  |  |  |  |  | my $ver = $perl->version; | 
| 146 |  |  |  |  |  |  |  | 
| 147 | 0 |  |  |  |  |  | my $selected = 1; | 
| 148 | 0 | 0 | 0 |  |  |  | $selected = 0 if $_since_version and $ver lt $_since_version; | 
| 149 | 0 | 0 | 0 |  |  |  | $selected = 0 if $_until_version and $ver gt $_until_version; | 
| 150 | 0 | 0 | 0 |  |  |  | $selected = 0 if $_no_system_perl and $perl->fullpath eq $^X; | 
| 151 | 0 | 0 | 0 |  |  |  | $selected = 0 if defined $_use_devel and $perl->is_devel ^ $_use_devel; | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 0 | 0 | 0 |  |  |  | if( $selected and defined $_only_if ) { | 
| 154 | 0 | 0 |  |  |  |  | IPC::Run::run( | 
| 155 |  |  |  |  |  |  | [ $perl->fullpath, "-Mstrict", "-Mwarnings", "-MConfig", | 
| 156 |  |  |  |  |  |  | "-e", "exit !do {$_only_if}" ] | 
| 157 |  |  |  |  |  |  | ) == 0 and $selected = 0; | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  |  | 
| 160 | 0 |  |  |  |  |  | $perl->selected = $selected; | 
| 161 |  |  |  |  |  |  |  | 
| 162 | 0 |  |  |  |  |  | $perl; | 
| 163 |  |  |  |  |  |  | } @perls; | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 | 0 |  |  |  |  |  | method run ( @argv ) | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 167 | 0 |  |  | 0 | 0 |  | { | 
| 168 | 0 | 0 |  |  |  |  | if( $argv[0] =~ m/^-/ ) { | 
| 169 | 0 |  |  |  |  |  | unshift @argv, "exec"; | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 | 0 |  |  |  |  |  | return Commandable::Finder::MethodAttributes->new( object => $self ) | 
| 173 |  |  |  |  |  |  | ->find_and_invoke( Commandable::Invocation->new_from_tokens( @argv ) ); | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | method command_list | 
| 177 |  |  |  |  |  |  | :Command_description("List the available perls") | 
| 178 | 0 |  |  |  |  | 0 | () | 
|  | 0 |  |  |  |  | 0 |  | 
| 179 | 0 |  |  | 0 | 0 | 0 | { | 
| 180 | 0 |  |  |  |  | 0 | foreach my $perl ( $self->perls ) { | 
| 181 | 0 |  |  |  |  | 0 | my @flags; | 
| 182 | 0 |  |  |  |  | 0 | push @flags, $perl->version; | 
| 183 | 0 | 0 |  |  |  | 0 | push @flags, "threads"   if $perl->is_threads; | 
| 184 | 0 | 0 |  |  |  | 0 | push @flags, "DEBUGGING" if $perl->is_debugging; | 
| 185 | 0 | 0 |  |  |  | 0 | push @flags, "devel"     if $perl->is_devel; | 
| 186 |  |  |  |  |  |  |  | 
| 187 | 0 | 0 |  |  |  | 0 | printf "%s%s: %s (%s)\n", | 
| 188 |  |  |  |  |  |  | ( $perl->selected ? "* " : "  " ), | 
| 189 |  |  |  |  |  |  | $perl->name, $perl->fullpath, join( ",", @flags ), | 
| 190 |  |  |  |  |  |  | ; | 
| 191 |  |  |  |  |  |  | } | 
| 192 | 0 |  |  |  |  | 0 | return 0; | 
| 193 | 1 |  |  | 1 |  | 2665 | } | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 7 |  | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 0 |  |  |  |  |  | method exec ( @argv ) | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 196 | 0 |  |  | 0 | 0 |  | { | 
| 197 | 0 | 0 | 0 |  |  |  | my %opts = %{ shift @argv } if @argv and ref $argv[0] eq "HASH"; | 
|  | 0 |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  |  | 
| 199 | 0 |  |  |  |  |  | my @results; | 
| 200 | 0 |  |  |  |  |  | my $ok = 1; | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 0 |  |  |  |  |  | my $signal; | 
| 203 |  |  |  |  |  |  |  | 
| 204 | 0 |  |  |  |  |  | my @perls = $self->perls; | 
| 205 | 0 |  |  |  |  |  | my $idx = 0; | 
| 206 | 0 |  |  |  |  |  | foreach ( @perls ) { | 
| 207 | 0 |  |  |  |  |  | $idx++; | 
| 208 | 0 | 0 |  |  |  |  | next unless $_->selected; | 
| 209 |  |  |  |  |  |  |  | 
| 210 | 0 |  |  |  |  |  | my $perl = $_->name; | 
| 211 | 0 |  |  |  |  |  | my $path = $_->fullpath; | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | my @status = ( | 
| 214 |  |  |  |  |  |  | ( $ok | 
| 215 |  |  |  |  |  |  | ? String::Tagged->new_tagged( "-OK-", fg => $COL{grey} ) | 
| 216 |  |  |  |  |  |  | : String::Tagged->new_tagged( "FAIL", fg => $COL{red} ) ), | 
| 217 |  |  |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | String::Tagged->new | 
| 219 |  |  |  |  |  |  | ->append( "Running " ) | 
| 220 |  |  |  |  |  |  | ->append_tagged( $perl, bold => 1 ), | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | ( $idx < @perls | 
| 223 |  |  |  |  |  |  | ? String::Tagged->new_tagged( sprintf( "(%d more)", @perls - $idx ), fg => $COL{grey} ) | 
| 224 | 0 | 0 |  |  |  |  | : () ), | 
|  |  | 0 |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | ); | 
| 226 |  |  |  |  |  |  |  | 
| 227 | 0 |  |  |  |  |  | $_io_term->set_status( | 
| 228 |  |  |  |  |  |  | String::Tagged->join( " | ", @status ) | 
| 229 |  |  |  |  |  |  | ->apply_tag( 0, -1, bg => Convert::Color->new( "vga:blue" ) ) | 
| 230 |  |  |  |  |  |  | ); | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | $opts{oneline} | 
| 233 | 0 | 0 |  |  |  |  | ? $_io_term->more_partial( "$BOLD$perl:$RESET " ) | 
| 234 |  |  |  |  |  |  | : $_io_term->print_line( "\n$BOLD  --- $perl --- $RESET" ); | 
| 235 |  |  |  |  |  |  |  | 
| 236 | 0 |  |  |  |  |  | my $has_partial = $opts{oneline}; | 
| 237 |  |  |  |  |  |  | IPC::Run::run [ $path, @argv ], ">pty>", sub { | 
| 238 | 0 |  |  | 0 |  |  | my @lines = split m/\r?\n/, $_[0], -1; | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 0 | 0 |  |  |  |  | if( $has_partial ) { | 
| 241 | 0 |  |  |  |  |  | my $line = shift @lines; | 
| 242 |  |  |  |  |  |  |  | 
| 243 | 0 | 0 |  |  |  |  | if( $line =~ s/^\r// ) { | 
| 244 | 0 |  |  |  |  |  | $_io_term->replace_partial( $line ); | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  | else { | 
| 247 | 0 |  |  |  |  |  | $_io_term->more_partial( $line ); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 0 | 0 |  |  |  |  | if( @lines ) { | 
| 251 | 0 |  |  |  |  |  | $_io_term->finish_partial; | 
| 252 | 0 |  |  |  |  |  | $has_partial = 0; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | # Final element will be empty string if it ended in a newline | 
| 257 | 0 |  |  |  |  |  | my $partial = pop @lines; | 
| 258 |  |  |  |  |  |  |  | 
| 259 | 0 |  |  |  |  |  | $_io_term->print_line( $_ ) for @lines; | 
| 260 |  |  |  |  |  |  |  | 
| 261 | 0 | 0 |  |  |  |  | if( length $partial ) { | 
| 262 | 0 |  |  |  |  |  | $_io_term->more_partial( $partial ); | 
| 263 | 0 |  |  |  |  |  | $has_partial = 1; | 
| 264 |  |  |  |  |  |  | } | 
| 265 | 0 |  |  |  |  |  | }; | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 0 | 0 |  |  |  |  | if( $has_partial ) { | 
| 268 | 0 |  |  |  |  |  | $_io_term->finish_partial; | 
| 269 |  |  |  |  |  |  | } | 
| 270 |  |  |  |  |  |  |  | 
| 271 | 0 | 0 |  |  |  |  | if( $? & 127 ) { | 
| 272 |  |  |  |  |  |  | # Exited via signal | 
| 273 | 0 |  |  |  |  |  | $signal = $?; | 
| 274 | 0 |  |  |  |  |  | push @results, [ $perl => "aborted on SIG$SIGNAMES[ $? ]" ]; | 
| 275 | 0 |  |  |  |  |  | last; | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  | else { | 
| 278 | 0 |  |  |  |  |  | push @results, [ $perl => $? >> 8 ]; | 
| 279 | 0 | 0 | 0 |  |  |  | last if $? and $_stop_on_fail; | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  |  | 
| 282 | 0 | 0 |  |  |  |  | $ok = 0 if $?; | 
| 283 |  |  |  |  |  |  | } | 
| 284 |  |  |  |  |  |  |  | 
| 285 | 0 |  |  |  |  |  | $_io_term->set_status( "" ); | 
| 286 |  |  |  |  |  |  |  | 
| 287 | 0 | 0 |  |  |  |  | unless( $opts{no_summary} ) { | 
| 288 | 0 |  |  |  |  |  | $_io_term->print_line( "\n----------" ); | 
| 289 | 0 |  |  |  |  |  | $_io_term->print_line( sprintf "%-20s: %s", @$_ ) for @results; | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  |  | 
| 292 | 0 | 0 |  |  |  |  | kill $signal, $$ if $signal; | 
| 293 | 0 |  |  |  |  |  | return 0; | 
| 294 |  |  |  |  |  |  | } | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | method command_exec | 
| 297 |  |  |  |  |  |  | :Command_description("Execute a given command on each selected perl") | 
| 298 |  |  |  |  |  |  | :Command_arg("argv...", "commandline arguments") | 
| 299 | 0 |  |  |  |  | 0 | ( $argv ) | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 300 | 0 |  |  | 0 | 0 | 0 | { | 
| 301 | 0 |  |  |  |  | 0 | return $self->exec( @$argv ); | 
| 302 | 1 |  |  | 1 |  | 1812 | } | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 303 |  |  |  |  |  |  |  | 
| 304 | 0 |  |  |  |  |  | method cpan ( $e, @argv ) | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 305 | 0 |  |  | 0 | 0 |  | { | 
| 306 | 0 |  |  |  |  |  | return $self->exec( "-MCPAN", "-e", $e, @argv ); | 
| 307 |  |  |  |  |  |  | } | 
| 308 |  |  |  |  |  |  |  | 
| 309 | 0 |  |  |  |  |  | method invoke_local ( %opts ) | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 310 | 0 |  |  | 0 | 0 |  | { | 
| 311 | 0 |  |  |  |  |  | my $perl = ""; | 
| 312 | 0 |  |  |  |  |  | my @args; | 
| 313 |  |  |  |  |  |  |  | 
| 314 | 0 | 0 |  |  |  |  | if( -r "Build.PL" ) { | 
|  |  | 0 |  |  |  |  |  | 
| 315 | 0 |  |  |  |  |  | $perl .= <<'EOPERL'; | 
| 316 |  |  |  |  |  |  | system( $^X, "Build.PL" ) == 0 and | 
| 317 |  |  |  |  |  |  | system( $^X, "Build", "clean" ) == 0 and | 
| 318 |  |  |  |  |  |  | system( $^X, "Build" ) == 0 | 
| 319 |  |  |  |  |  |  | EOPERL | 
| 320 | 0 | 0 |  |  |  |  | $perl .= ' and system( $^X, "Build", "test" ) == 0'    if $opts{test}; | 
| 321 | 0 | 0 |  |  |  |  | $perl .= ' and system( $^X, "Build", "install" ) == 0' if $opts{install}; | 
| 322 |  |  |  |  |  |  | } | 
| 323 |  |  |  |  |  |  | elsif( -r "Makefile.PL" ) { | 
| 324 | 0 |  |  |  |  |  | $perl .= <<'EOPERL'; | 
| 325 |  |  |  |  |  |  | system( $^X, "Makefile.PL" ) == 0 and | 
| 326 |  |  |  |  |  |  | system( "make" ) == 0 | 
| 327 |  |  |  |  |  |  | EOPERL | 
| 328 | 0 | 0 |  |  |  |  | $perl .= ' and system( "make", "test" ) == 0'    if $opts{test}; | 
| 329 | 0 | 0 |  |  |  |  | $perl .= ' and system( "make", "install" ) == 0' if $opts{install}; | 
| 330 |  |  |  |  |  |  | } | 
| 331 |  |  |  |  |  |  | else { | 
| 332 | 0 |  |  |  |  |  | die "TODO: Work out how to locally control dist when lacking Build.PL or Makefile.PL"; | 
| 333 |  |  |  |  |  |  | } | 
| 334 |  |  |  |  |  |  |  | 
| 335 | 0 | 0 |  |  |  |  | $perl .= ' and system( $^X, @ARGV ) == 0', push @args, "--", @{$opts{perl}} if $opts{perl}; | 
|  | 0 |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  |  | 
| 337 | 0 |  |  |  |  |  | return $self->exec( "-e", $perl . <<'EOPERL', @args); | 
| 338 |  |  |  |  |  |  | and print "-- PASS -\n" or print "-- FAIL --\n"; | 
| 339 |  |  |  |  |  |  | kill $?, $$ if $? & 127; | 
| 340 |  |  |  |  |  |  | exit +($? >> 8); | 
| 341 |  |  |  |  |  |  | EOPERL | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | method command_install | 
| 345 |  |  |  |  |  |  | :Command_description("Installs a given module") | 
| 346 |  |  |  |  |  |  | :Command_arg("module", "name of the module (or \".\" for current directory)") | 
| 347 | 0 |  |  |  |  | 0 | ( $module ) | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 348 | 0 |  |  | 0 | 0 | 0 | { | 
| 349 | 0 |  |  |  |  | 0 | dynamically $_no_system_perl = 1; | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 0 | 0 |  |  |  | 0 | return $self->command_install_local if $module eq "."; | 
| 352 | 0 |  |  |  |  | 0 | return $self->cpan( 'CPAN::Shell->install($ARGV[0])', $module ); | 
| 353 | 1 |  |  | 1 |  | 1111 | } | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | method command_install_local | 
| 356 |  |  |  |  |  |  | :Command_description("Installs a module from the current directory") | 
| 357 | 0 |  |  |  |  | 0 | () | 
|  | 0 |  |  |  |  | 0 |  | 
| 358 | 0 |  |  | 0 | 0 | 0 | { | 
| 359 | 0 |  |  |  |  | 0 | $self->invoke_local( test => !$_no_test, install => 1 ); | 
| 360 | 1 |  |  | 1 |  | 514 | } | 
|  | 1 |  |  |  |  | 13 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 361 |  |  |  |  |  |  |  | 
| 362 |  |  |  |  |  |  | method command_test | 
| 363 |  |  |  |  |  |  | :Command_description("Tests a given module") | 
| 364 |  |  |  |  |  |  | :Command_arg("module", "name of the module (or \".\" for current directory)") | 
| 365 | 0 |  |  |  |  | 0 | ( $module ) | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 366 | 0 |  |  | 0 | 0 | 0 | { | 
| 367 | 0 | 0 |  |  |  | 0 | return $self->command_test_local if $module eq "."; | 
| 368 | 0 |  |  |  |  | 0 | return $self->cpan( 'CPAN::Shell->test($ARGV[0])', $module ); | 
| 369 | 1 |  |  | 1 |  | 468 | } | 
|  | 1 |  |  |  |  | 23 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | method command_test_local | 
| 372 |  |  |  |  |  |  | :Command_description("Tests a module from the current directory") | 
| 373 | 0 |  |  |  |  | 0 | () | 
|  | 0 |  |  |  |  | 0 |  | 
| 374 | 0 |  |  | 0 | 0 | 0 | { | 
| 375 | 0 |  |  |  |  | 0 | $self->invoke_local( test => 1 ); | 
| 376 | 1 |  |  | 1 |  | 538 | } | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | method command_build_then_perl | 
| 379 |  |  |  |  |  |  | :Command_description("Build the module in the current directory then run a perl command") | 
| 380 |  |  |  |  |  |  | :Command_arg("argv...", "commandline arguments") | 
| 381 | 0 |  |  |  |  | 0 | ( $argv ) | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 382 | 0 |  |  | 0 | 0 | 0 | { | 
| 383 | 0 |  |  |  |  | 0 | $self->invoke_local( test => !$_no_test, perl => [ @$argv ] ); | 
| 384 | 1 |  |  | 1 |  | 417 | } | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | method command_modversion | 
| 387 |  |  |  |  |  |  | :Command_description("Print the installed module version") | 
| 388 |  |  |  |  |  |  | :Command_arg("module", "name of the module") | 
| 389 | 0 |  |  |  |  | 0 | ( $module ) | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 390 | 0 |  |  | 0 | 0 | 0 | { | 
| 391 | 0 |  |  |  |  | 0 | return $self->exec( | 
| 392 |  |  |  |  |  |  | { oneline => 1, no_summary => 1 }, | 
| 393 |  |  |  |  |  |  | "-M$module", "-e", "print ${module}\->VERSION, qq(\\n);" | 
| 394 |  |  |  |  |  |  | ); | 
| 395 | 1 |  |  | 1 |  | 563 | } | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | method command_modpath | 
| 398 |  |  |  |  |  |  | :Command_description("Print the installed module path") | 
| 399 |  |  |  |  |  |  | :Command_arg("module", "name of the module") | 
| 400 | 0 |  |  |  |  |  | ( $module ) | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 401 | 0 |  |  | 0 | 0 |  | { | 
| 402 | 0 |  |  |  |  |  | ( my $filename = "$module.pm" ) =~ s{::}{/}g; | 
| 403 |  |  |  |  |  |  |  | 
| 404 | 0 |  |  |  |  |  | return $self->exec( | 
| 405 |  |  |  |  |  |  | { oneline => 1, no_summary => 1 }, | 
| 406 |  |  |  |  |  |  | "-M$module", "-e", "print \$INC{qq($filename)}, qq(\\n);" | 
| 407 |  |  |  |  |  |  | ); | 
| 408 | 1 |  |  | 1 |  | 610 | } | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 9 |  | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | =head1 AUTHOR | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | Paul Evans | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | =cut | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | 0x55AA; |