Внутреннее устройство Linux
Шрифт:
Главной идеей утилиты make является цель — то, чего вы желаете достичь. Целью может быть файл (файл .o, исполняемый файл и т. д.) или ярлык. Кроме того, некоторые цели зависят от других целей; например, вам необходимо создать полный набор файлов .o, прежде чем вы сможете скомпоновать исполняемый файл. Эти требования называются зависимостями.
Чтобы собрать цель, утилита make следует какому-либо правилу, например, определяющему, как перейти от исходного файла .c к объектному файлу .o. Утилите make уже известны некоторые правила,
15.2.1. Пример файл Makefile
Следующий очень простой файл Makefile собирает программу myprog из файлов aux.c и main.c:
# object files
OBJS=aux.o main.o
all: myprog
myprog: $(OBJS)
$(CC) -o myprog $(OBJS)
Символ # в первой строке этого файла означает комментарий.
Следующая строка является всего лишь макроопределением; она задает для переменной OBJS два имени объектных файлов. Это будет важно в дальнейшем. Сейчас обратите внимание на то, как записывается макроопределение и как на него ссылаются далее ( ($(OBJS) ).
Следующий элемент файла Makefile содержит первую цель, all. Первая цель всегда является целью по умолчанию, утилита make будет собирать ее, если вы запустите команду make в командной строке саму по себе.
Правило сборки цели следует после двоеточия. Для цели all в этом файле Makefile сказано, что вам необходимо удовлетворить чему-то по имени myprog. Это первая зависимость в данном файле; цель all зависит от myprog. Заметьте, что myprog может быть реальным файлом или целью другого правила. В данном случае оно является и тем и другим (правилом для цели all и целью для OBJS).
Чтобы собрать программу myprog, этот файл Makefile использует макроопределение $(OBJS) в зависимостях. Макроопределение развертывается в имена aux.o и main.o, поэтому программа myprog зависит от этих двух файлов (они должны быть реальными файлами, поскольку нигде в файле Makefile нет целей с такими именами).
Данный файл Makefile предполагает, что у вас есть два файла с исходным кодом на языке C: aux.c и main.c в одном каталоге. Если запустить утилиту make для файла Makefile, то в результате будут показаны команды, выполняемые утилитой:
$ make
cc -c -o aux.o aux.c
cc -c -o main.o main.c
cc -o myprog aux.o main.o
Схема зависимостей приведена на рис. 15.1.
Рис. 15.1. Зависимости в файле Makefile
15.2.2. Встроенные правила
Каким же образом утилита make узнает о том, как перейти от файла aux.c к файлу aux.o? Ведь файла aux.c нет внутри файла Makefile. Ответ такой: утилита make следует встроенным правилам. Она знает о том, что следует искать файл .c, если вам необходим файл .o, и, более того, она знает, как запустить команду cc -c для этого файла .c, чтобы добиться цели — создать файл .o.
15.2.3. Окончательная сборка программы
Последний шаг при получении
$(CC) -o myprog $(OBJS)
Отступ перед переменной $(CC) является табуляцией. Вы обязаны вставлять табуляцию перед каждой реальной командой в той строке, где она находится.
Остерегайтесь следующего сообщения:
Makefile:7: *** missing separator. Stop.
Подобная ошибка означает, что файл Makefile не в порядке. Табуляция является разделителем, и, если она отсутствует или есть какая-либо другая помеха, вы увидите такую ошибку.
15.2.4. Поддержание актуальных версий файлов
Одним из последних фундаментальных свойств утилиты make является то, что ее цели должны соответствовать по времени зависимостям. Если вы дважды вызовете утилиту make из предыдущего примера, первая из них соберет программу myprog, но вторая выдаст такое сообщение:
make: Nothing to be done for 'all'.
Во второй раз утилита make посмотрела свои правила и обнаружила, что программа myprog уже существует, поэтому она не стала повторно собирать ее, поскольку ни одна из зависимостей не изменилась с тех пор, как была собрана программа. Чтобы поэкспериментировать, выполните следующее.
1. Запустите команду touch aux.c.
2. Запустите утилиту make еще раз. На этот раз она определит, что файл aux.c является более «свежим», чем файл aux.o, уже находящийся в каталоге, поэтому она скомпилирует файл aux.o заново.
3. Программа myprog зависит от файла aux.o, и теперь файл aux.o является более новым, чем уже существующая программа myprog, поэтому утилита make должна создать программу myprog заново.
«Цепные реакции» такого типа весьма характерны.
15.2.5. Аргументы и параметры командной строки
Вы можете извлечь существенную пользу от утилиты make, если будете знать, как работают ее аргументы и параметры командной строки.
Один из самых полезных параметров позволяет указать единичную цель в командной строке. Для предыдущего файла Makefile можно запустить команду make aux.o, если вам необходим только файл aux.o.
Можно также создать макроопределение в командной строке. Чтобы, например, использовать компилятор clang, попробуйте такую команду:
$ make CC=clang
Здесь утилита make использует ваше определение переменной CC вместо собственного компилятора по умолчанию cc. Макроопределения из командной строки становятся полезны при тестировании определений и библиотек препроцессора, в особенности с макроопределениями CFLAGS и LDFLAGS, о которых мы вкратце поговорим.
На самом деле вам даже не требуется файл Makefile для запуска утилиты make. Если встроенные в утилиту правила соответствуют цели, можно просто попросить утилиту создать цель. Например, если у вас есть исходный код для очень простой программы blah.c, попробуйте запустить команду make blah. Работа утилиты будет выглядеть так: