CPAN: меняем правила установки модулей

В данной статье пойдет речь об изменении поведения установки модулей в стандартном пакетном менеджере Perl, CPAN. Те, кто предпочитает использовать cpanm данная статья может быть интересной, но не более.

Введение

Началось все с того, что мне приходится использовать свои патчи для модулей при инсталляциях staticperl. Имеются разные сборки, с разным набором модулей, под разные архитектуры, на разных машинах. Самостоятельно патчить все файлы и прописывать это в скриптах, я понял, не самый лучший способ. Как-то раз, просматривая вывод o conf в консоли CPAN я заметил опцию patches_dir. И это навело на соответствующуюся мысль: а что если это то, что нужно?

И вот, я посвятил несколько часов на разбор документации к CPAN, потдвердив свою догадку. Опция patches_dir указывает расположение директории с патчами для модулей. Патчи накладываются автоматически менеджером CPAN при установке модуля. Тем не менее, это оказалось лишь самой легкой частью решения задачи.

Подготовка

Чтобы решить задачу с накладыванием патчей на соответствующие модули при их инсталяции требуется следующее:

  • Настроить менеджер CPAN
  • Написать патчи и положить их вместе в какую-либо директорию
  • Написать файл предпочтений (DistroPrefs) для нужных модулей

Итак, в моем случае патчи уже имеются, например, можно посмотреть на них здесь.

Настройка менеджера CPAN

YAML

Во-первых, нам нужно установить один из вариантов модулей для работы с YAML (это не обязятельно, но мне так удобней). Я выбираю YAML::XS, поэтому в настройке CPAN нужно выставить корректное значение опции yaml_module:

CPAN> o conf yaml_module
    yaml_module        [YAML::XS]
Type 'o conf' to view all configuration items

Допустим, чтобы установить и настроить все самостоятельно с нуля, достаточно выполнить следующее:

CPAN> install YAML::XS
CPAN> o conf yaml_module 'YAML::XS'
CPAN> o conf commit

Директория с патчами

Укажем CPAN, где расположены наши патчи. Дерево внутри этой директории может быть абсолютно любым, главное, чтобы все файлы были в одном месте.

CPAN> o conf patches_dir
    patches_dir        undef
CPAN> o conf patches_dir /home/gw/dev/perl/staticperl-modules/patches
    patches_dir        [/home/gw/dev/perl/staticperl-modules/patches]
CPAN> o conf commit

На всякий случай, проверьте, что утилита patch установлена и прописана:

CPAN> o conf patch  
    patch              [/usr/bin/patch]
CPAN> quit
shell> ls -l /usr/bin/patch
-rwxr-xr-x 1 root root 121584 Jan 25  2012 /usr/bin/patch

Файлы предпочтений

Файлы предпочтений, или на английском их именуют distroprefs, могут представлять из себя один из файлов типа Storable, Data::Dumper и YAML. Имя файла может быть любым, а расширение должно быть соответственно: .st, .dd, .yml. Расположение этих файлов задается через опцию prefs_dir:

CPAN> o conf prefs_dir
    prefs_dir          [/home/gw/.staticperl-5.20.1/cpan/prefs]

Формат файлов для каждого типа файл разный, но принцип примерно один. Имеются категории опций и их настройки. Ниже приведен пример из документации:

‘DistroPrefs YAML Blueprint’ (blueprint.yml) download
  ---
  comment: "Demo"
  match:
    module: "Dancing::Queen"
    distribution: "^CHACHACHA/Dancing-"
    not_distribution: "\.zip$"
    perl: "/usr/local/cariba-perl/bin/perl"
    perlconfig:
      archname: "freebsd"
      not_cc: "gcc"
    env:
      DANCING_FLOOR: "Shubiduh"
  disabled: 1
  cpanconfig:
    make: gmake
  pl:
    args:
      - "--somearg=specialcase"

    env: {}

    expect:
      - "Which is your favorite fruit"
      - "apple\n"

  make:
    args:
      - all
      - extra-all

    env: {}

    expect: []

    commandline: "echo SKIPPING make"

  test:
    args: []

    env: {}

    expect: []

  install:
    args: []

    env:
      WANT_TO_INSTALL: YES

    expect:
      - "Do you really want to install"
      - "y\n"

  patches:
    - "ABCDE/Fedcba-3.14-ABCDE-01.patch"

  depends:
    configure_requires:
      LWP: 5.8
    build_requires:
      Test::Exception: 0.25
    requires:
      Spiffy: 0.30

Не трудно догадаться, что к чему, верно? :) Попробуем написать свой файл предпочтений, чтобы наложить простейший патч для модуля Math::BigInt::GMP:

‘Math::BigInt::GMP Blueprint’ (Math-BigInt-GMP.yml) download
comment: |

    Math::BigInt::GMP, version 1.38
    Apply patch: https://rt.cpan.org/Public/Ticket/Attachment/1481298/789116
    RT: https://rt.cpan.org/Public/Bug/Display.html?id=96113

match:
    distribution: "^PJACKLAM/Math-BigInt-GMP-1.38"
patches:
    - "Math-BigInt-GMP/0001-bigfltpm.inc.patch"

