.PHONY: clean
clean:
for file in $(CLEANEXTS); do rm -f *.$$file; done
# Укажите зависимости файлов .cpp от файлов .hpp
john.obj: john.hpp
paul.obj: paul.hpp
johnpaul.obj: john.hpp paul.hpp johnpaul.hpp
В примере 1.21 я с помощью переменной среды MSVCDIR, устанавливаемой в'$(MSVCDIR)/bin/link'
. Это позволяет избежать путаницы между компоновщиком Visual C++ и командой Unix
Давайте подробно рассмотрим пример 1.20. Вначале я определяю переменные, представляющие выходной файл, директорию установки и расширения файлов, которые должны удаляться при сборке цели clean
. Затем я объявляю цель по умолчанию all
, как в примере 1.14.
Правило для сборки статической библиотеки выглядит так.
$(OUTPUTFILE): john.o paul.o johnpaul.о
ar ru $@ $^
ranlib $@
Это непосредственная адаптация записи для GCC из табл. 1.10. Здесь $(OUTPUTFILE)
и $@
раскрываются как libjohnpaul.a
, а $^
раскрывается в виде списка пререквизитов john.o paul.o johnpaul.о
.
Следующие два правила объявляют цели install
и clean
, как в рецепте 1.15. Единственное отличие состоит в том, что в примере 1.20 для удаления всех файлов, чьи расширения имеются в списке о а
— т. е. все объектные файлы и файлы статической библиотеки, - я использую цикл оболочки.
for file in $(CLEANEXTS); do rm -f *.$$file; done
Двойной знак доллара я использовал для того, чтобы запретить $$file
при передаче ее оболочке.
Три последних правила указывают отношения зависимостей между файлами
john.o: john.hpp
paul.o: paul.hpp
johnpaul.o. john.hpp paul.hpp johnpaul.hpp
Это можно понять следующим образом. Если
Это решение удобно только для очень небольших проектов, так как очень сложно постоянно отслеживать зависимости целей, представляющих собой исходные файлы, входящие в большую базу кода. К счастью, имеется несколько способов автоматической генерации этих зависимостей. Например, три последних правила примера 1.20 можно заменить на следующие.
# Генерируем зависимости .cpp-файлов от .hpp-файлов
include john.o paul.o johnpaul.о
%.d: %.cpp
$(CC) -M $(CPPFLAGS) $< > $@.$$$$;
sed 's.($*).o[ :]*.1.o $@ : ,g' < $@.$$$$ > $@,
rm -f $@.$$$$
Этот фрагмент кода основан на опции компилятора
Код для генерации зависимостей помещайте в конец make- файла.
Так как большинство компиляторов имеет опцию, аналогичную опции
Кроме того, можно использовать опцию
include john.d paul.d johnpaul.d
%d: %.cpp
'$(MSVCDIR)/bin/cl' -E -showIncludes $< 2> $@.$$$$ > /dev/null;
sed -n 's/^Note: including file: *(.*)/$*.obj•$*.d:1/gp'
< $@.$$$$ | sed 's:\:/:g:s: :\ :gp' > $@;
rm -f $@.$$$$
В этом примере символ • обозначает Tab.
Давайте сделаем еще одно последнее усовершенствование примера 1.20. В настоящий момент последовательность john paul johnpaul
содержится в двух местах — в пререквизитах правила для сборки статической библиотеки и в директиве include
, используемой для генерации зависимостей. Если список исходных файлов изменится, вам придется вносить изменения в двух местах make-файла. Гораздо лучше определить переменную SOURCES
и заменить оба