App-perlbrew

 view release on metacpan or  search on metacpan

lib/App/perlbrew.pm  view on Meta::CPAN

package App::perlbrew;
use strict;
use warnings;
use 5.008;
our $VERSION = "1.02";
use Config qw( %Config );

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

    @INC = (
        $Config{sitelibexp} . "/" . $Config{archname},
        $Config{sitelibexp}, @Config{qw<vendorlibexp vendorarchexp archlibexp privlibexp>},
    );

    require Cwd;
    @INC = @oldinc;
}

use Getopt::Long ();
use CPAN::Perl::Releases ();
use JSON::PP qw( decode_json );
use File::Copy qw( copy move );
use Capture::Tiny ();

use App::Perlbrew::Util qw( files_are_the_same uniq find_similar_tokens looks_like_url_of_skaji_relocatable_perl looks_like_sys_would_be_compatible_with_skaji_relocatable_perl make_skaji_relocatable_perl_url );
use App::Perlbrew::Path ();
use App::Perlbrew::Path::Root ();
use App::Perlbrew::HTTP qw( http_download http_get );
use App::Perlbrew::Patchperl qw( maybe_patchperl );
use App::Perlbrew::Sys;

### global variables

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

local $SIG{__DIE__} = sub {
    my $message = shift;
    warn $message;
    exit(1);
};

our $CONFIG;
our $PERLBREW_ROOT;
our $PERLBREW_HOME;

my @flavors = (
    {
        d_option => 'usethreads',
        implies  => 'multi',
        common   => 1,
        opt      => 'thread|threads'
    },    # threads is for backward compatibility

    {
        d_option => 'usemultiplicity',
        opt      => 'multi'
    },

    {
        d_option => 'uselongdouble',
        common   => 1,
        opt      => 'ld'
    },

    {
        d_option => 'use64bitint',
        common   => 1,
        opt      => '64int'
    },

    {
        d_option => 'use64bitall',
        implies  => '64int',
        opt      => '64all'
    },

    {
        d_option => 'DEBUGGING',
        opt      => 'debug'
    },

    {

lib/App/perlbrew.pm  view on Meta::CPAN


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

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

    my ( $latest_ver, $latest_minor );
    for my $cand ( $self->available_perls ) {
        my ( $ver, $minor ) = $cand =~ m/^perl-(5\.(6|8|[0-9]+[02468])\.[0-9]+)$/
            or next;
        ( $latest_ver, $latest_minor ) = ( $ver, $minor )
            if !defined $latest_minor
            || $latest_minor < $minor;
    }

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

    return $latest_ver;
}

sub do_install_release {
    my ( $self, $dist, $dist_version ) = @_;

    my $rd        = $self->release_detail($dist);
    my $dist_type = $rd->{type};

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

    my $dist_tarball      = $rd->{tarball_name};
    my $dist_tarball_url  = $rd->{tarball_url};
    my $dist_tarball_path = $self->root->dists($dist_tarball);

    if ( -f $dist_tarball_path ) {
        print "Using the previously fetched ${dist_tarball}\n"
            if $self->{verbose};
    }
    else {
        print "Fetching perl $dist_version as $dist_tarball_path\n" unless $self->{quiet};
        $self->run_command_download($dist);
    }

    my $dist_extracted_path = $self->do_extract_tarball($dist_tarball_path);
    $self->do_install_this( $dist_extracted_path, $dist_version, $dist );
    return;
}

sub run_command_install {
    my ( $self, $dist, $opts ) = @_;

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

    unless ($dist) {
        $self->run_command_help("install");
        exit(-1);
    }

    if ( my $url = make_skaji_relocatable_perl_url($dist, $self->sys) ) {
        return $self->run_command_install($url);
    }

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

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

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

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

        if ( $dist_type eq 'perl' && $dist_version eq 'blead' ) {
            $self->do_install_blead();
        }
        else {
            $self->do_install_release( $dist, $dist_version );
        }

    }

    # else it is some kind of special install:
    elsif ( -d "$dist/.git" ) {
        $self->do_install_git($dist);
    }
    elsif ( -f $dist ) {
        $self->do_install_archive( App::Perlbrew::Path->new($dist) );
    }
    elsif ( $dist =~ m/^(?:https?|ftp|file)/ ) {    # more protocols needed?
        $self->do_install_url($dist);
    }
    else {
        die "Unknown installation target \"$dist\", abort.\nPlease see `perlbrew help` "
            . "for the instruction on using the install command.\n\n";
    }

    if ( $self->{switch} ) {
        if ( defined( my $installation_name = $self->{installation_name} ) ) {
            $self->switch_to($installation_name);
        }
        else {
            warn "can't switch, unable to infer final destination name.\n\n";
        }
    }
    return;
}

