• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gugod / App-perlbrew / 5162889645

pending completion
5162889645

Pull #776

github

Pull Request #776: New command: make shim

69 of 69 new or added lines in 3 files covered. (100.0%)

3117 of 3939 relevant lines covered (79.13%)

79.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.82
/lib/App/perlbrew.pm
1
package App::perlbrew;
2
use strict;
68✔
3
use warnings;
68✔
4
use 5.008;
68✔
5
our $VERSION = "1.02";
6
use Config qw( %Config );
68✔
7

8
BEGIN {
9
    # Special treat for Cwd to prevent it to be loaded from somewhere binary-incompatible with system perl.
10
    my @oldinc = @INC;
68✔
11

12
    @INC = (
13
        $Config{sitelibexp} . "/" . $Config{archname},
14
        $Config{sitelibexp}, @Config{qw<vendorlibexp vendorarchexp archlibexp privlibexp>},
68✔
15
    );
16

17
    require Cwd;
68✔
18
    @INC = @oldinc;
68✔
19
}
20

21
use Getopt::Long ();
68✔
22
use CPAN::Perl::Releases ();
68✔
23
use JSON::PP qw( decode_json );
68✔
24
use File::Copy qw( copy move );
68✔
25
use Capture::Tiny ();
68✔
26

27
use App::Perlbrew::Util qw( files_are_the_same uniq find_similar_tokens looks_like_url_of_skaji_relocatable_perl looks_like_sys_would_be_compatible_with_skaji_relocatable_perl make_skaji_relocatable_perl_url prompt );
68✔
28
use App::Perlbrew::Path ();
68✔
29
use App::Perlbrew::Path::Root ();
68✔
30
use App::Perlbrew::HTTP qw( http_download http_get );
68✔
31
use App::Perlbrew::Patchperl qw( maybe_patchperl );
32
use App::Perlbrew::Sys;
33

34
### global variables
35

36
# set $ENV{SHELL} to executable path of parent process (= shell) if it's missing
37
# (e.g. if this script was executed by a daemon started with "service xxx start")
38
# ref: https://github.com/gugod/App-perlbrew/pull/404
39
$ENV{SHELL} ||= App::Perlbrew::Path->new( "/proc", getppid, "exe" )->readlink if -d "/proc";
40

41
local $SIG{__DIE__} = sub {
42
    my $message = shift;
43
    warn $message;
44
    exit(1);
45
};
46

47
our $CONFIG;
48
our $PERLBREW_ROOT;
49
our $PERLBREW_HOME;
50

51
my @flavors = (
52
    {
53
        d_option => 'usethreads',
54
        implies  => 'multi',
55
        common   => 1,
56
        opt      => 'thread|threads'
57
    },    # threads is for backward compatibility
58

59
    {
60
        d_option => 'usemultiplicity',
61
        opt      => 'multi'
62
    },
63

64
    {
65
        d_option => 'uselongdouble',
66
        common   => 1,
67
        opt      => 'ld'
68
    },
69

70
    {
71
        d_option => 'use64bitint',
72
        common   => 1,
73
        opt      => '64int'
74
    },
75

76
    {
77
        d_option => 'use64bitall',
78
        implies  => '64int',
79
        opt      => '64all'
80
    },
81

82
    {
83
        d_option => 'DEBUGGING',
84
        opt      => 'debug'
85
    },
86

87
    {
88
        d_option => 'cc=clang',
89
        opt      => 'clang'
90
    },
91
);
92

93
my %flavor;
94
my $flavor_ix = 0;
95
for (@flavors) {
96
    my ($name) = $_->{opt} =~ /([^|]+)/;
97
    $_->{name}     = $name;
98
    $_->{ix}       = ++$flavor_ix;
99
    $flavor{$name} = $_;
100
}
101
for (@flavors) {
102
    if ( my $implies = $_->{implies} ) {
103
        $flavor{$implies}{implied_by} = $_->{name};
104
    }
105
}
106

107
my %command_aliases = (
231✔
108
    'rm' => 'uninstall',
109
    'delete' => 'uninstall',
231✔
110
);
111

112
sub resolve_command_alias {
113
    my $x = shift;
114
    $command_aliases{$x};
115
}
116

117
### methods
118
sub new {
119
    my ( $class, @argv ) = @_;
120

121
    my %opt = (
122
        original_argv => \@argv,
123
        args          => [],
124
        yes           => 0,
125
        force         => 0,
126
        quiet         => 0,
127
        D             => [],
128
        U             => [],
231✔
129
        A             => [],
130
        sitecustomize => '',
231✔
131
        destdir       => '',
132
        noman         => '',
133
        variation     => '',
134
        both          => [],
155✔
135
        append        => '',
136
        reverse       => 0,
155✔
137
        verbose       => 0,
138
    );
139

140
    $opt{$_} = '' for keys %flavor;
141

142
    if (@argv) {
143

155✔
144
        # build a local @ARGV to allow us to use an older
145
        # Getopt::Long API in case we are building on an older system
155✔
146
        local (@ARGV) = @argv;
147

148
        Getopt::Long::Configure(
155✔
149
            'pass_through',
465✔
150
            'no_ignore_case',
13✔
151
            'bundling',
152
            'permute',    # default behaviour except 'exec'
153
        );
154

155
        $class->parse_cmdline( \%opt );
231✔
156

157
        $opt{args} = \@ARGV;
158

231✔
159
        # fix up the effect of 'bundling'
3✔
160
        foreach my $flags ( @opt{qw(D U A)} ) {
161
            foreach my $value ( @{$flags} ) {
162
                $value =~ s/^=//;
231✔
163
            }
1✔
164
        }
165
    }
166

167
    my $self = bless \%opt, $class;
231✔
168

231✔
169
    # Treat --root option same way as env variable PERLBREW_ROOT (with higher priority)
170
    if ( $opt{root} ) {
231✔
171
        $ENV{PERLBREW_ROOT} = $self->root( $opt{root} );
3✔
172
    }
173

174
    if ( $opt{builddir} ) {
231✔
175
        $self->{builddir} = App::Perlbrew::Path->new( $opt{builddir} );
176
    }
177

178
    # Ensure propagation of $PERLBREW_HOME and $PERLBREW_ROOT
189✔
179
    $self->root;
180
    $self->home;
189✔
181

182
    if ( $self->{verbose} ) {
189✔
183
        $App::Perlbrew::HTTP::HTTP_VERBOSE = 1;
184
    }
185

186
    return $self;
187
}
188

189
sub parse_cmdline {
190
    my ( $self, $params, @ext ) = @_;
191

192
    my @f = map { $flavor{$_}{opt} || $_ } keys %flavor;
193

194
    return Getopt::Long::GetOptions(
195
        $params,
196

197
        'yes',
198
        'force|f',
199
        'reverse',
200
        'notest|n',
201
        'quiet|q',
202
        'verbose|v',
203
        'input|i=s',
204
        'output|o=s',
205
        'as=s',
206
        'append=s',
207
        'help|h',
208
        'version',
209
        'root=s',
210
        'switch',
211
        'all',
212
        'shell=s',
213
        'no-patchperl',
214
        'no-decoration',
215

216
        "builddir=s",
217

218
        # options passed directly to Configure
219
        'D=s@',
220
        'U=s@',
221
        'A=s@',
222

223
        'j=i',
224

225
        # options that affect Configure and customize post-build
226
        'sitecustomize=s',
227
        'destdir=s',
760✔
228
        'noman',
229

230
        # flavors support
231
        'both|b=s@',
232
        'all-variations',
233
        'common-variations',
760✔
234
        @f,
235

760✔
236
        @ext
237
    );
238
}
239

760✔
240
sub sys { App::Perlbrew::Sys:: }
241

242
sub root {
760✔
243
    my ( $self, $new_root ) = @_;
244

760✔
245
    $new_root ||=
246
           $PERLBREW_ROOT
247
        || $ENV{PERLBREW_ROOT}
248
        || App::Perlbrew::Path->new( $ENV{HOME}, "perl5", "perlbrew" )->stringify
795✔
249
        unless $self->{root};
250

251
    $self->{root} = $PERLBREW_ROOT = $new_root
252
        if defined $new_root;
253

254
    $self->{root} = App::Perlbrew::Path::Root->new( $self->{root} )
795✔
255
        unless ref $self->{root};
256

795✔
257
    $self->{root} = App::Perlbrew::Path::Root->new( $self->{root}->stringify )
258
        unless $self->{root}->isa('App::Perlbrew::Path::Root');
259

260
    return $self->{root};
795✔
261
}
262

795✔
263
sub home {
264
    my ( $self, $new_home ) = @_;
265

266
    $new_home ||=
4✔
267
           $PERLBREW_HOME
268
        || $ENV{PERLBREW_HOME}
4✔
269
        || App::Perlbrew::Path->new( $ENV{HOME}, ".perlbrew" )->stringify
270
        unless $self->{home};
271

272
    $self->{home} = $PERLBREW_HOME = $new_home
694✔
273
        if defined $new_home;
694✔
274

694✔
275
    $self->{home} = App::Perlbrew::Path->new( $self->{home} )
276
        unless ref $self->{home};
277

278
    return $self->{home};
509✔
279
}
509✔
280

509✔
281
sub builddir {
282
    my ($self) = @_;
283

284
    return $self->{builddir} || $self->root->build;
4✔
285
}
4✔
286

287
sub current_perl {
288
    my ( $self, $v ) = @_;
289
    $self->{current_perl} = $v if $v;
22✔
290
    return $self->{current_perl} || $self->env('PERLBREW_PERL') || '';
22✔
291
}
22✔
292

2✔
293
sub current_lib {
2✔
294
    my ( $self, $v ) = @_;
2✔
295
    $self->{current_lib} = $v if $v;
296
    return $self->{current_lib} || $self->env('PERLBREW_LIB') || '';
297
}
298

299
sub current_shell_is_bashish {
467✔
300
    my ($self) = @_;
467✔
301
    return ( $self->current_shell eq 'bash' ) || ( $self->current_shell eq 'zsh' );
467✔
302
}
467✔
303

304
sub current_shell {
305
    my ( $self, $x ) = @_;
306
    $self->{current_shell} = $x if $x;
1✔
307
    return $self->{current_shell} ||= do {
1✔
308
        my $shell_name = App::Perlbrew::Path->new( $self->{shell} || $self->env('SHELL') )->basename;
309
        $shell_name =~ s/\d+$//;
1✔
310
        $shell_name;
1✔
311
    };
×
312
}
313

314
sub current_env {
315
    my ($self) = @_;
×
316
    my $l = $self->current_lib;
317
    $l = "@" . $l if $l;
×
318
    return $self->current_perl . $l;
×
319
}
320

×
321
sub installed_perl_executable {
322
    my ( $self, $name ) = @_;
×
323
    die unless $name;
×
324

×
325
    my $executable = $self->root->perls($name)->perl;
×
326
    return $executable if -e $executable;
327
    return "";
328
}
×
329

×
330
sub configure_args {
331
    my ( $self, $name ) = @_;
332

×
333
    my $perl_cmd = $self->installed_perl_executable($name);
334
    my $code     = 'while(($_,$v)=each(%Config)){print"$_ $v" if /config_arg/}';
335

336
    my @output = split "\n" => $self->do_capture( $perl_cmd, '-MConfig', '-wle', $code );
11✔
337

338
    my %arg;
11✔
339
    for (@output) {
340
        my ( $k, $v ) = split " ", $_, 2;
11✔
341
        $arg{$k} = $v;
5✔
342
    }
5✔
343

344
    if (wantarray) {
345
        return map { $arg{"config_arg$_"} } ( 1 .. $arg{config_argc} );
11✔
346
    }
347

348
    return $arg{config_args};
349
}
1,288✔
350

1,288✔
351
sub cpan_mirror {
×
352
    my ( $self, $v ) = @_;
353

354
    $self->{cpan_mirror} = $v if $v;
355

×
356
    unless ( $self->{cpan_mirror} ) {
×
357
        $self->{cpan_mirror} = $self->env("PERLBREW_CPAN_MIRROR") || "https://cpan.metacpan.org";
×
358
        $self->{cpan_mirror} =~ s{/+$}{};
359
    }
360

361
    return $self->{cpan_mirror};
362
}
363

364
sub env {
150✔
365
    my ( $self, $name ) = @_;
150✔
366
    return $ENV{$name} if $name;
367
    return \%ENV;
368
}
369

154✔
370
sub is_shell_csh {
371
    my ($self) = @_;
372
    return 1 if $self->env('SHELL') =~ /(t?csh)/;
154✔
373
    return 0;
374
}
154✔
375

376
# Entry point method: handles all the arguments
377
# and dispatches to an appropriate internal
378
# method to execute the corresponding command.
11✔
379
sub run {
380
    my ($self) = @_;
11✔
381
    $self->run_command( $self->args );
382
}
11✔
383

11✔
384
sub args {
68✔
385
    my ($self) = @_;
11✔
386

387
    # keep 'force' and 'yes' coherent across commands
388
    $self->{force} = $self->{yes} = 1 if ( $self->{force} || $self->{yes} );
11✔
389

1,512✔
390
    return @{ $self->{args} };
455✔
391
}
455✔
392

393
sub commands {
394
    my ($self) = @_;
451✔
395

451✔
396
    my $package = ref $self ? ref $self : $self;
451✔
397

398
    my @commands;
399
    my $symtable = do {
400
        no strict 'refs';
401
        \%{ $package . '::' };
11✔
402
    };
403

404
    foreach my $sym ( keys %$symtable ) {
405
        if ( $sym =~ /^run_command_/ ) {
5✔
406
            my $glob = $symtable->{$sym};
407
            if ( ref($glob) eq 'CODE' || defined *$glob{CODE} ) {
5✔
408

409
                # with perl >= 5.27 stash entry can points to a CV directly
5✔
410
                $sym =~ s/^run_command_//;
411
                $sym =~ s/_/-/g;
412
                push @commands, $sym;
413
            }
414
        }
415
    }
416

417
    return @commands;
418
}
419

420
sub find_similar_commands {
421
    my ( $self, $command ) = @_;
422

423
    $command =~ s/_/-/g;
424

425
    return @{ find_similar_tokens($command, [ sort $self->commands ]) };
426
}
427

428
# This method is called in the 'run' loop
429
# and executes every specific action depending
150✔
430
# on the type of command.
150✔
431
#
432
# The first argument to this method is a self reference,
150✔
433
# while the first "real" argument is the command to execute.
×
434
# Other parameters after the command to execute are
435
# considered as arguments for the command itself.
436
#
2✔
437
# In general the command is executed via a method named after the
2✔
438
# command itself and with the 'run_command' prefix. For instance
439
# the command 'exec' is handled by a method
440
# `run_command_exec`
4✔
441
#
442
# If no candidates can be found, an execption is thrown
443
# and a similar command is shown to the user.
150✔
444
sub run_command {
150✔
445
    my ( $self, $x, @args ) = @_;
22✔
446
    my $command = $x;
22✔
447

448
    if ( $self->{version} ) {
449
        $x = 'version';
150✔
450
    }
2✔
451
    elsif ( !$x ) {
452
        $x    = 'help';
2✔
453
        @args = ( 0, 0 );
×
454
    }
×
455
    elsif ( $x eq 'help' ) {
456
        @args = ( 0, 2 ) unless @args;
457
    }
×
458

459
    my $s = $self->can("run_command_$x");
460
    unless ($s) {
2✔
461
        $x =~ y/-/_/;
462
        $s = $self->can("run_command_$x");
463
    }
464

148✔
465
    unless ($s) {
466
        if (my $x = resolve_command_alias($x)) {
467
            $s = $self->can("run_command_$x")
468
        }
2✔
469
    }
2✔
470

2✔
471
    unless ($s) {
2✔
472
        my @commands = $self->find_similar_commands($x);
473

474
        if ( @commands > 1 ) {
475
            @commands = map { '    ' . $_ } @commands;
476
            die "Unknown command: `$command`. Did you mean one of the following?\n" . join( "\n", @commands ) . "\n";
477
        }
478
        elsif ( @commands == 1 ) {
479
            die "Unknown command: `$command`. Did you mean `$commands[0]`?\n";
480
        }
481
        else {
8✔
482
            die "Unknown command: `$command`. Typo?\n";
483
        }
8✔
484
    }
485

8✔
486
    $self->$s(@args);
3✔
487
}
×
488

489
sub run_command_version {
490
    my ($self)  = @_;
3✔
491
    my $package = ref $self;
3✔
492
    my $version = $self->VERSION;
493
    print "$0  - $package/$version\n";
3✔
494
}
495

496
# Provides help information about a command.
497
# The idea is similar to the 'run_command' and 'run_command_$x' chain:
498
# this method dispatches to a 'run_command_help_$x' method
499
# if found in the class, otherwise it tries to extract the help
500
# documentation via the POD of the class itself using the
3✔
501
# section 'COMMAND: $x' with uppercase $x.
3✔
502
sub run_command_help {
503
    my ( $self, $command, $verbose, $return_text ) = @_;
3✔
504

2✔
505
    require Pod::Usage;
506

507
    if ( $command && !defined($verbose) ) {
3✔
508
        if ( $self->can("run_command_help_$command") ) {
1✔
509
            $self->can("run_command_help_$command")->($self);
1✔
510
        }
511
        else {
512
            my $out = "";
513
            open my $fh, ">", \$out;
5✔
514

515
            if (my $x = resolve_command_alias($command)) {
516
                $command = $x;
517
            }
518

519
            Pod::Usage::pod2usage(
520
                -exitval   => "NOEXIT",
521
                -verbose   => 99,
522
                -sections  => "COMMAND: " . uc($command),
523
                -output    => $fh,
524
                -noperldoc => 1
525
            );
526
            $out =~ s/\A[^\n]+\n//s;
527
            $out =~ s/^    //gm;
528

7✔
529
            if ( $out =~ /\A\s*\Z/ ) {
530
                $out = "Cannot find documentation for '$command'\n\n";
7✔
531
            }
532

533
            return "\n$out" if ($return_text);
7✔
534
            print "\n$out";
×
535
            close $fh;
×
536
        }
537
    }
7✔
538
    else {
7✔
539
        Pod::Usage::pod2usage(
540
            -noperldoc => 1,
7✔
541
            -verbose   => $verbose || 0,
3✔
542
            -exitval   => ( defined $command ? $command : 1 )
543
        );
544
    }
4✔
545
}
4✔
546

