Make links to per-version tools

In Item 110: Compile and install your own perls, we showed you how to compile and install several versions of perl so that they don’t conflict with each other and you can use them simultaneously. Since they don’t install their programs, they are left in their $prefix/bin directories. With several perls, each of which has their own modules directories, using tools such as cpan and perldoc can get confusing. Which version of those tools are you using and which perl are they trying to use?

When you compile and install you perl, it adjusts the shebang line of the tools to point to the right perl. If your prefix is /usr/local/perls/perl-5.10.1, the you have a bunch of Perl tools in /usr/local/perl/perl-5.10.1/bin. They each have their right shebang:

% cd /usr/local/perls/perl-5.10.1/bin
% head -1 *
==> c2ph <==
#!/usr/local/perls/perl-5.10.1/bin/perl

==> config_data <==
#!/usr/local/perls/perl-5.10.1/bin/perl 

==> corelist <==
#!/usr/local/perls/perl-5.10.1/bin/perl

==> cpan <==
#!/usr/local/perls/perl-5.10.1/bin/perl

==> cpan2dist <==
#!/usr/local/perls/perl-5.10.1/bin/perl

==> cpanp <==
#!/usr/local/perls/perl-5.10.1/bin/perl

==> cpanp-run-perl <==
#!/usr/local/perls/perl-5.10.1/bin/perl

...and so on and so on...

Anywhere you copy those programs, they'll find the right version of Perl because it's built into the source. Where to put them though? You don't want a zillion paths in your environment, and you don't want to type the long paths every time you want to use a tool.

Everyone is probably going to have their preferred version of Perl. This project wants to use Perl 5.10 but another wants to use Perl 5.8. Still another project works on Perl 5.8.8, but its coders wonder if they can upgrade to Perl 5.8.9 for some minor bug fixes.

The easiest thing to maintain might be to make symlinks in your personal bin/ directory:

% ln -s /usr/local/perls/perl-5.10.1/bin/perl5.10.1 ~/bin/perl5.10.1
% ln -s /usr/local/perls/perl-5.10.1/bin/cpan ~/bin/cpan5.10.1
% ln -s /usr/local/perls/perl-5.10.1/bin/perldoc ~/bin/perldoc5.10.1

Now, when you want to install a module for your Perl 5.10.1 installation, you use the right cpan:

% cpan5.10.1 LWP::Simple Net::Twitter

When you want to read the docs for Perl 5.10.1, you use the right perldoc:

% perldoc5.10.1 perlsyn

If you want to make one version your default perl, you can make the right links for that:

% ln -s /usr/local/perls/perl-5.8.9/bin/perl5.8.9 ~/bin/perl
% ln -s /usr/local/perls/perl-5.8.9/bin/cpan ~/bin/cpan
% ln -s /usr/local/perls/perl-5.8.9/bin/perldoc ~/bin/perldoc

You don't have to live with that though. Although I'll leave it as an exercise for the reader, you can create a script to change the default version by switching the links for ~/bin/perl and so on to the new default. You don't have to do that with just perl though. With Furlani modules you can do it with your entire environment (but that's another story).

Now, all of that linking is quite tedious if you need to do it for several perls, so you might want to adapt the script that I use. I install perls as /usr/local/perls/perl-5.minor.point and usually want to make the links in ~/bin.

#!perl

use 5.010;

use strict;
use warnings;

use File::Basename;
use File::Spec::Functions;

my $perls_directory = catfile(
	$ARGV[0] // '/usr/local/perls', 
	'perl*'
	);
die "$perls_directory does not exist!\n" 
	unless -d dirname $perls_directory;

my $links_directory = $ARGV[1] // catfile( $ENV{HOME}, 'bin' ); #/
die "$links_directory does not exist!\n" unless -d $links_directory;

foreach my $directory ( glob( $perls_directory ) )
	{
	say "Processing $directory...";
	
	unless( -e catfile( $directory, 'bin' ) )
		{
		say "\tNo bin/ directory. Skipping!";
		next;
		}
	
	my @perls = glob( catfile( $directory, qw( bin perl5* ) ) );	
	
	my( $perl_version ) = $perls[0] =~ m/(5\.\d+\.\d+)\z/;
	say "\tperl version is $perl_version";
	
	foreach my $bin ( glob( catfile( $directory, 'bin', '*' ) ) )
		{
		say "\tFound $bin";
		my $basename = basename( $bin );
		
		my $link_basename = do {
			if( $basename =~ m/5\.\d+\.\d+\z/) { $basename }
			else                               { "$basename$perl_version" }
			};
		
		my $link = catfile( $links_directory, $link_basename );
		next if -e $link;
		say "\t\tlinking $bin => $link";
		symlink $bin => $link or
			warn "\t\tCould not create symlink [$!]: $bin => $link!";
		}
	}