Product SiteDocumentation Site

Глава 9. Сборочные зависимости

9.1. Autotools
9.2. Авто-автосборка
9.3. Сборка окружения с параметрами

9.1. Autotools

Использовавшийся до этого инструмент автосборки GNU Make показывает удобство сборки, однако для более тонкой настройки, проверки зависимостей, опциональности сборки необходим некоторый высокоуровневый инструмент с простым интерфейсом, использующий make в качестве сборочного компонента. Данную задачу решает семейство инструментов autotools, позволяющее автоматически искать, настраивать и параметризировать сборочные зависимости, а также обрабатывать тесты и, в целом, поддерживать рабочее окружение.
На примере сборки пакета разберёмся с тонкостями работы этого набора инструментов. Рассмотрим Ncurses-программу, считывающую символы и выводящую их на экран. Добавим в неё условные макросы: DX для размера отступа и KEYPAD для режима определения функциональных клавиш. Также заметим наличие некоего конфигурационного файла config.h, речь о котором пойдёт чуть позже:
@user: autoenv-pkg/src/prog.cs
#include <ncurses.h>
#include <unistd.h>
#include <error.h>
#include "config.h"

#ifndef DX
#define DX 3
#endif

int main(void) {
        WINDOW *win;
        int c = 0;

        if (!isatty(0))
                error(1, 0, "Not a terminal");
        initscr();
        noecho();
        cbreak();
        printw("%s-%s (%s)", PACKAGE_NAME, PACKAGE_VERSION, PACKAGE_BUGREPORT);
        refresh();

        win = newwin(LINES-2*DX, COLS-2*DX, DX, DX);
#ifdef KEYPAD
        keypad(win, TRUE);
#else
        keypad(win, FALSE);
#endif
        scrollok (win, TRUE);
        do {
                wprintw(win, "  Key: %d, Name: %s\n", c, keyname(c));
                box(win, 0, 0);
                wrefresh(win);
        } while((c = wgetch(win)) != 27);

        endwin();
	return 0;
}
Для начала работы необходимо воспользоваться утилитой autoscan. Она генерирует профиль на основе имеющихся файлов в директории с исходными текстами, затем добавляет некоторые зависимости в конфигурационный файл configure.scan. После использования утилиты получается заготовка профиля для итоговой сборки:
@user: autoenv-pkg/configure.scan
#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_PREREQ([2.72])
AC_INIT([FULL-PACKAGE-NAME], [VERSION], [BUG-REPORT-ADDRESS])
AC_CONFIG_SRCDIR([src/prog.c])
AC_CONFIG_HEADERS([config.h])

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_CHECK_HEADERS([unistd.h])

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.
AC_FUNC_ERROR_AT_LINE

AC_OUTPUT
Основные изменения, внесённые профиль:
  • убрана зависимость по версии autoconf;
  • поскольку основным сборочным инструментом будет являться make, добавлены зависимости для него (AM_INIT_AUTOMAKE и указание Makefile в AC_CONFIG_FILES);
  • добавлена зависимость на библиотеку Ncurses. Для этого используется макрос AC_CHECK_LIB, который генерирует временный файл с текстовым кодом, использующим указанную нами функцию исследуемой библиотеки (в нашем случае initscr). Для проверки наличия заголовочного файла библиотеки используется AC_CHECK_HEADERS;
  • добавлены два дополнительных ключа сборки: без параметров для KEYPAD и с параметром для DX.
Следующий использующийся инструмент —  autoheader. На основе confugire.ac он генерирует прототип общего заголовочного файла config.h.in. Здесь описаны все макросы, которые будут проверяться и обрабатываться при сборке программы:
@user: autoenv-pkg/config.h.in
/* config.h.in.  Generated from configure.ac by autoheader.  */

/* Frame width */
#undef DX

/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H

/* Define to 1 if you have the 'ncurses' library (-lncurses). */
#undef HAVE_LIBNCURSES

/* Define to 1 if you have the <ncurses.h> header file. */
#undef HAVE_NCURSES_H

/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H