4✔
547
# introspection for compgen
4✔
548
my %comp_installed = (
2✔
549
    use    => 1,
550
    switch => 1,
4✔
551
);
552

553
sub run_command_compgen {
554
    my ( $self, $cur, @args ) = @_;
×
555

×
556
    $cur = 0 unless defined($cur);
557

558
    # do `tail -f bashcomp.log` for debugging
559
    if ( $self->env('PERLBREW_DEBUG_COMPLETION') ) {
560
        open my $log, '>>', 'bashcomp.log';
561
        print $log "[$$] $cur of [@args]\n";
562
    }
563
    my $subcommand           = $args[1];
564
    my $subcommand_completed = ( $cur >= 2 );
565

4✔
566
    if ( !$subcommand_completed ) {
4✔
567
        $self->_compgen( $subcommand, $self->commands );
13✔
568
    }
569
    else {    # complete args of a subcommand
4✔
570
        if ( $comp_installed{$subcommand} ) {
571
            if ( $cur <= 2 ) {
572
                my $part;
573
                if ( defined( $part = $args[2] ) ) {
7✔
574
                    $part = qr/ \Q$part\E /xms;
7✔
575
                }
4✔
576
                $self->_compgen( $part, map { $_->{name} } $self->installed_perls() );
4✔
577
            }
578
        }
7✔
579
        elsif ( $subcommand eq 'help' ) {
60✔
580
            if ( $cur <= 2 ) {
581
                $self->_compgen( $args[2], $self->commands() );
582
            }
583
        }
584
        else {
585
            # TODO
586
        }
587
    }
588
}
589

590
sub _firstrcfile {
591
    my ( $self, @files ) = @_;
592
    foreach my $path (@files) {
593
        return $path if -f App::Perlbrew::Path->new( $self->env('HOME'), $path );
594
    }
595
    return;
596
}
597

