Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР 130, ОКТЯБРЬ 2009 г.

Танцы с бубном и напильником. Все, что ты хотел знать о сборке из исходников

Евгений Зобнин (zobnin@gmail.com)

Рано или поздно все мы сталкиваемся с необходимостью сборки софта из исходников. Причин тому огромное множество, а проблем, сопровождающих этот процесс, еще больше. Что выбрать – архив tar.gz или CVS-срез? Как накладывать патчи? Что делать, если в исходниках нет скрипта configure? Как побороть ошибки компиляции? Как создать дистрибутивный пакет и заставить программу работать? Ответы на эти и многие другие вопросы ты найдешь в этой статье.

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

  • Получение исходных текстов
  • Применение патчей
  • Подготовка к сборке
  • Конфигурирование и сборка
  • Проблемы компиляции
  • Проблемы компиляции в BSD
  • Установка
  • Проблемы запуска

Получение исходных текстов

Получить исходные тексты приложения можно несколькими способами. Самый простой и наименее трудозатратный - скачать архив tar.gz или tar.bz2 (или даже tar.lzma) с официального сайта разработчиков. В этом случае достаточно распаковать полученный файл с помощью одной из приведенных ниже команд и перейти к следующему разделу статьи.

$ tar xvzf имя.архива.tar.gz

или

$ tar xvjf имя.архива.tar.bz2

Примечание: если в твоей *nix-системе утилита tar не поддерживает флаг '-j', то задействуй конструкцию: «bunzip2 < имя.архива.tar.bz2 | tar xvf -».
Однако это не всегда будет самым удачным выбором. Команды разработчиков некоторых проектов допускают очень длительные перерывы между выпусками релизов своих детищ (вплоть до нескольких лет!), продолжая втихую работать над проектом. Причем это совсем даже не намек на огромное количество багов, которые программисты чинят днями и ночами - просто необходимый объем функциональности еще не накопился.
Выход из ситуации кроется в том, чтобы побродить по сайту разработчиков и найти ссылку на ежемесячные/еженедельные/ежедневные снапшоты. Если же ничего подобного на горизонте не виднеется, тогда бегом в святую святых - VCS-репозиторий исходных текстов проекта.

Использование репозитория VCS (системы контроля версий, к коим можно отнести CVS, Subversion, Git, Hg и множество других продуктов) в качестве источника «свежатинки» несет в себе массу полезнейших побочных эффектов. Тут и возможность всегда быть «на острие атаки», когда ты собираешь программу, последнее изменение в которую внесли 15 минут назад, и быстрое обновление (VCS выкачивают только изменившиеся файлы во время обновления), и чувство причастности к чему-то важному, в конце концов. Но есть и отрицательный момент - нестабильность хранящегося в VCS кода, которым, правда, можно пренебречь: в больших и серьезных проектах экспериментальные и особо неустойчивые нововведения обычно производят в отдельных ветках, а уже после вносят в основной код.

Учитывая , что в последнее время развелось множество различных систем контроля версий, беру на себя обязанность описать процесс получения исходных текстов из репозитория каждой из них:

CVS - уже мало где используется, но имеет место быть:

$ sudo apt-get install cvs
$ cvs -z3 -d:pserver:anonymous@cvs.bochs.sf.net:/cvsroot/bochs checkout bochs

Subversion - используется в проектах, размещенных на sf.net и code.google.com:

$ sudo apt-get install subversion
$ svn checkout http://inferno-ds.googlecode.com/svn/trunk/ inferno-ds-read-only

Git - особо любима Linux-разработчиками, используется на kernel.org и github.com:

$ sudo apt-get install git-core
$ git clone git://github.com/russel/scons.git

Mercurial - code.google.com и множество других, более мелких, хостингов:

$ sudo apt-get install mercurial
$ hg clone https://inferno-os.googlecode.com/hg/ inferno-os

Применение патчей

