Поговорим о системах сборки, а конкретнее — одной из них, Autotools (также известной под названием GNU Build System). Если вы когда-нибудь собирали программу при помощи волшебной последовательности команд ./configure && make && sudo make install
, значит вы использовали Autotools. Откровенно говоря, в новых проектах я бы рекомендовал использовать CMake , ну или хотя бы SCons. Но по историческим причинам многие проекты все еще используют Autotools. Также некоторые люди ошибочно считают Autotools чем-то типа стандарта де-факто. А значит, было бы неплохо примерно понимать, как им пользоваться.
Матчасть
Существует много *nix систем, и все они между собой немного различаются. Например, используют разные компиляторы, хранят заголовочные файлы установленных библиотек по разным путям, и так далее. Autotools нужен для того, чтобы один и тот же проект можно было собрать при помощи уже упомянутой последовательности команд на любой *nix системе.
Заметьте, что речь идет исключительно о *nix системах. Другими словами, проект, использующий Autotools, можно собрать на *BSD, MacOS, различных дистрибутивах Linux и под Cygwin , но не под обычной Windows с Visual Studio и вот этим всем. Например, в PostgreSQL для того, чтобы проект собирался и под Windows, имеется специальный набор скриптов на Perl . Еще из недостатков Autotools, как вы и сами скоро убедитесь, можно отметить использование довольно стремного синтаксиса. Именно поэтому в третьем тысячелетии, вообще-то говоря, лучше использовать какой-нибудь CMake.
Autotools содержит в себе три важных компонента:
- Autoscan — эта штука сканирует код проекта и выдает на выходе заготовку для будущего файла configure.ac.
- Autoconf — генерирует из написанного программистом файла configure.ac портабельный скрипт configure, запускаемый при сборке проекта.
- Automake — помогает в генерации Makefile при запуске скрипта configure. На вход Automake принимает написанный программистом файл Makefile.am, а на выходе выдает Makefile.in, который затем используется configure для генерации мейкфайла.
Разумеется, есть множество не менее важных компонентов, но обычно почему-то выделяют именно эти три. Если пока что не очень понятно, не переживайте. Давайте рассмотрим пример, и все сразу станет ясно.
Типичный пример — сборка GCC
Для начала рассмотрим сборку готового проекта. Сборка PostgreSQL ранее уже подробно рассматривалась в этом блоге, поэтому данный пример не интересен. Рассмотрим лучше сборку GCC.
Установка зависимостей:
Тянем исходники из Subversion репозитория:
cd trunk
Собственно сборка:
make -j2 all-gcc all-gdb
make check-gcc
# sudo make install
Вот так примерно собираются и устанавливаются проекты, использующие Autotools.
Autotools на примере нового небольшого проекта
Небольшой проект, рассмотренный ранее в заметке Не самый тривиальный пример использования libcurl , как раз использует Autotools. Хотя далее и будет рассмотрено создание совершенно нового проекта с нуля, вы можете ориентироваться на GitHub-репозиторий с исходным кодом проекта из той заметки. Пример еще одного проекта, использующего Autotools, вы найдете в посте Перехват сетевого трафика при помощи библиотеки libpcap . Репозиторий на GitHub с исходниками к посту находится здесь .
Итак, создаем первый файл с исходным кодом на C. Пусть будет shownotegen.c, пока что такого содержания:
#include <config.h>
int main ( )
{
printf ( «Hello, this is » PACKAGE_STRING » n » ) ;
}
Также создаем Makefile.am:
bin_PROGRAMS=shownotegen
shownotegen_SOURCES=shownotegen.c
Если у вас в проекте несколько программ, то пишем что-то вроде:
bin_PROGRAMS=compress decompress
compress_SOURCES=compress.c
decompress_SOURCES=decompress.c
После создания *.c файлов говорим:
Будут созданы файлы autoscan-2.69.log (пустой) и configure.scan. В последнем нужно поправить название пакета, его версию и контактный email, а также дописать после строчки AC_PROG_CC (это важно; если дописать, например, просто в конце файла, то все сломается):
Переименовываем файл:
Создаем файлы, необходимые согласно GNU coding standards (без них откажется работать утилита autoreconf):
Важно! Сейчас самое время добавить все полученные файлы в Git . Потом будет автоматически сгенерировано много «лишних» файлов.
Теперь, имея configure.ac, создаем кучу разных файлов, не исключая и тот самый configure:
Проверяем, что все работает (под FreeBSD вместо make
говорим gmake
):
make
Если все было сделано правильно, появится бинарничек:
… который можно установить:
… или удалить:
Пакет, который любой может распаковать и сказать ./configure && make && sudo make install
со всем необходимым создается так:
Не так уж и сложно!
Тестирование
В некоторых проектах можно прогонять тесты, сказав make check
. Чем наш проект хуже?
В Makefile.am дописываем в конце список тестов, например:
Снова делаем autoreconf, configure и make, как было описано выше. Затем:
Вывод тестов пишется в (название_теста).log
. Результат выполнения всех тестов пишется в test-suite.log.
Про зависимости
Также Autotools позволяет проверять наличие в системе зависимостей. Давайте для эксперимента добавим в наш проект зависимость от libcurl.
Поправим shownotegen.c таким образом:
#include <config.h>
#include <curl/curl.h>
int main ( )
{
printf ( «Hello, this is » PACKAGE_STRING
«, curl version: %s n » , curl_version ( ) ) ;
}
Для поиска библиотек и заголовочных файлов в configure.ac добавляем что-то вроде:
AC_CHECK_LIB([curl], [curl_version])
У макроса AC_CHECK_LIB первый аргумент — это название библиотеки (как -lcurl
), второй аргумент — это название какой-то одной процедуры из библиотеки. Autotools попытается собрать простую программу, дабы убедиться, что в библиотеке есть такая процедура. Это довольно странный способ проверки зависимостей, но вот в Autotools так принято.
Заметьте, что при такой конфигурации скрипт configure просто запишет в config.h:
… если libcurl будет найден, и не запишет иначе. Если нужно, чтобы configure завершался с ошибкой в случае отсутствия зависимости, пишем:
AC_MSG_ERROR([Unable to find curl/curl.h])
])
AC_CHECK_LIB([curl], [curl_version], [], [
AC_MSG_ERROR([Unable to find libcurl])
])
После правки configure.ac не забываем сказать:
Хочется отметить один интересный момент. Если взять, к примеру, FreeBSD, то в этой системе все устанавливаемые с помощью пакетов заголовочные файлы складываются в /usr/local/include. Но при этом Autotools по дэфолту ничего не ищет в /usr/local/include! Решить эту проблему можно несколькими способами. Например, можно дописать в configure.ac перед AC_CHEK_HEADER и AC_CHECK_LIB что-то вроде:
CPPFLAGS=»$CPPFLAGS -I/usr/local/include»
LDFLAGS=»$LDFLAGS -L/usr/local/lib»
Некоторые делают так, а кто-то считает, что все переменные окружения нужно выставить вручную при запуске configure.
Заключение
Выше, конечно, были описаны далеко не все возможности и особенности Autotools. Но этих знаний достаточно примерно в 90% случаев. В остальных же 10%, как всегда, приходится курить методичку .
С одной стороны, не так страшен черт, как его малюют. С другой, как по мне, уж проще описать все зависимости в файле REAMDE.md и написать простенький скрипт make.sh (или make.py), чем использовать Autotools. А вы как считаете?