sub check_and_calculate_variations {
    my $self = shift;
    my @both = @{ $self->{both} };

    if ( $self->{'all-variations'} ) {
        @both = keys %flavor;

lib/App/perlbrew.pm  view on Meta::CPAN

        if $test_target eq "test_harness" && ( $self->{j} || 1 ) > 1;

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

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

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

    $self->{log_file}->unlink;

    if ( $self->{verbose} ) {
        $cmd = "($cmd) 2>&1 | tee $self->{log_file}";
        print "$cmd\n" if $self->{verbose};
    }
    else {
        $cmd = "($cmd) >> '$self->{log_file}' 2>&1 ";
    }

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

    if ( $self->do_system($cmd) ) {
        my $newperl = $self->root->perls($installation_name)->perl;
        unless ( -e $newperl ) {
            $self->run_command_symlink_executables($installation_name);
        }

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

        if ($sitecustomize) {
            my $capture = $self->do_capture("$newperl -V:sitelib");
            my ($sitelib) = $capture =~ m/sitelib='([^']*)';/;
            $sitelib = $destdir . $sitelib if $destdir;
            $sitelib = App::Perlbrew::Path->new($sitelib);
            $sitelib->mkpath;
            my $target = $sitelib->child("sitecustomize.pl");
            open my $dst, ">", $target
                or die "Could not open '$target' for writing: $!\n";
            open my $src, "<", $sitecustomize
                or die "Could not open '$sitecustomize' for reading: $!\n";
            print {$dst} do { local $/; <$src> };
        }

        my $version_file = $self->root->perls($installation_name)->version_file;

        if ( -e $version_file ) {
            $version_file->unlink()
                or die "Could not unlink $version_file file: $!\n";
        }

        print "$installation_name is successfully installed.\n";
    }
    else {
        eval { $self->append_log('##### Brew Failed #####') };
        die $self->INSTALLATION_FAILURE_MESSAGE;
    }
    return;
}

sub do_install_skaji_relocatable_perl {
    my ($self, $detail) = @_;

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

    die "ERROR: Installation target \"${installation_name}\" already exists\n"
        if $installation_path->exists;

    my $path = $self->root->dists
        ->child("skaji-relocatable-perl")
        ->child($detail->{version})
        ->mkpath()
        ->child($detail->{original_filename});

    if (-f $path) {
        print "Re-using the downloaded $path\n";
    } else {
        my $url = $detail->{url};
        print "Downloading $url as $path\n";
        my $error = http_download( $detail->{url}, $path );
        if ($error) {
            die "Failed to download from $url\nError: $error";
        }
    }

    my $extracted_path = $self->do_extract_skaji_relocatable_perl_tarball($detail, $path);

    move $extracted_path, $installation_path;

    print "$installation_name is installed at $installation_path.\n";

    print "$installation_name is successfully installed.\n";
}

sub do_extract_skaji_relocatable_perl_tarball {
    my ($self, $detail, $tarball_path) = @_;

    my $workdir = $self->builddir
        ->child("skaji-relocatable-perl")
        ->child($detail->{version});

    $workdir->rmpath()
        if $workdir->exists();

    $workdir->mkpath();

    my $tarx = "tar xzf";
    my $extract_command = "cd $workdir; $tarx $tarball_path";

    system($extract_command) == 0
        or die "Failed to extract $tarball_path";

    my ($extracted_path) = $workdir->children;

    return $extracted_path;
}

sub do_install_program_from_url {
    my ( $self, $url, $program_name, $body_filter ) = @_;

    my $out = $self->root->bin($program_name);

    if ( -f $out && !$self->{force} && !$self->{yes} ) {
        require ExtUtils::MakeMaker;

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

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

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

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

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

        die $message;
    }

    if ( $body_filter && ref($body_filter) eq "CODE" ) {
        $body = $body_filter->($body);
    }

    $self->root->bin->mkpath;
    open my $OUT, '>', $out or die "cannot open file($out): $!";
    print $OUT $body;
    close $OUT;



( run in 0.976 second using v1.01-cache-2.11-cpan-71847e10f99 )