Некоторые сведения о Perl 5/CPAN
CPAN
Изначально Perl поставляется с небольшим набором модулей[1]. Хотя этого набора достаточно, чтобы написать любую программу, зачастую оказывается, что ваша задача уже когда-либо, кем-либо решалась. Быть может существующее решение помогло бы вам сэкономить кучу времени. Именно это является основной целью CPAN — помочь программистам найти модули и программы и установить их в целевой дистрибутив Perl.
Собственно CPAN (англ. Comprehensive Perl Archive Network — Всеобъемлющая сеть архивов Perl) является децентрализованным хранилищем модулей Perl. Технически она представляет собой сеть из репозиториев, хранящих архивы, к которым клиенты подключаются либо по FTP, либо по HTTP протоколу. Собственно протокол не играет роли, но репозиторий должен быть организован особым образом. Это требуется для того, чтобы можно было наладить версионирование и индексацию модулей.
Для подключения к этой сети написано некоторое количество клиентских утилит по типу CPAN.pm, CPAN++ и cpanminus. Эти утилиты не просто скачивают и распаковывают архивы с модулями, но и делают некоторые проверки, в частности они могут построить дерево зависимостей и установить эти зависимости вместе с заказываемым модулем, другими словами, это полноценный менеджер пакетов.
CPAN возник на заре развития сети Интернет и до сих пор следует своей идеологии, которая выражается в использовании согласованных пространств имен, режима тестирования модулей и четко определенного стиля документации.
Структура CPAN
[править]Файлы CPAN называются дистрибутивами. Один дистрибутив может состоять из одного или нескольких модулей, документации, либо исполняемых файлов сценариев, упакованных в общий формат архивации (обычно это GZIP). Каждый дистрибутив обычно содержит сценарий установки (Makefile.PL или Build.PL) и тестовые сценарии. Сценарий установки выясняет параметры системы, на основе которых формирует Makefile[2] утилиты make, по которой модуль может быть собран (если в нем есть задачи для компиляции), а затем собранные артефакты могут быть распространены по общеизвестным путям системы. Тестовые сценарии призваны понять, могут ли быть части модулей исполнены на целевой системе без ошибок по ABI.
Так как CPAN хранит тысячи дистрибутивов, разработчики стараются занимать пространства имен модулей Perl с естественной иерархией, которые подсказывают область их применения. Например, пространство Net начинает любой модуль, который как-то связан с сетью. Сравните:
Net::SSL— поддержка Secure Sockets Layer;Net::DNS— набор модулей для работы со службой DNS;Net::HTTP::Methods— подмодуль, который облегчает работу с методами протокола HTTP.
Название дистрибутива формируется из имени основного пространства имен, где символы :: заменяются на символ тире - с добавлением номера версии дистрибутива (например, CGI-Application-3.1), но на самом деле это только соглашение. Важно чтобы имя архива было уникальным. По этой причине новые имена дистрибутивов формируются благодаря номеру версии, который и создает уникальность.
Сеть зеркал CPAN
[править]Как уже было сказано ранее, CPAN это сеть из сетевых репозиториев, каждый из которых зеркалирует некоторого «соседа». На полноценном зеркале хранится около 36 ГБ данных в виде упакованных дистрибутивов. Большинство зеркал нацелены на центральный репозиторий, сайт которого расположен по адресу www.cpan.org, однако, существуют крупные FTP-серверы, на которых размещаются программы, которых нет в центральном репозитории. Некоторые компании могут создавать закрытые репозитории. Для поиска модулей в центральном хранилище используется сайт metacpan.org.
Группа разработчиков-добровольцев тестирует дистрибутивы по мере загрузки их на CPAN, после чего составляются отчеты тестирования, которые обобщаются и используются на различных веб-сайтах.
Для загрузки дистрибутива на CPAN разработчики используют сервер с интересным названием PAUSE (англ. Perl Authors Upload Server) — pause.perl.org. Для загрузки модуля разработчик должен создать учетную запись на сервере и с этого момента за автором будет закрепляться имя модуля, если такого ранее не существовало на CPAN.
Способы установки дистрибутивов CPAN
[править]Проверка существования модуля в системе
[править]Перед установкой модуля следует удостовериться, что его нет в системе, а если он есть, то какой он версии. Не существует какого-то стандартного метода узнать, какие модули установлены в системе. Ниже перечислены некоторые способы получения такой информации.
Если вы знаете пакет модуля, то самое простое с чего можно начать — попробовать написать однострочник
$ perl -e 'use <пакет-модуля>'
Если модуль установлен, то он успешно будет импортирован и программа завершится успешно, иначе будет выведена ошибка директивы use. Еще можно попытаться открыть документацию к модулю
$ perldoc <пакет-модуля>
Дистрибутив Perl для Windows (ActivePerl) имеет свой менеджер пакетов PPM, у которого есть команды для работы с модулями. Во всех остальных случаях можно воспользоваться утилитами pmtools, но, к сожалению, они не идут в стандартной поставке дистрибутива Perl и их требуется устанавливать отдельно (опять проблема курицы и яйца).
Следующие простые программы помогут без сторонних решений выведать некоторую информацию об установке Perl в вашей системе.
Если Perl установлен с документацией, то обычно в её составе всегда есть страница perlmodlib.pod, в которой перечислен список стандартных модулей дистрибутива. Вы можете её распарсить для вывода по крайней мере стандартных модулей
# list-standard-mods.pl
use Data::Dumper;
$Data::Dumper::Terse = 1;
my %modList;
sub listStandardModules {
my($module) = @_;
unless (keys %modList) {
chomp(my $perlmodlib = `perldoc -l perlmodlib`);
die "cannot locate perlmodlib\n" unless $perlmodlib;
open my $fh, "<", $perlmodlib
or die "$0: open $perlmodlib: $!\n";
while (<$fh>) {
next unless /^=head\d\s+Pragmatic\s+Modules/ ..
/^=head\d\s+CPAN/;
if (/^=item\s+(\w+(::\w+)*)/) {
++$modList{ lc $1 };
}
}
}
}
&listStandardModules;
print Dumper \%modList;
$ perl ./list-standard-mods.pl
{
'json::pp' => 1,
'net::servent' => 1,
'extutils::parsexs' => 1,
'extutils::constant::utils' => 1,
'cpan::meta::history::meta_1_0' => 1,
'test2::event' => 1,
'math::trig' => 1,
'extutils::embed' => 1,
..................
}
Ещё можно воспользоваться следующим «дедовским» сценарием[3], который позволяет распарсить всю POD-документацию и файлы *.pm из каталогов, записанных в @INC, либо переданных командной строкой:
#!/usr/bin/perl
# Файл: pmdesc.pl
use strict;
use warnings;
use File::Find;
use Getopt::Long;
use Carp;
our (
$OPT_v,
$OPT_w,
$OPT_a,
$OPT_s,
$START_DIR,
%FUTURE
);
GetOptions(
"v|verbose" => \$OPT_v,
"w|warnings" => \$OPT_w,
"a|prints-relative-path" => \$OPT_a,
"s|sort" => \$OPT_s,
) or die "Bad usage";
@ARGV = @INC unless @ARGV;
select((select(STDOUT),
$| = 1,
)[0]);
if ($OPT_s) {
if (open (ME, "-|")) {
$/ = '';
while (<ME>) {
chomp;
print join ("\n", sort split /\n/), "\n";
}
exit;
}
}
MAIN: {
my %visited;
my ($dev, $ino);
@FUTURE{@ARGV} = (1) x @ARGV;
foreach $START_DIR (@ARGV) {
delete $FUTURE{$START_DIR};
print "\n<<Modules from $START_DIR>>\n\n" if $OPT_v;
next unless ($dev, $ino) = stat ($START_DIR);
next if $visited{"$dev,$ino"}++;
next unless $OPT_a || $START_DIR =~ m!^/!;
find (\&wanted, $START_DIR);
}
exit;
}
sub modname {
local $_ = $File::Find::name;
if (index($_, $START_DIR . "/") == 0) {
substr($_, 0, 1 + length($START_DIR)) = '';
}
s { / }{::}gx;
s { \.p(m|od)$ }{}x;
return $_;
}
sub wanted {
if ($FUTURE{$File::Find::name}) {
warn "\t(Skipping $File::Find::name, qui venit in futuro.)\n" if 1 and $OPT_v;
$File::Find::prune = 1;
return;
}
return unless /\.pm$/ && -f;
my $Module = &modname;
if ($Module =~ /^CPAN(\Z|::)/) {
warn "$Module -- skipping because it misbehaves\n";
return;
}
my $file = $_;
unless (open (POD, "< $file")) {
warn "\tcannot open $file: $!";
return 0;
}
$: = " -:";
local $/ = '';
POD_READING : {
local $_;
while (<POD>) {
if (/=head\d\s+NAME/) {
chomp($_ = <POD>);
s/^.*?-\s+//s;
s/\n/ /g;
if (defined (my $v = getversion($Module))) {
print "$Module ($v) ";
} else {
print "$Module ";
}
print "- $_\n";
return 1;
}
}
}
warn "\t(MISSING DESC FOR $File::Find::name)\n" if $OPT_w;
return 0;
}
sub getversion {
my $mod = shift;
my $vers = `$^X -m$mod -e 'print \$${mod}::VERSION' 2>/dev/null`;
$vers =~ s/^\s*(.*?)\s*$/$1/;
return ($vers || undef);
}
Сценарий примечателен тем, что он использует только встроенные модули, поэтому без проблем будет запускаться в любых дистрибутивах Perl. Сценарий печатает имя пакета модуля, его версию и строчку раздела NAME из POD документации, если она есть.
# Пример вызова (опция -s сортирует модули по алфавиту):
$ ./pmdesc.pl -s
# Процедура может быть достаточно затяжной, но не подумайте - скрипт не завис.
CPAN -- skipping because it misbehaves
AnyDBM_File (1.01) - provide framework for multiple DBMs
App::Cpan - easily interact with CPAN from the command line
App::Prove - Implements the C<prove> command.
App::Prove::State - State storage for the C<prove> command.
App::Prove::State::Result (3.44) - Individual test suite results.
App::Prove::State::Result::Test (3.44) - Individual test results.
Archive::Tar (2.40) - module for manipulations of tar archives
Archive::Tar::File (2.40) - a subclass for in-memory extracted file from Archive::Tar
Attribute::Handlers (1.03) - Simpler definition of attribute handlers
Authen::SASL::Perl::ANONYMOUS (2.1700) - Anonymous Authentication class
Authen::SASL::Perl::CRAM_MD5 - CRAM MD5 Authentication class
Authen::SASL::Perl::DIGEST_MD5 - Digest MD5 Authentication class
Authen::SASL::Perl::EXTERNAL (2.1700) - External Authentication class
Authen::SASL::Perl::GSSAPI - GSSAPI (Kerberosv5) Authentication class
Authen::SASL::Perl::LOGIN (2.1700) - Login Authentication class
Authen::SASL::Perl::PLAIN (2.1700) - Plain Login Authentication class
AutoLoader (5.74) - load subroutines only on demand
AutoSplit (1.06) - split a package for autoloading
<---- вывод обрезан ---->
Если вы работаете в командной оболочке *nix системы, то можно узнать какие *.pm файлы существуют в общеизвестных путях с помощью команды find
$ find $(perl -e 'print "@INC"') -type f -name "*.pm" -print
Другие методы вращаются вокруг утилит работы с CPAN, так как они всегда делают проверки общеизвестных путей перед работой с модулями.
Устройство дистрибутива
[править]Всё, что касается устройства дистрибутивов Perl, на момент 2025 года стабильно находится в том же состоянии, что и примерно 30 лет назад. В лучших традициях TMTOWTDI в Perl существует несколько разных систем сборки и ни одна из них не является рекомендуемой кем-либо. В общем случае следует исходить из популярности того или иного способа.
В этой книге мы будем опираться на документ perlnewmod, который описывает самые базовые принципы создания модулей и сборки дистрибутивов. Так как Perl в основном использовался Unix сообществом, используемые подходы также пользуются инструментами Unix. Для исполнения графа сборки используется классическая утилита make; для упаковки — tar; для распространения по сети используется простая сетевая передача, а для установки в систему — простая распаковка и копирование файлов. В системе, отличной от Unix, инструменты могут быть другими, но суть в шагах останется такой же.
Если идти по классическому пути, получается как то так.
- Сначала разработчик генерирует каркас дистрибутива (т.н. boilerplate):
$ h2xs -AX --skip-exporter --use-new-tests -n Acme::HelloWorld # A - не генерирует код автозагрузки; # X - не генерирует XS-код; # n - задает имя модуля; # --skip-exporter - не генерирует код модуля Exporter; # --use-new-tests - генерирует модульные тесты для фреймворка Test::More. Defaulting to backwards compatibility with perl 5.40.0 If you intend this module to be compatible with earlier perl versions, please specify a minimum perl version with the -b option. Writing Acme-HelloWorld/lib/Acme/HelloWorld.pm Writing Acme-HelloWorld/Makefile.PL Writing Acme-HelloWorld/README Writing Acme-HelloWorld/t/Acme-HelloWorld.t Writing Acme-HelloWorld/Changes Writing Acme-HelloWorld/MANIFEST
- Затем разработчик пишет код. Обратите внимание, что код вашего модуля должен быть, в данном примере, в пределах директории
./lib/Acme, которая задает пространство имен для модуля. Попутно следует покрывать код модульными тестами, которые находятся в директории./t. Код модульных тестов может зависеть от используемого фреймворка (в данном случае используется Test::More). - Когда код достигает той стадии, что его нужно передавать другой стороне (например, тестировщикам или заказчикам), обычно подготавливается документация, а также вносятся правки в файлы
README,ChangesиMANIFEST. Также корректируется файл генерации сборочного файлаMakefile.PL(в данном случае используется ExtUtils::MakeMaker). Очень важно перед отправкой правильно выставить номер версии дистрибутива, так как он должен быть уникальным для каждого нового релиза. В основном в Perl используются две системы версионирования: более старая с десятичным числом (например,5.040) и более новая, в которой используется букваv(например,v5.40.0). Чтобы подчеркнуть, что это альфа-сборка (используемая разработчиками) обычно приписывают через нижнее подчеркивание номер сборки (5.040_01илиv5.40.0_1). Учтите, что обычно генераторы сборочного сценария берут версию модуля из исходного файла, где номер версии может быть указан по-разному (в зависимости от минимальной поддерживаемой версии интерпретатора):# Начиная с версии Perl 5.10 каждый модуль автоматически включает объект версии. # Чтобы назначить модулю версию, необходимо определить глобальную переменную our $VERSION = '0.09'; # или our $VERSION = 'v0.9.0'; # Для старых модулей, чтобы обеспечить совместимость с версией 5.10, использовалась директива use version 0.77; # но сейчас это скорее всего не актуально, потому что кто будет поддерживать настолько старый # интерпретатор. # С версии 5.12 допустимо версию объявлять рядом с директивой пространства имен. package MyModule v0.9.0;
- Затем нужно упаковать код в дистрибутивный пакет. Обычно это делается такой командой:
$ perl Makefile.PL && make test && make distcheck && make dist
- Затем полученный архив передается в некоторое сетевое хранилище, откуда его смогут запросить по сети, либо он передается по почте. Если получатель использует менеджер пакетов, то конкретный набор команд может зависеть от него. Если пакет устанавливается вручную, то получатель обычно распаковывает исходные коды и собирает модуль так, как это делал разработчик.
Хотя последовательность шагов кажется простой и понятной, сложности начинаются с момента, когда в вашем приложении появляются внешние зависимости. Здесь возникает вопрос, как сказать пользователю, какие модули он должен догрузить и установить, чтобы на его системе заработал ваш модуль.
В этот момент появляется множество подходов. Самое простое — это поставлять с дистрибутивом список внешних зависимостей. Это может быть список или структура. В некоторых менеджерах пакетов предусмотрен файл с таким списком, по которому они делает дозагрузку недостающих модулей.
Вторая сложность заключается в том, что ваш модуль может быть комплексным: он может использовать несколько технологий, которые могут требовать вызова различных препроцессоров, генераторов или компиляторов. Это уже выходит за рамки стандартного make-файла, и вам придется как то дописывать или интегрировать дополнительные операции в исходный сборочный сценарий. В любом случае, такой ситуации нужно избегать как можно дольше.
Ручная установка CPAN дистрибутивов
[править]Ручной установкой лучше пользоваться только в очень крайнем случае, например, если у вас нет прямого доступа к CPAN, так как это очень однообразный и сопряженный с трудностями процесс, особенно если у модуля очень много зависимостей. Мы рассмотрим классическую установку в *nix системе.
Для примера мы возьмем дистрибутив автора Ingy döt Net, который позволяет парсить YAML. На момент написания этой страницы дистрибутив имеет версию YAML-1.31. Этот дистрибутив был выбран только потому, что он не имеет зависимостей и написан только на Perl[4], что позволяет установить его в любой системе.
- Перейдите на страницу модуля в Metacpan — https://metacpan.org/dist/YAML/view/lib/YAML.pod.
- В левом меню вы сможете найти ссылку на его загрузку — https://cpan.metacpan.org/authors/id/I/IN/INGY/YAML-1.31.tar.gz.
- Распакуйте архив дистрибутива любым доступным вам способом. В этом примере мы воспользуемся утилитой
tar:tar xzvf YAML-1.31.tar.gz && cd YAML-1.31/
- Если посмотреть файлы дистрибутивато среди них можно найти
ls Changes CONTRIBUTING lib LICENSE Makefile.PL MANIFEST META.json META.yml README t xt
Makefile.PL, который хранит программу для стандартной утилитыExtUtils::MakeMaker. С помощью этой утилиты можно сгенерировать сборочный сценарий утилитыmake. Чтобы сгенерировать сборочный сценарий, нужно запуститьMakefile.PLследующим образомperl Makefile.PL - Если никаких серьезных проблем
ExtUtils::MakeMakerне выявит, то появится новый файлMakefile. Одной из проблем, которая может возникать во время установки модулей, это отсутствие нужного фреймворка тестирования. В данном примере дистрибутив использует модульTest::YAML, с помощью которого реализуются тесты всей спецификации YAML. В нашем случае это не критично, поэтому мы можем опустить тестирование. Для сборки модуля нужно вызвать утилитуmake. При этом важно, чтобы рабочей директорией была директория с файломMakefile: это позволит вам не передавать его утилите сборки аргументом.Данный модуль не требует какой-либо серьезной сборки, но в общем случае на этом этапе могут вызываться сторонние компиляторы и препроцессоры.make - Еще раз отметим, что если бы модуль был чуть сложнее, следовало бы запустить тесты командойно, к сожалению, тесты скорее всего не запустятся, потому что требуется установка фреймворка, что является зависимостью тестирования.
make test
- Для установки дистрибутива в систему, нужно вызвать цель
install. Так как установка потребует копирования в некоторые системные директории, могут понадобится права пользователяrootsudo make install
- Если вы введетето увидите, что документация была интегрирована в дистрибутив Perl.
perldoc YAML
Давайте напишем простую программу с этим модулем. Для этого сгенерируем YAML структуру
$ cat > /tmp/file.yml <<EOF
---
person:
name: Larry
surname: Wall
EOF
#!/usr/bin/perl
# yaml-printer.pl
use YAML qw { LoadFile };
use Data::Dumper;
$Data::Dumper::Terse = 1;
my $file = $ARGV[0];
my $hashref = LoadFile($file) or die "$!";
print Dumper $hashref;
$ perl ./yaml-printer.pl /tmp/file.yml
{
'person' => {
'surname' => 'Wall',
'name' => 'Larry'
}
}
У ручного метода есть ряд больших недостатков:
- Если модуль имеет зависимости, то необходимо установить все зависимости похожим образом, а также нужно убедиться, что установлены зависимости зависимостей. При этом каждая зависимость должна корректно быть установлена в системе, иначе нормальная работа модуля может быть невозможной.
- Удаление или обновление модулей вручную также могут быть сопряжено с трудностями.
- Обновленные версии модуля могут потребовать и обновление зависимостей, а также зависимости зависимостей.
Таким образом, ручная установка подходит в ситуациях, когда ваша система уже подготовлена, либо если модуль не очень сложный.
Установка дистрибутивов через менеджер пакетов
[править]CPAN.pm
[править]Модуль CPAN.pm представляет собой одновременно и библиотеку и клиент для работы со CPAN. Чтобы его не путать ни с чем другим, обычно к его имени приписывается расширение, когда имеют в виду именно модуль.
Этот модуль в большинстве ситуаций есть в дистрибутиве Perl[5]. Данный модуль может быть вызван либо через утилиту cpan, которая является его оболочкой, либо вы можете вызвать его такой командой:
$ perl -MCPAN -e shell
# либо
$ cpan
# Оба варианта без аргументов дадут одинаковый результат.
Если CPAN.pm был вызван без аргументов, то он запускается в интерактивном режиме. Чтобы узнать какие команды вам доступны в этом режиме, введите команду h.
cpan[1]> h
Display Information (ver 2.27)
command argument description
a,b,d,m WORD or /REGEXP/ about authors, bundles, distributions, modules
i WORD or /REGEXP/ about any of the above
ls AUTHOR or GLOB about files in the author's directory
(with WORD being a module, bundle or author name or a distribution
name of the form AUTHOR/DISTRIBUTION)
Download, Test, Make, Install...
get download clean make clean
make make (implies get) look open subshell in dist directory
test make test (implies make) readme display these README files
install make install (implies test) perldoc display POD documentation
Upgrade installed modules
r WORDs or /REGEXP/ or NONE report updates for some/matching/all
upgrade WORDs or /REGEXP/ or NONE upgrade some/matching/all modules
Pragmas
force CMD try hard to do command fforce CMD try harder
notest CMD skip testing
Other
h,? display this menu ! perl-code eval a perl command
o conf [opt] set and query options q quit the cpan shell
reload cpan load CPAN.pm again reload index load newer indices
autobundle Snapshot recent latest CPAN uploads
Вы можете видеть, что команды разделены на 4 категории плюс директивные опции:
- Команды, которые выводят справочную информацию;
- Устанавливающие команды;
- Обновляющие команды;
- Команды, которые управляют базой данных
CPAN.pm.
Давайте попробуем поработать со CPAN.pm. Установим модуль Text::Glob, который позволяет разрешать Glob-символы, в том числе и в оболочке CPAN.pm:
cpan[2]> install Text::Glob Reading '/home/john/.cpan/Metadata' Database was generated on Mon, 04 Aug 2025 08:17:01 GMT Running install for module 'Text::Glob' Fetching with LWP: http://www.cpan.org/authors/id/R/RC/RCLAMP/Text-Glob-0.11.tar.gz Fetching with LWP: HASH(0x564e23814490)authors/id/R/RC/RCLAMP/CHECKSUMS Fetching with LWP: HASH(0x564e23814490)authors/id/R/RC/RCLAMP/CHECKSUMS.gz Fetching with LWP: http://www.cpan.org/authors/id/R/RC/RCLAMP/CHECKSUMS Checksum for /home/john/.cpan/sources/authors/id/R/RC/RCLAMP/Text-Glob-0.11.tar.gz ok Scanning cache /home/john/.cpan/build for sizes ............................................................................DONE Configuring R/RC/RCLAMP/Text-Glob-0.11.tar.gz with Makefile.PL Checking if your kit is complete... Looks good Generating a Unix-style Makefile Writing Makefile for Text::Glob Writing MYMETA.yml and MYMETA.json RCLAMP/Text-Glob-0.11.tar.gz /usr/bin/perl Makefile.PL INSTALLDIRS=site -- OK Running make for R/RC/RCLAMP/Text-Glob-0.11.tar.gz cp lib/Text/Glob.pm blib/lib/Text/Glob.pm Manifying 1 pod document RCLAMP/Text-Glob-0.11.tar.gz /usr/bin/make -- OK The current configuration of allow_installing_outdated_dists is 'ask/no', but for this option we would need 'CPAN::DistnameInfo' installed. Please install 'CPAN::DistnameInfo' as soon as possible. As long as we are not equipped with 'CPAN::DistnameInfo' this option does not take effect Running make test for RCLAMP/Text-Glob-0.11.tar.gz PERL_DL_NONLAZY=1 "/usr/bin/perl" "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t t/Text-Glob.t ...... ok t/Text-Glob_Sep.t .. ok All tests successful. Files=2, Tests=74, 0 wallclock secs ( 0.01 usr 0.00 sys + 0.06 cusr 0.00 csys = 0.07 CPU) Result: PASS Lockfile removed. RCLAMP/Text-Glob-0.11.tar.gz /usr/bin/make test -- OK Running make install for RCLAMP/Text-Glob-0.11.tar.gz Manifying 1 pod document Installing /usr/local/share/perl/5.32.1/Text/Glob.pm Installing /usr/local/man/man3/Text::Glob.3pm Appending installation info to /usr/local/lib/x86_64-linux-gnu/perl/5.32.1/perllocal.pod RCLAMP/Text-Glob-0.11.tar.gz sudo /usr/bin/make install -- OK
Выше вы может видеть, что команда install выполняет все операции, которые мы ранее выполняли вручную, по порядку, а именно: скачивает дистрибутив из сети, распаковывает, собирает, тестирует и наконец устанавливает в систему. CPAN.pm позволяет вам выполнять не все этапы этого конвейера, а только некоторые из них, другими словами, команда в директивной форме задает цель. Вы могли бы, скажем, просто скачать дистрибутив(ы) (команда get) или только запустить тесты (команда test). Если во время команды вам не хватает каких-то пакетов, то CPAN.pm выгрузит их автоматически.
Модуль CPAN.pm кэширует результаты и сохраняет артефакты в каталоге пользователя. Например, выше это был каталог /home/john/.cpan. Не слишком увлекайтесь загрузкой модулей, так как вы можете быстро исчерпать дисковое пространство.
Теперь попробуем узнать модули, которые старее загруженных на CPAN. Для этого мы можем использовать команду r (от report). Эта команда покажет версии установленных модулей и тех, что хранится на CPAN:
cpan[3]> r Package namespace installed latest in CPAN file Archive::Tar 2.36 3.04 BINGOS/Archive-Tar-3.04.tar.gz Authen::SASL 2.16 2.1800 EHUELS/Authen-SASL-2.1800.tar.gz CGI 4.51 4.70 LEEJO/CGI-4.70.tar.gz CGI::Fast 2.15 2.17 LEEJO/CGI-Fast-2.17.tar.gz CPAN 2.27 2.38 ANDK/CPAN-2.38.tar.gz CPAN::Meta::Requirements 2.140 2.143 RJBS/CPAN-Meta-Requirements-2.143.tar.gz CPAN::Meta::YAML 0.018 0.020 ETHER/CPAN-Meta-YAML-0.020.tar.gz CPAN::Mini 1.111016 1.111017 RJBS/CPAN-Mini-1.111017.tar.gz Clone 0.45 0.47 ATOOMIC/Clone-0.47.tar.gz ............................... LWP 6.52 6.79 OALDERS/libwww-perl-6.79.tar.gz List::Util 1.55 1.70 PEVANS/Scalar-List-Utils-1.70.tar.gz Net::Cmd 3.11 3.15 SHAY/libnet-3.15.tar.gz Pod::Man 4.14 v6.0.2 RRA/podlators-v6.0.2.tar.gz Text::Tabs 2013.0523 2024.001 ARISTOTLE/Text-Tabs+Wrap-2024.001.tar.gz 78 installed modules have no parsable version number (use 'o conf show_unparsable_versions 1' to show them)
CPAN.pm пытается во многом помогать пользователю. Например, обратите внимание, что некоторые установленные модули имеют номер версии, который не может быть определен текущим методом (78 installed modules have no parsable version number). Такое может быть, когда модуль очень старый и не следует принятым соглашениям, либо модули были установлены в обход менеджера. Тем не менее, вы можете управлять работой модуля CPAN.pm через его многочисленные опции. Для получения и установки опции служит команда o. Попробуем установить опцию show_unparsable_versions, как на нам рекомендует модуль, чтобы вывести модули, чьи версии не парсятся:
cpan[4]> o conf show_unparsable_versions 1
show_unparsable_versions [1]
Please use 'o conf commit' to make the config permanent!
Опция будет установлена только для текущего сеанса. Чтобы установить её перманентно, нужно использовать команду o conf commit. Теперь снова выведем версии установленных модулей
cpan[5]> r
.............................................................................
Pod::Man 4.14 v6.0.2 RRA/podlators-v6.0.2.tar.gz
Text::Tabs 2013.0523 2024.001 ARISTOTLE/Text-Tabs+Wrap-2024.001.tar.gz
78 installed modules have no parsable version number
they are
Module = CGI::HTML::Functions (LEEJO/CGI-4.70.tar.gz)
Module = Data::Dump::FilterContext (GARU/Data-Dump-1.25.tar.gz)
Module = Data::Dump::Filtered (GARU/Data-Dump-1.25.tar.gz)
Module = File::FcntlLock::Errors (JTT/File-FcntlLock-0.22.tar.gz)
Module = File::FcntlLock::XS (JTT/File-FcntlLock-0.22.tar.gz)
Module = Font::Metrics::Courier (GAAS/Font-AFM-1.20.tar.gz)
Module = Font::Metrics::CourierBold (GAAS/Font-AFM-1.20.tar.gz)
Module = Font::Metrics::CourierBoldOblique (GAAS/Font-AFM-1.20.tar.gz)
...................................................
Module = XML::XPathEngine::Root (MIROD/XML-XPathEngine-0.14.tar.gz)
Module = XML::XPathEngine::Step (MIROD/XML-XPathEngine-0.14.tar.gz)
Module = XML::XPathEngine::Variable (MIROD/XML-XPathEngine-0.14.tar.gz)
Module = YAML::Dumper (INGY/YAML-1.31.tar.gz)
Module = YAML::Dumper::Base (INGY/YAML-1.31.tar.gz)
Module = YAML::Error (INGY/YAML-1.31.tar.gz)
Module = YAML::Loader (INGY/YAML-1.31.tar.gz)
Module = YAML::Loader::Base (INGY/YAML-1.31.tar.gz)
Module = YAML::Marshall (INGY/YAML-1.31.tar.gz)
Module = YAML::Mo (INGY/YAML-1.31.tar.gz)
Module = YAML::Node (INGY/YAML-1.31.tar.gz)
Module = YAML::Tag (INGY/YAML-1.31.tar.gz)
Module = YAML::Types (INGY/YAML-1.31.tar.gz)
Module = lib::core::only (HAARG/local-lib-2.000029.tar.gz)
Module = overload::numbers (SHAY/perl-5.40.3.tar.gz)
На распечатке вывода выше вы могли заметить, что на CPAN есть версии пакетов более новые, чем установлены в нашем дистрибутиве. Например, попробуем обновить Archive::Tar до версии 3.04:
cpan[6]> upgrade Archive::Tar
Package namespace installed latest in CPAN file
Archive::Tar 2.36 3.04 BINGOS/Archive-Tar-3.04.tar.gz
Running install for module 'Archive::Tar'
Fetching with LWP:
http://www.cpan.org/authors/id/B/BI/BINGOS/Archive-Tar-3.04.tar.gz
Fetching with LWP:
HASH(0x5587b37fb750)authors/id/B/BI/BINGOS/CHECKSUMS
Fetching with LWP:
HASH(0x5587b37fb750)authors/id/B/BI/BINGOS/CHECKSUMS.gz
Fetching with LWP:
http://www.cpan.org/authors/id/B/BI/BINGOS/CHECKSUMS
Checksum for /home/john/.cpan/sources/authors/id/B/BI/BINGOS/Archive-Tar-3.04.tar.gz ok
Scanning cache /home/john/.cpan/build for sizes
............................................................................DONE
Configuring B/BI/BINGOS/Archive-Tar-3.04.tar.gz with Makefile.PL
Archive::Tar comes with a utility called 'ptardiff' which lets you run diffs against tar archives.
However, this utility requires you to have Text::Diff installed.
To add Text::Diff as a prerequisite, please supply the '-d' option when invoking this Makefile.PL.
###############################################################
##
## Hi! Your script and sitescript locations are different
##
## As your perl is v5.12.0 or greater the script included
## in this distribution will be installed into sitescript
##
## You might want to check that the following location is
## in your PATH environment variable:
##
## '/usr/local/bin'
##
## Many thanks.
##
###############################################################
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for Archive::Tar
Writing MYMETA.yml and MYMETA.json
BINGOS/Archive-Tar-3.04.tar.gz
/usr/bin/perl Makefile.PL INSTALLDIRS=site -- OK
Running make for B/BI/BINGOS/Archive-Tar-3.04.tar.gz
---- Unsatisfied dependencies detected during ----
---- BINGOS/Archive-Tar-3.04.tar.gz ----
IO::Compress::Xz [requires,optional]
IO::Uncompress::UnXz [requires,optional]
cp lib/Archive/Tar.pm blib/lib/Archive/Tar.pm
cp lib/Archive/Tar/File.pm blib/lib/Archive/Tar/File.pm
cp lib/Archive/Tar/Constant.pm blib/lib/Archive/Tar/Constant.pm
cp bin/ptar blib/script/ptar
"/usr/bin/perl" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/ptar
cp bin/ptardiff blib/script/ptardiff
"/usr/bin/perl" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/ptardiff
cp bin/ptargrep blib/script/ptargrep
"/usr/bin/perl" -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/ptargrep
Manifying 3 pod documents
Manifying 2 pod documents
BINGOS/Archive-Tar-3.04.tar.gz
/usr/bin/make -- OK
The current configuration of allow_installing_outdated_dists is 'ask/no', but for this option we would need 'CPAN::DistnameInfo' installed. Please install 'CPAN::DistnameInfo' as soon as possible. As long as we are not equipped with 'CPAN::DistnameInfo' this option does not take effect
Running make test for BINGOS/Archive-Tar-3.04.tar.gz
PERL_DL_NONLAZY=1 "/usr/bin/perl" "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/01_use.t .............. ok
t/02_methods.t .......... ok
t/03_file.t ............. ok
t/04_resolved_issues.t .. ok
t/05_iter.t ............. ok
t/06_error.t ............ ok
t/07_ptardiff.t ......... skipped: Text::Diff required to test ptardiff
t/08_ptargrep.t ......... ok
t/09_roundtrip.t ........ ok
t/10_ptar.t ............. ok
t/90_symlink.t .......... ok
t/99_pod.t .............. skipped: Test::Pod v0.95 required for testing POD
All tests successful.
Files=12, Tests=1707, 1 wallclock secs ( 0.05 usr 0.03 sys + 1.07 cusr 0.16 csys = 1.31 CPU)
Result: PASS
Warning: Configuration not saved.
Lockfile removed.
BINGOS/Archive-Tar-3.04.tar.gz
/usr/bin/make test -- OK
Running make install for BINGOS/Archive-Tar-3.04.tar.gz
.................................
Данный модуль является примером сложного модуля, который требует компиляции и достаточно серьезного тестирования. После завершения процедуры, можно проверить установку командой
cpan[7]> m Archive::Tar
Module id = Archive::Tar
CPAN_USERID BINGOS (Chris Williams <chris@bingosnet.co.uk>)
CPAN_VERSION 3.04
CPAN_FILE B/BI/BINGOS/Archive-Tar-3.04.tar.gz
UPLOAD_DATE 2025-02-25
MANPAGE Archive::Tar - module for manipulations of tar archives
INST_FILE /usr/local/share/perl/5.32.1/Archive/Tar.pm
INST_VERSION 3.04
Чтобы завершить интерактивный сеанс оболочки, просто введите команду exit.
cpanminus
[править]Если CPAN.pm очень большой и сложный менеджер пакетов, то cpanminus — это небольшая консольная утилита, в которую встроен минимальный набор команд. Её удобно использовать в неинтерактивных сценариях, где участие пользователя минимально, либо в ситуациях, когда есть ограничения по ресурсам.
$ cpanm --help
Usage: cpanm [options] Module [...]
Опции:
-v,--verbose Включает подробный вывод сообщений
-q,--quiet Отключает большую часть выходных данных
--interactive Включает интерактивную настройку (требуется Task:: модулям)
-f,--force Принудительная установка
-n,--notest Не запускать модульные тесты
--test-only Запуск тестов без установки
-S,--sudo Переключиться в sudo режим, когда модуль устанавливается в системные директории
--installdeps Устанавливает зависимости целевого дистрибутива, не собирая их
--showdeps Показать только прямые зависимости
--reinstall Переустановить дистрибутив даже в том случае, когда установлена его последняя версия
--mirror Определяет базовый URL CPAN-сервера (например, http://cpan.cpantesters.org/)
--mirror-only Использовать только файл индекса CPAN-зеркала вместо мета-базы данных
-M,--from Использовать только этот URL в качестве CPAN-зеркала
--prompt Останавливать исполнение с вопросом, когда стадия configure/build/test проваливается
-l,--local-lib Указывает директорию, в которую устанавливаются дистрибутивы
-L,--local-lib-contained Как --local-lib вместе с --self-contained
--self-contained Устанавливает все неосновные модули, даже если они установлены
--auto-cleanup Время хранения файлов в рабочих директориях cpanm в днях. По умолчанию 7 дней
Команды:
--self-upgrade Обновить cpanm
--info Вывести информацию о дистрибутиве на CPAN
--look Скачивает и распаковывает дистрибутив и больше ничего не делает
-U,--uninstall Удаляет модуль
-V,--version Выводит версию cpanm
Примеры:
cpanm Test::More # Установить Test::More
cpanm MIYAGAWA/Plack-0.99_05.tar.gz # Установить по полному пути
cpanm http://example.org/LDS/CGI.pm-3.20.tar.gz # Установить по URL
cpanm ~/dists/MyCompany-Enterprise-1.00.tar.gz # Установить из файла архива дистрибутива
cpanm --interactive Task::Kensho # Интерактивное конфигурирование
cpanm . # Установить все из локальной директории
cpanm --installdeps . # Установить все зависимости для текущей директории
cpanm -L extlib Plack # Установить Plack и все не core-зависимости для extlib
cpanm --mirror http://cpan.cpantesters.org/ DBI # Использовать зеркало с облегченной синхронизацией
cpanm -M https://cpan.metacpan.org App::perlbrew # Использовать зеркало с защищенным соединением
Некоторые опции по умолчанию могут быть установлены в переменной окружения PERL_CPANM_OPT:
export PERL_CPANM_OPT="--prompt --reinstall -l ~/perl --mirror http://cpan.cpantesters.org"
Для более подробной информации используйте man cpanm или perldoc cpanm.
Создание локального сетевого репозитория
[править]В этом разделе мы поставим себе задачу развернуть локальный сетевой репозиторий, из которого мы могли бы выгружать и устанавливать дистрибутивы стандартными менеджерами пакетов типа CPAN.pm. В коммерческой разработке к репозиторию обычно «прикручивают» какой-нибудь сайт для поиска дистрибутивов, но мы не будем рассматривать этого здесь, так как это лишь вопрос удобства. Все это мы будем делать в окружении *nix, однако, с определенными поправками на установку и конфигурирование, все сказанное справедливо и для Windows.
Для решения нашей задачи нужно выполнить два пункта:
- Обеспечить транспорт для доставки дистрибутивов на целевую систему заказчика. Решения здесь могут быть самыми разными, но для примера мы обойдемся простым файловым сервером, работающим поверх HTTP.
- Организовать репозиторий в формате, понятном менеджеру пакетов.
Файловый HTTP сервер
[править]Что касается серверного приложения, то существует огромное количество готовых решений. Для этой задачи автор выбрал сервер Apache 2. Мы опустим шаг установки серверного приложения и запуска его как программы-демона, а начнем сразу с конфигурирования.
Для примера, имя сервера будет book08. Для Apache мы сконфигурируем виртуальный сервер, для чего необходимо в директории /etc/apache2/sites-available создать файл конфигурации для нового виртуального сервера local-cpan.conf:
<VirtualHost *:80>
ServerName book08
ServerAdmin webmaster@localhost
DocumentRoot /usr/src/cpan/sources
Alias /CPAN /usr/src/cpan/sources
<Directory /usr/src/cpan/sources>
Options Indexes FollowSymLinks MultiViews
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/cpan_error.log
CustomLog ${APACHE_LOG_DIR}/cpan_access.log combined
</VirtualHost>
В конфигурации мы объявили виртуальный сервер с именем book08 и указали для него корневую директорию /usr/src/cpan/sources. Разумеется эта директория должна существовать, а пользователь, от которого запускается серверное приложение, должен иметь к ней доступ. Здесь сервер привязывается на все доступные сетевые интерфейсы, а сам сервер работает на стандартном порту 80. Чтобы было понятно, для чего используется файловый сервер, мы навесили на корневую директорию псевдоним, таким образом, полный URL сервера будет http://book08:80/CPAN.
Далее нужно активизировать этот сервер и перезапустить демон apache2:
$ sudo a2ensite local-cpan.conf
...
$ sudo systemctl restart apache2
Убедимся, что сервер заработал, командой curl:
$ curl http://book08/CPAN
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="http://book08/CPAN/">here</a>.</p>
<hr>
<address>Apache/2.4.65 (Debian) Server at book08 Port 80</address>
</body></html>
Хорошим признаком является то, что сервер отвечает. В данном случае он ответил 301 кодом, но только потому, что мы не поставили еще один слеш в конце путевого имени, что и говорится в ответе. Мы можем считать первый шаг выполненным.
Построение репозитория
[править]Репозиторий CPAN не имеет какого-то очень сложного устройства и полностью встраивается в файловую систему. Обычно репозиторий минимально состоит из двух директорий:
authors— собственно в ней хранятся дистрибутивы авторов.id— индекс репозитория. Индекс репозитория состоит из разветвленной древовидной системы директорий. За каждым автором в репозитории закрепляется короткий идентификатор, записываемый в верхнем регистре, например,JDOE. Дистрибутивы определенного автора всегда находятся в каталоге, имя которого совпадает с идентификатором автора. Если вы знакомы с системой сборки Apache Maven, то это по смыслу очень напоминает элемент<group>, определяемый для зависимости. Собственно идентификатор призван создать пространство имен, закрепляемое за каждым уникальным автором, чтобы не было конфликта по именам в разных дистрибутивах. Другими словами, два автора могут иметь дистрибутив с одинаковым именем. Так как, в общем случае, авторов может быть очень много, они не лежат в индексе плоско, а разложены по каталогам по принципу картотеки. Например, если в индексе существует автор с идентификаторомJDOE, то в индексе заранее будет подготовлена такая веткаЭто позволяет реализовать эффективный поиск дистрибутивов через алгоритмы обхода деревьев, зная идентификатор автора.authors |- id |-J |-JD |-JDOE- дистрибутивы — файлы, которые в каталоге автора находятся в сжатом виде. Так как дистрибутивы никак не раскладываются, их имена должны быть уникальными. На практике обычно номер версии дает переменную часть файлу дистрибутива. Кроме файлов дистрибутивов, в этом каталоге находится файл
CHECKSUMS, генерируемый модулем CPAN::Checksums, в котором хранятся рассчитанные для дистрибутивов контрольные суммы. Они обычно используются для проверки целостности архива при доставке по сети.
- дистрибутивы — файлы, которые в каталоге автора находятся в сжатом виде. Так как дистрибутивы никак не раскладываются, их имена должны быть уникальными. На практике обычно номер версии дает переменную часть файлу дистрибутива. Кроме файлов дистрибутивов, в этом каталоге находится файл
modules— вторая по важности директория в репозитории. В ней хранится мета-информация с описанием репозитория.02packages.details.txt.gz— сжатый файл с описанием индекса. Обычно этот файл первым делом выгружают менеджеры пакетов для поиска того или иного дистрибутива. Сам файл имеет примерно такое содержимое:Содержимое файла представляет собой большую таблицу с описанием того, что хранится в репозитории. В полеFile: 02packages.details.txt URL: http://www.perl.com/CPAN/modules/02packages.details.txt Description: Package names found in directory $CPAN/authors/id/ Columns: package name, version, path Intended-For: Automated fetch routines, namespace documentation. Written-By: <программа-обходившая-индекс> Line-Count: <количество-записей> Last-Updated: Sun, 30 Nov 2025 21:08:01 GMT <таблица-со-всеми-дистрибутивами-в-индексе>
Columnsпредоставлены заголовки таблицы, а в полеLine-Count— количество строк в ней.
Так как описание индекса является статической информацией, после добавления нового дистрибутива, его нужно корректировать каждый раз. По этой причине добавление нового дистрибутива в хранилище проходит два этапа:
- Определение места в индексе, перенос дистрибутива в индекс и расчет контрольных сумм.
- Корректировка описания индекса (англ. inject, встраивание).
Для создания индекса в репозитории на CPAN существуют специальные утилиты разной степени сложности. Для нашей небольшой задачи привлекательной является утилита CPAN::Mini::Inject, которая создает минимальный индекс.
У этой утилиты есть 4 команды:
--add— добавляет дистрибутив в пользовательский репозиторий. По сути это индекс без описания. Пользовательский репозиторий не следует путать со CPAN-репозиторием, но в какой-то мере они похожи.--mirror— делает точное зеркало CPAN-репозитория по указанной внешней ссылке в локальном CPAN-репозитории. В этой задаче мы этим пользоваться не будем, но имейте в виду.--update— похожа на--mirror, но дополнительно делает шаг--inject.--inject— добавляет в CPAN-репозиторий дистрибутивы из пользовательского репозитория.
Итак, у нас есть все инструменты. Если у вас не установлен модуль CPAN::Mini::Inject сделайте это с помощью утилиты CPAN.pm или любого другого менеджера пакетов. Для примера мы будем использовать наш шуточный дистрибутив Acme::HelloWorld, который демонстрировался здесь.
- Сначала сконфигурируем
CPAN::Mini::Inject. Создайте файл конфигурацииmcpaniи разместите его в домашней директории пользователя, который будет делать inject:В этой конфигурации$ mkdir $HOME/.mcpani $ vim $HOME/.mcpani/config # # Редактор ... # local: /usr/src/cpan/sources remote: http://cpan.metacpan.org/ repository: /usr/src/repo passive: yes skip_cleanup: no skip_perl: yes trace: yes
local— директория локального CPAN-репозитория для команды--inject;remote— ссылка на удаленный репозиторий для команд--mirrorи--update;repository— директория пользовательского репозитория для команды--add. - Создайте директорию локального репозитория, если её еще нет:
$ mkdir -p /usr/src/repo
- Далее нужно подготовить архив дистрибутива. Предположим, что он уже упакован, тогда мы можем вызвать
mcpaniв режиме--add:Здесь$ mcpani --add --module Acme::HelloWorld --authorid JDOE --file ./Acme-HelloWorld-0.01.tar.gz
--module— имя модуля, по которому он регистрируется в репозитории;--authorid— идентификатор регистрирующего пользователя;--file— файл дистрибутива. Вы также можете указать версию дистрибутива через опцию--modversion, если имя архива какое-то нестандартное, но в данном случае имя стандартное, и версия будет вычислена автоматически. По умолчаниюmcpaniвозьмет все настройки из конфигурации пользователя, поэтому мы ничего больше не указываем. - Предыдущим шагом мы добавили дистрибутив в репозиторий пользователя, но не в CPAN-репозиторий. Для этого мы должны сделать inject:На практике дистрибутивы обычно вставляются в CPAN-репозиторий не по одному, а сразу кучей, так как каждое такое добавление требует перестраивать индекс.
$ mcpani --inject
- Для проверки того, что индекс был обновлен, мы снова можем спросить сервер:
$ curl http://book08/CPAN/modules/02packages.details.txt.gz -o /tmp/index && zcat /tmp/index % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 315 100 315 0 0 307k 0 --:--:-- --:--:-- --:--:-- 307k File: 02packages.details.txt URL: http://www.perl.com/CPAN/modules/02packages.details.txt Description: Package names found in directory $CPAN/authors/id/ Columns: package name, version, path Intended-For: Automated fetch routines, namespace documentation. Written-By: CPAN::Mini::Inject 1.012 Line-Count: 1 Last-Updated: Sun, 30 Nov 2025 21:08:01 GMT Acme::HelloWorld 0.01 J/JD/JDOE/Acme-HelloWorld-0.01.tar.gz
Наш репозиторий готов делиться своим единственным дистрибутивом.
Установка пробного пакета из репозитория
[править]Теперь попробуем установить наш пакет из CPAN-репозитория. Для этого запустим CPAN.pm в интерактивном режиме:
$ perl -MCPAN -e shell
Чтобы можно было пользоваться нашим репозиторием, его нужно добавить в список серверов:
cpan[1]> o conf urllist unshift http://book08/CPAN
Please use 'o conf commit' to make the config permanent!
cpan[2]> o conf urllist
urllist
0 [http://book08/CPAN]
1 [http://www.cpan.org/]
Type 'o conf' to view all configuration itemsОбратите внимание, что мы добавили ссылку только на момент сеанса, а не перманентно. Теперь нам нужно выгрузить индекс с сервера:
cpan[3]> reload index
Reading '/home/john/.cpan/Metadata'
Database was generated on Mon, 01 Dec 2025 07:17:01 GMT
Fetching with LWP:
http://book08/CPAN/authors/01mailrc.txt.gz
Reading '/home/john/.cpan/sources/authors/01mailrc.txt.gz'
............................................................................DONE
Fetching with LWP:
http://book08/CPAN/modules/02packages.details.txt.gz
Reading '/home/john/.cpan/sources/modules/02packages.details.txt.gz'
Database was generated on Sun, 30 Nov 2025 21:08:01 GMT
............................................................................DONE
......По выводу мы можем судить, что индекс был выгружен успешно. Однако, наш репозиторий не совсем полноценный с точки зрения CPAN.pm, так как сам клиент может пытаться выгружать дополнительные технологические файлы, которых нет у нашего зеркала.
Наконец, мы можем установить наш модуль:
cpan[4]> i Acme::HelloWorld
Module id = Acme::HelloWorld
CPAN_USERID JDOE (Custom Non-CPAN author <CENSORED>)
CPAN_VERSION 0.01
CPAN_FILE J/JD/JDOE/Acme-HelloWorld-0.01.tar.gz
INST_FILE (not installed)
cpan[5]> install Acme::HelloWorld
Running install for module 'Acme::HelloWorld'
Fetching with LWP:
HASH(0x5564e969abd0)authors/id/J/JD/JDOE/CHECKSUMS
Fetching with LWP:
HASH(0x5564e969abd0)authors/id/J/JD/JDOE/CHECKSUMS.gz
Fetching with LWP:
http://www.cpan.org/authors/id/J/JD/JDOE/CHECKSUMS
Fetching with LWP:
http://www.cpan.org/authors/id/J/JD/JDOE/CHECKSUMS.gz
..................................
Checksum for /home/john/.cpan/sources/authors/id/J/JD/JDOE/Acme-HelloWorld-0.01.tar.gz ok
Scanning cache /home/john/.cpan/build for sizes
............................................................................DONE
Configuring J/JD/JDOE/Acme-HelloWorld-0.01.tar.gz with Makefile.PL
Checking if your kit is complete...
Looks good
Generating a Unix-style Makefile
Writing Makefile for Acme::HelloWorld
Writing MYMETA.yml and MYMETA.json
JDOE/Acme-HelloWorld-0.01.tar.gz
/usr/bin/perl Makefile.PL INSTALLDIRS=site -- OK
Running make for J/JD/JDOE/Acme-HelloWorld-0.01.tar.gz
cp lib/Acme/HelloWorld.pm blib/lib/Acme/HelloWorld.pm
Manifying 1 pod document
JDOE/Acme-HelloWorld-0.01.tar.gz
/usr/bin/make -- OK
Running make test for JDOE/Acme-HelloWorld-0.01.tar.gz
PERL_DL_NONLAZY=1 "/usr/bin/perl" "-MExtUtils::Command::MM" "-MTest::Harness" "-e" "undef *Test::Harness::Switches; test_harness(0, 'blib/lib', 'blib/arch')" t/*.t
t/Acme-HelloWorld.t .. ok
All tests successful.
Files=1, Tests=2, 0 wallclock secs ( 0.00 usr 0.00 sys + 0.02 cusr 0.00 csys = 0.02 CPU)
Result: PASS
Warning: Configuration not saved.
Lockfile removed.
JDOE/Acme-HelloWorld-0.01.tar.gz
/usr/bin/make test -- OK
Running make install for JDOE/Acme-HelloWorld-0.01.tar.gz
[sudo] пароль для john:
Manifying 1 pod document
Installing /usr/local/share/perl/5.32.1/Acme/HelloWorld.pm
Installing /usr/local/man/man3/Acme::HelloWorld.3pm
Appending installation info to /usr/local/lib/x86_64-linux-gnu/perl/5.32.1/perllocal.pod
JDOE/Acme-HelloWorld-0.01.tar.gz
sudo /usr/bin/make install -- OKМы можем убедиться, что модуль был установлен в дистрибутив Perl вызовом команды perldoc:
$ perldoc Acme::HelloWorld
Acme::HelloWorld
Acme::HelloWorld - Prints 'Hello World!' to anyone who asks for it.
SYNOPSIS
use Acme::HelloWorld qw { say_hello };
say_hello();
DESCRIPTION
This incredibly complex module is a top-secret development by ACME. It
prints "Hello World!" securely and without delay. Our engineers spent
hours trying to figure out how to print this message.
In fact, we're wondering why we haven't received a prize for this module
yet.
To start all the gears of our module, you only need to call the
following procedure.
sayhello();
Our module has the following features:
*Thread safety*
Don't worry about deadlocks and such, because we're doing this in a
single thread.
*Speed*
In theory, if you're not trying to print text on your toaster,
everything should happen quickly.
*And more ...*
* We haven't decided what to write here yet.
The text has a left indent of 12 characters.
AUTHOR
John Doe, <jdoe@acme.com<gt>
COPYRIGHT AND LICENSE
Copyright (C) 2025 by ACME
This library is free software; you can redistribute it and/or modify it
under the same terms as Perl itself, either Perl version 5.32.1 or, at
your option, any later version of Perl 5 you may have available
Удаление дистрибутивов
[править]Вы могли заметить, что в CPAN.pm нет команды для удаления дистрибутива: есть установка, обновление, но не удаление. Логика этого кроется в том, что скорее всего вы хотите устанавливать только то, что вам нужно, а система установки не позволит встроить дистрибутив в систему, если проваливаются тесты, другими словами, логика в том, что вы устанавливаете нужные вам и стабильные модули, которые вы можете в дальнейшем обновлять.
Кроме того, когда модуль разрабатывается или тестируется, он обычно никогда не устанавливается сразу в систему, а существует где-то отдельно от неё и встраивается в запуск через переменную окружения PERL5LIB (см. модуль local::lib).
Тем не менее, всегда возможны ошибки и некоторые модули могут оказаться нежелательными. Удалить их всегда можно вручную, но в этом вопросе следует быть внимательным, так как модуль может раскидать множество своих файлов по разным участкам системы.
Немного облегчить эту задачу поможет утилита pm-uninstall. Например, попробуем удалить наш шуточный модуль:
$ sudo pm-uninstall Acme::HelloWorld
[sudo] пароль для john:
--> Working on Acme::HelloWorld
Acme::HelloWorld is included in the distribution Acme-HelloWorld and contains:
/usr/local/man/man3/Acme::HelloWorld.3pm
/usr/local/share/perl/5.32.1/Acme/HelloWorld.pm
Are you sure you want to uninstall Acme-HelloWorld? [y] y
Successfully uninstalled Acme::HelloWorld
You may want to rebuild man(1) entries. Try mandb -c if needed
Построение дерева зависимостей
[править]Иногда на практике бывает полезно посмотреть, какие зависимости требует тот или иной исходный код, написанный на Perl. Это может быть нужно в таких ситуациях:
- Для оптимизации. Возможно вам нужно уменьшить число зависимостей, если имеются облегченные или более быстрые альтернативы. Тогда вы можете узнать, что конкретно от чего зависит, чтобы узнать, в какую сторону оптимизировать.
- Для сопровождения. Возможно конечному заказчику будет интересно знать, какие модули использует ваше приложение, например, чтобы подготавливать рабочие мощности.
Так как Perl динамический язык и может загружать модули «лениво» (в момент, когда они потребуются) и компилировать код на ходу, существует два сценария сканирования зависимостей:
- Статическое сканирование. Это обход всех зависимых исходных файлов в поисках модулей или библиотек, которые всегда обрабатываются на этапе большой компиляции.
- Динамическое сканирование. Это статическое сканирование с добавлением всех зависимостей, которые получаются от работы директив
require, вызываемых при определенных условиях, и компиляции черезeval.
Сначала мы рассмотрим статическое сканирование. На CPAN уже давно существует модуль Module::ScanDeps, который позволяет получить все статические зависимости. Этому модулю может быть передан исходный код, либо файл с исходным кодом, которые будут отправной точкой для сканирования зависимостей. Модуль позволяет сканировать зависимости рекурсивно (в глубину), либо можно получить только зависимости первого уровня.
Ниже приведен код приложения, реализующего статическое сканирование. Приложение может сканировать как в простом режиме, так и рекурсивном. Для работы приложения, ему необходимо передать один или несколько исходных файлов, для которых будет построено дерево зависимостей. Для визуализации используется утилита Graphviz, поэтому перед использованием вы должны установить её в системе и убедиться, что команду dot видит исполняющее окружение. Graphviz вызывается опосредованно через модуль GraphViz2, поэтому этот модуль также нужно доустановить из CPAN. Получаемый граф сохраняется в файле PNG.
# Файл: gen-graph.pl
use strict;
use warnings;
use Module::ScanDeps;
use GraphViz2;
use Getopt::Long;
my $recurseFlag;
my $prefix;
GetOptions(
"recurse" => \$recurseFlag,
"out" => \$prefix
) or die "Bad usage: unknown option.";
$prefix = $prefix || "./";
for my $filename (@ARGV) {
do { print "Cannot open: $filename \n"; next; } unless -e $filename;
print "Scanning dependencies for '$filename' ...\n";
my $deps = scan_deps(
files => [$filename],
recurse => $recurseFlag,
);
my ($label) = $filename =~ m|([^/\\]+)$|;
my $gv = GraphViz2->new(
global => { directed => 1 },
graph => { rankdir => 'LR', label => "Dependency graph for $label" },
node => { shape => 'box', style => 'filled', fillcolor => 'lightblue' },
);
foreach my $key (keys %$deps) {
my $module = $deps->{$key};
my $name = $key;
$gv->add_node(name => $name);
if ($module->{used_by}) {
foreach my $parent (@{$module->{used_by}}) {
$gv->add_edge(from => $parent, to => $name);
}
}
}
my $out = $prefix . "${label}_deps.png";
$gv->run(format => 'png', output_file => $out);
print "File $out SUCCESSFULLY saved\n";
}
Пусть у нас есть файл с таким исходным кодом
# Файл: test.pl
use IO::Socket;
тогда для простого сканирования нужно вызвать утилиту так
$ perl gen-graph.pl ./test.pl Scanning dependencies for './test.pl' ... File ./test.pl_deps.png SUCCESSFULLY saved
а для рекурсивного сканирования нужно использовать опцию -r
$ perl gen-graph.pl -r ./test.pl Scanning dependencies for './test.pl' ... File ./test.pl_deps.png SUCCESSFULLY saved
Будьте осторожны с рекурсивным сканированием: при очень большом дереве утилита Graphviz может очень долго его визуализировать.
Динамическое сканирование осложняется тем, что для того чтобы узнать какие зависимости получаются по ходу исполнения, этот код нужно исполнить. По этой причине сканирование уже больше похоже на профилирование. Для динамического сканирования можно воспользоваться модулем Devel::GraphVizProf, тогда вызов с построением графа будет таким.
$ perl -d:GraphVizProf test.pl > graph.dot
$ dot -Tpng graph.dot > test.pl_deps.png
Другой подход основан на том, что в проверяемый код нужно добавить такой блок END:
use strict;
use warnings;
use GraphViz2;
# Основная программа
END {
my $gv = GraphViz2->new(global => { directed => 1 });
foreach my $module (sort keys %INC) {
$module =~ s/\//::/g;
$module =~ s/\.pm$//;
$gv->add_node(name => $module);
}
$gv->run(format => 'png', output_file => 'dynamic_deps.png');
}
Примечания
[править]- ↑ Список директив и стандартных модулей
- ↑ Эта техника напоминает, например, системы сборки GNU Autotools и CMake.
- ↑ Исходный код взят из книги Кристиансен Т., Торкингтон Н. Perl: библиотека программиста = Perl Cookbook / под ред. O'Reilly; пер. с англ. Е. Матвеев. — 2-е изд. — Шаблон:СПб.: Питер, 2000. — С. 445—448. — ISBN 5-8046-0094-X. Однако, приведенный здесь код немного осовременен, а также были выброшены неиспользуемые части.
- ↑ Такие модули называются чистыми. Очень часто авторы используют буквы PP (англ. Pure Perl), чтобы это подчеркнуть.
- ↑ Однако, он может отсутствовать в ужатых сборках.