В комментарии описывается, что используется патч из трекера под номером RT #96113. Опция match определяет как будет производится проверка соответствия с целевым архивом. В архиве может быть множество разных модулей. Значение представлено в виде регулярного выражения. В данном случае используется опция distribution, формат которой представлен в каноническом виде “AUTHOR/Foo-Bar-2.59.tar.gz”. Другие возможные опции соответствия: module, perl, perlconfig, и env. Наиболее полезным, на мой взгляд, является опция module, которая проводит проверку для всех модулей в целевом архиве.

Параметр patches указывает на массив с путями к патчам. Пути могут быть как полные, так и относительные (относительно значения опции patches_dir). В данном случае используется последний вариант.

Назовем файл как Math-BigInt-GMP.yml или скопируем его в директорию prefs_dir:

shell> echo "o conf prefs_dir" | perl -MCPAN -e shell
...
cpan[1]>     prefs_dir          [/home/gw/.staticperl-5.20.1/cpan/prefs]
...

shell> cp ~/Downloads/Math-BigInt-GMP.yml \
 /home/gw/.staticperl-5.20.1/cpan/prefs/

Патчи

Содержание файла с патчем Math-BigInt-GMP/0001-bigfltpm.inc.patch:

‘Math::BigInt::GMP bigfltpm.inc patch’ (0001-bigfltpm.inc.patch) download
--- i/t/bigfltpm.inc
+++ w/t/bigfltpm.inc
@@ -1406,8 +1406,8 @@ NaNmul:-inf:NaN
 &fdiv-list
 0:0:NaN,NaN
 0:1:0,0
-9:4:2.25,1
-9:5:1.8,4
+9:4:2,1
+9:5:1,4
 # bug in v1.74 with bdiv in list context, when $y is 1 or -1
 2.1:-1:-2.1,0
 2.1:1:2.1,0

Не забудем положить его в директорию, указанную в опции patches_dir:

CPAN> o conf patches_dir /home/gw/dev/perl/staticperl-modules/patches
    patches_dir        [/home/gw/dev/perl/staticperl-modules/patches]

shell> cp ~/Downloads/0001-bigfltpm.inc.patch \
 /home/gw/dev/perl/staticperl-modules/patches/Math-BigInt-GMP/0001-bigfltpm.inc.patch

Проверка работоспособности

Итак, осталось лишь убедиться, что все настроено и работает как положено!

CPAN> install Math::BigInt::GMP
...

Running install for module 'Math::BigInt::GMP'

______________________ D i s t r o P r e f s ______________________
                       Math-BigInt-GMP.yml[0]                       
Fetching with HTTP::Tiny:
http://mirror.netcologne.de/cpan/authors/id/P/PJ/PJACKLAM/Math-BigInt-GMP-1.38.tar.gz
Fetching with HTTP::Tiny:
http://mirror.netcologne.de/cpan/authors/id/P/PJ/PJACKLAM/CHECKSUMS
Checksum for /home/gw/.staticperl-5.20.1/cpan/sources/authors/id/P/PJ/PJACKLAM/Math-BigInt-GMP-1.38.tar.gz ok
Scanning cache /home/gw/.staticperl-5.20.1/cpan/build for sizes
............................................................................DONE
Applying 1 patch:
  /home/gw/dev/perl/staticperl-modules/patches/Math-BigInt-GMP/0001-bigfltpm.inc.patch
  /usr/bin/patch -N --fuzz=3 -p1
patching file t/bigfltpm.inc
Configuring P/PJ/PJACKLAM/Math-BigInt-GMP-1.38.tar.gz with Makefile.PL
Can't link/include C library 'gmp.h', 'gmp', aborting.
No 'Makefile' created  PJACKLAM/Math-BigInt-GMP-1.38.tar.gz
  /home/gw/.staticperl-5.20.1/perl/bin/perl Makefile.PL -- NOT OK
Failed during this command:
 PJACKLAM/Math-BigInt-GMP-1.38.tar.gz         : writemakefile NO -- No 'Makefile' created

Итак, мы видим заветный заголовок “D i s t r o P r e f s”, а также файл предпочтений, который будет использоваться: Math-BigInt-GMP.yml. Ниже видим, что CPAN успешно пропатчил файл t/bigfltpm.inc. Что нам и требовалось.

К сожалению, установка завершилась неудачей, т.к. на системе не установлены библиотечные файлы libgmp. Но, это уже другая задача :)

Заключение

Как показано выше, не так уж сложно настроить автоматическое накладываение патчей. В документации к модулю CPAN можно найти описание всех параметров для файлов предпочтений и воспользоваться ими по-своему. Кроме этого, если вас по каким-либо причинам не устраивает использование формата YAML можно узнать как использовать Storable и/или Data::Dumper.

Отмечу лишь то, что CPAN будет читать абсолютно все файлы предпочтений в указанной директории prefs_dir, а в случае синтаксических или иных ошибок настойчиво об этом сообщать.

Другой полезной опцией для меня оказалась передача параметров для Makefile.PL (и Build.PL). Попробуйте сделать это сами.

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