× К оглавлению На главную Об авторе

Дата и время публикации:

Построение и организация рекурсивной сборки


1. Что такое дерево проекта

Для простого проекта, который хранит все файлы в одной и той же директории достаточно иметь единственный Makefile.am, как это показано в статье Как использовать Automake.

Напротив, в больших проектах используется древовидная организация файлов в различных директориях, как показано на примере проекта gasrunparts версии 0.8 (рисунок 1.1) .

Рисунок 1.1

При такой организации построения дерева проекта отдельно в директории src хранятся файлы исходного кода, в libs и include – библиотеки и заголовочные файлы к ним , в man – документация к собираемым программам.

В тоже время, в очень больших проектах может существовать директории для каждой отдельной программы, библиотеки или даже модуля. Так в рассматриваемом проекте gasrunparts версии 0.8 (далее по тексту – пакет gasrunparts-0.8) существует поддиректория examples/libgnucalgthms, файлы из которой будут устанавливаться в /usr/local/share/gasrunparts/examples/gnucalgthms и собираться по необходимости программой gasrunparts в рабочей директории $(PWD) пользователя командой make в соответствие с правилами, прописанных в целях файла Makefile.real.

2. Что такое режим рекурсивной сборки

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

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

3. Объявление рекурсивных поддиректории

В пакете gasrunparts-0.8 используется рекурсивный make, где в корневом Makefile.am через переменную SUBDIRS указывается Automake какие поддиректории участвуют в процессе сборки или нет (см. п 5. "Отмена рекурсии по условной переменной") в листинге 3.1 .

Листинг 3.1. Фрагмент корневого Makefile.am из пакета gasrunparts-0.8


SUBDIRS = include libs src . man examples 

sllist_mergesort:
	 cd examples/libgnucalgthms && $(MAKE) -f Makefile.real $(AM_MAKEFLAGS) all

При этом, несмотря на заблуждениях некоторых, make будет вызван Automake после ввода make sllist_mergesort для выполнения сценария в Makefile.real, но когда будет вызвана команда make install собранная программа sllist_mergesort из поддиректории examples/libgnucalgthms установлена не будет.

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

Когда Automake вызывается в поддиректории, он использует переменную MAKE, которая при вызове make принимает значение из переменной AM_MAKEFLAGS, позволяя осуществлять проксирование make путем объявление в корневом Makefile.am цели с ‘$(MAKE) -f Makefile.real $(AM_MAKEFLAGS) target’, как было показано в листинге 3.1 .

По умолчанию, Automake генерирует Makefile'ы, которые работают с глубиной в постфиксном порядке – первой встраивается поддиректория, а уже потом содержащая её директория. Однако, этот порядок можно изменить путем вставки символа точки(.), как показано в листинге 3.1. При этом, порядок встраивания будет такой – первыми будут последовательно встроены директории include, libs и src, затем текущая, а в конце man и examples.

4. Объявление рекурсивных целей

В дополнение к встроенным рекурсивным целям в Automake, таким как all, check и т.д., разработчик может так же определить свою собственную, что делается путем передачи её имени в качестве аргумента в m4 макросе AM_EXTRA_RECURSIVE_TARGETS в configure.ac, как в листинге 4.1 .

Листинг 4.1. Фрагмент configure.ac из пакета gasrunparts-0.8


m4_ifdef([AM_EXTRA_RECURSIVE_TARGETS], [AM_EXTRA_RECURSIVE_TARGETS([foo])])
AC_CONFIG_FILES([
 Makefile
 src/Makefile
 man/Makefile
 include/Makefile 
 libs/Makefile 
 libs/libgasrunparts/Makefile
 libs/libgnucrunparts/Makefile
 examples/Makefile
 examples/libgnucalgthms/Makefile 
])

Соответственно Automake сгенерирует правила, которые необходимо объявить в файле Makefile.am с префиксом -local, как показано в листинге 3.4.

Листинг 4.2 . Фрагмент Makefile.am из пакета gasrunparts-0.8


foo-local:
	@echo This too will be run by a "make foo" issued in each of directories are 
	@echo 'src', 'man', 'libs', 'examples'  and top-level directory.
	@echo Directory 'examples/libgnucalgthms will be excluded

Примечание Если при переборке проекта будет выведено предупреждение, наподобие того, что показано в дампе 4.2.1 . Дамп 4.2.1 . Фрагмент вывода autoreconf сообщений на STDOUT .

$ autoreconf —if
...
configure.ac:14: warning: macro `AM_EXTRA_RECURSIVE_TARGETS' not found in library
...

Необходимо, например в дистрибутиве GNU/Linux Debian версии 8.0 и выше выполнить действия по обновлению Automake , описанные в дампе 4.2.2 . Дамп 4.2.2 . Перечень команд для обновления Automake

$ sudo apt-get update
$ sudo apt-get install automake

Причиной предупреждения является в том, что в automake версии 1.11 поддержка переменной AM_EXTRA_RECURSIVE_TARGETS не было, а появилась начиная с 1.13 .

5. Отмена рекурсивной сборки c помощью AM_CONDITIONAL

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

Для этого используется AM_CONDITIONAL в файле configure.ac для объявление условной переменной "$want_libgnucalgthms" , как это показано в листинге 5.1.

Листинг 5.1. Фрагмент configure.ac из пакета gasrunparts-0.8

...
AM_CONDITIONAL([COND_OPT_GNUCALGTHMS], [test "$want_libgnucalgthms" = yes])
AC_CONFIG_FILES([
...
 examples/libgnucalgthms/Makefile 
])
...

При этом предполагается, что условием выполнения рекурсии должно быть установка переменной "$want_libgnucalgthms" в ‘yes’, в обратном случае она будет отменена.

Листинг 5.2. Фрагмент Makefile.am директории examples из пакета gasrunparts-0.8

...
if COND_OPT_GNUCALGTHMS
  GNUCALGTHMS_OPT = libgnucalgthms 
endif

SUBDIRS = . $(GNUCALGTHMS_OPT) 
...

Символ точки (.) указывает, что нужно с начала провести сборку самой examples, а за тем поддиректории libgnucalgthms .

Несмотря на отмену рекурсивной сборки в поддиректории libgnucalgthms, никто не отменяет её использование правилами dist, distclean и maintainer-clean, используемых в процессе создания дистрибутива пакета (обычно архив с расширением 'tar.gz'). Поэтому вместо указания в переменной DIST_SUBDIRS всех файлов, используемых в ходе её выполнения, предпочтительней использовать альтернативный способ упаковки файлов из поддиректории libgnucalgthms, как показано в листинге 5.3 .

Листинг 5.3. Фрагмент Makefile.am поддиректории examples из пакета gasrunparts-0.8

...
examples_gnucalgthmsdir = $(pkgdatadir)/examples/gnucalgthms
dist_examples_gnucalgthms_DATA = libgnucalgthms/Makefile.real  \
				  libgnucalgthms/gnuc_algthms.c \
				  libgnucalgthms/single_llist.h
...

А в самой поддиректории libgnucalgthms в файле Makefile.am прописать куда будем класть заголовочные файлы, как показано в листинге 5.4 .

Листинг 5.4. Фрагмент Makefile.am поддиректории libgnucalgthms из пакета gasrunparts-0.8

...
inst_includedir = @includedir@/"gnucalgthms"                        
inst_include_HEADERS = \
			 single_llist.h
...

6. Библиография

1) Automake Manual. Directories

2) How do i find the value of am makeflags

3) Automake Manual. Third-Party Makefiles

4) Automake Manual for version 1.4 . Building documentation

5) Message #00021 . [RFC] {maint} Add support for user-defined recursive targets

6) Dependency check #499

7) autoreconf --install failed #494