HTML page

Глава 12 Пакеты, библиотеки и модули

12.12. Вывод сообщений об ошибках и предупреждений по аналогии со встроенными функциями

Проблема

Ваш модуль генерирует ошибки и предупреждения, однако при использовании warn или die пользователь видит имя вашего файла и номер строки. Вы хотите, чтобы функции модуля вели себя по аналогии со встроенными функциями и сообщали об ошибках с точки зрения пользовательского, а не вашего кода.

Решение

Соответствующие функции присутствуют в стандартном модуле Carp. Вместо warn используйте функцию carp, а вместо die - функцию croak (для коротких сообщений) или confess (для длинных сообщений).

Комментарий

Некоторые функции модуля, как и встроенные функции, могут генерировать предупреждения или ошибки. Предположим, вы вызвали функцию sqrt с отри- цательным аргументом (и не воспользовались модулем Math::Complex) - возникает исключение с выводом сообщения вида "Can't take sqrt of -3 at /tmp/ negroot line 17", где /tmp/negroot - имя вашей программы. Но если вы напишете собственную функцию с использованием die:
sub even_only { my $n = shift;
die "$n is not even" if $n & 1; # Один из способов проверки
#.. . .
}
то в сообщении вместо пользовательского файла, из которого вызывалась ваша функция, будет указан файл, в котором была откомпилирована функция even_only. На помощь приходит модуль Carp. Вместо die мы используем функцию croak:
use Carp;
sub even_only {
my $n = shift;
croak "$n is not even" if $n % 2;
Другой способ
#....
}

Если вы хотите просто вывести сообщение с номером строки пользовательской программы, где произошла ошибка, вызовите carp вместо warn (в отличие от warn и die, завершающий перевод строки в сообщениях carp и croak не имеет особой интерпретации). Например:
use Carp;
sub even_only { my $n = shift;
if ($n & 1) { # Проверка нечетности
carp "$n is not even, continuing";
++$n;
}
# . . . .
}

Многие встроенные функции выводят предупреждения лишь при использовании ключа командной строки -w. Переменная $"W сообщает о его состоянии. Например, предупреждения можно выдавать лишь при наличии запроса от пользователя: carp "$n is not even, continuing" if $"W;
Наконец, в модуле Carp существует третья функция - confess. Она работает аналогично croak за исключением того, что при аварийном завершении выводится полная информация о состоянии стека, вызовах функций и значениях аргументов.

> Смотри также -------------------------------
Описание функций warn и die в perlmod(l); описание метапеременных __WARN__ и __DIE_ в разделе "Global Special Arrays" perlvar(1) и в рецепте 16.15; документация по стандартному модулю Carp; рецепт 19.2.

12.13. Косвенные ссылки на пакеты

Проблема

Требуется сослаться на переменную или функцию в пакете, имена которых неизвестны до момента выполнения программы, однако синтаксис $packname: : $varname недопустим.

Решение

Воспользуйтесь символическими ссылками:
{
no strict 'rets';
$val = ${ $packname . "::" . $varname };
Ovals = @{ $packnanie . "::" . $aryname };
&{ $packname . "::" . $funcname }("args");
($packname . "::" . $funcname) -> ("args");
}

Комментарий

