====== make ======
(https://www.opennet.ru/docs/RUS/gnumake/)
NTB: //Сборка// = //компиляция// + //линковка//
Если запустить ''make'' то программа попытается найти файл с именем по умолчание ''Makefile'' в текущем каталоге и выполнить инструкции из него. Если в текущем каталоге есть несколько мейкфайлов, то можно указать на нужный вот таким образом: ''make -f MyMakefile''
Работу ''make'' можно представить себе так: ''{Из чего делаем? (реквизиты)} ---> [Как делаем? (команды)] ---> {Что делаем? (цели)}''
===== Синтаксис Makefile =====
==== Общая структура ====
MakeFile состоит из набора правил, которые в свою очередь описываются:
* целями (то, что данное правило делает);
* реквизитами (то, что необходимо для выполнения правила и получения целей);
* командами (выполняющими данные преобразования).
В общем виде синтаксис makefile можно представить так:
# Индентация осуществляется исключительно при помощи символов табуляции,
# каждой команде должен предшествовать отступ
<цели>: <реквизиты>
<команда #1>
...
<команда #n>
==== Инкрементная компиляция ====
Если файлов в проекте много, при каждой сборке весь проект компилируется полностью, а это долго. Решение - разделить компиляцию на два этапа: трансляция и линковка.
Теперь, когда внесены изменения в один из исходников, достаточно произвести его трансляцию, а затем произвести линковку всех объектных файлов. Такой подход называется **инкрементной компиляцией**. Для ее поддержки make сопоставляет время изменения целей и их реквизитов (используя данные файловой системы), благодаря чему самостоятельно решает какие правила следует выполнить, а какие можно просто проигнорировать:
main.o: main.c
gcc -c -o main.o main.c
hello.o: hello.c
gcc -c -o hello.o hello.c
hello: main.o hello.o
gcc -o hello main.o hello.o
При первом запуске ''make'' попытается сразу получить цель ''hello'', но для неё нужны ''main.o'' и ''hello.o'', а их нет, так что ''make'' ищет правила для них и выполняет. Как только все реквизиты будут получены, make вернется к выполнению отложенной цели. (Отсюда следует, что make выполняет правила рекурсивно.)
==== Фиктивные цели ====
"фиктивные" (phony) цели позволяют осуществлять с помощью ''make'' не только сборку программы. Вот краткий список стандартных целей:
''all'' — является стандартной целью по умолчанию. При вызове ''make'' ее можно явно не указывать.
''clean'' — очистить каталог от всех файлов полученных в результате компиляции.
''install'' — произвести инсталляцию
''uninstall'' — и деинсталляцию соответственно.
Для того чтобы make не искал файлы с такими именами, их следует определить в Makefile, при помощи директивы .PHONY
.PHONY: all clean install uninstall
all: hello # сборка
clean: # очистка от файлов, созданых во время сборки
rm -rf hello *.o
main.o: main.c
gcc -c -o main.o main.c
hello.o: hello.c
gcc -c -o hello.o hello.c
hello: main.o hello.o
gcc -o hello main.o hello.o
install: # инсталяция
install ./hello /usr/local/bin
uninstall: # деинсталяция
rm -rf /usr/local/bin/hello
ВАЖНО: ''clean'' необходим, что бы принудительно пересобрать проект с нуля, так как если реквизит ''hello'' уже существует, ''make'' его переделывать не будет. Однако, в данном случаи достаточно руками удалить ''hello'' и запустить сборку, что бы не пересобрать все объектники.
==== Использование переменных и комментариев ====
Переменные - удобный способ учесть возможность того, что проект будут собирать другим компилятором или с другими опциями.
CC=g++ # Это комментарий, который говорит, что переменная CC указывает компилятор, используемый для сборки
CFLAGS=-c -Wall # Это еще один комментарий. Он поясняет, что в переменной CFLAGS лежат флаги, которые передаются компилятору
hello: main.o
$(CC) main.o -o prog
main.o: main.cpp
$(CC) $(CFLAGS) main.cpp
По умолчанию make станет выполнять самое первое правило, если цель выполнения не была явно указана при вызове:
$ make <цель>
==== Автоматические переменные ====
* ''$@'' Имя цели обрабатываемого правила\\
* ''$<'' Имя первой зависимости обрабатываемого правила\\
* ''$^'' Список всех зависимостей обрабатываемого правила
подробнее [[https://rus-linux.net/nlib.php?name=/MyLDP/algol/gnu_make/gnu_make_3-79_russian_manual.html#SEC101|тут]]
===== Примеры =====
==== Демонстрация удобства использования MakeFile ====
Пример компиляции руками:
# main.cpp - главный файл
# functions.h - прототипы всех ф-ий
# factorial.cpp - инклудит functions.h и определяет реализацию тамошних ф-ий
# hello.cpp -
g++ main.cpp hello.cpp factorial.cpp -o prog
Долго каждый раз писать. Да и при разрастании проекта можно запутаться. Автоматизируем. Для нашего примера мейкфайл будет выглядеть так:
all:
g++ main.cpp hello.cpp factorial.cpp -o hello
Использовать несколько целей в одном мейкфайле полезно для больших проектов. Это связано с тем, что при изменении одного файла не понадобится пересобирать весь проект, а можно будет обойтись пересборкой только измененной части. Пример:
all: hello
hello: main.o factorial.o hello.o
g++ main.o factorial.o hello.o -o hello
main.o: main.cpp
g++ -c main.cppfactorial.o: factorial.cpp g++ -c factorial.cpp
hello.o: hello.cpp
g++ -c hello.cpp
clean:
rm -rf *.o hello
Теперь у цели ''all'' есть только зависимость, но нет команды. В этом случае make при вызове последовательно выполнит все указанные в файле зависимости этой цели.
Еще добавилась новая цель ''clean''. Она традиционно используется для быстрой очистки всех результатов сборки проекта. Очистка запускается так: ''make -f Makefile-2 clean''
==== Универсальный MakeFile ====
CC=g++
CFLAGS=-c -Wall
LDFLAGS=
SOURCES=main.cpp hello.cpp factorial.cpp
OBJECTS=$(SOURCES:.cpp=.o)
EXECUTABLE=prog
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $@
.cpp.o:
$(CC) $(CFLAGS) $< -o $@
==== Удобство для пользователя ====
установка при помощи исходников (любые, программы и библиотеки). Допустим, наш целевой пакет называется "hello".
''1)'' Получаем код:
можно клонировать репозиторий:
git clone hello
cd hello
ИЛИ используем тарболл:
tar -xf hello-1.0.tar.xz
''2)'' Сборка:
если хотим использовать голый make:
make PREFIX=/префикс-который-вы-хотите
если используем autotools:
./configure --prefix=/префикс-который-вы-хотите
make
если используем cmake:
cmake -DCMAKE_INSTALL_PREFIX=/префикс-который-вы-хотите .
make
Откуда точка в конце? Дело в том, что cmake'у нужно передавать путь к сорцам. А поскольку у нас не out of tree build, мы собираем здесь же. Сорцы находятся там же, где находимся мы. Поэтому точка, т. е. текущий каталог.
''3)'' Установка:
Используя make:
make PREFIX=/префикс-который-вы-хотите install
Используя autotools или cmake:
make install
Cборка всегда осуществляется с обычными правами, а вот установка осуществляется с теми правами, которые нужны, чтобы записать в prefix.