-
Notifications
You must be signed in to change notification settings - Fork 5
GNU Make
Компилировать любой проект руками -- занятие неблагодарное и весьма утомительное, даже если в проекте всего один файл с исходным кодом программы, а тем более когда файлов становится больше. Для каждого из них надо каждый раз вручную набивать команды компиляции и линковки. Обычно принято всю работу поручать IDE, но возникает грандиозная проблема -- если все настройки компиляции хранить в IDE-проекте, все разработчики должны работать не только в одной и той же среде, но иногда даже иметь одинаковую версию. Для группы из нескольких разработчиков такой подход еще допустим, но мы говорим не только о Linux-проектах их сотен разработчиков со своими привычками, но и о разработке кросс-платформенных программ, которые должны компилироваться и работать более чем под тремя операционными системами, и десятком различных аппаратных архитектур (включая микроконтроллеры).
К счастью, для задачи автоматического запуска компиляторов и утилит при изменении файлов проектов уже более 50 лет существует стандартная утилита make
, которая поставляется в комплекте с любым компилятором, или (в случае Linux) устанавливается отдельным пакетов дистрибутива. Создав в каталоге проекта обычный текстовый файл Makefile
, вы можете всего в десяток строчек прописать все зависимости между файлами, и как вызывать каждую утилиту или компилятор при их изменении.
Если запустить
$ make
то программа попытается найти файл с именем по умолчание Makefile в текущем каталоге и выполнить инструкции из него. Если в текущем каталоге есть несколько мейкфайлов, то можно указать на нужный вот таким образом:
make -f MyMakefile
Код для make
имеет два типа:
- определение переменных через
=
- правила
Правило имеет вид
цель : [источник ...]
<tab> команда
...
- цель -- файл(ы) который должен быть обновлен при изменении даты редактирования любого из
- файлов-источников с помощью
- команд отделенных слева символов табуляции (НЕ пробелами -- настройте ваш редактор, чтобы не заменял)
Каждый раз, когда вы меняете программу, изменяя хотя бы один из файлов-источников, make
отследит зависимости, и выполнит заданные команды, чтобы обновить все файлы-цели.
Другое применение -- в обратную сторону: в источники прописываются файлы, необходимые для сборки проекта (компиляции программы), и если таких файлов не существует, они будут созданы соответствующими правилами.
Переменные позволяют назначить символьным именам значения, которые могут поставляться:
A = B |
присвоение значения, подставляемого вместо имени переменной $(A)
|
C = |
обнуление переменной, вместо имени переменной будет подставляться пустая строка |
D ?= E |
условное назначение, если D ранее не была определена |
Переменные могут быть заданы не только в Makefile
, но и в командной строке при вызове make
.
Так вы можете передавать настройки сборки проекта, например можно явно указать, для какой платы вы собираете свою игру:
~/itstep$ make HW=rpi3 mygame
Эти встроенные переменные используются почти в каждом правиле make
, так как запуск компилятора
делается по одному и тому же шаблону для совершенно разных файлов:
$@ |
цель |
$< |
первый источник |
$^ |
все источники |
$? |
измененные источники |
Некоторым переменным приписано определенное значение
CC |
команда запуска компилятора Си |
CXX |
команда запуска компилятора С++ |
LD |
линкер |
CFLAGS |
|
CXXFLAGS |
|
LDFLAGS |
|
MAKE |
MODULE = $(notdir $(CURDIR))
CWD = $(CURDIR)
GZ = $(CWD)/gz
TMP = $(CWD)/tmp
SRC = $(TMP)/src
MODULE |
модуль -- имя текущего каталога\проекта |
CWD |
корневой каталог проекта из которого вызывается make
|
GZ |
каталог для архивов исходного кода |
TMP |
временный каталог, используемый при сборке из исходников |
SRC |
сюда распаковываются исходники программных пакетов |
разбор примера Makefile
используемого в Buildroot
## Makefile
# комментарий
.PHONY: os dirs gz
## по умолчанию при запуске `make` срабатывает самое первое правило
## для явного запуска используйте команду `make os`
os: dirs gz
## прописываем набор переменных для пакетов
# версия
BR_VER = 2019.02.4
# полное название пакета с версией
BR = buildroot-$(BR_VER)
# имя файла с архивом исходного кода
BR_GZ = $(BR).tar.gz
## набор типовых переменных предпочтительнее определять в самом начале `Makefile`
MODULE = $(notdir $(CURDIR))
CWD = $(CURDIR)
GZ = $(CWD)/gz
TMP = $(CWD)/tmp
SRC = $(TMP)/src
# готовим набор каталогов
dirs:
mkdir -p $(GZ) $(TMP) $(SRC)
# загрузка архивов, необходимых для сборки системы
gz: $(GZ)/$(BR_GZ)
# при отсутствии архива создаем его загрузкой с сети
# версия подставляется через переменную
$(GZ)/$(BR_GZ):
wget -c -O $@ https://github.com/buildroot/buildroot/archive/$(BR_VER).tar.gz