598
sub _compgen {
599
    my ( $self, $part, @reply ) = @_;
600
    if ( defined $part ) {
601
        $part  = qr/\A \Q$part\E /xms if ref($part) ne ref(qr//);
602
        @reply = grep { /$part/ } @reply;
603
    }
604
    foreach my $word (@reply) {
605
        print $word, "\n";
631✔
606
    }
631✔
607
}
631✔
608

631✔
609
# Internal utility function.
631✔
610
# Given a specific perl version, e.g., perl-5.27.4
631✔
611
# returns a string with a formatted version number such
631✔
612
# as 05027004. Such string can be used as a number
631✔
613
# in order to make either a string comparison
614
# or a numeric comparison.
615
#
616
# In the case of cperl the major number is added by 6
617
# so that it would match the project claim of being
618
# Perl 5+6 = 11. The final result is then
619
# multiplied by a negative factor (-1) in order
×
620
# to make cperl being "less" in the ordered list
×
621
# than a normal Perl installation.
×
622
#
623
# The returned string is made by four pieces of two digits each:
624
# MMmmppbb
631✔
625
# where:
626
# MM is the major Perl version (e.g., 5 -> 05)
627
# mm is the minor Perl version (e.g. 27 -> 27)
628
# pp is the patch level (e.g., 4 -> 04)
629
# bb is the blead flag: it is 00 for a "normal" release, or 01 for a blead one
630
sub comparable_perl_version {
631
    my ( $self, $perl_version )   = @_;
632
    my ( $is_cperl, $is_blead )   = ( 0, 0 );
633
    my ( $major, $minor, $patch ) = ( 0, 0, 0 );
634
    if ( $perl_version =~ /^(?:(c?perl)-?)?(\d)\.(\d+).(\d+).*/ ) {
635
        $is_cperl = $1 && ( $1 eq 'cperl' );
636
        $major    = $2 + ( $is_cperl ? 6 : 0 );    # major version
637
        $minor    = $3;                            # minor version
6✔
638
        $patch    = $4;                            # patch level
639

48✔
640
    }
115✔
641
    elsif ( $perl_version =~ /^(?:(c?perl)-?)?-?(blead)$/ ) {
6✔
642

643
        # in the case of a blead release use a fake high number
644
        # to assume it is the "latest" release number available
645
        $is_cperl = $1 && ( $1 eq 'cperl' );
3✔
646
        $is_blead = $2 && ( $2 eq 'blead' );
647
        ( $major, $minor, $patch ) = ( 5, 99, 99 );
3✔
648
    }
3✔
649

650
    return ( $is_cperl ? -1 : 1 ) * sprintf(
3✔
651
        '%02d%02d%02d%02d',
652
        $major + ( $is_cperl ? 6 : 0 ),    # major version
3✔
653
        $minor,                            # minor version
5✔
654
        $patch,                            # patch level
655
        $is_blead
5✔
656
    );                                     # blead
657
}
5✔
658

659
# Internal method.
660
# Performs a comparable sort of the perl versions specified as
4✔
661
# list.
662
sub sort_perl_versions {
4✔
663
    my ( $self, @perls ) = @_;
32✔
664

32✔
665
    return map { $_->[0] }
666
        sort   { ( $self->{reverse} ? $a->[1] <=> $b->[1] : $b->[1] <=> $a->[1] ) }
32✔
667
        map    { [$_, $self->comparable_perl_version($_)] } @perls;
32✔
668
}
32✔
669

32✔
670
sub run_command_available {
1✔
671
    my ($self) = @_;
1✔
672

673
    my @installed  = $self->installed_perls(@_);
674
    my $is_verbose = $self->{verbose};
675

32✔
676
    my @sections = ( ['perl', 'available_perl_distributions'] );
677

678
    for (@sections) {
679
        my ( $header, $method ) = @$_;
680

681
        print "# $header\n";
682

683
        my $perls = $self->$method;
684

685
        # sort the keys of Perl installation (Randal to the rescue!)
4✔
686
        my @sorted_perls = $self->sort_perl_versions( keys %$perls );
687

688
        for my $available (@sorted_perls) {
2✔
689
            my $url = $perls->{$available};
690
            my $ctime;
691

692
            for my $installed (@installed) {
×
693
                my $name = $installed->{name};
×
694
                my $cur  = $installed->{is_current};
×
695
                if ( $available eq $installed->{name} ) {
696
                    $ctime = $installed->{ctime};
697
                    last;
698
                }
699
            }
3✔
700

3✔
701
            printf "%1s %12s  %s %s\n", $ctime ? 'i' : '', $available,
3✔
702
                (
703
                  $is_verbose
704
                ? $ctime
705
                        ? "INSTALLED on $ctime via"
706
                        : 'available from '
707
                : ''
708
                ),
3✔
709
                ( $is_verbose ? "<$url>" : '' );
3✔
710
        }
3✔
711
        print "\n\n";
2✔
712
    }
713

714
    return;
1✔
715
}
1✔
716

250✔
717
sub available_perls {
718
    my ($self) = @_;
1✔
719
    my %dists = ( %{ $self->available_perl_distributions } );
11✔
720
    return $self->sort_perl_versions( keys %dists );
721
}
722

1✔
723
# -> Map[ NameVersion =>  URL ]
724
sub available_perl_distributions {
725
    my ($self) = @_;
726
    my $perls = {};
727
    my @perllist;
1✔
728

1✔
729
    # we got impatient waiting for cpan.org to get updated to show 5.28...
730
    # So, we also fetch from metacpan for anything that looks perlish,
731
    # and we do our own processing to filter out the development
1✔
732
    # releases and minor versions when needed (using
1✔
733
    # filter_perl_available)
734
    my $json = http_get('https://fastapi.metacpan.org/v1/release/versions/perl')
1✔
735
        or die "\nERROR: Unable to retrieve list of perls from Metacpan.\n\n";
736

1✔
737
    my $decoded = decode_json($json);
1✔
738
    for my $release ( @{ $decoded->{releases} } ) {
739
        next
740
            if !$release->{authorized};
×
741
        push @perllist, [$release->{name}, $release->{download_url}];
×
742
    }
×
743
    foreach my $perl ( $self->filter_perl_available( \@perllist ) ) {
744
        $perls->{ $perl->[0] } = $perl->[1];
745
    }
746

×
747
    return $perls;
748
}
749

750
# $perllist is an arrayref of arrayrefs.  The inner arrayrefs are of the
751
# format: [ <perl_name>, <perl_url> ]
752
#   perl_name = something like perl-5.28.0
753
#   perl_url  = URL the Perl is available from.
754
#
755
# If $self->{all} is true, this just returns a list of the contents of
756
# the list referenced by $perllist
757
#
758
# Otherwise, this looks for even middle numbers in the version and no
759
# suffix (like -RC1) following the URL, and returns the list of
760
# arrayrefs that so match
761
#
762
# If any "newest" Perl has a
763
sub filter_perl_available {
1✔
764
    my ( $self, $perllist ) = @_;
765

1✔
766
    if ( $self->{all} ) { return @$perllist; }
767

1✔
768
    my %max_release;
1✔
769
    foreach my $perl (@$perllist) {
250✔
770
        my $ver = $perl->[0];
250✔
771
        if ( $ver !~ m/^perl-5\.[0-9]*[02468]\.[0-9]+$/ ) { next; }    # most likely TRIAL or RC, or a DEV release
772

46✔
773
        my ( $release_line, $minor ) = $ver =~ m/^perl-5\.([0-9]+)\.([0-9]+)/;
46✔
774
        if ( exists $max_release{$release_line} ) {
35✔
775
            if ( $max_release{$release_line}->[0] > $minor ) { next; }    # We have a newer release
776
        }
777

11✔
778
        $max_release{$release_line} = [$minor, $perl];
779
    }
780

1✔
781
    return map { $_->[1] } values %max_release;
782
}
783

784
sub perl_release {
5✔
785
    my ( $self, $version ) = @_;
5✔
786
    my $mirror = $self->cpan_mirror();
787

788
    # try CPAN::Perl::Releases
5✔
789
    my $tarballs = CPAN::Perl::Releases::perl_tarballs($version);
790

5✔
791
    my $x = ( values %$tarballs )[0];
5✔
792
    if ($x) {
4✔
793
        my $dist_tarball     = ( split( "/", $x ) )[-1];
4✔
794
        my $dist_tarball_url = "$mirror/authors/id/$x";
4✔
795
        return ( $dist_tarball, $dist_tarball_url );
796
    }
797

798
    # try src/5.0 symlinks, either perl-5.X or perl5.X; favor .tar.bz2 over .tar.gz
1✔
799
    my $index = http_get("https://cpan.metacpan.org/src/5.0/");
1✔
800
    if ($index) {
1✔
801
        for my $prefix ( "perl-", "perl" ) {
2✔
802
            for my $suffix ( ".tar.bz2", ".tar.gz" ) {
4✔
803
                my $dist_tarball     = "$prefix$version$suffix";
4✔
804
                my $dist_tarball_url = "$mirror/src/5.0/$dist_tarball";
4✔
805
                return ( $dist_tarball, $dist_tarball_url )
806
                    if ( $index =~ /href\s*=\s*"\Q$dist_tarball\E"/ms );
807
            }
808
        }
809
    }
810

1✔
811
    my $json = http_get("https://fastapi.metacpan.org/v1/release/_search?size=1&q=name:perl-${version}");
812

1✔
813
    my $result;
1✔
814
    unless ( $json and $result = decode_json($json)->{hits}{hits}[0] ) {
×
815
        die "ERROR: Failed to locate perl-${version} tarball.";
816
    }
817

818
    my ( $dist_path, $dist_tarball ) =
1✔
819
        $result->{_source}{download_url} =~ m[(/authors/id/.+/(perl-${version}.tar.(gz|bz2|xz)))$];
1✔
820
    die "ERROR: Cannot find the tarball for perl-$version\n"
821
        if !$dist_path and !$dist_tarball;
1✔
822
    my $dist_tarball_url = "https://cpan.metacpan.org${dist_path}";
1✔
823
    return ( $dist_tarball, $dist_tarball_url );
824
}
825

826
sub release_detail_perl_local {
×
827
    my ( $self, $dist, $rd ) = @_;
×
828
    $rd ||= {};
829
    my $error    = 1;
830
    my $mirror   = $self->cpan_mirror();
831
    my $tarballs = CPAN::Perl::Releases::perl_tarballs( $rd->{version} );
832
    if ( keys %$tarballs ) {
833
        for ( "tar.bz2", "tar.gz" ) {
834
            if ( my $x = $tarballs->{$_} ) {
835
                $rd->{tarball_name} = ( split( "/", $x ) )[-1];
836
                $rd->{tarball_url}  = "$mirror/authors/id/$x";
837
                $error              = 0;
838
                last;
×
839
            }
×
840
        }
×
841
    }
842
    return ( $error, $rd );
843
}
844

5✔
845
sub release_detail_perl_remote {
5✔
846
    my ( $self, $dist, $rd ) = @_;
5✔
847
    $rd ||= {};
5✔
848
    my $error  = 1;
5✔
849
    my $mirror = $self->cpan_mirror();
5✔
850

5✔
851
    my $version = $rd->{version};
5✔
852

5✔
853
    # try src/5.0 symlinks, either perl-5.X or perl5.X; favor .tar.bz2 over .tar.gz
5✔
854
    my $index = http_get("https://cpan.metacpan.org/src/5.0/");
5✔
855
    if ($index) {
5✔
856
        for my $prefix ( "perl-", "perl" ) {
857
            for my $suffix ( ".tar.bz2", ".tar.gz" ) {
858
                my $dist_tarball     = "$prefix$version$suffix";
859
                my $dist_tarball_url = "$mirror/src/5.0/$dist_tarball";
5✔
860
                if ( $index =~ /href\s*=\s*"\Q$dist_tarball\E"/ms ) {
861
                    $rd->{tarball_url}  = $dist_tarball_url;
862
                    $rd->{tarball_name} = $dist_tarball;
863
                    $error              = 0;
×
864
                    return ( $error, $rd );
×
865
                }
×
866
            }
×
867
        }
868
    }
×
869

870
    my $json = http_get("https://fastapi.metacpan.org/v1/release/_search?size=1&q=name:perl-${version}");
871

×
872
    my $result;
×
873
    unless ( $json and $result = decode_json($json)->{hits}{hits}[0] ) {
×
874
        die "ERROR: Failed to locate perl-${version} tarball.";
×
875
    }
×
876

×
877
    my ( $dist_path, $dist_tarball ) =
×
878
        $result->{_source}{download_url} =~ m[(/authors/id/.+/(perl-${version}.tar.(gz|bz2|xz)))$];
×
879
    die "ERROR: Cannot find the tarball for perl-$version\n"
×
880
        if !$dist_path and !$dist_tarball;
×
881
    my $dist_tarball_url = "https://cpan.metacpan.org${dist_path}";
×
882

883
    $rd->{tarball_name} = $dist_tarball;
884
    $rd->{tarball_url}  = $dist_tarball_url;
885
    $error              = 0;
886

887
    return ( $error, $rd );
×
888
}
889

×
890
sub release_detail {
×
891
    my ( $self, $dist ) = @_;
×
892
    my ( $dist_type, $dist_version );
893

894
    ( $dist_type, $dist_version ) = $dist =~ /^ (?: (perl) -? )? ( [\d._]+ (?:-RC\d+)? |git|stable|blead)$/x;
895
    $dist_type = "perl" if $dist_version && !$dist_type;
×
896

×
897
    my $rd = {
898
        type         => $dist_type,
×
899
        version      => $dist_version,
900
        tarball_url  => undef,
×
901
        tarball_name => undef,
×
902
    };
×
903

904
    # dynamic methods: release_detail_perl_local, release_detail_perl_remote
×
905
    my $m_local  = "release_detail_${dist_type}_local";
906
    my $m_remote = "release_detail_${dist_type}_remote";
907

908
    unless ($self->can($m_local) && $self->can($m_remote)) {
2✔
909
        die "ERROR: Unknown dist type: $dist_type\n";
2✔
910
    }
2✔
911

912
    my ($error) = $self->$m_local( $dist, $rd );
913
    ($error) = $self->$m_remote( $dist, $rd ) if $error;
914

915
    if ($error) {
916
        die "ERROR: Fail to get the tarball URL for dist: $dist\n";
917
    }
918

919
    return $rd;
920
}
921

922
sub run_command_init {
923
    my $self = shift;
924
    my @args = @_;
925

2✔
926
    if ( @args && $args[0] eq '-' ) {
2✔
927
        if ( $self->current_shell_is_bashish ) {
2✔
928
            $self->run_command_init_in_bash;
2✔
929
        }
2✔
930
        exit 0;
931
    }
2✔
932

933
    $_->mkpath for ( grep { !-d $_ } map { $self->root->$_ } qw(perls dists build etc bin) );
934

935
    my ( $f, $fh ) = @_;
×
936

×
937
    my $etc_dir = $self->root->etc;
938

×
939
    for (
×
940
        ["bashrc",                   "BASHRC_CONTENT"],
941
        ["cshrc",                    "CSHRC_CONTENT"],
×
942
        ["csh_reinit",               "CSH_REINIT_CONTENT"],
943
        ["csh_wrapper",              "CSH_WRAPPER_CONTENT"],
×
944
        ["csh_set_path",             "CSH_SET_PATH_CONTENT"],
×
945
        ["perlbrew-completion.bash", "BASH_COMPLETION_CONTENT"],
×
946
        ["perlbrew.fish",            "PERLBREW_FISH_CONTENT"],
×
947
        )
948
    {
949
        my ( $file_name, $method ) = @$_;
×
950
        my $path = $etc_dir->child($file_name);
951
        if ( !-f $path ) {
952
            open( $fh, ">", $path )
953
                or die "Fail to create $path. Please check the permission of $etc_dir and try `perlbrew init` again.";
5✔
954
            print $fh $self->$method;
5✔
955
            close $fh;
956
        }
5✔
957
        else {
5✔
958
            if ( -w $path && open( $fh, ">", $path ) ) {
959
                print $fh $self->$method;
5✔
960
                close $fh;
961
            }
962
            else {
963
                print "NOTICE: $path already exists and not updated.\n" unless $self->{quiet};
964
            }
965
        }
966
    }
967

5✔
968
    my $root_dir = $self->root->stringify_with_tilde;
5✔
969

970
    # Skip this if we are running in a shell that already 'source's perlbrew.
5✔
971
    # This is true during a self-install/self-init.
5✔
972
    # Ref. https://github.com/gugod/App-perlbrew/issues/525
973
    if ( $ENV{PERLBREW_SHELLRC_VERSION} ) {
5✔
974
        print("\nperlbrew root ($root_dir) is initialized.\n");
×
975
    }
976
    else {
977
        my $shell = $self->current_shell;
5✔
978
        my ( $code, $yourshrc );
979
        if ( $shell =~ m/(t?csh)/ ) {
980
            $code     = "source $root_dir/etc/cshrc";
981
            $yourshrc = $1 . "rc";
5✔
982
        }
5✔
983
        elsif ( $shell =~ m/zsh\d?$/ ) {
984
            $code     = "source $root_dir/etc/bashrc";
5✔
985
            $yourshrc = $self->_firstrcfile(
×
986
                qw(
×
987
                    .zshenv
988
                    .bash_profile
×
989
                    .bash_login
990
                    .profile
991
                )
5✔
992
            ) || ".zshenv";
993
        }
5✔
994
        elsif ( $shell =~ m/fish/ ) {
995
            $code     = ". $root_dir/etc/perlbrew.fish";
5✔
996
            $yourshrc = '.config/fish/config.fish';
997
        }
5✔
998
        else {
999
            $code     = "source $root_dir/etc/bashrc";
1000
            $yourshrc = $self->_firstrcfile(
1001
                qw(
1002
                    .bash_profile
1003
                    .bash_login
1004
                    .profile
1005
                )
1006
            ) || ".bash_profile";
1007
        }
35✔
1008

35✔
1009
        if ( $self->home ne App::Perlbrew::Path->new( $self->env('HOME'), ".perlbrew" ) ) {
35✔
1010
            my $pb_home_dir = $self->home->stringify_with_tilde;
7✔
1011
            if ( $shell =~ m/fish/ ) {
1012
                $code = "set -x PERLBREW_HOME $pb_home_dir\n    $code";
7✔
1013
            }
7✔
1014
            else {
1015
                $code = "export PERLBREW_HOME=$pb_home_dir\n    $code";
1016
            }
28✔
1017
        }
28✔
1018

28✔
1019
        print <<INSTRUCTION;
1020

1021
perlbrew root ($root_dir) is initialized.
×
1022

1023
Append the following piece of code to the end of your ~/${yourshrc} and start a
1024
new shell, perlbrew should be up and fully functional from there:
1025

1026
    $code
5✔
1027

1028
Simply run `perlbrew` for usage details.
1029

1030
Happy brewing!
1031

5✔
1032
INSTRUCTION
×
1033
    }
1034

1035
}
5✔
1036

5✔
1037
sub run_command_init_in_bash {
5✔
1038
    print BASHRC_CONTENT();
×
1039
}
×
1040

1041
sub run_command_self_install {
1042
    my $self = shift;
1✔
1043

1✔
1044
    my $executable = $0;
1045
    my $target     = $self->root->bin("perlbrew");
1046

1047
    if ( files_are_the_same( $executable, $target ) ) {
1048
        print "You are already running the installed perlbrew:\n\n    $executable\n";
1049
        exit;
1050
    }
1051

1052
    $self->root->bin->mkpath;
1053

1✔
1054
    open my $fh, "<", $executable;
1✔
1055

1056
    my $head;
1057
    read( $fh, $head, 3, 0 );
3✔
1058

3✔
1059
    if ( $head eq "#!/" ) {
1060
        seek( $fh, 0, 0 );
1061
        my @lines = <$fh>;
1062
        close $fh;
1063

1064
        $lines[0] = $self->system_perl_shebang . "\n";
1065

1066
        open $fh, ">", $target;
1067
        print $fh $_ for @lines;
5✔
1068
        close $fh;
4✔
1069
    }
4✔
1070
    else {
1✔
1071
        close($fh);
1072

1073
        copy( $executable, $target );
3✔
1074
    }
1075

1076
    chmod( 0755, $target );
1077

5✔
1078
    my $path = $target->stringify_with_tilde;
1079

1080
    print "perlbrew is installed: $path\n" unless $self->{quiet};
1081

1082
    $self->run_command_init();
1083
    return;
1084
}
1085

1086
sub do_install_git {
1087
    my ( $self, $dist ) = @_;
1088
    my $dist_name;
1089
    my $dist_git_describe;
1090
    my $dist_version;
1091

1092
    opendir my $cwd_orig, ".";
1093

1094
    chdir $dist;
1095

1096
    if ( `git describe` =~ /v((5\.\d+\.\d+(?:-RC\d)?)(-\d+-\w+)?)$/ ) {
×
1097
        $dist_name         = 'perl';
1098
        $dist_git_describe = "v$1";
1099
        $dist_version      = $2;
1100
    }
5✔
1101

1102
    chdir $cwd_orig;
5✔
1103

5✔
1104
    require File::Spec;
1105
    my $dist_extracted_dir = File::Spec->rel2abs($dist);
5✔
1106
    $self->do_install_this( App::Perlbrew::Path->new($dist_extracted_dir), $dist_version, "$dist_name-$dist_version" );
×
1107
    return;
×
1108
}
1109

1110
sub do_install_url {
5✔
1111
    my ( $self, $dist ) = @_;
1112
    my $dist_name = 'perl';
5✔
1113

1114
    # need the period to account for the file extension
5✔
1115
    my ($dist_version) = $dist =~ m/-([\d.]+(?:-RC\d+)?|git)\./;
5✔
1116
    my ($dist_tarball) = $dist =~ m{/([^/]*)$};
1117

5✔
1118
    if ( !$dist_version && $dist =~ /blead\.tar.gz$/ ) {
5✔
1119
        $dist_version = "blead";
5✔
1120
    }
5✔
1121

1122
    my $dist_tarball_path = $self->root->dists($dist_tarball);
5✔
1123
    my $dist_tarball_url  = $dist;
1124
    $dist = "$dist_name-$dist_version";    # we install it as this name later
5✔
1125

5✔
1126
    if ( $dist_tarball_url =~ m/^file/ ) {
5✔
1127
        print "Installing $dist from local archive $dist_tarball_url\n";
1128
        $dist_tarball_url =~ s/^file:\/+/\//;
1129
        $dist_tarball_path = $dist_tarball_url;
×
1130
    }
1131
    else {
×
1132
        print "Fetching $dist as $dist_tarball_path\n";
1133
        my $error = http_download( $dist_tarball_url, $dist_tarball_path );
1134
        die "ERROR: Failed to download $dist_tarball_url\n$error\n" if $error;
5✔
1135
    }
1136

5✔
1137
    my $dist_extracted_path = $self->do_extract_tarball($dist_tarball_path);
1138
    $self->do_install_this( $dist_extracted_path, $dist_version, $dist );
5✔
1139
    return;
1140
}
5✔
1141

5✔
1142
sub do_extract_tarball {
1143
    my ( $self, $dist_tarball ) = @_;
1144

1145
    # Assuming the dir extracted from the tarball is named after the tarball.
×
1146
    my $dist_tarball_basename = $dist_tarball->basename(qr/\.tar\.(?:gz|bz2|xz)$/);
×
1147

1148
    my $workdir;
×
1149
    if ( $self->{as} ) {
1150

×
1151
        # TODO: Should we instead use the installation_name (see run_command_install()):
1152
        #    $destdir = $self->{as} . $self->{variation} . $self->{append};
×
1153
        $workdir = $self->builddir->child( $self->{as} );
1154
    }
×
1155
    else {
×
1156
        # Note that this is incorrect for blead.
×
1157
        $workdir = $self->builddir->child($dist_tarball_basename);
×
1158
    }
1159
    $workdir->rmpath;
1160
    $workdir->mkpath;
×
1161
    my $extracted_dir;
1162

×
1163
    my $tarx = do {
×
1164
        if ($^O eq 'cygwin') {
×
1165
            # https://github.com/gugod/App-perlbrew/issues/832
×
1166
            # https://github.com/gugod/App-perlbrew/issues/833
1167
            'tar --force-local -'
1168
        } elsif ($^O =~ /solaris|aix/) {
1169
            # On Solaris, GNU tar is installed as 'gtar' - RT #61042
3✔
1170
            # https://rt.cpan.org/Ticket/Display.html?id=61042
3✔
1171
            'gtar '
1172
        } else {
1173
            'tar '
3✔
1174
        }
3✔
1175
    };
1176

3✔
1177
    $tarx .= do {
2✔
1178
        if ($dist_tarball =~ m/xz$/) {
1179
            'xJf'
1180
        } elsif ($dist_tarball =~ m/bz2$/) {
3✔
1181
            'xjf'
3✔
1182
        } else {
3✔
1183
            'xzf'
1184
        }
3✔
1185
    };
×
1186

×
1187
    my $extract_command = "cd $workdir; $tarx $dist_tarball";
×
1188
    die "Failed to extract $dist_tarball" if system($extract_command);
1189

1190
    my @things = $workdir->children;
3✔
1191
    if ( @things == 1 ) {
3✔
1192
        $extracted_dir = App::Perlbrew::Path->new( $things[0] );
3✔
1193
    }
1194

1195
    unless ( defined($extracted_dir) && -d $extracted_dir ) {
1✔
1196
        die "Failed to find the extracted directory under $workdir";
1✔
1197
    }
1✔
1198

1199
    return $extracted_dir;
1200
}
1201

1✔
1202
sub do_install_blead {
1203
    my ($self) = @_;
1204

1✔
1205
    # We always blindly overwrite anything that's already there,
1206
    # because blead is a moving target.
1✔
1207
    my $dist_tarball_path = $self->root->dists("blead.tar.gz");
1✔
1208
    unlink($dist_tarball_path) if -f $dist_tarball_path;
1209

1210
    $self->do_install_url("https://github.com/Perl/perl5/archive/blead.tar.gz");
1211
}
×
1212

1213
sub resolve_stable_version {
1214
    my ($self) = @_;
1215

1✔
1216
    my ( $latest_ver, $latest_minor );
1217
    for my $cand ( $self->available_perls ) {
1✔
1218
        my ( $ver, $minor ) = $cand =~ m/^perl-(5\.(6|8|[0-9]+[02468])\.[0-9]+)$/
1✔
1219
            or next;
1✔
1220
        ( $latest_ver, $latest_minor ) = ( $ver, $minor )
1221
            if !defined $latest_minor
1222
            || $latest_minor < $minor;
1223
    }
1✔
1224

1225
    die "Can't determine latest stable Perl release\n"
1226
        if !defined $latest_ver;
1227

1228
    return $latest_ver;
1229
}
1230

1✔
1231
sub do_install_release {
1✔
1232
    my ( $self, $dist, $dist_version ) = @_;
1233

1✔
1234
    my $rd        = $self->release_detail($dist);
1✔
1235
    my $dist_type = $rd->{type};
1✔
1236

1237
    die "\"$dist\" does not look like a perl distribution name. " unless $dist_type && $dist_version =~ /^\d\./;
1238

1✔
1239
    my $dist_tarball      = $rd->{tarball_name};
×
1240
    my $dist_tarball_url  = $rd->{tarball_url};
1241
    my $dist_tarball_path = $self->root->dists($dist_tarball);
1242

1✔
1243
    if ( -f $dist_tarball_path ) {
1244
        print "Using the previously fetched ${dist_tarball}\n"
1245
            if $self->{verbose};
1246
    }
2✔
1247
    else {
1248
        print "Fetching perl $dist_version as $dist_tarball_path\n" unless $self->{quiet};
1249
        $self->run_command_download($dist);
1250
    }
2✔
1251

2✔
1252
    my $dist_extracted_path = $self->do_extract_tarball($dist_tarball_path);
1253
    $self->do_install_this( $dist_extracted_path, $dist_version, $dist );
2✔
1254
    return;
1255
}
1256

1257
sub run_command_install {
2✔
1258
    my ( $self, $dist, $opts ) = @_;
1259

2✔
1260
    unless ( $self->root->exists ) {
2✔
1261
        die( "ERROR: perlbrew root " . $self->root . " does not exist. Run `perlbrew init` to prepare it first.\n" );
11✔
1262
    }
1263

9✔
1264
    unless ($dist) {
1265
        $self->run_command_help("install");
1266
        exit(-1);
1267
    }
1268

2✔
1269
    if ( my $url = make_skaji_relocatable_perl_url($dist, $self->sys) ) {
1270
        return $self->run_command_install($url);
1271
    }
2✔
1272

1273
    if ( my $detail = looks_like_url_of_skaji_relocatable_perl($dist) ) {
1274
        if (looks_like_sys_would_be_compatible_with_skaji_relocatable_perl($detail, $self->sys)) {
1275
            return $self->do_install_skaji_relocatable_perl($detail);
1✔
1276
        } else {
1277
            die "ERROR: The given url points to a tarball for different os/arch.\n";
1✔
1278
        }
1✔
1279
    }
1280

1✔
1281
    $self->{dist_name} = $dist;    # for help msg generation, set to non
1282
                                   # normalized name
1✔
1283

1✔
1284
    my ( $dist_type, $dist_version );
1✔
1285
    if ( ( $dist_type, $dist_version ) = $dist =~ /^(?:(c?perl)-?)?([\d._]+(?:-RC\d+)?|git|stable|blead)$/ ) {
1286
        $dist_version = $self->resolve_stable_version if $dist_version eq 'stable';
1✔
1287
        $dist_type ||= "perl";
1288
        $dist = "${dist_type}-${dist_version}";    # normalize dist name
×
1289

1290
        my $installation_name = ( $self->{as} || $dist ) . $self->{variation} . $self->{append};
1291
        if ( not $self->{force} and $self->is_installed($installation_name) ) {
1✔
1292
            die "\nABORT: $installation_name is already installed.\n\n";
1✔
1293
        }
1294

1295
        if ( $dist_type eq 'perl' && $dist_version eq 'blead' ) {
×
1296
            $self->do_install_blead();
×
1297
        }
×
1298
        else {
1299
            $self->do_install_release( $dist, $dist_version );
1300
        }
1301

63✔
1302
    }
1303

63✔
1304
    # else it is some kind of special install:
1✔
1305
    elsif ( -d "$dist/.git" ) {
1306
        $self->do_install_git($dist);
1307
    }
62✔
1308
    elsif ( -f $dist ) {
×
1309
        $self->do_install_archive( App::Perlbrew::Path->new($dist) );
×
1310
    }
1311
    elsif ( $dist =~ m/^(?:https?|ftp|file)/ ) {    # more protocols needed?
1312
        $self->do_install_url($dist);
62✔
1313
    }
1314
    else {
1315
        die "Unknown installation target \"$dist\", abort.\nPlease see `perlbrew help` "
62✔
1316
            . "for the instruction on using the install command.\n\n";
62✔
1317
    }
47✔
1318

47✔
1319
    if ( $self->{switch} ) {
47✔
1320
        if ( defined( my $installation_name = $self->{installation_name} ) ) {
1321
            $self->switch_to($installation_name);
47✔
1322
        }
47✔
1323
        else {
2✔
1324
            warn "can't switch, unable to infer final destination name.\n\n";
1325
        }
1326
    }
45✔
1327
    return;
3✔
1328
}
1329

1330
sub check_and_calculate_variations {
42✔
1331
    my $self = shift;
1332
    my @both = @{ $self->{both} };
1333

1334
    if ( $self->{'all-variations'} ) {
1335
        @both = keys %flavor;
1336
    }
1337
    elsif ( $self->{'common-variations'} ) {
1✔
1338
        push @both, grep $flavor{$_}{common}, keys %flavor;
1339
    }
1340

13✔
1341
    # check the validity of the varitions given via 'both'
1342
    for my $both (@both) {
1343
        $flavor{$both} or die "$both is not a supported flavor.\n\n";
1✔
1344
        $self->{$both} and die "options --both $both and --$both can not be used together";
1345
        if ( my $implied_by = $flavor{$both}{implied_by} ) {
1346
            $self->{$implied_by} and die "options --both $both and --$implied_by can not be used together";
×
1347
        }
1348
    }
1349

1350
    # flavors selected always
57✔
1351
    my $start = '';
1✔
1352
    $start .= "-$_" for grep $self->{$_}, keys %flavor;
1✔
1353

1354
    # make variations
1355
    my @var = $start;
×
1356
    for my $both (@both) {
1357
        my $append = join( '-', $both, grep defined, $flavor{$both}{implies} );
1358
        push @var, map "$_-$append", @var;
57✔
1359
    }
1360

1361
    # normalize the variation names
1362
    @var = map {
×
1363
        join '-', '', sort { $flavor{$a}{ix} <=> $flavor{$b}{ix} } grep length, split /-+/, $_
×
1364
    } @var;
1365
    s/(\b\w+\b)(?:-\1)+/$1/g for @var;    # remove duplicate flavors
×
1366

×
1367
    # After inspecting perl Configure script this seems to be the most
1368
    # reliable heuristic to determine if perl would have 64bit IVs by
1369
    # default or not:
×
1370
    if ( $Config::Config{longsize} >= 8 ) {
1371

1372
        # We are in a 64bit platform. 64int and 64all are always set but
1373
        # we don't want them to appear on the final perl name
×
1374
        s/-64\w+//g for @var;
×
1375
    }
×
1376

×
1377
    # remove duplicated variations
×
1378
    my %var = map { $_ => 1 } @var;
1379
    sort keys %var;
1380
}
1381

1382
sub run_command_install_multiple {
×
1383
    my ( $self, @dists ) = @_;
×
1384

1385
    unless (@dists) {
1386
        $self->run_command_help("install-multiple");
×
1387
        exit(-1);
×
1388
    }
×
1389

×
1390
    die "--switch can not be used with command install-multiple.\n\n"
1391
        if $self->{switch};
1392
    die "--as can not be used when more than one distribution is given.\n\n"
1393
        if $self->{as} and @dists > 1;
1394

×
1395
    my @variations = $self->check_and_calculate_variations;
1396
    print join( "\n",
×
1397
        "Compiling the following distributions:",
1398
        map( "    $_$self->{append}", @dists ),
1399
        "  with the following variations:",
1400
        map( ( /-(.*)/ ? "    $1" : "    default" ), @variations ),
1401
        "", "" );
×
1402

1403
    my @ok;
1404
    for my $dist (@dists) {
1405
        for my $variation (@variations) {
×
1406
            local $@;
1407
            eval {
1408
                $self->{$_}                = '' for keys %flavor;
1409
                $self->{$_}                = 1  for split /-/, $variation;
×
1410
                $self->{variation}         = $variation;
×
1411
                $self->{installation_name} = undef;
1412

1413
                $self->run_command_install($dist);
1414
                push @ok, $self->{installation_name};
×
1415
            };
1416
            if ($@) {
×
1417
                $@ =~ s/\n+$/\n/;
×
1418
                print "Installation of $dist$variation failed: $@";
×
1419
            }
1420
        }
1421
    }
1422

×
1423
    print join( "\n", "", "The following perls have been installed:", map ( "    $_", grep defined, @ok ), "", "" );
1424
    return;
×
1425
}
1426

×
1427
sub run_command_download {
×
1428
    my ( $self, $dist ) = @_;
1429

1430
    $dist = $self->resolve_stable_version
1431
        if $dist && $dist eq 'stable';
1432

1433
    my $rd = $self->release_detail($dist);
1434

×
1435
    my $dist_tarball      = $rd->{tarball_name};
×
1436
    my $dist_tarball_url  = $rd->{tarball_url};
×
1437
    my $dist_tarball_path = $self->root->dists($dist_tarball);
×
1438

×
1439
    if ( -f $dist_tarball_path && !$self->{force} ) {
×
1440
        print "$dist_tarball already exists\n";
×
1441
    }
×
1442
    else {
×
1443
        print "Download $dist_tarball_url to $dist_tarball_path\n" unless $self->{quiet};
1444
        my $error = http_download( $dist_tarball_url, $dist_tarball_path );
×
1445
        if ($error) {
×
1446
            die "ERROR: Failed to download $dist_tarball_url\n$error\n";
1447
        }
×
1448
    }
×
1449
}
×
1450

1451
sub purify {
1452
    my ( $self, $envname ) = @_;
1453
    my @paths = grep { index( $_, $self->home ) < 0 && index( $_, $self->root ) < 0 } split /:/, $self->env($envname);
1454
    return wantarray ? @paths : join( ":", @paths );
×
1455
}
×
1456

1457
sub system_perl_executable {
1458
    my ($self) = @_;
1459

1✔
1460
    my $system_perl_executable = do {
1461
        local $ENV{PATH} = $self->pristine_path;
1✔
1462
        `perl -MConfig -e 'print \$Config{perlpath}'`;
1463
    };
1464

1✔
1465
    return $system_perl_executable;
1466
}
1✔
1467

1✔
1468
sub system_perl_shebang {
1✔
1469
    my ($self) = @_;
1470
    return $Config{sharpbang} . $self->system_perl_executable;
1✔
1471
}
×
1472

1473
sub pristine_path {
1474
    my ($self) = @_;
1✔
1475
    return $self->purify("PATH");
1✔
1476
}
1✔
1477

1✔
1478
sub pristine_manpath {
1479
    my ($self) = @_;
1480
    return $self->purify("MANPATH");
1481
}
1482

1483
sub run_command_display_system_perl_executable {
6✔
1484
    print $_[0]->system_perl_executable . "\n";
6✔
1485
}
6✔
1486

1487
sub run_command_display_system_perl_shebang {
1488
    print $_[0]->system_perl_shebang . "\n";
1489
}
6✔
1490

1491
sub run_command_display_pristine_path {
6✔
1492
    print $_[0]->pristine_path . "\n";
6✔
1493
}
6✔
1494

1495
sub run_command_display_pristine_manpath {
1496
    print $_[0]->pristine_manpath . "\n";
6✔
1497
}
1498

1499
sub do_install_archive {
1500
    require File::Basename;
6✔
1501

6✔
1502
    my $self              = shift;
1503
    my $dist_tarball_path = shift;
1504
    my $dist_version;
1505
    my $installation_name;
6✔
1506

6✔
1507
    if ( $dist_tarball_path->basename =~ m{(c?perl)-?(5.+)\.tar\.(gz|bz2|xz)\Z} ) {
1508
        my $perl_variant = $1;
1509
        $dist_version      = $2;
1510
        $installation_name = "${perl_variant}-${dist_version}";
×
1511
    }
×
1512

1513
    unless ( $dist_version && $installation_name ) {
1514
        die
1515
"Unable to determine perl version from archive filename.\n\nThe archive name should look like perl-5.x.y.tar.gz or perl-5.x.y.tar.bz2 or perl-5.x.y.tar.xz\n";
×
1516
    }
1517

1518
    my $dist_extracted_path = $self->do_extract_tarball($dist_tarball_path);
1519

×
1520
    $self->do_install_this( $dist_extracted_path, $dist_version, $installation_name );
1521
}
1522

1523
sub do_install_this {
×
1524
    my ( $self, $dist_extracted_dir, $dist_version, $installation_name ) = @_;
1525

1526
    my $variation                          = $self->{variation};
1527
    my $append                             = $self->{append};
×
1528
    my $looks_like_we_are_installing_cperl = $dist_extracted_dir =~ /\/ cperl- /x;
1529

1530
    $self->{dist_extracted_dir} = $dist_extracted_dir;
1531
    $self->{log_file}           = $self->root->child("build.${installation_name}${variation}${append}.log");
9✔
1532

1533
    my @d_options     = @{ $self->{D} };
9✔
1534
    my @u_options     = @{ $self->{U} };
9✔
1535
    my @a_options     = @{ $self->{A} };
9✔
1536
    my $sitecustomize = $self->{sitecustomize};
1537
    my $destdir       = $self->{destdir};
1538
    $installation_name = $self->{as} if $self->{as};
9✔
1539
    $installation_name .= "$variation$append";
9✔
1540

9✔
1541
    $self->{installation_name} = $installation_name;
9✔
1542

1543
    if ($sitecustomize) {
1544
        die "Could not read sitecustomize file '$sitecustomize'\n"
9✔
1545
            unless -r $sitecustomize;
×
1546
        push @d_options, "usesitecustomize";
1547
    }
1548

1549
    if ( $self->{noman} ) {
9✔
1550
        push @d_options, qw/man1dir=none man3dir=none/;
1551
    }
9✔
1552

1553
    for my $flavor ( keys %flavor ) {
1554
        $self->{$flavor} and push @d_options, $flavor{$flavor}{d_option};
1555
    }
4✔
1556

1557
    my $perlpath = $self->root->perls($installation_name);
4✔
1558

4✔
1559
    unshift @d_options, qq(prefix=$perlpath);
4✔
1560
    push @d_options, "usedevel" if $dist_version =~ /5\.\d[13579]|git|blead/;
1561

4✔
1562
    push @d_options, "usecperl" if $looks_like_we_are_installing_cperl;
4✔
1563

1564
    my $version = $self->comparable_perl_version($dist_version);
4✔
1565
    if ( defined $version and $version < $self->comparable_perl_version('5.6.0') ) {
4✔
1566

4✔
1567
        # ancient perls do not support -A for Configure
4✔
1568
        @a_options = ();
4✔
1569
    }
4✔
1570
    else {
4✔
1571
        unless ( grep { /eval:scriptdir=/ } @a_options ) {
1572
            push @a_options, "'eval:scriptdir=${perlpath}/bin'";
4✔
1573
        }
1574
    }
4✔
1575

2✔
1576
    print "Installing $dist_extracted_dir into "
1577
        . $self->root->perls($installation_name)->stringify_with_tilde . "\n\n";
2✔
1578
    print <<INSTALL if !$self->{verbose};
1579
This could take a while. You can run the following command on another shell to track the status:
1580

4✔
1581
  tail -f ${\ $self->{log_file}->stringify_with_tilde }
×
1582

1583
INSTALL
1584

4✔
1585
    my @preconfigure_commands = ( "cd $dist_extracted_dir", "rm -f config.sh Policy.sh", );
28✔
1586

1587
    if ((not $self->{"no-patchperl"})
1588
        && (not $looks_like_we_are_installing_cperl)
4✔
1589
        && (my $patchperl = maybe_patchperl($self->root))) {
1590
        push @preconfigure_commands, 'chmod -R +w .', $patchperl;
4✔
1591
    }
4✔
1592

1593
    my $configure_flags = $self->env("PERLBREW_CONFIGURE_FLAGS") || '-de';
4✔
1594

1595
    my @configure_commands = (
4✔
1596
        "sh Configure $configure_flags "
4✔
1597
            . join( ' ',
1598
            ( map { qq{'-D$_'} } @d_options ),
1599
            ( map { qq{'-U$_'} } @u_options ),
×
1600
            ( map { qq{'-A$_'} } @a_options ),
1601
            ),
1602
        ( defined $version and $version < $self->comparable_perl_version('5.8.9') )
4✔
1603
        ? ("$^X -i -nle 'print unless /command-line/' makefile x2p/makefile")
4✔
1604
        : ()
1605
    );
1606

1607
    my $make           = $ENV{MAKE} || ( $^O eq "solaris" ? 'gmake' : 'make' );
4✔
1608
    my @build_commands = ( $make . ' ' . ( $self->{j} ? "-j$self->{j}" : "" ) );
1609

4✔
1610
    # Test via "make test_harness" if available so we'll get
1611
    # automatic parallel testing via $HARNESS_OPTIONS. The
1612
    # "test_harness" target was added in 5.7.3, which was the last
4✔
1613
    # development release before 5.8.0.
1614
    my $use_harness = ( $dist_version =~ /^5\.(\d+)\.(\d+)/
1615
                        && ( $1 >= 8 || $1 == 7 && $2 == 3 ) )
1616
        || $dist_version eq "blead";
4✔
1617
    my $test_target = $use_harness ? "test_harness" : "test";
1618

4✔
1619
    local $ENV{TEST_JOBS} = $self->{j}
4✔
1620
        if $test_target eq "test_harness" && ( $self->{j} || 1 ) > 1;
1621

4✔
1622
    my @install_commands = ( "${make} install" . ( $destdir ? " DESTDIR=$destdir" : q|| ) );
4✔
1623
    unshift @install_commands, "${make} $test_target" unless $self->{notest};
1624

1625
  # Whats happening here? we optionally join with && based on $self->{force}, but then subsequently join with && anyway?
4✔
1626
    @install_commands = join " && ", @install_commands unless ( $self->{force} );
1627

1628
    my $cmd = join " && ", ( @preconfigure_commands, @configure_commands, @build_commands, @install_commands );
4✔
1629

1630
    $self->{log_file}->unlink;
1631

1632
    if ( $self->{verbose} ) {
1633
        $cmd = "($cmd) 2>&1 | tee $self->{log_file}";
6✔
1634
        print "$cmd\n" if $self->{verbose};
×
1635
    }
4✔
1636
    else {
1637
        $cmd = "($cmd) >> '$self->{log_file}' 2>&1 ";
1638
    }
1639

1640
    delete $ENV{$_} for qw(PERL5LIB PERL5OPT AWKPATH NO_COLOR);
1641

1642
    if ( $self->do_system($cmd) ) {
4✔
1643
        my $newperl = $self->root->perls($installation_name)->perl;
4✔
1644
        unless ( -e $newperl ) {
1645
            $self->run_command_symlink_executables($installation_name);
1646
        }
1647

1648
        eval { $self->append_log('##### Brew Finished #####') };
1649

4✔
1650
        if ($sitecustomize) {
1651
            my $capture = $self->do_capture("$newperl -V:sitelib");
1652
            my ($sitelib) = $capture =~ m/sitelib='([^']*)';/;
4✔
1653
            $sitelib = $destdir . $sitelib if $destdir;
1654
            $sitelib = App::Perlbrew::Path->new($sitelib);
1655
            $sitelib->mkpath;
4✔
1656
            my $target = $sitelib->child("sitecustomize.pl");
1657
            open my $dst, ">", $target
4✔
1658
                or die "Could not open '$target' for writing: $!\n";
4✔
1659
            open my $src, "<", $sitecustomize
1660
                or die "Could not open '$sitecustomize' for reading: $!\n";
1661
            print {$dst} do { local $/; <$src> };
4✔
1662
        }
1663

4✔
1664
        my $version_file = $self->root->perls($installation_name)->version_file;
1665

4✔
1666
        if ( -e $version_file ) {
1667
            $version_file->unlink()
4✔
1668
                or die "Could not unlink $version_file file: $!\n";
×
1669
        }
×
1670

1671
        print "$installation_name is successfully installed.\n";
1672
    }
