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,
},
);
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"]
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