Детально описание rpm-пакетов, и особенности их создания описаны тут (оригинал, перевод).
В данной статье будет дана краткая справка для ленивых =).
*.spec файл. В spec-файл записывается следующая информация:
Кроме исходников и патчей все дополнительные файлы src.rpm пакета должны быть скопированы в "корневую директорию бинарного пакета" в секции %install, а также должны быть указаны в секции %files.
Подробнее про spec-файл можно почитать тут.
В случае если из одного дерева исходников получается несколько программных продуктов (например серверное и клиентское приложение, которые могут быть установлены отдельно), то рациональнее написать один spec-файл, который будет собирать несколько rpm-пакетов (в указанном случае один пакет для серверного приложения, и один для клиентского приложения). Дополнительные пакеты называются субпакеты. Подробнее о субпакетах следует почитать тут и, обязательно тут. Отдельно замечу, что можно сделать так, что бы из spec-файла создавались бинарные rpm только для субпакетов, для этого в spec-файле должна отсутствовать секция %files без имения субпакета ( это секция %files для основного пакета ).
Под заголовком понимается выставление значений для директив: Name, Version, и так далее.
В упрощённом виде, необходимо задать значения для директив:
В случае, если ваш пакет зависит от стороннего ПО определённой версии, и в системе, под которую вы собираете ПО для rpm-пакета стороннего ПО указана эпоха (Epoch), то следует, обязательно, в зависимостях указать эпоху пакета ( лучше такую же, что и у оригинального стороннего пакета ).
Например: Requires: qt >= 1:4.7.4
Подробнее про эту тонкость можно прочитать тут.
Достаточно подробно об этапе подготовке к сборке ПО описано тут.
По сути, в данной секции макрос %setup распаковывает исходники в директорию сборки, и накладывает на распакованные исходники патчи (если это указано).Если не требуется наложить патчи на исходники, или ещё какой-экзотики, то секция %prep выглядит так:
Достаточно подробно об этапе сборки ПО описано тут.
По сути в данной секции записывается shell-скрипт, который выполняется в корневой директории распакованного тарбола (BUILD - обычно это папка ~/rpmbuild/BUILD/<tarbol_unpacket_folder> - например ~/rpmbuild/BUILD/qt-everywhere-opensource-src-4.7.4) с исходниками.
Пример секции %build:
Упрощая: RPM_BUILD_ROOT - это директория, которая является корнем для файловой системы бинарных rpm-пакетов (обычно это ~/rpmbuild/BUILDROOT/<packet-name> например ~/rpmbuild/BUILD/qt-4.7.4-4.el6.386)
Достаточно подробно об этапе подготовке к сборке ПО описано тут.
По сути, это скрипт, который тоже выполняется в директории BUILD.
Лучше всего, если в Makefile-е будет определена цель install - тогда достаточно просто вызывать INSTALL_ROOT="$RPM_BUILD_ROOT" make install
Если же цель install не определена в make-файле, тогда необходимо в данной секции расписать копирование необходимых файлов, с учётом того, что вместо корня ( / ) путь для файлов следует начинать с $RPM_BUILD_ROOT. Копирование надо расписать в синтаксисе shell-скрипта. Если необходимо для копируемых файлов создать папки, то их создание должно быть прописано до копирования, также, в виде shell-скрипта.
Пример секции %install:
Подробно это расписано тут (а также в подпунктах параграфа 9 из руководства, указанного ссылке).
Особенное внимание следует уделить информации, относительно файлов конфигураций и создаваемых во время работы ПО файлов, которые должны быть удалены при удалении пакетов, которую можно прочитать тут.
Добавлю, что для субпакетов надо писать отдельную секцию %files с именем субпакета через пробел. Также рекомендуется в начале секции пакета/субпакета, указывать атрибуты по умолчанию через %detattr.
Пример секции %files:
Подробнее об этом написано тут.
Обязательно обратите внимание на правильность формата даты, иначе при сборке пакета получите сообщение об ошибке.
Пример заполнения секции %changelog
В директорию SPECS следует положить написанный нами spec-файл. В директорию SOUCES, следует положить исходники ( тарбол, патчи, картинки и т.д. ).
Обычно в качестве системы контроля версий я использую git. Приведу пример скрипта в одном из проектов, который автоматически пакует необходимые для сборки файлы в тарбол в директорию ~/rpmbuild/SOUCES, а также на основе шаблона spec-файла и данных, возвращаемых комадой git describe формирует spec файл для сборки rpm-ок для текущей версии ПО.
В данной статье будет дана краткая справка для ленивых =).
Общая информация
RPM пакеты бывают двух видов:- бинарные rpm-пакеты (<name>.rpm)
- пакеты исходников (<name>.src.rpm)
- файлы, которые необходимо установить на компьютер
- пути, по которым эти файлы должны располагаться
- права доступа к этим файлам
- скрипты, которые должны выполняться в следующих ситуациях:
- перед установкой пакета (%pre)
- после установки пакета (%post)
- перед удалением пакета (%preun)
- после удаления пакета (%postun)
*.spec файл. В spec-файл записывается следующая информация:
- Общая информация о пакете/пакетах.
- Информация об исходниках
- Скрипт подготовки к сборке (%prep)
- Скрипт сборки ПО из исходников (%build)
- Скрипт инсталляции ПО (%install) ( тут осуществляется установка собранных файлов в "корневую директорию бинарного пакета" ).
- Скрипты установки/удаления. (%pre,%post,%preun,%postun).
- Информация о файлах (%files) ( описание файлов, их атрибутов из"корневой директории бинарного пакета", которые войдут в пакет ).
- История изменений (%changelog) данного spec-файла.
Кроме исходников и патчей все дополнительные файлы src.rpm пакета должны быть скопированы в "корневую директорию бинарного пакета" в секции %install, а также должны быть указаны в секции %files.
Состав spec-файла
Spec-файл - это текстовый файл, содержащий директивы RPM.Подробнее про spec-файл можно почитать тут.
В случае если из одного дерева исходников получается несколько программных продуктов (например серверное и клиентское приложение, которые могут быть установлены отдельно), то рациональнее написать один spec-файл, который будет собирать несколько rpm-пакетов (в указанном случае один пакет для серверного приложения, и один для клиентского приложения). Дополнительные пакеты называются субпакеты. Подробнее о субпакетах следует почитать тут и, обязательно тут. Отдельно замечу, что можно сделать так, что бы из spec-файла создавались бинарные rpm только для субпакетов, для этого в spec-файле должна отсутствовать секция %files без имения субпакета ( это секция %files для основного пакета ).
Заголовок и описание пакета
Достаточно подробно про заголовок и описание пакета написано тут.Под заголовком понимается выставление значений для директив: Name, Version, и так далее.
В упрощённом виде, необходимо задать значения для директив:
- Name (обязательно);
- Version (обязательно);
- Release (обязательно);
- Epoch (в случае необходимости, описано ниже);
- Requires (обязательно, для корректной установки пакета);
- BuildRequires (крайне желательно, для корректной пересборки бинарных пакетов, из src.rpm пакета);
- Provides (крайне желательно, в случае если вы заменяете установленный в системе пакет, для того, что бы RPM установил, что данный пакет предоставляет теже функции, что и ранее установленный пакет, и старый пакет можно обновить);
- Group ( желательно для графический менеджеров, для отображения информации о вашем ПО в соответствующих разделах меню;
- Summary ( желательно, для того, что бы можно было прочитать краткую информацию о вашем пакете, например в графическом менеджере, или в терминале, удобно при администрировании );
В случае, если ваш пакет зависит от стороннего ПО определённой версии, и в системе, под которую вы собираете ПО для rpm-пакета стороннего ПО указана эпоха (Epoch), то следует, обязательно, в зависимостях указать эпоху пакета ( лучше такую же, что и у оригинального стороннего пакета ).
Например: Requires: qt >= 1:4.7.4
Подробнее про эту тонкость можно прочитать тут.
Подготовка к сборке ПО
Подготовка к сборке выполняется в секции %prep.Достаточно подробно об этапе подготовке к сборке ПО описано тут.
По сути, в данной секции макрос %setup распаковывает исходники в директорию сборки, и накладывает на распакованные исходники патчи (если это указано).Если не требуется наложить патчи на исходники, или ещё какой-экзотики, то секция %prep выглядит так:
%prep
%setup -q
%setup -q
Сборка ПО
Cборка выполняется в секции %build.Достаточно подробно об этапе сборки ПО описано тут.
По сути в данной секции записывается shell-скрипт, который выполняется в корневой директории распакованного тарбола (BUILD - обычно это папка ~/rpmbuild/BUILD/<tarbol_unpacket_folder> - например ~/rpmbuild/BUILD/qt-everywhere-opensource-src-4.7.4) с исходниками.
Пример секции %build:
%build
qmake -r "CONFIG+=static" %{silent_cmd}
#make as quickly as possible
cpu_count=$(cat /proc/cpuinfo | grep ^processor | wc -l)
make -j${cpu_count} %{silent_cmd}
#сборка документации (требуется много дополнительных пакетов)
docs/latex/make_qthelp.sh %{silent_cmd}
В примере скрипт выполняет следующие действия:
qmake -r "CONFIG+=static" %{silent_cmd}
#make as quickly as possible
cpu_count=$(cat /proc/cpuinfo | grep ^processor | wc -l)
make -j${cpu_count} %{silent_cmd}
#сборка документации (требуется много дополнительных пакетов)
docs/latex/make_qthelp.sh %{silent_cmd}
- Через вызов qmake, формирует Makefile для сбора релизной версии ПО с определённой конфигурацией.
- Считывает из /proc/cpuinfo количество процессоров, для более быстрой сборки исходников.
- Запускает команду make (по умолчанию цель all), для которой указывает количество потоков выполнения, равным количеству процессоров в системе, для более быстрой сборки.
- Выполняет отдельный скрипт, для сборки файлов документации.
Инсталляция ПО
Инсталляция ПО в директорию RPM_BUILD_ROOT осуществляется в секции %install.Упрощая: RPM_BUILD_ROOT - это директория, которая является корнем для файловой системы бинарных rpm-пакетов (обычно это ~/rpmbuild/BUILDROOT/<packet-name> например ~/rpmbuild/BUILD/qt-4.7.4-4.el6.386)
Достаточно подробно об этапе подготовке к сборке ПО описано тут.
По сути, это скрипт, который тоже выполняется в директории BUILD.
Лучше всего, если в Makefile-е будет определена цель install - тогда достаточно просто вызывать INSTALL_ROOT="$RPM_BUILD_ROOT" make install
Если же цель install не определена в make-файле, тогда необходимо в данной секции расписать копирование необходимых файлов, с учётом того, что вместо корня ( / ) путь для файлов следует начинать с $RPM_BUILD_ROOT. Копирование надо расписать в синтаксисе shell-скрипта. Если необходимо для копируемых файлов создать папки, то их создание должно быть прописано до копирования, также, в виде shell-скрипта.
Пример секции %install:
%install
#запускаем make install
INSTALL_ROOT="$RPM_BUILD_ROOT" make install
# создаём папку doc - для файлов документации
install -d "$RPM_BUILD_ROOT/%{tds_install_path}/doc"
# копируем файлы документации в папку doc ( это не прописано в Makefile-е для цели install)
cp docs/latex/tds_cpanel.qch "$RPM_BUILD_ROOT/%{tds_install_path}/doc"
cp docs/latex/tds_cpanel.qhc "$RPM_BUILD_ROOT/%{tds_install_path}/doc"
#запускаем make install
INSTALL_ROOT="$RPM_BUILD_ROOT" make install
# создаём папку doc - для файлов документации
install -d "$RPM_BUILD_ROOT/%{tds_install_path}/doc"
# копируем файлы документации в папку doc ( это не прописано в Makefile-е для цели install)
cp docs/latex/tds_cpanel.qch "$RPM_BUILD_ROOT/%{tds_install_path}/doc"
cp docs/latex/tds_cpanel.qhc "$RPM_BUILD_ROOT/%{tds_install_path}/doc"
Очистка после инсталляции ПО
После сборки и инсталляции ПО, крайне желательно очистить директорию RPM_BUILD_ROOT. Если этого не сделать, то сохранившиеся файлы будут влиять на сборку следующего пакета. Это выполняется в секции %clean
Пример секции %clean
%clean
rm -rf "$RPM_BUILD_ROOT"
rm -rf "$RPM_BUILD_ROOT"
Определение скриптов установки/удаления
К описанному тут добавлю следующее:- для субпакетов после маркера скрипта необходимо указывать имя субпакета ( тоже, что указывали в макросу %package данного субпакета )
- при смене версии пакета ( вне зависимости от того, обновляем мы пакет до более новой версии, или откатываем пакет к более старой версии ), порядок выполнения скриптов следующий:
- %pre скрипт устанавливаемого пакета
- %post скрипт устанавливаемого пакета
- %preun скрипт удаляемого пакета
- %postun скрипт удаляемого пакета.
%pre server
if (( ${1} > 1 )); then
# upgrade
stop tds-server || true
fi
%post server
if (( ${1} == 1 )); then
# new installation
grep ^%{tds_user} /etc/passwd > /dev/null || useradd -r -U -M -s /bin/bash -d %{tds_install_path} %{tds_user}
TDS_ARCHIVE_DIR=$(dialog --stdout --title "Specify archive's path" --clear --dselect %{default_archive_path} 10 60)
case $? in
0)
echo "Select ${TDS_ARCHIVE_DIR}";;
1)
TDS_ARCHIVE_DIR="%{default_archive_path}";;
255)
TDS_ARCHIVE_DIR="%{default_archive_path}";;
esac
[ -d "${TDS_ARCHIVE_DIR}" ] || mkdir -p ${TDS_ARCHIVE_DIR}
chown -R %{tds_user}:%{tds_user} ${TDS_ARCHIVE_DIR}
echo -e "
description \"Topaz's documentation server\"
exec su -l tds -c '/usr/local/Lemz/tds/tds_server --archive_path ${TDS_ARCHIVE_DIR} --listen_port 3108' >> /var/log/tds/tds_server.log 2>&1
start on (runlevel [5])
stop on (runlevel [!5])
respawn
respawn limit 10 90
" > /etc/init/tds-server.conf
fi
start tds-server
%preun server
if (( $1 == 0 )); then
# removing package (not upgrade)
stop tds-server || true
rm -f /etc/init/tds-server.conf
fi
%postun server
if (( $1 == 0 )); then
# removing package (not upgrade)
userdel %{tds_user}
ls -A %{tds_install_path}/* || rm -rf %{tds_install_path}
ls -A %{lemz_install_path}/* || rm -rf %{lemz_install_path}
rm -rf /var/log/tds
fi
Как видно из примера, в данных скриптах используется синтаксис shell-скриптов, плюс к этому, можно использовать значения rpm-переменных через форму %{varname} (например %{tds_install_path}).if (( ${1} > 1 )); then
# upgrade
stop tds-server || true
fi
%post server
if (( ${1} == 1 )); then
# new installation
grep ^%{tds_user} /etc/passwd > /dev/null || useradd -r -U -M -s /bin/bash -d %{tds_install_path} %{tds_user}
TDS_ARCHIVE_DIR=$(dialog --stdout --title "Specify archive's path" --clear --dselect %{default_archive_path} 10 60)
case $? in
0)
echo "Select ${TDS_ARCHIVE_DIR}";;
1)
TDS_ARCHIVE_DIR="%{default_archive_path}";;
255)
TDS_ARCHIVE_DIR="%{default_archive_path}";;
esac
[ -d "${TDS_ARCHIVE_DIR}" ] || mkdir -p ${TDS_ARCHIVE_DIR}
chown -R %{tds_user}:%{tds_user} ${TDS_ARCHIVE_DIR}
echo -e "
description \"Topaz's documentation server\"
exec su -l tds -c '/usr/local/Lemz/tds/tds_server --archive_path ${TDS_ARCHIVE_DIR} --listen_port 3108' >> /var/log/tds/tds_server.log 2>&1
start on (runlevel [5])
stop on (runlevel [!5])
respawn
respawn limit 10 90
" > /etc/init/tds-server.conf
fi
start tds-server
%preun server
if (( $1 == 0 )); then
# removing package (not upgrade)
stop tds-server || true
rm -f /etc/init/tds-server.conf
fi
%postun server
if (( $1 == 0 )); then
# removing package (not upgrade)
userdel %{tds_user}
ls -A %{tds_install_path}/* || rm -rf %{tds_install_path}
ls -A %{lemz_install_path}/* || rm -rf %{lemz_install_path}
rm -rf /var/log/tds
fi
Заполнение списка файлов
Заполнение списка файлов, которые будут установлены в систему, при установке пакетов, а также какие надо/не надо удалить при удалении пакетов, осуществляется в секции %files.Подробно это расписано тут (а также в подпунктах параграфа 9 из руководства, указанного ссылке).
Особенное внимание следует уделить информации, относительно файлов конфигураций и создаваемых во время работы ПО файлов, которые должны быть удалены при удалении пакетов, которую можно прочитать тут.
Добавлю, что для субпакетов надо писать отдельную секцию %files с именем субпакета через пробел. Также рекомендуется в начале секции пакета/субпакета, указывать атрибуты по умолчанию через %detattr.
Пример секции %files:
%files server
# бинарный файл сервера
%attr(0555,root,root) %{tds_install_path}/tds_server
# папка для логов сервера
%attr(1777,root,root) /var/log/tds
%files cpanel
# бинарный файл клиента
%attr(0735,root,root) %{tds_install_path}/tds_cpanel
# папка для встроенной в клиент справки
%attr(1777,root,root) %{tds_install_path}/doc
# файлы справки
%doc %attr(0444,root,root) %{tds_install_path}/doc/tds_cpanel.qch
%doc %attr(0444,root,root) %{tds_install_path}/doc/tds_cpanel.qhc
Также добавлю, что если в директории RPM_BUILD_ROOT ( куда скопировали файлы при инсталляции ), будут файлы, которые, так или иначе, не указаны в какой-либо секции %files, то сборка пакета завершится с ошибкой. Такие файлы надо, либо удалять в конце секции %install, либо помечать их в какой-либо секции %files через макрос %exclude.# бинарный файл сервера
%attr(0555,root,root) %{tds_install_path}/tds_server
# папка для логов сервера
%attr(1777,root,root) /var/log/tds
%files cpanel
# бинарный файл клиента
%attr(0735,root,root) %{tds_install_path}/tds_cpanel
# папка для встроенной в клиент справки
%attr(1777,root,root) %{tds_install_path}/doc
# файлы справки
%doc %attr(0444,root,root) %{tds_install_path}/doc/tds_cpanel.qch
%doc %attr(0444,root,root) %{tds_install_path}/doc/tds_cpanel.qhc
История изменения пакета
История изменения пакета записывается в секцию %changelog.Подробнее об этом написано тут.
Обязательно обратите внимание на правильность формата даты, иначе при сборке пакета получите сообщение об ошибке.
Пример заполнения секции %changelog
%changelog
* Thu Dec 30 2015 Rinat Gadelshin <rinat@example.com> - 4.7.4-1
- Initial build for Oracle linux 6.5 i686
* Thu Dec 30 2015 Rinat Gadelshin <rinat@example.com> - 4.7.4-1
- Initial build for Oracle linux 6.5 i686
Сборка и отладка rpm-пакета
Для сборки rpm-пакетов используется утилита rpmbuild.
Также для сборки под различные дистрибутивы можно использовать mock. Но это отдельная тема, выходящая за рамки данной справки.
Самый "чистый" способ, это собирать бинарные пакеты в том же дистрибутиве и на той же архитектуре, как и те, для которых пакеты предназначены. Для этого можно развернуть образ в виртуальной машине ( например в VirtualBox ).
В примере ниже мы будем собирать пакеты для Oracle Linux 6 на этом же дистрибутиве.
Подготавливаем структуру папок. По умолчанию rpmbuild "работает" в директории ~/rpmbuild. Этот путь можно изменить в ~/.rpmmacros. Подробнее в man-е.
Создадим структуру папок, для сборки пакетов:
Также для сборки под различные дистрибутивы можно использовать mock. Но это отдельная тема, выходящая за рамки данной справки.
Самый "чистый" способ, это собирать бинарные пакеты в том же дистрибутиве и на той же архитектуре, как и те, для которых пакеты предназначены. Для этого можно развернуть образ в виртуальной машине ( например в VirtualBox ).
В примере ниже мы будем собирать пакеты для Oracle Linux 6 на этом же дистрибутиве.
Подготовка к сборке
Устанавливаем утилиту rpmbuild: [root@grinvb ~] yum install rpm-buildПодготавливаем структуру папок. По умолчанию rpmbuild "работает" в директории ~/rpmbuild. Этот путь можно изменить в ~/.rpmmacros. Подробнее в man-е.
Создадим структуру папок, для сборки пакетов:
[user@grinvb ~] mkdir -p ~/rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}
Назначение директорий следующее:BUILD | Утилита rpmbuild использует этот каталог в качестве каталога сборки ПО. |
RPMS | Утилита rpmbuild помещает в этот каталог собранные бинарные rpm-пакеты. |
SOURCES | В этот каталог необходимо поместить тарболлы с исходным кодом проектов, запланированных на сборку. |
SPECS | В этот каталог помещаются spec-файлы всех rpm-пакетов, которые запланированы на сборку. |
SRPMS | Утилита rpmbuild помещает в этот каталог собранные src.rpm-пакеты с исходным кодом. |
Маленький совет по подготовке к сборке rpm-пакетов для ПО
Обычно разработку я осуществляю не в директории ~/rpmbuild. Соответственно, мне нужно упаковать свои исходники для сборки в tarbol. Также при упаковке следует учитывать, что там должно быть достаточно исходников, что бы из них можно было пересобрать rpm-файлы для других дистрибутивов/платформ ( а вот лишние файлы увеличат размер тарбола, а вслед за ним и srp.rpm архива ). Также хочется автоматизировать процесс установки текущей версии ПО для rpm-ок.Обычно в качестве системы контроля версий я использую git. Приведу пример скрипта в одном из проектов, который автоматически пакует необходимые для сборки файлы в тарбол в директорию ~/rpmbuild/SOUCES, а также на основе шаблона spec-файла и данных, возвращаемых комадой git describe формирует spec файл для сборки rpm-ок для текущей версии ПО.
#!/bin/bash
# Говорим, где у нас находится корень для rpmbuild
rpmbuild_root=~/rpmbuild
# Запоминаем оригинальную директорию ( откуда вызван скрипт )
prevdir=$(pwd)
curdir=$(dirname ${0})
# Указываем, где относительно скрипта располагается корень проекта
repodir=$(cd ${curdir}/../../;pwd) # workspace/tds
reponame=$(basename ${repodir}) # tds
# Переходим в корневую папку проекта
cd ${repodir}
# Обновляемся из репозитория до последней версии, включая подмодули
git pull
git submodule update --init --recursive
# Запоминаем в переменные текущую версию и релиз ( на основе git tags )
tds_version=$(git describe --tags --abbrev=0)
tds_release=$(echo $(git describe --tags)-0-0 | cut -d- -f2)
# Формируем имя тарблоа, в соответствии со считанной версией ПО
tarbol=${rpmbuild_root}/SOURCES/tds-${tds_version}.tar.gz
# Переходим на папку выше каталога с исходиками,
# для того, что упаковать этот каталог
cd "${repodir}/.." # workspace
# Удаляем старый тарбол, если он есть
[ -e "${tarbol}" ] && rm "${tarbol}"
# Упаковываем в тарбол, каталог с исходниками, исключая из него файлы,
# не требующиеся для непосредственной сборки проекта
tar c --exclude-vcs \
--exclude=*.pro.user \
--exclude=Makefile \
--exclude=tds/.git* \
--exclude=tds/bin \
--exclude=tds/bin/* \
--exclude=tds/modules/ads_libs/tests \
--exclude=tds/modules/ads_libs/tests/* \
--exclude=tds/modules/ads_libs/apps \
--exclude=tds/modules/ads_libs/apps/* \
--exclude=tds/scripts \
--exclude=tds/scripts/* \
--exclude=tds/modules/ads_libs/ads_libs.pro \
--exclude=tds/modules/ads_libs/.git* \
--exclude=tds/modules/ads_libs/docs \
--exclude=tds/modules/ads_libs/docs/* \
--exclude=tds/modules/ads_libs/libs/tds_protocol \
--exclude=tds/modules/ads_libs/libs/tds_protocol/* \
--exclude=tds/modules/ads_libs/libs/ads_essential \
--exclude=tds/modules/ads_libs/libs/ads_essential/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/lib_ucl/ucl-1.03 \
--exclude=tds/modules/ads_libs/libs/3rdparty/lib_ucl/ucl-1.03/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/pcap \
--exclude=tds/modules/ads_libs/libs/3rdparty/pcap/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/ads_codec_g726 \
--exclude=tds/modules/ads_libs/libs/3rdparty/ads_codec_g726/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/segvcatch \
--exclude=tds/modules/ads_libs/libs/3rdparty/segvcatch/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/asn \
--exclude=tds/modules/ads_libs/libs/3rdparty/asn/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/jrtp \
--exclude=tds/modules/ads_libs/libs/3rdparty/jrtp/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/breakpad \
--exclude=tds/modules/ads_libs/libs/3rdparty/breakpad/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/ksv_srv_ctl \
--exclude=tds/modules/ads_libs/libs/3rdparty/ksv_srv_ctl/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/portaudio \
--exclude=tds/modules/ads_libs/libs/3rdparty/portaudio/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/qtlockedfile \
--exclude=tds/modules/ads_libs/libs/3rdparty/qtlockedfile/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/pixfc-sse \
--exclude=tds/modules/ads_libs/libs/3rdparty/pixfc-sse/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/qt47_backports \
--exclude=tds/modules/ads_libs/libs/3rdparty/qt47_backports/* \
--exclude=tds/src/apps/tds_readinput* \
--exclude=tds/src/libs/tds_readinput* \
--exclude=tds/src/libs/tds_endpoint* \
--file "${tarbol}" \
"${reponame}"
# Из шаблона ( scripts/rpm/tds.spec ) на основании считанных версий и релиза
# формируем ~/rpmbuild/SPECS/tds.spec для сборки rpm-пакетов
spectarget="${rpmbuild_root}/SPECS/tds.spec"
cat "${repodir}/scripts/rpm/tds.spec" | sed "s/tds-version-sed/${tds_version}/" | sed "s/tds-release-sed/${tds_release}/" > ${spectarget}
# Возвращаемся в оригинальную директорию ( откуда вызван скрипт )
cd ${prevdir}
# Говорим, где у нас находится корень для rpmbuild
rpmbuild_root=~/rpmbuild
# Запоминаем оригинальную директорию ( откуда вызван скрипт )
prevdir=$(pwd)
curdir=$(dirname ${0})
# Указываем, где относительно скрипта располагается корень проекта
repodir=$(cd ${curdir}/../../;pwd) # workspace/tds
reponame=$(basename ${repodir}) # tds
# Переходим в корневую папку проекта
cd ${repodir}
# Обновляемся из репозитория до последней версии, включая подмодули
git pull
git submodule update --init --recursive
# Запоминаем в переменные текущую версию и релиз ( на основе git tags )
tds_version=$(git describe --tags --abbrev=0)
tds_release=$(echo $(git describe --tags)-0-0 | cut -d- -f2)
# Формируем имя тарблоа, в соответствии со считанной версией ПО
tarbol=${rpmbuild_root}/SOURCES/tds-${tds_version}.tar.gz
# Переходим на папку выше каталога с исходиками,
# для того, что упаковать этот каталог
cd "${repodir}/.." # workspace
# Удаляем старый тарбол, если он есть
[ -e "${tarbol}" ] && rm "${tarbol}"
# Упаковываем в тарбол, каталог с исходниками, исключая из него файлы,
# не требующиеся для непосредственной сборки проекта
tar c --exclude-vcs \
--exclude=*.pro.user \
--exclude=Makefile \
--exclude=tds/.git* \
--exclude=tds/bin \
--exclude=tds/bin/* \
--exclude=tds/modules/ads_libs/tests \
--exclude=tds/modules/ads_libs/tests/* \
--exclude=tds/modules/ads_libs/apps \
--exclude=tds/modules/ads_libs/apps/* \
--exclude=tds/scripts \
--exclude=tds/scripts/* \
--exclude=tds/modules/ads_libs/ads_libs.pro \
--exclude=tds/modules/ads_libs/.git* \
--exclude=tds/modules/ads_libs/docs \
--exclude=tds/modules/ads_libs/docs/* \
--exclude=tds/modules/ads_libs/libs/tds_protocol \
--exclude=tds/modules/ads_libs/libs/tds_protocol/* \
--exclude=tds/modules/ads_libs/libs/ads_essential \
--exclude=tds/modules/ads_libs/libs/ads_essential/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/lib_ucl/ucl-1.03 \
--exclude=tds/modules/ads_libs/libs/3rdparty/lib_ucl/ucl-1.03/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/pcap \
--exclude=tds/modules/ads_libs/libs/3rdparty/pcap/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/ads_codec_g726 \
--exclude=tds/modules/ads_libs/libs/3rdparty/ads_codec_g726/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/segvcatch \
--exclude=tds/modules/ads_libs/libs/3rdparty/segvcatch/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/asn \
--exclude=tds/modules/ads_libs/libs/3rdparty/asn/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/jrtp \
--exclude=tds/modules/ads_libs/libs/3rdparty/jrtp/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/breakpad \
--exclude=tds/modules/ads_libs/libs/3rdparty/breakpad/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/ksv_srv_ctl \
--exclude=tds/modules/ads_libs/libs/3rdparty/ksv_srv_ctl/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/portaudio \
--exclude=tds/modules/ads_libs/libs/3rdparty/portaudio/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/qtlockedfile \
--exclude=tds/modules/ads_libs/libs/3rdparty/qtlockedfile/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/pixfc-sse \
--exclude=tds/modules/ads_libs/libs/3rdparty/pixfc-sse/* \
--exclude=tds/modules/ads_libs/libs/3rdparty/qt47_backports \
--exclude=tds/modules/ads_libs/libs/3rdparty/qt47_backports/* \
--exclude=tds/src/apps/tds_readinput* \
--exclude=tds/src/libs/tds_readinput* \
--exclude=tds/src/libs/tds_endpoint* \
--file "${tarbol}" \
"${reponame}"
# Из шаблона ( scripts/rpm/tds.spec ) на основании считанных версий и релиза
# формируем ~/rpmbuild/SPECS/tds.spec для сборки rpm-пакетов
spectarget="${rpmbuild_root}/SPECS/tds.spec"
cat "${repodir}/scripts/rpm/tds.spec" | sed "s/tds-version-sed/${tds_version}/" | sed "s/tds-release-sed/${tds_release}/" > ${spectarget}
# Возвращаемся в оригинальную директорию ( откуда вызван скрипт )
cd ${prevdir}
Данный скрипт легко адаптировать под свой проект и свою систему контроля версий ( svn, hg, etc... ).
В шаблоне speс-файла ( scrpits/rpm/tds.spec ) по сути записан полноценный spec-файл, за следующими исключениями:
В шаблоне speс-файла ( scrpits/rpm/tds.spec ) по сути записан полноценный spec-файл, за следующими исключениями:
- Для поля Version задан текст tds-version-sed
- Для поля Release задан текст tds-release-sed
Сборка пакетов
Достаточно подробно сборка пакетов при помощи утилиты rpmbuild описана тут.
В большинстве случаев следует набрать команду:
rpmbuild -ba --target $(uname -m) --clean <path to spec-file>
С этими опциями будут собираться бинарные пакеты и src.rpm пакет для текущей архитектуры (той же, что и архитектура машины, на которой выполняется команда), и после сборки все сгенерённые файлы будут удалены.
При успешном выполнении в папке RPMS будут находится бинарные rpm-пакеты (в подпапке своей архитектуры, например i686). А в папке SRPMS будет находится src.rpm пакет.
В случае если при сборке пакета возникла ошибка, то следует:
- Идентифицировать стадию, на которой возникла ошибка (%prep, %build, %install, %files). Для этого надо посмотреть на лог выполнения в обратном порядке, и найти одну из этих надписей, как можно ближе к концу вывода.
- Проанализировать ошибку и внести исправления в spec-файл, либо добавить недостающее для сборки ПО, не забыв дописать его в BuildRequires директиву в spec-файле.
- Запустить сборку пакета с той стадии, на которой возникла ошибка, для этого надо воспользоваться опцией --short-circuit. Например для возобновления сборки с секции %install: rpmbuild -bi --short-circuit ~/rpmbuild/SPECS/tds.spec
После того, как все ошибки сборки исправлены и все стадии пройдены успешно, следует пересобрать пакеты полностью ( с опциями -ba --target --clean ).
Подписывание пакета
Для идентификации того, кто поставляет пакет ( на первом этапе, это сборщик пакета ), необходимо подписывать пакет. Стандартным механизмом для подписи пакетов является GPG-подпись. Для использования этого механизма необходимо сделать следующее:
- Сгенерировать пару GPG-ключей.
- Сконфигурировать RPM для использование ключа.
- Подписать пакет.
- Экспортировать публичный ключ и выложить его в общедоступное место.
Генерация пары GPG-ключей
Для генерации ключа необходимо вызвать команду gpg --gen-key и отвечать на задаваемые вопросы.
Пример генерации ключа.
grin@grin:~$ gpg --gen-key gpg (GnuPG) 1.4.16; Copyright (C) 2013 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Please select what kind of key you want: (1) RSA and RSA (default) (2) DSA and Elgamal (3) DSA (sign only) (4) RSA (sign only) Your selection? 1 RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) Requested keysize is 2048 bits Please specify how long the key should be valid. 0 = key does not expire= key expires in n days w = key expires in n weeks m = key expires in n months y = key expires in n years Key is valid for? (0) Key does not expire at all Is this correct? (y/N) y You need a user ID to identify your key; the software constructs the user ID from the Real Name, Comment and Email Address in this form: "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de> Real name: Rinat Gadelshin Email address: rgadelsh@gmail.com Comment: grin You selected this USER-ID: "Rinat Gadelshin (grin)<rgadelsh@gmail.com> Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O You need a Passphrase to protect your secret key. We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. Not enough random bytes available. Please do some other work to give the OS a chance to collect more entropy! (Need 237 more bytes) ......+++++ ...........................+++++ We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. ..+++++ +++++ gpg: key 52D7329A marked as ultimately trusted public and secret key created and signed. gpg: checking the trustdb gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model gpg: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u pub 2048R/52D7329A 2015-01-26 Key fingerprint = 565C 5622 BD66 F17B DDB0 0378 CBF8 F0C1 52D7 329A uid Rinat Gadelshin (grin) <rgadelsh@gmail.com> sub 2048R/A394DD73 2015-01-26
В процессе генерации случайных данных (после того как в лог первый раз вывелась надпись We need to generate a lot of random bytes), я открыл флэш-игрушку в браузере ( стрелялка ) и играл в неё, пока не увидел, что ключ сгенерировался ( увидел надпись gpg: checking the trustdb ).
Пароль, заданный для данного ключа надо обязательно запомнить, мы будем вводить его каждый раз, когда будем подписывать пакет.
Также стоить запомнить идентификатор сгенерённого ключа ( в примере выше: 52D7329A ).
Посмотреть список сгенерённых ключей можно вызвав команду gpg --fingerprint
grin@grin:~$ gpg --fingerprint /home/grin/.gnupg/pubring.gpg ----------------------------- pub 2048R/8EF3B046 2015-01-26 Key fingerprint = EBBC 7265 2A5E B117 E397 E63E FE30 9913 8EF3 B046 uid Rinat Gadelshin <rgadelsh@gmail.com> sub 2048R/97A7AD93 2015-01-26 pub 2048R/52D7329A 2015-01-26 Key fingerprint = 565C 5622 BD66 F17B DDB0 0378 CBF8 F0C1 52D7 329A uid Rinat Gadelshin (grin) <rgadelsh@gmail.com> sub 2048R/A394DD73 2015-01-26
Конфигурирование RPM для использования ключа
Для того, что бы подписывать rpm-пакеты сгенерённым ключом, необходимо в файл ~/.rpmmacros добавить следующие строки:
%_signature gpg %_gpg_name Rinat Gadelshin (grin) <rgadelsh@gmail.com>
По сути, мы прописываем строку uid, относящуюся к нашему ключу из вывода команды gpg --fingerprint. Видно что для нашего ключа ( 52D7329A ) строка содержит комментарий (grin), а для другого ключа ( 8EF3B046 ) комментарий не содержится.
Подпись пакетов
Для подписи пакетов, при их создании, для rpmbuild требуется указать опцию --sign, тогда, перед сборкой пакета (до выполнения секции %prep), он будет спрашивать у вас пароль для ключа (конфигурация которого прописана в ~/.rpmmacros). Дальнейшую сборку rpmbuild будет продолжать только, если вы указали правильный пароль.
Подписать можно и готовый пакет.
Для добавления пакету подписи нужно вызвать команду
rpm --addsign <rpm-packet-file>
Аналогично команда
rpm --resign <rpm-packet-file>
позволяет изменить подпись у подписанного rpm-пакета.
Подписать можно и готовый пакет.
Для добавления пакету подписи нужно вызвать команду
rpm --addsign <rpm-packet-file>
Аналогично команда
rpm --resign <rpm-packet-file>
позволяет изменить подпись у подписанного rpm-пакета.
Экспорт ключа
При установке пакета RPM проверяет его подпись. В случае его ключ, которым подписан пакет, уже добавлен в базу RPM, то он устанавливает пакет без вопросов. В противном случае он уведомляет пользователя, что пакет подписан неизвестным ему ключом, или не подписан вовсе. Можно конечно поставить из терминала пакет без подписей, но это не верно, с точки зрения безопасности, и не каждый грамотный администратор на это пойдёт.
Для того, что бы RPM мог добавить себе в список доверенных ключей gpg-ключ, его открытую (public) часть необходимо сначала экспортировать в файл, который в дальнейшем RPM сможет импортировать.
Экспорт ключа осуществляется командой
gpg --export --armor 52D7329A > /tmp/grin-second-gpg-key
Вместо 52D7329A необходимо поставить идентификатор своего ключа. Вместо /tmp/grin-second-gpg-key можно поставить другой путь для файла экспортированного ключа.
Для того, что бы другие машины могли импортировать этот ключ, необходимо обеспечить свободный доступ к нему. Например его можно выложить на свой web-сервер.
После этого на прочих системах достаточно выполнить команду
rpm --import http://server.name/grin-second-gpg-key
Где вместо server.name/grin-second-gpg-key указать путь к файлу экспортированного ключа.
Экспорт ключа осуществляется командой
gpg --export --armor 52D7329A > /tmp/grin-second-gpg-key
Вместо 52D7329A необходимо поставить идентификатор своего ключа. Вместо /tmp/grin-second-gpg-key можно поставить другой путь для файла экспортированного ключа.
Для того, что бы другие машины могли импортировать этот ключ, необходимо обеспечить свободный доступ к нему. Например его можно выложить на свой web-сервер.
После этого на прочих системах достаточно выполнить команду
rpm --import http://server.name/grin-second-gpg-key
Где вместо server.name/grin-second-gpg-key указать путь к файлу экспортированного ключа.
Комментариев нет:
Отправить комментарий