Исходные тексты – это еще не все. Иногда возникает необходимость в наложении хитрых патчей на приложения с целью расширить их функционал или исправить баг (выпуск неофициальных патчей, временно устраняющих сложно уловимые баги в коде - частая практика в среде Open Source). Что делать?Все просто.

Первое: следует убедиться, что патч создан для той версии программы, исходниками которой ты завладел - небольшие расхождения в версиях допустимы, но не рекомендуемы.

Второе: просмотреть патч, возможно в нем содержится бэкдор. Третье: проверить, гладко ли накладывается патч, и накладывается ли он вообще:

$ cd исходники_программы
$ patch --dry-run -p1 < /путь/к/патчу.patch

Если все ОК, можно запустить процедуру модификации:

$ patch -p1 < /путь/к/патчу.patch

Патчи могут распространяться в сжатом виде (с расширением .gz или .bz2). В этом случае процедура наложения будет выглядеть так:

$ gzip -cd патч.gz | patch -p0

или

$ bzip2 -cd патч.bz2 | patch -p0

Не бойся экспериментировать, потому что изменения, созданные патчем, всегда можно отменить, запустив команду patch с флагом '-R'.

Подготовка к сборке

Перед тем как приступить к сборке приложения, хорошо бы убедиться в том, что все необходимые инструменты установлены и готовы к использованию. Во-первых, тебе понадобится собственно сам компилятор, содержащийся в пакетах gcc-* или тому подобных. Во-вторых, компоновщик и архиватор библиотек из пакета binutils. В-третьих, заголовочные файлы стандартной библиотеки языка Си - пакет libc-dev, и, конечно же, утилита make. Во многих дистрибутивах все это можно получить через установку специального мета-пакета. Пример для Ubuntu:

$ sudo apt-get install build-essential

Файл README, содержащийся в корне архива, обязателен к прочтению. Как правило, в нем описаны все необходимые приложению зависимости, а также рекомендации по сборке и ответы на вопросы. INSTALL тоже можно проглядеть, но обычно он является стандартной копией одноименного файла, поставляемого с набором утилит autotools, и не представляет интереса.

Конфигурирование и сборка

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

Традиционно в качестве такой системы использовался покрытый сединой Makefile, в котором были описаны правила компиляции и установки приложения. Однако простота его синтаксиса вынуждала программистов выполнять двойную работу (написание самого приложения, плюс написание большого Makefile, который учитывал все особенности низлежащей ОС, самостоятельно находил библиотеки и т.д.) Поэтому в рамках проекта GNU был разработан набор утилит autotools, которые автоматизировали 95% этой работы и генерировали готовый к использованию Makefile. Позднее autotools, написанные на sh и perl, разрослись и стали настолько неудобны, что были разработаны альтернативные системы сборки, наиболее популярны из которых scons и cmake.

К чему это я? К тому, что от используемой системы сборки напрямую зависит то, какие команды придется выполнять пользователю, чтобы, имея исходные тексты приложения, получить его бинарник. Например, в системах, основанных на Makefile (которые легко идентифицировать по наличию одноименного файла в корне архива с исходниками), для сборки используется привычная команда make, а конфигурация (пути установки, набор включаемых в приложение компонентов, флаги компилятора и т.д.) обычно указывается прямо в самом Makefile (хотя может находиться и в отдельном файле).

Популярная система сборки autotools, идентифицируемая по наличию скрипта configure, более дружелюбна к пользователю и позволяет указывать все опции компиляции через аргументы командной строки:

$ ./configure --prefix=/usr/local --without-debug --without-gtk --with-qt --enable-mmx

Где '--prefix' означает путь установки приложения, флаги '--with-что-то-там' и '--without-что-то-там' позволяют включить или отключить добавляемые в приложение компоненты, а флаги типа '--enable-вот-это' используются для указания на использование специального кода. Скрипт оценит пригодность операционной системы для сборки и установки приложения и сгенерирует стандартный (но очень большой) Makefile, а для компиляции приложения останется набрать заветную команду make.

