2. Создайте make-файл в еще одной директории. Этот make-файл будет использоваться для сборки приложения, но только после того, как будут выполнены make-файлы из шага 1. Укажите в этом make-файле фиктивную цель all, чьим пререквизитом будет являться исполняемый файл. Объявите цель исполняемого файла с пререквизитами, состоящими из библиотек, используемых приложением, а также объектных файлов, которые собираются из .cpp -файлов приложения. Напишите командный сценарий для сборки исполняемого файла из набора библиотек и объектных файлов, как описано в рецепте 1.5. Если необходимо, напишите шаблонное правило для генерации объектных файлов из .cpp -файлов, как показано в рецепте 1.16. Добавьте цели install и clean, как показано в рецепте 1.15, и механизм для автоматической генерации зависимостей исходных файлов, как показано в рецепте 1.16.
3. В директории, родительской по отношению к директориям, содержащим все остальные make-файлы, создайте новый make-файл — давайте называть его главным (top-level) make-файлом, а все остальные — подчиненными . Объявите цель по умолчанию all с пререквизитами в виде директории, содержащей make файл, созданный на шаге 2. Объявите правило, чьи цели состоят из директорий, содержащих подчиненные make-файлы, а командный сценарий вызывает make в каждой целевой директории для цели, указанной в виде значения переменной TARGET. Наконец, объявите цели, указывающие зависимости между целями по умолчанию подчиненных make-файлов.
Например, чтобы из исходных файлов из примера 1.3 собрать исполняемый файл с помощью GCC в Unix, создайте такой make-файл, как показанный в примере 1.23.
Пример 1.23. make файл для hellobeatles.exe с использованием GCC
# Укажите исходные файлы, целевой файл, директории сборки
# и директорию установки
SOURCES = hellobeatles.cpp
OUTPUTFILE = hellobeatles
LIBJOHNPAUL = libjohnpaul.a
LIBGEORGERINGO = libgeorgeringo.so
JOHNPAULDIR = ../johnpaul
GEORGERINGODIR = ../georgeringo
INSTALLDIR = ../binaries
#
# Добавьте в путь поиска заголовочных файлов родительскую директорию
#
CPPFLAGS += -I..
#
# Цель по умолчанию
#
.PHONY: all
all: $(HELLOBEATLES)
#
# Цель для сборки исполняемого файла.
#
$(OUTPUTFILE): $(subst .cpp,.о,$(SOURCES)) \
$(JOHNPAULDIR)/$(LIBJOHNPAUL) \
$(GEORGERINGODIR)/$(LIBGEORGERINGO)
$(CXX) $(LDFLAGS) -o $@ $^
.PHONY: install
install:
mkdir -p $(INSTALLDIR)
cp -p $(OUTPUTFILE) $(INSTALLDIR)
.PHONY: clean
clean:
rm -f *.o
rm -f $(OUTPUTFILE)
#Сгенерируйте зависимости .cpp-файлов от .hpp-файлов
include $(subst .cpp,.d,$(SOURCES))
%.d: %.cpp
$(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@..$$$$ > $@; \
rm -f $@.$$$$
Далее в директории, содержащей 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).
Читать дальше