Article publié dans Linux Magazine 101, janvier 2008.
La perle de ce mois-ci a été rédigée par Philippe "BooK" Bruhat
(book@mongueurs.net
), de Lyon.pm et Paris.pm.
Certains modules Perl sont connus pour avoir de très nombreuses dépendances. L'expression consacrée dans la communauté Perl est de dire qu'ils nécessitent « la moitié de CPAN ».
Dès qu'on installe un second framework ou module assez gourmand
en dépendances, on se trouve donc obligé d'installer « l'autre
moitié de CPAN ». :-)
D'après le fichier 02packages.details.txt.gz du 4 décembre 2007, CPAN contient 50317 modules dans 14260 distributions, par 3511 auteurs. La moitié de tout ça, ça ferait quand même beaucoup...
Tout d'abord, il faut savoir qu'une partie des modules listés dans ce fichier sont les modules du core de Perl, puisque Perl est téléchargeable sur CPAN. Certains sont dans leur propre distribution en plus : c'est ce qu'on appelle les modules dual-lived. Ils ont une double vie, à la fois dans le core de Perl et sur CPAN.
Re-calculons ces chiffres en excluant les modules du core (i.e. les
modules livrés avec les sources de Perl). Même s'il existe des mises à
jour de ces modules sur CPAN, nous n'en tiendrons pas compte dans nos
calculs.
Le module Module::CoreList
tient à jour la liste de tous les modules
fournis directement avec Perl, pour chaque version officielle de Perl.
use strict; use warnings; use Module::CoreList; my $details_file = shift || "$ENV{HOME}/.cpan/sources/modules/02packages.details.txt.gz"; open my $fh, "gzip -dc $details_file |" or die "Impossible d'ouvrir $details_file avec gzip: $!"; my %count; while (<$fh>) { next if 1 .. /^$/; # ignore l'en-tête du fichier chomp; my ( $module, $version, $distro ) = split ' '; next if Module::CoreList->first_release($module); $count{distro}{$distro}++; $count{module}{$module}++; } printf "%d modules non-core dans %d distributions sur CPAN\n", scalar( keys %{ $count{module} }), scalar keys %{ $count{distro} };
Ce qui nous donne :
49779 modules non-core dans 14159 distributions sur CPAN
Donc 99% de CPAN n'est pas lié au core de Perl... Ça en fait, des modules originaux !
Maintenant que vous avez installé quelques un des poids lourds de CPAN (comme Catalyst, Jifty, PPI) et fait quelques blagues sur les multiples moitiés de CPAN que vous avez dû installer, la question finit par vraiment vous tracasser : combien de modules (ou plutôt de distributions) CPAN sont installés chez vous ?
Quand on charge un module Perl à l'aide de use
et require
, perl parcours
les répertoires listés dans le tableau @INC
et cherche les fichiers
dont le nom est obtenu en remplaçant les ::
par des /
et en
ajoutant le suffixe .pm
à la fin.
Nous allons parcourir @INC
à la recherche de fichiers .pm pour
produire la liste des modules installés. Nous allons ensuite supprimer
de cette liste les modules du core (à l'aide de Module::CoreList
).
Enfin, à l'aide du contenu de 02packages.details.txt.gz, nous construisons un hash avec trois clés :
qui pointe vers un hash indexé par les noms des modules
qui pointe vers un hash indexé par les noms des distributions
qui pointe vers un hash faisant l'association entre un module et la distribution auquel il appartient.
À l'aide de ce hash et de la liste des modules non-core installés, nous pouvons faire nos petites statistiques.
#!/usr/bin/env perl use strict; use warnings; use File::Spec; use File::Find; use Module::CoreList; # non core # charge le hash des details (cf. routine à la fin) my $details_file = shift || "$ENV{HOME}/.cpan/sources/modules/02packages.details.txt.gz"; # construit le hash des relations module-distributions open my $fh, "gzip -dc $details_file |" or die "Impossible d'ouvrir $details_file avec gzip: $!"; my $details = {}; while (<$fh>) { next if 1 .. /^$/; # ignore l'en-tête du fichier chomp; my ( $module, $version, $distro ) = split ' '; next if Module::CoreList->first_release($module); $details->{module}{$module}++; $details->{distro}{$distro}++; $details->{distro_of}{$module} = $distro; } close $fh; # ne garde que les chemins absolus de @INC qui existent @INC = grep { -e && File::Spec->file_name_is_absolute($_) } @INC; # construit une expression régulière pour supprimer le chemin my $INC = qr/^(@{[join '|', map { quotemeta } reverse sort @INC ]})/; # récupère les modules non-core installés my %modules; find( sub { # ne garde que les fichiers .pm return if !-f || !/\.pm$/; # supprime le début du chemin $File::Find::name =~ /$INC/; my $path = File::Spec->abs2rel( $File::Find::name, $1 ); $path =~ s/\.pm$//; # construit le nom du module my $module = join '::', File::Spec->splitdir($path); # supprime les modules du core return if Module::CoreList->first_release($module); # ce module nous intéresse # (mais ne tenons pas compte des installations multiples) $modules{$module}++; }, @INC ); # calcul des statistiques printf "%d modules installés\n", scalar keys %modules; my @cpan = grep { exists $details->{module}{$_} } keys %modules; printf " dont %d de CPAN (%.2f%% de CPAN)\n", scalar @cpan, 100 * @cpan / keys %{ $details->{module} }; my %distro; $distro{ $details->{distro_of}{$_} }++ for @cpan; printf " soit %d distributions (%.2f%% de CPAN)\n", scalar keys %distro, 100 * keys(%distro) / keys %{ $details->{distro} };
Sébastien Aperghis-Tramoni installe un nombre non négligeable de modules CPAN afin de les découvrir et de les avoir à portée de main. Ses installations de Perl ressemblent à ceci :
5783 modules installés dont 5362 de CPAN (10.76% de CPAN) soit 793 distributions (5.59% de CPAN) 12578 modules installés dont 11629 de CPAN (23.33% de CPAN) soit 2251 distributions (15.88% de CPAN)
Ma propre installation de CPAN donne les résultats suivants :
3204 modules installés dont 2147 de CPAN (4.31% de CPAN) soit 185 distributions (1.31% de CPAN)
En bref, CPAN est tellement énorme que l'expression « la moitié de
CPAN » relève plus du mythe que de la réalité. :-)
Envoyez vos perles à perles@mongueurs.net
, elles seront peut-être
publiées dans un prochain numéro de Linux Magazine.
Copyright © Les Mongueurs de Perl, 2001-2011
pour le site.
Les auteurs conservent le copyright de leurs articles.