Deploying “Half of CPAN” on Debian using Carton

As systems developer I like just being able to pick the most useful Perl modules from CPAN and preferably use quite recent versions of the modules I need. On the other hand our systems administrators prefer running Debian Stable and deploy software using .deb packages.

I do not want to maintain hundred small packages by hand. I just want a single package with all needed the Perl dependencies for a given project. After looking at different possibilities for bundling Perl dependencies I finally found Carton written by Tatsuhiko Miyagawa.

Given a list of your direct Perl dependencies, Carton can maintain the full dependency chain and manage which versions of each module you deploy. One of the nice features is that you get a reproducible set of versions each time and not just the latest, greatest, and recently broken versions.

The initial step is to set Carton up to install the dependencies for my project. This part is done just like the Carton documentation describes. To ensure independence from whatever is available on CPAN I end by calling carton bundle.

The next step is to write a small Perl module making sure you load modules from the right place. I end up with installing modules in /usr/lib/projectname. My module looks like this:

package My::Bundle::ProjectName;

=head1 NAME

My::Bundle::ProjectName - Bundle of CPAN modules used by ProjectName

=cut

our $VERSION = 1.0;

use Config;
use Carp;
use Carp::Heavy;  # Ensure that all parts of Carp are loaded before @INC mangling
use File::Spec;

our $BASE = '/usr/lib/projectname';

sub import {
    carp (__PACKAGE__ . " bundle not installed")
        unless -d $BASE;

    unshift @INC, 
        File::Spec->catdir( $BASE, 'lib', 'perl5' ),
        File::Spec->catdir( $BASE, 'lib', 'perl5', $Config{archname});
}

1;

Now I need to install this module and all the modules maintained by Carton. I do this by a enhanced Module::Build script:

use v5.10.0;
use strict;
use warnings;

use Module::Build;

my $class = Module::Build->subclass(
    class => "My::Builder",
    code  => q{
        sub ACTION_build {
            my $self = shift;
            $self->SUPER::ACTION_build;

            !system 'carton', 'install', '--cached';
        }

        sub ACTION_install {
            my $self = shift;
            $self->SUPER::ACTION_install;
            
            my $installdir = $self->destdir . "/usr/lib/projectname";

            system 'install', '-d', $installdir;
            !system 'cp', '-a', 'local/lib', $installdir;
        }
    },
);

$class->new(
    module_name       => 'My::Bundle::ProjectName',
    dist_author       => 'Peter Makholm ',
    dist_version_from => 'lib/My/Bundle/ProjectName.pm',
    add_to_cleanup    => [ 'local/lib', 'local/bin' ],
)->create_build_script;

The last step is to create a .deb package. This is mostly just a standard debian/ directory using debhelper. You just need to override the dh_perl to pick up the correct binary dependencies. So this is my debian/rules:

#!/usr/bin/make -f

%:
        dh $@

override_dh_perl:
        dh_perl /usr/lib/projectname/lib

That’s it. Then all the scripts and applications making up my project just needes to use My::Bundle::ProjectName and the rest is automatic.

I am thinking about enhancing Carton to provide individual .deb packages instead of just installing the modules. This would make it quite easy to maintain the packages in a more The Debian Perl Group-compatible way. That could be cool.

4 Comments »

  1. andrew gray said,

    November 15, 2012 @ 10:53 am

    Tatsuhiko Miyagawa is a demon. Surprised the world hasn’t brought him a Ferrari yet.

  2. Josef Assad said,

    November 15, 2012 @ 11:26 am

    Ah, so carton is the perl equivalent of pip in python.

    Does perl have something equivalent to virtualenv also? It’s a lightweight way of compartmentalising one’s python software from the overall system by faking python library paths and so on.

  3. Peter Makholm said,

    November 15, 2012 @ 11:38 am

    The “See Also” section of Carton compares it to pip (Python), npm (JavaScript), and Bundler (Ruby?).

    I don’t know how much virtualenv does. If you just need to manage the search paths for modules local::lib is the right tool. If you also need to manage multiple versions of the Perl interpreter itself I strongly recommend looking at perlbrew.

    Using perlbrew is also useful just for getting a newer Perl than the system Perl and to follow the development version of Perl, while stil using a stable Perl for real tasks.

  4. Cord Beermann said,

    November 15, 2012 @ 2:38 pm

    Hmmm… what is wrong with dh-make-perl?

    http://packages.debian.org/dh-make-perl

RSS feed for comments on this post · TrackBack URI

Leave a Comment