Готовый перевод Bash by example / Bash в примерах: Part 3: Exploring the ebuild system

Внимание! Этот перевод, возможно, ещё не готов, так как модераторы установили для него статус «идёт перевод»

Введение в систему ебилдов.

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

Для этой статьи я приготовил изрядную порцию примеров, исользуемых в реальной жизни. А именно в проекте, на разработку и поддержку которого я потратил многие часы: системы ебилдов в Gentoo Linux.

Так как я являюсь создателем Gentoo Linux, а также человеком, создавшим Funtoo Linux - в число моих прямых обязанностей входит контроль того, что все пакеты операционной системы (похожие на пакеты RPM) правильно собраны и работают совместно без конфликтов. Как вы, вероятно, знаете, стандартная система Linux представляет из себя не единое унифицированное дерево исходников (как BSD), а состоит из примерно 25 с хвостиком основных пакетов, работающих совместно. Некоторые из пакетов включают в себя:

Пакет Описание
linux Действующее ядро
util-linux Набор разнообразных Linux-программ
e2fsprogs Набор утилит для работы с файловой системой ext2
glibc Библиотека GNU C

Примечание:
Фанаты Gentoo, имейте в виду, что заявление выше значит именно это: "Я главный архитектор Gentoo Linux, операционной системы нового поколения, основанной на Linux, находящейся в настоящий момент в beta стадии. Одна из моих прямых обязанностей - контроль за тем, что все бинарные пакеты (типа RPM) правильно собраны и работают совместно без конфликтов". Я акцентирую на этом внимание потому, что одной из основных целей создание Gentoo - было предоставление бинартных пакетов, которые на самом деле работают.

Каждый пакет входящий в тарболл поддерживаеться независимыми разработчиками, или командами разработчиков. Для создания дистрибутива, каждый пакет отдельно загружается, компилируется и упаковывается. Каждый раз когда пакет исправляется, обновляется или улучшается, компиляция и упаковка повторяются (это происходит довольно часто). Для исключения повторяющихся сложных действий при создании и обновлении пакетов, я создал ебилд систему, написанную полностью на Bash. Для увеличения ваших знаний Bash, я показываю применение распаковки и компилирования частей в ебилд системе, шаг за шагом. Объясняю каждое действие, а также почему оно имеет такой вид. В конце этой статьи, даже если вы имеете опыт крупномасштабных проектов в bash програмировании, вы также имеете возможность получить объяснения по созданию завершенной автоматически созданной системы.

Почему bash?

Bash - существенный компонент системы ебилдов Gentoo Linux. Он был выбран в качестве основново языка ебилдов по ряду причин. Во-первых он имеет простой и знакомый синтаксис, что особенно хорошо подходит для вызова внешних программ.

Обзор процесса сборки

Прежде чем мы рассмотрим систему ебилдов, давайте посмотрим что принимает участие в компиляции и инсталляции пакета. В нашем примере, мы будет рассматривать пакет "sed", являющийся стандартной GNU утилитой для потокового редактирования текста и входящий во все Linux дистрибутивы. В начале скачаем архив с исходными кодами (sed-3.02.tar.gz) (смотри Ресурсы). Мы будем хранить этот архив в /usr/src/distfiles, данную директорию будем использовать в значении переменной $DISTDIR. $DISTDIR это директория где находятся все наши оригинальные архивы с исходниками; это большое хранилище исходных кодов.

Следующим нашим шагом будет создание временной директории work, в которой будут находиться разархивированные исходные коды. Мы будем ссылаться на эту директорию позже, используя переменную $WORKDIR. Перейдите в директорию в которой мы имеем право на запись и выполните следующие команды:

$ mkdir work
$ cd work
$ tar xzf /usr/src/distfiles/sed-3.02.tar.gz

Архив разархивируется, создав директорию sed-3.02, содержащую все исходные коды. Мы будем позже ссылаться на директорию sed-3.02, используя переменную $SRCDIR. Для компиляции программы введите следующее:

$ cd sed-3.02
$ ./configure --prefix=/usr
(autoconf создаст make-файлы, это может занять некоторое время)

$ make

(пакет собирается из исходников, это также займет некоторое время)

Мы пропустим шаг "make install", поскольку мы рассмотрим только шаги распаковки и компиляции в этой статье. Если мы захотели бы написать bash-скрипт для выполнения всех этих шагов, то он выглядел бы примерно так:

#!/usr/bin/env bash

if [ -d work ]
then
# Удаление страых директорий если существуют
rm -rf work
fi
mkdir work
cd work
tar xzf /usr/src/distfiles/sed-3.02.tar.gz
cd sed-3.02
./configure --prefix=/usr
make

Обобщение кода

Хотя этот скрипт работает, он не очень гибкий. В основном bash-скрипт содержит только список всех команд, которые были набраны в командной строке. Хоть это решение и работает, неплохо было бы создать общий скрипт, который можно быстро настроить для распаковки и компиляции любого пакета, просто изменив несколько строк. Таким образом, он помогает создателям пакетов делать гораздо меньше работы во время добавления новых пакетов в дистрибутив. Давайте добъемся этого, используя множество различных переменных, делая наш скрипт более общим:

#!/usr/bin/env bash

# P имя пакета

P=sed-3.02

# A имя архива

A=${P}.tar.gz

export ORIGDIR=pwd
export WORKDIR=${ORIGDIR}/work
export SRCDIR=${WORKDIR}/${P}

if [ -z "$DISTDIR" ]
then
# назначение переменной DISTDIR пути /usr/src/distfiles если не назначено раньше
DISTDIR=/usr/src/distfiles
fi
export DISTDIR

if [ -d ${WORKDIR} ]
then
# удаление старого рабочего каталога если существует
rm -rf ${WORKDIR}
fi

mkdir ${WORKDIR}
cd ${WORKDIR}
tar xzf ${DISTDIR}/${A}
cd ${SRCDIR}
./configure --prefix=/usr
make

Мы добавили много переменных в скрипт, но он продолжает выполнять тоже самое. Хотя, сейчас для компиляции любого стандартного архива основанного на GNU autoconf, мы можем просто скопировать этот скрипт в новый файл (с соответствующим именем, означающим название нового пакета, который он компилирует), и затем изменить значения переменных $A и $P на нужные. Все остальные переменные автоматически принимают правильные значения, и скрипт работает как надо. Хоть это и удобно, есть еще одно улучшение, которое можно сделать с кодом скрипта. Данный код намного длиннее чем оригинальный "нетронутый" сценарий, который мы создали. Поскольку одной из целей для любого проекта должно стать снижение сложности для пользователя, было бы неплохо резко сократить код, или, по крайней мере лучше его организовать. Мы можем сделать это, выполнив изящный трюк - мы разделим код на два отдельных файла. Сохраните этот файл как sed-3.02.ebuild:

# Файл sed ebuild - очень просто!
P=sed-3.02
A=${P}.tar.gz

Наш первый файл тривиален, и содержит только те переменные окружения, которые должны быть изменены для различных пакетов. Теперь рассмотрим второй файл, который содержит мозг операции. Сохраните это в файл "ebuild" и сделайте его исполняемым:

#!/usr/bin/env bash


if [ $# -ne 1 ]
then
echo "принимается один аргумент."
exit 1
fi

if [ -e "$1" ]
then
source $1
else
echo "ebuild файл $1 не найден."
exit 1
fi

export ORIGDIR=pwd
export WORKDIR=${ORIGDIR}/work
export SRCDIR=${WORKDIR}/${P}

if [ -z "$DISTDIR" ]
then
# присваиваем переменной DISTDIR значение /usr/src/distfiles если не было присвоено раньше
DISTDIR=/usr/src/distfiles
fi
export DISTDIR

if [ -d ${WORKDIR} ]
then
# удаление старого рабочего каталога если он существовал
rm -rf ${WORKDIR}
fi

mkdir ${WORKDIR}
cd ${WORKDIR}
tar xzf ${DISTDIR}/${A}
cd ${SRCDIR}
./configure --prefix=/usr
make

Теперь когда мы разбили нашу систему сборки на два файла, я уверен, вам станет интересно как это работает. В принципе, для компиляции sed, наберите:

$ ./ebuild sed-3.02.ebuild

После запуска "ebuild", он сначала пытается выполнить "source" переменной $1. Что это означает? Из предыдущей главы, напомню, что $1 является первым аргументом командной строки -- в этом случае, sed-3.02.ebuild. В bash, команда "source" считывает все инструкции из указанного файла, и выполняет их как если бы они сразу находились в файле, содержащем команду "source". Таким образом, "source ${1}" заставляет "ebuild" скрипт выполнить команды из файла sed-3.02.ebuild, которые определяют переменные $P и $A. Это изменение конструкции очень удобно, потому что если мы захотим собрать другую программу вместо sed, мы можем просто создать новый .ebuild файл и передать его в качестве аргумента для нашего "ebuild" скрипта. Таким образом, .ebuild файлы становятся очень простыми, в то время как мозг системы ебилдов содержется в одном месте -- в нашем "ebuild" скрипте. Мы можем обновить или расширить систему ебилдов просто редактируя "ebuild" скрипт, сохраняя детали реализации за пределами ebuild файлов. Вот пример ebuild файла для gzip:

#другой действительно простой ebuild скрипт!
P=gzip-1.2.4a
A=${P}.tar.gz

Добавление функциональности

ОК, мы добились некоторого прогресса. Однако, есть некоторая дополнительная функциональность которую я бы хотел добавить. Я хочу чтобы ebuild скрипт принимал второй аргумент командной строки, который будет принимать значение compile, unpack или all. Этот второй аргумент сообщает ebuild скрипту какой шаг необходимо выполнить. Таким образом, я могу сказать ebuild скрипту разархивировать архив, но не компилировать его (на всякий случай мне нужно проверить исходный архив перед компиляцией). Для этого, я добавлю оператор case который будет проверять переменную $2, и делать различные вещи, основываясь на ее значении. Вот этот код:

#!/usr/bin/env bash

if [ $# -ne 2 ]
then
echo "Пожалуйста укажите два аргумента - .ebuild файл и unpack, compile или all"
exit 1
fi


if [ -z "$DISTDIR" ]
then
# присваиваем переменной DISTDIR значение /usr/src/distfiles если не было присвоено раньше
DISTDIR=/usr/src/distfiles
fi
export DISTDIR

ebuild_unpack() {
#убеждаемся что мы находимся в нyжной директории
cd ${ORIGDIR}

if [ -d ${WORKDIR} ]
then
rm -rf ${WORKDIR}
fi

mkdir ${WORKDIR}
cd ${WORKDIR}
if [ ! -e ${DISTDIR}/${A} ]
then
echo "${DISTDIR}/${A} не доступно. Пожалуйста вначале скачайте архив."
exit 1
fi
tar xzf ${DISTDIR}/${A}
echo "Unpacked ${DISTDIR}/${A}."
#теперь исходники разархивированы правильно
}


ebuild_compile() {

#убеждаемся что мы находимся в нyжной директории
cd ${SRCDIR}
if [ ! -d "${SRCDIR}" ]
then
echo "${SRCDIR} не доступно -- пожалуйста, выполните вначале unpack."
exit 1
fi
./configure --prefix=/usr
make
}

export ORIGDIR=pwd
export WORKDIR=${ORIGDIR}/work

if [ -e "$1" ]
then
source $1
else
echo "Ebuild файл $1 не найден."
exit 1
fi

export SRCDIR=${WORKDIR}/${P}

case "${2}" in
unpack)
ebuild_unpack
;;
compile)
ebuild_compile
;;
all)
ebuild_unpack
ebuild_compile
;;
*)
echo "Пожалуйста укажите unpack, compile или all в качестве второго аргумента"
exit 1
;;
esac

Мы сделали много изменений, поэтому давайте рассмотрим их. Во-первых, мы разместили шаги компиляции и распаковки в их собственные функции, и назвали их ebuild_compile () и ebuild_unpack (), соответственно. Это хороший шаг, так как код становится более сложным, и новые функции предоставляют некоторую модульность, которая помогает держать код более организовано. В первой строке каждой функции, я явно выполняю cd в нужный каталог, так как хочу быть увереным, потому что наш код становится более модульным, а не линейным, поэтому вероятно, что мы можем ошибиться и выполнить функцию в неправильном рабочем каталоге. Команды cd явно переносят нас в нужное место, и не дает нам сделать ошибку позже - это важный шаг - особенно, если вы будете удалять файлы внутри функции.

Кроме того, я добавил полезную проверку в начало функции ebuild_compile(). Теперь, проверяется, что переменная $SRCDIR существует, и если ее нет, то появляется сообщение об ошибке, говорящее пользователю о необходимости вначале распаковать архив, а затем происходит выход. Если вы хотите, вы можете изменить это поведение так, что если $SRCDIR не существует, наши ebuild скрипты будут распаковать исходный архив автоматически. Вы можете сделать это, заменив функцию ebuild_compile () следующим кодом:

ebuild_compile() {
#убеждаемся что мы находимся в правильной директории
if [ ! -d "${SRCDIR}" ]
then
ebuild_unpack
fi
cd ${SRCDIR}
./configure --prefix=/usr
make
}

Одно из наиболее очевидных изменений в нашей второй версии ebuild скрипта это новый оператор case в конце кода. Этот оператор case просто проверяет второй аргумент командной строки и выполняет правильное действие, в зависимости от его значения. Если мы теперь введем:

$ ebuild sed-3.02.ebuild

Мы действительно получим сообщение об ошибке. Ebuild теперь хочет чтобы ему сообщали, что ему делать:

$ ebuild sed-3.02.ebuild unpack

или:

$ ebuild sed-3.02.ebuild compile

или:

$ ebuild sed-3.02.ebuild all

Важно!
Если второй аргумент будет не таким, как перечислено выше, то вы увидите сообщение об ошибке (поле * оператора case), и программа завершит работу.

Конфигурационные файлы

В заключение

Ресурсы

Дэниэль Роббинс
Дэниэль Роббинс — основатель сообщества Gentoo и создатель операционной системы Gentoo Linux. Дэниэль проживает в Нью-Мехико со свой женой Мэри и двумя энергичными дочерьми. Он также основатель и глава Funtoo, написал множество технических статей для IBM developerWorks, Intel Developer Services и C/C++ Users Journal.

Внимание! Этот перевод, возможно, ещё не готов.
Его статус: идёт перевод

Переведено на Нотабеноиде
http://notabenoid.org/book/14299/46939

Переводчики: neumann16, Daemon786, bugzy, undeceived

zlyoha

Настройки

Готово:

66.66% КП = 1.1

Непереведённые фрагменты будут пропущены.