мета-данные страницы
  •  

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

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