====== 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.