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

gugod / App-perlbrew / 5101900754

pending completion
5101900754

Pull #776

github

web-flow
Merge 4fe3b6fef into 04173359a
Pull Request #776: New command: make shim

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

3116 of 3939 relevant lines covered (79.11%)

80.05 hits per line

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

68.73
/lib/App/perlbrew.pm
1
package App::perlbrew;
2
use strict;
68✔
3
use warnings;
68✔
4
use 5.008;
68✔
5
our $VERSION = "0.97";
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 );
68✔
25
use Capture::Tiny ();
68✔
26

27
use App::Perlbrew::Util qw( files_are_the_same uniq find_similar_tokens );
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

32
### global variables
33

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

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

45
our $CONFIG;
46
our $PERLBREW_ROOT;
47
our $PERLBREW_HOME;
48

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

57
    {
58
        d_option => 'usemultiplicity',
59
        opt      => 'multi'
60
    },
61

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

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

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

80
    {
81
        d_option => 'DEBUGGING',
82
        opt      => 'debug'
83
    },
84

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

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

105
### methods
106
sub new {
107
    my ( $class, @argv ) = @_;
231✔
108

109
    my %opt = (
231✔
110
        original_argv => \@argv,
111
        args          => [],
112
        yes           => 0,
113
        force         => 0,
114
        quiet         => 0,
115
        D             => [],
116
        U             => [],
117
        A             => [],
118
        sitecustomize => '',
119
        destdir       => '',
120
        noman         => '',
121
        variation     => '',
122
        both          => [],
123
        append        => '',
124
        reverse       => 0,
125
        verbose       => 0,
126
    );
127

128
    $opt{$_} = '' for keys %flavor;
231✔
129

130
    if (@argv) {
231✔
131

132
        # build a local @ARGV to allow us to use an older
133
        # Getopt::Long API in case we are building on an older system
134
        local (@ARGV) = @argv;
155✔
135

136
        Getopt::Long::Configure(
155✔
137
            'pass_through',
138
            'no_ignore_case',
139
            'bundling',
140
            'permute',    # default behaviour except 'exec'
141
        );
142

143
        $class->parse_cmdline( \%opt );
155✔
144

145
        $opt{args} = \@ARGV;
155✔
146

147
        # fix up the effect of 'bundling'
148
        foreach my $flags ( @opt{qw(D U A)} ) {
155✔
149
            foreach my $value ( @{$flags} ) {
465✔
150
                $value =~ s/^=//;
13✔
151
            }
152
        }
153
    }
154

155
    my $self = bless \%opt, $class;
231✔
156

157
    # Treat --root option same way as env variable PERLBREW_ROOT (with higher priority)
158
    if ( $opt{root} ) {
231✔
159
        $ENV{PERLBREW_ROOT} = $self->root( $opt{root} );
3✔
160
    }
161

162
    if ( $opt{builddir} ) {
231✔
163
        $self->{builddir} = App::Perlbrew::Path->new( $opt{builddir} );
1✔
164
    }
165

166
    # Ensure propagation of $PERLBREW_HOME and $PERLBREW_ROOT
167
    $self->root;
231✔
168
    $self->home;
231✔
169

170
    if ( $self->{verbose} ) {
231✔
171
        $App::Perlbrew::HTTP::HTTP_VERBOSE = 1;
3✔
172
    }
173

174
    return $self;
231✔
175
}
176

177
sub parse_cmdline {
178
    my ( $self, $params, @ext ) = @_;
189✔
179

180
    my @f = map { $flavor{$_}{opt} || $_ } keys %flavor;
189✔
181

182
    return Getopt::Long::GetOptions(
189✔
183
        $params,
184

185
        'yes',
186
        'force|f',
187
        'reverse',
188
        'notest|n',
189
        'quiet|q',
190
        'verbose|v',
191
        'as=s',
192
        'append=s',
193
        'help|h',
194
        'version',
195
        'root=s',
196
        'switch',
197
        'all',
198
        'shell=s',
199
        'no-patchperl',
200
        'no-decoration',
201

202
        "builddir=s",
203

204
        # options passed directly to Configure
205
        'D=s@',
206
        'U=s@',
207
        'A=s@',
208

209
        'j=i',
210

211
        # options that affect Configure and customize post-build
212
        'sitecustomize=s',
213
        'destdir=s',
214
        'noman',
215

216
        # flavors support
217
        'both|b=s@',
218
        'all-variations',
219
        'common-variations',
220
        @f,
221

222
        @ext
223
    );
224
}
225

226
sub root {
227
    my ( $self, $new_root ) = @_;
838✔
228

229
    $new_root ||=
230
           $PERLBREW_ROOT
231
        || $ENV{PERLBREW_ROOT}
232
        || App::Perlbrew::Path->new( $ENV{HOME}, "perl5", "perlbrew" )->stringify
233
        unless $self->{root};
838✔
234

235
    $self->{root} = $PERLBREW_ROOT = $new_root
838✔
236
        if defined $new_root;
237

238
    $self->{root} = App::Perlbrew::Path::Root->new( $self->{root} )
239
        unless ref $self->{root};
838✔
240

241
    $self->{root} = App::Perlbrew::Path::Root->new( $self->{root}->stringify )
242
        unless $self->{root}->isa('App::Perlbrew::Path::Root');
838✔
243

244
    return $self->{root};
838✔
245
}
246

247
sub home {
248
    my ( $self, $new_home ) = @_;
873✔
249

250
    $new_home ||=
251
           $PERLBREW_HOME
252
        || $ENV{PERLBREW_HOME}
253
        || App::Perlbrew::Path->new( $ENV{HOME}, ".perlbrew" )->stringify
254
        unless $self->{home};
873✔
255

256
    $self->{home} = $PERLBREW_HOME = $new_home
873✔
257
        if defined $new_home;
258

259
    $self->{home} = App::Perlbrew::Path->new( $self->{home} )
260
        unless ref $self->{home};
873✔
261

262
    return $self->{home};
873✔
263
}
264

265
sub builddir {
266
    my ($self) = @_;
4✔
267

268
    return $self->{builddir} || $self->root->build;
4✔
269
}
270

271
sub current_perl {
272
    my ( $self, $v ) = @_;
694✔
273
    $self->{current_perl} = $v if $v;
694✔
274
    return $self->{current_perl} || $self->env('PERLBREW_PERL') || '';
694✔
275
}
276

277
sub current_lib {
278
    my ( $self, $v ) = @_;
509✔
279
    $self->{current_lib} = $v if $v;
509✔
280
    return $self->{current_lib} || $self->env('PERLBREW_LIB') || '';
509✔
281
}
282

283
sub current_shell_is_bashish {
284
    my ($self) = @_;
4✔
285
    return ( $self->current_shell eq 'bash' ) || ( $self->current_shell eq 'zsh' );
4✔
286
}
287

288
sub current_shell {
289
    my ( $self, $x ) = @_;
22✔
290
    $self->{current_shell} = $x if $x;
22✔
291
    return $self->{current_shell} ||= do {
22✔
292
        my $shell_name = App::Perlbrew::Path->new( $self->{shell} || $self->env('SHELL') )->basename;
2✔
293
        $shell_name =~ s/\d+$//;
2✔
294
        $shell_name;
2✔
295
    };
296
}
297

298
sub current_env {
299
    my ($self) = @_;
467✔
300
    my $l = $self->current_lib;
467✔
301
    $l = "@" . $l if $l;
467✔
302
    return $self->current_perl . $l;
467✔
303
}
304

305
sub installed_perl_executable {
306
    my ( $self, $name ) = @_;
1✔
307
    die unless $name;
1✔
308

309
    my $executable = $self->root->perls($name)->perl;
1✔
310
    return $executable if -e $executable;
1✔
311
    return "";
×
312
}
313

314
sub configure_args {
315
    my ( $self, $name ) = @_;
×
316

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

320
    my @output = split "\n" => $self->do_capture( $perl_cmd, '-MConfig', '-wle', $code );
×
321

322
    my %arg;
×
323
    for (@output) {
×
324
        my ( $k, $v ) = split " ", $_, 2;
×
325
        $arg{$k} = $v;
×
326
    }
327

328
    if (wantarray) {
×
329
        return map { $arg{"config_arg$_"} } ( 1 .. $arg{config_argc} );
×
330
    }
331

332
    return $arg{config_args};
×
333
}
334

335
sub cpan_mirror {
336
    my ( $self, $v ) = @_;
11✔
337

338
    $self->{cpan_mirror} = $v if $v;
11✔
339

340
    unless ( $self->{cpan_mirror} ) {
11✔
341
        $self->{cpan_mirror} = $self->env("PERLBREW_CPAN_MIRROR") || "https://cpan.metacpan.org";
5✔
342
        $self->{cpan_mirror} =~ s{/+$}{};
5✔
343
    }
344

345
    return $self->{cpan_mirror};
11✔
346
}
347

348
sub env {
349
    my ( $self, $name ) = @_;
1,280✔
350
    return $ENV{$name} if $name;
1,280✔
351
    return \%ENV;
×
352
}
353

354
sub is_shell_csh {
355
    my ($self) = @_;
×
356
    return 1 if $self->env('SHELL') =~ /(t?csh)/;
×
357
    return 0;
×
358
}
359

360
# Entry point method: handles all the arguments
361
# and dispatches to an appropriate internal
362
# method to execute the corresponding command.
363
sub run {
364
    my ($self) = @_;
150✔
365
    $self->run_command( $self->args );
150✔
366
}
367

368
sub args {
369
    my ($self) = @_;
154✔
370

371
    # keep 'force' and 'yes' coherent across commands
372
    $self->{force} = $self->{yes} = 1 if ( $self->{force} || $self->{yes} );
154✔
373

374
    return @{ $self->{args} };
154✔
375
}
376

377
sub commands {
378
    my ($self) = @_;
11✔
379

380
    my $package = ref $self ? ref $self : $self;
11✔
381

382
    my @commands;
11✔
383
    my $symtable = do {
11✔
384
        no strict 'refs';
68✔
385
        \%{ $package . '::' };
11✔
386
    };
387

388
    foreach my $sym ( keys %$symtable ) {
11✔
389
        if ( $sym =~ /^run_command_/ ) {
1,512✔
390
            my $glob = $symtable->{$sym};
455✔
391
            if ( ref($glob) eq 'CODE' || defined *$glob{CODE} ) {
455✔
392

393
                # with perl >= 5.27 stash entry can points to a CV directly
394
                $sym =~ s/^run_command_//;
451✔
395
                $sym =~ s/_/-/g;
451✔
396
                push @commands, $sym;
451✔
397
            }
398
        }
399
    }
400

401
    return @commands;
11✔
402
}
403

404
sub find_similar_commands {
405
    my ( $self, $command ) = @_;
5✔
406

407
    $command =~ s/_/-/g;
5✔
408

409
    return @{ find_similar_tokens($command, [ sort $self->commands ]) };
5✔
410
}
411

412
# This method is called in the 'run' loop
413
# and executes every specific action depending
414
# on the type of command.
415
#
416
# The first argument to this method is a self reference,
417
# while the first "real" argument is the command to execute.
418
# Other parameters after the command to execute are
419
# considered as arguments for the command itself.
420
#
421
# In general the command is executed via a method named after the
422
# command itself and with the 'run_command' prefix. For instance
423
# the command 'exec' is handled by a method
424
# `run_command_exec`
425
#
426
# If no candidates can be found, an execption is thrown
427
# and a similar command is shown to the user.
428
sub run_command {
429
    my ( $self, $x, @args ) = @_;
150✔
430
    my $command = $x;
150✔
431

432
    if ( $self->{version} ) {
150✔
433
        $x = 'version';
×
434
    }
435
    elsif ( !$x ) {
436
        $x    = 'help';
2✔
437
        @args = ( 0, 0 );
2✔
438
    }
439
    elsif ( $x eq 'help' ) {
440
        @args = ( 0, 2 ) unless @args;
4✔
441
    }
442

443
    my $s = $self->can("run_command_$x");
150✔
444
    unless ($s) {
150✔
445
        $x =~ y/-/_/;
22✔
446
        $s = $self->can("run_command_$x");
22✔
447
    }
448

449
    unless ($s) {
150✔
450
        my @commands = $self->find_similar_commands($x);
2✔
451

452
        if ( @commands > 1 ) {
2✔
453
            @commands = map { '    ' . $_ } @commands;
×
454
            die "Unknown command: `$command`. Did you mean one of the following?\n" . join( "\n", @commands ) . "\n";
×
455
        }
456
        elsif ( @commands == 1 ) {
457
            die "Unknown command: `$command`. Did you mean `$commands[0]`?\n";
×
458
        }
459
        else {
460
            die "Unknown command: `$command`. Typo?\n";
2✔
461
        }
462
    }
463

464
    $self->$s(@args);
148✔
465
}
466

467
sub run_command_version {
468
    my ($self)  = @_;
2✔
469
    my $package = ref $self;
2✔
470
    my $version = $self->VERSION;
2✔
471
    print "$0  - $package/$version\n";
2✔
472
}
473

474
# Provides help information about a command.
475
# The idea is similar to the 'run_command' and 'run_command_$x' chain:
476
# this method dispatches to a 'run_command_help_$x' method
477
# if found in the class, otherwise it tries to extract the help
478
# documentation via the POD of the class itself using the
479
# section 'COMMAND: $x' with uppercase $x.
480
sub run_command_help {
481
    my ( $self, $status, $verbose, $return_text ) = @_;
8✔
482

483
    require Pod::Usage;
8✔
484

485
    if ( $status && !defined($verbose) ) {
8✔
486
        if ( $self->can("run_command_help_${status}") ) {
3✔
487
            $self->can("run_command_help_${status}")->($self);
×
488
        }
489
        else {
490
            my $out = "";
3✔
491
            open my $fh, ">", \$out;
3✔
492

493
            Pod::Usage::pod2usage(
3✔
494
                -exitval   => "NOEXIT",
495
                -verbose   => 99,
496
                -sections  => "COMMAND: " . uc($status),
497
                -output    => $fh,
498
                -noperldoc => 1
499
            );
500
            $out =~ s/\A[^\n]+\n//s;
3✔
501
            $out =~ s/^    //gm;
3✔
502

503
            if ( $out =~ /\A\s*\Z/ ) {
3✔
504
                $out = "Cannot find documentation for '$status'\n\n";
2✔
505
            }
506

507
            return "\n$out" if ($return_text);
3✔
508
            print "\n$out";
1✔
509
            close $fh;
1✔
510
        }
511
    }
512
    else {
513
        Pod::Usage::pod2usage(
5✔
514
            -noperldoc => 1,
515
            -verbose   => $verbose || 0,
516
            -exitval   => ( defined $status ? $status : 1 )
517
        );
518
    }
519
}
520

521
# introspection for compgen
522
my %comp_installed = (
523
    use    => 1,
524
    switch => 1,
525
);
526

527
sub run_command_compgen {
528
    my ( $self, $cur, @args ) = @_;
7✔
529

530
    $cur = 0 unless defined($cur);
7✔
531

532
    # do `tail -f bashcomp.log` for debugging
533
    if ( $self->env('PERLBREW_DEBUG_COMPLETION') ) {
7✔
534
        open my $log, '>>', 'bashcomp.log';
×
535
        print $log "[$$] $cur of [@args]\n";
×
536
    }
537
    my $subcommand           = $args[1];
7✔
538
    my $subcommand_completed = ( $cur >= 2 );
7✔
539

540
    if ( !$subcommand_completed ) {
7✔
541
        $self->_compgen( $subcommand, $self->commands );
3✔
542
    }
543
    else {    # complete args of a subcommand
544
        if ( $comp_installed{$subcommand} ) {
4✔
545
            if ( $cur <= 2 ) {
4✔
546
                my $part;
4✔
547
                if ( defined( $part = $args[2] ) ) {
4✔
548
                    $part = qr/ \Q$part\E /xms;
2✔
549
                }
550
                $self->_compgen( $part, map { $_->{name} } $self->installed_perls() );
4✔
551
            }
552
        }
553
        elsif ( $subcommand eq 'help' ) {
554
            if ( $cur <= 2 ) {
×
555
                $self->_compgen( $args[2], $self->commands() );
×
556
            }
557
        }
558
        else {
559
            # TODO
560
        }
561
    }
562
}
563