Объявление пакета имеет смысл во время компиляции. Если имя пакета или неременной неизвестно до времени выполнения, придется прибегнуть к символическим ссылкам и организовать прямые обращения к таблице символов пакета Включите в блок директиву no strict ' refs' и постройте строку с полным именем интересующей вас переменной или функции. Затем разыменуйте полученную строку так, словно она является нормальной ссылкой Perl. До выхода Perl версии 5 программистам в подобных случаях приходилось использовать eval:
eval "package $packname; \$'$val = \$$varname"; # Задать $main'val die if $@;
Как видите, такой подход затрудняет построение строки. Кроме того, такой кол работает относительно медленно. Впрочем, вам никогда не придется делать это лишь для того, чтобы косвенно обращаться к переменным по именам. Символические ссылки обеспечивают необходимый компромисс. Функция eval также используется для определения функций во время выполнения программы. Предположим, вы хотите иметь возможность вычислять двоичные и десятичные логарифмы:
printf "log2 of 100 is %.2f\n", log2(100):
printf "log-IO of 100 is %.2f\n", log10(100);
В Perl существует функция log для вычисления натуральных логарифмои. Давайте посмотрим, как использовать eval для построения функций во время выполнения программы. Мы создадим функции с именами от log2 до log999:
$packname = 'main';
for ($i =2; $i < 1000; $i++) { $logN = log($i);
eval "sub ${packname}::log$i { log(shift) / $logN }";
die if $@;

По крайней мере в данном случае это не нужно. Следующий фрагмент делает то же самое, но вместо того, чтобы компилировать новую функцию 998 раз, мы откомпилируем ее всего единожды в виде замыкания. Затем мы воспользуемся символическим разыменованием в таблице символов и присвоим одну и ту же ссылку на функцию по многим именам:
$packname = 'main';
for ($i =2; $i < 1000; $i++) {
my $logN = log($i);
no strict 'rets'; *{"${packname}::log$i"} = sub { log(shift) / $logN };
}

Присваивая ссылку тип-глобу, вы всего лишь создаете синоним для некоторого имени. На этом принципе построена работа Exporter. Первая строка следу юще го фрагмента вручную экспортирует имя функции Colors::blue в текущий пакет Вторая строка назначает функцию main: :blue синонимом функции Colors; : azure.
*blue = \&Colors::blue;
-main::blue = \&Colors::azure;

Принимая во внимание гибкость присваивании тип-глобов и символических ссылок, полноценные конструкции eval "СТРОКА" почти всегда оказываются излишеством, последней надеждой отчаявшегося программиста. Ничего худшего себе и представить нельзя - разве что если бы они были недоступны.

> Смотри также -------------------------------
Раздел "Symbolic References" perlsub(1); рецепт 11.4

12.14. Применение h2ph для преобразования заголовочных файлов

Проблема

Полученный от кого-то код выдает устрашающее сообщение об ошибке: Can't locate sys/syscall. ph in @INC (did you run h2ph?)
(@INC contains:
/usr/lib/perl5/i686-linux/5.00404
/usr/lib/perl5 /usr/lib/perl5/site_perl/i686-linux
/usr/lib/perl5/site_perl
.) at some_program line 7.

Вы хотите понять, что это значит и как справиться с ошибкой.

Решение

Попросите системного администратора выполнить следующую команду с правами привилегированного пользователя:
% cd /usr/include; h2ph sys/syscall.h

Однако многие заголовочные файлы включают другие заголовочные файлы; иными словами, придется преобразовать их все:
% cd /usr/include; h2ph *.h */*.h

Если вы получите сообщение о слишком большом количестве файлов или если некоторые файлы в подкаталогах не будут найдены, попробуйте другую команду:
% cd /usr/include; find . -name '*.h' -print | xargs h2ph

Комментарий

Файлы с расширением .ph создаются утилитой h2ph, которая преобразует директивы препроцессора С из #include-файлов в Perl. Это делается для того, чтобы программа на Perl могла работать с теми же константами, что и программа на С. Утилита h2xs обычно оказывается более удачным решением, поскольку вместо кода Perl, имитирующего код С, она предоставляет откомпилированный код С. Однако работа с h2xs требует намного большего опыта программирования (по крайней мере, в том, что касается С), чем h2ph. Если процесс преобразования h2ph работает, все прекрасно. Если нет - что ж, вам не повезло. Усложнение системных архитектур и заголовочных файлов приводит к более частым отказам h2ph. Если повезет, необходимые константы уже будут присутствовать в модулях Fcnti, Socket или POSIX. В частности, модуль POSIX реализует константы из sys/file.h, sys/ermo.h и sys/wait.h. Кроме того, он обеспечивает выполнение нестандартных операций с терминалом (см. рецепт 15.8).
Так что же можно сделать с файлом .ph? Рассмотрим несколько примеров. В первом примере непереносимая функция syscall используется для вызова системной функции gettimeofday. Перед вами реализация модуля FineTime, описанного в рецепте 12.11.
# Файл FineTime.pm package main;
require 'sys/syscall.ph';
die "No SYS_gettimeofday in sys/syscall.ph"
unless defined &SYS_gettimeofday;
package FineTime;
use strict;
require Exporter;
use vars qw(@ISA @EXPORT_OK);
@ISA = qw(exporter);
@EXPORT_OK = qw(time);
sub time() {
my $tv = pack("ll", ());
# presize buffer to two longs
syscall(&main::SYS_gettimeofday, $tv, undef) >= 0 or die "gettimeofday: $!";
my($seconds, $microseconds) = unpack("ll", $tv);
return $seconds + ($microseconds / 1_000_000);
}
1;

Если вам приходится вызывать require для старых файлов ,рl или .ph, сделайте это из главного пакета (package main в приведенном выше коде). Эти старые библиотеки всегда помещают свои символические имена в текущий пакет, a main служит "местом встречи". Чтобы использовать имя, уточните его, как мы поступили с main::SYS_gettimeofday. Файл sys/ioctl.ph, если вам удастся построить его в своей системе, открывает доступ к функциям ввода/вывода вашей системы через функции loctl. К их числу принадлежит функция TIOCSTI из примера 12.1. Сокращение TIOCSTI означает "управление терминальным вводом/выводом, имитация терминального ввода" (terminal I/O control, simulate terminal input). В системах, где эта функция реализована, она вставляет один символ в поток устройства, чтобы при следующем чтении из устройства со стороны любого процесса был получен вставленный символ. Пример 12.1. jam
#!/usr/bin/perl -w
# jam - вставка символов в STDIN
require 'sys/ioctl.ph';
die "no TIOCSTI" unless defined &TIOCSTI;
sub jam {
local $SIG{TTOU} = "ignore"; # "Остановка для вывода на терминал"
local *TTY; # Создать локальный манипулятор
open(TTY, "+for (split(//, $_[0])) {
vioctl(TTY, &TIOCSTI, $_) or die "bad TIOCSTI: $!";
}
close(TTY);
}
Jam("@ARGV\n");

Поскольку преобразование sys/ioctl.h может вызвать некоторые сложности, вероятно, для получения кода TIOCSTI вам придется запустить следующую программу на С:
% cat > tio.c "EOF && ее tio.c && a.out
#include
main() { printf("%#08x\n", TIOCSTI); }
EOF
0х005412

Функция iocti также часто применяется для определения размеров текущего окна в строках/столбцах и даже в пикселях. Исходный текст программы приведен в примере 12.2. Пример 12.2. winsz
#!/usr/bin/perl

# winsz - определение размеров окна в символах и пикселях
require 'sys/ioctl.ph';
die "no TIOCGWINSZ " unless defined &TIOCGWINSZ;
open(TTY, "+unless (ioctl(TTY, &TIOCGWINSZ, $winsize='o)) {
die sprintf "$0: iocti TIOCGWINSZ (%08x: $!)\n", &TIOCGWINSZ;
} '
($row, $col, $xpixel, $ypixel) = unpack('s4', $winsize);
print "(row,col) = ($row,$col)";
print " (xpixel.ypixel) = ($xpixel,$ypixel)" if $xpixel || $ypixel;
print "\n";

Как видите, для экспериментов с файлами .ph, распаковкой двоичных данных и вызовами syscall и iocti необходимо хорошо знать прикладной интерфейс С, обычно скрываемый Perl. Единственное, что требует такого же уровня знаний С -это интерфейс XS. Одни считают, что программисты должны бороться с искушением и за версту обходить подобные непереносимые решения. По мнению других, жесткие требования, поставленные перед рядовым программистом, оправдывают самые отчаянные меры. К счастью, все большее распространение получают менее хрупкие механизмы. Для большинства этих функций появились модули CPAN. Теоретически они работают надежнее, чем обращения к файлам .ph.

> Смотри также -------------------------------
Описание функций syscall и iocti в perlmod{\)\ инструкции по работе с h2ph в файле INSTALL исходной поставки Perl; h2ph(1); рецепт 12.15.

12.15. Применение h2xs для создания модулей с кодом С

Проблема

Вам хотелось бы работать с функциями С из Perl.

Решение

Воспользуйтесь утилитой h2xs для построения необходимых файлов шаблонов, заполните их соответствующим образом и введите: % perl Makefile.PL % make

Комментарий

При написании модуля Perl необязательно ограничиваться одним Perl. Как и для любого другого модуля, выберите имя и вызовите для него утилиту h2xs. Мы со- здаднм функцию FineTime::time с той же семантикой, что и в предыдущем рецепте, но на этот раз реализуем ее на С. Сначала выполните следующую команду:
% h2xs -en FineTime
Если бы у нас был файл .h с объявлениями прототипов функций, его можно было бы включить, но поскольку мы пишем модуль с нуля, используется флаг -с - тем самым мы отказываемся от построения кода, преобразующего директивы #define. Флаг -п требует создать для модуля каталог FineTime/, в котором будут находиться следующие файлы: Файл Список файлов в поставке
Changes Протокол изменений Makefile.PL
MeTa-make-файл FineTime.pm

Компоненты Perl FineTime.xs
Будущие компоненты С test.pl______Тестовая программа__
Перед тем как вводить команду make, необходимо сгенерировать make-файл для текущей системной конфигурации с помощью шаблона Makefile.PL. Вот как :)TO делается:
% perl Makefile.PL

Если код XS вызывает библиотечный код, отсутствующий в нормальном наборе библиотек Perl, сначала добавьте в Makefile.?! новую строку. Например, если мы хотим подключить библиотеку librpm.a из каталога /usr/redhat/lib, то нам надо изменить строку Makefile.PL:
'LIBS' =>["], # e.g. , '-1m' и привести ее к виду:
'LIBS' => ['-L/usr/redhat/lib -Irpm'],

Наконец, отредактируйте файлы FineTime.pm и FineTime.xs. В первом случае большая часть работы уже сделана за нас. Нам остается создать список экспортируемых функций. На этот раз мы помещаем его в @EXPORT_OK, чтобы нужные функции запрашивались пользователем по имени. Файл FineTime.pm выглядит так:
package FineTime;
use strict;
use vars qw($VERSION @ISA @EXPORT_OK);
require Exporter;
require DynaLoader;
@ISA = qw(exporter dynaloader);
@EXPORT_OK = qw(time);
$VERSION = 'О.О1';
bootstrap FineTime $VERSION;
1;
Make автоматически преобразует файл FineTime.xs в FineTime.c и общую библиотеку, которая на большинстве платформ будет называться FineTime.so. Преобразование выполняется утилитой xsubpp, описанной в ее собственной странице руководства Hperlxstut(i). Xsubpp автоматически вызывается в процессе построения.
Кроме хороших познаний в С, вы также должны разбираться в интерфейсе С-Рег1, который называется XS (eXternal Subroutine). Подробности и нюансы XS выходят за рамки этой книги. Автоматически сгенерированный файл FineTimeJCs содержит заголовочные файлы, специфические для Perl, а также объявление MODULE. Мы добавили несколько дополнительных файлов и переписали код новой функции time. На С пока не похоже, но после завершения работы xsubpp все придет в норму.
Использованный нами файл FineTime.xs выглядит так:
#include
#include
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
MODULE = finetime
PACKAGE = finetime
double time() CODE:
struct timeval tv;
gettimeofday(&tv,0);
RETVAL = tv.tv_sec + ((double) tv,tv_usec) / 1000000;
OUTPUT:
RETVAL

Определение функции с именем, присутствующем в стандартной библиотеке С, не вызовет проблем при компиляции - это не настоящее имя, а лишь псевдоним, используемый Perl. Компоновщик С увидит функцию с именем XS_FineTime_ time, поэтому конфликта не будет. При выполнении команды make install происходит следующее (с небольшими исправлениями):
% make install
mkdir ./blib/lib/auto/FineTime
cp FineTime.pm ./blib/lib/FineTime.pm
/usr/local/bin/perl -I/usr/lib/perl5/i686-linux/5.00403 -I/usr/lib/perl5
/usr/lib/perl5/ExtUtils/xsubpp -typemap
/usr/lib/perl5/ExtUtils/typemap
FineTime.xs FineTime.tc && mv FineTime.tc
FineTime.ccc -c -Dbool=char -dhas_bool
-02-DVERSION=\"0.01\" -dxs_version-\"0.01\" -fpic
-I/usr/lib/perl5/i686-linux/5.00403/CORE FineTime.cRunning
Mkbootstrap for FineTime () chmod 644 FineTime.bs LD_RUN_PATH="" cc -o
blib/arch/auto/FineTime/FineTime.so
-shared -L/usr/local/lib FineTime.о chmod 755
blib/arch/auto/FineTime/FineTime.so
ср FineTime.bs ,/blib/arch/auto/FineTime/FineTime. bs
chmod 644 blib/arch/auto/FineTime/FineTime.bs
Installing /home/tchrist/perllib/i686-linux/./auto/FineTime/FineTime.so
Installing /home/tchrist/perllib/i686-linux/./auto/FineTime/FineTime.bs
Installing /home/tchrist/perllib/./FineTime.pm Writing /home/tenrist/perllib/i686-linux/auto/FineTime/.packlist Appending installation info to /home/tchrist/perllib/i686-linux/perllocal.pod
Когда все будет готово, в интерпретаторе вводится следующая команда:
% perl -I ""/perllib -MFineTime=time -le '1 while print time()' | head
888177070.090978
888177070.09132
888177070.091389
888177070.091453
888177070.091515
888177070.091577
888177070.091639
888177070.0917
888177070.091763
888177070.091864


> Смотри также --------------------------------
Документация по стандартному модулю ExtUtils::MakeMaker; h2ph(1) В xsubpp(1). Вызовы функций С из Perl описаны в perlxstut(l) и perlxs(l), а вызовы функций Perl из С - в perlembed(l). Внутренний API Perl рассматривается в perlcall{1) nperlguts(1). По адресу http://www.perl.com/CPAN/authors/Dean _ Roehrich/ находится подробное руководство по XS с рекомендациями по организации интерфейса с C++.

12.16. Документирование модуля в формате pod

Проблема

Вы хотите документировать свой модуль, но не знаете, какой формат следует использовать.

Решение

Включите документацию в файл модуля в формате pod.

Комментарий

Сокращение pod означает "plain old documentation", то есть "простая документация". Документация в формате pod включается в программу с применением очень простого формата разметки. Как известно, программисты сначала пишут программу, а документацию... не пишут вообще. Формат pod был разработан для максимальной простоты документирования, чтобы с этой задачей справился даже лентяй. Иногда это даже помогает. Если во время анализа исходного текста Perl обнаруживает строку, начинающуюся со знака = (там, где ожидается новая команда), он игнорирует весь текст до строки, начинающейся с =cut, после чего продолжает анализировать код. Это позволяет смешивать в программах или файлах модулей perl код и документацию. Поскольку формат pod является сугубо текстовым, никакого особого форматирования не требуется. Трансляторы стараются проявить интеллект и преобразуют вывод так, чтобы программисту не приходилось особым образом форматировать имена переменных, вызовы функций и т. д.
Вместе с Perl поставляется несколько программ-трансляторов, которые фильтруют документацию в формате pod и преобразуют ее в другой формат вывода. Утилита pod2man преобразует pod в формат troff, используемый в программе man или в системах верстки и печати. Утилита pod2html создает Web-страницы, работающие в системах, не принадлежащих к семейству UNIX. Утилита pod2text преобразует pod в простой ASCII-текст. Другие трансляторы (pod2ipf, pod2fm, pod2texf pod2latex и pod2ps) могут входить в поставку Perl или распространяются чере:' CPAN.
Многие книги пишутся в коммерческих текстовых редакторах с ограниченными сценарными возможностями... но только не эта! Она была написана в формак pod в простых текстовых редакторах (Том использовал vi, а Нат - emacs). На стадии технической правки книга была преобразована в формат troff специальным транслятором pod2ora, написанным Ларри. Окончательный вариант книги были получен преобразованием pod-файлов в формат FrameMaker. Хотя в perlpod{1) приведено общее описание pod, вероятно, этот формат удобнее изучать на примере готовых модулей. Если вы начали создавать собственные модули с помощью утилиты h2xs, то у вас уже имеются образцы. Утилита Makefile знает, как преобразовать их в формат man и установить страницы руководства так, чтобы их могли прочитать другие. Кроме того, программа perldoc может транслировать документацию pod с помощью pod2text.
Абзацы с отступами остаются без изменений. Другие абзацы переформатируются для размещения на странице. В pod используются лишь два вида служебной разметки: абзацы, начинающиеся со знака = и одного или нескольких слов, и внутренние последовательности в виде буквы, за которой следует текст в угловых скобках. Теги абзацев определяют заголовки, перечисляемые элементы списков и служебные символы, предназначенные для конкретного транслятора. Последовательности в угловых скобках в основном используются для изменения начертания (например, выбора полужирного, курсивного или моноширинного шрифта), Приведем пример директивы =head2 в сочетании с изменениями шрифта:
=head2 discussion
If we had a dot-h file with function prototype declarations, we could include that, but since we're writing this one from scratch, we'll use the -c flag to omit building code to translate any #define symbols. The -n flag says to create a module directory named FineTime/, which will have the following files.

Последовательность =for определяет код для выходных файлов конкретного формата. Например, в этой книге, главным образом написанной в формате pod, присутствуют вызовы стандартных средств troff: eqn, tbi и pie. Ниже показан пример внутреннего вызова eqn, который обрабатывается лишь трансляторами, производящими данные в формате troff:
=for troff
.ЕО
log sub n (x) = { {log sub e (x)} over {log sub e (n)} }
.EN

Формат pod также позволяет создавать многострочные комментарии. В языке С комментарий /*. . . . */ может включать несколько строк текста - вам не придется ставить отдельный маркер в каждой строке. Поскольку Perl игнорирует директивы pod, этим можно воспользоваться для блочного комментирования. Весь фокус заключается в том, чтобы найти директиву, игнорируемую трансляторами pod. Например, можно воспользоваться тегом for later или for nobody:
=for later next if 1 . . ?"$'?;
s/"(.)/>$1/, s/(.{73})........*/$1/;
=cut back to perl или парой ^begin и =end:
=begin comment
if (!open(FILE, $file)) { unless ($opt_q) {
warn "$me: $file: $!\n";
$Errors++;
} next FILE;
}
$total = 0;
$matches = 0:
=end comment


> Смотри также
Раздел "POD: Enbedded Documentation" в perlsyn(1) ;perlpod(1); pod2man(1), pod2html( 1) и pod2text{ 1).

12.17. Построение и установка модуля CPAN

Проблема

Требуется установить файл модуля, загруженный с CPAN или взятый с компакт-диска.

Решение

Введите в интерпретаторе следующие команды (на примере установки модуля Some::Module версии 4.54):
% gunzip Some-Module-4.54.tar.gz
% tar xf Some-Module-4.54
% cd Some-Module-4.54
% perl Makefile.PL
% make
% make test
% make install

Комментарий

Модули Perl, как и большинство программ в Сети, распространяются в архивах tar, сжатых программой GNU zip1. Если tar выдает предупреждение об ошибках контрольных сумм каталогов ("Directory checksum errors"), значит, вы испортили двоичный файл, приняв его в текстовом формате.
Вероятно, для установки модуля в системные каталоги необходимо стать привилегированным пользователем с соответствующими правами доступа. Стандартные модули обычно устанавливаются в каталог /usr/lib/perl5, а прочие - в каталог /usr/lib/perl5/site_perl. Рассмотрим процесс установки модуля MD5:
% gunzip MD5-1.7.tar.gz
% tar xf MD5-1.7.tar
% cd MD5-1.7
% perl Makefile.PL
Checking if your kit is complete...
Looks good
Writing Makefile for MD5
X make
mkdir ./blib
mkdir ./blib/lib
cp MDS.pm ./blib/lib/MDS.pm
AutoSplitting MD5 (,/blib/lib/auto/MD5)
/usr/bin/perl -I/usr/local/lib/perl5/i386
cp MD5.bs ./blib/arch/auto/MD5/MD5.bs
Ghmod 644 ./blib/arch/auto/MD5/MD5.bsmkdir ./blib/man3
Manifying ,/blib/man3/MD5.3
% make test
PERL_DL_NONLAZY=1 /usr/bin/perl -i./blib/arch -i./blib/lib
-I/usr/local/lib/perl5/i386-freebsd/5.00404
-I/usr/local/lib/perlS test.pi
1..14 ok 1 ok 2
. . .
ok 13
ok 14
% sudo make install
Password:
Installing /usr/local/lib/perlS/site.perl/iSBe-freebsd/./auto/MDS/
MDS.so Installing /us(71ocal/lib/perl5/site_perl/i386-freebsd/./auto/MD5/
MDS.bs
Installing /usr/local/lib/perl5/site_perl/./auto/MD5/autosplit.ix Installing
/usr/local/lib/perl5/site_perl/./MD5.pm Installing
/usr/local/lib/perl5/man/man3/./MD5.3 Writing
/usr/local/lib/perl5/site_perl/i386-freebsd/auto/MD5/.packlist
Appending installation info to /usr/local/lib/perl5/i386-freebsd/
5,00404/perllocal.pod
Если ваш системный администратор где-то пропадает или у него нет времени на установку, не огорчайтесь. Используя Perl для построения .make-файла по шаблону Makefile.PL, можно выбрать альтернативный каталог для установки.
# Если вы хотите установить модули в свой каталог
% perl Makefile.PL LIB=~/lib
# Если у вас имеется полная поставка
% perl Makefile.PL PREFIX=~/perl5-private


> Смотри также -------------------------------
Документация по стандартному модулю ExtUtils::MakeMaker. Файл INSTALL в исходной поставке Perl содержит сведения о построении двоичного файла perl со статической компоновкой.

12.18. Пример: шаблон модуля

Ниже приведен "скелет" модуля. Если вы собираетесь написать собственный модуль, попробуйте скопировать и отредактировать его.
package Some::Module; # Должен находиться в Some/Module.pm use strict;
require Exporter;
use vars qw($VERSION @ISA EXPORT @>EXPORT_OK %EXPORT_TAGS);
# Установка версии для последующей проверки
$VERSION = 0.01;
@ISA = qw(exporter);
@EXPORT = qw(&func1 &func2 &func4);
%EXPORT_TAGS = ( ); # например:
TAG => [ qw!name-l name2! # Здесь находятся экспортируемые глобальные переменные, # а также функции с необязательным экспортированием
@EXPORT_OK = qw($var1 %hashit &func3);
use vars qw($Var1 %Hashit);
# Здесь находятся неэкспортируемые глобальные имена пакета use vars
qw(@more $stuff);
# Инициализировать глобальные переменные пакета,
# начиная с экспортируемых
$Var1 = ''; %hashit =();
# Затем все остальные (к которым можно обращаться
3 в виде $Some::Module::stuff)
$stuff = '';
@more =();
# Все лексические переменные с файловой областью действия
# должны быть созданы раньше функций, которые их используют.
# Лексические переменные, доступ к которым
# ограничивается данным файлом.
my $priv_var = '';
mу %secret_hash = ();
# Закрытая функция, оформленная в виде замыкания
# и вызываемая через
&$priv_func. my $priv_func = sub { # Содержимое функции.
}
# Все ваши функции, экспортируемые и нет;
# не забудьте вставить что-нибудь в заглушки {}
sub fund {....}
# без прототипа sub func2() {....}
# прототип - void sub func3($$) {....}
# прототип - 2 скаляра
# Функция не экспортируется автоматически, но може! вызываться!
sub func4(\%) {....} # прототип - 1 ссылка на хэш
END { } # Завершающий код модуля (глобальный деструктор)
1;

12.19. Программа: поиск версий и описаний установленных модулей

Perl распространяется вместе с множеством модулей. Еще больше модулей можно найти в CPAN. Следующая программа выводит имена, версии и описания всех модулей, установленных в вашей системе. Она использует стандартные модули (например, File::Find) и реализует некоторые приемы, описанные в этой главе. Программа запускается следующей командой: % pmdesc Она выводит список модулей с описаниями:
FileHandle (2.00) - supply object methods for filehandles 10 :File (1.06021) - supply object methods for filehandles 10 :Select (1.10) - 00 interface to the select system call 10 :Socket (1.1603) - Object interface to socket communications. . .
С флагом -v программа pmdesc выводит имена каталогов, в которых находятся файлы:
% pmdesc -v
<<>>
FileHandle (2.00) - supply object methods for filehandles
. . .

Флаг -w предупреждает о том, что модуль не включает документации в формате pod, а флаг -s сортирует список модулей в каждом каталоге. Исходный текст программы приведен в примере 12.3. Пример 12.3. pmdesc
#!/usr/bin/perl -w
# pmdesc - вывод описаний файлов pm
# tchrist@perl.conn
use strict;
use File::Find qw(find);
use Getopt::Std qw(getopts);
use Carp;
use vars (
q!$opt_v! # Вывод отладочной информации
q!$opt_w! # Предупреждения об отсутствующих
# описаниях модулей
q!$opt_a! # Вывод относительных путей
q!$opt_s! # Сортировка данных по каждому каталогу
};
$| = 1;
getopts('wvas') or die "bad usage";
@ARGV = @inc unless oargv;
# Глобальные переменные. Я бы предпочел обойтись без этого.
use vars (
q!$Start_Dir!, # Каталог верхнего уровня, для которого
# вызывалась функция find
q!%Future!, # Другие каталоги верхнего уровня,
# для которых find вызывается позднее
);
my $Moauie,
# Установить фильтр для сортировки списка модулей,
# если был указан соответствующий флаг.
if ($opt_s) {
if (open(ME, "- ")) { $/ = o o;
while () { chomp;
print join("\n", sort split /\n/), "\n";
} exit;
}
}
MAIN: {
my %visited;
my ($dev,$ino);
@Future{@ARGV} = (1) x oargv;
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 future.)\n" if 0 and $opt_v;
$File::Find::prune = 1;
return:
} return unless /\.pm$/ && -f;
$Module = &modname;
# skip obnoxious modules
if ($Module =~ /"cpan(\z|::)/) {
warn("$Module -- skipping because it misbehaves\n");
return;
}
my $file = $_;
unless (open(POD, "< $file")) {
warn "\tcannot open $file: $! # if $opt_w;
return 0;
}
$:=" -:";
local $/ = oo;
local $_;
while () {
if (/=head\d\s+name/)
chomp($_ = )
s/".*'?-\s+//s; vs/\n/ /g;
#write;
my $v,
if (defined ($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;
}
# Загрузить модуль и вывести его номер версии,
# перенаправляя ошибки в /dev/null
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);
}
format = <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
$Module, $_


copyright 2000 Soft group