Ruby Packaging mini-HOWTO

Материал из ALT Linux Wiki

(Различия между версиями)
Перейти к: навигация, поиск
(Import from freesource.info)
(Import from freesource.info)
Строка 1: Строка 1:
-
[[Category:Policy]]
+
[[Category:Sisyphus]]
-
{{MovedFromFreesourceInfo|AltLinux/Policy/Ruby}}
+
{{MovedFromFreesourceInfo|AltLinux/Sisyphus/ruby}}
-
== Ruby Packaging Policy ==
+
== Ruby ==
-
{| border="1"
+
Основные правила сборки приложений и модулей ruby изложены в [[Ruby|Ruby Packaging Policy]]. Цель же этого документа - об'яснить на простых примерах как следует поступать в различных ситуациях а также показать как можно собирать простые модули.
-
|-
+
-
|
+
-
Статус
+
-
|
+
-
Действующая политика с 16 апреля 2008
+
-
|-
+
-
|
+
-
Автор
+
-
|
+
-
[http://freesource.info/wiki//SirRaorn Alexey I. Froloff]
+
-
|-
+
-
|
+
-
Обязательно в
+
-
|
+
-
4.1 и выше
+
-
|-
+
-
|
+
-
Метабаг
+
-
|
+
-
пока не создан
+
-
|}
+
 +
Я не рассматриваю rubygems, так как эта система включает в себя свой пакетный менеджер, не совместимый с дистрибутивным.  Про отношение Debian к rubygems можно прочитать [http://pkg-ruby-extras.alioth.debian.org/rubygems.html тут], рекомендации апстриму изложены [http://pkg-ruby-extras.alioth.debian.org/upstream-devs.html тут].  У нас ситуация в общем аналогичная.
-
Правила упаковки модулей и программ на языке Ruby.
+
''Тут пока находится поток сознания, который я буду приводить к нормальному виду.''
__TOC__
__TOC__
-
=== Пакет ruby и его обновление ===
+
=== Общие принципы сборки модулей ===
-
Основная часть интерпретатора ruby находится в пакете librubyЭто библиотека libruby.so.X.Y и каталоги, входящие в <tt>$LOAD_PATH</tt>.
+
Есть два способа сборки модулейПри помощи rubygems (который мы не рассматриваем по ряду причин), и "нативная" сборка, с помещением файлов в специальные каталоги, которые находятся в <tt>$LOAD_PATH</tt>.
-
В системе и в публичном репозитарии может находиться только она версия пакета libruby.
+
Сборка пакета включает в себя:
-
При сборке новой версии ruby из-за изменения <tt>$LOAD_PATH</tt> необходимо пересобрать все пакеты, имеющие зависимости (прямые или косвенные) на пакет libruby.
+
* компиляцию бинарных модулей (если они есть);
 +
* выполнение тестов (если они есть и их выполнение возможно в hasher);
 +
* установку файлов в соответствующие каталоги;
 +
* генерацию документации в формате ri (class reference);
-
=== Внешний вид пакетов ===
+
Модули устанавливаются в так называемый vendor dir.  В ALT Linux это <tt>/usr/share/ruby/vendor_ruby/RUBY.VERSION</tt> и <tt>/usr/lib/ruby/RUBY.VERSION/ARCHITECTURE</tt>.  Поскольку по умолчанию установка модулей идёт в site dir, при сборке пакета надо использовать модуль <tt>vendor_specific</tt>, вызывая интерпретатор ruby как <tt>ruby -rvendor_specific</tt>.  Для этого есть макрос <tt>%ruby</tt>.
-
Пакеты должны иметь префикс "ruby-".  Если оригинальное название не включает этот префикс, он добавляется к имени пакета, например rcairo -> ruby-rcairo.  Исключение составляют пакеты, оригинальное название которых содержит постфикс "-ruby", например "sqlite3-ruby".
+
=== Внутри тарбола ===
-
Префикс "ruby-module-" является излишним и допустим только для модулей, собирающихся из пакета ruby.
+
Внутри тарбола с модулем (или программой) могут находиться следующие файлы и каталоги:
-
Пакеты должны иметь группу Development/Ruby.
+
* '''bin/''' - скрипты, будут установлены в <tt>%_bindir</tt>;
 +
* '''ext/''' - компилируемые модули (при использовании <tt>setup.rb</tt>), будут установлены в <tt>%ruby_sitearchdir</tt>;
 +
* '''lib/''' - pure-ruby модули, будут установлены в <tt>%ruby_sitelibdir</tt>;
 +
* '''data/''' - произвольные данные(при использовании <tt>setup.rb</tt>), будут установлены в <tt>%_datadir/ИМЯМОДУЛЯ</tt>;
 +
* '''test/''' - unit-тесты;
-
Вышесказанное относится к pure-module пакетам, а не end-user программам, написанным на языке ruby (например alexandria).  В этом случае имя и группа пакета должны соответствовать действительности.
+
Также как правило присутствует один или несколько "сценариев сборки", о них расскажу далее.
-
Пакет должен иметь архитектуру noarch если не содержит архитектурно-зависимых компонентов, упаковка ruby не накладывает дополнительных ограничений.
+
=== Собираем модуль ===
-
=== Расположение файлов ===
+
Существует несколько, различного уровня "стандартности", способов сборки модулей ruby.
-
Все модули, которые могут использоваться в "обычной программе на языке ruby", должны помещать свои файлы в так называемые "vendor dirs", описываемые макросами <tt>%ruby_sitelibdir</tt> и <tt>%ruby_sitearchdir</tt>. Каталоги <tt>%ruby_libdir</tt> и <tt>%ruby_archdir</tt> используются исключительно для модулей, собирающихся из пакета ruby.
+
==== <tt>extconf.rb</tt> AKA MkMf ====
-
=== Макросы RPM ===
+
Аналог configure, использует модуль <tt>mkmf</tt>, входящий в стандартную поставку ruby.  Внутри скрипта проверяется наличие необходимых заголовочных файлов и библиотек, на выходе генерится Makefile, который обрабатывается стандартным make.  Используется только в тех проектах, где есть бинарные модули.  Если "рядом" с <tt>extconf.rb</tt> находится файл <tt>depend</tt>, его содержимое добавляется к <tt>Makefile</tt>.  Исходные тексты и заголовочные файлы бинарного модуля как правило тоже лежат "рядом" с <tt>extconf.rb</tt>.
-
Макросы находятся в пакете rpm-build-ruby.  Рекомендуется добавить в spec строчку:
+
Типичные секции <tt>%build</tt> и <tt>%install</tt> выглядят следующим образом:
-
<pre>BuildRequires(pre): rpm-build-ruby</pre>
+
<pre>%build
 +
%ruby_configure <опции extconf.rb>
 +
%make_build
-
{| border="1"
+
%install
-
|-
+
%make_install DESTDIR=%buildroot install</pre>
-
|
+
 
-
'''Макрос'''
+
==== <tt>setup.rb</tt> имени Minero Aoki ====
-
|
+
 
-
'''описание'''
+
Скрипт сборки и установки общего назначения.  Как правило используется для сборки и установки pure-ruby модулей.  Имеет некоторое количество стандартных опций, может собирать бинарные модули, находящиеся в каталоге '''ext/''' (как правило там присутствует <tt>extconf.rb</tt>, см. выше).
-
|-
+
 
-
|colspan="2"
+
Типичные секции <tt>%build</tt> и <tt>%install</tt> выглядят следующим образом:
-
''каталоги''
+
 
-
|-
+
<pre>%build
-
|
+
%ruby_config <опции setup.rb>
-
%ruby_sitearchdir
+
-
|
+
-
%_libdir/ruby/vendor_ruby/$VERSION/$ARCH/
+
-
|-
+
-
|
+
-
%ruby_sitelibdir
+
-
|
+
-
%_datadir/ruby/vendor_ruby/$VERSION/
+
-
|-
+
-
|
+
-
%ruby_archdir
+
-
|
+
-
%_libdir/ruby/$VERSION/$ARCH/
+
-
|-
+
-
|
+
-
%ruby_libdir
+
-
|
+
-
%_datadir/ruby/$VERSION/
+
-
|-
+
-
|
+
-
%ruby_siteincludedir
+
-
|
+
-
/usr/include/ruby/$VERSION/ (изменится в ruby >= 1.9)
+
-
|-
+
-
|
+
-
%ruby_includedir
+
-
|
+
-
/usr/include/ruby/$VERSION/
+
-
|-
+
-
|
+
-
%ruby_ri_sitedir
+
-
|
+
-
%_datadir/ri/$VERSION/site/
+
-
|-
+
-
|colspan="2"
+
-
''опции поиска зависимостей''
+
-
|-
+
-
|
+
-
%set_ruby_req_method
+
-
|
+
-
strict, normal или relaxed
+
-
|-
+
-
|
+
-
%add_ruby_lib_path
+
-
|
+
-
добавляет путь для поиска Provides
+
-
|-
+
-
|
+
-
%add_ruby_weakprov_path
+
-
|
+
-
добавляет путь для поиска "статически слинкованных" модулей (пример использования в пакете ruby-actionpack)
+
-
|-
+
-
|colspan="2"
+
-
''сборка и установка''
+
-
|-
+
-
|
+
-
%ruby_vendor
+
-
|
+
-
ruby -rvendor-specific
+
-
|-
+
-
|
+
-
%rdoc
+
-
|
+
-
вызов rdoc (для секции %indtall) с опциями --ri-site --all
+
-
|-
+
-
|colspan="2"
+
-
''setup.rb имени Minero Aoki''
+
-
|-
+
-
|
+
-
%_ruby_setup_rb
+
-
|
+
-
путь к setup.rb (по умолчанию setup.rb)
+
-
|-
+
-
|
+
-
%ruby_setup_rb
+
-
|
+
-
%ruby_vendor %_ruby_setup_rb
+
-
|-
+
-
|
+
-
%ruby_config
+
-
|
+
-
%ruby_setup_rb config
+
-
|-
+
-
|
+
%ruby_build
%ruby_build
-
|
 
-
%ruby_setup_rb setup
 
-
|-
 
-
|
 
-
%ruby_install
 
-
|
 
-
%ruby_setup_rb install --prefix=%buildroot
 
-
|-
 
-
|colspan="2"
 
-
''mkmf и extconf.rb''
 
-
|-
 
-
|
 
-
%ruby_configure
 
-
|
 
-
%ruby_vendor extconf.rb --ruby=/usr/bin/ruby
 
-
|-
 
-
|colspan="2"
 
-
''rake и Rakefile''
 
-
|-
 
-
|
 
-
%rake
 
-
|
 
-
%ruby_vendor %_bindir/rake
 
-
|-
 
-
|
 
-
%rake_install
 
-
|
 
-
DESTDIR=%buildroot %rake install
 
-
|}
 
 +
%install
 +
%ruby_install</pre>
 +
 +
==== <tt>install.rb</tt> ====
 +
 +
Иногда это самописный скрипт, иногда встречается одна из первых версий <tt>setup.rb</tt>.  Как правило используется только для установки pure-ruby модулей.  Стандартных макросов для поддержки <tt>install.rb</tt> нет.
 +
 +
==== <tt>Rakefile</tt> и остальные случаи ====
 +
 +
Сценарий для <tt>rake</tt>.  Обычно может иметь task'и <tt>build</tt> (если есть бинарные модули) и <tt>test</tt>, но последнее время не имеет task'а <tt>install</tt>.  Зато использует <tt>rubygems</tt>.
 +
 +
Для вызова <tt>rake</tt> и <tt>rake install</tt> есть два стандартных макроса <tt>%rake</tt> и <tt>%rake_install</tt> соответственно.
 +
 +
Если task <tt>install</tt> не определён или вообще отсутствует установочный скрипт (в случае pure-ruby модуля), можно использовать <tt>setup.rb</tt> из пакета <tt>ruby-tool-setup</tt> примерно следующим образом:
 +
 +
<pre>%prep
 +
%setup
 +
%patch -p1
 +
cp %_datadir/ruby-setup/setup.rb .</pre>
 +
 +
Далее используются макросы <tt>%ruby_config</tt>, <tt>%ruby_build</tt> и <tt>%ruby_install</tt>.
 +
 +
=== Пакуем документацию ===
 +
 +
Документация в формате RI генерируется при помощи утилиты <tt>rdoc</tt>, находящейся в пакете <tt>ruby-tool-rdoc</tt>.  Для этого существует стандартный макрос <tt>%rdoc</tt> предназначенный для использования в секции <tt>%install</tt> (обычно одной из последних строк).  В качестве аргументов этого макроса перечисляются файлы и каталоги с исходниками и при необходимости другие опции утилиты <tt>rdoc</tt>.
 +
 +
Для pure-ruby модулей как правило используется конструкция:
 +
 +
<pre>%rdoc lib/</pre>
 +
 +
Если присутствуют бинарные модули:
 +
 +
<pre>%rdoc *.c lib/</pre>
 +
 +
Документацию желательно паковать в подпакет <tt>%name-doc</tt>.  При этом паковать следует только документацию для основных классов модуля, описание расширений сторонних классов паковать не нужно.
 +
 +
=== Складываем файлы в пакеты ===
 +
 +
Pure-ruby модули помещаются в <tt>%ruby_sitelibdir</tt>, бинарные модули в <tt>%ruby_sitearchdir</tt>, документация в формате RI в <tt>%ruby_ri_sitedir</tt> (при этом файл <tt>%ruby_ri_sitedir/created.rid</tt> упаковывать не нужно).
 +
 +
=== Добро пожаловать в реальный мир ===
 +
 +
В теории всё выглядит красиво, однако на практике среднего размера модуль представляет собой "нечто", что может работать в любой помойке.  Однако мы делаем не помойку, поэтому местечковые хаки нам не нужны.
 +
 +
==== Не загрязняем <tt>$LOAD_PATH</tt> (<tt>$:</tt>) ====
-
=== Зависимости ===
+
Очень часто в коде можно увидеть конструкции вида:
-
В пакете rpm-build-ruby (начиная с версии 1:0.0.1-alt1 или в branch/4.1) реализован автоматический поиск зависимостей. Таким образом все зависимости, поставленные вручную, являются не только бесполезными, но и вредными (за исключением особых случаев).
+
<pre>$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')</pre>
-
Зависимости имеют вид <tt>ruby(ИМЯ/МОДУЛЯ)</tt> и не привязаны к конкретной версии ruby.  Для модулей, расположенных внутри каталогов, входящих в <tt>$LOAD_PATH</tt> дополнительно появляется зависимость на конкретный каталогСтандартные каталоги принадлежат пакету librubyЭто косвенная привязка к версии rubyТакие пакеты необходимо пересобрать при изменении версии ruby.
+
Эта конструкция добавляет в <tt>$LOAD_PATH</tt> некоторый путьСделано это для того, чтобы модуль (или исполняемый скрипт) можно было использовать из любого местаПоскольку в нашем случае все файлы пакуются в стандартные места, подобные конструкции не нужныВ большинстве случаев такие конструкции можно безболезненно удалить.
-
Зависимости, найденные внутри блоков begin/rescue и внутри методов не экспортируются.  Также не экспортируются зависимости, которые невозможно разрешить в имя модуля, например '<tt>require "rake/tasks/#{task}.raketask"</tt>'.
+
==== Используем существующие модули и размаскируем зависимости ====
-
В случае "маскирования" зависимостей в коде (например при помощи блока begin/rescue) мантейнер пакета должен самостоятельно принять решение, убрать "маскировку" патчем, добавить в спек <tt>Requires: ruby(ИМЯ/МОДУЛЯ)</tt> или оставить всё как есть в зависимости от величины ущерба для функциональности пакета.
+
Поскольку наша помойка не является "любой", её ТТХ нам известны.  Например, известно что это Linux, есть Iconv и так далее.  Поэтому специфичный код, предназначенный для работы на других платформах в наших пакетах не нужен (а иногда бывает и вреден, поскольку даже "мёртвый" код может порождать зависимости, которые в некоторых случаях превращаются в unmet'ы).
-
Автоматический поиск зависимостей не отслеживает изменение <tt>$LOAD_PATH</tt>, в этих случаях надо вручную добавить необходимые каталоги к путям поиска при помощи макроса <tt>%add_ruby_lib_path</tt>.
+
Также мы можем превратить опциональную зависимость в явную. Пример:
-
=== Rubygems ===
+
<pre>begin
 +
  require 'iconv'
 +
rescue LoadError
 +
  module Iconv
 +
    # Далее следует некоторый код, который в результате предоставляет API
 +
    # похожий на API модуля iconv, возможно урезанный функционально.
 +
  end
 +
end</pre>
-
Зависимость на rubygems является недистрибутивной, поскольку скрывает реальные зависимостиЗависимости пакетов должны разрешаться пакетами.
+
В данном случае '''всю''' эту сложную конструкцию можно заменить на одну строку <tt>require 'iconv'</tt>Как бонус мы получаем зависимость на <tt>ruby(iconv)</tt> и полную функциональность данного модуля.
-
Есть мнение, неоднократно доказанное экспериментально, что любой пакет можно отучить требовать rubygems без ущерба для его функциональности.
+
Реальные примеры можно посмотреть в пакете [http://git.altlinux.org/people/raorn/packages/?p=ruby-gettext.git ruby-gettext].

Версия 16:07, 28 июля 2008

Freesource-logo.png Blue Glass Arrow.svg MediaWiki logo.png
Эта страница была перемещена с freesource.info.
Эта страница наверняка требует чистки и улучшения — смело правьте разметку и ссылки.
Просьба по окончанию убрать этот шаблон со страницы.


Ruby

Основные правила сборки приложений и модулей ruby изложены в Ruby Packaging Policy. Цель же этого документа - об'яснить на простых примерах как следует поступать в различных ситуациях а также показать как можно собирать простые модули.

Я не рассматриваю rubygems, так как эта система включает в себя свой пакетный менеджер, не совместимый с дистрибутивным. Про отношение Debian к rubygems можно прочитать тут, рекомендации апстриму изложены тут. У нас ситуация в общем аналогичная.

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

Содержание


Общие принципы сборки модулей

Есть два способа сборки модулей. При помощи rubygems (который мы не рассматриваем по ряду причин), и "нативная" сборка, с помещением файлов в специальные каталоги, которые находятся в $LOAD_PATH.

Сборка пакета включает в себя:

  • компиляцию бинарных модулей (если они есть);
  • выполнение тестов (если они есть и их выполнение возможно в hasher);
  • установку файлов в соответствующие каталоги;
  • генерацию документации в формате ri (class reference);

Модули устанавливаются в так называемый vendor dir. В ALT Linux это /usr/share/ruby/vendor_ruby/RUBY.VERSION и /usr/lib/ruby/RUBY.VERSION/ARCHITECTURE. Поскольку по умолчанию установка модулей идёт в site dir, при сборке пакета надо использовать модуль vendor_specific, вызывая интерпретатор ruby как ruby -rvendor_specific. Для этого есть макрос %ruby.

Внутри тарбола

Внутри тарбола с модулем (или программой) могут находиться следующие файлы и каталоги:

  • bin/ - скрипты, будут установлены в %_bindir;
  • ext/ - компилируемые модули (при использовании setup.rb), будут установлены в %ruby_sitearchdir;
  • lib/ - pure-ruby модули, будут установлены в %ruby_sitelibdir;
  • data/ - произвольные данные(при использовании setup.rb), будут установлены в %_datadir/ИМЯМОДУЛЯ;
  • test/ - unit-тесты;

Также как правило присутствует один или несколько "сценариев сборки", о них расскажу далее.

Собираем модуль

Существует несколько, различного уровня "стандартности", способов сборки модулей ruby.

extconf.rb AKA MkMf

Аналог configure, использует модуль mkmf, входящий в стандартную поставку ruby. Внутри скрипта проверяется наличие необходимых заголовочных файлов и библиотек, на выходе генерится Makefile, который обрабатывается стандартным make. Используется только в тех проектах, где есть бинарные модули. Если "рядом" с extconf.rb находится файл depend, его содержимое добавляется к Makefile. Исходные тексты и заголовочные файлы бинарного модуля как правило тоже лежат "рядом" с extconf.rb.

Типичные секции %build и %install выглядят следующим образом:

%build
%ruby_configure <опции extconf.rb>
%make_build

%install
%make_install DESTDIR=%buildroot install

setup.rb имени Minero Aoki

Скрипт сборки и установки общего назначения. Как правило используется для сборки и установки pure-ruby модулей. Имеет некоторое количество стандартных опций, может собирать бинарные модули, находящиеся в каталоге ext/ (как правило там присутствует extconf.rb, см. выше).

Типичные секции %build и %install выглядят следующим образом:

%build
%ruby_config <опции setup.rb>
%ruby_build

%install
%ruby_install

install.rb

Иногда это самописный скрипт, иногда встречается одна из первых версий setup.rb. Как правило используется только для установки pure-ruby модулей. Стандартных макросов для поддержки install.rb нет.

Rakefile и остальные случаи

Сценарий для rake. Обычно может иметь task'и build (если есть бинарные модули) и test, но последнее время не имеет task'а install. Зато использует rubygems.

Для вызова rake и rake install есть два стандартных макроса %rake и %rake_install соответственно.

Если task install не определён или вообще отсутствует установочный скрипт (в случае pure-ruby модуля), можно использовать setup.rb из пакета ruby-tool-setup примерно следующим образом:

%prep
%setup
%patch -p1
cp %_datadir/ruby-setup/setup.rb .

Далее используются макросы %ruby_config, %ruby_build и %ruby_install.

Пакуем документацию

Документация в формате RI генерируется при помощи утилиты rdoc, находящейся в пакете ruby-tool-rdoc. Для этого существует стандартный макрос %rdoc предназначенный для использования в секции %install (обычно одной из последних строк). В качестве аргументов этого макроса перечисляются файлы и каталоги с исходниками и при необходимости другие опции утилиты rdoc.

Для pure-ruby модулей как правило используется конструкция:

%rdoc lib/

Если присутствуют бинарные модули:

%rdoc *.c lib/

Документацию желательно паковать в подпакет %name-doc. При этом паковать следует только документацию для основных классов модуля, описание расширений сторонних классов паковать не нужно.

Складываем файлы в пакеты

Pure-ruby модули помещаются в %ruby_sitelibdir, бинарные модули в %ruby_sitearchdir, документация в формате RI в %ruby_ri_sitedir (при этом файл %ruby_ri_sitedir/created.rid упаковывать не нужно).

Добро пожаловать в реальный мир

В теории всё выглядит красиво, однако на практике среднего размера модуль представляет собой "нечто", что может работать в любой помойке. Однако мы делаем не помойку, поэтому местечковые хаки нам не нужны.

Не загрязняем $LOAD_PATH ($:)

Очень часто в коде можно увидеть конструкции вида:

$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')

Эта конструкция добавляет в $LOAD_PATH некоторый путь. Сделано это для того, чтобы модуль (или исполняемый скрипт) можно было использовать из любого места. Поскольку в нашем случае все файлы пакуются в стандартные места, подобные конструкции не нужны. В большинстве случаев такие конструкции можно безболезненно удалить.

Используем существующие модули и размаскируем зависимости

Поскольку наша помойка не является "любой", её ТТХ нам известны. Например, известно что это Linux, есть Iconv и так далее. Поэтому специфичный код, предназначенный для работы на других платформах в наших пакетах не нужен (а иногда бывает и вреден, поскольку даже "мёртвый" код может порождать зависимости, которые в некоторых случаях превращаются в unmet'ы).

Также мы можем превратить опциональную зависимость в явную. Пример:

begin
  require 'iconv'
rescue LoadError
  module Iconv
    # Далее следует некоторый код, который в результате предоставляет API
    # похожий на API модуля iconv, возможно урезанный функционально.
  end
end

В данном случае всю эту сложную конструкцию можно заменить на одну строку require 'iconv'. Как бонус мы получаем зависимость на ruby(iconv) и полную функциональность данного модуля.

Реальные примеры можно посмотреть в пакете ruby-gettext.

 
Личные инструменты