/* Define to 1 if you have the <stdio.h> header file. */
#undef HAVE_STDIO_H

/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H

/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H

/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H

/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H

/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H

/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H

/* ESC-sequences recognition */
#undef KEYPAD

/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT

/* Define to the full name of this package. */
#undef PACKAGE_NAME

/* Define to the full name and version of this package. */
#undef PACKAGE_STRING

/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME

/* Define to the home page for this package. */
#undef PACKAGE_URL

/* Define to the version of this package. */
#undef PACKAGE_VERSION

/* Define to 1 if all of the C89 standard headers exist (not just the ones
  required in a freestanding environment). This macro is provided for
  backward compatibility; new code need not use it. */
#undef STDC_HEADERS
Следующие два инструмента —  aclocal и automake. Утилита aclocal занимается переопределением процедур поиска и инструментов automake для конкретной системы. Утилита automake генерирует на основе прототипа Makefile.am специальный Makefile.in-файл для обработки autoconf-ом (такие .in-файлы обычно добавляются в макрос AC_CONFIG_FILES()). При работе утилиты также используется ключ --add-missing, с которым она автоматически устанавливает необходимые для работы исполняемые файлы, библиотеки макросов и прочую технологическую оснастку в каталог сборки.
Прототип пишется самостоятельно исходя из задач сборки. Это фактически Makefile, в который большая часть рецептов подставляется автоматически в процессе работы automake:
@user: autoenv-pkg/Makefile.am
            CFLAGS = -Wall -O0 -g

bin_PROGRAMS=autoenv

autoenv_SOURCES=src/prog.c

TESTS=isterm.sh

isterm.sh:
       echo 'test "`./autoenv < /dev/null 2>&1`" = "./autoenv: Not a terminal"' > $@
       chmod +x $@

gitclean:
       git clean -df
В нашем генераторе присутствует:
  • один итоговый исполняемый файл, исходники для которого лежат в отдельной директории src;

    Важно

    Для описания исходников используется конструкция вида <имя исполняемого файла только из alnum-символов>_SOURCES; имена исполняемых файлов могут быть перечислены через пробел в строке bin_PROGRAMS=
  • описан список тестов (из одного теста). Тест — это произвольный исполняемый файл; если его выполнение завершится с ненулевым кодом ошибки, тест считается непройденным.
Для объединения всех полученных профилей в единый сборочный сценарий используется утилита autoconf. Она генерирует основной исполняемый сборочный сценарий configure, который поддерживает разные параметры сборки, в том числе и добавленные разработчиком (например, --enable-keypad). При выполнении configure собираются все итоговые файлы сборки. К ним относятся, например, итоговый Makefile и заполненный config.h, описанный в заголовках исходного кода программы. После выполнения configure он заполнен всеми данными, необходимыми для сборки:
@user: autoenv-pkg/config.h
/* config.h.  Generated from config.h.in by configure.  */
/* config.h.in.  Generated from configure.ac by autoheader.  */

/* Frame width */
#define DX 20

/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1

/* Define to 1 if you have the 'ncurses' library (-lncurses). */
#define HAVE_LIBNCURSES 1

/* Define to 1 if you have the <ncurses.h> header file. */
#define HAVE_NCURSES_H 1

/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1

/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1

/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1

/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1

/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1

/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1

/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1

/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1

/* ESC-sequences recognition */
/* #undef KEYPAD */

/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "UsamG1t"

/* Define to the full name of this package. */
#define PACKAGE_NAME "autoenv-pkg"

/* Define to the full name and version of this package. */
#define PACKAGE_STRING "autoenv-pkg 1.0"

/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "autoenv-pkg"

/* Define to the home page for this package. */
#define PACKAGE_URL ""

/* Define to the version of this package. */
#define PACKAGE_VERSION "1.0"

/* Define to 1 if all of the C89 standard headers exist (not just the ones
  required in a freestanding environment). This macro is provided for
  backward compatibility; new code need not use it. */
#define STDC_HEADERS 1
После исполнения configure можно запускать make, который произведёт итоговую сборку самой программы с учётом всех настроек и зависимостей.