Если же ни Makefile, ни configure в архиве не наблюдается - значит, программа использует одну из альтернативных систем сборки: scons (файл SConstruct в корне) или cmake (CMakeLists.txt). В первом случае необходимо установить сам scons:

$ sudo apt-get install scons

И запустить процесс компиляции:

$ scons PREFIX=/usr/local
$ sudo scons

Опции обычно описаны в файле README и передаются утилите scons в качестве аргументов.Сборка с помощью cmake напоминает использование autotools с той лишь разницей, что вместо запуска ./configure необходимо набирать команду cmake. Вся последовательность команд выглядит примерно так (cmake требует указания каталога сборки, поэтому мы указываем точку):

$ sudo apt-get install cmake

$ cmake .
$ make

Опции передаются на манер scons и также обычно описаны в README или INSTALL.

Проблемы компиляции

Проблемы со сборкой приложения могут возникнуть как на этапе конфигурирования системы сборки, так и на этапе непосредственной компиляции приложения. Большинство из них связано с поиском тех или иных библиотек, заголовочных файлов и других компонентов, от которых зависит работоспособность приложения. Их легко идентифицировать по более чем многословным сообщениям системы сборки:

Checking for qt4... no

В этом случае достаточно установить требуемый пакет (и его заголовочные файлы, которые содержатся в пакетах *-dev) с помощью пакетного менеджера и вновь запустить процесс конфигурирования.В некоторых случаях сборщик может искать не сам пакет, а только его заголовочные файлы, выводя на экран сообщения вроде:

Checking for unistd.h... no

Тогда придется обратиться к поиску google, чтобы найти название пакета, содержащего необходимый заголовочный файл.Но это еще не все, и ты можешь сильно удивиться, обнаружив, что после установки пакета конфигуратор продолжает ругаться на его отсутствие. Причин тому может быть три: недоустановленные dev-пакеты, устаревшие версии пакетов в дистрибутиве, неправильные пути поиска библиотек и заголовочных файлов. Но если первая проблема решается легко, то с двумя другими есть сложности. Ты можешь попытаться обновить необходимый пакет с помощью пакетного менеджера, но это не сработает, если дистрибутив уже устарел, и в качестве обновлений к нему выходят только багфиксы, или если приложение требует новейшую версию библиотеки, которую еще не успели упаковать в пакет. Придется собирать из исходников уже саму зависимость, а также зависимость зависимости и т.д.

Проблема с неправильными путями поиска обычно связана:

  1. с ошибкой того, кто составлял правила для системы сборки;
  2. установкой зависимости в нестандартный каталог (например, если она тоже была собрана из исходников).

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

$ export CFLAGS="$CFLAGS -I/usr/local/include -I/opt/include -I/usr/mysoft/include"
$ export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/opt/lib -L/usr/mysoft/lib"

Встречаются и совсем хардкорные случаи, когда зависимость нужной версии установлена вместе со всеми заголовочными файлами в каталоги, расположенные по правильным путям, а конфигуратор все равно отказывается их находить. Придется лезть в сам скрипт configure и комментировать все места, где производится поиск зависимости. Обычно после такого вмешательства конфигуратор сборщика отрабатывает без ошибок, и программа благополучно собирается.

Достаточно часто можно наткнуться на приложения, которые не могут быть собраны новым компилятором (как это до недавнего времени было с эмулятором qemu). Поэтому, если ты встретил упоминание о чем-то подобном в README, просто установи рекомендованную автором версию компилятора и укажи путь к нему через переменную CC перед запуском сборщика:

$ sudo apt-get install gcc-3.4
$ export CC=`which gcc-3.4`
$ ./configure --prefix=/usr/local

Проблемы компиляции в BSD

