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 )