Выбрать главу

Пример 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) --directory=$@ $(TARGET)

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

johnpauclass="underline"

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

georgeringo:

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

hellobeatles:

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

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

johnpauclass="underline"

    $(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.

hellobeatles: johnpaul georgeringo

В более сложном приложении может иметься большое количество зависимостей между исполняемым файлом и входящими в него библиотеками. Для каждой такой компоненты требуется объявить правило, указывающее другие компоненты, от которых она явно зависит.

Смотри также

Рецепты 1.5, 1.10 и 1.13.

1.19. Определение макроса

Проблема

Вы хотите определить символ препроцессора name, присвоив ему либо неопределенное значение, либо значение value.

Решение

Опции компилятора для определения макросов в командной строке показаны в табл. 1.16. Инструкции для определения макросов в IDE приведены в табл. 1.17. Чтобы определить макрос с помощью Boost.Build, просто добавьте в требования цели свойство вида <define>name[=value], как показано в табл. 1.15 и примере 1.12.

Табл. 1.16. Определение макроса из командной строки

Инструментарий Опции
Все -Dname[-value]

Табл. 1.17. Определение макроса из IDE

IDE Конфигурация
Visual C++ На страницах свойств проекта перейдите к Configuration Properties→C/C++→Preprocessor и в Preprocessor Definitions (определения препроцессора) введите name[=value], для разделения нескольких записей используя точку с запятой
CodeWarrior В окне Target Settings перейдите к Language Settings→C/C++ Preprocessor и введите: #define name[=value] в поле с именем Prefix Text
C++Builder В Project Options перейдите к Directories/Conditionals и в Preprocessor Definitions введите name[=value], для разделения нескольких записей используя точку с запятой
Dev-C++ В Project Options выберите Parameters и введите: -Dname[=value] в области C++ Compiler
Обсуждение

Символы препроцессора часто используются в коде C++ для того, чтобы один набор исходных файлов мог быть использован в нескольких конфигурациях сборки или операционных системах. Например, предположим, что вы хотите написать функцию, проверяющую, является ли имя объекта именем файла или директории. Сейчас стандартная библиотека C++ не предоставляет функциональности, необходимой для выполнения этой задачи. Следовательно, эта функция должна использовать функции, специфичные для платформы. Если вы хотите, чтобы этот код работал и в Windows, и в Unix, вы должны убедиться, что код, использующий специфичные для Windows функции, невидим для компилятора при компиляции под Unix, и наоборот. Обычным способом достижения этого эффекта является использование условной компиляции, иллюстрируемой в примере 1.25.

Пример 1.25. Условная компиляция с помощью предопределенных макросов

#ifdef _WIN32

# include <windows.h>

#else // He Windows - предположим, что мы в Unix