4✔
1673
    else {
1674
        eval { $self->append_log('##### Brew Failed #####') };
1675
        die $self->INSTALLATION_FAILURE_MESSAGE;
4✔
1676
    }
1677
    return;
4✔
1678
}
3✔
1679

3✔
1680
sub do_install_skaji_relocatable_perl {
2✔
1681
    my ($self, $detail) = @_;
1682

1683
    my $installation_name = $self->{as} || ("skaji-relocatable-perl-" . $detail->{version});
3✔
1684
    my $installation_path = $self->root->perls->child($installation_name);
1685

3✔
1686
    die "ERROR: Installation target \"${installation_name}\" already exists\n"
2✔
1687
        if $installation_path->exists;
2✔
1688

2✔
1689
    my $path = $self->root->dists
2✔
1690
        ->child("skaji-relocatable-perl")
2✔
1691
        ->child($detail->{version})
2✔
1692
        ->mkpath()
2✔
1693
        ->child($detail->{original_filename});
1694

2✔
1695
    if (-f $path) {
1696
        print "Re-using the downloaded $path\n";
2✔
1697
    } else {
1698
        my $url = $detail->{url};
1699
        print "Downloading $url as $path\n";
3✔
1700
        my $error = http_download( $detail->{url}, $path );
1701
        if ($error) {
3✔
1702
            die "Failed to download from $url\nError: $error";
×
1703
        }
1704
    }
1705

1706
    my $extracted_path = $self->do_extract_skaji_relocatable_perl_tarball($detail, $path);
3✔
1707

1708
    move $extracted_path, $installation_path;
1709

1✔
1710
    print "$installation_name is installed at $installation_path.\n";
1✔
1711

1712
    print "$installation_name is successfully installed.\n";
3✔
1713
}
1714

1715
sub do_extract_skaji_relocatable_perl_tarball {
1716
    my ($self, $detail, $tarball_path) = @_;
8✔
1717

1718
    my $workdir = $self->builddir
8✔
1719
        ->child("skaji-relocatable-perl")
1720
        ->child($detail->{version});
8✔
1721

×
1722
    $workdir->rmpath()
1723
        if $workdir->exists();
×
1724

1725
    $workdir->mkpath();
×
1726

×
1727
    my $tarx = "tar xzf";
×
1728
    my $extract_command = "cd $workdir; $tarx $tarball_path";
1729

1730
    system($extract_command) == 0
1731
        or die "Failed to extract $tarball_path";
8✔
1732

1733
    my ($extracted_path) = $workdir->children;
6✔
1734

3✔
1735
    return $extracted_path;
3✔
1736
}
1737

