Ruby Packaging mini-HOWTO: различия между версиями
SirRaorn (обсуждение | вклад) (/usr/share/ruby-setup/setup.rb - настоятельно рекомендуемый способ сборки pure-ruby модулей.) |
SirRaorn (обсуждение | вклад) Нет описания правки |
||
Строка 2: | Строка 2: | ||
{{Stub}} | {{Stub}} | ||
Основные правила сборки приложений и модулей ruby изложены в [[Ruby Policy|Ruby Packaging Policy]]. Цель этого | Основные правила сборки приложений и модулей ruby изложены в [[Ruby Policy|Ruby Packaging Policy]]. Цель этого документа ? объяснить на простых примерах, как следует поступать в различных ситуациях при упаковке ruby-приложений, а также показать, как можно собирать простые модули. | ||
<tt>rubygems</tt> не рассматриваются в данном HOWTO, так как их дополнительная функциональность (например, установка нескольких версий | <tt>rubygems</tt> не рассматриваются в данном HOWTO, так как их дополнительная функциональность (например, установка нескольких версий gem?а), необходимая в некоторых случаях (shared hosting, web-приложения), конфликтует с наличием дистрибутивного пакетного менеджера. Другие дистрибутивы занимают аналогичную позицию в отношении к rubygems: [http://pkg-ruby-extras.alioth.debian.org/rubygems.html позиция Debian], [http://pkg-ruby-extras.alioth.debian.org/upstream-devs.html их рекомендации апстриму]. | ||
Для ситуаций, в которых дополнительная функциональность <tt>rubygems</tt> необходима, сам <tt>rubygems</tt> упакован и работает. | Для ситуаций, в которых дополнительная функциональность <tt>rubygems</tt> необходима, сам <tt>rubygems</tt> упакован и работает. | ||
== Общие принципы сборки модулей == | |||
При сборке пакета в дистрибутив используется ?нативная? сборка ? с помещением файлов в специальные каталоги, которые находятся в <tt>$LOAD_PATH</tt>. | |||
При сборке пакета в дистрибутив используется | |||
Сборка включает в себя: | Сборка включает в себя: | ||
Строка 23: | Строка 21: | ||
Модули устанавливаются в так называемый 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>. | Модули устанавливаются в так называемый 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>. | ||
== Внутри тарбола == | |||
Внутри тарбола с модулем (или программой) могут находиться следующие файлы и каталоги: | Внутри тарбола с модулем (или программой) могут находиться следующие файлы и каталоги: | ||
* '''bin/''' | * '''bin/''' ? скрипты, будут установлены в <tt>%_bindir</tt>; | ||
* '''ext/''' | * '''ext/''' ? компилируемые модули (при использовании <tt>setup.rb</tt>), будут установлены в <tt>%ruby_sitearchdir</tt>; | ||
* '''lib/''' | * '''lib/''' ? pure-ruby модули, будут установлены в <tt>%ruby_sitelibdir</tt>; | ||
* '''data/''' | * '''data/''' ? произвольные данные(при использовании <tt>setup.rb</tt>), будут установлены в <tt>%_datadir/ИМЯМОДУЛЯ</tt>; | ||
* '''test/''' | * '''test/''' ? unit-тесты; | ||
Также как правило присутствует один или несколько | Также как правило присутствует один или несколько ?сценариев сборки?, о них расскажу далее. | ||
== Собираем модуль == | |||
Существует несколько, различного уровня | Существует несколько, различного уровня ?стандартности?, способов сборки модулей ruby. | ||
Забегая вперёд скажу, что крайне рекомендуется использовать для сборки <tt>setup.rb</tt> из пакета <tt>ruby-tool-setup</tt> (за исключением случаев, когда используется только <tt>extconf.rb</tt>). | Забегая вперёд скажу, что крайне рекомендуется использовать для сборки <tt>setup.rb</tt> из пакета <tt>ruby-tool-setup</tt> (за исключением случаев, когда используется только <tt>extconf.rb</tt>). | ||
=== <tt>extconf.rb</tt> AKA MkMf === | |||
Аналог configure, использует модуль <tt>mkmf</tt>, входящий в стандартную поставку ruby. Внутри скрипта проверяется наличие необходимых заголовочных файлов и библиотек, на выходе генерится Makefile, который обрабатывается стандартным make. Используется только в тех проектах, где есть бинарные модули. Если | Аналог configure, использует модуль <tt>mkmf</tt>, входящий в стандартную поставку ruby. Внутри скрипта проверяется наличие необходимых заголовочных файлов и библиотек, на выходе генерится Makefile, который обрабатывается стандартным make. Используется только в тех проектах, где есть бинарные модули. Если ?рядом? с <tt>extconf.rb</tt> находится файл <tt>depend</tt>, его содержимое добавляется к <tt>Makefile</tt>. Исходные тексты и заголовочные файлы бинарного модуля как правило тоже лежат ?рядом? с <tt>extconf.rb</tt>. | ||
Типичные секции <tt>%build</tt> и <tt>%install</tt> выглядят следующим образом: | Типичные секции <tt>%build</tt> и <tt>%install</tt> выглядят следующим образом: | ||
Строка 56: | Строка 54: | ||
</pre> | </pre> | ||
=== <tt>setup.rb</tt> имени Minero Aoki === | |||
Скрипт сборки и установки общего назначения. Как правило используется для сборки и установки pure-ruby модулей. Имеет некоторое количество стандартных опций, может собирать бинарные модули, находящиеся в каталоге '''ext/''' (как правило там присутствует <tt>extconf.rb</tt>, см. выше). | Скрипт сборки и установки общего назначения. Как правило используется для сборки и установки pure-ruby модулей. Имеет некоторое количество стандартных опций, может собирать бинарные модули, находящиеся в каталоге '''ext/''' (как правило там присутствует <tt>extconf.rb</tt>, см. выше). | ||
Строка 71: | Строка 69: | ||
</pre> | </pre> | ||
=== <tt>install.rb</tt> === | |||
Иногда это самописный скрипт, иногда встречается одна из первых версий <tt>setup.rb</tt>. Как правило используется только для установки pure-ruby модулей. Стандартных макросов для поддержки <tt>install.rb</tt> нет. | Иногда это самописный скрипт, иногда встречается одна из первых версий <tt>setup.rb</tt>. Как правило используется только для установки pure-ruby модулей. Стандартных макросов для поддержки <tt>install.rb</tt> нет. | ||
=== <tt>Rakefile</tt> и остальные случаи === | |||
Сценарий для <tt>rake</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> соответственно. | Для вызова <tt>rake</tt> и <tt>rake install</tt> есть два стандартных макроса <tt>%rake</tt> и <tt>%rake_install</tt> соответственно. | ||
Строка 94: | Строка 92: | ||
Очень часто в Rakefile включена поддержка создания .gem. Поскольку rubygems использовать при сборке пакетов нельзя, следует воспользоваться [http://pkg-ruby-extras.alioth.debian.org/upstream-devs.html рецептом из Debian]. | Очень часто в Rakefile включена поддержка создания .gem. Поскольку rubygems использовать при сборке пакетов нельзя, следует воспользоваться [http://pkg-ruby-extras.alioth.debian.org/upstream-devs.html рецептом из Debian]. | ||
=== Пакуем документацию | == Запускаем тесты == | ||
TBD | |||
== l10n при помощи <tt>ruby-gettext</tt> == | |||
TBD | |||
== Пакуем документацию == | |||
Документация в формате RI генерируется при помощи утилиты <tt>rdoc</tt>, находящейся в пакете <tt>ruby-tool-rdoc</tt>. Для этого существует стандартный макрос <tt>%rdoc</tt> предназначенный для использования в секции <tt>%install</tt> (обычно одной из последних строк). В качестве аргументов этого макроса перечисляются файлы и каталоги с исходниками и при необходимости другие опции утилиты <tt>rdoc</tt>. | Документация в формате RI генерируется при помощи утилиты <tt>rdoc</tt>, находящейся в пакете <tt>ruby-tool-rdoc</tt>. Для этого существует стандартный макрос <tt>%rdoc</tt> предназначенный для использования в секции <tt>%install</tt> (обычно одной из последних строк). В качестве аргументов этого макроса перечисляются файлы и каталоги с исходниками и при необходимости другие опции утилиты <tt>rdoc</tt>. | ||
Строка 112: | Строка 118: | ||
Документацию желательно паковать в подпакет <tt>%name-doc</tt>. При этом паковать следует только документацию для основных классов модуля, описание расширений сторонних классов паковать не нужно. | Документацию желательно паковать в подпакет <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> упаковывать не нужно). | 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>) === | |||
Очень часто в коде можно увидеть конструкции вида: | Очень часто в коде можно увидеть конструкции вида: | ||
Строка 130: | Строка 136: | ||
Эта конструкция добавляет в <tt>$LOAD_PATH</tt> некоторый путь. Сделано это для того, чтобы модуль (или исполняемый скрипт) можно было использовать из любого места. Поскольку в нашем случае все файлы пакуются в стандартные места, подобные конструкции не нужны. В большинстве случаев такие конструкции можно безболезненно удалить. | Эта конструкция добавляет в <tt>$LOAD_PATH</tt> некоторый путь. Сделано это для того, чтобы модуль (или исполняемый скрипт) можно было использовать из любого места. Поскольку в нашем случае все файлы пакуются в стандартные места, подобные конструкции не нужны. В большинстве случаев такие конструкции можно безболезненно удалить. | ||
=== Используем существующие модули и размаскируем зависимости === | |||
Поскольку наша помойка не является | Поскольку наша помойка не является ?любой?, её ТТХ нам известны. Например, известно что это Linux, есть Iconv и так далее. Поэтому специфичный код, предназначенный для работы на других платформах в наших пакетах не нужен (а иногда бывает и вреден, поскольку даже ?мёртвый? код может порождать зависимости, которые в некоторых случаях превращаются в unmet?ы). | ||
Также мы можем превратить опциональную зависимость в явную. Пример: | Также мы можем превратить опциональную зависимость в явную. Пример: | ||
Строка 151: | Строка 157: | ||
Реальные примеры можно посмотреть в пакете [http://git.altlinux.org/people/raorn/packages/?p=ruby-gettext.git ruby-gettext]. | Реальные примеры можно посмотреть в пакете [http://git.altlinux.org/people/raorn/packages/?p=ruby-gettext.git ruby-gettext]. | ||
=== Отрываем rubygems === | |||
В реальной жизни можно встретить множество вариантов использования rubygems. Вот самые распространённые: | В реальной жизни можно встретить множество вариантов использования rubygems. Вот самые распространённые: | ||
Строка 198: | Строка 204: | ||
Этим мы убираем зависимость на rubygems и размаскируем скрытые зависимости. Таким образом наш модуль автоматически получает зависимсти на <tt>ruby(some/module)</tt> и <tt>ruby(some/other/module)</tt>. | Этим мы убираем зависимость на rubygems и размаскируем скрытые зависимости. Таким образом наш модуль автоматически получает зависимсти на <tt>ruby(some/module)</tt> и <tt>ruby(some/other/module)</tt>. | ||
=== Файлы специального вида (темплейты и плагины) === | |||
Очень часто модуль (или приложение) носит с собой какие-то данные, которые обычно находятся в каталоге с | Очень часто модуль (или приложение) носит с собой какие-то данные, которые обычно находятся в каталоге с gem?ом. Выглядит это приблизительно вот так: | ||
<pre> | <pre> | ||
Строка 208: | Строка 214: | ||
</pre> | </pre> | ||
В таком случае этот каталог (в нашем примере | В таком случае этот каталог (в нашем примере ?<tt>plugins</tt>?) помещается в <tt>/usr/share/ИМЯМОДУЛЯ</tt> а код заменяется на такой: | ||
<pre> | <pre> | ||
Строка 216: | Строка 222: | ||
</pre> | </pre> | ||
=== Потрошим новый пакет === | |||
При сборке нового пакета следует пробежать глазами его код на тему вышеописанных конструкций. Начать можно с <tt>grep -r</tt> по каталогам <tt>lib/</tt> и <tt>bin/</tt> по паттернам '<tt>__FILE__</tt>', '<tt>ubygems</tt>', '<tt>LoadError</tt>', '<tt>\$:</tt>', '<tt>\$LOAD_PATH</tt>'. | При сборке нового пакета следует пробежать глазами его код на тему вышеописанных конструкций. Начать можно с <tt>grep -r</tt> по каталогам <tt>lib/</tt> и <tt>bin/</tt> по паттернам '<tt>__FILE__</tt>', '<tt>ubygems</tt>', '<tt>LoadError</tt>', '<tt>\$:</tt>', '<tt>\$LOAD_PATH</tt>'. | ||
<!-- vim: set ft=mediawiki spell spelllang=ru,en wrap lbr: --> | <!-- vim: set ft=mediawiki spell spelllang=ru,en wrap lbr: --> |
Версия от 17:58, 25 августа 2008
Основные правила сборки приложений и модулей ruby изложены в Ruby Packaging Policy. Цель этого документа ? объяснить на простых примерах, как следует поступать в различных ситуациях при упаковке ruby-приложений, а также показать, как можно собирать простые модули.
rubygems не рассматриваются в данном HOWTO, так как их дополнительная функциональность (например, установка нескольких версий gem?а), необходимая в некоторых случаях (shared hosting, web-приложения), конфликтует с наличием дистрибутивного пакетного менеджера. Другие дистрибутивы занимают аналогичную позицию в отношении к 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.
Забегая вперёд скажу, что крайне рекомендуется использовать для сборки setup.rb из пакета ruby-tool-setup (за исключением случаев, когда используется только extconf.rb).
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.
Очень часто в Rakefile включена поддержка создания .gem. Поскольку rubygems использовать при сборке пакетов нельзя, следует воспользоваться рецептом из Debian.
Запускаем тесты
TBD
l10n при помощи ruby-gettext
TBD
Пакуем документацию
Документация в формате 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.
Отрываем rubygems
В реальной жизни можно встретить множество вариантов использования rubygems. Вот самые распространённые:
Пример 1:
require 'rubygems' require 'some/module' require 'some/other/module'
Пример 2:
begin require 'rubygems' require 'some/module' require 'some/other/module' rescue LoadError require 'some/module' require 'some/other/module' end
Пример 3:
begin require 'some/module' require 'some/other/module' rescue LoadError require 'rubygems' require 'some/module' require 'some/other/module' end
Все эти три примера можно привести к одному виду:
require 'some/module' require 'some/other/module'
Этим мы убираем зависимость на rubygems и размаскируем скрытые зависимости. Таким образом наш модуль автоматически получает зависимсти на ruby(some/module) и ruby(some/other/module).
Файлы специального вида (темплейты и плагины)
Очень часто модуль (или приложение) носит с собой какие-то данные, которые обычно находятся в каталоге с gem?ом. Выглядит это приблизительно вот так:
def mock_framework_path(framework_name) File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "plugins", "mock_frameworks", framework_name)) end
В таком случае этот каталог (в нашем примере ?plugins?) помещается в /usr/share/ИМЯМОДУЛЯ а код заменяется на такой:
def mock_framework_path(framework_name) File.expand_path(File.join("/usr/share/ИМЯМОДУЛЯ/plugins/mock_frameworks", framework_name)) end
Потрошим новый пакет
При сборке нового пакета следует пробежать глазами его код на тему вышеописанных конструкций. Начать можно с grep -r по каталогам lib/ и bin/ по паттернам '__FILE__', 'ubygems', 'LoadError', '\$:', '\$LOAD_PATH'.