Работа с утилитой make
Для понятности я опишу логику работы утилиты make с этим Makefile: сначала проверяется наличие файла main.o – первой зависимости у цели kv. Если его нет, то выполняется его компиляция, если есть, то проверяется, не был ли изменен файл main.c с момента последней его компиляции (вот зачем нужно указывать main.c как зависимость для main.o!). Если он был изменен, он компилируется заново. Если же он не был изменен, то просто пропускается; таким образом при повторной сборке можно значительно сократить затрачиваемое время.
Далее та же проверка происходит и для kvadrat.o, и как только будут получены main.o и kvadrat.o, включающие все изменения в исходном коде, начнется сборка исполняемого файла.
Итак, сохраните Makefile и запустите make. Должны появиться два .o-файла и сам исполняемый файл kv. Для проверки запустите его:
Теперь давайте добавим в Makefile еще одну цель: clean. При вводе make clean должны удаляться три полученных в результате компиляции файла. Допишите в конец Makefile такие строки:
Сохранив Makefile, введите make clean. Все три двоичных файла будут удалены.
Теперь создадим последнюю цель – install. Она будет служить для копирования двоичного файла программы в системный каталог.
Тут есть несколько вариантов. Во-первых, можно сделать так, чтобы при выполнении make install проверялось, была ли уже скомпилирована программа, и если нет, то перед установкой выполнялась бы ее компиляция (логично, не правда ли?). Это можно сделать, указав kv в качестве зависимости. Во-вторых, для копирования программы в системный каталог можно использовать или традиционный метод – утилиту cp, а можно – специально предназначенную для этого программу install. Она может при установке заодно изменять права доступа к программе, удалять из нее отладочную информацию (но мы это уже предусмотрели при помощи команды strip); нам же понадобится такая возможность, как автоматическое создание каталогов по указанному пути. Возможно, это звучит запутанно, так что я поясню. Мы установим программу в каталог /opt/kv/ (каталог /opt предназначен для хранения пользовательских программ, необязательных для функционирования системы). Разумеется, этого каталога еще не существует, поэтому, если бы мы использовали для установки команду «cp kv /opt/kv/», это привело бы к ошибке. Если же использовать команду install с ключом -D, она автоматически создаст все отстутствующие каталоги. Кроме того, нам нужно будет сделать так, чтобы никто, кроме root, не мог выполнять программу, а смог только считывать ее файл (права доступа r-xr—r—, или 544 в восьмеричном виде).
Добавьте в конец Makefile следующие строки:
install -D -m 544 kv /opt/kv/kv
Запустите make install, затем введите команду:
чтобы убедиться в правильности установки.
После параметра -m у команды install указываются права доступа, предпоследним параметром – файл, который нужно скопировать, последним параметром является каталог назначения с именем файла (т.е. исполняемый файл можно установить под другим именем, введя, например, /opt/kv/program_name в качестве последнего параметра).
Теперь поговорим о переменных. Представьте, что вам нужен Makefile для более сложного проекта, где в каталог назначения копируется одновременно несколько файлов. Вы укажете путь установки для каждого из них, например, так:
Но вдруг вам потребовалось изменить путь установки. Вам придется поменять параметр у каждой команды. И хотя это можно сделать относительно быстро при помощи команды «заменить» в текстовом редакторе, проще всего определить переменную один раз в начале Makefile и потом использовать ее (при необходимости изменяя только ее значение). Добавьте в начало Makefile определение переменной:
И затем измените команду install таким образом:
install -D -m 544 kv $(INSTALL_PATH)/kv
На что здесь нужно обратить внимание:
- в объявлении переменной указывается просто ее имя, а когда вы ее используете – знак $ и имя;
- если имя переменной состоит более чем из одного символа, при ее использовании нужно заключать такое имя в скобки;
- нужно или нет ставить косую черту в значении переменной (/opt/kv/ или /opt/kv)? Ответ: лучше перестраховаться и поставить. Как вы можете заметить, в команде установки косая черта идет подряд дважды: /opt/kv//kv (одна косая черта из значения переменной, другая – из записи $(INSTALL_PATH)/kv). Это не приведет к ошибке – вы можете поставить символ «/» хоть десять раз подряд. А вот если забудете поставить ее хотя бы один раз, то результаты, ясное дело, будут отличаться от ожидаемых.
Теперь рассмотрим такой момент: допустим, нужно выполнить некоторую команду и присвоить переменной выводимое этой командой значение. Например, если для разных версий ядра Linux существуют разные версии вашей программы. В таком случае при выполнении установки программы на системе с ядром 2.6.1 можно установить ее в каталог /opt/kv/2.6.1/, а если система запущена с ядром 2.4.24 – в каталог /opt/kv/2.4.24/. Версию ядра можно определить при помощи команды uname -r (попробуйте выполнить ее, чтобы посмотреть вашу версию ядра). Вопрос в том, как же передать полученное значение переменной? Очень просто:
INSTALL_PATH = /opt/kv/`uname -r`/
Теперь установка будет производиться в каталог /opt/kv/версия_вашего_ядра/. Обратите внимание на косые кавычки: они вводятся при помощи Shift+~ (крайняя левая клавиша в ряду кнопок с цифрами).
И последнее: а ведь make может использоваться не только для компиляции программ! Например, можно использовать Makefile для сборки документации, да и вообще для автоматизации любой работы. Для примера можно создать программу, которая по команде make автоматически определяет, какие из трех файлов с именами «1», «2», «3» изменились, и при необходимости выполняет их резервное копирование в каталог backup, а по окончании работы выводит сообщение «Backup completed»:
backup: backup/1 backup/2 backup/3
Домашнее задание для вас: разберитесь, как работает этот пример, а к первому добавьте цель uninstall, так, чтобы при выполнении команды make uninstall установленная программа удалялась.
Linux make clean install
Команда make позволяет задействовать одноименную утилиту, предназначенную для компиляции программного обеспечения из исходных кодов. Данная команда востребована главным образом программистами и системными администраторами, но может оказаться полезной и для обычных пользователей, желающих собрать то или иное программное обеспечение из исходных кодов вместо установки бинарных пакетов. Рассматриваемая утилита использует файлы описания целей сборки под названием Makefile и автоматически создает список файлов исходного кода, которые должны быть скомпилированы, экономя тем самым время программистов, постоянно модифицирующих исходный код своих продуктов. Поддерживаются любые компиляторы, которые могут запускаться с помощью терминала, причем в рамках файлов описания целей сборки могут описываться цели, предназначенные для установки ПО (обычно называются «install») и очистки окружения сборки (обычно называются «clean»). Нередко файлы Makefile генерируются автоматически, причем в таких случаях перед компиляцией ПО необходимо исполнять сценарий командной оболочки configure.sh, осуществляющий их генерацию и настройку сборочного окружения.
Базовый синтаксис команды выглядит следующим образом:
Команда принимает названия целей в качестве аргументов, причем в случае отсутствия названия цели считается, что нужно использовать цель «all», обычно включающую все остальные цели. Для описания целей используются специальные файлы с именами Makefile, причем данные файлы имеют достаточно строгий синтаксис (к примеру, в них запрещается смешивать символы табуляции и пробела). Что касается параметров, то наиболее важными являются параметр -f для указания нестандартного имени файла описания целей, параметр -C для смены директории перед сборкой ПО, параметр -d для вывода отладочных сообщений, параметр -e для изменения переменных окружения, а также параметр -B для безусловной сборки всех целей.
Примеры использования
В качестве примера будет использоваться примитивная программа на языке C, выводящая сообщение «It works!». Она будет состоять из трех файлов исходного кода и файла с описанием целей сборки. Файл с описанием целей сборки создан вручную и не требует предварительной конфигурации.
Содержимое файла main.c:
int main(int argc, char **argv)
{
core();
return 0;
}
Содержимое файла core.h:
#ifndef _CORE_H_
#define _CORE_H_
Содержимое файла core.c:
void core(void)
{
printf(«It works!n»);
}
Содержимое файла Makefile:
GCC = gcc -g -Wall
OBJ = core.o main.o
test: $(OBJ)
$(GCC) $(LDFLAGS) $(OBJ) -o test
Сборка программы из исходных кодов
Для сборки программы достаточно выполнить команду make без каких-либо параметров:
$ make
gcc -g -Wall -c core.c -o core.o
gcc -g -Wall -c main.c -o main.o
gcc -g -Wall core.o main.o -o test
$ ./test
It works!
Очевидно, что программа была собрана и корректно функционирует.
Сборка программы с отладкой системы сборки
Если вы желаете отладить систему сборки, вы можете использовать параметр -d команды make:
$ make -d
GNU Make 4.3
Эта программа собрана для x86_64-pc-linux-gnu
Copyright (C) 1988-2020 Free Software Foundation, Inc.
Лицензия GPLv3+: GNU GPL версии 3 или новее
Это свободное программное обеспечение: вы можете свободно изменять его и
распространять. НЕТ НИКАКИХ ГАРАНТИЙ вне пределов, допустимых законом.
Чтение make-файлов.
Чтение make-файла «Makefile».
Обновление make-файлов.
Обработка целевого файла «Makefile».
Поиск неявного правила для «Makefile».
Попытка применения правила с образцом «Makefile».
Попытка применения неявной зависимости «Makefile.o».
Попытка применения правила с образцом «Makefile».
Попытка применения неявной зависимости «Makefile.c».
.
Целевой файл «test» успешно пересоздан.
Обновление целей, от которых зависит целевой файл «all», завершено.
Необходимо пересобрать цель «all».
Целевой файл «all» успешно пересоздан.
$ ./test
It works!
Программа также успешно собрана и корректно функционирует, но в данном случае выводится огромный объем отладочной информации, относящейся к работе утилиты make.
Сборка отдельных целей
Для сборки отдельной цели следует передать названием этой цели команде make. В нашем случае подходящей целью является цель с названием «test»:
$ make test
gcc -g -Wall -c core.c -o core.o
gcc -g -Wall -c main.c -o main.o
gcc -g -Wall core.o main.o -o test
$ ./test
It works!
И снова программа успешно собрана, причем в нашем случае цель «all» автоматически подразумевает сборку цели «test», поэтому никаких отличий с использованием команды make без параметров не будет наблюдаться.
Для очистки рабочего окружения следует инициировать сборку цели «clean»:
$ make clean
rm -f *.o
rm -f test
В результате будут удалены объектные файлы и результирующий исполняемый файл программы.
Сборка программы в заданной директории
Для сборки программы в заданной директории следует использовать параметр -C и путь к этой директории. Для демонстрации перейдем в директорию на уровень выше и попробуем пересобрать программу.
$ cd ..
$ make -C ./test/
make: вход в каталог «/home/alex/code/test»
gcc -g -Wall -c core.c -o core.o
gcc -g -Wall -c main.c -o main.o
gcc -g -Wall core.o main.o -o test
make: выход из каталога «/home/alex/code/test»
$ ./test/test
It works!
Разумеется, и в этом случае программа была успешно собрана.
Сборка программы с нестандартным именем файла описания целей сборки
Если программа поставляется с файлом описания целей сборки с нестандартным именем (обычно разработчики используют различные суффиксы для добавления поддержки различных компиляторов или платформ), вы можете использовать параметр -f и передать утилите make имя этого файла. В качестве примера переименуем файл Makefile в Makefile.gcc:
$ mv Makefile Makefile.gcc
$ make
make: *** Не заданы цели и не найден make-файл. Останов.
И в этом случае сборка не будет представлять каких-либо сложностей:
$ make -f Makefile.gcc
gcc -g -Wall -c core.c -o core.o
gcc -g -Wall -c main.c -o main.o
gcc -g -Wall core.o main.o -o test
$ ./test
It works!
Таким образом вы можете использовать несколько файлов описания целей сборки в рамках одного проекта.