564
sub _firstrcfile {
565
    my ( $self, @files ) = @_;
4✔
566
    foreach my $path (@files) {
4✔
567
        return $path if -f App::Perlbrew::Path->new( $self->env('HOME'), $path );
5✔
568
    }
569
    return;
×
570
}
571

572
sub _compgen {
573
    my ( $self, $part, @reply ) = @_;
7✔
574
    if ( defined $part ) {
7✔
575
        $part  = qr/\A \Q$part\E /xms if ref($part) ne ref(qr//);
4✔
576
        @reply = grep { /$part/ } @reply;
4✔
577
    }
578
    foreach my $word (@reply) {
7✔
579
        print $word, "\n";
60✔
580
    }
581
}
582

583
# Internal utility function.
584
# Given a specific perl version, e.g., perl-5.27.4
585
# returns a string with a formatted version number such
586
# as 05027004. Such string can be used as a number
587
# in order to make either a string comparison
588
# or a numeric comparison.
589
#
590
# In the case of cperl the major number is added by 6
591
# so that it would match the project claim of being
592
# Perl 5+6 = 11. The final result is then
593
# multiplied by a negative factor (-1) in order
594
# to make cperl being "less" in the ordered list
595
# than a normal Perl installation.
596
#
597
# The returned string is made by four pieces of two digits each:
598
# MMmmppbb
599
# where:
600
# MM is the major Perl version (e.g., 5 -> 05)
601
# mm is the minor Perl version (e.g. 27 -> 27)
602
# pp is the patch level (e.g., 4 -> 04)
603
# bb is the blead flag: it is 00 for a "normal" release, or 01 for a blead one
604
sub comparable_perl_version {
605
    my ( $self, $perl_version )   = @_;
631✔
606
    my ( $is_cperl, $is_blead )   = ( 0, 0 );
631✔
607
    my ( $major, $minor, $patch ) = ( 0, 0, 0 );
631✔
608
    if ( $perl_version =~ /^(?:(c?perl)-?)?(\d)\.(\d+).(\d+).*/ ) {
631✔
609
        $is_cperl = $1 && ( $1 eq 'cperl' );
631✔
610
        $major    = $2 + ( $is_cperl ? 6 : 0 );    # major version
631✔
611
        $minor    = $3;                            # minor version
631✔
612
        $patch    = $4;                            # patch level
631✔
613

614
    }
615
    elsif ( $perl_version =~ /^(?:(c?perl)-?)?-?(blead)$/ ) {
616

617
        # in the case of a blead release use a fake high number
618
        # to assume it is the "latest" release number available
619
        $is_cperl = $1 && ( $1 eq 'cperl' );
×
620
        $is_blead = $2 && ( $2 eq 'blead' );
×
621
        ( $major, $minor, $patch ) = ( 5, 99, 99 );
×
622
    }
623

624
    return ( $is_cperl ? -1 : 1 ) * sprintf(
631✔
625
        '%02d%02d%02d%02d',
626
        $major + ( $is_cperl ? 6 : 0 ),    # major version
627
        $minor,                            # minor version
628
        $patch,                            # patch level
629
        $is_blead
630
    );                                     # blead
631
}
632

633
# Internal method.
634
# Performs a comparable sort of the perl versions specified as
635
# list.
636
sub sort_perl_versions {
637
    my ( $self, @perls ) = @_;
6✔
638

639
    return map { $_->[0] }
48✔
640
        sort   { ( $self->{reverse} ? $a->[1] <=> $b->[1] : $b->[1] <=> $a->[1] ) }
107✔
641
        map    { [$_, $self->comparable_perl_version($_)] } @perls;
6✔
642
}
643

644
sub run_command_available {
645
    my ($self) = @_;
3✔
646

647
    my @installed  = $self->installed_perls(@_);
3✔
648
    my $is_verbose = $self->{verbose};
3✔
649

650
    my @sections = ( ['perl', 'available_perl_distributions'], ['cperl', 'available_cperl_distributions'], );
3✔
651

652
    for (@sections) {
3✔
653
        my ( $header, $method ) = @$_;
5✔
654

655
        print "# $header\n";
5✔
656

657
        my $perls = $self->$method;
5✔
658

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

662
        for my $available (@sorted_perls) {
4✔
663
            my $url = $perls->{$available};
32✔
664
            my $ctime;
32✔
665

666
            for my $installed (@installed) {
32✔
667
                my $name = $installed->{name};
32✔
668
                my $cur  = $installed->{is_current};
32✔
669
                if ( $available eq $installed->{name} ) {
32✔
670
                    $ctime = $installed->{ctime};
1✔
671
                    last;
1✔
672
                }
673
            }
674

675
            printf "%1s %12s  %s %s\n", $ctime ? 'i' : '', $available,
32✔
676
                (
677
                  $is_verbose
678
                ? $ctime
679
                        ? "INSTALLED on $ctime via"
680
                        : 'available from '
681
                : ''
682
                ),
683
                ( $is_verbose ? "<$url>" : '' );
684
        }
685
        print "\n\n";
4✔
686
    }
687

688
    return;
2✔
689
}
690

691
sub available_perls {
692
    my ($self) = @_;
×
693
    my %dists = ( %{ $self->available_perl_distributions }, %{ $self->available_cperl_distributions }, );
×
694
    return $self->sort_perl_versions( keys %dists );
×
695
}
696

697
# -> Map[ NameVersion =>  URL ]
698
sub available_perl_distributions {
699
    my ($self) = @_;
3✔
700
    my $perls = {};
3✔
701
    my @perllist;
3✔
702

703
    # we got impatient waiting for cpan.org to get updated to show 5.28...
704
    # So, we also fetch from metacpan for anything that looks perlish,
705
    # and we do our own processing to filter out the development
706
    # releases and minor versions when needed (using
707
    # filter_perl_available)
708
    my $url  = 'https://fastapi.metacpan.org/v1/release/versions/perl';
3✔
709
    my $json = http_get( $url, undef, undef );
3✔
710
    unless ($json) {
3✔
711
        die "\nERROR: Unable to retrieve list of perls from Metacpan.\n\n";
2✔
712
    }
713

714
    my $decoded = decode_json($json);
1✔
715
    for my $release ( @{ $decoded->{releases} } ) {
1✔
716
        push @perllist, [$release->{name}, $release->{download_url}];
250✔
717
    }
718
    foreach my $perl ( $self->filter_perl_available( \@perllist ) ) {
1✔
719
        $perls->{ $perl->[0] } = $perl->[1];
11✔
720
    }
721

722
    return $perls;
1✔
723
}
724

725
# -> Map[ NameVersion =>  URL ]
726
sub available_cperl_distributions {
727
    my ($self) = @_;
1✔
728
    my %dist;
1✔
729

730
    # cperl releases: https://github.com/perl11/cperl/tags
731
    my $cperl_remote           = 'https://github.com';
1✔
732
    my $url_cperl_release_list = $cperl_remote . '/perl11/cperl/releases';
1✔
733

734
    my $html = http_get($url_cperl_release_list);
1✔
735

736
    unless ($html) {
1✔
737
        die "\nERROR: Unable to retrieve the list of cperl releases from ${url_cperl_release_list}\n";
1✔
738
    }
739

740
    if ($html) {
×
741
        while ( $html =~ m{href="(/perl11/cperl/releases/download/cperl-(5.+?)/cperl-.+?\.tar\.gz)"}g ) {
×
742
            $dist{"cperl-$2"} = $cperl_remote . $1;
×
743
        }
744
    }
745

746
    return \%dist;
×
747
}
748

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

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

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

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

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

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

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

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

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

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

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

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

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

825
sub cperl_release {
826
    my ( $self, $version ) = @_;
×
827
    my %url = (
×
828
        "5.22.3"     => "https://github.com/perl11/cperl/releases/download/cperl-5.22.3/cperl-5.22.3.tar.gz",
829
        "5.22.2"     => "https://github.com/perl11/cperl/releases/download/cperl-5.22.2/cperl-5.22.2.tar.gz",
830
        "5.24.0-RC1" => "https://github.com/perl11/cperl/releases/download/cperl-5.24.0-RC1/cperl-5.24.0-RC1.tar.gz",
831
    );
832

833
    # my %digest => {
834
    #     "5.22.3" => "bcf494a6b12643fa5e803f8e0d9cef26312b88fc",
835
    #     "5.22.2" => "8615964b0a519cf70d69a155b497de98e6a500d0",
836
    # };
837

838
    my $dist_tarball_url = $url{$version} or die "ERROR: Cannot find the tarball for cperl-$version\n";
×
839
    my $dist_tarball     = "cperl-${version}.tar.gz";
×
840
    return ( $dist_tarball, $dist_tarball_url );
×
841
}
842

843
sub release_detail_perl_local {
844
    my ( $self, $dist, $rd ) = @_;
5✔
845
    $rd ||= {};
5✔
846
    my $error    = 1;
5✔
847
    my $mirror   = $self->cpan_mirror();
5✔
848
    my $tarballs = CPAN::Perl::Releases::perl_tarballs( $rd->{version} );
5✔
849
    if ( keys %$tarballs ) {
5✔
850
        for ( "tar.bz2", "tar.gz" ) {
5✔
851
            if ( my $x = $tarballs->{$_} ) {
5✔
852
                $rd->{tarball_name} = ( split( "/", $x ) )[-1];
5✔
853
                $rd->{tarball_url}  = "$mirror/authors/id/$x";
5✔
854
                $error              = 0;
5✔
855
                last;
5✔
856
            }
857
        }
858
    }
859
    return ( $error, $rd );
5✔
860
}
861

862
sub release_detail_perl_remote {
863
    my ( $self, $dist, $rd ) = @_;
×
864
    $rd ||= {};
×
865
    my $error  = 1;
×
866
    my $mirror = $self->cpan_mirror();
×
867

868
    my $version = $rd->{version};
×
869

870
    # try src/5.0 symlinks, either perl-5.X or perl5.X; favor .tar.bz2 over .tar.gz
871
    my $index = http_get("https://cpan.metacpan.org/src/5.0/");
×
872
    if ($index) {
×
873
        for my $prefix ( "perl-", "perl" ) {
×
874
            for my $suffix ( ".tar.bz2", ".tar.gz" ) {
×
875
                my $dist_tarball     = "$prefix$version$suffix";
×
876
                my $dist_tarball_url = "$mirror/src/5.0/$dist_tarball";
×
877
                if ( $index =~ /href\s*=\s*"\Q$dist_tarball\E"/ms ) {
×
878
                    $rd->{tarball_url}  = $dist_tarball_url;
×
879
                    $rd->{tarball_name} = $dist_tarball;
×
880
                    $error              = 0;
×
881
                    return ( $error, $rd );
×
882
                }
883
            }
884
        }
885
    }
886

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

889
    my $result;
×
890
    unless ( $json and $result = decode_json($json)->{hits}{hits}[0] ) {
×
891
        die "ERROR: Failed to locate perl-${version} tarball.";
×
892
    }
893

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

900
    $rd->{tarball_name} = $dist_tarball;
×
901
    $rd->{tarball_url}  = $dist_tarball_url;
×
902
    $error              = 0;
×
903

904
    return ( $error, $rd );
×
905
}
906

907
sub release_detail_cperl_local {
908
    my ( $self, $dist, $rd ) = @_;
2✔
909
    $rd ||= {};
2✔
910
    my %url = (
2✔
911
        "cperl-5.22.3"     => "https://github.com/perl11/cperl/releases/download/cperl-5.22.3/cperl-5.22.3.tar.gz",
912
        "cperl-5.22.2"     => "https://github.com/perl11/cperl/releases/download/cperl-5.22.2/cperl-5.22.2.tar.gz",
913
        "cperl-5.24.0-RC1" =>
914
            "https://github.com/perl11/cperl/releases/download/cperl-5.24.0-RC1/cperl-5.24.0-RC1.tar.gz",
915
        "cperl-5.24.2" => "https://github.com/perl11/cperl/releases/download/cperl-5.24.2/cperl-5.24.2.tar.gz",
916
        "cperl-5.25.2" => "https://github.com/perl11/cperl/releases/download/cperl-5.24.2/cperl-5.25.2.tar.gz",
917
        "cperl-5.26.4" => "https://github.com/perl11/cperl/releases/download/cperl-5.26.4/cperl-5.26.4.tar.gz",
918
        "cperl-5.26.5" => "https://github.com/perl11/cperl/releases/download/cperl-5.26.5/cperl-5.26.5.tar.gz",
919
        "cperl-5.28.2" => "https://github.com/perl11/cperl/releases/download/cperl-5.28.2/cperl-5.28.2.tar.gz",
920
        "cperl-5.29.0" => "https://github.com/perl11/cperl/releases/download/cperl-5.29.0/cperl-5.29.0.tar.gz",
921
        "cperl-5.29.1" => "https://github.com/perl11/cperl/releases/download/cperl-5.29.1/cperl-5.29.1.tar.gz",
922
        "cperl-5.30.0" => "https://github.com/perl11/cperl/releases/download/cperl-5.30.0/cperl-5.30.0.tar.gz",
923
    );
924

925
    my $error = 1;
2✔
926
    if ( my $u = $url{$dist} ) {
2✔
927
        $rd->{tarball_name} = "${dist}.tar.gz";
2✔
928
        $rd->{tarball_url}  = $u;
2✔
929
        $error              = 0;
2✔
930
    }
931
    return ( $error, $rd );
2✔
932
}
933

934
sub release_detail_cperl_remote {
935
    my ( $self, $dist, $rd ) = @_;
×
936
    $rd ||= {};
×
937

938
    my $expect_href = "/perl11/cperl/releases/download/${dist}/${dist}.tar.gz";
×
939
    my $error       = 1;
×
940

941
    my $html = eval { http_get( 'https://github.com/perl11/cperl/releases/tag/' . $dist ); } || "";
×
942

943
    if ( $html =~ m{ <a \s+ href="($expect_href)" }xsi ) {
×
944
        $rd->{tarball_name} = "${dist}.tar.gz";
×
945
        $rd->{tarball_url}  = "https://github.com" . $1;
×
946
        $error              = 0;
×
947
    }
948

949
    return ( $error, $rd );
×
950
}
951

952
sub release_detail {
953
    my ( $self, $dist ) = @_;
5✔
954
    my ( $dist_type, $dist_version );
5✔
955

956
    ( $dist_type, $dist_version ) = $dist =~ /^ (?: (c?perl) -? )? ( [\d._]+ (?:-RC\d+)? |git|stable|blead)$/x;
5✔
957
    $dist_type = "perl" if $dist_version && !$dist_type;
5✔
958

959
    my $rd = {
5✔
960
        type         => $dist_type,
961
        version      => $dist_version,
962
        tarball_url  => undef,
963
        tarball_name => undef,
964
    };
965

966
# dynamic methods: release_detail_perl_local, release_detail_cperl_local, release_detail_perl_remote, release_detail_cperl_remote
967
    my $m_local  = "release_detail_${dist_type}_local";
5✔
968
    my $m_remote = "release_detail_${dist_type}_remote";
5✔
969

970
    my ($error) = $self->$m_local( $dist, $rd );
5✔
971
    ($error) = $self->$m_remote( $dist, $rd ) if $error;
5✔
972

973
    if ($error) {
5✔
974
        die "ERROR: Fail to get the tarball URL for dist: $dist\n";
×
975
    }
976

977
    return $rd;
5✔
978
}
979

980
sub run_command_init {
981
    my $self = shift;
5✔
982
    my @args = @_;
5✔
983

984
    if ( @args && $args[0] eq '-' ) {
5✔
985
        if ( $self->current_shell_is_bashish ) {
×
986
            $self->run_command_init_in_bash;
×
987
        }
988
        exit 0;
×
989
    }
990

991
    $_->mkpath for ( grep { !-d $_ } map { $self->root->$_ } qw(perls dists build etc bin) );
5✔
992

993
    my ( $f, $fh ) = @_;
5✔
994

995
    my $etc_dir = $self->root->etc;
5✔
996

997
    for (
5✔
998
        ["bashrc",                   "BASHRC_CONTENT"],
999
        ["cshrc",                    "CSHRC_CONTENT"],
1000
        ["csh_reinit",               "CSH_REINIT_CONTENT"],
1001
        ["csh_wrapper",              "CSH_WRAPPER_CONTENT"],
1002
        ["csh_set_path",             "CSH_SET_PATH_CONTENT"],
1003
        ["perlbrew-completion.bash", "BASH_COMPLETION_CONTENT"],
1004
        ["perlbrew.fish",            "PERLBREW_FISH_CONTENT"],
1005
        )
1006
    {
1007
        my ( $file_name, $method ) = @$_;
35✔
1008
        my $path = $etc_dir->child($file_name);
35✔
1009
        if ( !-f $path ) {
35✔
1010
            open( $fh, ">", $path )
7✔
1011
                or die "Fail to create $path. Please check the permission of $etc_dir and try `perlbrew init` again.";
1012
            print $fh $self->$method;
7✔
1013
            close $fh;
7✔
1014
        }
1015
        else {
1016
            if ( -w $path && open( $fh, ">", $path ) ) {
28✔
1017
                print $fh $self->$method;
28✔
1018
                close $fh;
28✔
1019
            }
1020
            else {
1021
                print "NOTICE: $path already exists and not updated.\n" unless $self->{quiet};
×
1022
            }
1023
        }
1024
    }
1025

1026
    my $root_dir = $self->root->stringify_with_tilde;
5✔
1027

1028
    # Skip this if we are running in a shell that already 'source's perlbrew.
1029
    # This is true during a self-install/self-init.
1030
    # Ref. https://github.com/gugod/App-perlbrew/issues/525
1031
    if ( $ENV{PERLBREW_SHELLRC_VERSION} ) {
5✔
1032
        print("\nperlbrew root ($root_dir) is initialized.\n");
×
1033
    }
1034
    else {
1035
        my $shell = $self->current_shell;
5✔
1036
        my ( $code, $yourshrc );
5✔
1037
        if ( $shell =~ m/(t?csh)/ ) {
5✔
1038
            $code     = "source $root_dir/etc/cshrc";
×
1039
            $yourshrc = $1 . "rc";
×
1040
        }
1041
        elsif ( $shell =~ m/zsh\d?$/ ) {
1042
            $code     = "source $root_dir/etc/bashrc";
1✔
1043
            $yourshrc = $self->_firstrcfile(
1✔
1044
                qw(
1045
                    .zshenv
1046
                    .bash_profile
1047
                    .bash_login
1048
                    .profile
1049
                )
1050
            ) || ".zshenv";
1051
        }
1052
        elsif ( $shell =~ m/fish/ ) {
1053
            $code     = ". $root_dir/etc/perlbrew.fish";
1✔
1054
            $yourshrc = '.config/fish/config.fish';
1✔
1055
        }
1056
        else {
1057
            $code     = "source $root_dir/etc/bashrc";
3✔
1058
            $yourshrc = $self->_firstrcfile(
3✔
1059
                qw(
1060
                    .bash_profile
1061
                    .bash_login
1062
                    .profile
1063
                )
1064
            ) || ".bash_profile";
1065
        }
1066

1067
        if ( $self->home ne App::Perlbrew::Path->new( $self->env('HOME'), ".perlbrew" ) ) {
5✔
1068
            my $pb_home_dir = $self->home->stringify_with_tilde;
4✔
1069
            if ( $shell =~ m/fish/ ) {
4✔
1070
                $code = "set -x PERLBREW_HOME $pb_home_dir\n    $code";
1✔
1071
            }
1072
            else {
1073
                $code = "export PERLBREW_HOME=$pb_home_dir\n    $code";
3✔
1074
            }
1075
        }
1076

1077
        print <<INSTRUCTION;
5✔
1078

1079
perlbrew root ($root_dir) is initialized.
1080

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

1084
    $code
1085

1086
Simply run `perlbrew` for usage details.
1087

1088
Happy brewing!
1089

1090
INSTRUCTION
1091
    }
1092

1093
}
1094

1095
sub run_command_init_in_bash {
1096
    print BASHRC_CONTENT();
×
1097
}
1098

1099
sub run_command_self_install {
1100
    my $self = shift;
5✔
1101

1102
    my $executable = $0;
5✔
1103
    my $target     = $self->root->bin("perlbrew");
5✔
1104

1105
    if ( files_are_the_same( $executable, $target ) ) {
5✔
1106
        print "You are already running the installed perlbrew:\n\n    $executable\n";
×
1107
        exit;
×
1108
    }
1109

1110
    $self->root->bin->mkpath;
5✔
1111

1112
    open my $fh, "<", $executable;
5✔
1113

1114
    my $head;
5✔
1115
    read( $fh, $head, 3, 0 );
5✔
1116

1117
    if ( $head eq "#!/" ) {
5✔
1118
        seek( $fh, 0, 0 );
5✔
1119
        my @lines = <$fh>;
5✔
1120
        close $fh;
5✔
1121

1122
        $lines[0] = $self->system_perl_shebang . "\n";
5✔
1123

1124
        open $fh, ">", $target;
5✔
1125
        print $fh $_ for @lines;
5✔
1126
        close $fh;
5✔
1127
    }
1128
    else {
1129
        close($fh);
×
1130

1131
        copy( $executable, $target );
×
1132
    }
1133

1134
    chmod( 0755, $target );
5✔
1135

1136
    my $path = $target->stringify_with_tilde;
5✔
1137

1138
    print "perlbrew is installed: $path\n" unless $self->{quiet};
5✔
1139

1140
    $self->run_command_init();
5✔
1141
    return;
5✔
1142
}
1143

1144
sub do_install_git {
1145
    my ( $self, $dist ) = @_;
×
1146
    my $dist_name;
×
1147
    my $dist_git_describe;
1148
    my $dist_version;
×
1149

1150
    opendir my $cwd_orig, ".";
×
1151

1152
    chdir $dist;
×
1153

1154
    if ( `git describe` =~ /v((5\.\d+\.\d+(?:-RC\d)?)(-\d+-\w+)?)$/ ) {
×
1155
        $dist_name         = 'perl';
×
1156
        $dist_git_describe = "v$1";
×
1157
        $dist_version      = $2;
×
1158
    }
1159

1160
    chdir $cwd_orig;
×
1161

1162
    require File::Spec;
×
1163
    my $dist_extracted_dir = File::Spec->rel2abs($dist);
×
1164
    $self->do_install_this( App::Perlbrew::Path->new($dist_extracted_dir), $dist_version, "$dist_name-$dist_version" );
×
1165
    return;
×
1166
}
1167

1168
sub do_install_url {
1169
    my ( $self, $dist ) = @_;
3✔
1170
    my $dist_name = 'perl';
3✔
1171

1172
    # need the period to account for the file extension
1173
    my ($dist_version) = $dist =~ m/-([\d.]+(?:-RC\d+)?|git)\./;
3✔
1174
    my ($dist_tarball) = $dist =~ m{/([^/]*)$};
3✔
1175

1176
    if ( !$dist_version && $dist =~ /blead\.tar.gz$/ ) {
3✔
1177
        $dist_version = "blead";
2✔
1178
    }
1179

1180
    my $dist_tarball_path = $self->root->dists($dist_tarball);
3✔
1181
    my $dist_tarball_url  = $dist;
3✔
1182
    $dist = "$dist_name-$dist_version";    # we install it as this name later
3✔
1183

1184
    if ( $dist_tarball_url =~ m/^file/ ) {
3✔
1185
        print "Installing $dist from local archive $dist_tarball_url\n";
×
1186
        $dist_tarball_url =~ s/^file:\/+/\//;
×
1187
        $dist_tarball_path = $dist_tarball_url;
×
1188
    }
1189
    else {
1190
        print "Fetching $dist as $dist_tarball_path\n";
3✔
1191
        my $error = http_download( $dist_tarball_url, $dist_tarball_path );
3✔
1192
        die "ERROR: Failed to download $dist_tarball_url\n$error\n" if $error;
3✔
1193
    }
1194

1195
    my $dist_extracted_path = $self->do_extract_tarball($dist_tarball_path);
1✔
1196
    $self->do_install_this( $dist_extracted_path, $dist_version, $dist );
1✔
1197
    return;
1✔
1198
}
1199

1200
sub do_extract_tarball {
1201
    my ( $self, $dist_tarball ) = @_;
1✔
1202

1203
    # Assuming the dir extracted from the tarball is named after the tarball.
1204
    my $dist_tarball_basename = $dist_tarball->basename(qr/\.tar\.(?:gz|bz2|xz)$/);
1✔
1205

1206
    my $workdir;
1✔
1207
    if ( $self->{as} ) {
1✔
1208

1209
        # TODO: Should we instead use the installation_name (see run_command_install()):
1210
        #    $destdir = $self->{as} . $self->{variation} . $self->{append};
1211
        $workdir = $self->builddir->child( $self->{as} );
×
1212
    }
1213
    else {
1214
        # Note that this is incorrect for blead.
1215
        $workdir = $self->builddir->child($dist_tarball_basename);
1✔
1216
    }
1217
    $workdir->rmpath;
1✔
1218
    $workdir->mkpath;
1✔
1219
    my $extracted_dir;
1✔
1220

1221
    # Was broken on Solaris, where GNU tar is probably
1222
    # installed as 'gtar' - RT #61042
1223
    my $tarx = ( $^O =~ /solaris|aix/ ? 'gtar ' : 'tar ' )
1✔
1224
        . (
1225
          $dist_tarball =~ m/xz$/  ? 'xJf'
1226
        : $dist_tarball =~ m/bz2$/ ? 'xjf'
1227
        :                            'xzf'
1228
        );
1229

1230
    my $extract_command = "cd $workdir; $tarx $dist_tarball";
1✔
1231
    die "Failed to extract $dist_tarball" if system($extract_command);
1✔
1232

1233
    my @things = $workdir->children;
1✔
1234
    if ( @things == 1 ) {
1✔
1235
        $extracted_dir = App::Perlbrew::Path->new( $things[0] );
1✔
1236
    }
1237

1238
    unless ( defined($extracted_dir) && -d $extracted_dir ) {
1✔
1239
        die "Failed to find the extracted directory under $workdir";
×
1240
    }
1241

1242
    return $extracted_dir;
1✔
1243
}
1244

1245
sub do_install_blead {
1246
    my ($self) = @_;
2✔
1247

1248
    # We always blindly overwrite anything that's already there,
1249
    # because blead is a moving target.
1250
    my $dist_tarball_path = $self->root->dists("blead.tar.gz");
2✔
1251
    unlink($dist_tarball_path) if -f $dist_tarball_path;
2✔
1252

1253
    $self->do_install_url("https://github.com/Perl/perl5/archive/blead.tar.gz");
2✔
1254
}
1255

1256
sub resolve_stable_version {
1257
    my ($self) = @_;
2✔
1258

1259
    my ( $latest_ver, $latest_minor );
2✔
1260
    for my $cand ( $self->available_perls ) {
2✔
1261
        my ( $ver, $minor ) = $cand =~ m/^perl-(5\.(6|8|[0-9]+[02468])\.[0-9]+)$/
11✔
1262
            or next;
1263
        ( $latest_ver, $latest_minor ) = ( $ver, $minor )
9✔
1264
            if !defined $latest_minor
1265
            || $latest_minor < $minor;
1266
    }
1267

1268
    die "Can't determine latest stable Perl release\n"
2✔
1269
        if !defined $latest_ver;
1270

1271
    return $latest_ver;
2✔
1272
}
1273

1274
sub do_install_release {
1275
    my ( $self, $dist, $dist_version ) = @_;
1✔
1276

1277
    my $rd        = $self->release_detail($dist);
1✔
1278
    my $dist_type = $rd->{type};
1✔
1279

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

1282
    my $dist_tarball      = $rd->{tarball_name};
1✔
1283
    my $dist_tarball_url  = $rd->{tarball_url};
1✔
1284
    my $dist_tarball_path = $self->root->dists($dist_tarball);
1✔
1285

1286
    if ( -f $dist_tarball_path ) {
1✔
1287
        print "Using the previously fetched ${dist_tarball}\n"
1288
            if $self->{verbose};
×
1289
    }
1290
    else {
1291
        print "Fetching perl $dist_version as $dist_tarball_path\n" unless $self->{quiet};
1✔
1292
        $self->run_command_download($dist);
1✔
1293
    }
1294

1295
    my $dist_extracted_path = $self->do_extract_tarball($dist_tarball_path);
×
1296
    $self->do_install_this( $dist_extracted_path, $dist_version, $dist );
×
1297
    return;
×
1298
}
1299

1300
sub run_command_install {
1301
    my ( $self, $dist, $opts ) = @_;
63✔
1302

1303
    unless ( $self->root->exists ) {
63✔
1304
        die( "ERROR: perlbrew root " . $self->root . " does not exist. Run `perlbrew init` to prepare it first.\n" );
1✔
1305
    }
1306

1307
    unless ($dist) {
62✔
1308
        $self->run_command_help("install");
×
1309
        exit(-1);
×
1310
    }
1311

1312
    $self->{dist_name} = $dist;    # for help msg generation, set to non
62✔
1313
                                   # normalized name
1314

1315
    my ( $dist_type, $dist_version );
62✔
1316
    if ( ( $dist_type, $dist_version ) = $dist =~ /^(?:(c?perl)-?)?([\d._]+(?:-RC\d+)?|git|stable|blead)$/ ) {
62✔
1317
        $dist_version = $self->resolve_stable_version if $dist_version eq 'stable';
47✔
1318
        $dist_type ||= "perl";
47✔
1319
        $dist = "${dist_type}-${dist_version}";    # normalize dist name
47✔
1320

1321
        my $installation_name = ( $self->{as} || $dist ) . $self->{variation} . $self->{append};
47✔
1322
        if ( not $self->{force} and $self->is_installed($installation_name) ) {
47✔
1323
            die "\nABORT: $installation_name is already installed.\n\n";
2✔
1324
        }
1325

1326
        if ( $dist_type eq 'perl' && $dist_version eq 'blead' ) {
45✔
1327
            $self->do_install_blead();
3✔
1328
        }
1329
        else {
1330
            $self->do_install_release( $dist, $dist_version );
42✔
1331
        }
1332

1333
    }
1334

1335
    # else it is some kind of special install:
1336
    elsif ( -d "$dist/.git" ) {
1337
        $self->do_install_git($dist);
1✔
1338
    }
1339
    elsif ( -f $dist ) {
1340
        $self->do_install_archive( App::Perlbrew::Path->new($dist) );
13✔
1341
    }
1342
    elsif ( $dist =~ m/^(?:https?|ftp|file)/ ) {    # more protocols needed?
1343
        $self->do_install_url($dist);
1✔
1344
    }
1345
    else {
1346
        die "Unknown installation target \"$dist\", abort.\nPlease see `perlbrew help` "
×
1347
            . "for the instruction on using the install command.\n\n";
1348
    }
1349

1350
    if ( $self->{switch} ) {
57✔
1351
        if ( defined( my $installation_name = $self->{installation_name} ) ) {
1✔
1352
            $self->switch_to($installation_name);
1✔
1353
        }
1354
        else {
1355
            warn "can't switch, unable to infer final destination name.\n\n";
×
1356
        }
1357
    }
1358
    return;
57✔
1359
}
1360

1361
sub check_and_calculate_variations {
1362
    my $self = shift;
×
1363
    my @both = @{ $self->{both} };
×
1364

1365
    if ( $self->{'all-variations'} ) {
×
1366
        @both = keys %flavor;
×
1367
    }
1368
    elsif ( $self->{'common-variations'} ) {
1369
        push @both, grep $flavor{$_}{common}, keys %flavor;
×
1370
    }
1371

1372
    # check the validity of the varitions given via 'both'
1373
    for my $both (@both) {
×
1374
        $flavor{$both} or die "$both is not a supported flavor.\n\n";
×
1375
        $self->{$both} and die "options --both $both and --$both can not be used together";
×
1376
        if ( my $implied_by = $flavor{$both}{implied_by} ) {
×
1377
            $self->{$implied_by} and die "options --both $both and --$implied_by can not be used together";
×
1378
        }
1379
    }
1380

1381
    # flavors selected always
1382
    my $start = '';
×
1383
    $start .= "-$_" for grep $self->{$_}, keys %flavor;
×
1384

1385
    # make variations
1386
    my @var = $start;
×
1387
    for my $both (@both) {
×
1388
        my $append = join( '-', $both, grep defined, $flavor{$both}{implies} );
×
1389
        push @var, map "$_-$append", @var;
×
1390
    }
1391

1392
    # normalize the variation names
1393
    @var = map {
1394
        join '-', '', sort { $flavor{$a}{ix} <=> $flavor{$b}{ix} } grep length, split /-+/, $_
×
1395
    } @var;
1396
    s/(\b\w+\b)(?:-\1)+/$1/g for @var;    # remove duplicate flavors
×
1397

1398
    # After inspecting perl Configure script this seems to be the most
1399
    # reliable heuristic to determine if perl would have 64bit IVs by
1400
    # default or not:
1401
    if ( $Config::Config{longsize} >= 8 ) {
×
1402

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

1408
    # remove duplicated variations
1409
    my %var = map { $_ => 1 } @var;
×
1410
    sort keys %var;
×
1411
}
1412

1413
sub run_command_install_multiple {
1414
    my ( $self, @dists ) = @_;
×
1415

1416
    unless (@dists) {
×
1417
        $self->run_command_help("install-multiple");
×
1418
        exit(-1);
×
1419
    }
1420

1421
    die "--switch can not be used with command install-multiple.\n\n"
1422
        if $self->{switch};
×
1423
    die "--as can not be used when more than one distribution is given.\n\n"
1424
        if $self->{as} and @dists > 1;
×
1425

1426
    my @variations = $self->check_and_calculate_variations;
×
1427
    print join( "\n",
×
1428
        "Compiling the following distributions:",
1429
        map( "    $_$self->{append}", @dists ),
1430
        "  with the following variations:",
1431
        map( ( /-(.*)/ ? "    $1" : "    default" ), @variations ),
1432
        "", "" );
1433

1434
    my @ok;
×
1435
    for my $dist (@dists) {
×
1436
        for my $variation (@variations) {
×
1437
            local $@;
×
1438
            eval {
×
1439
                $self->{$_}                = '' for keys %flavor;
×
1440
                $self->{$_}                = 1  for split /-/, $variation;
×
1441
                $self->{variation}         = $variation;
×
1442
                $self->{installation_name} = undef;
×
1443

1444
                $self->run_command_install($dist);
×
1445
                push @ok, $self->{installation_name};
×
1446
            };
1447
            if ($@) {
×
1448
                $@ =~ s/\n+$/\n/;
×
1449
                print "Installation of $dist$variation failed: $@";
×
1450
            }
1451
        }
1452
    }
1453

1454
    print join( "\n", "", "The following perls have been installed:", map ( "    $_", grep defined, @ok ), "", "" );
×
1455
    return;
×
1456
}
1457

1458
sub run_command_download {
1459
    my ( $self, $dist ) = @_;
1✔
1460

1461
    $dist = $self->resolve_stable_version
1✔
1462
        if $dist && $dist eq 'stable';
1463

1464
    my $rd = $self->release_detail($dist);
1✔
1465

1466
    my $dist_tarball      = $rd->{tarball_name};
1✔
1467
    my $dist_tarball_url  = $rd->{tarball_url};
1✔
1468
    my $dist_tarball_path = $self->root->dists($dist_tarball);
1✔
1469

1470
    if ( -f $dist_tarball_path && !$self->{force} ) {
1✔
1471
        print "$dist_tarball already exists\n";
×
1472
    }
1473
    else {
1474
        print "Download $dist_tarball_url to $dist_tarball_path\n" unless $self->{quiet};
1✔
1475
        my $error = http_download( $dist_tarball_url, $dist_tarball_path );
1✔
1476
        if ($error) {
1✔
1477
            die "ERROR: Failed to download $dist_tarball_url\n$error\n";
1✔
1478
        }
1479
    }
1480
}
1481

1482
sub purify {
1483
    my ( $self, $envname ) = @_;
6✔
1484
    my @paths = grep { index( $_, $self->home ) < 0 && index( $_, $self->root ) < 0 } split /:/, $self->env($envname);
6✔
1485
    return wantarray ? @paths : join( ":", @paths );
6✔
1486
}
1487

1488
sub system_perl_executable {
1489
    my ($self) = @_;
6✔
1490

1491
    my $system_perl_executable = do {
6✔
1492
        local $ENV{PATH} = $self->pristine_path;
6✔
1493
        `perl -MConfig -e 'print \$Config{perlpath}'`;
6✔
1494
    };
1495

1496
    return $system_perl_executable;
6✔
1497
}
1498

1499
sub system_perl_shebang {
1500
    my ($self) = @_;
6✔
1501
    return $Config{sharpbang} . $self->system_perl_executable;
6✔
1502
}
1503

1504
sub pristine_path {
1505
    my ($self) = @_;
6✔
1506
    return $self->purify("PATH");
6✔
1507
}
1508

1509
sub pristine_manpath {
1510
    my ($self) = @_;
×
1511
    return $self->purify("MANPATH");
×
1512
}
1513

1514
sub run_command_display_system_perl_executable {
1515
    print $_[0]->system_perl_executable . "\n";
×
1516
}
1517

1518
sub run_command_display_system_perl_shebang {
1519
    print $_[0]->system_perl_shebang . "\n";
×
1520
}
1521

1522
sub run_command_display_pristine_path {
1523
    print $_[0]->pristine_path . "\n";
×
1524
}
1525

1526
sub run_command_display_pristine_manpath {
1527
    print $_[0]->pristine_manpath . "\n";
×
1528
}
1529

1530
sub do_install_archive {
1531
    require File::Basename;
9✔
1532

1533
    my $self              = shift;
9✔
1534
    my $dist_tarball_path = shift;
9✔
1535
    my $dist_version;
9✔
1536
    my $installation_name;
1537

1538
    if ( $dist_tarball_path->basename =~ m{(c?perl)-?(5.+)\.tar\.(gz|bz2|xz)\Z} ) {
9✔
1539
        my $perl_variant = $1;
9✔
1540
        $dist_version      = $2;
9✔
1541
        $installation_name = "${perl_variant}-${dist_version}";
9✔
1542
    }
1543

1544
    unless ( $dist_version && $installation_name ) {
9✔
1545
        die
×
1546
"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";
1547
    }
1548

1549
    my $dist_extracted_path = $self->do_extract_tarball($dist_tarball_path);
9✔
1550

1551
    $self->do_install_this( $dist_extracted_path, $dist_version, $installation_name );
9✔
1552
}
1553

1554
sub do_install_this {
1555
    my ( $self, $dist_extracted_dir, $dist_version, $installation_name ) = @_;
4✔
1556

1557
    my $variation                          = $self->{variation};
4✔
1558
    my $append                             = $self->{append};
4✔
1559
    my $looks_like_we_are_installing_cperl = $dist_extracted_dir =~ /\/ cperl- /x;
4✔
1560

1561
    $self->{dist_extracted_dir} = $dist_extracted_dir;
4✔
1562
    $self->{log_file}           = $self->root->child("build.${installation_name}${variation}${append}.log");
4✔
1563

1564
    my @d_options     = @{ $self->{D} };
4✔
1565
    my @u_options     = @{ $self->{U} };
4✔
1566
    my @a_options     = @{ $self->{A} };
4✔
1567
    my $sitecustomize = $self->{sitecustomize};
4✔
1568
    my $destdir       = $self->{destdir};
4✔
1569
    $installation_name = $self->{as} if $self->{as};
4✔
1570
    $installation_name .= "$variation$append";
4✔
1571

1572
    $self->{installation_name} = $installation_name;
4✔
1573

1574
    if ($sitecustomize) {
4✔
1575
        die "Could not read sitecustomize file '$sitecustomize'\n"
2✔
1576
            unless -r $sitecustomize;
1577
        push @d_options, "usesitecustomize";
2✔
1578
    }
1579

1580
    if ( $self->{noman} ) {
4✔
1581
        push @d_options, qw/man1dir=none man3dir=none/;
×
1582
    }
1583

1584
    for my $flavor ( keys %flavor ) {
4✔
1585
        $self->{$flavor} and push @d_options, $flavor{$flavor}{d_option};
28✔
1586
    }
1587

1588
    my $perlpath = $self->root->perls($installation_name);
4✔
1589

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

1593
    push @d_options, "usecperl" if $looks_like_we_are_installing_cperl;
4✔
1594

1595
    my $version = $self->comparable_perl_version($dist_version);
4✔
1596
    if ( defined $version and $version < $self->comparable_perl_version('5.6.0') ) {
4✔
1597

1598
        # ancient perls do not support -A for Configure
1599
        @a_options = ();
×
1600
    }
1601
    else {
1602
        unless ( grep { /eval:scriptdir=/ } @a_options ) {
4✔
1603
            push @a_options, "'eval:scriptdir=${perlpath}/bin'";
4✔
1604
        }
1605
    }
1606

1607
    print "Installing $dist_extracted_dir into "
4✔
1608
        . $self->root->perls($installation_name)->stringify_with_tilde . "\n\n";
1609
    print <<INSTALL if !$self->{verbose};
4✔
1610
This could take a while. You can run the following command on another shell to track the status:
1611

1612
  tail -f ${\ $self->{log_file}->stringify_with_tilde }
4✔
1613

1614
INSTALL
1615

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

1618
    unless ( $self->{"no-patchperl"} || $looks_like_we_are_installing_cperl ) {
4✔
1619
        my $patchperl = $self->root->bin("patchperl");
4✔
1620

1621
        unless ( -x $patchperl && -f _ ) {
4✔
1622
            $patchperl = "patchperl";
4✔
1623
        }
1624

1625
        push @preconfigure_commands, 'chmod -R +w .', $patchperl;
4✔
1626
    }
1627

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

1630
    my @configure_commands = (
1631
        "sh Configure $configure_flags "
1632
            . join( ' ',
1633
            ( map { qq{'-D$_'} } @d_options ),
6✔
1634
            ( map { qq{'-U$_'} } @u_options ),
×
1635
            ( map { qq{'-A$_'} } @a_options ),
4✔
1636
            ),
1637
        ( defined $version and $version < $self->comparable_perl_version('5.8.9') )
1638
        ? ("$^X -i -nle 'print unless /command-line/' makefile x2p/makefile")
1639
        : ()
1640
    );
1641

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

1645
    # Test via "make test_harness" if available so we'll get
1646
    # automatic parallel testing via $HARNESS_OPTIONS. The
1647
    # "test_harness" target was added in 5.7.3, which was the last
1648
    # development release before 5.8.0.
1649
    my $use_harness = ( $dist_version =~ /^5\.(\d+)\.(\d+)/
4✔
1650
                        && ( $1 >= 8 || $1 == 7 && $2 == 3 ) )
1651
        || $dist_version eq "blead";
1652
    my $test_target = $use_harness ? "test_harness" : "test";
4✔
1653

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

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

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

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

1665
    $self->{log_file}->unlink;
4✔
1666

1667
    if ( $self->{verbose} ) {
4✔
1668
        $cmd = "($cmd) 2>&1 | tee $self->{log_file}";
×
1669
        print "$cmd\n" if $self->{verbose};
×
1670
    }
1671
    else {
1672
        $cmd = "($cmd) >> '$self->{log_file}' 2>&1 ";
4✔
1673
    }
1674

1675
    delete $ENV{$_} for qw(PERL5LIB PERL5OPT AWKPATH NO_COLOR);
4✔
1676

1677
    if ( $self->do_system($cmd) ) {
4✔
1678
        my $newperl = $self->root->perls($installation_name)->perl;
3✔
1679
        unless ( -e $newperl ) {
3✔
1680
            $self->run_command_symlink_executables($installation_name);
2✔
1681
        }
1682

1683
        eval { $self->append_log('##### Brew Finished #####') };
3✔
1684

1685
        if ($sitecustomize) {
3✔
1686
            my $capture = $self->do_capture("$newperl -V:sitelib");
2✔
1687
            my ($sitelib) = $capture =~ m/sitelib='([^']*)';/;
2✔
1688
            $sitelib = $destdir . $sitelib if $destdir;
2✔
1689
            $sitelib = App::Perlbrew::Path->new($sitelib);
2✔
1690
            $sitelib->mkpath;
2✔
1691
            my $target = $sitelib->child("sitecustomize.pl");
2✔
1692
            open my $dst, ">", $target
2✔
1693
                or die "Could not open '$target' for writing: $!\n";
1694
            open my $src, "<", $sitecustomize
2✔
1695
                or die "Could not open '$sitecustomize' for reading: $!\n";
1696
            print {$dst} do { local $/; <$src> };
2✔
1697
        }
1698

1699
        my $version_file = $self->root->perls($installation_name)->version_file;
3✔
1700

1701
        if ( -e $version_file ) {
3✔
1702
            $version_file->unlink()
×
1703
                or die "Could not unlink $version_file file: $!\n";
1704
        }
1705

1706
        print "$installation_name is successfully installed.\n";
3✔
1707
    }
1708
    else {
1709
        eval { $self->append_log('##### Brew Failed #####') };
1✔
1710
        die $self->INSTALLATION_FAILURE_MESSAGE;
1✔
1711
    }
1712
    return;
3✔
1713
}
1714