В довесок ко всему перечисленному пользователи FreeBSD получают массу других проблем, корни которых растут из специфики самой операционной системы. Ведь, несмотря на наличие системы портов, которая как раз и создана для того, чтобы автоматизировать процесс сборки стороннего софта, необходимость «ручного вмешательства» возникает достаточно часто. Порты некоторых программ совсем не обновляются, другие помечены флагом BROKEN, а новый софт может пройти очень долгий путь перед включением в официальное дерево. Вот и приходится выкачивать исходники и самостоятельно компилировать программу, спотыкаясь по пути о множество повсюду разбросанных камней.

Часто новички входят в ступор просто от того, что пытаются использовать привычную команду make для сборки приложения и получают гору ошибок о неправильном синтаксисе мэйкфайла. Связано это с тем, что BSD-системы используют собственную версию make, не унаследовавшую весь ворох нововведений, сделанных в GNU Make. Поэтому следует устанавливать gmake через систему портов и собирать приложение с его помощью (gmake && gmake install).
Вторая загвоздка: пути поиска библиотек и заголовочных файлов, без явного указания которых во FreeBSD просто невозможно собрать сторонний софт.
Третья проблема зачастую останавливает даже самых настойчивых. Имя ей - несовместимость заголовочных файлов и системных библиотек, а истоки в собственном пути BSD и отказе от использования компонентов GNU на системном уровне. FreeBSD (почти) совместима со стандартом POSIX, и правильно написанные приложения собираются для нее без всяких проблем, но стоит программисту задействовать какие-то Linux-специфичные системные вызовы или макрокоманды в своем приложении - все, тупик. Сборка останавливается, и заставить ее продолжиться бывает очень и очень сложно. Вот список проблем, с которыми может столкнуться пользователь FreeBSD во время сборки приложения, ориентированного на Linux-системы:

1. Ругань компилятора на необъявленные функции. В заголовочных файлах (*.h) ядра и библиотеки языка Си между Linux и FreeBSD есть отличие, – некоторые функции/системные вызовы могут быть объявлены в разных местах. Зачастую следует лишь набрать «man имя_функции» в терминале FreeBSD и посмотреть, к какому заголовочному файлу она относится. Если хидер не включен в файл, обруганный компилятором, значит, проблема решится за счет добавления строки «#include <заголовочный файл.h>» в исходник. Если же искомая функция вообще отсутствует в системе, то остается найти ее код на просторах интернета и скопировать прямо в исходник.

2. Ругань на отсутствующие макроопределения (по соглашению их имена пишутся заглавными буквами). Корень проблемы, опять же, в различии заголовочных файлов, Linux-варианты которых содержат множество подручных макроопределений, а их может и не быть в заголовочных файлах FreeBSD. Решение: найти код макроопределения в интернете или Linux-хидерах и вставить его прямо в исходник приложения.

3. Ругань на функции, которые являются системными вызовами (проверяется с помощью обращения ко второй секции man-страниц в Linux). В большинстве случаев это тупик. Если сам разработчик не позаботился об опции, отвечающей за их отключение, единственный путь - переписывание исходника, что сложно и недостойно.

Установка

После сборки приложение необходимо установить. В подавляющем большинстве случаев эта операция выполняется с помощью простой команды «make install» (или «scons install»), которая копирует компоненты приложения на свои места и устанавливает права доступа. Вот только удалить приложение после зачастую можно только с помощью ручного затирания его файлов (может помочь также команда «make uninstall», выполненная из корня исходников, но она наличествует далеко не всегда). Поэтому лучше прибегнуть к специализированным средствам для создания настоящего дистрибутивного пакета.

