The 2003 Perl Advent Calendar
[about] | [archives] | [contact] | [home]

On the 13th day of Advent my True Language brought to me..
only

One of the great things about CPAN is that there's so much great code out there that's being constantly updated and improved to make it better. One of the problems about CPAN is that there's so much great code out there that's being constantly updated and improved.

The trouble with improvements to code is that sometimes it changes the functionality or interface of existing code. And, if you need the old functionality you can't upgrade to the latest version. This situation gets really complicated when you have two projects running on the same computer, one of which requires the old module and one of which requires a later version. It used to be the case that you'd need separate installs to get this to work or experiment with altering @INC from within your code, which is messy to say the least.

What you want is to be able to only load a particular version of a module in one situation and only load another version on a different occasion. only will allow you to do this - to have two (or more) septate installs of a module on your machine and determine from code which should be loaded when.

The first step to using only is to install extra versions of modules with only that it'll be able to access when it needs a particular version of the code. This places the module in a special versioned only only directory in your module tree where it won't interfere with other modules.

To install a module so that only can use it you need to go though the standard extraction and test cycle, and then use the only module for the last step.

Let's get the very old version of File::chdir that has an old interface that's no longer supported by the later versions - the ability to override the built-in chdir command - and install it with only. This distribution is so old it isn't on CPAN anymore, so we have to go to BACKPAN to download it. BACKPAN contains all the modules that have ever been released to CPAN and then removed by the authors because they're out of date and would be wasting space if they were left on the main CPAN which is mirrored across a great many servers.

We download the distribution and test it as normal:

  bash-2.05b$ wget http://backpan.perl.org/modules/by-authors/\
                     id/M/MS/MSCHWERN/File-chdir-0.02.tar.gz
  bash-2.05b$ gunzip -c File-chdir-0.02.tar.gz | tar -xvf -
  bash-2.05b$ cd File-chdir-0.02
  bash-2.05b$ perl Makefile.PL 
  bash-2.05b$ make
  bash-2.05b$ make test

Now we've now got our module uncompressed and tested, all we need to do now is install it. We'd normally just do:

  bash-2.05b$ sudo make install

to run the make install command as root that would install the module globally. We don't want to do that though - that would break the nice new bug free version of File::chdir we installed yesterday. Instead we want to install via only. We can do this from the command line like so:

  bash-2.05b$ sudo perl -Monly=install
  Password:
  Installing /usr/local/share/perl/5.8.0/version-lib/0.02/File/chdir.pm
  Installing /usr/local/share/perl/5.8.0/version-lib/0.02/File/chdir.yaml

You can see it's installed it into the special versioned location on the hard drive (you can also see I should update my Perl to 5.8.2, but that's another issue.) You need to make sure that the perl you use to run this command is the same perl you ran when you did perl Makefile.PL, which you shouldn't have to worry about unless you've got multiple perls installed.

The process is similar if you're using Module::Build to install your module, where you do everything but the sudo ./Build install, and insted use sudo ./Build versioninstall.

Only using the correct version

In our code we'd normally load our module like this (and indeed, we still can if we want the main version installed on the system.)

  use File::chdir;

If we want to load code installed by only we need to use the only module to load it instead:

  use only 'File::chdir' => 0.02;

This loads the old version of File::chdir. This version exports a replacement chdir function - something the new API doesn't do. This means that this code will now function:

  #!/usr/bin/perl
  # turn on the safety features
  use strict;
  use warnings;
  use only 'File::chdir' => 0.02;
  use IO::File;
 
  my @dotfiles = dotfiles();
  # create report in current directory
  my $fh = IO::File->new(">report.txt")
     or die "Can't open report";
  print $fh "There are ".@dotfiles." dotfiles:\n";
  print $fh " * $_\n" foreach @dotfiles;
  sub dotfiles
  {
     # return all the .dotfiles in the home dir
     my $old_dir = chdir($ENV{HOME});
     return grep { -f || -l } <.*>
  }

If we'd used the latest version of File::chdir the chdir function wouldn't have worked like that. It wouldn't have changed directory back when we left dotfiles and we'd have written reports.txt in the home directory.

only can be more flexible than this, so we can require that we have either version 0.01 or 0.02 installed:

  use only 'File::chdir' => '0.01-0.02';

Or, as there's no 0.001, we can say 'any version that's 0.02 or less'

  use only 'File::chdir' => '-0.02';

You can include as many of these blocks in the string as you want to get the right version separated by spaces. You can also use an exclamation mark to block out a version that might otherwise be loaded. All in all, you can have a whole plethora of versions of modules installed and use only to select the right one. This is very handy if you're a module author and you want to check if code runs on older versions of libraries.

Passing arguments to modules

When loading modules normally it's common to pass arguments to them to tell them to export functions or perform other tasks. If we'd installed File::chdir version 0.02 we'd be able to say

  use File::chdir ':EVERYWHERE';

to get it to replace chdir everywhere, for all code loaded. We can do the same thing with our only statement by writing

  use only 'File::chdir' => '0.01-0.02', ':EVERYWHERE';

The other thing we can do with all modules that export functions is call them in a way that prevent that from happening. There's a special bit of Perl syntax that can be used to skip calling the 'import' routine in a module when it's loaded so the symbols aren't exported:

  use File::chdir ();

only emulates this syntax by allowing you to pass an empty arrayref to it:

  use only 'File::chdir' => '0.01-0.02', [];

Further features

only has a bunch of other features that are too numerous to provide examples of. The ability to specify module arguments is present. You can get it to install modules into other locations so you can have multiple only module repositories. There's even an object orientated interface should you need it. All these points are covered quite clearly in the documentation.

  • BACKPAN
  • only article on perl.com