1715
sub do_install_program_from_url {
1716
    my ( $self, $url, $program_name, $body_filter ) = @_;
8✔
1717

1718
    my $out = $self->root->bin($program_name);
8✔
1719

1720
    if ( -f $out && !$self->{force} && !$self->{yes} ) {
8✔
1721
        require ExtUtils::MakeMaker;
×
1722

1723
        my $ans = ExtUtils::MakeMaker::prompt( "\n$out already exists, are you sure to override ? [y/N]", "N" );
×
1724

1725
        if ( $ans !~ /^Y/i ) {
×
1726
            print "\n$program_name installation skipped.\n\n" unless $self->{quiet};
×
1727
            return;
×
1728
        }
1729
    }
1730

1731
    my $body = http_get($url) or die "\nERROR: Failed to retrieve $program_name executable.\n\n";
8✔
1732

1733
    unless ( $body =~ m{\A#!/}s ) {
6✔
1734
        my $x = App::Perlbrew::Path->new( $self->env('TMPDIR') || "/tmp", "${program_name}.downloaded.$$" );
3✔
1735
        my $message =
3✔
1736
"\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.";
1737

1738
        unless ( -f $x ) {
3✔
1739
            open my $OUT, ">", $x;
3✔
1740
            print $OUT $body;
3✔
1741
            close($OUT);
3✔
1742
            $message .= "\n\nThe previously downloaded file is saved at $x for manual inspection.\n\n";
3✔
1743
        }
1744

1745
        die $message;
3✔
1746
    }
1747

1748
    if ( $body_filter && ref($body_filter) eq "CODE" ) {
3✔
1749
        $body = $body_filter->($body);
1✔
1750
    }
1751

1752
    $self->root->bin->mkpath;
3✔
1753
    open my $OUT, '>', $out or die "cannot open file($out): $!";
3✔
1754
    print $OUT $body;
3✔
1755
    close $OUT;
3✔
1756
    chmod 0755, $out;
3✔
1757
    print "\n$program_name is installed to\n\n    $out\n\n" unless $self->{quiet};
3✔
1758
}
1759

1760
sub do_exit_with_error_code {
1761
    my ( $self, $code ) = @_;
×
1762
    exit($code);
×
1763
}
1764

1765
sub do_system_with_exit_code {
1766
    my ( $self, @cmd ) = @_;
4✔
1767
    return system(@cmd);
4✔
1768
}
1769

1770
sub do_system {
1771
    my ( $self, @cmd ) = @_;
4✔
1772
    return !$self->do_system_with_exit_code(@cmd);
4✔
1773
}
1774

1775
sub do_capture {
1776
    my ( $self, @cmd ) = @_;
4✔
1777
    return Capture::Tiny::capture(
1778
        sub {
1779
            $self->do_system(@cmd);
4✔
1780
        }
1781
    );
4✔
1782
}
1783

1784
sub format_perl_version {
1785
    my $self    = shift;
471✔
1786
    my $version = shift;
471✔
1787
    return sprintf "%d.%d.%d", substr( $version, 0, 1 ), substr( $version, 2, 3 ), substr( $version, 5 ) || 0;
471✔
1788
}
1789

1790
sub installed_perls {
1791
    my $self = shift;
175✔
1792

1793
    my @result;
175✔
1794
    my $root = $self->root;
175✔
1795

1796
    for my $installation ( $root->perls->list ) {
175✔
1797
        my $name       = $installation->name;
467✔
1798
        my $executable = $installation->perl;
467✔
1799
        next unless -f $executable;
467✔
1800

1801
        my $version_file = $installation->version_file;
467✔
1802
        my $ctime        = localtime( ( stat $executable )[10] );    # localtime in scalar context!
467✔
1803

1804
        my $orig_version;
467✔
1805
        if ( -e $version_file ) {
467✔
1806
            open my $fh, '<', $version_file;
434✔
1807
            local $/;
434✔
1808
            $orig_version = <$fh>;
434✔
1809
            chomp $orig_version;
434✔
1810
        }
1811
        else {
1812
            $orig_version = `$executable -e 'print \$]'`;
33✔
1813
            if ( defined $orig_version and length $orig_version ) {
33✔
1814
                if ( open my $fh, '>', $version_file ) {
33✔
1815
                    print {$fh} $orig_version;
33✔
1816
                }
1817
            }
1818
        }
1819

1820
        push @result,
467✔
1821
            {
1822
            name               => $name,
1823
            orig_version       => $orig_version,
1824
            version            => $self->format_perl_version($orig_version),
1825
            is_current         => ( $self->current_perl eq $name ) && !( $self->current_lib ),
1826
            libs               => [$self->local_libs($name)],
1827
            executable         => $executable,
1828
            dir                => $installation,
1829
            comparable_version => $self->comparable_perl_version($orig_version),
1830
            ctime              => $ctime,
1831
            };
1832
    }
1833

1834
    return sort {
1835
        (
175✔
1836
            $self->{reverse}
1837
            ? ( $a->{comparable_version} <=> $b->{comparable_version} or $b->{name} cmp $a->{name} )
1838
            : ( $b->{comparable_version} <=> $a->{comparable_version} or $a->{name} cmp $b->{name} )
1839
        )
415✔
1840
    } @result;
1841
}
1842

1843
sub compose_locallib {
1844
    my ( $self, $perl_name, $lib_name ) = @_;
8✔
1845
    return join '@', $perl_name, $lib_name;
8✔
1846
}
1847

1848
sub decompose_locallib {
1849
    my ( $self, $name ) = @_;
121✔
1850
    return split '@', $name;
121✔
1851
}
1852

1853
sub enforce_localib {
1854
    my ( $self, $name ) = @_;
9✔
1855
    $name =~ s/^/@/ unless $name =~ m/@/;
9✔
1856
    return $name;
9✔
1857
}
1858

1859
sub local_libs {
1860
    my ( $self, $perl_name ) = @_;
469✔
1861

1862
    my $current = $self->current_env;
469✔
1863
    my @libs    = map {
1864
        my $name = $_->basename;
469✔
1865
        my ( $p, $l ) = $self->decompose_locallib($name);
39✔
1866
        +{
1867
            name       => $name,
39✔
1868
            is_current => $name eq $current,
1869
            perl_name  => $p,
1870
            lib_name   => $l,
1871
            dir        => $_,
1872
        }
1873
    } $self->home->child("libs")->children;
1874
    if ($perl_name) {
469✔
1875
        @libs = grep { $perl_name eq $_->{perl_name} } @libs;
469✔
1876
    }
1877
    return @libs;
469✔
1878
}
1879

1880
sub is_installed {
1881
    my ( $self, $name ) = @_;
136✔
1882

1883
    return grep { $name eq $_->{name} } $self->installed_perls;
136✔
1884
}
1885

1886
sub assert_known_installation {
1887
    my ( $self, $name ) = @_;
×
1888
    return 1 if $self->is_installed($name);
×
1889
    die "ERROR: The installation \"$name\" is unknown\n\n";
×
1890
}
1891

1892
# Return a hash of PERLBREW_* variables
1893
sub perlbrew_env {
1894
    my ( $self, $name ) = @_;
35✔
1895
    my ( $perl_name, $lib_name );
35✔
1896

1897
    if ($name) {
35✔
1898
        ( $perl_name, $lib_name ) = $self->resolve_installation_name($name);
34✔
1899

1900
        unless ($perl_name) {
34✔
1901
            die "\nERROR: The installation \"$name\" is unknown.\n\n";
×
1902
        }
1903

1904
        unless ( !$lib_name || grep { $_->{lib_name} eq $lib_name } $self->local_libs($perl_name) ) {
34✔
1905
            die "\nERROR: The lib name \"$lib_name\" is unknown.\n\n";
×
1906
        }
1907
    }
1908

1909
    my %env = (
35✔
1910
        PERLBREW_VERSION => $VERSION,
1911
        PERLBREW_PATH    => $self->root->bin,
1912
        PERLBREW_MANPATH => "",
1913
        PERLBREW_ROOT    => $self->root
1914
    );
1915

1916
    require local::lib;
35✔
1917
    my $pb_home                   = $self->home;
35✔
1918
    my $current_local_lib_root    = $self->env("PERL_LOCAL_LIB_ROOT") || "";
35✔
1919
    my $current_local_lib_context = local::lib->new;
35✔
1920
    my @perlbrew_local_lib_root   = uniq( grep { /\Q${pb_home}\E/ } split( /:/, $current_local_lib_root ) );
35✔
1921
    if ( $current_local_lib_root =~ /^\Q${pb_home}\E/ ) {
35✔
1922
        $current_local_lib_context = $current_local_lib_context->activate($_) for @perlbrew_local_lib_root;
×
1923
    }
1924

1925
    if ($perl_name) {
35✔
1926
        my $installation = $self->root->perls($perl_name);
34✔
1927
        if ( -d $installation->child("bin") ) {
34✔
1928
            $env{PERLBREW_PERL} = $perl_name;
34✔
1929
            $env{PERLBREW_PATH} .= ":" . $installation->child("bin");
34✔
1930
            $env{PERLBREW_MANPATH} = $installation->child("man");
34✔
1931
        }
1932

1933
        if ($lib_name) {
34✔
1934
            $current_local_lib_context = $current_local_lib_context->deactivate($_) for @perlbrew_local_lib_root;
2✔
1935

1936
            my $base = $self->home->child( "libs", "${perl_name}\@${lib_name}" );
2✔
1937

1938
            if ( -d $base ) {
2✔
1939
                $current_local_lib_context = $current_local_lib_context->activate($base);
2✔
1940

1941
                if ( $self->env('PERLBREW_LIB_PREFIX') ) {
2✔
1942
                    unshift
1943
                        @{ $current_local_lib_context->libs },
1✔
1944
                        $self->env('PERLBREW_LIB_PREFIX');
1945
                }
1946

1947
                $env{PERLBREW_PATH}    = $base->child("bin") . ":" . $env{PERLBREW_PATH};
2✔
1948
                $env{PERLBREW_MANPATH} = $base->child("man") . ":" . $env{PERLBREW_MANPATH};
2✔
1949
                $env{PERLBREW_LIB}     = $lib_name;
2✔
1950
            }
1951
        }
1952
        else {
1953
            $current_local_lib_context = $current_local_lib_context->deactivate($_) for @perlbrew_local_lib_root;
32✔
1954
            $env{PERLBREW_LIB} = undef;
32✔
1955
        }
1956

1957
        my %ll_env = $current_local_lib_context->build_environment_vars;
34✔
1958
        delete $ll_env{PATH};
34✔
1959
        for my $key ( keys %ll_env ) {
34✔
1960
            $env{$key} = $ll_env{$key};
72✔
1961
        }
1962
    }
1963
    else {
1964
        $current_local_lib_context = $current_local_lib_context->deactivate($_) for @perlbrew_local_lib_root;
1✔
1965

1966
        my %ll_env = $current_local_lib_context->build_environment_vars;
1✔
1967
        delete $ll_env{PATH};
1✔
1968
        for my $key ( keys %ll_env ) {
1✔
1969
            $env{$key} = $ll_env{$key};
2✔
1970
        }
1971
        $env{PERLBREW_LIB}  = undef;
1✔
1972
        $env{PERLBREW_PERL} = undef;
1✔
1973
    }
1974

1975
    return %env;
35✔
1976
}
1977

1978
sub run_command_list {
1979
    my $self       = shift;
5✔
1980
    my $is_verbose = $self->{verbose};
5✔
1981

1982
    if ( $self->{'no-decoration'} ) {
5✔
1983
        for my $i ( $self->installed_perls ) {
2✔
1984
            print $i->{name} . "\n";
8✔
1985
            for my $lib ( @{ $i->{libs} } ) {
8✔
1986
                print $lib->{name} . "\n";
2✔
1987
            }
1988
        }
1989
    }
1990
    else {
1991
        for my $i ( $self->installed_perls ) {
3✔
1992
            printf "%-2s%-20s %-20s %s\n", $i->{is_current} ? '*' : '', $i->{name},
1993
                (
1994
                  $is_verbose
1995
                ? ( index( $i->{name}, $i->{version} ) < 0 )
12✔
1996
                        ? "($i->{version})"
1997
                        : ''
1998
                : ''
1999
                ),
2000
                ( $is_verbose ? "(installed on $i->{ctime})" : '' );
2001

2002
            for my $lib ( @{ $i->{libs} } ) {
12✔
2003
                print $lib->{is_current} ? "* " : "  ", $lib->{name}, "\n";
4✔
2004
            }
2005
        }
2006
    }
2007

2008
    return 0;
5✔
2009
}
2010

2011
sub launch_sub_shell {
2012
    my ( $self, $name ) = @_;
×
2013
    my $shell = $self->env('SHELL');
×
2014

2015
    my $shell_opt = "";
×
2016

2017
    if ( $shell =~ /\/zsh\d?$/ ) {
×
2018
        $shell_opt = "-d -f";
×
2019

2020
        if ( $^O eq 'darwin' ) {
×
2021
            my $root_dir = $self->root;
×
2022
            print <<"WARNINGONMAC";
×
2023
--------------------------------------------------------------------------------
2024
WARNING: zsh perlbrew sub-shell is not working on Mac OSX Lion.
2025

2026
It is known that on MacOS Lion, zsh always resets the value of PATH on launching
2027
a sub-shell. Effectively nullify the changes required by perlbrew sub-shell. You
2028
may `echo \$PATH` to examine it and if you see perlbrew related paths are in the
2029
end, instead of in the beginning, you are unfortunate.
2030

2031
You are advised to include the following line to your ~/.zshenv as a better
2032
way to work with perlbrew:
2033

2034
    source $root_dir/etc/bashrc
2035

2036
--------------------------------------------------------------------------------
2037
WARNINGONMAC
2038

2039
        }
2040
    }
2041

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

2044
    unless ( $ENV{PERLBREW_VERSION} ) {
×
2045
        my $root = $self->root;
×
2046

2047
        # The user does not source bashrc/csh in their shell initialization.
2048
        $env{PATH}    = $env{PERLBREW_PATH} . ":" . join ":", grep { !/$root\/bin/ } split ":", $ENV{PATH};
×
2049
        $env{MANPATH} = $env{PERLBREW_MANPATH} . ":" . join ":",
2050
            grep { !/$root\/man/ } ( defined( $ENV{MANPATH} ) ? split( ":", $ENV{MANPATH} ) : () );
×
2051
    }
2052

2053
    my $command = "env ";
×
2054
    while ( my ( $k, $v ) = each(%env) ) {
×
2055
        no warnings "uninitialized";
68✔
2056
        $command .= "$k=\"$v\" ";
×
2057
    }
2058
    $command .= " $shell $shell_opt";
×
2059

2060
    my $pretty_name = defined($name) ? $name : "the default perl";
×
2061
    print "\nA sub-shell is launched with $pretty_name as the activated perl. Run 'exit' to finish it.\n\n";
×
2062
    exec($command);
×
2063
}
2064

2065
sub run_command_use {
2066
    my $self = shift;
×
2067
    my $perl = shift;
×
2068

2069
    if ( !$perl ) {
×
2070
        my $current = $self->current_env;
×
2071
        if ($current) {
×
2072
            print "Currently using $current\n";
×
2073
        }
2074
        else {
2075
            print "No version in use; defaulting to system\n";
×
2076
        }
2077
        return;
×
2078
    }
2079

2080
    $self->launch_sub_shell($perl);
×
2081

2082
}
2083

2084
sub run_command_switch {
2085
    my ( $self, $dist, $alias ) = @_;
×
2086

2087
    unless ($dist) {
×
2088
        my $current = $self->current_env;
×
2089
        printf "Currently switched %s\n", ( $current ? "to $current" : 'off' );
×
2090
        return;
×
2091
    }
2092

2093
    $self->switch_to( $dist, $alias );
×
2094
}
2095

2096
sub switch_to {
2097
    my ( $self, $dist, $alias ) = @_;
×
2098

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

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

2104
    if ( $self->env("PERLBREW_SHELLRC_VERSION") && $self->current_shell_is_bashish ) {
×
2105
        local $ENV{PERLBREW_PERL} = $dist;
×
2106
        my $HOME    = $self->env('HOME');
×
2107
        my $pb_home = $self->home;
×
2108

2109
        $pb_home->mkpath;
×
2110
        system( "$0 env $dist > " . $pb_home->child("init") );
×
2111

2112
        print "Switched to $dist.\n\n";
×
2113
    }
2114
    else {
2115
        $self->launch_sub_shell($dist);
×
2116
    }
2117
}
2118

2119
sub run_command_off {
2120
    my $self = shift;
×
2121
    $self->launch_sub_shell;
×
2122
}
2123

2124
sub run_command_switch_off {
2125
    my $self    = shift;
×
2126
    my $pb_home = $self->home;
×
2127

2128
    $pb_home->mkpath;
×
2129
    system( "env PERLBREW_PERL= $0 env > " . $pb_home->child("init") );
×
2130

2131
    print "\nperlbrew is switched off. Please exit this shell and start a new one to make it effective.\n";
×
2132
    print
×
2133
        "To immediately make it effective, run this line in this terminal:\n\n    exec @{[ $self->env('SHELL') ]}\n\n";
×
2134
}
2135

2136
sub shell_env {
2137
    my ( $self, $env ) = @_;
5✔
2138
    my %env = %$env;
5✔
2139

2140
    my @statements;
5✔
2141
    for my $k ( sort keys %env ) {
5✔
2142
        my $v = $env{$k};
37✔
2143
        if ( defined($v) && $v ne '' ) {
37✔
2144
            $v =~ s/(\\")/\\$1/g;
32✔
2145
            push @statements, ["set", $k, $v];
32✔
2146
        }
2147
        else {
2148
            push @statements, ["unset", $k];
5✔
2149
        }
2150
    }
2151

2152
    my $statements = "";
5✔
2153

2154
    if ( $self->env('SHELL') =~ /(ba|k|z|\/)sh\d?$/ ) {
5✔
2155
        for (@statements) {
3✔
2156
            my ( $o, $k, $v ) = @$_;
28✔
2157
            if ( $o eq 'unset' ) {
28✔
2158
                $statements .= "unset $k\n";
3✔
2159
            }
2160
            else {
2161
                $v =~ s/(\\")/\\$1/g;
25✔
2162
                $statements .= "export $k=\"$v\"\n";
25✔
2163
            }
2164
        }
2165
    }
2166
    else {
2167
        for (@statements) {
2✔
2168
            my ( $o, $k, $v ) = @$_;
9✔
2169
            if ( $o eq 'unset' ) {
9✔
2170
                $statements .= "unsetenv $k\n";
2✔
2171
            }
2172
            else {
2173
                $statements .= "setenv $k \"$v\"\n";
7✔
2174
            }
2175
        }
2176
    }
2177

2178
    return $statements;
5✔
2179
}
2180

2181
sub run_command_env {
2182
    my ( $self, $name ) = @_;
3✔
2183

2184
    print $self->shell_env({ $self->perlbrew_env($name) });
3✔
2185
}
2186

2187
sub run_command_symlink_executables {
2188
    my ( $self, @perls ) = @_;
2✔
2189
    my $root = $self->root;
2✔
2190

2191
    unless (@perls) {
2✔
2192
        @perls = map { $_->name } grep { -d $_ && !-l $_ } $root->perls->list;
×
2193
    }
2194

2195
    for my $perl (@perls) {
2✔
2196
        for my $executable ( $root->perls($perl)->bin->children ) {
2✔
2197
            my ( $name, $version ) = $executable =~ m/bin\/(.+?)(5\.\d.*)?$/;
×
2198
            next unless $version;
×
2199

2200
            $executable->symlink( $root->perls($perl)->bin($name) );
×
2201
            $executable->symlink( $root->perls($perl)->perl ) if $name eq "cperl";
×
2202
        }
2203
    }
2204
}
2205

2206
sub run_command_install_patchperl {
2207
    my ($self) = @_;
2✔
2208
    $self->do_install_program_from_url(
2209
        'https://raw.githubusercontent.com/gugod/patchperl-packing/master/patchperl',
2210
        'patchperl',
2211
        sub {
2212
            my ($body) = @_;
1✔
2213
            $body =~ s/\A#!.+?\n/ $self->system_perl_shebang . "\n" /se;
1✔
2214
            return $body;
1✔
2215
        }
2216
    );
2✔
2217
}
2218

2219
sub run_command_install_cpanm {
2220
    my ($self) = @_;
3✔
2221
    $self->do_install_program_from_url(
3✔
2222
        'https://raw.githubusercontent.com/miyagawa/cpanminus/master/cpanm' => 'cpanm' );
2223
}
2224

2225
sub run_command_install_cpm {
2226
    my ($self) = @_;
3✔
2227
    $self->do_install_program_from_url( 'https://raw.githubusercontent.com/skaji/cpm/main/cpm' => 'cpm' );
3✔
2228
}
2229

2230
sub run_command_self_upgrade {
2231
    my ($self) = @_;
×
2232

2233
    require FindBin;
×
2234
    unless ( -w $FindBin::Bin ) {
×
2235
        die "Your perlbrew installation appears to be system-wide.  Please upgrade through your package manager.\n";
×
2236
    }
2237

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

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

2243
    chmod 0755, $TMP_PERLBREW;
×
2244
    my $new_version = qx($TMP_PERLBREW version);
×
2245
    chomp $new_version;
×
2246
    if ( $new_version =~ /App::perlbrew\/(\d+\.\d+)$/ ) {
×
2247
        $new_version = $1;
×
2248
    }
2249
    else {
2250
        $TMP_PERLBREW->unlink;
×
2251
        die "Unable to detect version of new perlbrew!\n";
×
2252
    }
2253

2254
    if ( $new_version <= $VERSION ) {
×
2255
        print "Your perlbrew is up-to-date (version $VERSION).\n" unless $self->{quiet};
×
2256
        $TMP_PERLBREW->unlink;
×
2257
        return;
×
2258
    }
2259

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

2262
    system $TMP_PERLBREW, "self-install";
×
2263
    $TMP_PERLBREW->unlink;
×
2264
}
2265

2266
sub run_command_uninstall {
2267
    my ( $self, $target ) = @_;
×
2268

2269
    unless ($target) {
×
2270
        $self->run_command_help("uninstall");
×
2271
        exit(-1);
×
2272
    }
2273

2274
    my @installed = $self->installed_perls(@_);
×
2275

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

2278
    die "'$target' is not installed\n" unless $to_delete;
×
2279

2280
    my @dir_to_delete;
×
2281
    for ( @{ $to_delete->{libs} } ) {
×
2282
        push @dir_to_delete, $_->{dir};
×
2283
    }
2284
    push @dir_to_delete, $to_delete->{dir};
×
2285

2286
    my $ans = ( $self->{yes} ) ? "Y" : undef;
×
2287
    if ( !defined($ans) ) {
×
2288
        require ExtUtils::MakeMaker;
×
2289
        $ans = ExtUtils::MakeMaker::prompt(
×
2290
            "\nThe following perl+lib installation(s) will be deleted:\n\n\t"
2291
                . join( "\n\t", @dir_to_delete )
2292
                . "\n\n... are you sure ? [y/N]",
2293
            "N"
2294
        );
2295
    }
2296

2297
    if ( $ans =~ /^Y/i ) {
×
2298
        for (@dir_to_delete) {
×
2299
            print "Deleting: $_\n" unless $self->{quiet};
×
2300
            App::Perlbrew::Path->new($_)->rmpath;
×
2301
            print "Deleted:  $_\n" unless $self->{quiet};
×
2302
        }
2303
    }
2304
    else {
2305
        print "\nOK. Not deleting anything.\n\n";
×
2306
        return;
×
2307
    }
2308
}
2309

2310
sub run_command_exec {
2311
    my $self = shift;
17✔
2312
    my %opts;
17✔
2313

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

2316
    Getopt::Long::Configure('require_order');
17✔
2317
    my @command_options = ( 'with=s', 'halt-on-error', 'min=s', 'max=s' );
17✔
2318

2319
    $self->parse_cmdline( \%opts, @command_options );
17✔
2320
    shift @ARGV;    # "exec"
17✔
2321
    $self->parse_cmdline( \%opts, @command_options );
17✔
2322

2323
    my @exec_with;
17✔
2324
    if ( $opts{with} ) {
17✔
2325
        my %installed = map { $_->{name} => $_ } map { ( $_, @{ $_->{libs} } ) } $self->installed_perls;
14✔
2326

2327
        my $d    = ( $opts{with} =~ m/ / ) ? qr( +) : qr(,+);
14✔
2328
        my @with = grep { $_ } map {
21✔
2329
            my ( $p, $l ) = $self->resolve_installation_name($_);
21✔
2330
            $p .= "\@$l" if $l;
21✔
2331
            $p;
21✔
2332
        } split $d, $opts{with};
14✔
2333

2334
        @exec_with = map { $installed{$_} } @with;
14✔
2335
    }
2336
    else {
2337
        @exec_with = grep {
2338
            not -l $self->root->perls( $_->{name} );    # Skip Aliases
12✔
2339
        } map { ( $_, @{ $_->{libs} } ) } $self->installed_perls;
3✔
2340
    }
2341

2342
    if ( $opts{min} ) {
17✔
2343

2344
        # TODO use comparable version.
2345
        # For now, it doesn't produce consistent results for 5.026001 and 5.26.1
2346
        @exec_with = grep { $_->{orig_version} >= $opts{min} } @exec_with;
1✔
2347
    }
2348

2349
    if ( $opts{max} ) {
17✔
2350
        @exec_with = grep { $_->{orig_version} <= $opts{max} } @exec_with;
1✔
2351
    }
2352

2353
    if ( 0 == @exec_with ) {
17✔
2354
        print "No perl installation found.\n" unless $self->{quiet};
×
2355
    }
2356

2357
    my $no_header = 0;
17✔
2358
    if ( 1 == @exec_with ) {
17✔
2359
        $no_header = 1;
9✔
2360
    }
2361

2362
    my $overall_success = 1;
17✔
2363
    for my $i (@exec_with) {
17✔
2364
        my %env = $self->perlbrew_env( $i->{name} );
28✔
2365
        next if !$env{PERLBREW_PERL};
28✔
2366

2367
        local %ENV = %ENV;
28✔
2368
        $ENV{$_}       = defined $env{$_} ? $env{$_} : '' for keys %env;
28✔
2369
        $ENV{PATH}     = join( ':', $env{PERLBREW_PATH},    $ENV{PATH} );
28✔
2370
        $ENV{MANPATH}  = join( ':', $env{PERLBREW_MANPATH}, $ENV{MANPATH} || "" );
28✔
2371
        $ENV{PERL5LIB} = $env{PERL5LIB} || "";
28✔
2372

2373
        print "$i->{name}\n==========\n" unless $no_header || $self->{quiet};
28✔
2374

2375
        if ( my $err = $self->do_system_with_exit_code(@ARGV) ) {
28✔
2376
            my $exit_code = $err >> 8;
8✔
2377

2378
         # return 255 for case when process was terminated with signal, in that case real exit code is useless and weird
2379
            $exit_code       = 255 if $exit_code > 255;
8✔
2380
            $overall_success = 0;
8✔
2381

2382
            unless ( $self->{quiet} ) {
8✔
2383
                print "Command terminated with non-zero status.\n";
7✔
2384

2385
                print STDERR "Command ["
2386
                    . join( ' ', map { /\s/ ? "'$_'" : $_ } @ARGV )
7✔
2387
                    .    # trying reverse shell escapes - quote arguments containing spaces
2388
                    "] terminated with exit code $exit_code (\$? = $err) under the following perl environment:\n";
2389
                print STDERR $self->format_info_output;
7✔
2390
            }
2391

2392
            $self->do_exit_with_error_code($exit_code) if ( $opts{'halt-on-error'} );
8✔
2393
        }
2394
        print "\n" unless $self->{quiet} || $no_header;
25✔
2395
    }
2396
    $self->do_exit_with_error_code(1) unless $overall_success;
14✔
2397
}
2398

2399
sub run_command_clean {
2400
    my ($self)     = @_;
×
2401
    my $root       = $self->root;
×
2402
    my @build_dirs = $root->build->children;
×
2403

2404
    for my $dir (@build_dirs) {
×
2405
        print "Removing $dir\n";
×
2406
        App::Perlbrew::Path->new($dir)->rmpath;
×
2407
    }
2408

2409
    my @tarballs = $root->dists->children;
×
2410
    for my $file (@tarballs) {
×
2411
        print "Removing $file\n";
×
2412
        $file->unlink;
×
2413
    }
2414

2415
    print "\nDone\n";
×
2416
}
2417

2418
sub run_command_alias {
2419
    my ( $self, $cmd, $name, $alias ) = @_;
1✔
2420

2421
    unless ($cmd) {
1✔
2422
        $self->run_command_help("alias");
×
2423
        exit(-1);
×
2424
    }
2425

2426
    my $path_name  = $self->root->perls($name)  if $name;
1✔
2427
    my $path_alias = $self->root->perls($alias) if $alias;
1✔
2428

2429
    if ( $alias && -e $path_alias && !-l $path_alias ) {
1✔
2430
        die "\nABORT: The installation name `$alias` is not an alias, cannot override.\n\n";
×
2431
    }
2432

2433
    if ( $cmd eq 'create' ) {
1✔
2434
        $self->assert_known_installation($name);
×
2435

2436
        if ( $self->is_installed($alias) && !$self->{force} ) {
×
2437
            die "\nABORT: The installation `${alias}` already exists. Cannot override.\n\n";
×
2438
        }
2439

2440
        $path_alias->unlink;
×
2441
        $path_name->symlink($path_alias);
×
2442
    }
2443
    elsif ( $cmd eq 'delete' ) {
2444
        $self->assert_known_installation($name);
×
2445

2446
        unless ( -l $path_name ) {
×
2447
            die "\nABORT: The installation name `$name` is not an alias, cannot remove.\n\n";
×
2448
        }
2449

2450
        $path_name->unlink;
×
2451
    }
2452
    elsif ( $cmd eq 'rename' ) {
2453
        $self->assert_known_installation($name);
×
2454

2455
        unless ( -l $path_name ) {
×
2456
            die "\nABORT: The installation name `$name` is not an alias, cannot rename.\n\n";
×
2457
        }
2458

2459
        if ( -l $path_alias && !$self->{force} ) {
×
2460
            die "\nABORT: The alias `$alias` already exists, cannot rename to it.\n\n";
×
2461
        }
2462

2463
        rename( $path_name, $path_alias );
×
2464
    }
2465
    elsif ( $cmd eq 'help' ) {
2466
        $self->run_command_help("alias");
×
2467
    }
2468
    else {
2469
        die "\nERROR: Unrecognized action: `${cmd}`.\n\n";
1✔
2470
    }
2471
}
2472

2473
sub run_command_display_bashrc {
2474
    print BASHRC_CONTENT();
×
2475
}
2476

2477
sub run_command_display_cshrc {
2478
    print CSHRC_CONTENT();
×
2479
}
2480

2481
sub run_command_display_installation_failure_message {
2482
    my ($self) = @_;
×
2483
}
2484

2485
sub run_command_lib {
2486
    my ( $self, $subcommand, @args ) = @_;
12✔
2487

2488
    unless ($subcommand) {
12✔
2489
        $self->run_command_help("lib");
×
2490
        exit(-1);
×
2491
    }
2492

2493
    my $sub = "run_command_lib_$subcommand";
12✔
2494
    if ( $self->can($sub) ) {
12✔
2495
        $self->$sub(@args);
11✔
2496
    }
2497
    else {
2498
        print "Unknown command: $subcommand\n";
1✔
2499
    }
2500
}
2501

2502
sub run_command_lib_create {
2503
    my ( $self, $name ) = @_;
8✔
2504

2505
    die "ERROR: No lib name\n", $self->run_command_help( "lib", undef, 'return_text' ) unless $name;
8✔
2506

2507
    $name = $self->enforce_localib($name);
7✔
2508

2509
    my ( $perl_name, $lib_name ) = $self->resolve_installation_name($name);
7✔
2510

2511
    if ( !$perl_name ) {
7✔
2512
        my ( $perl_name, $lib_name ) = $self->decompose_locallib($name);
1✔
2513
        die "ERROR: '$perl_name' is not installed yet, '$name' cannot be created.\n";
1✔
2514
    }
2515

2516
    my $fullname = $self->compose_locallib( $perl_name, $lib_name );
6✔
2517
    my $dir      = $self->home->child( "libs", $fullname );
6✔
2518

2519
    if ( -d $dir ) {
6✔
2520
        die "$fullname is already there.\n";
×
2521
    }
2522

2523
    $dir->mkpath;
6✔
2524

2525
    print "lib '$fullname' is created.\n" unless $self->{quiet};
6✔
2526

2527
    return;
6✔
2528
}
2529

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

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

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

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

2539
    my $fullname = $self->compose_locallib( $perl_name, $lib_name );
2✔
2540

2541
    my $current = $self->current_env;
2✔
2542

2543
    my $dir = $self->home->child( "libs", $fullname );
2✔
2544

2545
    if ( -d $dir ) {
2✔
2546

2547
        if ( $fullname eq $current ) {
1✔
2548
            die "$fullname is currently being used in the current shell, it cannot be deleted.\n";
×
2549
        }
2550

2551
        $dir->rmpath;
1✔
2552

2553
        print "lib '$fullname' is deleted.\n"
2554
            unless $self->{quiet};
1✔
2555
    }
2556
    else {
2557
        die "ERROR: '$fullname' does not exist.\n";
1✔
2558
    }
2559

2560
    return;
1✔
2561
}
2562

2563
sub run_command_lib_list {
2564
    my ($self) = @_;
×
2565
    my $dir = $self->home->child("libs");
×
2566
    return unless -d $dir;
×
2567

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

2571
    my $current = $self->current_env;
×
2572
    for (@libs) {
×
2573
        print $current eq $_ ? "* " : "  ";
×
2574
        print "$_\n";
×
2575
    }
2576
}
2577

2578
sub run_command_upgrade_perl {
2579
    my ($self) = @_;
×
2580

2581
    my $PERL_VERSION_RE = qr/(\d+)\.(\d+)\.(\d+)/;
×
2582

2583
    my ($current) = grep { $_->{is_current} } $self->installed_perls;
×
2584

2585
    unless ( defined $current ) {
×
2586
        print "no perlbrew environment is currently in use\n";
×
2587
        exit(1);
×
2588
    }
2589

2590
    my ( $major, $minor, $release );
×
2591

2592
    if ( $current->{version} =~ /^$PERL_VERSION_RE$/ ) {
×
2593
        ( $major, $minor, $release ) = ( $1, $2, $3 );
×
2594
    }
2595
    else {
2596
        print "unable to parse version '$current->{version}'\n";
×
2597
        exit(1);
×
2598
    }
2599

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

2602
    my $latest_available_perl = $release;
×
2603

2604
    foreach my $perl (@available) {
×
2605
        if ( $perl =~ /^perl-$PERL_VERSION_RE$/ ) {
×
2606
            my $this_release = $3;
×
2607
            if ( $this_release > $latest_available_perl ) {
×
2608
                $latest_available_perl = $this_release;
×
2609
            }
2610
        }
2611
    }
2612

2613
    if ( $latest_available_perl == $release ) {
×
2614
        print "This perlbrew environment ($current->{name}) is already up-to-date.\n";
×
2615
        exit(0);
×
2616
    }
2617

2618
    my $dist_version = "$major.$minor.$latest_available_perl";
×
2619
    my $dist         = "perl-$dist_version";
×
2620

2621
    print "Upgrading $current->{name} to $dist_version\n" unless $self->{quiet};
×
2622
    local $self->{as}        = $current->{name};
×
2623
    local $self->{dist_name} = $dist;
×
2624

2625
    my @d_options  = map { '-D' . $flavor{$_}->{d_option} } keys %flavor;
×
2626
    my %sub_config = map { $_ => $Config{$_} } grep { /^config_arg\d/ } keys %Config;
×
2627
    for my $value ( values %sub_config ) {
×
2628
        my $value_wo_D = $value;
×
2629
        $value_wo_D =~ s/^-D//;
×
2630
        push @{ $self->{D} }, $value_wo_D if grep { /$value/ } @d_options;
×
2631
    }
2632

2633
    $self->do_install_release( $dist, $dist_version );
×
2634
}
2635

2636
sub list_modules {
2637
    my ( $self, $env ) = @_;
1✔
2638

2639
    $env ||= $self->current_env;
1✔
2640
    my ( $stdout, $stderr, $success ) = Capture::Tiny::capture(
2641
        sub {
2642
            __PACKAGE__->new( "--quiet", "exec", "--with", $env, 'perl', '-MExtUtils::Installed', '-le',
1✔
2643
                'BEGIN{@INC=grep {$_ ne q!.!} @INC}; print for ExtUtils::Installed->new->modules;',
2644
            )->run;
2645
        }
2646
    );
1✔
2647

2648
    unless ($success) {
1✔
2649
        unless ( $self->{quiet} ) {
×
2650
            print STDERR "Failed to retrive the list of installed modules.\n";
×
2651
            if ( $self->{verbose} ) {
×
2652
                print STDERR "STDOUT\n======\n$stdout\nSTDERR\n======\n$stderr\n";
×
2653
            }
2654
        }
2655
        return [];
×
2656
    }
2657

2658
    my %rename = (
1✔
2659
        "ack"                    => "App::Ack",
2660
        "libwww::perl"           => "LWP",
2661
        "libintl-perl"           => "Locale::Messages",
2662
        "Role::Identifiable"     => "Role::Identifiable::HasTags",
2663
        "TAP::Harness::Multiple" => "TAP::Harness::ReportByDescription",
2664
    );
2665

2666
    return [map { $rename{$_} || $_ } grep { $_ ne "Perl" } split( /\n/, $stdout )];
1✔
2667
}
2668

2669
sub run_command_list_modules {
2670
    my ($self) = @_;
×
2671
    my ( $modules, $error ) = $self->list_modules();
×
2672
    print "$_\n" for @$modules;
×
2673
}
2674

2675
sub resolve_installation_name {
2676
    my ( $self, $name ) = @_;
82✔
2677
    die "App::perlbrew->resolve_installation_name requires one argument." unless $name;
82✔
2678

2679
    my ( $perl_name, $lib_name ) = $self->decompose_locallib($name);
81✔
2680
    $perl_name = $name unless $lib_name;
81✔
2681
    $perl_name ||= $self->current_perl;
81✔
2682

2683
    if ( !$self->is_installed($perl_name) ) {
81✔
2684
        if ( $self->is_installed("perl-${perl_name}") ) {
6✔
2685
            $perl_name = "perl-${perl_name}";
3✔
2686
        }
2687
        else {
2688
            return undef;
3✔
2689
        }
2690
    }
2691

2692
    return wantarray ? ( $perl_name, $lib_name ) : $perl_name;
78✔
2693
}
2694

2695
# Implementation of the 'clone-modules' command.
2696
#
2697
# This method accepts a destination and source installation
2698
# of Perl to clone modules from and into.
2699
# For instance calling
2700
# $app->run_command_clone_modules($perl_a, $perl_b);
2701
# installs all modules that have been installed on Perl A
2702
# to the instance of Perl B.
2703
# The source instance is optional, that is if the method
2704
# is invoked with a single argument, the currently
2705
# running instance is used as source. Therefore the
2706
# two following calls are the same:
2707
#
2708
# $app->run_command_clone_modules( $self->current_perl, $perl_b );
2709
# $app->run_command_clone_modules( $perl_b );
2710
#
2711
# Of course, both Perl installation must exist on this
2712
# perlbrew enviroment.
2713
#
2714
# The method extracts the modules installed on the source Perl
2715
# instance and put them on a temporary file, such file is then
2716
# passed to another instance of the application to
2717
# execute cpanm on it. The final result is the installation
2718
# of source modules into the destination instance.
2719
sub run_command_clone_modules {
2720
    my $self = shift;
4✔
2721

2722
    # default to use the currently installation
2723
    my ( $dst_perl, $src_perl );
4✔
2724

2725
    # the first argument is the destination, the second
2726
    # optional argument is the source version, default
2727
    # to use the current installation
2728
    $dst_perl = pop || $self->current_env;
4✔
2729
    $src_perl = pop || $self->current_env;
4✔
2730

2731
    # check source and destination do exist
2732
    undef $src_perl if ( !$self->resolve_installation_name($src_perl) );
4✔
2733
    undef $dst_perl if ( !$self->resolve_installation_name($dst_perl) );
4✔
2734

2735
    if (   !$src_perl
4✔
2736
        || !$dst_perl
2737
        || $src_perl eq $dst_perl )
2738
    {
2739
        # cannot understand from where to where or
2740
        # the user did specify the same versions
2741
        $self->run_command_help('clone-modules');
×
2742
        exit(-1);
×
2743
    }
2744

2745
    my @modules_to_install = @{ $self->list_modules($src_perl) };
4✔
2746

2747
    unless (@modules_to_install) {
4✔
2748
        print "\nNo modules installed on $src_perl !\n" unless $self->{quiet};
×
2749
        return;
×
2750
    }
2751

2752
    print "\nInstalling $#modules_to_install modules from $src_perl to $dst_perl ...\n"
2753
        unless $self->{quiet};
4✔
2754

2755
    # create a new application to 'exec' the 'cpanm'
2756
    # with the specified module list
2757

2758
    my @args = ( qw(--quiet exec --with), $dst_perl, 'cpanm' );
4✔
2759
    push @args, '--notest' if $self->{notest};
4✔
2760
    push @args, @modules_to_install;
4✔
2761

2762
    __PACKAGE__->new(@args)->run;
4✔
2763
}
2764

2765
sub format_info_output {
2766
    my ( $self, $module ) = @_;
4✔
2767

2768
    my $out = '';
4✔
2769

2770
    $out .= "Current perl:\n";
4✔
2771
    if ( $self->current_perl ) {
4✔
2772
        $out .= "  Name: " . $self->current_env . "\n";
3✔
2773
        $out .= "  Path: " . $self->installed_perl_executable( $self->current_perl ) . "\n";
3✔
2774
        $out .= "  Config: " . $self->configure_args( $self->current_perl ) . "\n";
3✔
2775
        $out .= join(
2776
            '',
2777
            "  Compiled at: ",
2778
            (
2779
                map { /  Compiled at (.+)\n/ ? $1 : () }
3✔
2780
                    `@{[ $self->installed_perl_executable($self->current_perl) ]} -V`
2781
            ),
2782
            "\n"
2783
        );
2784
    }
2785
    else {
2786
        $out .= "Using system perl." . "\n";
1✔
2787
        $out .= "Shebang: " . $self->system_perl_shebang . "\n";
1✔
2788
    }
2789

2790
    $out .= "\nperlbrew:\n";
4✔
2791
    $out .= "  version: " . $self->VERSION . "\n";
4✔
2792
    $out .= "  ENV:\n";
4✔
2793
    for ( map { "PERLBREW_$_" } qw(ROOT HOME PATH MANPATH) ) {
4✔
2794
        $out .= "    $_: " . ( $self->env($_) || "" ) . "\n";
16✔
2795
    }
2796

2797
    if ($module) {
4✔
2798
        my $code =
2✔
2799
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?" } };
2800
        $out .=
2✔
2801
            "\nModule: " . $self->do_capture( $self->installed_perl_executable( $self->current_perl ), "-le", $code );
2802
    }
2803

2804
    $out;
4✔
2805
}
2806

2807
sub run_command_info {
2808
    my ($self) = shift;
4✔
2809
    print $self->format_info_output(@_);
4✔
2810
}
2811

2812
sub run_command_make_shim {
2813
    my ($self, $program) = @_;
3✔
2814

2815
    if (-f $program) {
3✔
2816
        die "ERROR: $program already exists under current directory.\n";
1✔
2817
    }
2818

2819
    my $current_env = $self->current_env
2✔
2820
        or die "ERROR: perlbrew is current off. make-shim requires an perlbrew environment to be activated.\nRead the usage by running: perlbrew help make-shim\n";
2821

2822
    my %env = $self->perlbrew_env( $current_env );
1✔
2823

2824
    my $shebang = '#!' . $self->env('SHELL');
1✔
2825
    my $preemble = $self->shell_env(\%env);
1✔
2826
    my $path = $self->shell_env({ PATH => $env{"PERLBREW_PATH"} . ":" . $self->env("PATH") });
1✔
2827
    my $shim = join(
1✔
2828
        "\n",
2829
        $shebang,
2830
        $preemble,
2831
        $path,
2832
        'exec ' . $program . ' "$@"',
2833
        "\n"
2834
    );
2835

2836
    open my $fh, ">", "$program" or die $!;
1✔
2837
    print $fh $shim;
1✔
2838
    close $fh;
1✔
2839
    chmod 0755, $program;
1✔
2840

2841
    if ( $self->{verbose} ) {
1✔
2842
        print "A shim of $program is made.\n";
×
2843
    }
2844
}
2845

2846
sub BASHRC_CONTENT() {
2847
    return
2848
          "export PERLBREW_SHELLRC_VERSION=$VERSION\n"
2849
        . ( exists $ENV{PERLBREW_ROOT} ? "export PERLBREW_ROOT=$PERLBREW_ROOT\n" : "" ) . "\n"
10✔
2850
        . <<'RC';
2851

2852
__perlbrew_reinit() {
2853
    if [[ ! -d "$PERLBREW_HOME" ]]; then
2854
        mkdir -p "$PERLBREW_HOME"
2855
    fi
2856

2857
    [ -f "$PERLBREW_HOME/init" ] && rm "$PERLBREW_HOME/init"
2858
    echo '# DO NOT EDIT THIS FILE' > "$PERLBREW_HOME/init"
2859
    command perlbrew env $1 | \grep PERLBREW_ >> "$PERLBREW_HOME/init"
2860
    . "$PERLBREW_HOME/init"
2861
    __perlbrew_set_path
2862
}
2863

2864
__perlbrew_purify () {
2865
    local path patharray outsep
2866
    IFS=: read -r${BASH_VERSION+a}${ZSH_VERSION+A} patharray <<< "$1"
2867
    for path in "${patharray[@]}" ; do
2868
        case "$path" in
2869
            (*"$PERLBREW_HOME"*) ;;
2870
            (*"$PERLBREW_ROOT"*) ;;
2871
            (*) printf '%s' "$outsep$path" ; outsep=: ;;
2872
        esac
2873
    done
2874
}
2875

2876
__perlbrew_set_path () {
2877
    export MANPATH=${PERLBREW_MANPATH:-}${PERLBREW_MANPATH:+:}$(__perlbrew_purify "$(manpath 2>/dev/null)")
2878
    export PATH=${PERLBREW_PATH:-$PERLBREW_ROOT/bin}:$(__perlbrew_purify "$PATH")
2879
    hash -r
2880
}
2881

2882
__perlbrew_set_env() {
2883
    local code
2884
    code="$($perlbrew_command env $@)" || return $?
2885
    eval "$code"
2886
}
2887

2888
__perlbrew_activate() {
2889
    [[ -n $(alias perl 2>/dev/null) ]] && unalias perl 2>/dev/null
2890

2891
    if [[ -n "${PERLBREW_PERL:-}" ]]; then
2892
          __perlbrew_set_env "${PERLBREW_PERL:-}${PERLBREW_LIB:+@}$PERLBREW_LIB"
2893
    fi
2894

2895
    __perlbrew_set_path
2896
}
2897

2898
__perlbrew_deactivate() {
2899
    __perlbrew_set_env
2900
    unset PERLBREW_PERL
2901
    unset PERLBREW_LIB
2902
    __perlbrew_set_path
2903
}
2904

2905
perlbrew () {
2906
    local exit_status
2907
    local short_option
2908
    export SHELL
2909

2910
    if [[ $1 == -* ]]; then
2911
        short_option=$1
2912
        shift
2913
    else
2914
        short_option=""
2915
    fi
2916

2917
    case $1 in
2918
        (use)
2919
            if [[ -z "$2" ]] ; then
2920
                echo -n "Currently using ${PERLBREW_PERL:-system perl}"
2921
                [ -n "$PERLBREW_LIB" ] && echo -n "@$PERLBREW_LIB"
2922
                echo
2923
            else
2924
                __perlbrew_set_env "$2" && { __perlbrew_set_path ; true ; }
2925
                exit_status="$?"
2926
            fi
2927
            ;;
2928

2929
        (switch)
2930
              if [[ -z "$2" ]] ; then
2931
                  command perlbrew switch
2932
              else
2933
                  perlbrew use $2 && { __perlbrew_reinit $2 ; true ; }
2934
                  exit_status=$?
2935
              fi
2936
              ;;
2937

2938
        (off)
2939
            __perlbrew_deactivate
2940
            echo "perlbrew is turned off."
2941
            ;;
2942

2943
        (switch-off)
2944
            __perlbrew_deactivate
2945
            __perlbrew_reinit
2946
            echo "perlbrew is switched off."
2947
            ;;
2948

2949
        (*)
2950
            command perlbrew $short_option "$@"
2951
            exit_status=$?
2952
            ;;
2953
    esac
2954
    hash -r
2955
    return ${exit_status:-0}
2956
}
2957

2958
[[ -z "${PERLBREW_ROOT:-}" ]] && export PERLBREW_ROOT="$HOME/perl5/perlbrew"
2959
[[ -z "${PERLBREW_HOME:-}" ]] && export PERLBREW_HOME="$HOME/.perlbrew"
2960

2961
if [[ ! -n "${PERLBREW_SKIP_INIT:-}" ]]; then
2962
    if [[ -f "${PERLBREW_HOME:-}/init" ]]; then
2963
        . "$PERLBREW_HOME/init"
2964
    fi
2965
fi
2966

2967
if [[ -f "${PERLBREW_ROOT:-}/bin/perlbrew" ]]; then
2968
    perlbrew_command="${PERLBREW_ROOT:-}/bin/perlbrew"
2969
else
2970
    perlbrew_command="perlbrew"
2971
fi
2972

2973
__perlbrew_activate
2974

2975
RC
2976

2977
}
2978

2979
sub BASH_COMPLETION_CONTENT() {
2980
    return <<'COMPLETION';
5✔
2981
if [[ -n ${ZSH_VERSION-} ]]; then
2982
    autoload -U +X bashcompinit && bashcompinit
2983
fi
2984

2985
export PERLBREW="command perlbrew"
2986
_perlbrew_compgen()
2987
{
2988
    COMPREPLY=( $($PERLBREW compgen $COMP_CWORD ${COMP_WORDS[*]}) )
2989
}
2990
complete -F _perlbrew_compgen perlbrew
2991
COMPLETION
2992
}
2993

2994
sub PERLBREW_FISH_CONTENT {
2995
    return "set -x PERLBREW_SHELLRC_VERSION $VERSION\n" . <<'END';
5✔
2996

2997
function __perlbrew_reinit
2998
    if not test -d "$PERLBREW_HOME"
2999
        mkdir -p "$PERLBREW_HOME"
3000
    end
3001

3002
    echo '# DO NOT EDIT THIS FILE' > "$PERLBREW_HOME/init"
3003
    command perlbrew env $argv[1] | \grep PERLBREW_ >> "$PERLBREW_HOME/init"
3004
    __source_init
3005
    __perlbrew_set_path
3006
end
3007

3008
function __perlbrew_set_path
3009
    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);')
3010

3011
    if test -n "$PERLBREW_MANPATH"
3012
        set -l PERLBREW_MANPATH $PERLBREW_MANPATH":"
3013
        set -x MANPATH {$PERLBREW_MANPATH}{$MANPATH_WITHOUT_PERLBREW}
3014
    else
3015
        set -x MANPATH $MANPATH_WITHOUT_PERLBREW
3016
    end
3017

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

3020
    # silencing stderr in case there's a non-existent path in $PATH (see GH#446)
3021
    if test -n "$PERLBREW_PATH"
3022
        set -x PERLBREW_PATH (echo $PERLBREW_PATH | perl -pe 'y/:/ /' )
3023
        eval set -x PATH $PERLBREW_PATH $PATH_WITHOUT_PERLBREW 2> /dev/null
3024
    else
3025
        eval set -x PATH $PERLBREW_ROOT/bin $PATH_WITHOUT_PERLBREW 2> /dev/null
3026
    end
3027
end
3028

3029
function __perlbrew_set_env
3030
    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/:/ /')
3031

3032
    if test -z "$code"
3033
        return 0;
3034
    else
3035
        eval $code
3036
    end
3037
end
3038

3039
function __perlbrew_activate
3040
    functions -e perl
3041

3042
    if test -n "$PERLBREW_PERL"
3043
        if test -z "$PERLBREW_LIB"
3044
            __perlbrew_set_env $PERLBREW_PERL
3045
        else
3046
            __perlbrew_set_env $PERLBREW_PERL@$PERLBREW_LIB
3047
        end
3048
    end
3049

3050
    __perlbrew_set_path
3051
end
3052

3053
function __perlbrew_deactivate
3054
    __perlbrew_set_env
3055
    set -x PERLBREW_PERL
3056
    set -x PERLBREW_LIB
3057
    set -x PERLBREW_PATH
3058
    __perlbrew_set_path
3059
end
3060

3061
function perlbrew
3062

3063
    test -z "$argv"
3064
    and echo "    Usage: perlbrew <command> [options] [arguments]"
3065
    and echo "       or: perlbrew help"
3066
    and return 1
3067

3068
    switch $argv[1]
3069
        case use
3070
            if test ( count $argv ) -eq 1
3071
                if test -z "$PERLBREW_PERL"
3072
                    echo "Currently using system perl"
3073
                else
3074
                    echo "Currently using $PERLBREW_PERL"
3075
                end
3076
            else
3077
                __perlbrew_set_env $argv[2]
3078
                if test "$status" -eq 0
3079
                    __perlbrew_set_path
3080
                end
3081
            end
3082

3083
        case switch
3084
            if test ( count $argv ) -eq 1
3085
                command perlbrew switch
3086
            else
3087
                perlbrew use $argv[2]
3088
                if test "$status" -eq 0
3089
                    __perlbrew_reinit $argv[2]
3090
                end
3091
            end
3092

3093
        case off
3094
            __perlbrew_deactivate
3095
            echo "perlbrew is turned off."
3096

3097
        case switch-off
3098
            __perlbrew_deactivate
3099
            __perlbrew_reinit
3100
            echo "perlbrew is switched off."
3101

3102
        case '*'
3103
            command perlbrew $argv
3104
    end
3105
end
3106

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

3111
if test -z "$PERLBREW_ROOT"
3112
    set -x PERLBREW_ROOT "$HOME/perl5/perlbrew"
3113
end
3114

3115
if test -z "$PERLBREW_HOME"
3116
    set -x PERLBREW_HOME "$HOME/.perlbrew"
3117
end
3118

3119
if test -z "$PERLBREW_SKIP_INIT" -a -f "$PERLBREW_HOME/init"
3120
    __source_init
3121
end
3122

3123
set perlbrew_bin_path "$PERLBREW_ROOT/bin"
3124

3125
if test -f "$perlbrew_bin_path/perlbrew"
3126
    set perlbrew_command "$perlbrew_bin_path/perlbrew"
3127
else
3128
    set perlbrew_command perlbrew
3129
end
3130

3131
set -e perlbrew_bin_path
3132

3133
__perlbrew_activate
3134

3135
## autocomplete stuff #############################################
3136

3137
function __fish_perlbrew_needs_command
3138
  set cmd (commandline -opc)
3139
  if test (count $cmd) -eq 1 -a $cmd[1] = 'perlbrew'
3140
    return 0
3141
  end
3142
  return 1
3143
end
3144

3145
function __fish_perlbrew_using_command
3146
  set cmd (commandline -opc)
3147
  if test (count $cmd) -gt 1
3148
    if [ $argv[1] = $cmd[2] ]
3149
      return 0
3150
    end
3151
  end
3152
end
3153

3154
for com in (perlbrew help | perl -ne'print lc if s/^COMMAND:\s+//')
3155
    complete -f -c perlbrew -n '__fish_perlbrew_needs_command' -a $com
3156
end
3157

3158
for com in switch use;
3159
    complete -f -c perlbrew -n "__fish_perlbrew_using_command $com" \
3160
        -a '(perlbrew list | perl -pe\'s/\*?\s*(\S+).*/$1/\')'
3161
end
3162

3163
END
3164
}
3165

3166
sub CSH_WRAPPER_CONTENT {
3167
    return <<'WRAPPER';
5✔
3168
set perlbrew_exit_status=0
3169

3170
if ( "$1" =~ -* ) then
3171
    set perlbrew_short_option="$1"
3172
    shift
3173
else
3174
    set perlbrew_short_option=""
3175
endif
3176

3177
switch ( "$1" )
3178
    case use:
3179
        if ( $%2 == 0 ) then
3180
            if ( $?PERLBREW_PERL == 0 ) then
3181
                echo "Currently using system perl"
3182
            else
3183
                if ( $%PERLBREW_PERL == 0 ) then
3184
                    echo "Currently using system perl"
3185
                else
3186
                    echo "Currently using $PERLBREW_PERL"
3187
                endif
3188
            endif
3189
        else
3190
            set perlbrew_line_count=0
3191
            foreach perlbrew_line ( "`\perlbrew env $2:q`" )
3192
                eval "$perlbrew_line"
3193
                @ perlbrew_line_count++
3194
            end
3195
            if ( $perlbrew_line_count == 0 ) then
3196
                set perlbrew_exit_status=1
3197
            else
3198
                source "$PERLBREW_ROOT/etc/csh_set_path"
3199
            endif
3200
        endif
3201
        breaksw
3202

3203
    case switch:
3204
        if ( $%2 == 0 ) then
3205
            \perlbrew switch
3206
        else
3207
            perlbrew use "$2" && source "$PERLBREW_ROOT/etc/csh_reinit" "$2"
3208
        endif
3209
        breaksw
3210

3211
    case off:
3212
        unsetenv PERLBREW_PERL
3213
        foreach perlbrew_line ( "`\perlbrew env`" )
3214
            eval "$perlbrew_line"
3215
        end
3216
        source "$PERLBREW_ROOT/etc/csh_set_path"
3217
        echo "perlbrew is turned off."
3218
        breaksw
3219

3220
    case switch-off:
3221
        unsetenv PERLBREW_PERL
3222
        source "$PERLBREW_ROOT/etc/csh_reinit" ''
3223
        echo "perlbrew is switched off."
3224
        breaksw
3225

3226
    default:
3227
        \perlbrew $perlbrew_short_option:q $argv:q
3228
        set perlbrew_exit_status=$?
3229
        breaksw
3230
endsw
3231
rehash
3232
exit $perlbrew_exit_status
3233
WRAPPER
3234
}
3235

3236
sub CSH_REINIT_CONTENT {
3237
    return <<'REINIT';
5✔
3238
if ( ! -d "$PERLBREW_HOME" ) then
3239
    mkdir -p "$PERLBREW_HOME"
3240
endif
3241

3242
echo '# DO NOT EDIT THIS FILE' >! "$PERLBREW_HOME/init"
3243
\perlbrew env $1 >> "$PERLBREW_HOME/init"
3244
source "$PERLBREW_HOME/init"
3245
source "$PERLBREW_ROOT/etc/csh_set_path"
3246
REINIT
3247
}
3248

3249
sub CSH_SET_PATH_CONTENT {
3250
    return <<'SETPATH';
5✔
3251
unalias perl
3252

3253
if ( $?PERLBREW_PATH == 0 ) then
3254
    setenv PERLBREW_PATH "$PERLBREW_ROOT/bin"
3255
endif
3256

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

3260
setenv MANPATH_WITHOUT_PERLBREW `perl -e 'print join ":", grep { index($_, $ENV{PERLBREW_ROOT}) } split/:/,qx(manpath 2> /dev/null);'`
3261
if ( $?PERLBREW_MANPATH == 1 ) then
3262
    setenv MANPATH "${PERLBREW_MANPATH}:${MANPATH_WITHOUT_PERLBREW}"
3263
else
3264
    setenv MANPATH "${MANPATH_WITHOUT_PERLBREW}"
3265
endif
3266
SETPATH
3267
}
3268

3269
sub CSHRC_CONTENT {
3270
    return "setenv PERLBREW_SHELLRC_VERSION $VERSION\n\n" . <<'CSHRC';
5✔
3271

3272
if ( $?PERLBREW_HOME == 0 ) then
3273
    setenv PERLBREW_HOME "$HOME/.perlbrew"
3274
endif
3275

3276
if ( $?PERLBREW_ROOT == 0 ) then
3277
    setenv PERLBREW_ROOT "$HOME/perl5/perlbrew"
3278
endif
3279

3280
if ( $?PERLBREW_SKIP_INIT == 0 ) then
3281
    if ( -f "$PERLBREW_HOME/init" ) then
3282
        source "$PERLBREW_HOME/init"
3283
    endif
3284
endif
3285

3286
if ( $?PERLBREW_PATH == 0 ) then
3287
    setenv PERLBREW_PATH "$PERLBREW_ROOT/bin"
3288
endif
3289

3290
source "$PERLBREW_ROOT/etc/csh_set_path"
3291
alias perlbrew 'source "$PERLBREW_ROOT/etc/csh_wrapper"'
3292
CSHRC
3293

3294
}
3295

3296
sub append_log {
3297
    my ( $self, $message ) = @_;
4✔
3298
    my $log_handler;
4✔
3299
    open( $log_handler, '>>', $self->{log_file} )
3300
        or die "Cannot open log file for appending: $!";
4✔
3301
    print $log_handler "$message\n";
4✔
3302
    close($log_handler);
4✔
3303
}
3304

3305
sub INSTALLATION_FAILURE_MESSAGE {
3306
    my ($self) = @_;
1✔
3307
    return <<FAIL;
1✔
3308
Installation process failed. To spot any issues, check
3309

3310
  $self->{log_file}
3311

3312
If some perl tests failed and you still want to install this distribution anyway,
3313
do:
3314

3315
  (cd $self->{dist_extracted_dir}; make install)
3316

3317
You might also want to try upgrading patchperl before trying again:
3318

3319
  perlbrew install-patchperl
3320

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

3324
  perlbrew --notest install $self->{dist_name}
3325
  perlbrew --force install $self->{dist_name}
3326

3327
FAIL
3328

3329
}
3330

3331
1;
3332

3333
__END__
3334

3335
=encoding utf8
3336

3337
=head1 NAME
3338

3339
App::perlbrew - Manage perl installations in your C<$HOME>
3340

3341
=head1 SYNOPSIS
3342

3343
    # Installation
3344
    curl -L https://install.perlbrew.pl | bash
3345

3346
    # Initialize
3347
    perlbrew init
3348

3349
    # See what is available
3350
    perlbrew available
3351

3352
    # Install some Perls
3353
    perlbrew install 5.32.1
3354
    perlbrew install perl-5.28.3
3355
    perlbrew install perl-5.33.6
3356

3357
    # See what were installed
3358
    perlbrew list
3359

3360
    # Swith to an installation and set it as default
3361
    perlbrew switch perl-5.32.1
3362

3363
    # Temporarily use another version only in current shell.
3364
    perlbrew use perl-5.28.3
3365
    perl -v
3366

3367
    # Turn it off and go back to the system perl.
3368
    perlbrew off
3369

3370
    # Turn it back on with 'switch', or 'use'
3371
    perlbrew switch perl-5.32.1
3372
    perlbrew use perl-5.32.1
3373

3374
    # Exec something with all perlbrew-ed perls
3375
    perlbrew exec -- perl -E 'say $]'
3376

3377
=head1 DESCRIPTION
3378

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

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

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

3394
=head1 INSTALLATION
3395

3396
It is the simplest to use the perlbrew installer, just paste this statement to
3397
your terminal:
3398

3399
    curl -L https://install.perlbrew.pl | bash
3400

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

3403
    fetch -o- https://install.perlbrew.pl | sh
3404

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

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

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

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

3422
    export PERLBREW_ROOT=/opt/perl5
3423
    curl -L https://install.perlbrew.pl | bash
3424

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

3429
You may also install perlbrew from CPAN:
3430

3431
    cpan App::perlbrew
3432

3433
In this case, the perlbrew command is installed as C</usr/bin/perlbrew> or
3434
C</usr/local/bin/perlbrew> or others, depending on the location of your system
3435
perl installation.
3436

3437
Please make sure not to run this with one of the perls brewed with
3438
perlbrew. It's the best to turn perlbrew off before you run that, if you're
3439
upgrading.
3440

3441
    perlbrew off
3442
    cpan App::perlbrew
3443

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

3448
The C<self-upgrade> command will not upgrade the perlbrew installed by cpan
3449
command, but it is also easy to upgrade perlbrew by running C<cpan App::perlbrew>
3450
again.
3451

3452
=head1 PROJECT DEVELOPMENT
3453

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

3459
Please briefly read the short instructions about how to get your work
3460
released to CPAN:
3461

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

3464
=head1 AUTHOR
3465

3466
Kang-min Liu  C<< <gugod@gugod.org> >>
3467

3468
=head1 COPYRIGHT
3469

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

3472
=head1 LICENCE
3473

3474
The MIT License
3475

3476
=head1 DISCLAIMER OF WARRANTY
3477

3478
BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
3479
FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
3480
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
3481
PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
3482
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
3483
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
3484
ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
3485
YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
3486
NECESSARY SERVICING, REPAIR, OR CORRECTION.
3487

3488
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
3489
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
3490
REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
3491
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
3492
OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
3493
THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
3494
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
3495
FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
3496
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
3497
SUCH DAMAGES.
3498

3499
=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