Aller au contenu

Construction et packaging

La gestion des dépendances Perl repose sur CPAN et son client moderne cpanm. Carton joue le rôle d'un bundler (équivalent de Bundler pour Ruby ou pip-tools pour Python) en verrouillant les versions via un fichier cpanfile.snapshot. Pour la distribution de modules sur CPAN, Dist::Zilla automatise la gestion du cycle de vie complet.


CPAN et cpanm

cpanm (App::cpanminus) est le client CPAN recommande pour installer des modules. Il est plus rapide et moins interactif que le client CPAN natif.

# Installation de cpanm lui-meme
curl -L https://cpanmin.us | perl - App::cpanminus

# Ou via le client CPAN natif
perl -MCPAN -e 'install App::cpanminus'

# Installation d'un module
cpanm Mojolicious
cpanm DBI DBD::SQLite
cpanm Moose Moo Types::Standard

# Installation d'une version specifique
cpanm Mojolicious@9.35

# Installation sans les tests (plus rapide en CI)
cpanm --notest Mojolicious

# Installation locale (sans droits root)
cpanm --local-lib ~/perl5 Mojolicious

# Desinstallation
cpanm --uninstall Mojolicious
# Variables d'environnement pour une installation locale
export PERL_LOCAL_LIB_ROOT="$HOME/perl5"
export PERL_MB_OPT="--install_base \"$HOME/perl5\""
export PERL_MM_OPT="INSTALL_BASE=$HOME/perl5"
export PERL5LIB="$HOME/perl5/lib/perl5"
export PATH="$HOME/perl5/bin:$PATH"

cpanm et proxies

cpanm respecte les variables d'environnement http_proxy et https_proxy. En environnement d'entreprise, definissez ces variables avant d'installer des modules.


cpanfile — declaration des dépendances

Le fichier cpanfile est l'équivalent de requirements.txt (Python) ou package.json (Node.js). Il déclaré les dépendances du projet avec leurs contraintes de version.

# cpanfile — a la racine du projet

# Dependances de production
requires 'perl',               '5.036';
requires 'Mojolicious',        '>= 9.0';
requires 'DBI',                '>= 1.643';
requires 'DBD::SQLite',        '>= 1.72';
requires 'Moo',                '>= 2.005';
requires 'Types::Standard',    '>= 2.0';
requires 'Try::Tiny',          '>= 0.31';
requires 'Log::Any',           '>= 1.717';
requires 'JSON::PP';           # Version quelconque

# Dependances de test uniquement
on 'test' => sub {
    requires 'Test::More',       '>= 1.302';
    requires 'Test::Mojo';
    requires 'Test::MockModule', '>= 0.177';
    requires 'Devel::Cover',     '>= 1.36';
};

# Dependances de developpement
on 'develop' => sub {
    requires 'Perl::Critic',   '>= 1.148';
    requires 'Perl::Tidy',     '>= 20230912';
    requires 'Devel::NYTProf';
};
# Installation de toutes les dependances depuis cpanfile
cpanm --installdeps .

# Installation avec les dependances de test
cpanm --installdeps --with-test .

# Installation avec les dependances de developpement
cpanm --installdeps --with-develop .

Carton — gestionnaire de dépendances verrouillees

Carton est l'équivalent de Bundler (Ruby) ou pip-compile (Python). Il installe les dépendances dans un dossier local/ et généré un fichier cpanfile.snapshot qui verrouille les versions exactes.

# Installation de Carton
cpanm Carton

# Installer les dependances (cree local/ et cpanfile.snapshot)
carton install

# Installer uniquement les dependances de production
carton install --deployment

# Executer un script avec les dependances locales
carton exec perl app.pl

# Mettre a jour toutes les dependances
carton update

# Mettre a jour un module specifique
carton update Mojolicious
# Structure d'un projet avec Carton
mon-projet/
├── app.pl
├── lib/
│   └── MonApp/
├── t/
├── cpanfile              # Declaration des dependances
├── cpanfile.snapshot     # Versions verrouillees (commiter dans git)
└── local/                # Modules installes (NE PAS commiter)
# Extrait d'un cpanfile.snapshot
DISTRIBUTIONS
  DBI-1.643
    pathname: T/TI/TIMB/DBI-1.643.tar.gz
    provides:
      DBI 1.643
    requirements:
      perl 5.008
  DBD-SQLite-1.74
    pathname: I/IS/ISHIGAKI/DBD-SQLite-1.74.tar.gz
    provides:
      DBD::SQLite 1.74
    requirements:
      DBI 1.57
      perl 5.008003

cpanfile.snapshot dans git

Commitez toujours cpanfile.snapshot dans git. Ce fichier garantit que tous les développeurs et le CI utilisent exactement les mêmes versions de modules, evitant les surprises de mise à jour.


Distribution CPAN avec ExtUtils::MakeMaker

Pour publier un module sur CPAN, le fichier Makefile.PL définit les metadonnees de la distribution.

# Makefile.PL — configuration de distribution classique
use ExtUtils::MakeMaker;

