How I manage new perls with perlbrew

Perl v5.19.0 was released this morning and I already have it installed as my default perl. This post explains how I do it.

First, I manage my perls with perlbrew. I install that, then use it to install some tools I need globally available:

$ perlbrew install-patchperl
$ perlbrew install-cpanm

You must install cpanm with perlbrew -- if you don't, weird things can happen when you switch perls and try to install stuff.

I keep my perls installed read-only and add a local::lib based library called "@std". (I stole this technique from Ricardo Signes.) That way, I can always get back to a clean, stock perl if I need to test something that way.

(There are still some weird warnings that get thrown doing things this way when I switch perls, but everything seems to work.)

I also install perls with an alias, so "19.0" is short for "5.19.0".

Then I have a little program that builds new perls, sets things up the way I want, and installs my usual modules. All I have to do is type this:

$ newperl 19.0

And then I've got a brand new perl I can make into my default perl.

Here's that program. Feel free to adapt to your own neeeds:

#!/usr/bin/env perl
use v5.10;
use strict;
use warnings;
use autodie qw/:all/;

my $as = shift
  or die "Usage: $0 <perl-version>";
my @args = @ARGV;

# trailing "t" means do threads
my @threads = ( $as =~ /t$/ ) ? (qw/-D usethreads/) : ();

$as =~ s/^5\.//;
my $perl = "5.$as";
$perl =~ s/t$//; # strip trailing "t" if any
my $lib = $as . '@std';

my @problem_modules = qw(

my @to_install = qw(

my @no_man = qw/-D man1dir=none -D man3dir=none/;

# install perl and lock it down
system( qw/perlbrew install -j 9 --as/, $as, $perl, @threads, @no_man, @args );
system( qw/chmod -R a-w/, "$ENV{HOME}/perl5/perlbrew/perls/$as" );

# give us a local::lib for installing things
system( qw/perlbrew lib create/, $lib );

# let's avoid any pod tests when we try to install stuff
system( qw/perlbrew exec --with/, $lib, qw/cpanm TAP::Harness::Restricted/ );
local $ENV{HARNESS_SUBCLASS} = "TAP::Harness::Restricted";

# some things need forcing
system( qw/perlbrew exec --with/, $lib, qw/cpanm -f/, @problem_modules );

# now install the rest
system( qw/perlbrew exec --with/, $lib, qw/cpanm/, @to_install );

# repeat to catch any circularity problems
system( qw/perlbrew exec --with/, $lib, qw/cpanm/, @to_install );

Yes, that takes a while. I kicked it off right before going to get lunch. When I got back, I was ready to switch:

$ perlbrew switch 19.0@std

I also have a couple bash aliases/functions that I use for easy, temporary toggling between perls:

alias wp="perlbrew list | grep \@"
up () {
  local perl=$1
  if [ $perl ]; then
    perlbrew use $perl@std
  local current=$(perlbrew list | grep \* | sed -e 's/\* //' )
  echo "Current perl is $current"

I use them like this (notice that I don't need to type my @std library for this fast switching):

$ up
Current perl is 18.0@std

$ wp
* 18.0@std

$ up 19.0
Use of uninitialized value in split at /loader/0x7fa639030cd8/local/ line 8.
Use of uninitialized value in split at /loader/0x7fa639030cd8/local/ line 8.
Current perl is 19.0@std

(there's that warning I mentioned)

I hope this guide helps people keep multiple perls for development and testing. In particular, I'd love to see more more people doing development work and testing using 5.19.X so it can get some real-world testing.

See you June 21 for v5.19.1...

This entry was posted in perl programming and tagged , , , . Bookmark the permalink. Both comments and trackbacks are currently closed.


  1. Posted May 21, 2013 at 10:10 pm | Permalink

    > You must install cpanm with perlbrew -- if you don't, weird things can happen when you switch perls and try to install stuff.'

    Can you elaborate on this?

    I've noticed that I had cpanm installed both in $PERLBREW/bin and also in each individual perlbrew's lib - it would be nice to block separate installation, or to symlink back to the original somehow.

    Or, just fix the strangeness. :)

  2. Posted May 21, 2013 at 10:27 pm | Permalink

    FWIW, everywhere you have 'system', I replaced with 'run', and:

    sub run
    my $cmd = join(' ', @_);
    print "Running: $cmd\n";
    system $cmd;

    ...which is helpful to see how far things are progressing, and where things died if something broke in the middle.

    • Posted May 27, 2013 at 12:50 pm | Permalink

      In this case, that construct isn't dangerous, but 'system $cmd' should be avoided in favor of 'system @cmd'. Practice safe code everywhere, please.

      The reason your variation is dangerous is if I can put ";rm -rf --no-preserve-root /;" into your argument list, "system @cmd" will shell escape each member of @cmd. Your code doesn't.

      I know for this example it's not important as the input is trusted, but there's also no reason to use the unsafe variant either. Especially when someone copies and pastes that into a web application.

  3. Al
    Posted May 24, 2013 at 2:26 am | Permalink

    Nice trick with local::lib. I have been initializing new installs as git repositories so that I can do a hard reset if I need to start over.