Основы сборки проектов при помощи Autotools
29 августа 2016
Поговорим о системах сборки, а конкретнее — одной из них, 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. А вы как считаете?
Метки: C/C++, Кроссплатформенность.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.