#!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') ]; } sub layout_lib_specs { my $base_dir = $self->layout_base_dir; local *_ = sub { Cwd::realpath($fs->rel2abs(shift, $base_dir)) }; [ map [ ($_->[0] =~ /\.([^.]+)$/), _($_->[1]) ], @{$self->layout_libspec_config} ]; } sub layout_file { my ($self, @path) = @_; $fs->catfile($self->layout_base_dir, @path); } sub layout_dir { my ($self, @path) = @_; $fs->catdir($self->layout_base_dir, @path); } sub ensure_layout_config_dir { barf ".plx directory does not exist" unless -d $self->layout_dir('.plx'); my $format = $self->read_config_entry('format'); barf ".plx directory has no format specifier" unless $format; barf ".plx format ${format} unknown" unless $format eq '1'; } sub layout_config_file { shift->layout_file('.plx', @_) } sub layout_config_dir { shift->layout_dir('.plx', @_) } sub write_config_entry { my ($self, $path, $value) = @_; my $file = $self->layout_config_file(ref($path) ? @$path : $path); open my $wfh, '>', $file or die "Couldn't open ${file}: $!"; print $wfh "${value}\n"; } sub clear_config_entry { my ($self, $path) = @_; my $file = $self->layout_config_file(ref($path) ? @$path : $path); unlink($file) or barf "Failed to unlink ${file}: $!" if -e $file; } sub read_config_entry { my ($self, $path) = @_; my $file = $self->layout_config_file(ref($path) ? @$path : $path); return undef unless -f $file; open my $rfh, '<', $file or die "Couldn't open ${file}: $!"; chomp(my $value = <$rfh>); return $value; } sub list_config_names { my ($self, $path) = @_; my $dir = $self->layout_config_dir(ref($path) ? @$path : $path); return () unless -d $dir; opendir my($dh), $dir or die "Couldn't opendir ${dir}: $!"; return grep -f $fs->catfile($dir, $_), sort readdir($dh); } sub slurp_command { my ($self, @cmd) = @_; open my $slurp_fh, '-|', @cmd or barf "Failed to start command (".join(' ', @cmd)."): $!"; chomp(my @slurp = <$slurp_fh>); return @slurp; } sub prepend_env { my ($self, $env, @parts) = @_; $ENV{$env} = join(':', @parts, $ENV{$env}||()); } sub setup_env_for_ll { my ($self, $path) = @_; local $0 = $0 eq '-' ? 'plx' : $0; local::lib->import($path); } sub setup_env_for_dir { my ($self, $path) = @_; $self->prepend_env(PERL5LIB => $path); } sub setup_env { $ENV{PERL_PLX_BASE} = $self->layout_base_dir; my ($site_libs) = $self->slurp_command( $self->layout_perl, '-MConfig', '-e', 'print join(",", @Config{qw(sitearch sitelibexp)})' ); $ENV{PERL5OPT} = '-M-lib='.$site_libs; $ENV{$_} = $self->read_config_entry([ env => $_ ]) for $self->list_config_names('env'); my $perl_dirname = File::Basename::dirname($self->layout_perl); our %orig_env; unless (grep $_ eq $perl_dirname, split ':', $orig_env{PATH}) { $self->prepend_env(PATH => $perl_dirname); } foreach my $lib_spec (@{$self->layout_lib_specs}) { my ($type, $path) = @$lib_spec; next unless $path and -d $path; $self->${\"setup_env_for_${type}"}($path); } return; } sub cmd_search_path { qw(.plx/cmd dev bin) } sub run_action_commands { my ($self, $filter) = @_; $self->ensure_layout_config_dir; my @commands; my %seen; foreach my $dirname ($self->cmd_search_path) { next unless -d (my $dir = $self->layout_dir($dirname)); opendir my ($dh), $dir or barf "Couldn't open ${dir}: $!"; foreach my $entry (sort readdir($dh)) { next if $entry =~ /^\.+$/; my $file = $self->layout_file($dirname, $entry); next unless -f $file; unless ($seen{$entry}++) { push @commands, [ $entry, "${dirname}/${entry}" ]; } } } my $path = do { local $ENV{PATH} = ''; $self->setup_env; $ENV{PATH} }; foreach my $dir (split ':', $path) { opendir my ($dh), $dir; foreach my $entry (sort readdir($dh)) { next if $entry =~ /^\.+$/; my $file = $fs->catfile($dir, $entry); next unless -x $file; push @commands, [ $entry, $file ] unless $seen{$entry}++; } } if ($filter) { my $match = $filter =~ m{^/(.+)/$} ? $1 : qr/^\Q${filter}/; @commands = grep { $_->[0] =~ $match } @commands; } my $max = List::Util::max(map length($_->[0]), @commands); my $base = $self->layout_base_dir; my $home = $ENV{HOME}; foreach my $command (@commands) { my ($name, $path) = @$command; $path =~ s/^\Q${base}\///; $path =~ s/^\Q${home}/~/ if $home; say sprintf("%-${max}s %s", $name, $path); } } sub run_action_bareinit { my ($self, $perl) = @_; my $dir = $fs->catdir($self->{layout_base_dir}||Cwd::getcwd(), '.plx'); if (-d $dir) { if ($perl) { stderr <run_config_perl_set($perl||'perl'); $self->write_config_entry(format => 1); } sub run_action_userinit { my ($self, @args) = @_; my @perl = ( (@args and !ref($args[0]) and $args[0] ne '[') ? shift(@args) : () ); barf "--userinit requires \$HOME to be set" unless $ENV{HOME}; $self->run_action_base( $ENV{HOME}, '--multi' => [ '--bareinit', @perl ], [ qw(--config libspec add 25.perl5.ll perl5) ], (@args ? [ '--multi', @args ] : ()), ); } sub run_action_userstrap { my ($self, @args) = @_; my @perl = ( (@args and !ref($args[0]) and $args[0] ne '[') ? shift(@args) : () ); $self->run_action_userinit( @perl, [ '--installself' ], [ '--installenv' ], @args ); } sub run_action_installself { my $last_ll; foreach my $lib_spec (@{$self->layout_lib_specs}) { my ($type, $path) = @$lib_spec; $last_ll = $path if $type eq 'll'; } barf "No local::lib in libspec config" unless $last_ll; $self->run_action_cpanm( "-l${last_ll}", '-n', qw(App::cpanminus App::plx) ); } sub run_action_installenv { $self->ensure_layout_config_dir; barf "--installenv action currently assumes bash" unless $ENV{SHELL} =~ /bash/; barf "Couldn't find .bashrc" unless -f (my $bashrc = $fs->catfile($ENV{HOME}, ".bashrc")); my $plx_bin = do { local %ENV = our %orig_env; File::Which::which('plx-packed'); } || do { local %ENV = %ENV; $self->setup_env; File::Which::which('plx-packed'); }; barf "Couldn't find plx in PATH" unless $plx_bin; { open my $fh, '<', $bashrc or die "Couldn't open ${bashrc} to read: $!"; if (my ($line) = grep /plx-packed/, <$fh>) { chomp($line); stderr("Found line in .bashrc: $line"); return; } } my $base = $self->layout_base_dir; stderr("Appending to .bashrc"); open my $fh, '>>', $bashrc or die "Couldn't open ${bashrc} to append: $!"; print $fh "\neval \$(${plx_bin} --base ${base} --env)\n"; } sub run_action_init { my ($self, $perl) = @_; $self->run_action_bareinit($perl); my $libspec_dir = $self->layout_config_dir('libspec'); mkdir($libspec_dir) or barf "Couldn't create ${libspec_dir}: $!"; $self->run_config_libspec_add(@$_) for ( [ '25-local.ll' => 'local' ], [ '50-devel.ll' => 'devel' ], [ '75-lib.dir' => 'lib' ], ); } sub _which { my ($self, @args) = @_; $self->ensure_layout_config_dir; my @env; push @env, shift @args while $args[0] =~ /^\w+=/; my $cmd = shift @args; barf "--cmd " unless $cmd; if ($fs->file_name_is_absolute($cmd)) { return (exec => @env => $cmd => @args); } if ($cmd eq 'perl') { return (perl => @env => @args); } if ($cmd =~ m{/}) { return (perl => @env => $cmd, @args); } if ($cmd =~ /^-/) { my @optargs = ($cmd, @args); foreach my $optarg (@optargs) { next if $optarg =~ /^-/; foreach my $dirname ($self->cmd_search_path) { if (-f (my $file = $self->layout_file($dirname => $optarg))) { $optarg = $file; last; } } last; } return (perl => @env => @optargs); } foreach my $dirname ($self->cmd_search_path) { if (-f (my $file = $self->layout_file($dirname => $cmd))) { return (perl => @env => $file, @args); } } return (exec => @env => $cmd, @args); } sub run_action_which { my ($self, @args) = @_; my ($action, @call) = $self->_which(@args); say join(' ', 'plx', "--${action}", @call); } sub run_action_cmd { my ($self, @args) = @_; my ($action, @call) = $self->_which(@args); $self->${\"run_action_${action}"}(@call); } sub run_action_perl { my ($self, @call) = @_; $self->ensure_layout_config_dir; return $self->show_config_perl unless @call; my @env; push @env, shift @call while $call[0] =~ /^\w+=/; $self->run_action_exec(@env, $self->layout_perl, @call); } sub run_action_exec { my ($self, @exec) = @_; $self->ensure_layout_config_dir; $self->setup_env; shift @exec and $ENV{$1} = $2 while $exec[0] =~ /^(\w+)=(.*)$/; exec(@exec) or barf "exec of (".join(' ', @exec).") failed: $!"; } sub find_cpanm { local %ENV = our %orig_env; barf "Couldn't find cpanm in \$PATH" unless my $cpanm = File::Which::which('cpanm'); $cpanm; } sub run_action_cpanm { my ($self, @args) = @_; $self->ensure_layout_config_dir; my @cpanm = $self->find_cpanm; unless (@args and $args[0] =~ /^-[lL]/) { barf "--cpanm args must start with -l or -L to specify target local::lib"; } $self->setup_env; system($self->layout_perl, @cpanm, @args); } sub run_action_config { my ($self, $config, @args) = @_; $self->ensure_layout_config_dir; unless ($config) { say "# perl"; $self->show_config_perl; say "# libspec"; $self->show_config_libspec; if ($self->list_config_names('env')) { say "# env"; $self->show_config_env; } return; } barf "Unknown config key ${config}" unless my $show = $self->can("show_config_${config}"); return $self->$show unless @args; if (my $code = $self->can("run_config_${config}")) { return $self->$code(@args); } my ($subcmd, @rest) = @args; barf "Invalid subcommand ${subcmd} for config key ${config}" unless my $code = $self->can("run_config_${config}_${subcmd}"); return $self->$code(@rest); } sub show_config_perl { say $self->layout_perl } sub resolve_perl_via_perlbrew { my ($self, $perl) = @_; stderr "Resolving perl '${perl}' via perlbrew"; local %ENV = our %orig_env; barf "Couldn't find perlbrew in \$PATH" unless my $perlbrew = File::Which::which('perlbrew'); my @list = $self->slurp_command($perlbrew, 'list'); barf join( "\n", "No such perlbrew perl '${perl}', choose from:\n", @list, '' ) unless grep $_ eq $perl, map /(\S+)/, @list; my ($perl_path) = $self->slurp_command( $perlbrew, qw(exec --with), $perl, qw(perl -e), 'print $^X' ); return $perl_path; } sub run_config_perl_set { my ($self, $new_perl) = @_; barf "plx --config perl set " unless $new_perl; my $perl_spec = $new_perl; unless ($new_perl =~ m{/}) { $new_perl = "perl${new_perl}" if $new_perl =~ /^5/; $new_perl =~ s/perl-5/perl5/; # perlbrew name to perl binary require File::Which; stderr "Resolving perl '${new_perl}' via PATH"; if (my $resolved = File::Which::which($new_perl)) { $new_perl = $resolved; } else { $new_perl =~ s/^perl5/perl-5/; # perl binary to perlbrew name $new_perl = $self->resolve_perl_via_perlbrew($new_perl); } } barf "Not executable: $new_perl" unless -x $new_perl; $self->write_config_entry('perl.spec' => $perl_spec); $self->write_config_entry(perl => $new_perl); } sub show_config_libspec { my @ent = @{$self->layout_libspec_config}; my $max = List::Util::max(map length($_->[0]), @ent); say sprintf("%-${max}s %s", @$_) for @ent; } sub run_named_config_add { my ($self, $type, $name, $value) = @_; barf "plx --config ${type} add " unless $name and defined($value); unless (-d (my $dir = $self->layout_config_dir($type))) { mkdir($dir) or die "Couldn't make config dir ${dir}: $!"; } $self->write_config_entry([ $type => $name ], $value); } sub run_named_config_del { my ($self, $type, $name) = @_; barf "plx --config ${type} dev " unless $name; $self->clear_config_entry([ $type => $name ]); } sub run_config_libspec_add { shift->run_named_config_add(libspec => @_) } sub run_config_libspec_del { shift->run_named_config_del(libspec => @_) } sub show_config_env { my $max = List::Util::max( map length, my @names = $self->list_config_names('env') ); say sprintf("%-${max}s %s", $_, $self->read_config_entry([ env => $_ ])) for @names; } sub run_config_env_add { shift->run_named_config_add(env => @_) } sub run_config_env_del { shift->run_named_config_del(env => @_) } sub show_env { my ($self, $env) = @_; $self->ensure_layout_config_dir; local $ENV{$env} = ''; $self->setup_env; say $_ for split ':', $ENV{$env}; } sub run_action_libs { $self->show_env('PERL5LIB') } sub run_action_paths { $self->show_env('PATH') } sub run_action_env { $self->ensure_layout_config_dir; $self->setup_env; my @env_change; our %orig_env; foreach my $key (sort(keys %{{ %orig_env, %ENV }})) { my ($oval, $eval) = ($orig_env{$key}, $ENV{$key}); if (!defined($eval) or ($oval//'') ne $eval) { push @env_change, [ $key, $eval ]; } } my $shelltype = local::lib->guess_shelltype; my $shellbuild = "build_${shelltype}_env_declaration"; foreach my $change (@env_change) { print +local::lib->$shellbuild(@$change); } } sub run_action_help { require Pod::Usage; Pod::Usage::pod2usage(); } sub run_action_version { say sprintf "%f", $VERSION; } sub run_action_base { my ($self, $base, @chain) = @_; unless ($base) { say $self->layout_base_dir; return; } barf "--base " unless @chain; $self->new({ layout_base_dir => $base })->run(@chain); } 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 # 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 # Run perl within layout plx -E '...' # (ditto) plx script-in-dev # Run dev/ script within layout plx script-in-bin # Run bin/ script within layout plx ./script # Run script within layout plx script/in/cwd # (ditto) plx program # 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, you find yourself running one of perl -Ilocal/lib/perl -Ilib bin/myapp carton exec perl -Ilib bin/myapp With L, perlbrew switch perl-5.28.0@libname perl -Ilib bin/myapp With L, plenv exec perl -Ilib bin/myapp and if you have more than one distinct layer of dependencies, while L 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 However, much like back in 2007 frustration with explaining to other developers how to set up L to install into C<~/perl5> and how to set up one's environment variables to then find the modules so installed led to the exercise in rage driven development that first created L, walking newbies through the creation and subsequent use of such a script was not the most enjoyable experience for anybody involved. Thus, the creation of this module to reduce the setup process to: cpanm App::plx cd MyProject plx --init 5.28.0 plx --cpanm -llocal --notest --installdeps . Follwed by being able to immediately (and even more concisely) run: plx myapp which will execute C with the correct C and the relevant L already in scope. If this seems of use to you, the L is next and the L section of this document lists the full capabilities of plx. Onwards! =head1 QUICKSTART Let's assume we're going to be working on Foo-Bar, so we start with: git clone git@github.com:arthur-nonymous/Foo-Bar.git cd Foo-Bar Assuming the perl we'd get from running just C suffices, then we next run: plx --init If we want a different perl - say, we have a C in our path, or a C built in perlbrew, we'd instead run: plx --init 5.30.1 To quickly get our dependencies available, we then run: plx --cpanm -llocal --notest --installdeps . If the project is designed to use L and has a C, instead we would run: plx --cpanm -ldevel --notest Carton plx carton install If the goal is to test this against our current development version of another library, then we'd also want to run: plx --config libspec add 40otherlib.dir ../Other-Lib/lib If we want our ~/perl L available within the plx environment, we can add that as the least significant libspec with: plx --config libspec add 00tilde.ll $HOME/perl5 At which point, we're ready to go, and can run: plx myapp # to run bin/myapp plx t/foo.t # to run one test file plx prove # to run all t/*.t test files plx -E 'say for @INC' # to run a one liner within the layout To learn everything else plx is capable of, read on to the L section coming next. Have fun! =head1 BOOTSTRAP Under normal circumstances, one would run something like: cpanm App::plx However, if you want a self-contained plx script without having a cpan installer available, you can run: mkdir bin wget https://raw.githubusercontent.com/shadowcat-mst/plx/master/bin/plx-packed -O bin/plx to get the current latest packed version. The packed version bundles L and L, and also includes a modified C<--cpanm> action that uses an inline C. =head1 ENVIRONMENT C actions that execute external commands all clear any existing environment variables that start with C to keep an encapsulated setup for commands being run within the layouts - and also set C to exclude C (but not C) to avoid locally installed modules causing unexpected effects. Having done so, C then loads each env config entry and sets those variables - then prepends the C specific entries to both C and C. You can add env config entries with L: plx --config env add NAME VALUE The changes that will be made to your environment can be output by calling the L command. Additionally, environment variable overrides may be provided to the L, L and L commands by providing them in C format: # do not do this, it will be deleted PERL_RL=Perl5 plx # do this instead, it will provide the environment variable to the command plx PERL_RL=Perl5 =head1 ACTIONS plx --help # Print synopsis plx --version # Print plx version plx --init # Initialize layout config for . plx --bareinit # Initialize bare layout config for . plx --base # Show layout base dir plx --base # Run action with specified base dir 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 --config perl # Show perl binary plx --config perl set /path/to/perl # Select exact perl binary plx --config perl set perl-5.xx.y # Select perl via $PATH or perlbrew plx --config libspec # Show lib specifications plx --config libspec add # Add lib specification plx --config libspec del # Delete lib specification plx --config env # Show additional env vars plx --config env add # Add env var plx --config env del # Delete env var plx --exec # exec()s with env vars set plx --perl # Run perl with args plx --cmd # DWIM command: cmd = perl -> --perl cmd = - -> --perl - cmd = some/file -> --perl some/file cmd = ./file -> --perl ./file cmd = name -> exists .plx/cmd/ -> --perl .plx/cmd/ exists dev/ -> --perl dev/ exists bin/ -> --perl bin/ else -> --exec plx --which # Expands --cmd without running plx # Shorthand for plx --cmd plx --commands ? # List available commands plx --multi [ ] [ ... ] # Run multiple actions plx --showmulti [ ... ] [ ... ] # Show multiple action running plx [ ... ] [ ... ] # Shorthand for plx --multi plx --userinit # Init ~/.plx with ~/perl5 ll plx --installself # Installs plx and cpanm into layout plx --installenv # Appends plx --env call to .bashrc plx --userstrap # userinit+installself+installenv =head2 --help Prints out the usage information (i.e. the L) for plx. =head2 --init plx --init # resolve 'perl' in $PATH plx --init perl # (ditto) plx --init 5.28.0 # looks for perl5.28.0 in $PATH # or perl-5.28.0 in perlbrew plx --init /path/to/some/perl # uses the absolute path directly Initializes the layout. If a perl name is passed, attempts to resolve it via C<$PATH> and C and sets the result as the layout perl; if not looks for just C. Creates the following libspec config: 25-local.ll local 50-devel.ll devel 75-lib.dir lib =head2 --bareinit Identical to C<--init> but creates no default configs except for C. =head2 --base plx --base plx --base Without arguments, shows the selected base dir - C finds this by checking for a C<.plx> directory in the current directory, and if not tries the parent directory, recursively. The search stops either when C finds a C<.git> directory, to avoid accidentally escaping a project repository, or at the last directory before the root - i.e. C will test C but not C. With arguments, specifies a base dir to use, and then invokes the rest of the arguments with that base dir selected - so for example one can make a default configuration in C<$HOME> available as C by running: plx --init $HOME alias plh='plx --base $HOME' =head2 --libs Prints the directories that will be added to C, one per line. These will include the C subdirectory for each C entry in the libspecs, and the directory for each C entry. =head2 --paths Prints the directories that will be added to C, one per line. These will include the containing directory of the environment's perl binary if not already in C, followed by the C directories of any C entries in the libspecs. =head2 --env Prints the changes that will be made to your environment variables, in a syntax that is (hopefully) correct for your current shell. =head2 --cpanm plx --cpanm -Llocal --installdeps . plx --cpanm -ldevel App::Ack Finds the C binary in the C that C was executed I, and executes it using the layout's perl binary and environment variables. Requires the user to specify a L to install into via C<-l> or C<-L> in order to avoid installing modules into unexpected places. Note that this action exists primarily for bootstrapping, and if you want to use a different installer such as L, you'd install it with: plx --cpanm -ldevel App::cpm and then subsequently run e.g. plx cpm install App::Ack to install modules. =head2 --exec plx --exec Sets up the layout's environment variables and Cs the command. =head2 --perl plx --perl plx --perl