Quantcast
Viewing latest article 19
Browse Latest Browse All 21

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.


Viewing latest article 19
Browse Latest Browse All 21

Trending Articles