WriteMakefile(
    NAME             => 'MonApp::Util',
    VERSION_FROM     => 'lib/MonApp/Util.pm',  # Version lue depuis le module
    ABSTRACT_FROM    => 'lib/MonApp/Util.pm',  # Description lue depuis POD
    AUTHOR           => 'Fabien Dupont <fabien@example.com>',

    # Dependances de production
    PREREQ_PM => {
        'perl'         => '5.036',
        'Moo'          => '2.005',
        'Try::Tiny'    => '0.31',
    },

    # Dependances de test
    TEST_REQUIRES => {
        'Test::More'   => '1.302',
    },

    # Metadonnees pour MetaCPAN
    META_MERGE => {
        'meta-spec' => { version => 2 },
        resources   => {
            repository => {
                type => 'git',
                url  => 'https://github.com/exemple/MonApp-Util.git',
                web  => 'https://github.com/exemple/MonApp-Util',
            },
            bugtracker => {
                web => 'https://github.com/exemple/MonApp-Util/issues',
            },
        },
        x_static_install => 1,
    },
);
# Cycle de build classique
perl Makefile.PL
make
make test
make dist           # Cree MonApp-Util-1.00.tar.gz

Dist::Zilla — automation de la distribution

Dist::Zilla (dzil) automatise la gestion complète du cycle de vie d'une distribution CPAN : versioning, génération de Makefile.PL, tests, upload vers PAUSE.

; dist.ini — configuration Dist::Zilla
name    = MonApp-Util
author  = Fabien Dupont <fabien@example.com>
license = Perl_5
copyright_holder = Fabien Dupont
copyright_year   = 2025

version = 1.02

; Gestion automatique de la version
[@Basic]

; Genere un Makefile.PL compatible
[MakeMaker]

; Verifie la syntaxe Perl de tous les fichiers
[PodSyntaxTests]

; Teste que le module charge correctement
[Test::Compile]

; Genere les metadonnees CPAN
[MetaJSON]
[MetaYAML]

; Recupère les prereqs depuis le code
[AutoPrereqs]

; Gestion du changelog
[ChangelogFromGit]
# Commandes dzil courantes
dzil test           # Lance les tests
dzil build          # Construit la distribution
dzil release        # Build + upload vers PAUSE (necessite un compte PAUSE)
dzil listdeps       # Liste toutes les dependances

Dockerfile multi-stage

# Dockerfile multi-stage pour application Perl/Mojolicious
# Stage 1 : installation des dependances
FROM perl:5.40-slim AS deps

WORKDIR /app

# Outils de compilation
RUN apt-get update && apt-get install -y --no-install-recommends \
    build-essential \
    libsqlite3-dev \
    && rm -rf /var/lib/apt/lists/*

# Installation de Carton
RUN cpanm --notest Carton

# Copie des fichiers de dependances uniquement (cache Docker)
COPY cpanfile cpanfile.snapshot ./

# Installation des dependances de production dans local/
RUN carton install --deployment

# Stage 2 : image de production
FROM perl:5.40-slim AS production

WORKDIR /app

# Dependances systeme minimales
RUN apt-get update && apt-get install -y --no-install-recommends \
    libsqlite3-0 \
    && rm -rf /var/lib/apt/lists/*

# Copie des modules depuis le stage deps
COPY --from=deps /app/local ./local

# Copie du code applicatif
COPY lib ./lib
COPY app.pl ./

# Utilisateur non-root
RUN useradd -r -u 1001 appuser
USER appuser

EXPOSE 3000

CMD ["carton", "exec", "perl", "app.pl", "daemon", "-l", "http://*:3000"]
# Build et run
docker build -t mon-app-perl .
docker run -p 3000:3000 mon-app-perl

Fatpacking — distribution standalone

App::FatPacker regroupe un script Perl et toutes ses dépendances dans un seul fichier executable, sans necessiter l'installation de modules sur la machine cible.

# Installation de fatpacker
cpanm App::FatPacker

# Trace les modules utilises par le script
fatpack trace mon_script.pl

# Copie les modules traces dans fatlib/
fatpack packlists-for `cat fatpacker.trace` > packlists
fatpack tree `cat packlists`

# Cree le script standalone
fatpack pack mon_script.pl > mon_script_standalone.pl

chmod +x mon_script_standalone.pl
./mon_script_standalone.pl

Limites de fatpacking

Fatpacker ne fonctionne qu'avec des modules Pure Perl. Les modules XS (avec du code C compile) comme DBD::SQLite ne peuvent pas être fatpackes. Utilisez cette technique pour des scripts d'administration ou d'audit qui ne doivent pas dépendre de l'environnement cible.


Pipeline CI/CD complet

# .github/workflows/ci.yml
name: CI Perl

on:
  push:
    branches: [main, develop]
  pull_request:

jobs:
  test:
    name: Tests Perl ${{ matrix.perl }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        perl: ['5.36', '5.38', '5.40']

    steps:
      - uses: actions/checkout@v4

      - name: Setup Perl
        uses: shogo82148/actions-setup-perl@v1
        with:
          perl-version: ${{ matrix.perl }}

      - name: Cache Carton
        uses: actions/cache@v4
        with:
          path: local/
          key: carton-${{ matrix.perl }}-${{ hashFiles('cpanfile.snapshot') }}

      - name: Installer Carton
        run: cpanm --notest Carton

      - name: Installer les dependances
        run: carton install --deployment

      - name: Lancer les tests
        run: carton exec prove -r t/

      - name: Couverture de code
        if: matrix.perl == '5.40'
        run: |
          cpanm --notest Devel::Cover
          carton exec cover -test -report text
          carton exec cover -threshold 70

  lint:
    name: Analyse statique
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shogo82148/actions-setup-perl@v1
        with:
          perl-version: '5.40'
      - run: cpanm --notest Perl::Critic Perl::Tidy
      - run: perlcritic --severity 3 lib/
      - run: perltidy --check-syntax lib/**/*.pm