Как заставить GNU Make исполнить файл многократно с разными аргументами
В современных публикациях, все чаще можно слышать о недостатках утилиты Make. Например, в статье Qt Build System: спасательный круг для сборки приводятся мнения двух экспертов, первым из которых выступает Peter Miller с критикой рекурсивных возможностей Make. Разумеется, обоснованная критика необходима, иначе мы не сможем двигаться дальше. Но все-таки, хочется заступиться за старую, добрую утилиту Make. Тем более, что данная утилита используется в миллионах проектов и, ее рано сбрасывать со щетов. Надо просто помнить, что, по сути, Make является простой программой, связывающей цели пользователя с действиями, которые необходимо осуществить для достижения этих самых целей. Нельзя требовать от программы того, что она не может делать в принципе, а именно исправлять ошибки пользователя. Она может лишь пожаловаться на то, что ее заставляют делать неприличные вещи.
О рекурсивных Make-файлах, параллельной и многопоточной сборке, а также о скорости сборки, мы поговорим позднее, надеюсь у нас еще будет повод, а сейчас хотелось бы представить одну из возможностей, которую предоставляет утилита GNU Make
Предположим, что нам необходимо собрать программу или отчуждаемый пакет для работы на трех устройствах с именами ci20, bt01 и dm64. Первые два устройства ci20 и bt01 основаны на архитектуре MIPS, третье устройство dm64 построенно на базе процессора ARM. Toolchain-ы, для простоты, назовем mips и arm, соответственно.
Сценарий сборки исходной программы одинаков для всех трех устройств и написан на языке GNU Make.
Если представить все комбинации вызовов команды Make, необходимые для сборки программы на наши устройства, получим:
$ TOOLCHAIN=mips HARDWARE=ci20 make
$ TOOLCHAIN=mips HARDWARE=bt01 make
$ TOOLCHAIN=arm HARDWARE=dm64 make
или, при передаче имен устройств и Toolchain-ов в качестве аргументов:
$ make TOOLCHAIN=mips HARDWARE=ci20
$ make TOOLCHAIN=mips HARDWARE=bt01
$ make TOOLCHAIN=arm HARDWARE=dm64
Таким образом, система сборки должна принимать пары TOOLCHAIN — HARDWARE, которые определяют какой именно Toolchain необходимо использовать для того или иного устройства.
Рассмотрим теперь, как, на уровне системы сборки, организовать последовательность вызовов утилиты Make для нашего сценария таким образом, чтобы пользователь мог осуществить данные действия с помощью лишь одного вызова:
$ make
без задания дополнительных аргументов, отвечающих за выбор целевого устройства и связанного с ним Toolchain-а.
Если включить в начало нашего сценария список допустимых целевых устройств, например, следующим образом:
COMPONENT_TARGETS = $(HARDWARE_CI20)
COMPONENT_TARGETS += $(HARDWARE_BT01)
COMPONENT_TARGETS += $(HARDWARE_DM64)
то система сборки сможет автоматически построить список возможных, для данного сценария, комбинаций TOOLCHAIN — HARDWARE, который будет выглядеть, например, следующим образом:
targets = target_mips_ci20 target_mips_bt01 target_arm_dm64
Имея такой список, система сборки может восстановить аргументы, которые необходимо передавать при каждом вызове утилиты Make, для нашего сценария. Сделать это нетрудно, на языке GNU Make эти действия можно описать так:
target_%: TOOLCHAIN = $(shell echo $(word 2, $(subst _, , $@)))
target_%: HARDWARE = $(shell echo $(word 3, $(subst _, , $@)))
target_%:
$(MAKE) TOOLCHAIN=$(TOOLCHAIN) HARDWARE=$(HARDWARE)
Таким образом, при вызове команды Make без аргументов, переменные TOOLCHAIN и HARDWARE будут не определены и, в этом случае, система сборки займется созданием списка targets из числа допустимых комбинаций. Когда же список будет составлен, система сборки сможет осуществить вызов
$(MAKE) TOOLCHAIN=$(TOOLCHAIN) HARDWARE=$(HARDWARE)
с действительными аргументами.
Когда же, при очередном вызове, система убедится в том, что переменные TOOLCHAIN и HARDWARE определены, управление будет передано нашему сценарию без дополнительных вычислений.
Разумеется, код приведенный здесь, не полон, но отражает фундаментальный принцип и, при использовании условных операторов, может быть доведен до рабочего.
Описанный здесь механизм, напрямую вытекает из возможностей утилиты Make, а работающий код, можно детально рассмотреть в исходном тексте системы сборки Radix.pro.
В дальнейшем, я надеюсь продолжить серию публикаций на разные темы, относящиеся к таким средствам, как Make, autoconf, automake, разумеется без излишнего цитирования вещей давно известных, но с целью показа основных принципов, которые позволяют разобраться в сути происходящих вещей.
Вопросы можно задавать на любой странице Radix.pro (Contact) или в блогах на Тумблере [build-system, radix-platform], где я стараюсь рассказывать о новостях проекта.