Наиболее простое и эффективное из них - утилита checkinstall (http://checkinstall.izto.org), предназначенная для создания пакетов в момент установки программы из исходников. Ее уникальность в удивительной простоте использования. Все, что нужно сделать для создания пакета и установки его в систему - набрать одну из приведенных ниже команд вместо «make install»:

# checkinstall -R // RPM-пакет
# checkinstall -D // Deb-пакет
# checkinstall -S // Slackware

После того как установка приложения будет завершена, выполни команду strip для бинарников и библиотек приложения с целью уменьшить их объем. Уже установленные приложения можно «стрипнуть» с помощью двух команд:

# find / | xargs file | grep "executable" | grep ELF | cut -f 1 -d : | xargs strip --strip-unneeded
# find / | xargs file | grep "shared object" | grep ELF | cut -f 1 -d : | xargs strip --strip-unneeded

Проблемы запуска

С гордостью за выполненную работу ты набираешь в терминале заветную команду и – бац! – программа завершается, не успев начать выполняться. Wtf? По всей видимости, где-то в процессе инициализации программа встретила ошибку и завершилась. Хорошо было бы увидеть хоть какое-то диагностическое сообщение, но далеко не все программисты столь заботливы. Что делать?

Первое: перечитать README. Возможно, в некоторых системах или при стечении определенных обстоятельств софтина не работает, о чем автор уже знает. Второе: проверить, какие компоненты программа установила в систему, и доступны ли они ей. Например, она может использовать один из подкаталогов каталога /var для хранения своих временных файлов, а он почему-то не был создан или получил неправильные права доступа. Третье: запустить программу с флагом '--help' и проверить, поддерживает ли она дебаг-режим с выводом диагностических сообщений на экран. Если это так - включить его. Наверняка на экране появится сообщение об ошибке и, возможно, даже описание способов устранения. Четвертое: если это сервер, который ведет логи - проверить их на наличие диагностических сообщений.

Если же ничего не помогло, задействуй тяжелую артиллерию. В любой *nix-системе есть трассировщик системных вызовов, предназначенный для отслеживания всех сисколлов, произведенных приложением, и проверки их возвращаемого значения. В Linux он зовется strace, а во FreeBSD - truss (хотя через систему портов доступен и strace). Просто запусти программу под его управлением, и ты увидишь все ее обращения к операционной системе в режиме реального времени:

$ strace софтина

Вывод лучше смотреть с конца, постепенно пролистывая все вызовы close в поисках последовательности вызовов open. Затем необходимо проверить, какие из последних вызовов open вернули отрицательный статус (-1), и просмотреть, к чему происходило обращение. В 95% случаев ошибка связана с отсутствием (или недоступности вследствие неправильных прав доступа) какого-либо файла или каталога. Трассировщик покажет все обращения к файлам, и найти недоступного будет не сложно.

Флаги оптимизации

Игры с флагами оптимизации - одна из основных причин краха или неправильной работы самостоятельно собранных приложений. Следует понимать, что любая оптимизация выше уровня '-O1' (или просто '-O') потенциально опасна. Флаг '-O2', хоть и не страшен для большинства приложений, запросто убивает низкоуровневый код с ассемблерными вставками, а '-O3', активирующий агрессивный режим, идущий против стандартов, легко отправляет в корку каждое второе приложение. Об остальных флагах и говорить не стоит, они предназначены для весьма узкого круга приложений, поэтому мы бы порекомендовали остановиться на следующих опциях оптимизации и беречь свои нервы и нервы других.

O — базовая оптимизация. Значительно увеличивает скорость исполнения программы.

O2 — стандартный уровень оптимизации. По сравнению с '–O' несущественно увеличивает как размер бинарника, так и скорость исполнения программы.

Os — оптимизация уровня '-O2' в совокупности с флагами, уменьшающими размер.

fomit-frame-pointer — указываем компилятору не сохранять указатель на кадр стека (так мы избегаем временных затрат на его сохранение и восстановление). Использование этого флага может благотворно повлиять на скорость исполнения программы.

INFO

Система портов Gentoo, ArchLinux или FreeBSD - отличный источник информации о том, как правильно собрать программу с учетом всех зависимостей.

Если для запуска собранного бинарника требуется подключить динамические библиотеки, расположенные в нестандартных каталогах, используй ldconfig(8). Пример для OpenBSD: «ldconfig /usr/local/lib /usr/X11R6/lib».

WARNING

BSD-версия команды patch не поддерживает флаг '--dry-run'.

Осторожнее с расширениями tgz и tbz! Такие архивы могут содержать не исходный текст программы, а бинарный пакет для установки в BSD-системы.

Содержание
загрузка...
Журнал Хакер #151Журнал Хакер #150Журнал Хакер #149Журнал Хакер #148Журнал Хакер #147Журнал Хакер #146Журнал Хакер #145Журнал Хакер #144Журнал Хакер #143Журнал Хакер #142Журнал Хакер #141Журнал Хакер #140Журнал Хакер #139Журнал Хакер #138Журнал Хакер #137Журнал Хакер #136Журнал Хакер #135Журнал Хакер #134Журнал Хакер #133Журнал Хакер #132Журнал Хакер #131Журнал Хакер #130Журнал Хакер #129Журнал Хакер #128Журнал Хакер #127Журнал Хакер #126Журнал Хакер #125Журнал Хакер #124Журнал Хакер #123Журнал Хакер #122Журнал Хакер #121Журнал Хакер #120Журнал Хакер #119Журнал Хакер #118Журнал Хакер #117Журнал Хакер #116Журнал Хакер #115Журнал Хакер #114Журнал Хакер #113Журнал Хакер #112Журнал Хакер #111Журнал Хакер #110Журнал Хакер #109Журнал Хакер #108Журнал Хакер #107Журнал Хакер #106Журнал Хакер #105Журнал Хакер #104Журнал Хакер #103Журнал Хакер #102Журнал Хакер #101Журнал Хакер #100Журнал Хакер #099Журнал Хакер #098Журнал Хакер #097Журнал Хакер #096Журнал Хакер #095Журнал Хакер #094Журнал Хакер #093Журнал Хакер #092Журнал Хакер #091Журнал Хакер #090Журнал Хакер #089Журнал Хакер #088Журнал Хакер #087Журнал Хакер #086Журнал Хакер #085Журнал Хакер #084Журнал Хакер #083Журнал Хакер #082Журнал Хакер #081Журнал Хакер #080Журнал Хакер #079Журнал Хакер #078Журнал Хакер #077Журнал Хакер #076Журнал Хакер #075Журнал Хакер #074Журнал Хакер #073Журнал Хакер #072Журнал Хакер #071Журнал Хакер #070Журнал Хакер #069Журнал Хакер #068Журнал Хакер #067Журнал Хакер #066Журнал Хакер #065Журнал Хакер #064Журнал Хакер #063Журнал Хакер #062Журнал Хакер #061Журнал Хакер #060Журнал Хакер #059Журнал Хакер #058Журнал Хакер #057Журнал Хакер #056Журнал Хакер #055Журнал Хакер #054Журнал Хакер #053Журнал Хакер #052Журнал Хакер #051Журнал Хакер #050Журнал Хакер #049Журнал Хакер #048Журнал Хакер #047Журнал Хакер #046Журнал Хакер #045Журнал Хакер #044Журнал Хакер #043Журнал Хакер #042Журнал Хакер #041Журнал Хакер #040Журнал Хакер #039Журнал Хакер #038Журнал Хакер #037Журнал Хакер #036Журнал Хакер #035Журнал Хакер #034Журнал Хакер #033Журнал Хакер #032Журнал Хакер #031Журнал Хакер #030Журнал Хакер #029Журнал Хакер #028Журнал Хакер #027Журнал Хакер #026Журнал Хакер #025Журнал Хакер #024Журнал Хакер #023Журнал Хакер #022Журнал Хакер #021Журнал Хакер #020Журнал Хакер #019Журнал Хакер #018Журнал Хакер #017Журнал Хакер #016Журнал Хакер #015Журнал Хакер #014Журнал Хакер #013Журнал Хакер #012Журнал Хакер #011Журнал Хакер #010Журнал Хакер #009Журнал Хакер #008Журнал Хакер #007Журнал Хакер #006Журнал Хакер #005Журнал Хакер #004Журнал Хакер #003Журнал Хакер #002Журнал Хакер #001