App-plx

 view release on metacpan or  search on metacpan

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

#!perl

package App::plx;

our $VERSION = '0.902003'; # 0.902.3

$VERSION = eval $VERSION;

use strict;
use warnings;
use File::Spec;
use File::Basename ();
use Cwd ();
use lib ();
use Config;
use File::Which ();
use List::Util ();
use local::lib ();

BEGIN {
  our %orig_env = %ENV;
  { local $0 = $0 eq '-' ? 'plx' : $0;
    local::lib->import('--deactivate-all') }
  delete @ENV{grep /^PERL/, keys %ENV}
}
no lib @Config{qw(sitearch sitelibexp)};

my $fs = 'File::Spec';

my $self = do {
  package Perl::Layout::Executor::_self;
  sub self { package DB; () = caller(2); $DB::args[0] }
  use overload '%{}' => sub { self }, fallback => 1;
  sub AUTOLOAD {
    my ($meth) = (our $AUTOLOAD =~ /([^:]+)$/);
    self->$meth(@_[1..$#_]);
  }
  sub DESTROY {}
  bless([], __PACKAGE__);
};

sub barf { die "$_[0]\n" }

sub stderr { warn "$_[0]\n" }

sub say { print "$_[0]\n" }

sub new {
  my $class = shift;
  bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class;
}

sub layout_base_dir {
  $self->{layout_base_dir} //= $self->_build_layout_base_dir
}
sub layout_perl {
  $self->{layout_perl} //= $self->_build_layout_perl
}

sub _build_layout_base_dir {
  my @parts = $fs->splitdir(Cwd::realpath(Cwd::getcwd()));
  my $cand;
  my $reason = '';
  while (@parts > 1) { # go back to one step before root at most
    $cand = $fs->catdir(@parts);
    return $cand if -d $fs->catdir($cand, '.plx');
    if (-d $fs->catdir($cand, '.git')) { # don't escape current repository
      $reason = ' due to .git directory';
      last;
    }
    pop @parts;
  }
  barf "Couldn't find .plx directory (stopped searching at ${cand}${reason})";
}

sub _build_layout_perl {
  my $perl_bin = $self->read_config_entry('perl');
  unless ($perl_bin) {
    my $perl_spec = $self->read_config_entry('perl.spec');
    barf "No perl and no perl.spec in config" unless $perl_spec;
    $self->run_config_perl_set($perl_spec);
    $perl_bin = $self->read_config_entry('perl');
    barf "Rehydration of perl from perl.spec failed" unless $perl_bin;
  }
  barf "perl binary ${perl_bin} not executable" unless -x $perl_bin;
  return $perl_bin;
}

sub layout_libspec_config {
  [ grep $_->[1],
      map [ $_, $self->read_config_entry([ libspec => $_ ]) ],
        $self->list_config_names('libspec') ];

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

}

sub _parse_multi {
  my ($self, @args) = @_;
  my @multi;
  MULTI: while (@args) {
    barf "Expected multi arg [, got: $args[0]" unless $args[0] eq '[';
    shift @args;
    my @action;
    while (my $el = shift @args) {
      push @multi, \@action and next MULTI if $el eq ']';
      push @action, $el;
    }
    barf "Missing closing ] for multi";
  }
  return @multi;
}

sub run_action_multi {
  my ($self, @args) = @_;
  return $self->run_multi(@args) if @args and ref($args[0]);
  my @multi = $self->_parse_multi(@args);
  $self->run_multi(@multi);
}

sub run_multi {
  my ($self, @multi) = @_;
  foreach my $multi (@multi) {
    my @debug_multi = map +(ref($_) ? ('[', @$_, ']') : $_), @$multi;
    stderr '# '.join(' ', plx => @debug_multi);
    $self->run(@$multi);
  }
}

sub run_action_showmulti {
  my ($self, @args) = @_;
  my @multi = $self->_parse_multi(@args);
  say join(' ', plx => @$_) for @multi;
}

sub run {
  my ($self, $cmd, @args) = @_;
  $cmd ||= '--help';
  if ($cmd eq '[') {
    return $self->run_action_multi($cmd, @args);
  }
  if ($cmd =~ s/^--//) {
    if ($cmd) {
      my $method = join('_', 'run_action', split '-', $cmd);
      if (my $code = $self->can($method)) {
        return $self->$code(@args);
      }
      barf "No such action --${cmd}, see 'perldoc plx' for the full list";
    }
    $cmd = shift @args;
  }
  $self->ensure_layout_config_dir;
  return $self->run_action_cmd($cmd, @args);
}

caller() ? 1 : __PACKAGE__->new->run(@ARGV);

=head1 NAME

App::plx - Perl Layout Executor

=head1 SYNOPSIS

  plx --help                             # This output

  plx --init <perl>                      # Initialize layout config
  plx --perl                             # Show layout perl binary
  plx --libs                             # Show layout $PERL5LIB entries
  plx --paths                            # Show layout additional $PATH entries
  plx --env                              # Show layout env var changes
  plx --cpanm -llocal --installdeps .    # Run cpanm from outside $PATH
 
  plx perl <args>                        # Run perl within layout
  plx -E '...'                           # (ditto)
  plx script-in-dev <args>               # Run dev/ script within layout
  plx script-in-bin <args>               # Run bin/ script within layout
  plx ./script <args>                    # Run script within layout
  plx script/in/cwd <args>               # (ditto)
  plx program <args>                     # Run program from layout $PATH

=head1 WHY PLX

While perl has many tools for configuring per-project development
environments, using them can still be a little on the lumpy side. With
L<Carton>, you find yourself running one of

  perl -Ilocal/lib/perl -Ilib bin/myapp
  carton exec perl -Ilib bin/myapp

With L<App::perlbrew>,

  perlbrew switch perl-5.28.0@libname
  perl -Ilib bin/myapp

With L<https://github.com/tokuhirom/plenv>,

  plenv exec perl -Ilib bin/myapp

and if you have more than one distinct layer of dependencies, while
L<local::lib> will happily handle that, integrating it with everything else
becomes a pain in the buttocks.

As a result of this, your not-so-humble author found himself regularly having
a miniature perl executor script at the root of git clones that looked
something like:

  #!/bin/sh
  eval $(perl -Mlocal::lib=--deactivate-all)
  export PERL5LIB=$PWD/local/lib/perl5
  bin=$1
  shift
  ~/perl5/perlbrew/perls/perl-5.28.0/bin/$bin "$@"

and then running:

  ./pl perl -Ilib bin/myapp



( run in 1.745 second using v1.01-cache-2.11-cpan-0bb4e1dffa6 )