KnigaRead.com/

Д. Стефенс - C++. Сборник рецептов

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Д. Стефенс, "C++. Сборник рецептов" бесплатно, без регистрации.
Перейти на страницу:

$(OUTPUTFILE): $(subst .cpp,.o,$(SOURCES))

$(CXX) -shared -fPIC $(LDFLAGS) -о [email protected] $^

Здесь $(OUTPUTFILE) раскрывается как libgeorgeringo.so, а выражение $(subst.cpp, .o, $(SOURCES)) раскрывается как george.о, ringo.о и georgeringo.o, как показано в рецепте 1.16. Командный сценарий $(CXX) -shared -fPIC $(LDFLAGS) -о — это адаптация командной строки GCC, показанной в табл. 1.11.

Смотри также

Рецепты 1.4, 1.9, 1.12, 1.19 и 1.23.

1.18. Сборка сложного приложения с помощью GNU make

Проблема

Вы хотите использовать GNU make для сборки исполняемого файла, зависящего от нескольких статических и динамических библиотек.

Решение

Выполните следующие действия.

1. Создайте make-файлы для библиотек, используемых приложением, как описано в рецептах 1.16 и 1.17. Эти make-файлы должны находиться в отдельных директориях.

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 [email protected] $^


.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) $< > [email protected]$$$$;

    sed 's,($*).o[ :]*,1.o [email protected] : ,g' < [email protected]$$$$ > [email protected];

    rm -f [email protected]$$$$

Далее в директории, содержащей 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) [email protected] $(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, которое могло быть указано в командной строке или в переменной среды.

Теперь давайте посмотрим на то, как работает пример 1.24. Наиболее важным правилом является то, которое заставляет make вызываться для каждой из директорий johnpaul, georgeringo и hellobeatles.

johnpaul georgeringo hellobeatles:

$(MAKE) [email protected] $(TARGET)

Чтобы понять это правило, вы должны знать три вещи. Во-первых, переменная MAKE раскрывается как имя запущенного в данный момент экземпляра make. Обычно это будет make, но на некоторых системах это может быть gmake. Во-вторых, опция командной строки --directory=<path> заставляет make вызваться с <path> в качестве текущей директории. В-третьих, правило с несколькими целями эквивалентно набору правил, каждое из которых имеет по одной цели и которые содержат одинаковые командные сценарии. Так что приведенное выше правило эквивалентно следующим.

johnpaul:

    $(MAKE) [email protected] $(TARGET)


georgeringo:

    $(MAKE) [email protected] $(TARGET)


hellobeatles:

    $(MAKE) [email protected] $(TARGET)

В свою очередь это эквивалентно:

johnpaul:

    $(MAKE) --directory=johnpaul $(TARGET)


georgeringo:

    $(MAKE) --directory=georgeringo $(TARGET)


hellobeatles:

    $(MAKE) -directory=hellobeatles $(TARGET)

Следовательно, эффект от этого правила состоит в вызове make-файлов в каждой из директорий johnpaul, georgeringo и hellobeatles, а в командной строке передаётся значение переменной TARGET. В результате для сборки цели xxx в каждом из подчиненных make-файлов требуется выполнить главный make-файл с опцией TARGET=xxx.

Последнее правило make-файла гарантирует, что подчиненные make-файлы вызываются в правильном порядке; оно просто объявляет цель hellobeatles, зависящую от целей johnpaul и georgeringo.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*