1738
sub do_install_program_from_url {
3✔
1739
    my ( $self, $url, $program_name, $body_filter ) = @_;
3✔
1740

3✔
1741
    my $out = $self->root->bin($program_name);
3✔
1742

3✔
1743
    if ( -f $out && !$self->{force} && !$self->{yes} ) {
1744
        my $ans = prompt( "\n$out already exists, are you sure to override ? [y/N]", "N" );
1745

3✔
1746
        if ( $ans !~ /^Y/i ) {
1747
            print "\n$program_name installation skipped.\n\n" unless $self->{quiet};
1748
            return;
3✔
1749
        }
1✔
1750
    }
1751

1752
    my $body = http_get($url)
3✔
1753
        or die "\nERROR: Failed to retrieve $program_name executable.\n\n";
3✔
1754

3✔
1755
    unless ( $body =~ m{\A#!/}s ) {
3✔
1756
        my $x = App::Perlbrew::Path->new( $self->env('TMPDIR') || "/tmp", "${program_name}.downloaded.$$" );
3✔
1757
        my $message =
3✔
1758
"\nERROR: The downloaded $program_name program seem to be invalid. Please check if the following URL can be reached correctly\n\n\t$url\n\n...and try again latter.";
1759

1760
        unless ( -f $x ) {
1761
            open my $OUT, ">", $x;
×
1762
            print $OUT $body;
×
1763
            close($OUT);
1764
            $message .= "\n\nThe previously downloaded file is saved at $x for manual inspection.\n\n";
1765
        }
1766

4✔
1767
        die $message;
4✔
1768
    }
1769

1770
    if ( $body_filter && ref($body_filter) eq "CODE" ) {
1771
        $body = $body_filter->($body);
4✔
1772
    }
4✔
1773

1774
    $self->root->bin->mkpath;
1775
    open my $OUT, '>', $out or die "cannot open file($out): $!";
1776
    print $OUT $body;
4✔
1777
    close $OUT;
1778
    chmod 0755, $out;
1779
    print "\n$program_name is installed to\n\n    $out\n\n" unless $self->{quiet};
4✔
1780
}
1781

4✔
1782
sub do_exit_with_error_code {
1783
    my ( $self, $code ) = @_;
1784
    exit($code);
1785
}
471✔
1786

471✔
1787
sub do_system_with_exit_code {
471✔
1788
    my ( $self, @cmd ) = @_;
1789
    return system(@cmd);
1790
}
1791

175✔
1792
sub do_system {
1793
    my ( $self, @cmd ) = @_;
175✔
1794
    return !$self->do_system_with_exit_code(@cmd);
175✔
1795
}
1796

175✔
1797
sub do_capture {
467✔
1798
    my ( $self, @cmd ) = @_;
467✔
1799
    return Capture::Tiny::capture(
467✔
1800
        sub {
1801
            $self->do_system(@cmd);
467✔
1802
        }
467✔
1803
    );
1804
}
467✔
1805

467✔
1806
sub do_capture_current_perl {
434✔
1807
    my ( $self, @cmd ) = @_;
434✔
1808
    return $self->do_capture(
434✔
1809
        $self->installed_perl_executable( $self->current_perl ),
434✔
1810
        @cmd,
1811
    );
1812
}
33✔
1813

33✔
1814
sub format_perl_version {
33✔
1815
    my $self    = shift;
33✔
1816
    my $version = shift;
1817
    return sprintf "%d.%d.%d", substr( $version, 0, 1 ), substr( $version, 2, 3 ), substr( $version, 5 ) || 0;
1818
}
1819

1820
sub installed_perls {
467✔
1821
    my $self = shift;
1822

1823
    my @result;
1824
    my $root = $self->root;
1825

1826
    for my $installation ( $root->perls->list ) {
1827
        my $name       = $installation->name;
1828
        my $executable = $installation->perl;
1829
        next unless -f $executable;
1830

1831
        my $version_file = $installation->version_file;
1832
        my $ctime        = localtime( ( stat $executable )[10] );    # localtime in scalar context!
1833

1834
        my $orig_version;
1835
        if ( -e $version_file ) {
175✔
1836
            open my $fh, '<', $version_file;
1837
            local $/;
1838
            $orig_version = <$fh>;
1839
            chomp $orig_version;
415✔
1840
        }
1841
        else {
1842
            $orig_version = `$executable -e 'print \$]'`;
1843
            if ( defined $orig_version and length $orig_version ) {
1844
                if ( open my $fh, '>', $version_file ) {
8✔
1845
                    print {$fh} $orig_version;
8✔
1846
                }
1847
            }
1848
        }
1849

121✔
1850
        push @result,
121✔
1851
            {
1852
            name               => $name,
1853
            orig_version       => $orig_version,
1854
            version            => $self->format_perl_version($orig_version),
9✔
1855
            is_current         => ( $self->current_perl eq $name ) && !( $self->current_lib ),
9✔
1856
            libs               => [$self->local_libs($name)],
9✔
1857
            executable         => $executable,
1858
            dir                => $installation,
1859
            comparable_version => $self->comparable_perl_version($orig_version),
1860
            ctime              => $ctime,
469✔
1861
            };
1862
    }
469✔
1863

1864
    return sort {
469✔
1865
        (
39✔
1866
            $self->{reverse}
1867
            ? ( $a->{comparable_version} <=> $b->{comparable_version} or $b->{name} cmp $a->{name} )
39✔
1868
            : ( $b->{comparable_version} <=> $a->{comparable_version} or $a->{name} cmp $b->{name} )
1869
        )
1870
    } @result;
1871
}
1872

1873
sub compose_locallib {
1874
    my ( $self, $perl_name, $lib_name ) = @_;
469✔
1875
    return join '@', $perl_name, $lib_name;
469✔
1876
}
1877

469✔
1878
sub decompose_locallib {
1879
    my ( $self, $name ) = @_;
1880
    return split '@', $name;
1881
}
136✔
1882

1883
sub enforce_localib {
136✔
1884
    my ( $self, $name ) = @_;
1885
    $name =~ s/^/@/ unless $name =~ m/@/;
1886
    return $name;
1887
}
×
1888

×
1889
sub local_libs {
×
1890
    my ( $self, $perl_name ) = @_;
1891

1892
    my $current = $self->current_env;
1893
    my @libs    = map {
1894
        my $name = $_->basename;
35✔
1895
        my ( $p, $l ) = $self->decompose_locallib($name);
35✔
1896
        +{
1897
            name       => $name,
35✔
1898
            is_current => $name eq $current,
34✔
1899
            perl_name  => $p,
1900
            lib_name   => $l,
34✔
1901
            dir        => $_,
×
1902
        }
1903
    } $self->home->child("libs")->children;
1904
    if ($perl_name) {
34✔
1905
        @libs = grep { $perl_name eq $_->{perl_name} } @libs;
×
1906
    }
1907
    return @libs;
1908
}
1909

35✔
1910
sub is_installed {
1911
    my ( $self, $name ) = @_;
1912

1913
    return grep { $name eq $_->{name} } $self->installed_perls;
1914
}
1915

1916
sub assert_known_installation {
35✔
1917
    my ( $self, $name ) = @_;
35✔
1918
    return 1 if $self->is_installed($name);
35✔
1919
    die "ERROR: The installation \"$name\" is unknown\n\n";
35✔
1920
}
35✔
1921

35✔
1922
# Return a hash of PERLBREW_* variables
×
1923
sub perlbrew_env {
1924
    my ( $self, $name ) = @_;
1925
    my ( $perl_name, $lib_name );
35✔
1926

34✔
1927
    if ($name) {
34✔
1928
        ( $perl_name, $lib_name ) = $self->resolve_installation_name($name);
34✔
1929

34✔
1930
        unless ($perl_name) {
34✔
1931
            die "\nERROR: The installation \"$name\" is unknown.\n\n";
1932
        }
1933

34✔
1934
        unless ( !$lib_name || grep { $_->{lib_name} eq $lib_name } $self->local_libs($perl_name) ) {
2✔
1935
            die "\nERROR: The lib name \"$lib_name\" is unknown.\n\n";
1936
        }
2✔
1937
    }
1938

2✔
1939
    my %env = (
2✔
1940
        PERLBREW_VERSION => $VERSION,
1941
        PERLBREW_PATH    => $self->root->bin,
2✔
1942
        PERLBREW_MANPATH => "",
1943
        PERLBREW_ROOT    => $self->root
1✔
1944
    );
1945

1946
    require local::lib;
1947
    my $pb_home                   = $self->home;
2✔
1948
    my $current_local_lib_root    = $self->env("PERL_LOCAL_LIB_ROOT") || "";
2✔
1949
    my $current_local_lib_context = local::lib->new;
2✔
1950
    my @perlbrew_local_lib_root   = uniq( grep { /\Q${pb_home}\E/ } split( /:/, $current_local_lib_root ) );
1951
    if ( $current_local_lib_root =~ /^\Q${pb_home}\E/ ) {
1952
        $current_local_lib_context = $current_local_lib_context->activate($_) for @perlbrew_local_lib_root;
1953
    }
32✔
1954

32✔
1955
    if ($perl_name) {
1956
        my $installation = $self->root->perls($perl_name);
1957
        if ( -d $installation->child("bin") ) {
34✔
1958
            $env{PERLBREW_PERL} = $perl_name;
34✔
1959
            $env{PERLBREW_PATH} .= ":" . $installation->child("bin");
34✔
1960
            $env{PERLBREW_MANPATH} = $installation->child("man");
72✔
1961
        }
1962

1963
        if ($lib_name) {
1964
            $current_local_lib_context = $current_local_lib_context->deactivate($_) for @perlbrew_local_lib_root;
1✔
1965

1966
            my $base = $self->home->child( "libs", "${perl_name}\@${lib_name}" );
1✔
1967

1✔
1968
            if ( -d $base ) {
1✔
1969
                $current_local_lib_context = $current_local_lib_context->activate($base);
2✔
1970

1971
                if ( $self->env('PERLBREW_LIB_PREFIX') ) {
1✔
1972
                    unshift
1✔
1973
                        @{ $current_local_lib_context->libs },
1974
                        $self->env('PERLBREW_LIB_PREFIX');
1975
                }
35✔
1976

1977
                $env{PERLBREW_PATH}    = $base->child("bin") . ":" . $env{PERLBREW_PATH};
1978
                $env{PERLBREW_MANPATH} = $base->child("man") . ":" . $env{PERLBREW_MANPATH};
1979
                $env{PERLBREW_LIB}     = $lib_name;
5✔
1980
            }
5✔
1981
        }
1982
        else {
5✔
1983
            $current_local_lib_context = $current_local_lib_context->deactivate($_) for @perlbrew_local_lib_root;
2✔
1984
            $env{PERLBREW_LIB} = undef;
8✔
1985
        }
8✔
1986

2✔
1987
        my %ll_env = $current_local_lib_context->build_environment_vars;
1988
        delete $ll_env{PATH};
1989
        for my $key ( keys %ll_env ) {
1990
            $env{$key} = $ll_env{$key};
1991
        }
3✔
1992
    }
1993
    else {
1994
        $current_local_lib_context = $current_local_lib_context->deactivate($_) for @perlbrew_local_lib_root;
1995

12✔
1996
        my %ll_env = $current_local_lib_context->build_environment_vars;
1997
        delete $ll_env{PATH};
1998
        for my $key ( keys %ll_env ) {
1999
            $env{$key} = $ll_env{$key};
2000
        }
2001
        $env{PERLBREW_LIB}  = undef;
2002
        $env{PERLBREW_PERL} = undef;
12✔
2003
    }
4✔
2004

2005
    return %env;
2006
}
2007

2008
sub run_command_list {
5✔
2009
    my $self       = shift;
2010
    my $is_verbose = $self->{verbose};
2011

2012
    if ( $self->{'no-decoration'} ) {
×
2013
        for my $i ( $self->installed_perls ) {
×
2014
            print $i->{name} . "\n";
2015
            for my $lib ( @{ $i->{libs} } ) {
×
2016
                print $lib->{name} . "\n";
2017
            }
×
2018
        }
×
2019
    }
2020
    else {
×
2021
        for my $i ( $self->installed_perls ) {
×
2022
            printf "%-2s%-20s %-20s %s\n", $i->{is_current} ? '*' : '', $i->{name},
×
2023
                (
2024
                  $is_verbose
2025
                ? ( index( $i->{name}, $i->{version} ) < 0 )
2026
                        ? "($i->{version})"
2027
                        : ''
2028
                : ''
2029
                ),
2030
                ( $is_verbose ? "(installed on $i->{ctime})" : '' );
2031

2032
            for my $lib ( @{ $i->{libs} } ) {
2033
                print $lib->{is_current} ? "* " : "  ", $lib->{name}, "\n";
2034
            }
2035
        }
2036
    }
2037

2038
    return 0;
2039
}
2040

2041
sub launch_sub_shell {
2042
    my ( $self, $name ) = @_;
×
2043
    my $shell = $self->env('SHELL');
2044

×
2045
    my $shell_opt = "";
×
2046

2047
    if ( $shell =~ /\/zsh\d?$/ ) {
2048
        $shell_opt = "-d -f";
×
2049

2050
        if ( $^O eq 'darwin' ) {
×
2051
            my $root_dir = $self->root;
2052
            print <<"WARNINGONMAC";
2053
--------------------------------------------------------------------------------
×
2054
WARNING: zsh perlbrew sub-shell is not working on Mac OSX Lion.
×
2055

68✔
2056
It is known that on MacOS Lion, zsh always resets the value of PATH on launching
×
2057
a sub-shell. Effectively nullify the changes required by perlbrew sub-shell. You
2058
may `echo \$PATH` to examine it and if you see perlbrew related paths are in the
×
2059
end, instead of in the beginning, you are unfortunate.
2060

×
2061
You are advised to include the following line to your ~/.zshenv as a better
×
2062
way to work with perlbrew:
×
2063

2064
    source $root_dir/etc/bashrc
2065

2066
--------------------------------------------------------------------------------
×
2067
WARNINGONMAC
×
2068

2069
        }
×
2070
    }
×
2071

×
2072
    my %env = ( $self->perlbrew_env($name), PERLBREW_SKIP_INIT => 1 );
×
2073

2074
    unless ( $ENV{PERLBREW_VERSION} ) {
2075
        my $root = $self->root;
×
2076

2077
        # The user does not source bashrc/csh in their shell initialization.
×
2078
        $env{PATH}    = $env{PERLBREW_PATH} . ":" . join ":", grep { !/$root\/bin/ } split ":", $ENV{PATH};
2079
        $env{MANPATH} = $env{PERLBREW_MANPATH} . ":" . join ":",
2080
            grep { !/$root\/man/ } ( defined( $ENV{MANPATH} ) ? split( ":", $ENV{MANPATH} ) : () );
×
2081
    }
2082

2083
    my $command = "env ";
2084
    while ( my ( $k, $v ) = each(%env) ) {
2085
        no warnings "uninitialized";
×
2086
        $command .= "$k=\"$v\" ";
2087
    }
×
2088
    $command .= " $shell $shell_opt";
×
2089

×
2090
    my $pretty_name = defined($name) ? $name : "the default perl";
×
2091
    print "\nA sub-shell is launched with $pretty_name as the activated perl. Run 'exit' to finish it.\n\n";
2092
    exec($command);
2093
}
×
2094

2095
sub run_command_use {
2096
    my $self = shift;
2097
    my $perl = shift;
×
2098

2099
    if ( !$perl ) {
×
2100
        my $current = $self->current_env;
2101
        if ($current) {
2102
            print "Currently using $current\n";
×
2103
        }
2104
        else {
×
2105
            print "No version in use; defaulting to system\n";
×
2106
        }
×
2107
        return;
×
2108
    }
2109

×
2110
    $self->launch_sub_shell($perl);
×
2111

2112
}
×
2113

2114
sub run_command_switch {
2115
    my ( $self, $dist, $alias ) = @_;
×
2116

2117
    unless ($dist) {
2118
        my $current = $self->current_env;
2119
        printf "Currently switched %s\n", ( $current ? "to $current" : 'off' );
2120
        return;
×
2121
    }
×
2122

2123
    $self->switch_to( $dist, $alias );
2124
}
2125

×
2126
sub switch_to {
×
2127
    my ( $self, $dist, $alias ) = @_;
2128

×
2129
    die "Cannot use for alias something that starts with 'perl-'\n"
×
2130
        if $alias && $alias =~ /^perl-/;
2131

×
2132
    die "${dist} is not installed\n" unless -d $self->root->perls($dist);
×
2133

×
2134
    if ( $self->env("PERLBREW_SHELLRC_VERSION") && $self->current_shell_is_bashish ) {
2135
        local $ENV{PERLBREW_PERL} = $dist;
2136
        my $HOME    = $self->env('HOME');
2137
        my $pb_home = $self->home;
5✔
2138

5✔
2139
        $pb_home->mkpath;
2140
        system( "$0 env $dist > " . $pb_home->child("init") );
5✔
2141

5✔
2142
        print "Switched to $dist.\n\n";
37✔
2143
    }
37✔
2144
    else {
31✔
2145
        $self->launch_sub_shell($dist);
31✔
2146
    }
2147
}
2148

6✔
2149
sub run_command_off {
2150
    my $self = shift;
2151
    $self->launch_sub_shell;
2152
}
5✔
2153

2154
sub run_command_switch_off {
5✔
2155
    my $self    = shift;
3✔
2156
    my $pb_home = $self->home;
28✔
2157

28✔
2158
    $pb_home->mkpath;
3✔
2159
    system( "env PERLBREW_PERL= $0 env > " . $pb_home->child("init") );
2160

2161
    print "\nperlbrew is switched off. Please exit this shell and start a new one to make it effective.\n";
25✔
2162
    print
25✔
2163
        "To immediately make it effective, run this line in this terminal:\n\n    exec @{[ $self->env('SHELL') ]}\n\n";
2164
}
2165

2166
sub shell_env {
2167
    my ( $self, $env ) = @_;
2✔
2168
    my %env = %$env;
9✔
2169

9✔
2170
    my @statements;
3✔
2171
    for my $k ( sort keys %env ) {
2172
        my $v = $env{$k};
2173
        if ( defined($v) && $v ne '' ) {
6✔
2174
            $v =~ s/(\\")/\\$1/g;
2175
            push @statements, ["set", $k, $v];
2176
        }
2177
        else {
2178
            push @statements, ["unset", $k];
5✔
2179
        }
2180
    }
2181

2182
    my $statements = "";
3✔
2183

2184
    if ( $self->env('SHELL') =~ /(ba|k|z|\/)sh\d?$/ ) {
3✔
2185
        for (@statements) {
2186
            my ( $o, $k, $v ) = @$_;
2187
            if ( $o eq 'unset' ) {
2188
                $statements .= "unset $k\n";
2✔
2189
            }
2✔
2190
            else {
2191
                $v =~ s/(\\")/\\$1/g;
2✔
2192
                $statements .= "export $k=\"$v\"\n";
×
2193
            }
2194
        }
2195
    }
2✔
2196
    else {
2✔
2197
        for (@statements) {
×
2198
            my ( $o, $k, $v ) = @$_;
×
2199
            if ( $o eq 'unset' ) {
2200
                $statements .= "unsetenv $k\n";
×
2201
            }
×
2202
            else {
2203
                $statements .= "setenv $k \"$v\"\n";
2204
            }
2205
        }
2206
    }
2207

2✔
2208
    return $statements;
2209
}
2210

2211
sub run_command_env {
2212
    my ( $self, $name ) = @_;
1✔
2213

1✔
2214
    print $self->shell_env({ $self->perlbrew_env($name) });
1✔
2215
}
2216

2✔
2217
sub run_command_symlink_executables {
2218
    my ( $self, @perls ) = @_;
2219
    my $root = $self->root;
2220

3✔
2221
    unless (@perls) {
3✔
2222
        @perls = map { $_->name } grep { -d $_ && !-l $_ } $root->perls->list;
2223
    }
2224

2225
    for my $perl (@perls) {
2226
        for my $executable ( $root->perls($perl)->bin->children ) {
3✔
2227
            my ( $name, $version ) = $executable =~ m/bin\/(.+?)(5\.\d.*)?$/;
3✔
2228
            next unless $version;
2229

2230
            $executable->symlink( $root->perls($perl)->bin($name) );
2231
            $executable->symlink( $root->perls($perl)->perl ) if $name eq "cperl";
×
2232
        }
2233
    }
×
2234
}
×
2235

×
2236
sub run_command_install_patchperl {
2237
    my ($self) = @_;
2238
    $self->do_install_program_from_url(
×
2239
        'https://raw.githubusercontent.com/gugod/patchperl-packing/master/patchperl',
×
2240
        'patchperl',
2241
        sub {
×
2242
            my ($body) = @_;
2243
            $body =~ s/\A#!.+?\n/ $self->system_perl_shebang . "\n" /se;
×
2244
            return $body;
×
2245
        }
×
2246
    );
×
2247
}
×
2248

2249
sub run_command_install_cpanm {
2250
    my ($self) = @_;
×
2251
    $self->do_install_program_from_url(
×
2252
        'https://raw.githubusercontent.com/miyagawa/cpanminus/master/cpanm' => 'cpanm' );
2253
}
2254

×
2255
sub run_command_install_cpm {
×
2256
    my ($self) = @_;
×
2257
    $self->do_install_program_from_url( 'https://raw.githubusercontent.com/skaji/cpm/main/cpm' => 'cpm' );
×
2258
}
2259

2260
sub run_command_self_upgrade {
×
2261
    my ($self) = @_;
2262

×
2263
    require FindBin;
×
2264
    unless ( -w $FindBin::Bin ) {
2265
        die "Your perlbrew installation appears to be system-wide.  Please upgrade through your package manager.\n";
2266
    }
2267

×
2268
    my $TMPDIR       = $ENV{TMPDIR} || "/tmp";
2269
    my $TMP_PERLBREW = App::Perlbrew::Path->new( $TMPDIR, "perlbrew" );
×
2270

×
2271
    http_download( 'https://raw.githubusercontent.com/gugod/App-perlbrew/master/perlbrew', $TMP_PERLBREW );
×
2272

2273
    chmod 0755, $TMP_PERLBREW;
2274
    my $new_version = qx($TMP_PERLBREW version);
×
2275
    chomp $new_version;
2276
    if ( $new_version =~ /App::perlbrew\/(\d+\.\d+)$/ ) {
×
2277
        $new_version = $1;
2278
    }
×
2279
    else {
2280
        $TMP_PERLBREW->unlink;
×
2281
        die "Unable to detect version of new perlbrew!\n";
×
2282
    }
×
2283

2284
    if ( $new_version <= $VERSION ) {
×
2285
        print "Your perlbrew is up-to-date (version $VERSION).\n" unless $self->{quiet};
2286
        $TMP_PERLBREW->unlink;
×
2287
        return;
×
2288
    }
×
2289

×
2290
    print "Upgrading from $VERSION to $new_version\n" unless $self->{quiet};
2291

2292
    system $TMP_PERLBREW, "self-install";
2293
    $TMP_PERLBREW->unlink;
2294
}
2295

2296
sub run_command_uninstall {
2297
    my ( $self, $target ) = @_;
×
2298

×
2299
    unless ($target) {
×
2300
        $self->run_command_help("uninstall");
×
2301
        exit(-1);
×
2302
    }
2303

2304
    my @installed = $self->installed_perls(@_);
2305

×
2306
    my ($to_delete) = grep { $_->{name} eq $target } @installed;
×
2307

2308
    die "'$target' is not installed\n" unless $to_delete;
2309

2310
    my @dir_to_delete;
2311
    for ( @{ $to_delete->{libs} } ) {
17✔
2312
        push @dir_to_delete, $_->{dir};
17✔
2313
    }
2314
    push @dir_to_delete, $to_delete->{dir};
17✔
2315

2316
    my $ans = ( $self->{yes} ) ? "Y" : undef;
17✔
2317
    if ( !defined($ans) ) {
17✔
2318
        $ans = prompt(
2319
            "\nThe following perl+lib installation(s) will be deleted:\n\n\t"
17✔
2320
                . join( "\n\t", @dir_to_delete )
17✔
2321
                . "\n\n... are you sure ? [y/N]",
17✔
2322
            "N"
2323
        );
17✔
2324
    }
17✔
2325

14✔
2326
    if ( $ans =~ /^Y/i ) {
2327
        for (@dir_to_delete) {
14✔
2328
            print "Deleting: $_\n" unless $self->{quiet};
21✔
2329
            App::Perlbrew::Path->new($_)->rmpath;
21✔
2330
            print "Deleted:  $_\n" unless $self->{quiet};
21✔
2331
        }
21✔
2332
    }
14✔
2333
    else {
2334
        print "\nOK. Not deleting anything.\n\n";
14✔
2335
        return;
2336
    }
2337
}
2338

12✔
2339
sub run_command_exec {
3✔
2340
    my $self = shift;
2341
    my %opts;
2342

17✔
2343
    local (@ARGV) = @{ $self->{original_argv} };
2344

2345
    Getopt::Long::Configure( 'require_order', 'nopass_through' );
2346
    my @command_options = ( 'with=s', 'halt-on-error', 'min=s', 'max=s' );
1✔
2347

2348
    $self->parse_cmdline( \%opts, @command_options );
2349
    shift @ARGV;    # "exec"
17✔
2350
    $self->parse_cmdline( \%opts, @command_options );
1✔
2351

2352
    my @exec_with;
2353
    if ( $opts{with} ) {
17✔
2354
        my %installed = map { $_->{name} => $_ } map { ( $_, @{ $_->{libs} } ) } $self->installed_perls;
×
2355

2356
        my $d    = ( $opts{with} =~ m/ / ) ? qr( +) : qr(,+);
2357
        my @with = grep { $_ } map {
17✔
2358
            my ( $p, $l ) = $self->resolve_installation_name($_);
17✔
2359
            $p .= "\@$l" if $l;
9✔
2360
            $p;
2361
        } split $d, $opts{with};
2362

17✔
2363
        @exec_with = map { $installed{$_} } @with;
17✔
2364
    }
28✔
2365
    else {
28✔
2366
        @exec_with = grep {
2367
            not -l $self->root->perls( $_->{name} );    # Skip Aliases
28✔
2368
        } map { ( $_, @{ $_->{libs} } ) } $self->installed_perls;
28✔
2369
    }
28✔
2370

28✔
2371
    if ( $opts{min} ) {
28✔
2372

2373
        # TODO use comparable version.
28✔
2374
        # For now, it doesn't produce consistent results for 5.026001 and 5.26.1
2375
        @exec_with = grep { $_->{orig_version} >= $opts{min} } @exec_with;
28✔
2376
    }
8✔
2377

2378
    if ( $opts{max} ) {
2379
        @exec_with = grep { $_->{orig_version} <= $opts{max} } @exec_with;
8✔
2380
    }
8✔
2381

2382
    if ( 0 == @exec_with ) {
8✔
2383
        print "No perl installation found.\n" unless $self->{quiet};
7✔
2384
    }
2385

2386
    my $no_header = 0;
7✔
2387
    if ( 1 == @exec_with ) {
2388
        $no_header = 1;
2389
    }
7✔
2390

2391
    my $overall_success = 1;
2392
    for my $i (@exec_with) {
8✔
2393
        my %env = $self->perlbrew_env( $i->{name} );
2394
        next if !$env{PERLBREW_PERL};
25✔
2395

2396
        local %ENV = %ENV;
14✔
2397
        $ENV{$_}       = defined $env{$_} ? $env{$_} : '' for keys %env;
2398
        $ENV{PATH}     = join( ':', $env{PERLBREW_PATH},    $ENV{PATH} );
2399
        $ENV{MANPATH}  = join( ':', $env{PERLBREW_MANPATH}, $ENV{MANPATH} || "" );
2400
        $ENV{PERL5LIB} = $env{PERL5LIB} || "";
×
2401

×
2402
        print "$i->{name}\n==========\n" unless $no_header || $self->{quiet};
×
2403

2404
        if ( my $err = $self->do_system_with_exit_code(@ARGV) ) {
×
2405
            my $exit_code = $err >> 8;
×
2406

×
2407
         # return 255 for case when process was terminated with signal, in that case real exit code is useless and weird
2408
            $exit_code       = 255 if $exit_code > 255;
2409
            $overall_success = 0;
×
2410

×
2411
            unless ( $self->{quiet} ) {
×
2412
                print "Command terminated with non-zero status.\n";
×
2413

2414
                print STDERR "Command ["
2415
                    . join( ' ', map { /\s/ ? "'$_'" : $_ } @ARGV )
×
2416
                    .    # trying reverse shell escapes - quote arguments containing spaces
2417
                    "] terminated with exit code $exit_code (\$? = $err) under the following perl environment:\n";
2418
                print STDERR $self->format_info_output;
2419
            }
1✔
2420

2421
            $self->do_exit_with_error_code($exit_code) if ( $opts{'halt-on-error'} );
1✔
2422
        }
×
2423
        print "\n" unless $self->{quiet} || $no_header;
×
2424
    }
2425
    $self->do_exit_with_error_code(1) unless $overall_success;
2426
}
1✔
2427

1✔
2428
sub run_command_clean {
2429
    my ($self)     = @_;
1✔
2430
    my $root       = $self->root;
×
2431
    my @build_dirs = $root->build->children;
2432

2433
    for my $dir (@build_dirs) {
1✔
2434
        print "Removing $dir\n";
×
2435
        App::Perlbrew::Path->new($dir)->rmpath;
2436
    }
×
2437

×
2438
    my @tarballs = $root->dists->children;
2439
    for my $file (@tarballs) {
2440
        print "Removing $file\n";
×
2441
        $file->unlink;
×
2442
    }
2443

2444
    print "\nDone\n";
×
2445
}
2446

×
2447
sub run_command_alias {
×
2448
    my ( $self, $cmd, $name, $alias ) = @_;
2449

2450
    unless ($cmd) {
×
2451
        $self->run_command_help("alias");
2452
        exit(-1);
2453
    }
×
2454

2455
    my $path_name  = $self->root->perls($name)  if $name;
×
2456
    my $path_alias = $self->root->perls($alias) if $alias;
×
2457

2458
    if ( $alias && -e $path_alias && !-l $path_alias ) {
2459
        die "\nABORT: The installation name `$alias` is not an alias, cannot override.\n\n";
×
2460
    }
×
2461

2462
    if ( $cmd eq 'create' ) {
2463
        $self->assert_known_installation($name);
×
2464

2465
        if ( $self->is_installed($alias) && !$self->{force} ) {
2466
            die "\nABORT: The installation `${alias}` already exists. Cannot override.\n\n";
×
2467
        }
2468

2469
        $path_alias->unlink;
1✔
2470
        $path_name->symlink($path_alias);
2471
    }
2472
    elsif ( $cmd eq 'delete' ) {
2473
        $self->assert_known_installation($name);
2474

×
2475
        unless ( -l $path_name ) {
2476
            die "\nABORT: The installation name `$name` is not an alias, cannot remove.\n\n";
2477
        }
2478

×
2479
        $path_name->unlink;
2480
    }
2481
    elsif ( $cmd eq 'rename' ) {
2482
        $self->assert_known_installation($name);
×
2483

2484
        unless ( -l $path_name ) {
2485
            die "\nABORT: The installation name `$name` is not an alias, cannot rename.\n\n";
2486
        }
12✔
2487

2488
        if ( -l $path_alias && !$self->{force} ) {
12✔
2489
            die "\nABORT: The alias `$alias` already exists, cannot rename to it.\n\n";
×
2490
        }
×
2491

2492
        rename( $path_name, $path_alias );
2493
    }
12✔
2494
    elsif ( $cmd eq 'help' ) {
12✔
2495
        $self->run_command_help("alias");
11✔
2496
    }
2497
    else {
2498
        die "\nERROR: Unrecognized action: `${cmd}`.\n\n";
1✔
2499
    }
2500
}
2501

2502
sub run_command_display_bashrc {
2503
    print BASHRC_CONTENT();
8✔
2504
}
2505

8✔
2506
sub run_command_display_cshrc {
2507
    print CSHRC_CONTENT();
7✔
2508
}
2509

7✔
2510
sub run_command_display_installation_failure_message {
2511
    my ($self) = @_;
7✔
2512
}
1✔
2513

1✔
2514
sub run_command_lib {
2515
    my ( $self, $subcommand, @args ) = @_;
2516

6✔
2517
    unless ($subcommand) {
6✔
2518
        $self->run_command_help("lib");
2519
        exit(-1);
6✔
2520
    }
×
2521

2522
    my $sub = "run_command_lib_$subcommand";
2523
    if ( $self->can($sub) ) {
6✔
2524
        $self->$sub(@args);
2525
    }
6✔
2526
    else {
2527
        print "Unknown command: $subcommand\n";
6✔
2528
    }
2529
}
2530

2531
sub run_command_lib_create {
3✔
2532
    my ( $self, $name ) = @_;
2533

3✔
2534
    die "ERROR: No lib name\n", $self->run_command_help( "lib", undef, 'return_text' ) unless $name;
2535

2✔
2536
    $name = $self->enforce_localib($name);
2537

2✔
2538
    my ( $perl_name, $lib_name ) = $self->resolve_installation_name($name);
2539

2✔
2540
    if ( !$perl_name ) {
2541
        my ( $perl_name, $lib_name ) = $self->decompose_locallib($name);
2✔
2542
        die "ERROR: '$perl_name' is not installed yet, '$name' cannot be created.\n";
2543
    }
2✔
2544

2545
    my $fullname = $self->compose_locallib( $perl_name, $lib_name );
2✔
2546
    my $dir      = $self->home->child( "libs", $fullname );
2547

1✔
2548
    if ( -d $dir ) {
×
2549
        die "$fullname is already there.\n";
2550
    }
2551

1✔
2552
    $dir->mkpath;
2553

2554
    print "lib '$fullname' is created.\n" unless $self->{quiet};
1✔
2555

2556
    return;
2557
}
1✔
2558

2559
sub run_command_lib_delete {
2560
    my ( $self, $name ) = @_;
1✔
2561

2562
    die "ERROR: No lib to delete\n", $self->run_command_help( "lib", undef, 'return_text' ) unless $name;
2563

2564
    $name = $self->enforce_localib($name);
×
2565

×
2566
    my ( $perl_name, $lib_name ) = $self->resolve_installation_name($name);
×
2567

2568
    my $fullname = $self->compose_locallib( $perl_name, $lib_name );
×
2569

×
2570
    my $current = $self->current_env;
2571

×
2572
    my $dir = $self->home->child( "libs", $fullname );
×
2573

×
2574
    if ( -d $dir ) {
×
2575

2576
        if ( $fullname eq $current ) {
2577
            die "$fullname is currently being used in the current shell, it cannot be deleted.\n";
2578
        }
2579

×
2580
        $dir->rmpath;
2581

×
2582
        print "lib '$fullname' is deleted.\n"
2583
            unless $self->{quiet};
×
2584
    }
2585
    else {
×
2586
        die "ERROR: '$fullname' does not exist.\n";
×
2587
    }
×
2588

2589
    return;
2590
}
×
2591

2592
sub run_command_lib_list {
×
2593
    my ($self) = @_;
×
2594
    my $dir = $self->home->child("libs");
2595
    return unless -d $dir;
2596

×
2597
    opendir my $dh, $dir or die "open $dir failed: $!";
×
2598
    my @libs = grep { !/^\./ && /\@/ } readdir($dh);
2599

2600
    my $current = $self->current_env;
×
2601
    for (@libs) {
2602
        print $current eq $_ ? "* " : "  ";
×
2603
        print "$_\n";
2604
    }
×
2605
}
×
2606

×
2607
sub run_command_upgrade_perl {
×
2608
    my ($self) = @_;
×
2609

2610
    my $PERL_VERSION_RE = qr/(\d+)\.(\d+)\.(\d+)/;
2611

2612
    my ($current) = grep { $_->{is_current} } $self->installed_perls;
2613

×
2614
    unless ( defined $current ) {
×
2615
        print "no perlbrew environment is currently in use\n";
×
2616
        exit(1);
2617
    }
2618

×
2619
    my ( $major, $minor, $release );
×
2620

2621
    if ( $current->{version} =~ /^$PERL_VERSION_RE$/ ) {
×
2622
        ( $major, $minor, $release ) = ( $1, $2, $3 );
×
2623
    }
×
2624
    else {
2625
        print "unable to parse version '$current->{version}'\n";
×
2626
        exit(1);
×
2627
    }
×
2628

×
2629
    my @available = grep { /^perl-$major\.$minor/ } $self->available_perls;
×
2630

×
2631
    my $latest_available_perl = $release;
2632

2633
    foreach my $perl (@available) {
×
2634
        if ( $perl =~ /^perl-$PERL_VERSION_RE$/ ) {
2635
            my $this_release = $3;
2636
            if ( $this_release > $latest_available_perl ) {
2637
                $latest_available_perl = $this_release;
1✔
2638
            }
2639
        }
1✔
2640
    }
2641

2642
    if ( $latest_available_perl == $release ) {
1✔
2643
        print "This perlbrew environment ($current->{name}) is already up-to-date.\n";
2644
        exit(0);
2645
    }
2646

1✔
2647
    my $dist_version = "$major.$minor.$latest_available_perl";
2648
    my $dist         = "perl-$dist_version";
1✔
2649

×
2650
    print "Upgrading $current->{name} to $dist_version\n" unless $self->{quiet};
×
2651
    local $self->{as}        = $current->{name};
×
2652
    local $self->{dist_name} = $dist;
×
2653

2654
    my @d_options  = map { '-D' . $flavor{$_}->{d_option} } keys %flavor;
2655
    my %sub_config = map { $_ => $Config{$_} } grep { /^config_arg\d/ } keys %Config;
×
2656
    for my $value ( values %sub_config ) {
2657
        my $value_wo_D = $value;
2658
        $value_wo_D =~ s/^-D//;
1✔
2659
        push @{ $self->{D} }, $value_wo_D if grep { /$value/ } @d_options;
2660
    }
2661

2662
    $self->do_install_release( $dist, $dist_version );
2663
}
2664

2665
sub list_modules {
2666
    my ( $self, $env ) = @_;
1✔
2667

2668
    $env ||= $self->current_env;
2669
    my ( $stdout, $stderr, $success ) = Capture::Tiny::capture(
2670
        sub {
×
2671
            __PACKAGE__->new( "--quiet", "exec", "--with", $env, 'perl', '-MExtUtils::Installed', '-le',
×
2672
                'BEGIN{@INC=grep {$_ ne q!.!} @INC}; print for ExtUtils::Installed->new->modules;',
×
2673
            )->run;
2674
        }
2675
    );
2676

82✔
2677
    unless ($success) {
82✔
2678
        unless ( $self->{quiet} ) {
2679
            print STDERR "Failed to retrive the list of installed modules.\n";
81✔
2680
            if ( $self->{verbose} ) {
81✔
2681
                print STDERR "STDOUT\n======\n$stdout\nSTDERR\n======\n$stderr\n";
81✔
2682
            }
2683
        }
81✔
2684
        return [];
6✔
2685
    }
3✔
2686

2687
    my %rename = (
2688
        "ack"                    => "App::Ack",
3✔
2689
        "libwww::perl"           => "LWP",
2690
        "libintl-perl"           => "Locale::Messages",
2691
        "Role::Identifiable"     => "Role::Identifiable::HasTags",
2692
        "TAP::Harness::Multiple" => "TAP::Harness::ReportByDescription",
78✔
2693
    );
2694

2695
    return [map { $rename{$_} || $_ } grep { $_ ne "Perl" } split( /\n/, $stdout )];
2696
}
2697

2698
sub run_command_list_modules {
2699
    my ($self) = @_;
2700
    my ( $modules, $error ) = $self->list_modules();
2701
    print "$_\n" for @$modules;
2702
}
2703

2704
sub resolve_installation_name {
2705
    my ( $self, $name ) = @_;
2706
    die "App::perlbrew->resolve_installation_name requires one argument." unless $name;
2707

2708
    my ( $perl_name, $lib_name ) = $self->decompose_locallib($name);
2709
    $perl_name = $name unless $lib_name;
2710
    $perl_name ||= $self->current_perl;
2711

2712
    if ( !$self->is_installed($perl_name) ) {
2713
        if ( $self->is_installed("perl-${perl_name}") ) {
2714
            $perl_name = "perl-${perl_name}";
2715
        }
2716
        else {
2717
            return undef;
2718
        }
2719
    }
2720

4✔
2721
    return wantarray ? ( $perl_name, $lib_name ) : $perl_name;
2722
}
2723

4✔
2724
# Implementation of the 'clone-modules' command.
2725
#
2726
# This method accepts a destination and source installation
2727
# of Perl to clone modules from and into.
2728
# For instance calling
4✔
2729
# $app->run_command_clone_modules($perl_a, $perl_b);
4✔
2730
# installs all modules that have been installed on Perl A
2731
# to the instance of Perl B.
2732
# The source instance is optional, that is if the method
4✔
2733
# is invoked with a single argument, the currently
4✔
2734
# running instance is used as source. Therefore the
2735
# two following calls are the same:
4✔
2736
#
2737
# $app->run_command_clone_modules( $self->current_perl, $perl_b );
2738
# $app->run_command_clone_modules( $perl_b );
2739
#
2740
# Of course, both Perl installation must exist on this
2741
# perlbrew enviroment.
×
2742
#
×
2743
# The method extracts the modules installed on the source Perl
2744
# instance and put them on a temporary file, such file is then
2745
# passed to another instance of the application to
4✔
2746
# execute cpanm on it. The final result is the installation
2747
# of source modules into the destination instance.
4✔
2748
sub run_command_clone_modules {
×
2749
    my $self = shift;
×
2750

2751
    # default to use the currently installation
2752
    my ( $dst_perl, $src_perl );
2753

4✔
2754
    # the first argument is the destination, the second
2755
    # optional argument is the source version, default
2756
    # to use the current installation
2757
    $dst_perl = pop || $self->current_env;
2758
    $src_perl = pop || $self->current_env;
4✔
2759

4✔
2760
    # check source and destination do exist
4✔
2761
    undef $src_perl if ( !$self->resolve_installation_name($src_perl) );
2762
    undef $dst_perl if ( !$self->resolve_installation_name($dst_perl) );
4✔
2763

2764
    if (   !$src_perl
2765
        || !$dst_perl
2766
        || $src_perl eq $dst_perl )
4✔
2767
    {
2768
        # cannot understand from where to where or
4✔
2769
        # the user did specify the same versions
2770
        $self->run_command_help('clone-modules');
4✔
2771
        exit(-1);
4✔
2772
    }
3✔
2773

3✔
2774
    my @modules_to_install = @{ $self->list_modules($src_perl) };
3✔
2775

2776
    unless (@modules_to_install) {
2777
        print "\nNo modules installed on $src_perl !\n" unless $self->{quiet};
2778
        return;
2779
    }
3✔
2780

2781
    print "\nInstalling $#modules_to_install modules from $src_perl to $dst_perl ...\n"
2782
        unless $self->{quiet};
2783

2784
    # create a new application to 'exec' the 'cpanm'
2785
    # with the specified module list
2786

1✔
2787
    my @args = ( qw(--quiet exec --with), $dst_perl, 'cpanm' );
1✔
2788
    push @args, '--notest' if $self->{notest};
2789
    push @args, @modules_to_install;
2790

4✔
2791
    __PACKAGE__->new(@args)->run;
4✔
2792
}
4✔
2793

4✔
2794
sub format_info_output {
16✔
2795
    my ( $self, $module ) = @_;
2796

2797
    my $out = '';
4✔
2798

2✔
2799
    $out .= "Current perl:\n";
2800
    if ( $self->current_perl ) {
2✔
2801
        $out .= "  Name: " . $self->current_env . "\n";
2802
        $out .= "  Path: " . $self->installed_perl_executable( $self->current_perl ) . "\n";
2803
        $out .= "  Config: " . $self->configure_args( $self->current_perl ) . "\n";
2804
        $out .= join(
4✔
2805
            '',
2806
            "  Compiled at: ",
2807
            (
2808
                map { /  Compiled at (.+)\n/ ? $1 : () }
4✔
2809
                    `@{[ $self->installed_perl_executable($self->current_perl) ]} -V`
4✔
2810
            ),
2811
            "\n"
2812
        );
2813
    }
3✔
2814
    else {
2815
        $out .= "Using system perl." . "\n";
3✔
2816
        $out .= "Shebang: " . $self->system_perl_shebang . "\n";
1✔
2817
    }
2818

2819
    $out .= "\nperlbrew:\n";
2✔
2820
    $out .= "  version: " . $self->VERSION . "\n";
2821
    $out .= "  ENV:\n";
2822
    for ( map { "PERLBREW_$_" } qw(ROOT HOME PATH MANPATH) ) {
1✔
2823
        $out .= "    $_: " . ( $self->env($_) || "" ) . "\n";
2824
    }
1✔
2825

1✔
2826
    if ($module) {
1✔
2827
        my $code =
1✔
2828
qq{eval "require $module" and do { (my \$f = "$module") =~ s<::></>g; \$f .= ".pm"; print "$module\n  Location: \$INC{\$f}\n  Version: " . ($module->VERSION ? $module->VERSION : "no VERSION specified" ) } or do { print "$module could not be found, is it installed?" } };
2829
        $out .=
2830
            "\nModule: " . $self->do_capture_current_perl( '-le', $code );
2831
    }
2832

2833
    $out;
2834
}
2835

2836
sub run_command_info {
1✔
2837
    my ($self) = shift;
1✔
2838
    print $self->format_info_output(@_);
1✔
2839
}
1✔
2840

2841
sub run_command_make_shim {
1✔
2842
    my ($self, $program) = @_;
×
2843

2844
    unless ($program) {
2845
        $self->run_command_help("make-shim");
2846
        return;
2847
    }
2848

2849
    my $output = $self->{output} || $program;
10✔
2850

2851
    if (-f $output) {
2852
        die "ERROR: $program already exists under current directory.\n";
2853
    }
2854

2855
    my $current_env = $self->current_env
2856
        or die "ERROR: perlbrew is not activated. make-shim requires an perlbrew environment to be activated.\nRead the usage by running: perlbrew help make-shim\n";
2857

2858
    my %env = $self->perlbrew_env( $current_env );
2859

2860
    my $shebang = '#!' . $self->env('SHELL');
2861
    my $preemble = $self->shell_env(\%env);
2862
    my $path = $self->shell_env({ PATH => $env{"PERLBREW_PATH"} . ":" . $self->env("PATH") });
2863
    my $shim = join(
2864
        "\n",
2865
        $shebang,
2866
        $preemble,
2867
        $path,
2868
        'exec ' . $program . ' "$@"',
2869
        "\n"
2870
    );
2871

2872
    open my $fh, ">", "$output" or die $!;
2873
    print $fh $shim;
2874
    close $fh;
2875
    chmod 0755, $output;
2876

2877
    if ( $self->{verbose} ) {
2878
        print "The shim $output is made.\n";
2879
    }
2880
}
2881

2882
sub run_command_make_pp {
2883
    my ($self, $program) = @_;
2884

2885
    my $current_env = $self->current_env
2886
        or die "ERROR: perlbrew is not activated. make-pp requires an perlbrew environment to be activated.\nRead the usage by running: perlbrew help make-pp\n";
2887
    my $path_pp = $self->whereis_in_env("pp", $current_env)
2888
            or die "ERROR: pp cannot be found in $current_env";
2889

2890
    my $input = $self->{input};
2891
    my $output = $self->{output};
2892

2893
    unless ($input && $output) {
2894
        $self->run_command_help("make-pp");
2895
        return;
2896
    }
2897

2898
    unless (-f $input) {
2899
        die "ERROR: The specified input $input do not exists\n";
2900
    }
2901

2902
    if (-f $output) {
2903
        die "ERROR: $output already exists.\n";
2904
    }
2905

2906
    my $sitelib = $self->do_capture_current_perl(
2907
        '-MConfig',
2908
        '-e',
2909
        'print $Config{sitelibexp}',
2910
    );
2911

2912
    my $privlib = $self->do_capture_current_perl(
2913
        '-MConfig',
2914
        '-e',
2915
        'print $Config{privlibexp}',
2916
    );
2917

2918
    my $locallib;
2919
    if ($self->current_lib) {
2920
        require local::lib;
2921
        my ($current_lib) = grep { $_->{is_current} } $self->local_libs();
2922
        my @llpaths = sort { length($a) <=> length($b) }
2923
            local::lib->lib_paths_for( $current_lib->{dir} );
2924
        $locallib = $llpaths[0];
2925
    }
2926

2927
    my $perlversion = $self->do_capture_current_perl(
2928
        '-MConfig',
2929
        '-e',
2930
        'print $Config{version}',
2931
    );
2932

2933
    my @cmd = (
2934
        $path_pp,
2935
        "-B", # core modules
2936
        "-a", "$privlib;$perlversion",
2937
        "-a", "$sitelib;$perlversion",
2938
        ($locallib ? ("-a", "$locallib;$perlversion") : ()),
2939
        "-z", "9",
2940
        "-o", $output,
2941
        $input,
2942
    );
2943

2944
    $self->do_system(@cmd);
2945
}
2946

2947
sub whereis_in_env {
2948
    my ($self, $program, $env) = @_;
2949
    my %env = $self->perlbrew_env( $env );
2950
    my @paths = split /:/, $env{PERLBREW_PATH};
2951

2952
    my ($path) = grep { -x $_ } map { App::Perlbrew::Path->new($_, $program) } @paths;
2953

2954
    return $path;
2955
}
2956

2957

2958
sub BASHRC_CONTENT() {
2959
    return
2960
          "export PERLBREW_SHELLRC_VERSION=$VERSION\n"
2961
        . ( exists $ENV{PERLBREW_ROOT} ? "export PERLBREW_ROOT=$PERLBREW_ROOT\n" : "" ) . "\n"
2962
        . <<'RC';
2963

2964
__perlbrew_reinit() {
2965
    if [[ ! -d "$PERLBREW_HOME" ]]; then
2966
        mkdir -p "$PERLBREW_HOME"
2967
    fi
2968

2969
    [ -f "$PERLBREW_HOME/init" ] && rm "$PERLBREW_HOME/init"
2970
    echo '# DO NOT EDIT THIS FILE' > "$PERLBREW_HOME/init"
2971
    command perlbrew env $1 | \grep PERLBREW_ >> "$PERLBREW_HOME/init"
2972
    . "$PERLBREW_HOME/init"
2973
    __perlbrew_set_path
2974
}
2975

2976
__perlbrew_purify () {
2977
    local path patharray outsep
2978
    IFS=: read -r${BASH_VERSION+a}${ZSH_VERSION+A} patharray <<< "$1"
2979
    for path in "${patharray[@]}" ; do
2980
        case "$path" in
5✔
2981
            (*"$PERLBREW_HOME"*) ;;
2982
            (*"$PERLBREW_ROOT"*) ;;
2983
            (*) printf '%s' "${outsep:-}$path" ; outsep=: ;;
2984
        esac
2985
    done
2986
}
2987

2988
__perlbrew_set_path () {
2989
    export MANPATH=${PERLBREW_MANPATH:-}${PERLBREW_MANPATH:+:}$(__perlbrew_purify "$(manpath 2>/dev/null)")
2990
    export PATH=${PERLBREW_PATH:-$PERLBREW_ROOT/bin}:$(__perlbrew_purify "$PATH")
2991
    if [[ -o hashall ]] ; then
2992
        hash -r
2993
    fi
2994
}
2995

5✔
2996
__perlbrew_set_env() {
2997
    local code
2998
    code="$($perlbrew_command env $@)" || return $?
2999
    eval "$code"
3000
}
3001

3002
__perlbrew_activate() {
3003
    [[ -n $(alias perl 2>/dev/null) ]] && unalias perl 2>/dev/null
3004

3005
    if [[ -n "${PERLBREW_PERL:-}" ]]; then
3006
          __perlbrew_set_env "${PERLBREW_PERL:-}${PERLBREW_LIB:+@}${PERLBREW_LIB:-}"
3007
    fi
3008

3009
    __perlbrew_set_path
3010
}
3011

3012
__perlbrew_deactivate() {
3013
    __perlbrew_set_env
3014
    unset PERLBREW_PERL
3015
    unset PERLBREW_LIB
3016
    __perlbrew_set_path
3017
}
3018

3019
perlbrew () {
3020
    local exit_status
3021
    local short_option
3022
    export SHELL
3023

3024
    if [[ $1 == -* ]]; then
3025
        short_option=$1
3026
        shift
3027
    else
3028
        short_option=""
3029
    fi
3030

3031
    case $1 in
3032
        (use)
3033
            if [[ -z "$2" ]] ; then
3034
                echo -n "Currently using ${PERLBREW_PERL:-system perl}"
3035
                [ -n "${PERLBREW_LIB:-}" ] && echo -n "@${PERLBREW_LIB:-}"
3036
                echo
3037
            else
3038
                __perlbrew_set_env "$2" && { __perlbrew_set_path ; true ; }
3039
                exit_status="$?"
3040
            fi
3041
            ;;
3042

3043
        (switch)
3044
              if [[ -z "$2" ]] ; then
3045
                  command perlbrew switch
3046
              else
3047
                  perlbrew use $2 && { __perlbrew_reinit $2 ; true ; }
3048
                  exit_status=$?
3049
              fi
3050
              ;;
3051

3052
        (off)
3053
            __perlbrew_deactivate
3054
            echo "perlbrew is turned off."
3055
            ;;
3056

3057
        (switch-off)
3058
            __perlbrew_deactivate
3059
            __perlbrew_reinit
3060
            echo "perlbrew is switched off."
3061
            ;;
3062

3063
        (*)
3064
            command perlbrew $short_option "$@"
3065
            exit_status=$?
3066
            ;;
3067
    esac
3068
    hash -r
3069
    return ${exit_status:-0}
3070
}
3071

3072
[[ -z "${PERLBREW_ROOT:-}" ]] && export PERLBREW_ROOT="$HOME/perl5/perlbrew"
3073
[[ -z "${PERLBREW_HOME:-}" ]] && export PERLBREW_HOME="$HOME/.perlbrew"
3074

3075
if [[ ! -n "${PERLBREW_SKIP_INIT:-}" ]]; then
3076
    if [[ -f "${PERLBREW_HOME:-}/init" ]]; then
3077
        . "$PERLBREW_HOME/init"
3078
    fi
3079
fi
3080

3081
if [[ -f "${PERLBREW_ROOT:-}/bin/perlbrew" ]]; then
3082
    perlbrew_command="${PERLBREW_ROOT:-}/bin/perlbrew"
3083
else
3084
    perlbrew_command="perlbrew"
3085
fi
3086

3087
__perlbrew_activate
3088

3089
RC
3090

3091
}
3092

3093
sub BASH_COMPLETION_CONTENT() {
3094
    return <<'COMPLETION';
3095
if [[ -n ${ZSH_VERSION-} ]]; then
3096
    autoload -U +X bashcompinit && bashcompinit
3097
fi
3098

3099
export PERLBREW="command perlbrew"
3100
_perlbrew_compgen()
3101
{
3102
    COMPREPLY=( $($PERLBREW compgen $COMP_CWORD ${COMP_WORDS[*]}) )
3103
}
3104
complete -F _perlbrew_compgen perlbrew
3105
COMPLETION
3106
}
3107

3108
sub PERLBREW_FISH_CONTENT {
3109
    return "set -x PERLBREW_SHELLRC_VERSION $VERSION\n" . <<'END';
3110

3111
function __perlbrew_reinit
3112
    if not test -d "$PERLBREW_HOME"
3113
        mkdir -p "$PERLBREW_HOME"
3114
    end
3115

3116
    echo '# DO NOT EDIT THIS FILE' > "$PERLBREW_HOME/init"
3117
    command perlbrew env $argv[1] | \grep PERLBREW_ >> "$PERLBREW_HOME/init"
3118
    __source_init
3119
    __perlbrew_set_path
3120
end
3121

3122
function __perlbrew_set_path
3123
    set -l MANPATH_WITHOUT_PERLBREW (perl -e 'print join ":", grep { index($_, $ENV{PERLBREW_HOME}) < 0 } grep { index($_, $ENV{PERLBREW_ROOT}) < 0 } split/:/,qx(manpath 2> /dev/null);')
3124

3125
    if test -n "$PERLBREW_MANPATH"
3126
        set -l PERLBREW_MANPATH $PERLBREW_MANPATH":"
3127
        set -x MANPATH {$PERLBREW_MANPATH}{$MANPATH_WITHOUT_PERLBREW}
3128
    else
3129
        set -x MANPATH $MANPATH_WITHOUT_PERLBREW
3130
    end
3131

3132
    set -l PATH_WITHOUT_PERLBREW (eval $perlbrew_command display-pristine-path | perl -pe'y/:/ /')
3133

3134
    # silencing stderr in case there's a non-existent path in $PATH (see GH#446)
3135
    if test -n "$PERLBREW_PATH"
3136
        set -x PERLBREW_PATH (echo $PERLBREW_PATH | perl -pe 'y/:/ /' )
3137
        eval set -x PATH $PERLBREW_PATH $PATH_WITHOUT_PERLBREW 2> /dev/null
3138
    else
3139
        eval set -x PATH $PERLBREW_ROOT/bin $PATH_WITHOUT_PERLBREW 2> /dev/null
3140
    end
3141
end
3142

3143
function __perlbrew_set_env
3144
    set -l code (eval $perlbrew_command env $argv | perl -pe 's/^(export|setenv)/set -xg/; s/=/ /; s/^unset(env)* (.*)/if test -n "\$$2"; set -eg $2; end/; s/$/;/; y/:/ /')
3145

3146
    if test -z "$code"
3147
        return 0;
3148
    else
3149
        eval $code
3150
    end
3151
end
3152

3153
function __perlbrew_activate
3154
    functions -e perl
3155

3156
    if test -n "$PERLBREW_PERL"
3157
        if test -z "$PERLBREW_LIB"
3158
            __perlbrew_set_env $PERLBREW_PERL
3159
        else
3160
            __perlbrew_set_env $PERLBREW_PERL@$PERLBREW_LIB
3161
        end
3162
    end
3163

3164
    __perlbrew_set_path
3165
end
3166

3167
function __perlbrew_deactivate
5✔
3168
    __perlbrew_set_env
3169
    set -x PERLBREW_PERL
3170
    set -x PERLBREW_LIB
3171
    set -x PERLBREW_PATH
3172
    __perlbrew_set_path
3173
end
3174

3175
function perlbrew
3176

3177
    test -z "$argv"
3178
    and echo "    Usage: perlbrew <command> [options] [arguments]"
3179
    and echo "       or: perlbrew help"
3180
    and return 1
3181

3182
    switch $argv[1]
3183
        case use
3184
            if test ( count $argv ) -eq 1
3185
                if test -z "$PERLBREW_PERL"
3186
                    echo "Currently using system perl"
3187
                else
3188
                    echo "Currently using $PERLBREW_PERL"
3189
                end
3190
            else
3191
                __perlbrew_set_env $argv[2]
3192
                if test "$status" -eq 0
3193
                    __perlbrew_set_path
3194
                end
3195
            end
3196

3197
        case switch
3198
            if test ( count $argv ) -eq 1
3199
                command perlbrew switch
3200
            else
3201
                perlbrew use $argv[2]
3202
                if test "$status" -eq 0
3203
                    __perlbrew_reinit $argv[2]
3204
                end
3205
            end
3206

3207
        case off
3208
            __perlbrew_deactivate
3209
            echo "perlbrew is turned off."
3210

3211
        case switch-off
3212
            __perlbrew_deactivate
3213
            __perlbrew_reinit
3214
            echo "perlbrew is switched off."
3215

3216
        case '*'
3217
            command perlbrew $argv
3218
    end
3219
end
3220

3221
function __source_init
3222
    perl -pe 's/^(export|setenv)/set -xg/; s/^unset(env)* (.*)/if test -n "\$$2"; set -eg $2; end/; s/=/ /; s/$/;/;' "$PERLBREW_HOME/init" | source
3223
end
3224

3225
if test -z "$PERLBREW_ROOT"
3226
    set -x PERLBREW_ROOT "$HOME/perl5/perlbrew"
3227
end
3228

3229
if test -z "$PERLBREW_HOME"
3230
    set -x PERLBREW_HOME "$HOME/.perlbrew"
3231
end
3232

3233
if test -z "$PERLBREW_SKIP_INIT" -a -f "$PERLBREW_HOME/init"
3234
    __source_init
3235
end
3236

3237
set perlbrew_bin_path "$PERLBREW_ROOT/bin"
5✔
3238

3239
if test -f "$perlbrew_bin_path/perlbrew"
3240
    set perlbrew_command "$perlbrew_bin_path/perlbrew"
3241
else
3242
    set perlbrew_command perlbrew
3243
end
3244

3245
set -e perlbrew_bin_path
3246

3247
__perlbrew_activate
3248

3249
## autocomplete stuff #############################################
3250

5✔
3251
function __fish_perlbrew_needs_command
3252
  set cmd (commandline -opc)
3253
  if test (count $cmd) -eq 1 -a $cmd[1] = 'perlbrew'
3254
    return 0
3255
  end
3256
  return 1
3257
end
3258

3259
function __fish_perlbrew_using_command
3260
  set cmd (commandline -opc)
3261
  if test (count $cmd) -gt 1
3262
    if [ $argv[1] = $cmd[2] ]
3263
      return 0
3264
    end
3265
  end
3266
end
3267

3268
for com in (perlbrew help | perl -ne'print lc if s/^COMMAND:\s+//')
3269
    complete -f -c perlbrew -n '__fish_perlbrew_needs_command' -a $com
3270
end
5✔
3271

3272
for com in switch use;
3273
    complete -f -c perlbrew -n "__fish_perlbrew_using_command $com" \
3274
        -a '(perlbrew list | perl -pe\'s/\*?\s*(\S+).*/$1/\')'
3275
end
3276

3277
END
3278
}
3279

3280
sub CSH_WRAPPER_CONTENT {
3281
    return <<'WRAPPER';
3282
set perlbrew_exit_status=0
3283

3284
if ( "$1" =~ -* ) then
3285
    set perlbrew_short_option="$1"
3286
    shift
3287
else
3288
    set perlbrew_short_option=""
3289
endif
3290

3291
switch ( "$1" )
3292
    case use:
3293
        if ( $%2 == 0 ) then
3294
            if ( $?PERLBREW_PERL == 0 ) then
3295
                echo "Currently using system perl"
3296
            else
3297
                if ( $%PERLBREW_PERL == 0 ) then
4✔
3298
                    echo "Currently using system perl"
4✔
3299
                else
3300
                    echo "Currently using $PERLBREW_PERL"
4✔
3301
                endif
4✔
3302
            endif
4✔
3303
        else
3304
            set perlbrew_line_count=0
3305
            foreach perlbrew_line ( "`\perlbrew env $2:q`" )
3306
                eval "$perlbrew_line"
1✔
3307
                @ perlbrew_line_count++
1✔
3308
            end
3309
            if ( $perlbrew_line_count == 0 ) then
3310
                set perlbrew_exit_status=1
3311
            else
3312
                source "$PERLBREW_ROOT/etc/csh_set_path"
3313
            endif
3314
        endif
3315
        breaksw
3316

3317
    case switch:
3318
        if ( $%2 == 0 ) then
3319
            \perlbrew switch
3320
        else
3321
            perlbrew use "$2" && source "$PERLBREW_ROOT/etc/csh_reinit" "$2"
3322
        endif
3323
        breaksw
3324

3325
    case off:
3326
        unsetenv PERLBREW_PERL
3327
        foreach perlbrew_line ( "`\perlbrew env`" )
3328
            eval "$perlbrew_line"
3329
        end
3330
        source "$PERLBREW_ROOT/etc/csh_set_path"
3331
        echo "perlbrew is turned off."
3332
        breaksw
3333

3334
    case switch-off:
3335
        unsetenv PERLBREW_PERL
3336
        source "$PERLBREW_ROOT/etc/csh_reinit" ''
3337
        echo "perlbrew is switched off."
3338
        breaksw
3339

3340
    default:
3341
        \perlbrew $perlbrew_short_option:q $argv:q
3342
        set perlbrew_exit_status=$?
3343
        breaksw
3344
endsw
3345
rehash
3346
exit $perlbrew_exit_status
3347
WRAPPER
3348
}
3349

3350
sub CSH_REINIT_CONTENT {
3351
    return <<'REINIT';
3352
if ( ! -d "$PERLBREW_HOME" ) then
3353
    mkdir -p "$PERLBREW_HOME"
3354
endif
3355

3356
echo '# DO NOT EDIT THIS FILE' >! "$PERLBREW_HOME/init"
3357
\perlbrew env $1 >> "$PERLBREW_HOME/init"
3358
source "$PERLBREW_HOME/init"
3359
source "$PERLBREW_ROOT/etc/csh_set_path"
3360
REINIT
3361
}
3362

3363
sub CSH_SET_PATH_CONTENT {
3364
    return <<'SETPATH';
3365
unalias perl
3366

3367
if ( $?PERLBREW_PATH == 0 ) then
3368
    setenv PERLBREW_PATH "$PERLBREW_ROOT/bin"
3369
endif
3370

3371
setenv PATH_WITHOUT_PERLBREW `perl -e 'print join ":", grep { index($_, $ENV{PERLBREW_ROOT}) } split/:/,$ENV{PATH};'`
3372
setenv PATH "${PERLBREW_PATH}:${PATH_WITHOUT_PERLBREW}"
3373

3374
setenv MANPATH_WITHOUT_PERLBREW `perl -e 'print join ":", grep { index($_, $ENV{PERLBREW_ROOT}) } split/:/,qx(manpath 2> /dev/null);'`
3375
if ( $?PERLBREW_MANPATH == 1 ) then
3376
    setenv MANPATH "${PERLBREW_MANPATH}:${MANPATH_WITHOUT_PERLBREW}"
3377
else
3378
    setenv MANPATH "${MANPATH_WITHOUT_PERLBREW}"
3379
endif
3380
SETPATH
3381
}
3382

3383
sub CSHRC_CONTENT {
3384
    return "setenv PERLBREW_SHELLRC_VERSION $VERSION\n\n" . <<'CSHRC';
3385

3386
if ( $?PERLBREW_HOME == 0 ) then
3387
    setenv PERLBREW_HOME "$HOME/.perlbrew"
3388
endif
3389

3390
if ( $?PERLBREW_ROOT == 0 ) then
3391
    setenv PERLBREW_ROOT "$HOME/perl5/perlbrew"
3392
endif
3393

3394
if ( $?PERLBREW_SKIP_INIT == 0 ) then
3395
    if ( -f "$PERLBREW_HOME/init" ) then
3396
        source "$PERLBREW_HOME/init"
3397
    endif
3398
endif
3399

3400
if ( $?PERLBREW_PATH == 0 ) then
3401
    setenv PERLBREW_PATH "$PERLBREW_ROOT/bin"
3402
endif
3403

3404
source "$PERLBREW_ROOT/etc/csh_set_path"
3405
alias perlbrew 'source "$PERLBREW_ROOT/etc/csh_wrapper"'
3406
CSHRC
3407

3408
}
3409

3410
sub append_log {
3411
    my ( $self, $message ) = @_;
3412
    my $log_handler;
3413
    open( $log_handler, '>>', $self->{log_file} )
3414
        or die "Cannot open log file for appending: $!";
3415
    print $log_handler "$message\n";
3416
    close($log_handler);
3417
}
3418

3419
sub INSTALLATION_FAILURE_MESSAGE {
3420
    my ($self) = @_;
3421
    return <<FAIL;
3422
Installation process failed. To spot any issues, check
3423

3424
  $self->{log_file}
3425

3426
If some perl tests failed and you still want to install this distribution anyway,
3427
do:
3428

3429
  (cd $self->{dist_extracted_dir}; make install)
3430

3431
You might also want to try upgrading patchperl before trying again:
3432

3433
  perlbrew install-patchperl
3434

3435
Generally, if you need to install a perl distribution known to have minor test
3436
failures, do one of these commands to avoid seeing this message:
3437

3438
  perlbrew --notest install $self->{dist_name}
3439
  perlbrew --force install $self->{dist_name}
3440

3441
FAIL
3442

3443
}
3444

3445
1;
3446

3447
__END__
3448

3449
=encoding utf8
3450

3451
=head1 NAME
3452

3453
App::perlbrew - Manage perl installations in your C<$HOME>
3454

3455
=head1 SYNOPSIS
3456

3457
    # Installation
3458
    curl -L https://install.perlbrew.pl | bash
3459

3460
    # Initialize
3461
    perlbrew init
3462

3463
    # See what is available
3464
    perlbrew available
3465

3466
    # Install some Perls
3467
    perlbrew install 5.32.1
3468
    perlbrew install perl-5.28.3
3469
    perlbrew install perl-5.33.6
3470

3471
    # See what were installed
3472
    perlbrew list
3473

3474
    # Swith to an installation and set it as default
3475
    perlbrew switch perl-5.32.1
3476

3477
    # Temporarily use another version only in current shell.
3478
    perlbrew use perl-5.28.3
3479
    perl -v
3480

3481
    # Turn it off and go back to the system perl.
3482
    perlbrew off
3483

3484
    # Turn it back on with 'switch', or 'use'
3485
    perlbrew switch perl-5.32.1
3486
    perlbrew use perl-5.32.1
3487

3488
    # Exec something with all perlbrew-ed perls
3489
    perlbrew exec -- perl -E 'say $]'
3490

3491
=head1 DESCRIPTION
3492

3493
L<perlbrew> is a program to automate the building and installation of perl in an
3494
easy way. It provides multiple isolated perl environments, and a mechanism
3495
for you to switch between them.
3496

3497
Everything are installed unter C<~/perl5/perlbrew>. You then need to include a
3498
bashrc/cshrc provided by perlbrew to tweak the PATH for you. You then can
3499
benefit from not having to run C<sudo> commands to install
3500
cpan modules because those are installed inside your C<HOME> too.
3501

3502
For the documentation of perlbrew usage see L<perlbrew> command
3503
on L<MetaCPAN|https://metacpan.org/>, or by running C<perlbrew help>,
3504
or by visiting L<perlbrew's official website|https://perlbrew.pl/>. The following documentation
3505
features the API of C<App::perlbrew> module, and may not be remotely
3506
close to what your want to read.
3507

3508
=head1 INSTALLATION
3509

3510
It is the simplest to use the perlbrew installer, just paste this statement to
3511
your terminal:
3512

3513
    curl -L https://install.perlbrew.pl | bash
3514

3515
Or this one, if you have C<fetch> (default on FreeBSD):
3516

3517
    fetch -o- https://install.perlbrew.pl | sh
3518

3519
After that, C<perlbrew> installs itself to C<~/perl5/perlbrew/bin>, and you
3520
should follow the instruction on screen to modify your shell rc file to put it
3521
in your PATH.
3522

3523
The installed perlbrew command is a standalone executable that can be run with
3524
system perl. The minimum required version of system perl is 5.8.0, which should
3525
be good enough for most of the OSes these days.
3526

3527
A fat-packed version of L<patchperl> is also installed to
3528
C<~/perl5/perlbrew/bin>, which is required to build old perls.
3529

3530
The directory C<~/perl5/perlbrew> will contain all install perl executables,
3531
libraries, documentations, lib, site_libs. In the documentation, that directory
3532
is referred as C<perlbrew root>. If you need to set it to somewhere else because,
3533
say, your C<HOME> has limited quota, you can do that by setting C<PERLBREW_ROOT>
3534
environment variable before running the installer:
3535

3536
    export PERLBREW_ROOT=/opt/perl5
3537
    curl -L https://install.perlbrew.pl | bash
3538

3539
As a result, different users on the same machine can all share the same perlbrew
3540
root directory (although only original user that made the installation would
3541
have the permission to perform perl installations.)
3542

3543
If you need to install perlbrew using a Perl that isn't either C</usr/bin/perl>
3544
or C</usr/local/bin/perl>, set and export the environment variable
3545
C<PERLBREW_SYSTEM_PERL> and then install as described above. Note that you
3546
must not use a perlbrew-managed perl.
3547

3548
You may also install perlbrew from CPAN:
3549

3550
    cpan App::perlbrew
3551

3552
In this case, the perlbrew command is installed as C</usr/bin/perlbrew> or
3553
C</usr/local/bin/perlbrew> or others, depending on the location of your system
3554
perl installation.
3555

3556
Please make sure not to run this with one of the perls brewed with
3557
perlbrew. It's the best to turn perlbrew off before you run that, if you're
3558
upgrading.
3559

3560
    perlbrew off
3561
    cpan App::perlbrew
3562

3563
You should always use system cpan (like /usr/bin/cpan) to install
3564
C<App::perlbrew> because it will be installed under a system PATH like
3565
C</usr/bin>, which is not affected by perlbrew C<switch> or C<use> command.
3566

3567
The C<self-upgrade> command will not upgrade the perlbrew installed by cpan
3568
command, but it is also easy to upgrade perlbrew by running C<cpan App::perlbrew>
3569
again.
3570

3571
=head1 PROJECT DEVELOPMENT
3572

3573
L<perlbrew project|https://perlbrew.pl/> uses github
3574
L<https://github.com/gugod/App-perlbrew/issues> for issue
3575
tracking. Issues sent to these two systems will eventually be reviewed
3576
and handled. To participate, you need a github account.
3577

3578
Please briefly read the short instructions about how to get your work
3579
released to CPAN:
3580

3581
L<https://github.com/gugod/App-perlbrew/blob/develop/CONTRIBUTING.md>
3582

3583
=head1 AUTHOR
3584

3585
Kang-min Liu  C<< <gugod@gugod.org> >>
3586

3587
=head1 COPYRIGHT
3588

3589
Copyright (c) 2023 Kang-min Liu C<< <gugod@gugod.org> >>.
3590

3591
=head1 LICENCE
3592

3593
The MIT License
3594

3595
=head1 DISCLAIMER OF WARRANTY
3596

3597
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
3598
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
3599
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
3600
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
3601
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
3602
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
3603
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
3604
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
3605
NECESSARY SERVICING, REPAIR, OR CORRECTION.
3606

3607
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
3608
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
3609
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
3610
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
3611
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
3612
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
3613
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
3614
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
3615
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
3616
SUCH DAMAGES.
3617

3618
=cut
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc