Например, чтобы из исходных файлов из примера 1.3 собрать исполняемый файл с помощью GCC в Unix, создайте такой make-файл, как показанный в примере 1.23.
Пример 1.23. make файл для hellobeatles.exe с использованием GCC
Далее в директории, содержащей johnpaul, georgeringo, hellobeatles и binaries, создайте главный make-файл, как показано в примере 1.24.
Пример 1.24. Главный make-файл для исходного кода из примеров 1.1, 1.2 и 1.3
# Все цели в этом make-файле — фиктивные
PHONY: all johnpaul georgeringo hellobeatles
# Цель по умолчанию
all: hellobeatles
# Цели johnpaul, georgeringo и hellobeatles представляют
# директории, командный сценарий вызывает make в каждой из них
johnpaul georgeringo hellobeatles
$(MAKE) --directory=$@ $(TARGET)
#
Это правило указывает что цель по умолчанию make-файла
# в директории hellobeatles зависит от целей по умолчанию
# make-файлов из директорий johnpaul и georgeringo
.PHONY: hellobeatles
hellobeatles: johnpaul georgeringo
Чтобы собрать hellobeatles, перейдите в директорию, содержащую главный make-файл, и введите
make
. Чтобы скопировать файлы libjohnpaul.a, libgeorgeringo.so и hellobeatles в директорию binaries, введите
make TARGET=install
. Чтобы очистить проект, введите
make TARGET=clean
.
Обсуждение
Подход к управлению сложными проектами, продемонстрированный в этом рецепте, известен как рекурсивный make (recursive make). Он позволяет организовать проект в виде набора модулей, каждый со своим собственным make-файлом, и указать зависимости между этими модулями. Он не ограничен одним главным make-файлом с набором дочерних make-файлов: эта методика может применяться для обработки многоуровневых древовидных структур. В то время, пока рекурсивный make был стандартной методикой управления большими проектами с помощью make, появились другие методы, которые теперь рассматриваются как более качественные. За подробностями снова обратитесь к Managing Projects with GNU make, Third Edition Роберта Мекленбурга (O'Reilly).
Пример 1.23 — это прямое применение методик, продемонстрированных в рецептах 1.15, 1,16 и 1.17. В нем есть один очень интересный момент. Как показано в рецепте 1.15, при компиляции hellobeatles.cpp из командной строки необходимо использовать опцию – I.., говорящую компилятору, где искать заголовочные файлы johnpaul.hpp и georgeringo.hpp. Одним из возможных решений является написание явного правила сборки hellobeatles.o с помощью командного сценария, содержащего опцию – I.. подобно этому.
hellobeatles.o: hellobeatles.cpp
g++ -с -I.. – о hellobeatles.o hellobeatles.cpp
Вместо этого я использовал точку настройки
CPPFLAGS
, описанную в рецепте 1.15, и указал, что всегда, когда происходит компиляция объектного файла из файла .cpp, в командную строку должна быть добавлена опция – I..:
CPPFLAGS += -I..
Вместо оператора присвоения
=
я использовал
+=
, так что новое значение будет добавляться к тому значению
CPPFLAGS
, которое могло быть указано в командной строке или в переменной среды.